[clang] 34b422b - clang-format: [JS] support import/export type
Krasimir Georgiev via cfe-commits
cfe-commits at lists.llvm.org
Wed May 10 06:28:27 PDT 2023
Author: Jan Kuhle
Date: 2023-05-10T15:27:03+02:00
New Revision: 34b422bafbd934ee3c644fd7a8f0b6803976c818
URL: https://github.com/llvm/llvm-project/commit/34b422bafbd934ee3c644fd7a8f0b6803976c818
DIFF: https://github.com/llvm/llvm-project/commit/34b422bafbd934ee3c644fd7a8f0b6803976c818.diff
LOG: clang-format: [JS] support import/export type
Contributed by @jankuehle!
Users can choose to only import/export the type of the symbol (not value nor namespace) by adding a `type` keyword, e.g.:
```
import type {x} from 'y';
import {type x} from 'y';
export type {x};
export {type x};
```
Previously, this was not handled and would:
- Terminate import sorting
- Remove the space before the curly bracket in `export type {`
With this change, both formatting and import sorting work as expected.
Reviewed By: MyDeveloperDay, krasimir
Differential Revision: https://reviews.llvm.org/D150116
Added:
Modified:
clang/lib/Format/SortJavaScriptImports.cpp
clang/lib/Format/UnwrappedLineParser.cpp
clang/unittests/Format/FormatTestJS.cpp
clang/unittests/Format/SortImportsTestJS.cpp
Removed:
################################################################################
diff --git a/clang/lib/Format/SortJavaScriptImports.cpp b/clang/lib/Format/SortJavaScriptImports.cpp
index ce83ddea3e076..af2d3400c970d 100644
--- a/clang/lib/Format/SortJavaScriptImports.cpp
+++ b/clang/lib/Format/SortJavaScriptImports.cpp
@@ -72,6 +72,7 @@ struct JsImportedSymbol {
struct JsModuleReference {
bool FormattingOff = false;
bool IsExport = false;
+ bool IsTypeOnly = false;
// Module references are sorted into these categories, in order.
enum ReferenceCategory {
SIDE_EFFECT, // "import 'something';"
@@ -306,6 +307,7 @@ class JavaScriptImportSorter : public TokenAnalyzer {
if (Reference->Category == JsModuleReference::SIDE_EFFECT ||
PreviousReference->Category == JsModuleReference::SIDE_EFFECT ||
Reference->IsExport != PreviousReference->IsExport ||
+ Reference->IsTypeOnly != PreviousReference->IsTypeOnly ||
!PreviousReference->Prefix.empty() || !Reference->Prefix.empty() ||
!PreviousReference->DefaultImport.empty() ||
!Reference->DefaultImport.empty() || Reference->Symbols.empty() ||
@@ -488,6 +490,11 @@ class JavaScriptImportSorter : public TokenAnalyzer {
bool parseStarBinding(const AdditionalKeywords &Keywords,
JsModuleReference &Reference) {
// * as prefix from '...';
+ if (Current->is(Keywords.kw_type) && Current->Next &&
+ Current->Next->is(tok::star)) {
+ Reference.IsTypeOnly = true;
+ nextToken();
+ }
if (Current->isNot(tok::star))
return false;
nextToken();
@@ -503,6 +510,12 @@ class JavaScriptImportSorter : public TokenAnalyzer {
bool parseNamedBindings(const AdditionalKeywords &Keywords,
JsModuleReference &Reference) {
+ if (Current->is(Keywords.kw_type) && Current->Next &&
+ Current->Next->isOneOf(tok::identifier, tok::l_brace)) {
+ Reference.IsTypeOnly = true;
+ nextToken();
+ }
+
// eat a potential "import X, " prefix.
if (Current->is(tok::identifier)) {
Reference.DefaultImport = Current->TokenText;
@@ -535,14 +548,19 @@ class JavaScriptImportSorter : public TokenAnalyzer {
nextToken();
if (Current->is(tok::r_brace))
break;
- if (!Current->isOneOf(tok::identifier, tok::kw_default))
+ bool isTypeOnly =
+ Current->is(Keywords.kw_type) && Current->Next &&
+ Current->Next->isOneOf(tok::identifier, tok::kw_default);
+ if (!isTypeOnly && !Current->isOneOf(tok::identifier, tok::kw_default))
return false;
JsImportedSymbol Symbol;
- Symbol.Symbol = Current->TokenText;
// Make sure to include any preceding comments.
Symbol.Range.setBegin(
Current->getPreviousNonComment()->Next->WhitespaceRange.getBegin());
+ if (isTypeOnly)
+ nextToken();
+ Symbol.Symbol = Current->TokenText;
nextToken();
if (Current->is(Keywords.kw_as)) {
diff --git a/clang/lib/Format/UnwrappedLineParser.cpp b/clang/lib/Format/UnwrappedLineParser.cpp
index 2e5bdf96deb88..64035b0976f55 100644
--- a/clang/lib/Format/UnwrappedLineParser.cpp
+++ b/clang/lib/Format/UnwrappedLineParser.cpp
@@ -4068,7 +4068,9 @@ void UnwrappedLineParser::parseJavaScriptEs6ImportExport() {
// parsing the structural element, i.e. the declaration or expression for
// `export default`.
if (!IsImport && !FormatTok->isOneOf(tok::l_brace, tok::star) &&
- !FormatTok->isStringLiteral()) {
+ !FormatTok->isStringLiteral() &&
+ !(FormatTok->is(Keywords.kw_type) &&
+ Tokens->peekNextToken()->isOneOf(tok::l_brace, tok::star))) {
return;
}
diff --git a/clang/unittests/Format/FormatTestJS.cpp b/clang/unittests/Format/FormatTestJS.cpp
index 6f07d8b084f56..ce81bd5d2f044 100644
--- a/clang/unittests/Format/FormatTestJS.cpp
+++ b/clang/unittests/Format/FormatTestJS.cpp
@@ -1476,6 +1476,17 @@ TEST_F(FormatTestJS, ImportExportASI) {
" export class Y {}");
}
+TEST_F(FormatTestJS, ImportExportType) {
+ verifyFormat("import type {x, y} from 'y';\n"
+ "import type * as x from 'y';\n"
+ "import type x from 'y';\n"
+ "import {x, type yu, z} from 'y';\n");
+ verifyFormat("export type {x, y} from 'y';\n"
+ "export {x, type yu, z} from 'y';\n"
+ "export type {x, y};\n"
+ "export {x, type yu, z};\n");
+}
+
TEST_F(FormatTestJS, ClosureStyleCasts) {
verifyFormat("var x = /** @type {foo} */ (bar);");
}
diff --git a/clang/unittests/Format/SortImportsTestJS.cpp b/clang/unittests/Format/SortImportsTestJS.cpp
index 1c7e508630360..9d779cd988d0c 100644
--- a/clang/unittests/Format/SortImportsTestJS.cpp
+++ b/clang/unittests/Format/SortImportsTestJS.cpp
@@ -465,6 +465,46 @@ TEST_F(SortImportsTestJS, ImportEqAliases) {
"console.log(Z);\n");
}
+TEST_F(SortImportsTestJS, ImportExportType) {
+ verifySort("import type {sym} from 'a';\n"
+ "import {type sym} from 'b';\n"
+ "import {sym} from 'c';\n"
+ "import type sym from 'd';\n"
+ "import type * as sym from 'e';\n"
+ "\n"
+ "let x = 1;",
+ "import {sym} from 'c';\n"
+ "import type {sym} from 'a';\n"
+ "import type * as sym from 'e';\n"
+ "import type sym from 'd';\n"
+ "import {type sym} from 'b';\n"
+ "let x = 1;");
+
+ // Symbols within import statement
+ verifySort("import {type sym1, type sym2 as a, sym3} from 'b';\n",
+ "import {type sym2 as a, type sym1, sym3} from 'b';\n");
+
+ // Merging
+ verifySort("import {X, type Z} from 'a';\n"
+ "import type {Y} from 'a';\n"
+ "\n"
+ "X + Y + Z;\n",
+ "import {X} from 'a';\n"
+ "import {type Z} from 'a';\n"
+ "import type {Y} from 'a';\n"
+ "\n"
+ "X + Y + Z;\n");
+
+ // Merging: empty imports
+ verifySort("import type {A} from 'foo';\n", "import type {} from 'foo';\n"
+ "import type {A} from 'foo';");
+
+ // Merging: exports
+ verifySort("export {A, type B} from 'foo';\n",
+ "export {A} from 'foo';\n"
+ "export {type B} from 'foo';");
+}
+
} // end namespace
} // end namespace format
} // end namespace clang
More information about the cfe-commits
mailing list