sammccall created this revision.
Herald added subscribers: cfe-commits, kadircet, arphaman, jkorous.
Herald added a project: clang.

This reverts commit d2254dbf21a3243233b75294ef901086199df1b9 
<https://reviews.llvm.org/rGd2254dbf21a3243233b75294ef901086199df1b9>.
This (unintentionally?) changed behavior, disallowing e.g. -x 
objective-c++-header


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D65485

Files:
  clang-tools-extra/clangd/Selection.cpp
  clang-tools-extra/clangd/Selection.h
  clang-tools-extra/clangd/refactor/Tweak.cpp
  clang-tools-extra/clangd/unittests/SelectionTests.cpp
  clang/include/clang/Driver/Types.def
  clang/include/clang/Driver/Types.h
  clang/lib/Driver/Types.cpp

Index: clang/lib/Driver/Types.cpp
===================================================================
--- clang/lib/Driver/Types.cpp
+++ clang/lib/Driver/Types.cpp
@@ -18,14 +18,15 @@
 
 struct TypeInfo {
   const char *Name;
+  const char *Flags;
   const char *TempSuffix;
   ID PreprocessedType;
   const llvm::SmallVector<phases::ID, phases::MaxNumberOfPhases> Phases;
 };
 
 static const TypeInfo TypeInfos[] = {
-#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, ...) \
-  { NAME, TEMP_SUFFIX, TY_##PP_TYPE, { __VA_ARGS__ }, },
+#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, FLAGS, ...) \
+  { NAME, FLAGS, TEMP_SUFFIX, TY_##PP_TYPE, { __VA_ARGS__ }, },
 #include "clang/Driver/Types.def"
 #undef TYPE
 };
@@ -89,15 +90,7 @@
 }
 
 bool types::canTypeBeUserSpecified(ID Id) {
-  static const clang::driver::types::ID kStaticLangageTypes[] = {
-      TY_CUDA_DEVICE,   TY_HIP_DEVICE,    TY_PP_CHeader,
-      TY_PP_ObjCHeader, TY_PP_CXXHeader,  TY_ObjCXXHeader,
-      TY_PP_CXXModule,  TY_LTO_IR,        TY_LTO_BC,
-      TY_Plist,         TY_RewrittenObjC, TY_RewrittenLegacyObjC,
-      TY_Remap,         TY_PCH,           TY_Object,
-      TY_Image,         TY_dSYM,          TY_Dependencies,
-      TY_CUDA_FATBIN,   TY_HIP_FATBIN};
-  return !llvm::is_contained(kStaticLangageTypes, Id);
+  return strchr(getInfo(Id).Flags, 'u');
 }
 
 bool types::appendSuffixForType(ID Id) {
Index: clang/include/clang/Driver/Types.h
===================================================================
--- clang/include/clang/Driver/Types.h
+++ clang/include/clang/Driver/Types.h
@@ -20,7 +20,7 @@
 namespace types {
   enum ID {
     TY_INVALID,
-#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, ...) TY_##ID,
+#define TYPE(NAME, ID, PP_TYPE, TEMP_SUFFIX, FLAGS, ...) TY_##ID,
 #include "clang/Driver/Types.def"
 #undef TYPE
     TY_LAST
Index: clang/include/clang/Driver/Types.def
===================================================================
--- clang/include/clang/Driver/Types.def
+++ clang/include/clang/Driver/Types.def
@@ -29,73 +29,76 @@
 // The fourth value is the suffix to use when creating temporary files
 // of this type, or null if unspecified.
 
-// The final value is a variadic list of phases for each type. Eventually the
+// The fifth value is a string containing option flags. Valid values:
+//  u - The type can be user specified (with -x).
+
+// The sixth value is a variadic list of phases for each type. Eventually the
 // options flag string will be replaced with this variadic list.
 // Most of the options in Flags have been removed in favor of subsuming their
 // meaning from the phases list.
 
 // C family source language (with and without preprocessing).
-TYPE("cpp-output",               PP_C,         INVALID,         "i",      phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("c",                        C,            PP_C,            "c",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("cl",                       CL,           PP_C,            "cl",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("cuda-cpp-output",          PP_CUDA,      INVALID,         "cui",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("cuda",                     CUDA,         PP_CUDA,         "cu",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("cuda",                     CUDA_DEVICE,  PP_CUDA,         "cu",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("hip-cpp-output",           PP_HIP,       INVALID,         "cui",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("hip",                      HIP,          PP_HIP,          "cu",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("hip",                      HIP_DEVICE,   PP_HIP,          "cu",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("objective-c-cpp-output",   PP_ObjC,      INVALID,         "mi",     phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("objc-cpp-output",          PP_ObjC_Alias, INVALID,        "mi",     phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("objective-c",              ObjC,         PP_ObjC,         "m",      phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("c++-cpp-output",           PP_CXX,       INVALID,         "ii",     phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("c++",                      CXX,          PP_CXX,          "cpp",    phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("objective-c++-cpp-output", PP_ObjCXX,    INVALID,         "mii",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("objc++-cpp-output",        PP_ObjCXX_Alias, INVALID,      "mii",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("objective-c++",            ObjCXX,       PP_ObjCXX,       "mm",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("renderscript",             RenderScript, PP_C,            "rs",     phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cpp-output",               PP_C,         INVALID,         "i",     "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("c",                        C,            PP_C,            "c",     "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cl",                       CL,           PP_C,            "cl",    "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cuda-cpp-output",          PP_CUDA,      INVALID,         "cui",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cuda",                     CUDA,         PP_CUDA,         "cu",    "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cuda",                     CUDA_DEVICE,  PP_CUDA,         "cu",    "" , phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("hip-cpp-output",           PP_HIP,       INVALID,         "cui",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("hip",                      HIP,          PP_HIP,          "cu",    "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("hip",                      HIP_DEVICE,   PP_HIP,          "cu",    "" , phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("objective-c-cpp-output",   PP_ObjC,      INVALID,         "mi",    "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("objc-cpp-output",          PP_ObjC_Alias, INVALID,        "mi",    "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("objective-c",              ObjC,         PP_ObjC,         "m",     "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("c++-cpp-output",           PP_CXX,       INVALID,         "ii",    "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("c++",                      CXX,          PP_CXX,          "cpp",   "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("objective-c++-cpp-output", PP_ObjCXX,    INVALID,         "mii",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("objc++-cpp-output",        PP_ObjCXX_Alias, INVALID,      "mii",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("objective-c++",            ObjCXX,       PP_ObjCXX,       "mm",    "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("renderscript",             RenderScript, PP_C,            "rs",    "u", phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 // C family input files to precompile.
-TYPE("c-header-cpp-output",      PP_CHeader,   INVALID,         "i",      phases::Precompile)
-TYPE("c-header",                 CHeader,      PP_CHeader,      "h",      phases::Preprocess, phases::Precompile)
-TYPE("cl-header",                CLHeader,     PP_CHeader,      "h",      phases::Preprocess, phases::Precompile)
-TYPE("objective-c-header-cpp-output", PP_ObjCHeader, INVALID,   "mi",     phases::Precompile)
-TYPE("objective-c-header",       ObjCHeader,   PP_ObjCHeader,   "h",      phases::Preprocess, phases::Precompile)
-TYPE("c++-header-cpp-output",    PP_CXXHeader, INVALID,         "ii",     phases::Precompile)
-TYPE("c++-header",               CXXHeader,    PP_CXXHeader,    "hh",     phases::Preprocess, phases::Precompile)
-TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID, "mii",  phases::Precompile)
-TYPE("objective-c++-header",     ObjCXXHeader, PP_ObjCXXHeader, "h",      phases::Preprocess, phases::Precompile)
-TYPE("c++-module",               CXXModule,    PP_CXXModule,    "cppm",   phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("c++-module-cpp-output",    PP_CXXModule, INVALID,         "iim",    phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("c-header-cpp-output",      PP_CHeader,   INVALID,         "i",     "",   phases::Precompile)
+TYPE("c-header",                 CHeader,      PP_CHeader,      "h",     "u",  phases::Preprocess, phases::Precompile)
+TYPE("cl-header",                CLHeader,     PP_CHeader,      "h",     "u",  phases::Preprocess, phases::Precompile)
+TYPE("objective-c-header-cpp-output", PP_ObjCHeader, INVALID,   "mi",    "",   phases::Precompile)
+TYPE("objective-c-header",       ObjCHeader,   PP_ObjCHeader,   "h",     "u",  phases::Preprocess, phases::Precompile)
+TYPE("c++-header-cpp-output",    PP_CXXHeader, INVALID,         "ii",    "",   phases::Precompile)
+TYPE("c++-header",               CXXHeader,    PP_CXXHeader,    "hh",    "u",  phases::Preprocess, phases::Precompile)
+TYPE("objective-c++-header-cpp-output", PP_ObjCXXHeader, INVALID, "mii", "",   phases::Precompile)
+TYPE("objective-c++-header",     ObjCXXHeader, PP_ObjCXXHeader, "h",     "u",  phases::Preprocess, phases::Precompile)
+TYPE("c++-module",               CXXModule,    PP_CXXModule,    "cppm",  "u",  phases::Preprocess, phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("c++-module-cpp-output",    PP_CXXModule, INVALID,         "iim",   "",   phases::Precompile, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 // Other languages.
-TYPE("ada",                      Ada,          INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("assembler",                PP_Asm,       INVALID,         "s",      phases::Assemble, phases::Link)
-TYPE("assembler-with-cpp",       Asm,          PP_Asm,          "S",      phases::Preprocess, phases::Assemble, phases::Link)
-TYPE("f95",                      PP_Fortran,   INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("f95-cpp-input",            Fortran,      PP_Fortran,      nullptr,  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("java",                     Java,         INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("ada",                      Ada,          INVALID,         nullptr, "u",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("assembler",                PP_Asm,       INVALID,         "s",     "u",  phases::Assemble, phases::Link)
+TYPE("assembler-with-cpp",       Asm,          PP_Asm,          "S",     "u",  phases::Preprocess, phases::Assemble, phases::Link)
+TYPE("f95",                      PP_Fortran,   INVALID,         nullptr, "u",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("f95-cpp-input",            Fortran,      PP_Fortran,      nullptr, "u",  phases::Preprocess, phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("java",                     Java,         INVALID,         nullptr, "u",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 // LLVM IR/LTO types. We define separate types for IR and LTO because LTO
 // outputs should use the standard suffixes.
-TYPE("ir",                       LLVM_IR,      INVALID,         "ll",     phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("ir",                       LLVM_BC,      INVALID,         "bc",     phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("lto-ir",                   LTO_IR,       INVALID,         "s",      phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("lto-bc",                   LTO_BC,       INVALID,         "o",      phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("ir",                       LLVM_IR,      INVALID,         "ll",    "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("ir",                       LLVM_BC,      INVALID,         "bc",    "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("lto-ir",                   LTO_IR,       INVALID,         "s",     "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("lto-bc",                   LTO_BC,       INVALID,         "o",     "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
 
 // Misc.
-TYPE("ast",                      AST,          INVALID,         "ast",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("ifs",                      IFS,          INVALID,         "ifs",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("pcm",                      ModuleFile,   INVALID,         "pcm",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("plist",                    Plist,        INVALID,         "plist",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("rewritten-objc",           RewrittenObjC,INVALID,         "cpp",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("rewritten-legacy-objc",    RewrittenLegacyObjC,INVALID,   "cpp",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("remap",                    Remap,        INVALID,         "remap",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("precompiled-header",       PCH,          INVALID,         "gch",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("object",                   Object,       INVALID,         "o",      phases::Link)
-TYPE("treelang",                 Treelang,     INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("image",                    Image,        INVALID,         "out",    phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("dSYM",                     dSYM,         INVALID,         "dSYM",   phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("dependencies",             Dependencies, INVALID,         "d",      phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("cuda-fatbin",              CUDA_FATBIN,  INVALID,         "fatbin", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("hip-fatbin",               HIP_FATBIN,   INVALID,         "hipfb",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
-TYPE("none",                     Nothing,      INVALID,         nullptr,  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("ast",                      AST,          INVALID,         "ast",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("ifs",                      IFS,          INVALID,         "ifs",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("pcm",                      ModuleFile,   INVALID,         "pcm",   "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("plist",                    Plist,        INVALID,         "plist", "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("rewritten-objc",           RewrittenObjC,INVALID,         "cpp",   "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("rewritten-legacy-objc",    RewrittenLegacyObjC,INVALID,   "cpp",   "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("remap",                    Remap,        INVALID,         "remap", "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("precompiled-header",       PCH,          INVALID,         "gch",   "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("object",                   Object,       INVALID,         "o",     "",  phases::Link)
+TYPE("treelang",                 Treelang,     INVALID,         nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("image",                    Image,        INVALID,         "out",   "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("dSYM",                     dSYM,         INVALID,         "dSYM",  "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("dependencies",             Dependencies, INVALID,         "d",     "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("cuda-fatbin",              CUDA_FATBIN,  INVALID,         "fatbin","",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("hip-fatbin",               HIP_FATBIN,   INVALID,         "hipfb", "",  phases::Compile, phases::Backend, phases::Assemble, phases::Link)
+TYPE("none",                     Nothing,      INVALID,         nullptr, "u", phases::Compile, phases::Backend, phases::Assemble, phases::Link)
Index: clang-tools-extra/clangd/unittests/SelectionTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SelectionTests.cpp
+++ clang-tools-extra/clangd/unittests/SelectionTests.cpp
@@ -23,16 +23,16 @@
   Annotations Test(MarkedCode);
   switch (Test.points().size()) {
   case 1: // Point selection.
-    return SelectionTree(AST.getASTContext(),
+    return SelectionTree(AST.getASTContext(), AST.getTokens(),
                          cantFail(positionToOffset(Test.code(), Test.point())));
   case 2: // Range selection.
     return SelectionTree(
-        AST.getASTContext(),
+        AST.getASTContext(), AST.getTokens(),
         cantFail(positionToOffset(Test.code(), Test.points()[0])),
         cantFail(positionToOffset(Test.code(), Test.points()[1])));
   default:
     ADD_FAILURE() << "Expected 1-2 points for selection.\n" << MarkedCode;
-    return SelectionTree(AST.getASTContext(), 0u, 0u);
+    return SelectionTree(AST.getASTContext(), AST.getTokens(), 0u, 0u);
   }
 }
 
@@ -219,6 +219,9 @@
       {"void foo() { [[foo^]] (); }", "DeclRefExpr"},
       {"int bar; void foo() [[{ foo (); }]]^", "CompoundStmt"},
 
+      // Ignores whitespace, comments, and semicolons in the selection.
+      {"void foo() { [[foo^()]]; /*comment*/^}", "CallExpr"},
+
       // Tricky case: FunctionTypeLoc in FunctionDecl has a hole in it.
       {"[[^void]] foo();", "BuiltinTypeLoc"},
       {"[[void foo^()]];", "FunctionProtoTypeLoc"},
Index: clang-tools-extra/clangd/refactor/Tweak.cpp
===================================================================
--- clang-tools-extra/clangd/refactor/Tweak.cpp
+++ clang-tools-extra/clangd/refactor/Tweak.cpp
@@ -41,7 +41,7 @@
 Tweak::Selection::Selection(ParsedAST &AST, unsigned RangeBegin,
                             unsigned RangeEnd)
     : AST(AST), SelectionBegin(RangeBegin), SelectionEnd(RangeEnd),
-      ASTSelection(AST.getASTContext(), RangeBegin, RangeEnd) {
+      ASTSelection(AST.getASTContext(), AST.getTokens(), RangeBegin, RangeEnd) {
   auto &SM = AST.getSourceManager();
   Code = SM.getBufferData(SM.getMainFileID());
   Cursor = SM.getComposedLoc(SM.getMainFileID(), RangeBegin);
Index: clang-tools-extra/clangd/Selection.h
===================================================================
--- clang-tools-extra/clangd/Selection.h
+++ clang-tools-extra/clangd/Selection.h
@@ -35,6 +35,7 @@
 #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_SELECTION_H
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/PrettyPrinter.h"
+#include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/ADT/SmallVector.h"
 
 namespace clang {
@@ -56,8 +57,9 @@
 //
 // SelectionTree tries to behave sensibly in the presence of macros, but does
 // not model any preprocessor concepts: the output is a subset of the AST.
-// Currently comments, directives etc are treated as part of the lexically
-// containing AST node, (though we may want to change this in future).
+//
+// Comments, directives and whitespace are completely ignored.
+// Semicolons are also ignored, as the AST generally does not model them well.
 //
 // The SelectionTree owns the Node structures, but the ASTNode attributes
 // point back into the AST it was constructed with.
@@ -66,11 +68,13 @@
   // Creates a selection tree at the given byte offset in the main file.
   // This is approximately equivalent to a range of one character.
   // (Usually, the character to the right of Offset, sometimes to the left).
-  SelectionTree(ASTContext &AST, unsigned Offset);
+  SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
+                unsigned Offset);
   // Creates a selection tree for the given range in the main file.
   // The range includes bytes [Start, End).
   // If Start == End, uses the same heuristics as SelectionTree(AST, Start).
-  SelectionTree(ASTContext &AST, unsigned Start, unsigned End);
+  SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
+                unsigned Start, unsigned End);
 
   // Describes to what extent an AST node is covered by the selection.
   enum Selection {
Index: clang-tools-extra/clangd/Selection.cpp
===================================================================
--- clang-tools-extra/clangd/Selection.cpp
+++ clang-tools-extra/clangd/Selection.cpp
@@ -17,6 +17,7 @@
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TokenKinds.h"
+#include "clang/Tooling/Syntax/Tokens.h"
 #include "llvm/ADT/STLExtras.h"
 #include "llvm/Support/raw_ostream.h"
 #include <algorithm>
@@ -28,39 +29,83 @@
 using Node = SelectionTree::Node;
 using ast_type_traits::DynTypedNode;
 
-// Stores a collection of (possibly-overlapping) integer ranges.
-// When new ranges are added, hit-tests them against existing ones.
-class RangeSet {
+// Identifies which tokens are selected, and evaluates claims of source ranges
+// by AST nodes. Tokens may be claimed only once.
+class SelectedTokens {
 public:
-  // Returns true if any new offsets are covered.
-  // This is naive (linear in number of successful add() calls), but ok for now.
-  bool add(unsigned Begin, unsigned End) {
-    assert(std::is_sorted(Ranges.begin(), Ranges.end()));
-    assert(Begin < End);
-
-    if (covered(Begin, End))
-      return false;
-    auto Pair = std::make_pair(Begin, End);
-    Ranges.insert(llvm::upper_bound(Ranges, Pair), Pair);
-    return true;
+  SelectedTokens(llvm::ArrayRef<syntax::Token> Spelled, const SourceManager &SM,
+                 unsigned SelBegin, unsigned SelEnd)
+      : SelBegin(SelBegin), SelEnd(SelEnd) {
+    Tokens.reserve(Spelled.size());
+    for (const auto& Tok : Spelled) {
+      // As well as comments, don't count semicolons as real tokens.
+      // They're not properly claimed as expr-statement is missing from the AST.
+      if (Tok.kind() == tok::comment || Tok.kind() == tok::semi)
+        continue;
+
+      Tokens.emplace_back();
+      TokInfo &S = Tokens.back();
+      S.StartOffset = SM.getFileOffset(Tok.location());
+      S.EndOffset = S.StartOffset + Tok.length();
+      if (S.StartOffset >= SelBegin && S.EndOffset <= SelEnd)
+        S.Selected = SelectionTree::Complete;
+      else if (S.EndOffset > SelBegin && S.StartOffset < SelEnd)
+        S.Selected = SelectionTree::Partial;
+      else
+        S.Selected = SelectionTree::Unselected;
+      S.Claimed = false;
+    }
   }
 
-private:
-  bool covered(unsigned Begin, unsigned End) {
-    assert(Begin < End);
-    for (const auto &R : Ranges) {
-      if (Begin < R.first)
-        return false; // The prefix [Begin, R.first) is not covered.
-      if (Begin < R.second) {
-        Begin = R.second; // Prefix is covered, truncate the range.
-        if (Begin >= End)
-          return true;
+  SelectionTree::Selection claim(unsigned Begin, unsigned End) {
+    assert(Begin <= End);
+
+    // Fast-path for missing the selection entirely.
+    if (Begin >= SelEnd || End <= SelBegin)
+      return SelectionTree::Unselected;
+
+    // We will consider the range selected if it hit any selected and previously
+    // unclaimed token.
+    bool ClaimedAnyToken = false;
+    // The selection is partial if:
+    // - any claimed token is partially selected
+    // - any token in the range is unselected
+    bool PartialSelection = false;
+
+    // Find the first token that (maybe) overlaps the claimed range.
+    auto Start = llvm::partition_point(Tokens, [&](const TokInfo &Tok) {
+      return Tok.EndOffset <= Begin;
+    });
+    // Iterate over every token that overlaps the range.
+    // Claim selected tokens, and update the two result flags.
+    for (auto It = Start; It != Tokens.end() && It->StartOffset < End; ++It) {
+      if (It->Selected) {
+        if (!It->Claimed) {
+          It->Claimed = true;
+          ClaimedAnyToken = true;
+          PartialSelection |= (It->Selected == SelectionTree::Partial);
+        }
+      } else {
+        PartialSelection = true;
       }
     }
-    return false;
+    if (!ClaimedAnyToken)
+      return SelectionTree::Unselected;
+    return PartialSelection ? SelectionTree::Partial : SelectionTree::Complete;
   }
 
-  std::vector<std::pair<unsigned, unsigned>> Ranges; // Always sorted.
+private:
+  struct TokInfo {
+    unsigned StartOffset;
+    unsigned EndOffset;
+    SelectionTree::Selection Selected;
+    bool Claimed;
+    bool operator<(const TokInfo &Other) const {
+      return StartOffset < Other.StartOffset;
+    }
+  };
+  std::vector<TokInfo> Tokens;
+  unsigned SelBegin, SelEnd;
 };
 
 // Show the type of a node for debugging.
@@ -99,14 +144,16 @@
 public:
   // Runs the visitor to gather selected nodes and their ancestors.
   // If there is any selection, the root (TUDecl) is the first node.
-  static std::deque<Node> collect(ASTContext &AST, const PrintingPolicy &PP,
-                                  unsigned Begin, unsigned End, FileID File) {
-    SelectionVisitor V(AST, PP, Begin, End, File);
+  static std::deque<Node> collect(ASTContext &AST,
+                                  const syntax::TokenBuffer &Tokens,
+                                  const PrintingPolicy &PP, unsigned Begin,
+                                  unsigned End, FileID File) {
+    SelectionVisitor V(AST, Tokens, PP, Begin, End, File);
     V.TraverseAST(AST);
     assert(V.Stack.size() == 1 && "Unpaired push/pop?");
     assert(V.Stack.top() == &V.Nodes.front());
-    // We selected TUDecl if characters were unclaimed (or the file is empty).
-    if (V.Nodes.size() == 1 || V.Claimed.add(Begin, End)) {
+    // We selected TUDecl if tokens were unclaimed (or the file is empty).
+    if (V.Nodes.size() == 1 || V.Claimed.claim(Begin, End)) {
       StringRef FileContent = AST.getSourceManager().getBufferData(File);
       // Don't require the trailing newlines to be selected.
       bool SelectedAll = Begin == 0 && End >= FileContent.rtrim().size();
@@ -176,15 +223,18 @@
 private:
   using Base = RecursiveASTVisitor<SelectionVisitor>;
 
-  SelectionVisitor(ASTContext &AST, const PrintingPolicy &PP, unsigned SelBegin,
-                   unsigned SelEnd, FileID SelFile)
+  SelectionVisitor(ASTContext &AST, const syntax::TokenBuffer &Tokens,
+                   const PrintingPolicy &PP, unsigned SelBegin, unsigned SelEnd,
+                   FileID SelFile)
       : SM(AST.getSourceManager()), LangOpts(AST.getLangOpts()),
 #ifndef NDEBUG
         PrintPolicy(PP),
 #endif
-        SelBegin(SelBegin), SelEnd(SelEnd), SelFile(SelFile),
+        Claimed(Tokens.spelledTokens(SelFile), SM, SelBegin, SelEnd),
+        SelFile(SelFile),
         SelBeginTokenStart(SM.getFileOffset(Lexer::GetBeginningOfToken(
-            SM.getComposedLoc(SelFile, SelBegin), SM, LangOpts))) {
+            SM.getComposedLoc(SelFile, SelBegin), SM, LangOpts))),
+        SelEnd(SelEnd) {
     // Ensure we have a node for the TU decl, regardless of traversal scope.
     Nodes.emplace_back();
     Nodes.back().ASTNode = DynTypedNode::create(*AST.getTranslationUnitDecl());
@@ -318,30 +368,16 @@
     // Otherwise, nodes in macro expansions can't be selected.
     if (B.first != SelFile || E.first != SelFile)
       return SelectionTree::Unselected;
-    // Is there any overlap at all between the selection and range?
-    if (B.second >= SelEnd || E.second < SelBegin)
-      return SelectionTree::Unselected;
-    // We may have hit something.
-    auto PreciseBounds = std::make_pair(B.second, E.second);
-    // Trim range using the selection, drop it if empty.
-    B.second = std::max(B.second, SelBegin);
-    E.second = std::min(E.second, SelEnd);
-    if (B.second >= E.second)
-      return SelectionTree::Unselected;
     // Attempt to claim the remaining range. If there's nothing to claim, only
     // children were selected.
-    if (!Claimed.add(B.second, E.second))
-      return SelectionTree::Unselected;
-    dlog("{1}hit selection: {0}",
-         SourceRange(SM.getComposedLoc(B.first, B.second),
-                     SM.getComposedLoc(E.first, E.second))
-             .printToString(SM),
-         indent());
-    // Some of our own characters are covered, this is a true hit.
-    // Determine whether the node was completely covered.
-    return (PreciseBounds.first >= SelBegin && PreciseBounds.second <= SelEnd)
-               ? SelectionTree::Complete
-               : SelectionTree::Partial;
+    SelectionTree::Selection Result = Claimed.claim(B.second, E.second);
+    if (Result)
+      dlog("{1}hit selection: {0}",
+           SourceRange(SM.getComposedLoc(B.first, B.second),
+                       SM.getComposedLoc(E.first, E.second))
+               .printToString(SM),
+           indent());
+    return Result;
   }
 
   std::string indent(int Offset = 0) {
@@ -357,11 +393,8 @@
   const PrintingPolicy &PrintPolicy;
 #endif
   std::stack<Node *> Stack;
-  RangeSet Claimed;
+  SelectedTokens Claimed;
   std::deque<Node> Nodes; // Stable pointers as we add more nodes.
-  // Half-open selection range.
-  unsigned SelBegin;
-  unsigned SelEnd;
   FileID SelFile;
   // If the selection start slices a token in half, the beginning of that token.
   // This is useful for checking whether the end of a token range overlaps
@@ -369,6 +402,7 @@
   // range.end + measureToken(range.end) < SelBegin (assuming range.end points
   // to a token), and it saves a lex every time.
   unsigned SelBeginTokenStart;
+  unsigned SelEnd;
 };
 
 } // namespace
@@ -414,7 +448,8 @@
   return {Offset, Offset + 1};
 }
 
-SelectionTree::SelectionTree(ASTContext &AST, unsigned Begin, unsigned End)
+SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
+                             unsigned Begin, unsigned End)
     : PrintPolicy(AST.getLangOpts()) {
   // No fundamental reason the selection needs to be in the main file,
   // but that's all clangd has needed so far.
@@ -428,13 +463,14 @@
   dlog("Computing selection for {0}",
        SourceRange(SM.getComposedLoc(FID, Begin), SM.getComposedLoc(FID, End))
            .printToString(SM));
-  Nodes = SelectionVisitor::collect(AST, PrintPolicy, Begin, End, FID);
+  Nodes = SelectionVisitor::collect(AST, Tokens, PrintPolicy, Begin, End, FID);
   Root = Nodes.empty() ? nullptr : &Nodes.front();
   dlog("Built selection tree\n{0}", *this);
 }
 
-SelectionTree::SelectionTree(ASTContext &AST, unsigned Offset)
-    : SelectionTree(AST, Offset, Offset) {}
+SelectionTree::SelectionTree(ASTContext &AST, const syntax::TokenBuffer &Tokens,
+                             unsigned Offset)
+    : SelectionTree(AST, Tokens, Offset, Offset) {}
 
 const Node *SelectionTree::commonAncestor() const {
   const Node *Ancestor = Root;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D65485: Revert "[N... Sam McCall via Phabricator via cfe-commits

Reply via email to