diff --git a/clang/lib/Format/FormatToken.h b/clang/lib/Format/FormatToken.h --- a/clang/lib/Format/FormatToken.h +++ b/clang/lib/Format/FormatToken.h @@ -910,9 +910,65 @@ /// Returns \c true if \p Tok is a true JavaScript identifier, returns /// \c false if it is a keyword or a pseudo keyword. bool IsJavaScriptIdentifier(const FormatToken &Tok) const { - return Tok.is(tok::identifier) && - JsExtraKeywords.find(Tok.Tok.getIdentifierInfo()) == - JsExtraKeywords.end(); + switch (Tok.Tok.getKind()) { + case tok::kw_auto: + case tok::kw_char: + case tok::kw_concept: + case tok::kw_double: + case tok::kw_extern: + case tok::kw_float: + case tok::kw_inline: + case tok::kw_int: + case tok::kw_long: + case tok::kw_register: + case tok::kw_restrict: + case tok::kw_sizeof: + case tok::kw_static: + case tok::kw_struct: + case tok::kw_typedef: + case tok::kw_union: + case tok::kw_unsigned: + case tok::kw_volatile: + case tok::kw__Alignas: + case tok::kw__Alignof: + case tok::kw__Atomic: + case tok::kw__Bool: + case tok::kw__Complex: + case tok::kw__Generic: + case tok::kw__Imaginary: + case tok::kw__Noreturn: + case tok::kw__Static_assert: + case tok::kw__Thread_local: + case tok::kw___func__: + case tok::kw___objc_yes: + case tok::kw___objc_no: + case tok::kw_asm: + case tok::kw_bool: + case tok::kw_const_cast: + case tok::kw_dynamic_cast: + case tok::kw_explicit: + case tok::kw_friend: + case tok::kw_mutable: + case tok::kw_operator: + case tok::kw_reinterpret_cast: + case tok::kw_static_cast: + case tok::kw_template: + case tok::kw_typename: + case tok::kw_typeid: + case tok::kw_using: + case tok::kw_virtual: + case tok::kw_wchar_t: + // Exclude C++ keywords that aren't keywords in TypeScript. + return true; + case tok::identifier: + // For identifiers, make sure they are true identifiers, excluding the + // JavaScript pseudo-keywords. + return JsExtraKeywords.find(Tok.Tok.getIdentifierInfo()) == + JsExtraKeywords.end(); + default: + // Everything else is not an identifier. + return false; + } } /// Returns \c true if \p Tok is a C# keyword, returns diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp --- a/clang/lib/Format/TokenAnnotator.cpp +++ b/clang/lib/Format/TokenAnnotator.cpp @@ -1522,9 +1522,9 @@ if (Style.Language == FormatStyle::LK_JavaScript) { if (Current.is(tok::exclaim)) { if (Current.Previous && - (Current.Previous->isOneOf(tok::identifier, tok::kw_namespace, - tok::r_paren, tok::r_square, - tok::r_brace) || + (Keywords.IsJavaScriptIdentifier(*Current.Previous) || + Current.Previous->isOneOf(tok::kw_namespace, tok::r_paren, + tok::r_square, tok::r_brace) || Current.Previous->Tok.isLiteral())) { Current.Type = TT_JsNonNullAssertion; return; @@ -3070,10 +3070,8 @@ (Right.is(TT_TemplateString) && Right.TokenText.startswith("}"))) return false; // In tagged template literals ("html`bar baz`"), there is no space between - // the tag identifier and the template string. getIdentifierInfo makes sure - // that the identifier is not a pseudo keyword like `yield`, either. - if (Left.is(tok::identifier) && Keywords.IsJavaScriptIdentifier(Left) && - Right.is(TT_TemplateString)) + // the tag identifier and the template string. + if (Keywords.IsJavaScriptIdentifier(Left) && Right.is(TT_TemplateString)) return false; if (Right.is(tok::star) && Left.isOneOf(Keywords.kw_function, Keywords.kw_yield)) diff --git a/clang/unittests/Format/FormatTestJS.cpp b/clang/unittests/Format/FormatTestJS.cpp --- a/clang/unittests/Format/FormatTestJS.cpp +++ b/clang/unittests/Format/FormatTestJS.cpp @@ -2366,6 +2366,58 @@ verifyFormat("return !!x;\n"); } +TEST_F(FormatTestJS, CppKeywordsInJavaScript) { + // use the "!" assertion operator to validate that clang-format understands + // these C++ keywords aren't keywords in JS/TS. + verifyFormat("auto!;"); + verifyFormat("char!;"); + verifyFormat("concept!;"); + verifyFormat("double!;"); + verifyFormat("extern!;"); + verifyFormat("float!;"); + verifyFormat("inline!;"); + verifyFormat("int!;"); + verifyFormat("long!;"); + verifyFormat("register!;"); + verifyFormat("restrict!;"); + verifyFormat("sizeof!;"); + verifyFormat("static!;"); + verifyFormat("struct!;"); + verifyFormat("typedef!;"); + verifyFormat("union!;"); + verifyFormat("unsigned!;"); + verifyFormat("volatile!;"); + verifyFormat("_Alignas!;"); + verifyFormat("_Alignof!;"); + verifyFormat("_Atomic!;"); + verifyFormat("_Bool!;"); + verifyFormat("_Complex!;"); + verifyFormat("_Generic!;"); + verifyFormat("_Imaginary!;"); + verifyFormat("_Noreturn!;"); + verifyFormat("_Static_assert!;"); + verifyFormat("_Thread_local!;"); + verifyFormat("__func__!;"); + verifyFormat("__objc_yes!;"); + verifyFormat("__objc_no!;"); + verifyFormat("asm!;"); + verifyFormat("bool!;"); + verifyFormat("const_cast!;"); + verifyFormat("dynamic_cast!;"); + verifyFormat("explicit!;"); + verifyFormat("friend!;"); + verifyFormat("mutable!;"); + verifyFormat("operator!;"); + verifyFormat("reinterpret_cast!;"); + verifyFormat("static_cast!;"); + verifyFormat("template!;"); + verifyFormat("typename!;"); + verifyFormat("typeid!;"); + verifyFormat("using!;"); + verifyFormat("virtual!;"); + verifyFormat("wchar_t!;"); +} + TEST_F(FormatTestJS, NullPropagatingOperator) { verifyFormat("let x = foo?.bar?.baz();\n"); verifyFormat("let x = foo?.(foo);\n");