sammccall updated this revision to Diff 319592.
sammccall retitled this revision from "[clangd] WIP playing with semantic 
highlighting modifiers" to "[clangd] Implement semanticTokens modifiers".
sammccall edited the summary of this revision.
sammccall added a comment.

Update phab with commit message


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D77811/new/

https://reviews.llvm.org/D77811

Files:
  clang-tools-extra/clangd/ClangdLSPServer.cpp
  clang-tools-extra/clangd/SemanticHighlighting.cpp
  clang-tools-extra/clangd/SemanticHighlighting.h
  clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
  clang-tools-extra/clangd/test/initialize-params.test
  clang-tools-extra/clangd/test/semantic-tokens.test
  clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
  clang-tools-extra/clangd/unittests/tweaks/AnnotateHighlightingsTests.cpp
  llvm/lib/Testing/Support/Annotations.cpp

Index: llvm/lib/Testing/Support/Annotations.cpp
===================================================================
--- llvm/lib/Testing/Support/Annotations.cpp
+++ llvm/lib/Testing/Support/Annotations.cpp
@@ -53,7 +53,8 @@
       continue;
     }
     if (Text.consume_front("$")) {
-      Name = Text.take_while(llvm::isAlnum);
+      Name =
+          Text.take_while([](char C) { return llvm::isAlnum(C) || C == '_'; });
       Text = Text.drop_front(Name->size());
       continue;
     }
Index: clang-tools-extra/clangd/unittests/tweaks/AnnotateHighlightingsTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/tweaks/AnnotateHighlightingsTests.cpp
+++ clang-tools-extra/clangd/unittests/tweaks/AnnotateHighlightingsTests.cpp
@@ -18,15 +18,17 @@
 TEST_F(AnnotateHighlightingsTest, Test) {
   EXPECT_AVAILABLE("^vo^id^ ^f(^) {^}^"); // available everywhere.
   EXPECT_AVAILABLE("[[int a; int b;]]");
-  EXPECT_EQ("void /* entity.name.function.cpp */f() {}", apply("void ^f() {}"));
+  EXPECT_EQ("void /* Function [decl] */f() {}", apply("void ^f() {}"));
 
-  EXPECT_EQ(apply("[[void f1(); void f2();]]"),
-            "void /* entity.name.function.cpp */f1(); "
-            "void /* entity.name.function.cpp */f2();");
+  EXPECT_EQ(
+      apply("[[int f1(); const int x = f1();]]"),
+      "int /* Function [decl] */f1(); "
+      "const int /* Variable [decl] [readonly] */x = /* Function */f1();");
 
+  // Only the targeted range is annotated.
   EXPECT_EQ(apply("void f1(); void f2() {^}"),
             "void f1(); "
-            "void /* entity.name.function.cpp */f2() {}");
+            "void /* Function [decl] */f2() {}");
 }
 
 } // namespace
Index: clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
+++ clang-tools-extra/clangd/unittests/SemanticHighlightingTests.cpp
@@ -18,6 +18,7 @@
 #include "llvm/ADT/StringRef.h"
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ScopedPrinter.h"
+#include "llvm/Support/raw_ostream.h"
 #include "gmock/gmock.h"
 #include <algorithm>
 
@@ -86,7 +87,8 @@
         return L.R.start < R.R.start;
       }));
 
-  std::string Result;
+  std::string Buf;
+  llvm::raw_string_ostream OS(Buf);
   unsigned NextChar = 0;
   for (auto &T : Tokens) {
     unsigned StartOffset = llvm::cantFail(positionToOffset(Input, T.R.start));
@@ -94,14 +96,18 @@
     assert(StartOffset <= EndOffset);
     assert(NextChar <= StartOffset);
 
-    Result += Input.substr(NextChar, StartOffset - NextChar);
-    Result += std::string(
-        llvm::formatv("${0}[[{1}]]", T.Kind,
-                      Input.substr(StartOffset, EndOffset - StartOffset)));
+    OS << Input.substr(NextChar, StartOffset - NextChar);
+    OS << '$' << T.Kind;
+    for (unsigned I = 0;
+         I < static_cast<uint32_t>(HighlightingModifier::LastModifier); ++I) {
+      if (T.Modifiers & (1 << I))
+        OS << '_' << static_cast<HighlightingModifier>(I);
+    }
+    OS << "[[" << Input.substr(StartOffset, EndOffset - StartOffset) << "]]";
     NextChar = EndOffset;
   }
-  Result += Input.substr(NextChar);
-  return Result;
+  OS << Input.substr(NextChar);
+  return std::move(OS.str());
 }
 
 void checkHighlightings(llvm::StringRef Code,
@@ -160,337 +166,340 @@
 TEST(SemanticHighlighting, GetsCorrectTokens) {
   const char *TestCases[] = {
       R"cpp(
-      struct $Class[[AS]] {
-        double $Field[[SomeMember]];
+      struct $Class_decl[[AS]] {
+        double $Field_decl[[SomeMember]];
       };
       struct {
-      } $Variable[[S]];
-      void $Function[[foo]](int $Parameter[[A]], $Class[[AS]] $Parameter[[As]]) {
-        $Primitive[[auto]] $LocalVariable[[VeryLongVariableName]] = 12312;
-        $Class[[AS]]     $LocalVariable[[AA]];
-        $Primitive[[auto]] $LocalVariable[[L]] = $LocalVariable[[AA]].$Field[[SomeMember]] + $Parameter[[A]];
-        auto $LocalVariable[[FN]] = [ $LocalVariable[[AA]]](int $Parameter[[A]]) -> void {};
+      } $Variable_decl[[S]];
+      void $Function_decl[[foo]](int $Parameter_decl[[A]], $Class[[AS]] $Parameter_decl[[As]]) {
+        $Primitive_deduced[[auto]] $LocalVariable_decl[[VeryLongVariableName]] = 12312;
+        $Class[[AS]]     $LocalVariable_decl[[AA]];
+        $Primitive_deduced[[auto]] $LocalVariable_decl[[L]] = $LocalVariable[[AA]].$Field[[SomeMember]] + $Parameter[[A]];
+        auto $LocalVariable_decl[[FN]] = [ $LocalVariable[[AA]]](int $Parameter_decl[[A]]) -> void {};
         $LocalVariable[[FN]](12312);
       }
     )cpp",
       R"cpp(
-      void $Function[[foo]](int);
-      void $Function[[Gah]]();
-      void $Function[[foo]]() {
-        auto $LocalVariable[[Bou]] = $Function[[Gah]];
+      void $Function_decl[[foo]](int);
+      void $Function_decl[[Gah]]();
+      void $Function_decl[[foo]]() {
+        auto $LocalVariable_decl[[Bou]] = $Function[[Gah]];
       }
-      struct $Class[[A]] {
-        void $Method[[abc]]();
+      struct $Class_decl[[A]] {
+        void $Method_decl[[abc]]();
       };
     )cpp",
       R"cpp(
-      namespace $Namespace[[abc]] {
-        template<typename $TemplateParameter[[T]]>
-        struct $Class[[A]] {
-          $TemplateParameter[[T]] $Field[[t]];
+      namespace $Namespace_decl[[abc]] {
+        template<typename $TemplateParameter_decl[[T]]>
+        struct $Class_decl[[A]] {
+          $TemplateParameter[[T]] $Field_decl[[t]];
         };
       }
-      template<typename $TemplateParameter[[T]]>
-      struct $Class[[C]] : $Namespace[[abc]]::$Class[[A]]<$TemplateParameter[[T]]> {
-        typename $TemplateParameter[[T]]::$DependentType[[A]]* $Field[[D]];
-      };
-      $Namespace[[abc]]::$Class[[A]]<int> $Variable[[AA]];
-      typedef $Namespace[[abc]]::$Class[[A]]<int> $Class[[AAA]];
-      struct $Class[[B]] {
-        $Class[[B]]();
-        ~$Class[[B]]();
+      template<typename $TemplateParameter_decl[[T]]>
+      struct $Class_decl[[C]] : $Namespace[[abc]]::$Class[[A]]<$TemplateParameter[[T]]> {
+        typename $TemplateParameter[[T]]::$DependentType[[A]]* $Field_decl[[D]];
+      };
+      $Namespace[[abc]]::$Class[[A]]<int> $Variable_decl[[AA]];
+      typedef $Namespace[[abc]]::$Class[[A]]<int> $Class_decl[[AAA]];
+      struct $Class_decl[[B]] {
+        $Class_decl[[B]]();
+        ~$Class[[B]](); // FIXME: inconsistent with constructor
         void operator<<($Class[[B]]);
-        $Class[[AAA]] $Field[[AA]];
+        $Class[[AAA]] $Field_decl[[AA]];
       };
-      $Class[[B]]::$Class[[B]]() {}
-      $Class[[B]]::~$Class[[B]]() {}
-      void $Function[[f]] () {
-        $Class[[B]] $LocalVariable[[BB]] = $Class[[B]]();
+      $Class[[B]]::$Class_decl[[B]]() {}
+      $Class[[B]]::~$Class[[B]]() {} // FIXME: inconsistent with constructor
+      void $Function_decl[[f]] () {
+        $Class[[B]] $LocalVariable_decl[[BB]] = $Class[[B]]();
         $LocalVariable[[BB]].~$Class[[B]]();
         $Class[[B]]();
       }
     )cpp",
       R"cpp(
-      enum class $Enum[[E]] {
-        $EnumConstant[[A]],
-        $EnumConstant[[B]],
+      enum class $Enum_decl[[E]] {
+        $EnumConstant_decl_readonly[[A]],
+        $EnumConstant_decl_readonly[[B]],
       };
-      enum $Enum[[EE]] {
-        $EnumConstant[[Hi]],
+      enum $Enum_decl[[EE]] {
+        $EnumConstant_decl_readonly[[Hi]],
       };
-      struct $Class[[A]] {
-        $Enum[[E]] $Field[[EEE]];
-        $Enum[[EE]] $Field[[EEEE]];
+      struct $Class_decl[[A]] {
+        $Enum[[E]] $Field_decl[[EEE]];
+        $Enum[[EE]] $Field_decl[[EEEE]];
       };
-      int $Variable[[I]] = $EnumConstant[[Hi]];
-      $Enum[[E]] $Variable[[L]] = $Enum[[E]]::$EnumConstant[[B]];
+      int $Variable_decl[[I]] = $EnumConstant_readonly[[Hi]];
+      $Enum[[E]] $Variable_decl[[L]] = $Enum[[E]]::$EnumConstant_readonly[[B]];
     )cpp",
       R"cpp(
-      namespace $Namespace[[abc]] {
+      namespace $Namespace_decl[[abc]] {
         namespace {}
-        namespace $Namespace[[bcd]] {
-          struct $Class[[A]] {};
-          namespace $Namespace[[cde]] {
-            struct $Class[[A]] {
-              enum class $Enum[[B]] {
-                $EnumConstant[[Hi]],
+        namespace $Namespace_decl[[bcd]] {
+          struct $Class_decl[[A]] {};
+          namespace $Namespace_decl[[cde]] {
+            struct $Class_decl[[A]] {
+              enum class $Enum_decl[[B]] {
+                $EnumConstant_decl_readonly[[Hi]],
               };
             };
           }
         }
       }
       using namespace $Namespace[[abc]]::$Namespace[[bcd]];
-      namespace $Namespace[[vwz]] =
+      namespace $Namespace_decl[[vwz]] =
             $Namespace[[abc]]::$Namespace[[bcd]]::$Namespace[[cde]];
-      $Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable[[AA]];
-      $Namespace[[vwz]]::$Class[[A]]::$Enum[[B]] $Variable[[AAA]] =
-            $Namespace[[vwz]]::$Class[[A]]::$Enum[[B]]::$EnumConstant[[Hi]];
-      ::$Namespace[[vwz]]::$Class[[A]] $Variable[[B]];
-      ::$Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable[[BB]];
-    )cpp",
-      R"cpp(
-      struct $Class[[D]] {
-        double $Field[[C]];
-      };
-      struct $Class[[A]] {
-        double $Field[[B]];
-        $Class[[D]] $Field[[E]];
-        static double $StaticField[[S]];
-        static void $StaticMethod[[bar]]() {}
-        void $Method[[foo]]() {
+      $Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable_decl[[AA]];
+      $Namespace[[vwz]]::$Class[[A]]::$Enum[[B]] $Variable_decl[[AAA]] =
+            $Namespace[[vwz]]::$Class[[A]]::$Enum[[B]]::$EnumConstant_readonly[[Hi]];
+      ::$Namespace[[vwz]]::$Class[[A]] $Variable_decl[[B]];
+      ::$Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable_decl[[BB]];
+    )cpp",
+      R"cpp(
+      struct $Class_decl[[D]] {
+        double $Field_decl[[C]];
+      };
+      struct $Class_decl[[A]] {
+        double $Field_decl[[B]];
+        $Class[[D]] $Field_decl[[E]];
+        static double $StaticField_decl_static[[S]];
+        static void $StaticMethod_decl_static[[bar]]() {}
+        void $Method_decl[[foo]]() {
           $Field[[B]] = 123;
           this->$Field[[B]] = 156;
           this->$Method[[foo]]();
           $Method[[foo]]();
-          $StaticMethod[[bar]]();
-          $StaticField[[S]] = 90.1;
+          $StaticMethod_static[[bar]]();
+          $StaticField_static[[S]] = 90.1;
         }
       };
-      void $Function[[foo]]() {
-        $Class[[A]] $LocalVariable[[AA]];
+      void $Function_decl[[foo]]() {
+        $Class[[A]] $LocalVariable_decl[[AA]];
         $LocalVariable[[AA]].$Field[[B]] += 2;
         $LocalVariable[[AA]].$Method[[foo]]();
         $LocalVariable[[AA]].$Field[[E]].$Field[[C]];
-        $Class[[A]]::$StaticField[[S]] = 90;
+        $Class[[A]]::$StaticField_static[[S]] = 90;
       }
     )cpp",
       R"cpp(
-      struct $Class[[AA]] {
-        int $Field[[A]];
+      struct $Class_decl[[AA]] {
+        int $Field_decl[[A]];
       };
-      int $Variable[[B]];
-      $Class[[AA]] $Variable[[A]]{$Variable[[B]]};
+      int $Variable_decl[[B]];
+      $Class[[AA]] $Variable_decl[[A]]{$Variable[[B]]};
     )cpp",
       R"cpp(
-      namespace $Namespace[[a]] {
-        struct $Class[[A]] {};
-        typedef char $Primitive[[C]];
+      namespace $Namespace_decl[[a]] {
+        struct $Class_decl[[A]] {};
+        typedef char $Primitive_decl[[C]];
       }
-      typedef $Namespace[[a]]::$Class[[A]] $Class[[B]];
-      using $Class[[BB]] = $Namespace[[a]]::$Class[[A]];
-      enum class $Enum[[E]] {};
-      typedef $Enum[[E]] $Enum[[C]];
-      typedef $Enum[[C]] $Enum[[CC]];
-      using $Enum[[CD]] = $Enum[[CC]];
-      $Enum[[CC]] $Function[[f]]($Class[[B]]);
-      $Enum[[CD]] $Function[[f]]($Class[[BB]]);
-      typedef $Namespace[[a]]::$Primitive[[C]] $Primitive[[PC]];
-      typedef float $Primitive[[F]];
-    )cpp",
-      R"cpp(
-      template<typename $TemplateParameter[[T]], typename = void>
-      class $Class[[A]] {
-        $TemplateParameter[[T]] $Field[[AA]];
-        $TemplateParameter[[T]] $Method[[foo]]();
-      };
-      template<class $TemplateParameter[[TT]]>
-      class $Class[[B]] {
-        $Class[[A]]<$TemplateParameter[[TT]]> $Field[[AA]];
-      };
-      template<class $TemplateParameter[[TT]], class $TemplateParameter[[GG]]>
-      class $Class[[BB]] {};
-      template<class $TemplateParameter[[T]]>
-      class $Class[[BB]]<$TemplateParameter[[T]], int> {};
-      template<class $TemplateParameter[[T]]>
-      class $Class[[BB]]<$TemplateParameter[[T]], $TemplateParameter[[T]]*> {};
-
-      template<template<class> class $TemplateParameter[[T]], class $TemplateParameter[[C]]>
-      $TemplateParameter[[T]]<$TemplateParameter[[C]]> $Function[[f]]();
+      typedef $Namespace[[a]]::$Class[[A]] $Class_decl[[B]];
+      using $Class_decl[[BB]] = $Namespace[[a]]::$Class[[A]];
+      enum class $Enum_decl[[E]] {};
+      typedef $Enum[[E]] $Enum_decl[[C]];
+      typedef $Enum[[C]] $Enum_decl[[CC]];
+      using $Enum_decl[[CD]] = $Enum[[CC]];
+      $Enum[[CC]] $Function_decl[[f]]($Class[[B]]);
+      $Enum[[CD]] $Function_decl[[f]]($Class[[BB]]);
+      typedef $Namespace[[a]]::$Primitive[[C]] $Primitive_decl[[PC]];
+      typedef float $Primitive_decl[[F]];
+    )cpp",
+      R"cpp(
+      template<typename $TemplateParameter_decl[[T]], typename = void>
+      class $Class_decl[[A]] {
+        $TemplateParameter[[T]] $Field_decl[[AA]];
+        $TemplateParameter[[T]] $Method_decl[[foo]]();
+      };
+      template<class $TemplateParameter_decl[[TT]]>
+      class $Class_decl[[B]] {
+        $Class[[A]]<$TemplateParameter[[TT]]> $Field_decl[[AA]];
+      };
+      template<class $TemplateParameter_decl[[TT]], class $TemplateParameter_decl[[GG]]>
+      class $Class_decl[[BB]] {};
+      template<class $TemplateParameter_decl[[T]]>
+      class $Class_decl[[BB]]<$TemplateParameter[[T]], int> {};
+      template<class $TemplateParameter_decl[[T]]>
+      class $Class_decl[[BB]]<$TemplateParameter[[T]], $TemplateParameter[[T]]*> {};
+
+      template<template<class> class $TemplateParameter_decl[[T]], class $TemplateParameter_decl[[C]]>
+      $TemplateParameter[[T]]<$TemplateParameter[[C]]> $Function_decl[[f]]();
 
       template<typename>
-      class $Class[[Foo]] {};
+      class $Class_decl[[Foo]] {};
 
-      template<typename $TemplateParameter[[T]]>
-      void $Function[[foo]]($TemplateParameter[[T]] ...);
+      template<typename $TemplateParameter_decl[[T]]>
+      void $Function_decl[[foo]]($TemplateParameter[[T]] ...);
     )cpp",
       R"cpp(
-      template <class $TemplateParameter[[T]]>
-      struct $Class[[Tmpl]] {$TemplateParameter[[T]] $Field[[x]] = 0;};
-      extern template struct $Class[[Tmpl]]<float>;
-      template struct $Class[[Tmpl]]<double>;
+      template <class $TemplateParameter_decl[[T]]>
+      struct $Class_decl[[Tmpl]] {$TemplateParameter[[T]] $Field_decl[[x]] = 0;};
+      // FIXME: highlights dropped due to conflicts.
+      // explicitReferenceTargets reports ClassTemplateSpecializationDecl twice
+      // at its location, calling it a declaration/non-declaration once each.
+      extern template struct Tmpl<float>;
+      template struct Tmpl<double>;
     )cpp",
       // This test is to guard against highlightings disappearing when using
       // conversion operators as their behaviour in the clang AST differ from
       // other CXXMethodDecls.
       R"cpp(
-      class $Class[[Foo]] {};
-      struct $Class[[Bar]] {
+      class $Class_decl[[Foo]] {};
+      struct $Class_decl[[Bar]] {
         explicit operator $Class[[Foo]]*() const;
         explicit operator int() const;
         operator $Class[[Foo]]();
       };
-      void $Function[[f]]() {
-        $Class[[Bar]] $LocalVariable[[B]];
-        $Class[[Foo]] $LocalVariable[[F]] = $LocalVariable[[B]];
-        $Class[[Foo]] *$LocalVariable[[FP]] = ($Class[[Foo]]*)$LocalVariable[[B]];
-        int $LocalVariable[[I]] = (int)$LocalVariable[[B]];
+      void $Function_decl[[f]]() {
+        $Class[[Bar]] $LocalVariable_decl[[B]];
+        $Class[[Foo]] $LocalVariable_decl[[F]] = $LocalVariable[[B]];
+        $Class[[Foo]] *$LocalVariable_decl[[FP]] = ($Class[[Foo]]*)$LocalVariable[[B]];
+        int $LocalVariable_decl[[I]] = (int)$LocalVariable[[B]];
       }
     )cpp",
       R"cpp(
-      struct $Class[[B]] {};
-      struct $Class[[A]] {
-        $Class[[B]] $Field[[BB]];
-        $Class[[A]] &operator=($Class[[A]] &&$Parameter[[O]]);
+      struct $Class_decl[[B]] {};
+      struct $Class_decl[[A]] {
+        $Class[[B]] $Field_decl[[BB]];
+        $Class[[A]] &operator=($Class[[A]] &&$Parameter_decl[[O]]);
       };
 
-      $Class[[A]] &$Class[[A]]::operator=($Class[[A]] &&$Parameter[[O]]) = default;
+      $Class[[A]] &$Class[[A]]::operator=($Class[[A]] &&$Parameter_decl[[O]]) = default;
     )cpp",
       R"cpp(
-      enum $Enum[[En]] {
-        $EnumConstant[[EC]],
+      enum $Enum_decl[[En]] {
+        $EnumConstant_decl_readonly[[EC]],
       };
-      class $Class[[Foo]] {};
-      class $Class[[Bar]] {
+      class $Class_decl[[Foo]] {};
+      class $Class_decl[[Bar]] {
       public:
-        $Class[[Foo]] $Field[[Fo]];
-        $Enum[[En]] $Field[[E]];
-        int $Field[[I]];
-        $Class[[Bar]] ($Class[[Foo]] $Parameter[[F]],
-                $Enum[[En]] $Parameter[[E]])
+        $Class[[Foo]] $Field_decl[[Fo]];
+        $Enum[[En]] $Field_decl[[E]];
+        int $Field_decl[[I]];
+        $Class_decl[[Bar]] ($Class[[Foo]] $Parameter_decl[[F]],
+                $Enum[[En]] $Parameter_decl[[E]])
         : $Field[[Fo]] ($Parameter[[F]]), $Field[[E]] ($Parameter[[E]]),
           $Field[[I]] (123) {}
       };
-      class $Class[[Bar2]] : public $Class[[Bar]] {
-        $Class[[Bar2]]() : $Class[[Bar]]($Class[[Foo]](), $EnumConstant[[EC]]) {}
+      class $Class_decl[[Bar2]] : public $Class[[Bar]] {
+        $Class_decl[[Bar2]]() : $Class[[Bar]]($Class[[Foo]](), $EnumConstant_readonly[[EC]]) {}
       };
     )cpp",
       R"cpp(
-      enum $Enum[[E]] {
-        $EnumConstant[[E]],
+      enum $Enum_decl[[E]] {
+        $EnumConstant_decl_readonly[[E]],
       };
-      class $Class[[Foo]] {};
-      $Enum[[auto]] $Variable[[AE]] = $Enum[[E]]::$EnumConstant[[E]];
-      $Class[[auto]] $Variable[[AF]] = $Class[[Foo]]();
-      $Class[[decltype]](auto) $Variable[[AF2]] = $Class[[Foo]]();
-      $Class[[auto]] *$Variable[[AFP]] = &$Variable[[AF]];
-      $Enum[[auto]] &$Variable[[AER]] = $Variable[[AE]];
-      $Primitive[[auto]] $Variable[[Form]] = 10.2 + 2 * 4;
-      $Primitive[[decltype]]($Variable[[Form]]) $Variable[[F]] = 10;
-      auto $Variable[[Fun]] = []()->void{};
+      class $Class_decl[[Foo]] {};
+      $Enum_deduced[[auto]] $Variable_decl[[AE]] = $Enum[[E]]::$EnumConstant_readonly[[E]];
+      $Class_deduced[[auto]] $Variable_decl[[AF]] = $Class[[Foo]]();
+      $Class_deduced[[decltype]](auto) $Variable_decl[[AF2]] = $Class[[Foo]]();
+      $Class_deduced[[auto]] *$Variable_decl[[AFP]] = &$Variable[[AF]];
+      $Enum_deduced[[auto]] &$Variable_decl[[AER]] = $Variable[[AE]];
+      $Primitive_deduced[[auto]] $Variable_decl[[Form]] = 10.2 + 2 * 4;
+      $Primitive_deduced[[decltype]]($Variable[[Form]]) $Variable_decl[[F]] = 10;
+      auto $Variable_decl[[Fun]] = []()->void{};
     )cpp",
       R"cpp(
-      class $Class[[G]] {};
-      template<$Class[[G]] *$TemplateParameter[[U]]>
-      class $Class[[GP]] {};
-      template<$Class[[G]] &$TemplateParameter[[U]]>
-      class $Class[[GR]] {};
-      template<int *$TemplateParameter[[U]]>
-      class $Class[[IP]] {
-        void $Method[[f]]() {
-          *$TemplateParameter[[U]] += 5;
+      class $Class_decl[[G]] {};
+      template<$Class[[G]] *$TemplateParameter_decl_readonly[[U]]>
+      class $Class_decl[[GP]] {};
+      template<$Class[[G]] &$TemplateParameter_decl_readonly[[U]]>
+      class $Class_decl[[GR]] {};
+      template<int *$TemplateParameter_decl_readonly[[U]]>
+      class $Class_decl[[IP]] {
+        void $Method_decl[[f]]() {
+          *$TemplateParameter_readonly[[U]] += 5;
         }
       };
-      template<unsigned $TemplateParameter[[U]] = 2>
-      class $Class[[Foo]] {
-        void $Method[[f]]() {
-          for(int $LocalVariable[[I]] = 0;
-            $LocalVariable[[I]] < $TemplateParameter[[U]];) {}
+      template<unsigned $TemplateParameter_decl_readonly[[U]] = 2>
+      class $Class_decl[[Foo]] {
+        void $Method_decl[[f]]() {
+          for(int $LocalVariable_decl[[I]] = 0;
+            $LocalVariable[[I]] < $TemplateParameter_readonly[[U]];) {}
         }
       };
 
-      $Class[[G]] $Variable[[L]];
-      void $Function[[f]]() {
-        $Class[[Foo]]<123> $LocalVariable[[F]];
-        $Class[[GP]]<&$Variable[[L]]> $LocalVariable[[LL]];
-        $Class[[GR]]<$Variable[[L]]> $LocalVariable[[LLL]];
+      $Class[[G]] $Variable_decl[[L]];
+      void $Function_decl[[f]]() {
+        $Class[[Foo]]<123> $LocalVariable_decl[[F]];
+        $Class[[GP]]<&$Variable[[L]]> $LocalVariable_decl[[LL]];
+        $Class[[GR]]<$Variable[[L]]> $LocalVariable_decl[[LLL]];
       }
     )cpp",
       R"cpp(
-      template<typename $TemplateParameter[[T]],
-        void ($TemplateParameter[[T]]::*$TemplateParameter[[method]])(int)>
-      struct $Class[[G]] {
-        void $Method[[foo]](
-            $TemplateParameter[[T]] *$Parameter[[O]]) {
-          ($Parameter[[O]]->*$TemplateParameter[[method]])(10);
+      template<typename $TemplateParameter_decl[[T]],
+        void ($TemplateParameter[[T]]::*$TemplateParameter_decl_readonly[[method]])(int)>
+      struct $Class_decl[[G]] {
+        void $Method_decl[[foo]](
+            $TemplateParameter[[T]] *$Parameter_decl[[O]]) {
+          ($Parameter[[O]]->*$TemplateParameter_readonly[[method]])(10);
         }
       };
-      struct $Class[[F]] {
-        void $Method[[f]](int);
+      struct $Class_decl[[F]] {
+        void $Method_decl[[f]](int);
       };
-      template<void (*$TemplateParameter[[Func]])()>
-      struct $Class[[A]] {
-        void $Method[[f]]() {
-          (*$TemplateParameter[[Func]])();
+      template<void (*$TemplateParameter_decl_readonly[[Func]])()>
+      struct $Class_decl[[A]] {
+        void $Method_decl[[f]]() {
+          (*$TemplateParameter_readonly[[Func]])();
         }
       };
 
-      void $Function[[foo]]() {
-        $Class[[F]] $LocalVariable[[FF]];
-        $Class[[G]]<$Class[[F]], &$Class[[F]]::$Method[[f]]> $LocalVariable[[GG]];
+      void $Function_decl[[foo]]() {
+        $Class[[F]] $LocalVariable_decl[[FF]];
+        $Class[[G]]<$Class[[F]], &$Class[[F]]::$Method[[f]]> $LocalVariable_decl[[GG]];
         $LocalVariable[[GG]].$Method[[foo]](&$LocalVariable[[FF]]);
-        $Class[[A]]<$Function[[foo]]> $LocalVariable[[AA]];
+        $Class[[A]]<$Function[[foo]]> $LocalVariable_decl[[AA]];
       }
     )cpp",
       // Tokens that share a source range but have conflicting Kinds are not
       // highlighted.
       R"cpp(
-      #define $Macro[[DEF_MULTIPLE]](X) namespace X { class X { int X; }; }
-      #define $Macro[[DEF_CLASS]](T) class T {};
+      #define $Macro_decl[[DEF_MULTIPLE]](X) namespace X { class X { int X; }; }
+      #define $Macro_decl[[DEF_CLASS]](T) class T {};
       // Preamble ends.
       $Macro[[DEF_MULTIPLE]](XYZ);
       $Macro[[DEF_MULTIPLE]](XYZW);
-      $Macro[[DEF_CLASS]]($Class[[A]])
-      #define $Macro[[MACRO_CONCAT]](X, V, T) T foo##X = V
-      #define $Macro[[DEF_VAR]](X, V) int X = V
-      #define $Macro[[DEF_VAR_T]](T, X, V) T X = V
-      #define $Macro[[DEF_VAR_REV]](V, X) DEF_VAR(X, V)
-      #define $Macro[[CPY]](X) X
-      #define $Macro[[DEF_VAR_TYPE]](X, Y) X Y
-      #define $Macro[[SOME_NAME]] variable
-      #define $Macro[[SOME_NAME_SET]] variable2 = 123
-      #define $Macro[[INC_VAR]](X) X += 2
-      void $Function[[foo]]() {
-        $Macro[[DEF_VAR]]($LocalVariable[[X]],  123);
-        $Macro[[DEF_VAR_REV]](908, $LocalVariable[[XY]]);
-        int $Macro[[CPY]]( $LocalVariable[[XX]] );
-        $Macro[[DEF_VAR_TYPE]]($Class[[A]], $LocalVariable[[AA]]);
+      $Macro[[DEF_CLASS]]($Class_decl[[A]])
+      #define $Macro_decl[[MACRO_CONCAT]](X, V, T) T foo##X = V
+      #define $Macro_decl[[DEF_VAR]](X, V) int X = V
+      #define $Macro_decl[[DEF_VAR_T]](T, X, V) T X = V
+      #define $Macro_decl[[DEF_VAR_REV]](V, X) DEF_VAR(X, V)
+      #define $Macro_decl[[CPY]](X) X
+      #define $Macro_decl[[DEF_VAR_TYPE]](X, Y) X Y
+      #define $Macro_decl[[SOME_NAME]] variable
+      #define $Macro_decl[[SOME_NAME_SET]] variable2 = 123
+      #define $Macro_decl[[INC_VAR]](X) X += 2
+      void $Function_decl[[foo]]() {
+        $Macro[[DEF_VAR]]($LocalVariable_decl[[X]],  123);
+        $Macro[[DEF_VAR_REV]](908, $LocalVariable_decl[[XY]]);
+        int $Macro[[CPY]]( $LocalVariable_decl[[XX]] );
+        $Macro[[DEF_VAR_TYPE]]($Class[[A]], $LocalVariable_decl[[AA]]);
         double $Macro[[SOME_NAME]];
         int $Macro[[SOME_NAME_SET]];
         $LocalVariable[[variable]] = 20.1;
         $Macro[[MACRO_CONCAT]](var, 2, float);
         $Macro[[DEF_VAR_T]]($Class[[A]], $Macro[[CPY]](
-              $Macro[[CPY]]($LocalVariable[[Nested]])),
+              $Macro[[CPY]]($LocalVariable_decl[[Nested]])),
             $Macro[[CPY]]($Class[[A]]()));
         $Macro[[INC_VAR]]($LocalVariable[[variable]]);
       }
       void $Macro[[SOME_NAME]]();
-      $Macro[[DEF_VAR]]($Variable[[MMMMM]], 567);
-      $Macro[[DEF_VAR_REV]](756, $Variable[[AB]]);
+      $Macro[[DEF_VAR]]($Variable_decl[[MMMMM]], 567);
+      $Macro[[DEF_VAR_REV]](756, $Variable_decl[[AB]]);
 
-      #define $Macro[[CALL_FN]](F) F();
-      #define $Macro[[DEF_FN]](F) void F ()
-      $Macro[[DEF_FN]]($Function[[g]]) {
+      #define $Macro_decl[[CALL_FN]](F) F();
+      #define $Macro_decl[[DEF_FN]](F) void F ()
+      $Macro[[DEF_FN]]($Function_decl[[g]]) {
         $Macro[[CALL_FN]]($Function[[foo]]);
       }
     )cpp",
       R"cpp(
-      #define $Macro[[fail]](expr) expr
-      #define $Macro[[assert]](COND) if (!(COND)) { fail("assertion failed" #COND); }
+      #define $Macro_decl[[fail]](expr) expr
+      #define $Macro_decl[[assert]](COND) if (!(COND)) { fail("assertion failed" #COND); }
       // Preamble ends.
-      int $Variable[[x]];
-      int $Variable[[y]];
-      int $Function[[f]]();
-      void $Function[[foo]]() {
+      int $Variable_decl[[x]];
+      int $Variable_decl[[y]];
+      int $Function_decl[[f]]();
+      void $Function_decl[[foo]]() {
         $Macro[[assert]]($Variable[[x]] != $Variable[[y]]);
         $Macro[[assert]]($Variable[[x]] != $Function[[f]]());
       }
@@ -498,10 +507,10 @@
       // highlighting all macro references
       R"cpp(
       #ifndef $Macro[[name]]
-      #define $Macro[[name]]
+      #define $Macro_decl[[name]]
       #endif
 
-      #define $Macro[[test]]
+      #define $Macro_decl[[test]]
       #undef $Macro[[test]]
 $InactiveCode[[#ifdef test]]
 $InactiveCode[[#endif]]
@@ -510,104 +519,104 @@
 $InactiveCode[[#endif]]
     )cpp",
       R"cpp(
-      struct $Class[[S]] {
-        float $Field[[Value]];
-        $Class[[S]] *$Field[[Next]];
+      struct $Class_decl[[S]] {
+        float $Field_decl[[Value]];
+        $Class[[S]] *$Field_decl[[Next]];
       };
-      $Class[[S]] $Variable[[Global]][2] = {$Class[[S]](), $Class[[S]]()};
-      auto [$Variable[[G1]], $Variable[[G2]]] = $Variable[[Global]];
-      void $Function[[f]]($Class[[S]] $Parameter[[P]]) {
-        int $LocalVariable[[A]][2] = {1,2};
-        auto [$LocalVariable[[B1]], $LocalVariable[[B2]]] = $LocalVariable[[A]];
-        auto [$LocalVariable[[G1]], $LocalVariable[[G2]]] = $Variable[[Global]];
-        $Class[[auto]] [$LocalVariable[[P1]], $LocalVariable[[P2]]] = $Parameter[[P]];
+      $Class[[S]] $Variable_decl[[Global]][2] = {$Class[[S]](), $Class[[S]]()};
+      auto [$Variable_decl[[G1]], $Variable_decl[[G2]]] = $Variable[[Global]];
+      void $Function_decl[[f]]($Class[[S]] $Parameter_decl[[P]]) {
+        int $LocalVariable_decl[[A]][2] = {1,2};
+        auto [$LocalVariable_decl[[B1]], $LocalVariable_decl[[B2]]] = $LocalVariable[[A]];
+        auto [$LocalVariable_decl[[G1]], $LocalVariable_decl[[G2]]] = $Variable[[Global]];
+        $Class_deduced[[auto]] [$LocalVariable_decl[[P1]], $LocalVariable_decl[[P2]]] = $Parameter[[P]];
         // Highlights references to BindingDecls.
         $LocalVariable[[B1]]++;
       }
     )cpp",
       R"cpp(
-      template<class $TemplateParameter[[T]]>
-      class $Class[[A]] {
-        using $TemplateParameter[[TemplateParam1]] = $TemplateParameter[[T]];
-        typedef $TemplateParameter[[T]] $TemplateParameter[[TemplateParam2]];
-        using $Primitive[[IntType]] = int;
+      template<class $TemplateParameter_decl[[T]]>
+      class $Class_decl[[A]] {
+        using $TemplateParameter_decl[[TemplateParam1]] = $TemplateParameter[[T]];
+        typedef $TemplateParameter[[T]] $TemplateParameter_decl[[TemplateParam2]];
+        using $Primitive_decl[[IntType]] = int;
 
-        using $Typedef[[Pointer]] = $TemplateParameter[[T]] *;
-        using $Typedef[[LVReference]] = $TemplateParameter[[T]] &;
-        using $Typedef[[RVReference]] = $TemplateParameter[[T]]&&;
-        using $Typedef[[Array]] = $TemplateParameter[[T]]*[3];
-        using $Typedef[[MemberPointer]] = int ($Class[[A]]::*)(int);
+        using $Typedef_decl[[Pointer]] = $TemplateParameter[[T]] *;
+        using $Typedef_decl[[LVReference]] = $TemplateParameter[[T]] &;
+        using $Typedef_decl[[RVReference]] = $TemplateParameter[[T]]&&;
+        using $Typedef_decl[[Array]] = $TemplateParameter[[T]]*[3];
+        using $Typedef_decl[[MemberPointer]] = int ($Class[[A]]::*)(int);
 
         // Use various previously defined typedefs in a function type.
-        void $Method[[func]](
+        void $Method_decl[[func]](
           $Typedef[[Pointer]], $Typedef[[LVReference]], $Typedef[[RVReference]],
           $Typedef[[Array]], $Typedef[[MemberPointer]]);
       };
     )cpp",
       R"cpp(
-      template <class $TemplateParameter[[T]]>
-      void $Function[[phase1]]($TemplateParameter[[T]]);
-      template <class $TemplateParameter[[T]]>
-      void $Function[[foo]]($TemplateParameter[[T]] $Parameter[[P]]) {
+      template <class $TemplateParameter_decl[[T]]>
+      void $Function_decl[[phase1]]($TemplateParameter[[T]]);
+      template <class $TemplateParameter_decl[[T]]>
+      void $Function_decl[[foo]]($TemplateParameter[[T]] $Parameter_decl[[P]]) {
         $Function[[phase1]]($Parameter[[P]]);
         $DependentName[[phase2]]($Parameter[[P]]);
       }
     )cpp",
       R"cpp(
-      class $Class[[A]] {
-        template <class $TemplateParameter[[T]]>
-        void $Method[[bar]]($TemplateParameter[[T]]);
+      class $Class_decl[[A]] {
+        template <class $TemplateParameter_decl[[T]]>
+        void $Method_decl[[bar]]($TemplateParameter[[T]]);
       };
 
-      template <class $TemplateParameter[[U]]>
-      void $Function[[foo]]($TemplateParameter[[U]] $Parameter[[P]]) {
+      template <class $TemplateParameter_decl[[U]]>
+      void $Function_decl[[foo]]($TemplateParameter[[U]] $Parameter_decl[[P]]) {
         $Class[[A]]().$Method[[bar]]($Parameter[[P]]);
       }
     )cpp",
       R"cpp(
-      struct $Class[[A]] {
-        template <class $TemplateParameter[[T]]>
-        static void $StaticMethod[[foo]]($TemplateParameter[[T]]);
+      struct $Class_decl[[A]] {
+        template <class $TemplateParameter_decl[[T]]>
+        static void $StaticMethod_decl_static[[foo]]($TemplateParameter[[T]]);
       };
 
-      template <class $TemplateParameter[[T]]>
-      struct $Class[[B]] {
-        void $Method[[bar]]() {
-          $Class[[A]]::$StaticMethod[[foo]]($TemplateParameter[[T]]());
+      template <class $TemplateParameter_decl[[T]]>
+      struct $Class_decl[[B]] {
+        void $Method_decl[[bar]]() {
+          $Class[[A]]::$StaticMethod_static[[foo]]($TemplateParameter[[T]]());
         }
       };
     )cpp",
       R"cpp(
-      template <class $TemplateParameter[[T]]>
-      void $Function[[foo]](typename $TemplateParameter[[T]]::$DependentType[[Type]]
+      template <class $TemplateParameter_decl[[T]]>
+      void $Function_decl[[foo]](typename $TemplateParameter[[T]]::$DependentType[[Type]]
                                             = $TemplateParameter[[T]]::$DependentName[[val]]);
     )cpp",
       R"cpp(
-      template <class $TemplateParameter[[T]]>
-      void $Function[[foo]]($TemplateParameter[[T]] $Parameter[[P]]) {
+      template <class $TemplateParameter_decl[[T]]>
+      void $Function_decl[[foo]]($TemplateParameter[[T]] $Parameter_decl[[P]]) {
         $Parameter[[P]].$DependentName[[Field]];
       }
     )cpp",
       R"cpp(
-      template <class $TemplateParameter[[T]]>
-      class $Class[[A]] {
-        int $Method[[foo]]() {
+      template <class $TemplateParameter_decl[[T]]>
+      class $Class_decl[[A]] {
+        int $Method_decl[[foo]]() {
           return $TemplateParameter[[T]]::$DependentName[[Field]];
         }
       };
     )cpp",
       // Highlighting the using decl as the underlying using shadow decl.
       R"cpp(
-      void $Function[[foo]]();
+      void $Function_decl[[foo]]();
       using ::$Function[[foo]];
     )cpp",
       // Highlighting of template template arguments.
       R"cpp(
-      template <template <class> class $TemplateParameter[[TT]],
-                template <class> class ...$TemplateParameter[[TTs]]>
-      struct $Class[[Foo]] {
+      template <template <class> class $TemplateParameter_decl[[TT]],
+                template <class> class ...$TemplateParameter_decl[[TTs]]>
+      struct $Class_decl[[Foo]] {
         $Class[[Foo]]<$TemplateParameter[[TT]], $TemplateParameter[[TTs]]...>
-          *$Field[[t]];
+          *$Field_decl[[t]];
       };
     )cpp",
       // Inactive code highlighting
@@ -618,86 +627,86 @@
 $InactiveCode[[#endif]]
 
       // A declaration to cause the preamble to end.
-      int $Variable[[EndPreamble]];
+      int $Variable_decl[[EndPreamble]];
 
       // Code after the preamble.
       // Code inside inactive blocks does not get regular highlightings
       // because it's not part of the AST.
-      #define $Macro[[test2]]
+      #define $Macro_decl[[test2]]
 $InactiveCode[[#if defined(test)]]
 $InactiveCode[[int Inactive2;]]
 $InactiveCode[[#elif defined(test2)]]
-      int $Variable[[Active1]];
+      int $Variable_decl[[Active1]];
 $InactiveCode[[#else]]
 $InactiveCode[[int Inactive3;]]
 $InactiveCode[[#endif]]
 
       #ifndef $Macro[[test]]
-      int $Variable[[Active2]];
+      int $Variable_decl[[Active2]];
       #endif
 
 $InactiveCode[[#ifdef test]]
 $InactiveCode[[int Inactive4;]]
 $InactiveCode[[#else]]
-      int $Variable[[Active3]];
+      int $Variable_decl[[Active3]];
       #endif
     )cpp",
       // Argument to 'sizeof...'
       R"cpp(
-      template <typename... $TemplateParameter[[Elements]]>
-      struct $Class[[TupleSize]] {
-        static const int $StaticField[[size]] =
+      template <typename... $TemplateParameter_decl[[Elements]]>
+      struct $Class_decl[[TupleSize]] {
+        static const int $StaticField_decl_readonly_static[[size]] =
 sizeof...($TemplateParameter[[Elements]]);
       };
     )cpp",
       // More dependent types
       R"cpp(
-      template <typename $TemplateParameter[[T]]>
-      struct $Class[[Waldo]] {
-        using $Typedef[[Location1]] = typename $TemplateParameter[[T]]
+      template <typename $TemplateParameter_decl[[T]]>
+      struct $Class_decl[[Waldo]] {
+        using $Typedef_decl[[Location1]] = typename $TemplateParameter[[T]]
             ::$DependentType[[Resolver]]::$DependentType[[Location]];
-        using $Typedef[[Location2]] = typename $TemplateParameter[[T]]
+        using $Typedef_decl[[Location2]] = typename $TemplateParameter[[T]]
             ::template $DependentType[[Resolver]]<$TemplateParameter[[T]]>
             ::$DependentType[[Location]];
-        using $Typedef[[Location3]] = typename $TemplateParameter[[T]]
+        using $Typedef_decl[[Location3]] = typename $TemplateParameter[[T]]
             ::$DependentType[[Resolver]]
             ::template $DependentType[[Location]]<$TemplateParameter[[T]]>;
-        static const int $StaticField[[Value]] = $TemplateParameter[[T]]
+        static const int $StaticField_decl_readonly_static[[Value]] = $TemplateParameter[[T]]
             ::$DependentType[[Resolver]]::$DependentName[[Value]];
       };
     )cpp",
       // Dependent name with heuristic target
       R"cpp(
       template <typename>
-      struct $Class[[Foo]] {
-        int $Field[[Waldo]];
-        void $Method[[bar]]() {
+      struct $Class_decl[[Foo]] {
+        int $Field_decl[[Waldo]];
+        void $Method_decl[[bar]]() {
           $Class[[Foo]]().$Field[[Waldo]];
         }
-        template <typename $TemplateParameter[[U]]>
-        void $Method[[bar1]]() {
+        template <typename $TemplateParameter_decl[[U]]>
+        void $Method_decl[[bar1]]() {
           $Class[[Foo]]<$TemplateParameter[[U]]>().$Field[[Waldo]];
         }
       };
     )cpp",
       // Concepts
       R"cpp(
-      template <typename $TemplateParameter[[T]]>
-      concept $Concept[[Fooable]] = 
-          requires($TemplateParameter[[T]] $Parameter[[F]]) {
+      template <typename $TemplateParameter_decl[[T]]>
+      concept $Concept_decl[[Fooable]] = 
+          requires($TemplateParameter[[T]] $Parameter_decl[[F]]) {
             $Parameter[[F]].$DependentName[[foo]]();
           };
-      template <typename $TemplateParameter[[T]]>
+      template <typename $TemplateParameter_decl[[T]]>
           requires $Concept[[Fooable]]<$TemplateParameter[[T]]>
-      void $Function[[bar]]($TemplateParameter[[T]] $Parameter[[F]]) {
+      void $Function_decl[[bar]]($TemplateParameter[[T]] $Parameter_decl[[F]]) {
         $Parameter[[F]].$DependentName[[foo]]();
       }
     )cpp",
       // Dependent template name
       R"cpp(
-      template <template <typename> class> struct $Class[[A]] {};
-      template <typename $TemplateParameter[[T]]>
-      using $Typedef[[W]] = $Class[[A]]<
+      template <template <typename> class> struct $Class_decl[[A]] {};
+      template <typename $TemplateParameter_decl[[T]]>
+      using $Typedef_decl[[W]] = $Class[[A]]<
         $TemplateParameter[[T]]::template $DependentType[[Waldo]]
       >;
     )cpp"};
@@ -706,7 +715,7 @@
   }
 
   checkHighlightings(R"cpp(
-    class $Class[[A]] {
+    class $Class_decl[[A]] {
       #include "imp.h"
     };
   )cpp",
@@ -758,35 +767,43 @@
   Annotations A(MarkedText);
   std::vector<HighlightingToken> Results;
   for (const Range& R : A.ranges())
-    Results.push_back({HighlightingKind::Variable, R});
+    Results.push_back({HighlightingKind::Variable, 0, R});
   for (unsigned I = 0; I < static_cast<unsigned>(HighlightingKind::LastKind); ++I) {
     HighlightingKind Kind = static_cast<HighlightingKind>(I);
     for (const Range& R : A.ranges(llvm::to_string(Kind)))
-      Results.push_back({Kind, R});
+      Results.push_back({Kind, 0, R});
   }
   llvm::sort(Results);
   return Results;
 }
 
 TEST(SemanticHighlighting, toSemanticTokens) {
-  auto Results = toSemanticTokens(tokens(R"(
+  auto Tokens = tokens(R"(
  [[blah]]
 
     $Function[[big]] [[bang]]
-  )"));
+  )");
+  Tokens.front().Modifiers |= unsigned(HighlightingModifier::Declaration);
+  Tokens.front().Modifiers |= unsigned(HighlightingModifier::Readonly);
+  auto Results = toSemanticTokens(Tokens);
 
   ASSERT_THAT(Results, SizeIs(3));
   EXPECT_EQ(Results[0].tokenType, unsigned(HighlightingKind::Variable));
+  EXPECT_EQ(Results[0].tokenModifiers,
+            unsigned(HighlightingModifier::Declaration) |
+                unsigned(HighlightingModifier::Readonly));
   EXPECT_EQ(Results[0].deltaLine, 1u);
   EXPECT_EQ(Results[0].deltaStart, 1u);
   EXPECT_EQ(Results[0].length, 4u);
 
   EXPECT_EQ(Results[1].tokenType, unsigned(HighlightingKind::Function));
+  EXPECT_EQ(Results[1].tokenModifiers, 0u);
   EXPECT_EQ(Results[1].deltaLine, 2u);
   EXPECT_EQ(Results[1].deltaStart, 4u);
   EXPECT_EQ(Results[1].length, 3u);
 
   EXPECT_EQ(Results[2].tokenType, unsigned(HighlightingKind::Variable));
+  EXPECT_EQ(Results[1].tokenModifiers, 0u);
   EXPECT_EQ(Results[2].deltaLine, 0u);
   EXPECT_EQ(Results[2].deltaStart, 4u);
   EXPECT_EQ(Results[2].length, 4u);
@@ -834,13 +851,13 @@
 
   std::vector<LineHighlightings> Tokens{
       {3,
-       {{HighlightingKind::Variable,
+       {{HighlightingKind::Variable, 0,
          Range{CreatePosition(3, 8), CreatePosition(3, 12)}},
-        {HighlightingKind::Function,
+        {HighlightingKind::Function, 0,
          Range{CreatePosition(3, 4), CreatePosition(3, 7)}}},
        /* IsInactive = */ false},
       {1,
-       {{HighlightingKind::Variable,
+       {{HighlightingKind::Variable, 0,
          Range{CreatePosition(1, 1), CreatePosition(1, 5)}}},
        /* IsInactive = */ true}};
   std::vector<TheiaSemanticHighlightingInformation> ActualResults =
Index: clang-tools-extra/clangd/test/semantic-tokens.test
===================================================================
--- clang-tools-extra/clangd/test/semantic-tokens.test
+++ clang-tools-extra/clangd/test/semantic-tokens.test
@@ -18,12 +18,12 @@
 # CHECK-NEXT:  "jsonrpc": "2.0",
 # CHECK-NEXT:  "result": {
 # CHECK-NEXT:    "data": [
-#                  First line, char 5, variable, no modifiers.
+#                  First line, char 5, variable, declaration
 # CHECK-NEXT:      0,
 # CHECK-NEXT:      4,
 # CHECK-NEXT:      1,
 # CHECK-NEXT:      0,
-# CHECK-NEXT:      0
+# CHECK-NEXT:      1
 # CHECK-NEXT:    ],
 # CHECK-NEXT:    "resultId": "1"
 # CHECK-NEXT:  }
@@ -44,12 +44,12 @@
 # CHECK-NEXT:    "edits": [
 # CHECK-NEXT:      {
 # CHECK-NEXT:        "data": [
-#                      Next line, char 5, variable, no modifiers
+#                      Next line, char 5, variable, declaration
 # CHECK-NEXT:          1,
 # CHECK-NEXT:          4,
 # CHECK-NEXT:          1,
 # CHECK-NEXT:          0,
-# CHECK-NEXT:          0
+# CHECK-NEXT:          1
 # CHECK-NEXT:        ],
 #                    Inserted at position 1
 # CHECK-NEXT:        "deleteCount": 0,
@@ -72,12 +72,12 @@
 # CHECK-NEXT:      4,
 # CHECK-NEXT:      1,
 # CHECK-NEXT:      0,
-# CHECK-NEXT:      0,
+# CHECK-NEXT:      1,
 # CHECK-NEXT:      1,
 # CHECK-NEXT:      4,
 # CHECK-NEXT:      1,
 # CHECK-NEXT:      0,
-# CHECK-NEXT:      0
+# CHECK-NEXT:      1
 # CHECK-NEXT:    ],
 # CHECK-NEXT:    "resultId": "3"
 # CHECK-NEXT:  }
Index: clang-tools-extra/clangd/test/initialize-params.test
===================================================================
--- clang-tools-extra/clangd/test/initialize-params.test
+++ clang-tools-extra/clangd/test/initialize-params.test
@@ -81,7 +81,14 @@
 # CHECK-NEXT:          "delta": true
 # CHECK-NEXT:        },
 # CHECK-NEXT:        "legend": {
-# CHECK-NEXT:          "tokenModifiers": [],
+# CHECK-NEXT:          "tokenModifiers": [
+# CHECK-NEXT:            "declaration",
+# CHECK-NEXT:            "deprecated",
+# CHECK-NEXT:            "deduced",
+# CHECK-NEXT:            "readonly",
+# CHECK-NEXT:            "static",
+# CHECK-NEXT:            "abstract"
+# CHECK-NEXT:          ],
 # CHECK-NEXT:          "tokenTypes": [
 # CHECK-NEXT:            "variable",
 # CHECK:               ]
Index: clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
+++ clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp
@@ -8,6 +8,7 @@
 #include "SemanticHighlighting.h"
 #include "refactor/Tweak.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/Support/ScopedPrinter.h"
 
 namespace clang {
 namespace clangd {
@@ -67,9 +68,19 @@
     if (!InsertOffset)
       return InsertOffset.takeError();
 
-    auto InsertReplacement = tooling::Replacement(
-        FilePath, *InsertOffset, 0,
-        ("/* " + toTextMateScope(Token.Kind) + " */").str());
+    std::string Comment = "/* ";
+    Comment.append(llvm::to_string(Token.Kind));
+    for (unsigned I = 0;
+         I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I) {
+      if (Token.Modifiers & (1 << I)) {
+        Comment.append(" [");
+        Comment.append(llvm::to_string(static_cast<HighlightingModifier>(I)));
+        Comment.push_back(']');
+      }
+    }
+    Comment.append(" */");
+    auto InsertReplacement =
+        tooling::Replacement(FilePath, *InsertOffset, 0, Comment);
     if (auto Err = Result.add(InsertReplacement))
       return std::move(Err);
   }
Index: clang-tools-extra/clangd/SemanticHighlighting.h
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.h
+++ clang-tools-extra/clangd/SemanticHighlighting.h
@@ -64,12 +64,34 @@
 
   LastKind = InactiveCode
 };
+
 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K);
 
+enum class HighlightingModifier {
+  Declaration,
+  // FIXME: Definition (needs findExplicitReferences support)
+  Deprecated,
+  Deduced,
+  Readonly,
+  Static,
+  Abstract,
+
+  LastModifier = Abstract
+};
+static_assert(static_cast<unsigned>(HighlightingModifier::LastModifier) < 32,
+              "Increase width of modifiers bitfield!");
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K);
+
 // Contains all information needed for the highlighting a token.
 struct HighlightingToken {
   HighlightingKind Kind;
+  uint32_t Modifiers = 0;
   Range R;
+
+  HighlightingToken &addModifier(HighlightingModifier M) {
+    Modifiers |= 1 << static_cast<unsigned>(M);
+    return *this;
+  }
 };
 
 bool operator==(const HighlightingToken &L, const HighlightingToken &R);
@@ -89,9 +111,10 @@
 std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST);
 
 std::vector<SemanticToken> toSemanticTokens(llvm::ArrayRef<HighlightingToken>);
-llvm::StringRef toSemanticTokenType(HighlightingKind Kind);
 std::vector<SemanticTokensEdit> diffTokens(llvm::ArrayRef<SemanticToken> Before,
                                            llvm::ArrayRef<SemanticToken> After);
+llvm::StringRef toSemanticTokenType(HighlightingKind Kind);
+llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier);
 
 /// Converts a HighlightingKind to a corresponding TextMate scope
 /// (https://manual.macromates.com/en/language_grammars).
Index: clang-tools-extra/clangd/SemanticHighlighting.cpp
===================================================================
--- clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -15,6 +15,8 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/Decl.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
+#include "clang/AST/DeclTemplate.h"
 #include "clang/AST/DeclarationName.h"
 #include "clang/AST/ExprCXX.h"
 #include "clang/AST/RecursiveASTVisitor.h"
@@ -118,17 +120,60 @@
   return llvm::None;
 }
 
-llvm::Optional<HighlightingKind> kindForReference(const ReferenceLoc &R) {
-  llvm::Optional<HighlightingKind> Result;
-  for (const NamedDecl *Decl : R.Targets) {
-    if (!canHighlightName(Decl->getDeclName()))
-      return llvm::None;
-    auto Kind = kindForDecl(Decl);
-    if (!Kind || (Result && Kind != Result))
-      return llvm::None;
-    Result = Kind;
+// Whether T is const in a loose sense - is a variable with this type readonly?
+bool isConst(QualType T) {
+  if (T.isNull() || T->isDependentType())
+    return false;
+  T = T.getNonReferenceType();
+  if (T.isConstQualified())
+    return true;
+  if (const auto *AT = T->getAsArrayTypeUnsafe())
+    return isConst(AT->getElementType());
+  return isConst(T->getPointeeType());
+}
+
+// Whether D is const in a loose sense (should it be highlighted as such?)
+bool isConst(const Decl *D) {
+  if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
+    return true;
+  if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
+      llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
+    if (isConst(llvm::cast<ValueDecl>(D)->getType()))
+      return true;
   }
-  return Result;
+  if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
+    if (OCPD->isReadOnly())
+      return true;
+  }
+  if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
+    if (!MPD->hasSetter())
+      return true;
+  }
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
+    if (CMD->isConst())
+      return true;
+  }
+  return false;
+}
+
+bool isStatic(const Decl *D) {
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
+    return CMD->isStatic();
+  if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
+    return VD->isStaticDataMember() || VD->isStaticLocal();
+  if (const auto *FD = llvm::dyn_cast<FieldDecl>(D))
+    return FD->isCXXClassMember() && !FD->isCXXInstanceMember();
+  if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
+    return OMD->isClassMethod();
+  return false;
+}
+
+bool isAbstract(const Decl *D) {
+  if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
+    return CMD->isPure();
+  if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
+    return CRD->isAbstract();
+  return false;
 }
 
 // For a macro usage `DUMP(foo)`, we want:
@@ -182,18 +227,24 @@
       : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()),
         LangOpts(AST.getLangOpts()) {}
 
-  void addToken(HighlightingToken T) { Tokens.push_back(T); }
-
-  void addToken(SourceLocation Loc, HighlightingKind Kind) {
+  HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) {
     Loc = getHighlightableSpellingToken(Loc, SourceMgr);
     if (Loc.isInvalid())
-      return;
+      return Dummy;
     const auto *Tok = TB.spelledTokenAt(Loc);
     assert(Tok);
+    return addToken(
+        halfOpenToRange(SourceMgr,
+                        Tok->range(SourceMgr).toCharRange(SourceMgr)),
+        Kind);
+  }
 
-    auto Range = halfOpenToRange(SourceMgr,
-                                 Tok->range(SourceMgr).toCharRange(SourceMgr));
-    Tokens.push_back(HighlightingToken{Kind, std::move(Range)});
+  HighlightingToken &addToken(Range R, HighlightingKind Kind) {
+    HighlightingToken HT;
+    HT.R = std::move(R);
+    HT.Kind = Kind;
+    Tokens.push_back(std::move(HT));
+    return Tokens.back();
   }
 
   std::vector<HighlightingToken> collect(ParsedAST &AST) && {
@@ -248,10 +299,13 @@
               MainCode.drop_front(*StartOfLine).take_until([](char C) {
                 return C == '\n';
               });
-          WithInactiveLines.push_back(
-              {HighlightingKind::InactiveCode,
-               {Position{Line, 0},
-                Position{Line, static_cast<int>(lspLength(LineText))}}});
+          HighlightingToken HT;
+          WithInactiveLines.emplace_back();
+          WithInactiveLines.back().Kind = HighlightingKind::InactiveCode;
+          WithInactiveLines.back().R.start.line = Line;
+          WithInactiveLines.back().R.end.line = Line;
+          WithInactiveLines.back().R.end.character =
+              static_cast<int>(lspLength(LineText));
         } else {
           elog("Failed to convert position to offset: {0}",
                StartOfLine.takeError());
@@ -277,6 +331,7 @@
   const SourceManager &SourceMgr;
   const LangOptions &LangOpts;
   std::vector<HighlightingToken> Tokens;
+  HighlightingToken Dummy; // returned from addToken(InvalidLoc)
 };
 
 /// Produces highlightings, which are not captured by findExplicitReferences,
@@ -288,7 +343,8 @@
 
   bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) {
     if (auto K = kindForType(L.getTypePtr()))
-      H.addToken(L.getBeginLoc(), *K);
+      H.addToken(L.getBeginLoc(), *K)
+          .addModifier(HighlightingModifier::Deduced);
     return true;
   }
 
@@ -297,7 +353,8 @@
     if (!AT)
       return true;
     if (auto K = kindForType(AT->getDeducedType().getTypePtrOrNull()))
-      H.addToken(D->getTypeSpecStartLoc(), *K);
+      H.addToken(D->getTypeSpecStartLoc(), *K)
+          .addModifier(HighlightingModifier::Deduced);
     return true;
   }
 
@@ -391,16 +448,45 @@
   CollectExtraHighlightings(Builder).TraverseAST(C);
   // Highlight all decls and references coming from the AST.
   findExplicitReferences(C, [&](ReferenceLoc R) {
-    if (auto Kind = kindForReference(R))
-      Builder.addToken(R.NameLoc, *Kind);
+    llvm::Optional<HighlightingKind> Result;
+    for (const NamedDecl *Decl : R.Targets) {
+      if (!canHighlightName(Decl->getDeclName()))
+        continue;
+      auto Kind = kindForDecl(Decl);
+      if (!Kind || (Result && Kind != Result))
+        continue;
+      Result = Kind;
+
+      auto &Tok = Builder.addToken(R.NameLoc, *Kind);
+
+      // The attribute tests don't want to look at the template.
+      if (auto *TD = dyn_cast<TemplateDecl>(Decl)) {
+        if (auto *Templated = TD->getTemplatedDecl())
+          Decl = Templated;
+      }
+      if (isConst(Decl))
+        Tok.addModifier(HighlightingModifier::Readonly);
+      if (isStatic(Decl))
+        Tok.addModifier(HighlightingModifier::Static);
+      if (isAbstract(Decl))
+        Tok.addModifier(HighlightingModifier::Abstract);
+      if (Decl->isDeprecated())
+        Tok.addModifier(HighlightingModifier::Deprecated);
+      if (R.IsDecl)
+        Tok.addModifier(HighlightingModifier::Declaration);
+    }
   });
   // Add highlightings for macro references.
-  for (const auto &SIDToRefs : AST.getMacros().MacroRefs) {
+  auto AddMacro = [&](const MacroOccurrence &M) {
+    auto &T = Builder.addToken(M.Rng, HighlightingKind::Macro);
+    if (M.IsDefinition)
+      T.addModifier(HighlightingModifier::Declaration);
+  };
+  for (const auto &SIDToRefs : AST.getMacros().MacroRefs)
     for (const auto &M : SIDToRefs.second)
-      Builder.addToken({HighlightingKind::Macro, M.Rng});
-  }
+      AddMacro(M);
   for (const auto &M : AST.getMacros().UnknownMacros)
-    Builder.addToken({HighlightingKind::Macro, M.Rng});
+    AddMacro(M);
 
   return std::move(Builder).collect(AST);
 }
@@ -450,6 +536,14 @@
   }
   llvm_unreachable("invalid HighlightingKind");
 }
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) {
+  switch (K) {
+  case HighlightingModifier::Declaration:
+    return OS << "decl"; // abbrevation for common case
+  default:
+    return OS << toSemanticTokenModifier(K);
+  }
+}
 
 std::vector<LineHighlightings>
 diffHighlightings(ArrayRef<HighlightingToken> New,
@@ -510,10 +604,12 @@
 }
 
 bool operator==(const HighlightingToken &L, const HighlightingToken &R) {
-  return std::tie(L.R, L.Kind) == std::tie(R.R, R.Kind);
+  return std::tie(L.R, L.Kind, L.Modifiers) ==
+         std::tie(R.R, R.Kind, R.Modifiers);
 }
 bool operator<(const HighlightingToken &L, const HighlightingToken &R) {
-  return std::tie(L.R, L.Kind) < std::tie(R.R, R.Kind);
+  return std::tie(L.R, L.Kind, R.Modifiers) <
+         std::tie(R.R, R.Kind, R.Modifiers);
 }
 bool operator==(const LineHighlightings &L, const LineHighlightings &R) {
   return std::tie(L.Line, L.Tokens) == std::tie(R.Line, R.Tokens);
@@ -544,6 +640,7 @@
     assert(Tok.R.end.line == Tok.R.start.line);
     Out.length = Tok.R.end.character - Tok.R.start.character;
     Out.tokenType = static_cast<unsigned>(Tok.Kind);
+    Out.tokenModifiers = Tok.Modifiers;
 
     Last = &Tok;
   }
@@ -575,7 +672,6 @@
   case HighlightingKind::Typedef:
     return "type";
   case HighlightingKind::DependentType:
-    return "dependent"; // nonstandard
   case HighlightingKind::DependentName:
     return "dependent"; // nonstandard
   case HighlightingKind::Namespace:
@@ -594,6 +690,23 @@
   llvm_unreachable("unhandled HighlightingKind");
 }
 
+llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) {
+  switch (Modifier) {
+  case HighlightingModifier::Declaration:
+    return "declaration";
+  case HighlightingModifier::Deprecated:
+    return "deprecated";
+  case HighlightingModifier::Readonly:
+    return "readonly";
+  case HighlightingModifier::Static:
+    return "static";
+  case HighlightingModifier::Deduced:
+    return "deduced"; // nonstandard
+  case HighlightingModifier::Abstract:
+    return "abstract";
+  }
+}
+
 std::vector<TheiaSemanticHighlightingInformation>
 toTheiaSemanticHighlightingInformation(
     llvm::ArrayRef<LineHighlightings> Tokens) {
Index: clang-tools-extra/clangd/ClangdLSPServer.cpp
===================================================================
--- clang-tools-extra/clangd/ClangdLSPServer.cpp
+++ clang-tools-extra/clangd/ClangdLSPServer.cpp
@@ -490,6 +490,15 @@
   return Types;
 }
 
+static std::vector<llvm::StringRef> semanticTokenModifiers() {
+  std::vector<llvm::StringRef> Modifiers;
+  for (unsigned I = 0;
+       I <= static_cast<unsigned>(HighlightingModifier::LastModifier); ++I)
+    Modifiers.push_back(
+        toSemanticTokenModifier(static_cast<HighlightingModifier>(I)));
+  return Modifiers;
+}
+
 void ClangdLSPServer::onInitialize(const InitializeParams &Params,
                                    Callback<llvm::json::Value> Reply) {
   // Determine character encoding first as it affects constructed ClangdServer.
@@ -623,8 +632,9 @@
                  {"full", llvm::json::Object{{"delta", true}}},
                  {"range", false},
                  {"legend",
-                  llvm::json::Object{{"tokenTypes", semanticTokenTypes()},
-                                     {"tokenModifiers", llvm::json::Array()}}},
+                  llvm::json::Object{
+                      {"tokenTypes", semanticTokenTypes()},
+                      {"tokenModifiers", semanticTokenModifiers()}}},
              }},
             {"signatureHelpProvider",
              llvm::json::Object{
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to