Revert "[clangd] Implement rename by using SelectionTree and findExplicitReferences."
This reverts commit 4f80fc2491.
Caused buildbot failure at
http://lab.llvm.org:8011/builders/llvm-clang-lld-x86_64-scei-ps4-ubuntu-fast/builds/58251
			
			
This commit is contained in:
		
							parent
							
								
									327904d3cf
								
							
						
					
					
						commit
						f805c60a09
					
				| 
						 | 
				
			
			@ -8,16 +8,14 @@
 | 
			
		|||
 | 
			
		||||
#include "refactor/Rename.h"
 | 
			
		||||
#include "AST.h"
 | 
			
		||||
#include "FindTarget.h"
 | 
			
		||||
#include "Logger.h"
 | 
			
		||||
#include "ParsedAST.h"
 | 
			
		||||
#include "Selection.h"
 | 
			
		||||
#include "SourceCode.h"
 | 
			
		||||
#include "index/SymbolCollector.h"
 | 
			
		||||
#include "clang/AST/DeclCXX.h"
 | 
			
		||||
#include "clang/AST/DeclTemplate.h"
 | 
			
		||||
#include "clang/Basic/SourceLocation.h"
 | 
			
		||||
#include "clang/Tooling/Refactoring/Rename/RenamingAction.h"
 | 
			
		||||
#include "clang/Tooling/Refactoring/Rename/USRFinder.h"
 | 
			
		||||
#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
 | 
			
		||||
#include "clang/Tooling/Refactoring/Rename/USRLocFinder.h"
 | 
			
		||||
 | 
			
		||||
namespace clang {
 | 
			
		||||
namespace clangd {
 | 
			
		||||
| 
						 | 
				
			
			@ -36,17 +34,6 @@ llvm::Optional<std::string> filePath(const SymbolLocation &Loc,
 | 
			
		|||
  return *Path;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns true if the given location is expanded from any macro body.
 | 
			
		||||
bool isInMacroBody(const SourceManager &SM, SourceLocation Loc) {
 | 
			
		||||
  while (Loc.isMacroID()) {
 | 
			
		||||
    if (SM.isMacroBodyExpansion(Loc))
 | 
			
		||||
      return true;
 | 
			
		||||
    Loc = SM.getImmediateMacroCallerLoc(Loc);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Query the index to find some other files where the Decl is referenced.
 | 
			
		||||
llvm::Optional<std::string> getOtherRefFile(const Decl &D, StringRef MainFile,
 | 
			
		||||
                                            const SymbolIndex &Index) {
 | 
			
		||||
| 
						 | 
				
			
			@ -69,41 +56,12 @@ llvm::Optional<std::string> getOtherRefFile(const Decl &D, StringRef MainFile,
 | 
			
		|||
  return OtherFile;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
llvm::DenseSet<const Decl *> locateDeclAt(ParsedAST &AST,
 | 
			
		||||
                                          SourceLocation TokenStartLoc) {
 | 
			
		||||
  unsigned Offset =
 | 
			
		||||
      AST.getSourceManager().getDecomposedSpellingLoc(TokenStartLoc).second;
 | 
			
		||||
 | 
			
		||||
  SelectionTree Selection(AST.getASTContext(), AST.getTokens(), Offset);
 | 
			
		||||
  const SelectionTree::Node *SelectedNode = Selection.commonAncestor();
 | 
			
		||||
  if (!SelectedNode)
 | 
			
		||||
    return {};
 | 
			
		||||
 | 
			
		||||
  // If the location points to a Decl, we check it is actually on the name
 | 
			
		||||
  // range of the Decl. This would avoid allowing rename on unrelated tokens.
 | 
			
		||||
  //   ^class Foo {} // SelectionTree returns CXXRecordDecl,
 | 
			
		||||
  //                 // we don't attempt to trigger rename on this position.
 | 
			
		||||
  // FIXME: make this work on destructors, e.g. "~F^oo()".
 | 
			
		||||
  if (const auto *D = SelectedNode->ASTNode.get<Decl>()) {
 | 
			
		||||
    if (D->getLocation() != TokenStartLoc)
 | 
			
		||||
      return {};
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  llvm::DenseSet<const Decl *> Result;
 | 
			
		||||
  for (const auto *D :
 | 
			
		||||
       targetDecl(SelectedNode->ASTNode,
 | 
			
		||||
                  DeclRelation::Alias | DeclRelation::TemplatePattern))
 | 
			
		||||
    Result.insert(D);
 | 
			
		||||
  return Result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum ReasonToReject {
 | 
			
		||||
  NoSymbolFound,
 | 
			
		||||
  NoIndexProvided,
 | 
			
		||||
  NonIndexable,
 | 
			
		||||
  UsedOutsideFile,
 | 
			
		||||
  UnsupportedSymbol,
 | 
			
		||||
  AmbiguousSymbol,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// Check the symbol Decl is renameable (per the index) within the file.
 | 
			
		||||
| 
						 | 
				
			
			@ -167,8 +125,6 @@ llvm::Error makeError(ReasonToReject Reason) {
 | 
			
		|||
      return "symbol may be used in other files (not eligible for indexing)";
 | 
			
		||||
    case UnsupportedSymbol:
 | 
			
		||||
      return "symbol is not a supported kind (e.g. namespace, macro)";
 | 
			
		||||
    case AmbiguousSymbol:
 | 
			
		||||
      return "there are multiple symbols at the given location";
 | 
			
		||||
    }
 | 
			
		||||
    llvm_unreachable("unhandled reason kind");
 | 
			
		||||
  };
 | 
			
		||||
| 
						 | 
				
			
			@ -178,38 +134,22 @@ llvm::Error makeError(ReasonToReject Reason) {
 | 
			
		|||
}
 | 
			
		||||
 | 
			
		||||
// Return all rename occurrences in the main file.
 | 
			
		||||
std::vector<SourceLocation> findOccurrencesWithinFile(ParsedAST &AST,
 | 
			
		||||
                                                      const NamedDecl &ND) {
 | 
			
		||||
  // In theory, locateDeclAt should return the primary template. However, if the
 | 
			
		||||
  // cursor is under the underlying CXXRecordDecl of the ClassTemplateDecl, ND
 | 
			
		||||
  // will be the CXXRecordDecl, for this case, we need to get the primary
 | 
			
		||||
  // template maunally.
 | 
			
		||||
  const auto &RenameDecl =
 | 
			
		||||
      ND.getDescribedTemplate() ? *ND.getDescribedTemplate() : ND;
 | 
			
		||||
  // getUSRsForDeclaration will find other related symbols, e.g. virtual and its
 | 
			
		||||
  // overriddens, primary template and all explicit specializations.
 | 
			
		||||
  // FIXME: get rid of the remaining tooling APIs.
 | 
			
		||||
  std::vector<std::string> RenameUSRs = tooling::getUSRsForDeclaration(
 | 
			
		||||
      tooling::getCanonicalSymbolDeclaration(&RenameDecl), AST.getASTContext());
 | 
			
		||||
  llvm::DenseSet<SymbolID> TargetIDs;
 | 
			
		||||
  for (auto &USR : RenameUSRs)
 | 
			
		||||
    TargetIDs.insert(SymbolID(USR));
 | 
			
		||||
 | 
			
		||||
  std::vector<SourceLocation> Results;
 | 
			
		||||
tooling::SymbolOccurrences
 | 
			
		||||
findOccurrencesWithinFile(ParsedAST &AST, const NamedDecl *RenameDecl) {
 | 
			
		||||
  const NamedDecl *CanonicalRenameDecl =
 | 
			
		||||
      tooling::getCanonicalSymbolDeclaration(RenameDecl);
 | 
			
		||||
  assert(CanonicalRenameDecl && "RenameDecl must be not null");
 | 
			
		||||
  std::vector<std::string> RenameUSRs =
 | 
			
		||||
      tooling::getUSRsForDeclaration(CanonicalRenameDecl, AST.getASTContext());
 | 
			
		||||
  std::string OldName = CanonicalRenameDecl->getNameAsString();
 | 
			
		||||
  tooling::SymbolOccurrences Result;
 | 
			
		||||
  for (Decl *TopLevelDecl : AST.getLocalTopLevelDecls()) {
 | 
			
		||||
    findExplicitReferences(TopLevelDecl, [&](ReferenceLoc Ref) {
 | 
			
		||||
      if (Ref.Targets.empty())
 | 
			
		||||
        return;
 | 
			
		||||
      for (const auto *Target : Ref.Targets) {
 | 
			
		||||
        auto ID = getSymbolID(Target);
 | 
			
		||||
        if (!ID || TargetIDs.find(*ID) == TargetIDs.end())
 | 
			
		||||
          return;
 | 
			
		||||
      }
 | 
			
		||||
      Results.push_back(Ref.NameLoc);
 | 
			
		||||
    });
 | 
			
		||||
    tooling::SymbolOccurrences RenameInDecl =
 | 
			
		||||
        tooling::getOccurrencesOfUSRs(RenameUSRs, OldName, TopLevelDecl);
 | 
			
		||||
    Result.insert(Result.end(), std::make_move_iterator(RenameInDecl.begin()),
 | 
			
		||||
                  std::make_move_iterator(RenameInDecl.end()));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return Results;
 | 
			
		||||
  return Result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
| 
						 | 
				
			
			@ -225,41 +165,30 @@ renameWithinFile(ParsedAST &AST, llvm::StringRef File, Position Pos,
 | 
			
		|||
  if (locateMacroAt(SourceLocationBeg, AST.getPreprocessor()))
 | 
			
		||||
    return makeError(UnsupportedSymbol);
 | 
			
		||||
 | 
			
		||||
  auto DeclsUnderCursor = locateDeclAt(AST, SourceLocationBeg);
 | 
			
		||||
  if (DeclsUnderCursor.empty())
 | 
			
		||||
    return makeError(NoSymbolFound);
 | 
			
		||||
  if (DeclsUnderCursor.size() > 1)
 | 
			
		||||
    return makeError(AmbiguousSymbol);
 | 
			
		||||
 | 
			
		||||
  const auto *RenameDecl = llvm::dyn_cast<NamedDecl>(*DeclsUnderCursor.begin());
 | 
			
		||||
  const auto *RenameDecl =
 | 
			
		||||
      tooling::getNamedDeclAt(AST.getASTContext(), SourceLocationBeg);
 | 
			
		||||
  if (!RenameDecl)
 | 
			
		||||
    return makeError(UnsupportedSymbol);
 | 
			
		||||
    return makeError(NoSymbolFound);
 | 
			
		||||
 | 
			
		||||
  if (auto Reject =
 | 
			
		||||
          renamableWithinFile(*RenameDecl->getCanonicalDecl(), File, Index))
 | 
			
		||||
    return makeError(*Reject);
 | 
			
		||||
 | 
			
		||||
  // Rename sometimes returns duplicate edits (which is a bug). A side-effect of
 | 
			
		||||
  // adding them to a single Replacements object is these are deduplicated.
 | 
			
		||||
  tooling::Replacements FilteredChanges;
 | 
			
		||||
  for (SourceLocation Loc : findOccurrencesWithinFile(AST, *RenameDecl)) {
 | 
			
		||||
    SourceLocation RenameLoc = Loc;
 | 
			
		||||
    // We don't rename in any macro bodies, but we allow rename the symbol
 | 
			
		||||
    // spelled in a top-level macro argument in the main file.
 | 
			
		||||
    if (RenameLoc.isMacroID()) {
 | 
			
		||||
      if (isInMacroBody(SM, RenameLoc))
 | 
			
		||||
        continue;
 | 
			
		||||
      RenameLoc = SM.getSpellingLoc(Loc);
 | 
			
		||||
    }
 | 
			
		||||
    // Filter out locations not from main file.
 | 
			
		||||
    // We traverse only main file decls, but locations could come from an
 | 
			
		||||
    // non-preamble #include file e.g.
 | 
			
		||||
    //   void test() {
 | 
			
		||||
    //     int f^oo;
 | 
			
		||||
    //     #include "use_foo.inc"
 | 
			
		||||
    //   }
 | 
			
		||||
    if (!isInsideMainFile(RenameLoc, SM))
 | 
			
		||||
  for (const tooling::SymbolOccurrence &Rename :
 | 
			
		||||
       findOccurrencesWithinFile(AST, RenameDecl)) {
 | 
			
		||||
    // Currently, we only support normal rename (one range) for C/C++.
 | 
			
		||||
    // FIXME: support multiple-range rename for objective-c methods.
 | 
			
		||||
    if (Rename.getNameRanges().size() > 1)
 | 
			
		||||
      continue;
 | 
			
		||||
    // We shouldn't have conflicting replacements. If there are conflicts, it
 | 
			
		||||
    // means that we have bugs either in clangd or in Rename library, therefore
 | 
			
		||||
    // we refuse to perform the rename.
 | 
			
		||||
    if (auto Err = FilteredChanges.add(tooling::Replacement(
 | 
			
		||||
            SM, CharSourceRange::getTokenRange(RenameLoc), NewName)))
 | 
			
		||||
            AST.getASTContext().getSourceManager(),
 | 
			
		||||
            CharSourceRange::getCharRange(Rename.getNameRanges()[0]), NewName)))
 | 
			
		||||
      return std::move(Err);
 | 
			
		||||
  }
 | 
			
		||||
  return FilteredChanges;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -38,27 +38,27 @@ std::string expectedResult(Annotations Test, llvm::StringRef NewName) {
 | 
			
		|||
  return Result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(RenameTest, WithinFileRename) {
 | 
			
		||||
  // rename is runnning on all "^" points, and "[[]]" ranges point to the
 | 
			
		||||
TEST(RenameTest, SingleFile) {
 | 
			
		||||
  // "^" points to the position of the rename, and "[[]]" ranges point to the
 | 
			
		||||
  // identifier that is being renamed.
 | 
			
		||||
  llvm::StringRef Tests[] = {
 | 
			
		||||
      // Function.
 | 
			
		||||
      // Rename function.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        void [[foo^]]() {
 | 
			
		||||
        void [[foo]]() {
 | 
			
		||||
          [[fo^o]]();
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Type.
 | 
			
		||||
      // Rename type.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        struct [[foo^]] {};
 | 
			
		||||
        struct [[foo]]{};
 | 
			
		||||
        [[foo]] test() {
 | 
			
		||||
           [[f^oo]] x;
 | 
			
		||||
           return x;
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Local variable.
 | 
			
		||||
      // Rename variable.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        void bar() {
 | 
			
		||||
          if (auto [[^foo]] = 5) {
 | 
			
		||||
| 
						 | 
				
			
			@ -66,313 +66,18 @@ TEST(RenameTest, WithinFileRename) {
 | 
			
		|||
          }
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Rename class, including constructor/destructor.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        class [[F^oo]] {
 | 
			
		||||
          [[F^oo]]();
 | 
			
		||||
          ~[[Foo]]();
 | 
			
		||||
          void foo(int x);
 | 
			
		||||
        };
 | 
			
		||||
        [[Foo]]::[[Fo^o]]() {}
 | 
			
		||||
        void [[Foo]]::foo(int x) {}
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Class in template argument.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        class [[F^oo]] {};
 | 
			
		||||
        template <typename T> void func();
 | 
			
		||||
        template <typename T> class Baz {};
 | 
			
		||||
        int main() {
 | 
			
		||||
          func<[[F^oo]]>();
 | 
			
		||||
          Baz<[[F^oo]]> obj;
 | 
			
		||||
          return 0;
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Forward class declaration without definition.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        class [[F^oo]];
 | 
			
		||||
        [[Foo]] *f();
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Class methods overrides.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        struct A {
 | 
			
		||||
         virtual void [[f^oo]]() {}
 | 
			
		||||
        };
 | 
			
		||||
        struct B : A {
 | 
			
		||||
          void [[f^oo]]() override {}
 | 
			
		||||
        };
 | 
			
		||||
        struct C : B {
 | 
			
		||||
          void [[f^oo]]() override {}
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        void func() {
 | 
			
		||||
          A().[[f^oo]]();
 | 
			
		||||
          B().[[f^oo]]();
 | 
			
		||||
          C().[[f^oo]]();
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Template class (partial) specializations.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        template <typename T>
 | 
			
		||||
        class [[F^oo]] {};
 | 
			
		||||
 | 
			
		||||
        template<>
 | 
			
		||||
        class [[F^oo]]<bool> {};
 | 
			
		||||
        template <typename T>
 | 
			
		||||
        class [[F^oo]]<T*> {};
 | 
			
		||||
 | 
			
		||||
        void test() {
 | 
			
		||||
          [[Foo]]<int> x;
 | 
			
		||||
          [[Foo]]<bool> y;
 | 
			
		||||
          [[Foo]]<int*> z;
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Template class instantiations.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        template <typename T>
 | 
			
		||||
        class [[F^oo]] {
 | 
			
		||||
        public:
 | 
			
		||||
          T foo(T arg, T& ref, T* ptr) {
 | 
			
		||||
            T value;
 | 
			
		||||
            int number = 42;
 | 
			
		||||
            value = (T)number;
 | 
			
		||||
            value = static_cast<T>(number);
 | 
			
		||||
            return value;
 | 
			
		||||
          }
 | 
			
		||||
          static void foo(T value) {}
 | 
			
		||||
          T member;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        template <typename T>
 | 
			
		||||
        void func() {
 | 
			
		||||
          [[F^oo]]<T> obj;
 | 
			
		||||
          obj.member = T();
 | 
			
		||||
          [[Foo]]<T>::foo();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void test() {
 | 
			
		||||
          [[F^oo]]<int> i;
 | 
			
		||||
          i.member = 0;
 | 
			
		||||
          [[F^oo]]<int>::foo(0);
 | 
			
		||||
 | 
			
		||||
          [[F^oo]]<bool> b;
 | 
			
		||||
          b.member = false;
 | 
			
		||||
          [[Foo]]<bool>::foo(false);
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Template class methods.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        template <typename T>
 | 
			
		||||
        class A {
 | 
			
		||||
        public:
 | 
			
		||||
          void [[f^oo]]() {}
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        void func() {
 | 
			
		||||
          A<int>().[[f^oo]]();
 | 
			
		||||
          A<double>().[[f^oo]]();
 | 
			
		||||
          A<float>().[[f^oo]]();
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Complicated class type.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
         // Forward declaration.
 | 
			
		||||
        class [[Fo^o]];
 | 
			
		||||
        class Baz {
 | 
			
		||||
          virtual int getValue() const = 0;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        class [[F^oo]] : public Baz  {
 | 
			
		||||
        public:
 | 
			
		||||
          [[Foo]](int value = 0) : x(value) {}
 | 
			
		||||
 | 
			
		||||
          [[Foo]] &operator++(int);
 | 
			
		||||
 | 
			
		||||
          bool operator<([[Foo]] const &rhs);
 | 
			
		||||
          int getValue() const;
 | 
			
		||||
        private:
 | 
			
		||||
          int x;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        void func() {
 | 
			
		||||
          [[Foo]] *Pointer = 0;
 | 
			
		||||
          [[Foo]] Variable = [[Foo]](10);
 | 
			
		||||
          for ([[Foo]] it; it < Variable; it++);
 | 
			
		||||
          const [[Foo]] *C = new [[Foo]]();
 | 
			
		||||
          const_cast<[[Foo]] *>(C)->getValue();
 | 
			
		||||
          [[Foo]] foo;
 | 
			
		||||
          const Baz &BazReference = foo;
 | 
			
		||||
          const Baz *BazPointer = &foo;
 | 
			
		||||
          dynamic_cast<const [[^Foo]] &>(BazReference).getValue();
 | 
			
		||||
          dynamic_cast<const [[^Foo]] *>(BazPointer)->getValue();
 | 
			
		||||
          reinterpret_cast<const [[^Foo]] *>(BazPointer)->getValue();
 | 
			
		||||
          static_cast<const [[^Foo]] &>(BazReference).getValue();
 | 
			
		||||
          static_cast<const [[^Foo]] *>(BazPointer)->getValue();
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // CXXConstructor initializer list.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        class Baz {};
 | 
			
		||||
        class Qux {
 | 
			
		||||
          Baz [[F^oo]];
 | 
			
		||||
        public:
 | 
			
		||||
          Qux();
 | 
			
		||||
        };
 | 
			
		||||
        Qux::Qux() : [[F^oo]]() {}
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // DeclRefExpr.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        class C {
 | 
			
		||||
        public:
 | 
			
		||||
          static int [[F^oo]];
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        int foo(int x);
 | 
			
		||||
        #define MACRO(a) foo(a)
 | 
			
		||||
 | 
			
		||||
        void func() {
 | 
			
		||||
          C::[[F^oo]] = 1;
 | 
			
		||||
          MACRO(C::[[Foo]]);
 | 
			
		||||
          int y = C::[[F^oo]];
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Macros.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        // no rename inside macro body.
 | 
			
		||||
        #define M1 foo
 | 
			
		||||
        #define M2(x) x
 | 
			
		||||
        int [[fo^o]]();
 | 
			
		||||
        void boo(int);
 | 
			
		||||
 | 
			
		||||
        void qoo() {
 | 
			
		||||
          [[foo]]();
 | 
			
		||||
          boo([[foo]]());
 | 
			
		||||
          M1();
 | 
			
		||||
          boo(M1());
 | 
			
		||||
          M2([[foo]]());
 | 
			
		||||
          M2(M1()); // foo is inside the nested macro body.
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // MemberExpr in macros
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        class Baz {
 | 
			
		||||
        public:
 | 
			
		||||
          int [[F^oo]];
 | 
			
		||||
        };
 | 
			
		||||
        int qux(int x);
 | 
			
		||||
        #define MACRO(a) qux(a)
 | 
			
		||||
 | 
			
		||||
        int main() {
 | 
			
		||||
          Baz baz;
 | 
			
		||||
          baz.[[Foo]] = 1;
 | 
			
		||||
          MACRO(baz.[[Foo]]);
 | 
			
		||||
          int y = baz.[[Foo]];
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Template parameters.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        template <typename [[^T]]>
 | 
			
		||||
        class Foo {
 | 
			
		||||
          [[T]] foo([[T]] arg, [[T]]& ref, [[^T]]* ptr) {
 | 
			
		||||
            [[T]] value;
 | 
			
		||||
            int number = 42;
 | 
			
		||||
            value = ([[T]])number;
 | 
			
		||||
            value = static_cast<[[^T]]>(number);
 | 
			
		||||
            return value;
 | 
			
		||||
          }
 | 
			
		||||
          static void foo([[T]] value) {}
 | 
			
		||||
          [[T]] member;
 | 
			
		||||
        };
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Typedef.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        namespace std {
 | 
			
		||||
        class basic_string {};
 | 
			
		||||
        typedef basic_string [[s^tring]];
 | 
			
		||||
        } // namespace std
 | 
			
		||||
 | 
			
		||||
        std::[[s^tring]] foo();
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Variable.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        namespace A {
 | 
			
		||||
        int [[F^oo]];
 | 
			
		||||
        }
 | 
			
		||||
        int Foo;
 | 
			
		||||
        int Qux = Foo;
 | 
			
		||||
        int Baz = A::[[^Foo]];
 | 
			
		||||
        void fun() {
 | 
			
		||||
          struct {
 | 
			
		||||
            int Foo;
 | 
			
		||||
          } b = {100};
 | 
			
		||||
          int Foo = 100;
 | 
			
		||||
          Baz = Foo;
 | 
			
		||||
          {
 | 
			
		||||
            extern int Foo;
 | 
			
		||||
            Baz = Foo;
 | 
			
		||||
            Foo = A::[[F^oo]] + Baz;
 | 
			
		||||
            A::[[Fo^o]] = b.Foo;
 | 
			
		||||
          }
 | 
			
		||||
          Foo = b.Foo;
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Namespace alias.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        namespace a { namespace b { void foo(); } }
 | 
			
		||||
        namespace [[^x]] = a::b;
 | 
			
		||||
        void bar() {
 | 
			
		||||
          [[x]]::foo();
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // Scope enums.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        enum class [[K^ind]] { ABC };
 | 
			
		||||
        void ff() {
 | 
			
		||||
          [[K^ind]] s;
 | 
			
		||||
          s = [[Kind]]::ABC;
 | 
			
		||||
        }
 | 
			
		||||
      )cpp",
 | 
			
		||||
 | 
			
		||||
      // template class in template argument list.
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        template<typename T>
 | 
			
		||||
        class [[Fo^o]] {};
 | 
			
		||||
        template <template<typename> class Z> struct Bar { };
 | 
			
		||||
        template <> struct Bar<[[Foo]]> {};
 | 
			
		||||
      )cpp",
 | 
			
		||||
  };
 | 
			
		||||
  for (const auto T : Tests) {
 | 
			
		||||
    Annotations Code(T);
 | 
			
		||||
    auto TU = TestTU::withCode(Code.code());
 | 
			
		||||
    auto AST = TU.build();
 | 
			
		||||
    EXPECT_TRUE(AST.getDiagnostics().empty())
 | 
			
		||||
        << AST.getDiagnostics().front() << Code.code();
 | 
			
		||||
 | 
			
		||||
    llvm::StringRef NewName = "abcde";
 | 
			
		||||
    for (const auto &RenamePos : Code.points()) {
 | 
			
		||||
      auto RenameResult =
 | 
			
		||||
          renameWithinFile(AST, testPath(TU.Filename), RenamePos, NewName);
 | 
			
		||||
      ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << T;
 | 
			
		||||
      auto ApplyResult = llvm::cantFail(
 | 
			
		||||
          tooling::applyAllReplacements(Code.code(), *RenameResult));
 | 
			
		||||
      EXPECT_EQ(expectedResult(Code, NewName), ApplyResult);
 | 
			
		||||
    }
 | 
			
		||||
    auto RenameResult =
 | 
			
		||||
        renameWithinFile(AST, testPath(TU.Filename), Code.point(), NewName);
 | 
			
		||||
    ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError();
 | 
			
		||||
    auto ApplyResult = llvm::cantFail(
 | 
			
		||||
        tooling::applyAllReplacements(Code.code(), *RenameResult));
 | 
			
		||||
    EXPECT_EQ(expectedResult(Code, NewName), ApplyResult);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -443,32 +148,12 @@ TEST(RenameTest, Renameable) {
 | 
			
		|||
 | 
			
		||||
      {R"cpp(// foo is declared outside the file.
 | 
			
		||||
        void fo^o() {}
 | 
			
		||||
      )cpp",
 | 
			
		||||
       "used outside main file", !HeaderFile /*cc file*/, Index},
 | 
			
		||||
      )cpp", "used outside main file", !HeaderFile /*cc file*/, Index},
 | 
			
		||||
 | 
			
		||||
      {R"cpp(
 | 
			
		||||
         // We should detect the symbol is used outside the file from the AST.
 | 
			
		||||
         void fo^o() {})cpp",
 | 
			
		||||
       "used outside main file", !HeaderFile, nullptr /*no index*/},
 | 
			
		||||
 | 
			
		||||
      {R"cpp(
 | 
			
		||||
         void foo(int);
 | 
			
		||||
         void foo(char);
 | 
			
		||||
         template <typename T> void f(T t) {
 | 
			
		||||
           fo^o(t);
 | 
			
		||||
         })cpp",
 | 
			
		||||
       "multiple symbols", !HeaderFile, nullptr /*no index*/},
 | 
			
		||||
 | 
			
		||||
      {R"cpp(// disallow rename on unrelated token.
 | 
			
		||||
         cl^ass Foo {};
 | 
			
		||||
       )cpp",
 | 
			
		||||
       "no symbol", !HeaderFile, nullptr},
 | 
			
		||||
 | 
			
		||||
      {R"cpp(// disallow rename on unrelated token.
 | 
			
		||||
         temp^late<typename T>
 | 
			
		||||
         class Foo {};
 | 
			
		||||
       )cpp",
 | 
			
		||||
       "no symbol", !HeaderFile, nullptr},
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  for (const auto& Case : Cases) {
 | 
			
		||||
| 
						 | 
				
			
			@ -506,36 +191,6 @@ TEST(RenameTest, Renameable) {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
TEST(RenameTest, MainFileReferencesOnly) {
 | 
			
		||||
  // filter out references not from main file.
 | 
			
		||||
  llvm::StringRef Test =
 | 
			
		||||
      R"cpp(
 | 
			
		||||
        void test() {
 | 
			
		||||
          int [[fo^o]] = 1;
 | 
			
		||||
          // rename references not from main file are not included.
 | 
			
		||||
          #include "foo.inc"
 | 
			
		||||
        })cpp";
 | 
			
		||||
 | 
			
		||||
  Annotations Code(Test);
 | 
			
		||||
  auto TU = TestTU::withCode(Code.code());
 | 
			
		||||
  TU.AdditionalFiles["foo.inc"] = R"cpp(
 | 
			
		||||
      #define Macro(X) X
 | 
			
		||||
      &Macro(foo);
 | 
			
		||||
      &foo;
 | 
			
		||||
    )cpp";
 | 
			
		||||
  auto AST = TU.build();
 | 
			
		||||
  EXPECT_TRUE(AST.getDiagnostics().empty())
 | 
			
		||||
      << AST.getDiagnostics().front() << Code.code();
 | 
			
		||||
  llvm::StringRef NewName = "abcde";
 | 
			
		||||
 | 
			
		||||
  auto RenameResult =
 | 
			
		||||
      renameWithinFile(AST, testPath(TU.Filename), Code.point(), NewName);
 | 
			
		||||
  ASSERT_TRUE(bool(RenameResult)) << RenameResult.takeError() << Code.point();
 | 
			
		||||
  auto ApplyResult =
 | 
			
		||||
      llvm::cantFail(tooling::applyAllReplacements(Code.code(), *RenameResult));
 | 
			
		||||
  EXPECT_EQ(expectedResult(Code, NewName), ApplyResult);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace
 | 
			
		||||
} // namespace clangd
 | 
			
		||||
} // namespace clang
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue