SureYeaah created this revision.
SureYeaah added reviewers: sammccall, kadircet.
Herald added subscribers: cfe-commits, arphaman, jkorous, MaskRay, 
ilya-biryukov.
Herald added a project: clang.

Fixed SelectionTree bug for macros

- Fixed SelectionTree claimRange for macros and template instantiations
- Fixed SelectionTree unit tests


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D64329

Files:
  clang-tools-extra/clangd/Selection.cpp
  clang-tools-extra/clangd/SourceCode.cpp
  clang-tools-extra/clangd/SourceCode.h
  clang-tools-extra/clangd/unittests/SelectionTests.cpp

Index: clang-tools-extra/clangd/unittests/SelectionTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/SelectionTests.cpp
+++ clang-tools-extra/clangd/unittests/SelectionTests.cpp
@@ -37,15 +37,13 @@
 Range nodeRange(const SelectionTree::Node *N, ParsedAST &AST) {
   if (!N)
     return Range{};
-  SourceManager &SM = AST.getSourceManager();
+  const SourceManager &SM = AST.getSourceManager();
+  const LangOptions &LangOpts = AST.getASTContext().getLangOpts();
   StringRef Buffer = SM.getBufferData(SM.getMainFileID());
   SourceRange SR = N->ASTNode.getSourceRange();
-  SR.setBegin(SM.getFileLoc(SR.getBegin()));
-  SR.setEnd(SM.getFileLoc(SR.getEnd()));
-  CharSourceRange R =
-      Lexer::getAsCharRange(SR, SM, AST.getASTContext().getLangOpts());
-  return Range{offsetToPosition(Buffer, SM.getFileOffset(R.getBegin())),
-               offsetToPosition(Buffer, SM.getFileOffset(R.getEnd()))};
+  SR = getFileRange(SR, SM, LangOpts);
+  return Range{offsetToPosition(Buffer, SM.getFileOffset(SR.getBegin())),
+               offsetToPosition(Buffer, SM.getFileOffset(SR.getEnd()))};
 }
 
 std::string nodeKind(const SelectionTree::Node *N) {
@@ -144,17 +142,17 @@
           R"cpp(
             void foo();
             #define CALL_FUNCTION(X) X()
-            void bar() [[{ CALL_FUNC^TION(fo^o); }]]
+            void bar() { [[CALL_FUNC^TION(fo^o)]]; }
           )cpp",
-          "CompoundStmt",
+          "CallExpr",
       },
       {
           R"cpp(
             void foo();
             #define CALL_FUNCTION(X) X()
-            void bar() [[{ C^ALL_FUNC^TION(foo); }]]
+            void bar() { [[C^ALL_FUNC^TION(foo)]]; }
           )cpp",
-          "CompoundStmt",
+          "CallExpr",
       },
       {
           R"cpp(
@@ -308,7 +306,7 @@
       R"cpp(
           template <class T>
           struct unique_ptr {};
-          void foo(^$C[[unique_ptr<unique_ptr<$C[[int]]>>]]^ a) {}
+          void foo(^$C[[unique_ptr<$C[[unique_ptr<$C[[int]]>]]>]]^ a) {}
       )cpp",
   };
   for (const char *C : Cases) {
Index: clang-tools-extra/clangd/SourceCode.h
===================================================================
--- clang-tools-extra/clangd/SourceCode.h
+++ clang-tools-extra/clangd/SourceCode.h
@@ -20,8 +20,8 @@
 #include "clang/Basic/SourceManager.h"
 #include "clang/Format/Format.h"
 #include "clang/Tooling/Core/Replacement.h"
-#include "llvm/ADT/StringSet.h"
 #include "llvm/ADT/StringRef.h"
+#include "llvm/ADT/StringSet.h"
 #include "llvm/Support/SHA1.h"
 
 namespace clang {
@@ -201,6 +201,9 @@
 std::vector<std::string> visibleNamespaces(llvm::StringRef Code,
                                            const format::FormatStyle &Style);
 
+// Return the FileRange for a given range where the ends can be in different
+// files. Note that the end of the FileRange is the end of the last token.
+SourceRange getFileRange(SourceRange Range, const SourceManager &SM, const LangOptions &LangOpts);
 } // namespace clangd
 } // namespace clang
 #endif
Index: clang-tools-extra/clangd/SourceCode.cpp
===================================================================
--- clang-tools-extra/clangd/SourceCode.cpp
+++ clang-tools-extra/clangd/SourceCode.cpp
@@ -12,6 +12,7 @@
 #include "Logger.h"
 #include "Protocol.h"
 #include "clang/AST/ASTContext.h"
+#include "clang/Basic/LangOptions.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Basic/TokenKinds.h"
 #include "clang/Format/Format.h"
@@ -24,6 +25,7 @@
 #include "llvm/Support/Error.h"
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/Path.h"
+#include "llvm/Support/raw_ostream.h"
 #include <algorithm>
 
 namespace clang {
@@ -444,8 +446,8 @@
 namespace {
 enum NamespaceEvent {
   BeginNamespace, // namespace <ns> {.     Payload is resolved <ns>.
-  EndNamespace,   // } // namespace <ns>.  Payload is resolved *outer* namespace.
-  UsingDirective  // using namespace <ns>. Payload is unresolved <ns>.
+  EndNamespace,  // } // namespace <ns>.  Payload is resolved *outer* namespace.
+  UsingDirective // using namespace <ns>. Payload is unresolved <ns>.
 };
 // Scans C++ source code for constructs that change the visible namespaces.
 void parseNamespaceEvents(
@@ -468,7 +470,7 @@
   std::string NSName;
 
   lex(Code, Style, [&](const clang::Token &Tok) {
-    switch(Tok.getKind()) {
+    switch (Tok.getKind()) {
     case tok::raw_identifier:
       // In raw mode, this could be a keyword or a name.
       switch (State) {
@@ -570,40 +572,37 @@
   // Map from namespace to (resolved) namespaces introduced via using directive.
   llvm::StringMap<llvm::StringSet<>> UsingDirectives;
 
-  parseNamespaceEvents(Code, Style,
-                       [&](NamespaceEvent Event, llvm::StringRef NS) {
-                         switch (Event) {
-                         case BeginNamespace:
-                         case EndNamespace:
-                           Current = NS;
-                           break;
-                         case UsingDirective:
-                           if (NS.consume_front("::"))
-                             UsingDirectives[Current].insert(NS);
-                           else {
-                             for (llvm::StringRef Enclosing :
-                                  ancestorNamespaces(Current)) {
-                               if (Enclosing.empty())
-                                 UsingDirectives[Current].insert(NS);
-                               else
-                                 UsingDirectives[Current].insert(
-                                     (Enclosing + "::" + NS).str());
-                             }
-                           }
-                           break;
-                         }
-                       });
+  parseNamespaceEvents(
+      Code, Style, [&](NamespaceEvent Event, llvm::StringRef NS) {
+        switch (Event) {
+        case BeginNamespace:
+        case EndNamespace:
+          Current = NS;
+          break;
+        case UsingDirective:
+          if (NS.consume_front("::"))
+            UsingDirectives[Current].insert(NS);
+          else {
+            for (llvm::StringRef Enclosing : ancestorNamespaces(Current)) {
+              if (Enclosing.empty())
+                UsingDirectives[Current].insert(NS);
+              else
+                UsingDirectives[Current].insert((Enclosing + "::" + NS).str());
+            }
+          }
+          break;
+        }
+      });
 
   std::vector<std::string> Found;
   for (llvm::StringRef Enclosing : ancestorNamespaces(Current)) {
     Found.push_back(Enclosing);
     auto It = UsingDirectives.find(Enclosing);
     if (It != UsingDirectives.end())
-      for (const auto& Used : It->second)
+      for (const auto &Used : It->second)
         Found.push_back(Used.getKey());
   }
 
-
   llvm::sort(Found, [&](const std::string &LHS, const std::string &RHS) {
     if (Current == RHS)
       return false;
@@ -653,5 +652,59 @@
   return Result;
 }
 
+// Returns the location of the end of the token present at a given location
+static SourceLocation getLocationAtTokenEnd(SourceLocation Loc,
+                                            const SourceManager &SM,
+                                            const LangOptions &LangOpts) {
+  unsigned TokLength = Lexer::MeasureTokenLength(Loc, SM, LangOpts);
+  return Loc.getLocWithOffset(TokLength);
+}
+
+// Finds the union of two ranges. If any of the ranges is a Token Range, it uses
+// the token ending.
+static CharSourceRange unionCharSourceRange(CharSourceRange R1,
+                                            CharSourceRange R2,
+                                            const SourceManager &SM,
+                                            const LangOptions &LangOpts) {
+  if (R1.isTokenRange())
+    R1.setEnd(getLocationAtTokenEnd(R1.getEnd(), SM, LangOpts));
+  if (R2.isTokenRange())
+    R2.setEnd(getLocationAtTokenEnd(R2.getEnd(), SM, LangOpts));
+  return CharSourceRange({std::min(R1.getBegin(), R2.getBegin()),
+                          std::max(R1.getEnd(), R2.getEnd())},
+                         false);
+}
+
+// returns the FileRange for a given Location as CharSourceRange. Note that the
+// end of the FileRange is the end of the last token.
+static CharSourceRange getFileRange(SourceLocation Loc, const SourceManager &SM,
+                             const LangOptions &LangOpts) {
+  if (Loc.isFileID())
+    return {{Loc, getLocationAtTokenEnd(Loc, SM, LangOpts)}, false};
+  CharSourceRange FileRange({Loc}, false);
+  do {
+    if (SM.isMacroArgExpansion(FileRange.getBegin())) {
+      FileRange.setBegin(SM.getImmediateSpellingLoc(FileRange.getBegin()));
+      FileRange.setEnd(getLocationAtTokenEnd(
+          SM.getImmediateSpellingLoc(FileRange.getEnd()), SM, LangOpts));
+    } else {
+      CharSourceRange ExpansionRangeForBegin =
+          SM.getImmediateExpansionRange(FileRange.getBegin());
+      CharSourceRange ExpansionRangeForEnd =
+          SM.getImmediateExpansionRange(FileRange.getEnd());
+      FileRange = unionCharSourceRange(ExpansionRangeForBegin,
+                                       ExpansionRangeForEnd, SM, LangOpts);
+    }
+  } while (!FileRange.getBegin().isFileID());
+  return FileRange;
+}
+
+SourceRange getFileRange(SourceRange Range, const SourceManager &SM,
+                         const LangOptions &LangOpts) {
+  CharSourceRange R1 = getFileRange(Range.getBegin(), SM, LangOpts);
+  CharSourceRange R2 = getFileRange(Range.getEnd(), SM, LangOpts);
+  return unionCharSourceRange(R1, R2, SM, LangOpts).getAsRange();
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/Selection.cpp
===================================================================
--- clang-tools-extra/clangd/Selection.cpp
+++ clang-tools-extra/clangd/Selection.cpp
@@ -8,10 +8,13 @@
 
 #include "Selection.h"
 #include "ClangdUnit.h"
+#include "SourceCode.h"
 #include "clang/AST/ASTTypeTraits.h"
 #include "clang/AST/PrettyPrinter.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/TypeLoc.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Basic/TokenKinds.h"
 #include "llvm/ADT/STLExtras.h"
 #include <algorithm>
 
@@ -68,8 +71,8 @@
 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, unsigned Begin,
-                                  unsigned End, FileID File) {
+  static std::deque<Node> collect(ASTContext &AST, unsigned Begin, unsigned End,
+                                  FileID File) {
     SelectionVisitor V(AST, Begin, End, File);
     V.TraverseAST(AST);
     assert(V.Stack.size() == 1 && "Unpaired push/pop?");
@@ -205,6 +208,7 @@
   // Performs primary hit detection.
   void pop() {
     Node &N = *Stack.top();
+    N.ASTNode.dump(llvm::errs(), SM);
     if (auto Sel = claimRange(N.ASTNode.getSourceRange()))
       N.Selected = Sel;
     if (N.Selected || !N.Children.empty()) {
@@ -236,39 +240,42 @@
   // Perform hit-testing of a complete Node against the selection.
   // This runs for every node in the AST, and must be fast in common cases.
   // This is usually called from pop(), so we can take children into account.
+  // FIXME: Doesn't select the binary operator node in
+  //          #define FOO(X) X + 1
+  //          int a, b = [[FOO(a)]];
   SelectionTree::Selection claimRange(SourceRange S) {
+    llvm::errs() << "S: "; S.dump(SM);
     if (!S.isValid())
       return SelectionTree::Unselected;
-    // getTopMacroCallerLoc() allows selection of constructs in macro args. e.g:
+    // getFileRange() allows selecting macro arg expansions
     //   #define LOOP_FOREVER(Body) for(;;) { Body }
     //   void IncrementLots(int &x) {
     //     LOOP_FOREVER( ++x; )
     //   }
     // Selecting "++x" or "x" will do the right thing.
-    auto B = SM.getDecomposedLoc(SM.getTopMacroCallerLoc(S.getBegin()));
-    auto E = SM.getDecomposedLoc(SM.getTopMacroCallerLoc(S.getEnd()));
+    // The end of the range returned by getFileRange is the ending of last token
+    SourceRange Range = getFileRange(S, SM, LangOpts);
+    auto B = SM.getDecomposedLoc(Range.getBegin());
+    auto E = SM.getDecomposedLoc(Range.getEnd());
     // Otherwise, nodes in macro expansions can't be selected.
     if (B.first != SelFile || E.first != SelFile)
       return SelectionTree::Unselected;
     // Cheap test: is there any overlap at all between the selection and range?
-    // Note that E.second is the *start* of the last token, which is why we
-    // compare against the "rounded-down" SelBegin.
-    if (B.second >= SelEnd || E.second < SelBeginTokenStart)
+    if (B.second >= SelEnd || E.second < SelBegin)
       return SelectionTree::Unselected;
-
     // We may have hit something, need some more precise checks.
-    // Adjust [B, E) to be a half-open character range.
-    E.second += Lexer::MeasureTokenLength(S.getEnd(), SM, LangOpts);
     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;
+    llvm::errs() << "HERE1\n";
     // 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;
+    llvm::errs() << "HERE2\n";
     // 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)
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to