mprobst created this revision.
mprobst added a reviewer: krasimir.
Herald added subscribers: cfe-commits, jfb.
Herald added a project: clang.

C++ defines a number of keywords that are regular identifiers in
JavaScript, e.g. `concept`:

  const concept = 1; // legit JS

This change expands the existing `IsJavaScriptIdentifier(Tok)` function
to return false for C++ keywords that aren't keywords in JS.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D77311

Files:
  clang/lib/Format/FormatToken.h
  clang/lib/Format/TokenAnnotator.cpp
  clang/unittests/Format/FormatTestJS.cpp

Index: clang/unittests/Format/FormatTestJS.cpp
===================================================================
--- clang/unittests/Format/FormatTestJS.cpp
+++ 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");
Index: clang/lib/Format/TokenAnnotator.cpp
===================================================================
--- clang/lib/Format/TokenAnnotator.cpp
+++ 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))
Index: clang/lib/Format/FormatToken.h
===================================================================
--- clang/lib/Format/FormatToken.h
+++ 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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to