317 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			317 lines
		
	
	
		
			12 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- USRFindingAction.cpp - Clang refactoring library -----------------===//
 | |
| //
 | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 | |
| // See https://llvm.org/LICENSE.txt for license information.
 | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
 | |
| //
 | |
| //===----------------------------------------------------------------------===//
 | |
| ///
 | |
| /// \file
 | |
| /// Provides an action to find USR for the symbol at <offset>, as well as
 | |
| /// all additional USRs.
 | |
| ///
 | |
| //===----------------------------------------------------------------------===//
 | |
| 
 | |
| #include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"
 | |
| #include "clang/AST/AST.h"
 | |
| #include "clang/AST/ASTConsumer.h"
 | |
| #include "clang/AST/ASTContext.h"
 | |
| #include "clang/AST/Decl.h"
 | |
| #include "clang/AST/RecursiveASTVisitor.h"
 | |
| #include "clang/Basic/FileManager.h"
 | |
| #include "clang/Frontend/CompilerInstance.h"
 | |
| #include "clang/Frontend/FrontendAction.h"
 | |
| #include "clang/Lex/Lexer.h"
 | |
| #include "clang/Lex/Preprocessor.h"
 | |
| #include "clang/Tooling/CommonOptionsParser.h"
 | |
| #include "clang/Tooling/Refactoring.h"
 | |
| #include "clang/Tooling/Refactoring/Rename/USRFinder.h"
 | |
| #include "clang/Tooling/Tooling.h"
 | |
| 
 | |
| #include <algorithm>
 | |
| #include <set>
 | |
| #include <string>
 | |
| #include <vector>
 | |
| 
 | |
| using namespace llvm;
 | |
| 
 | |
| namespace clang {
 | |
| namespace tooling {
 | |
| 
 | |
| const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {
 | |
|   if (!FoundDecl)
 | |
|     return nullptr;
 | |
|   // If FoundDecl is a constructor or destructor, we want to instead take
 | |
|   // the Decl of the corresponding class.
 | |
|   if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))
 | |
|     FoundDecl = CtorDecl->getParent();
 | |
|   else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))
 | |
|     FoundDecl = DtorDecl->getParent();
 | |
|   // FIXME: (Alex L): Canonicalize implicit template instantions, just like
 | |
|   // the indexer does it.
 | |
| 
 | |
|   // Note: please update the declaration's doc comment every time the
 | |
|   // canonicalization rules are changed.
 | |
|   return FoundDecl;
 | |
| }
 | |
| 
 | |
| namespace {
 | |
| // NamedDeclFindingConsumer should delegate finding USRs of given Decl to
 | |
| // AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given
 | |
| // Decl refers to class and adds USRs of all overridden methods if Decl refers
 | |
| // to virtual method.
 | |
| class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {
 | |
| public:
 | |
|   AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)
 | |
|       : FoundDecl(FoundDecl), Context(Context) {}
 | |
| 
 | |
|   std::vector<std::string> Find() {
 | |
|     // Fill OverriddenMethods and PartialSpecs storages.
 | |
|     TraverseAST(Context);
 | |
|     if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {
 | |
|       addUSRsOfOverridenFunctions(MethodDecl);
 | |
|       for (const auto &OverriddenMethod : OverriddenMethods) {
 | |
|         if (checkIfOverriddenFunctionAscends(OverriddenMethod))
 | |
|           USRSet.insert(getUSRForDecl(OverriddenMethod));
 | |
|       }
 | |
|       addUSRsOfInstantiatedMethods(MethodDecl);
 | |
|     } else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {
 | |
|       handleCXXRecordDecl(RecordDecl);
 | |
|     } else if (const auto *TemplateDecl =
 | |
|                    dyn_cast<ClassTemplateDecl>(FoundDecl)) {
 | |
|       handleClassTemplateDecl(TemplateDecl);
 | |
|     } else if (const auto *FD = dyn_cast<FunctionDecl>(FoundDecl)) {
 | |
|       USRSet.insert(getUSRForDecl(FD));
 | |
|       if (const auto *FTD = FD->getPrimaryTemplate())
 | |
|         handleFunctionTemplateDecl(FTD);
 | |
|     } else if (const auto *FD = dyn_cast<FunctionTemplateDecl>(FoundDecl)) {
 | |
|       handleFunctionTemplateDecl(FD);
 | |
|     } else if (const auto *VTD = dyn_cast<VarTemplateDecl>(FoundDecl)) {
 | |
|       handleVarTemplateDecl(VTD);
 | |
|     } else if (const auto *VD =
 | |
|                    dyn_cast<VarTemplateSpecializationDecl>(FoundDecl)) {
 | |
|       // FIXME: figure out why FoundDecl can be a VarTemplateSpecializationDecl.
 | |
|       handleVarTemplateDecl(VD->getSpecializedTemplate());
 | |
|     } else if (const auto *VD = dyn_cast<VarDecl>(FoundDecl)) {
 | |
|       USRSet.insert(getUSRForDecl(VD));
 | |
|       if (const auto *VTD = VD->getDescribedVarTemplate())
 | |
|         handleVarTemplateDecl(VTD);
 | |
|     } else {
 | |
|       USRSet.insert(getUSRForDecl(FoundDecl));
 | |
|     }
 | |
|     return std::vector<std::string>(USRSet.begin(), USRSet.end());
 | |
|   }
 | |
| 
 | |
|   bool shouldVisitTemplateInstantiations() const { return true; }
 | |
| 
 | |
|   bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {
 | |
|     if (MethodDecl->isVirtual())
 | |
|       OverriddenMethods.push_back(MethodDecl);
 | |
|     if (MethodDecl->getInstantiatedFromMemberFunction())
 | |
|       InstantiatedMethods.push_back(MethodDecl);
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
| private:
 | |
|   void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {
 | |
|     if (!RecordDecl->getDefinition()) {
 | |
|       USRSet.insert(getUSRForDecl(RecordDecl));
 | |
|       return;
 | |
|     }
 | |
|     RecordDecl = RecordDecl->getDefinition();
 | |
|     if (const auto *ClassTemplateSpecDecl =
 | |
|             dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))
 | |
|       handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());
 | |
|     addUSRsOfCtorDtors(RecordDecl);
 | |
|   }
 | |
| 
 | |
|   void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {
 | |
|     for (const auto *Specialization : TemplateDecl->specializations())
 | |
|       addUSRsOfCtorDtors(Specialization);
 | |
|     SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;
 | |
|     TemplateDecl->getPartialSpecializations(PartialSpecs);
 | |
|     for (const auto *Spec : PartialSpecs)
 | |
|       addUSRsOfCtorDtors(Spec);
 | |
|     addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());
 | |
|   }
 | |
| 
 | |
|   void handleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) {
 | |
|     USRSet.insert(getUSRForDecl(FTD));
 | |
|     USRSet.insert(getUSRForDecl(FTD->getTemplatedDecl()));
 | |
|     for (const auto *S : FTD->specializations())
 | |
|       USRSet.insert(getUSRForDecl(S));
 | |
|   }
 | |
| 
 | |
|   void handleVarTemplateDecl(const VarTemplateDecl *VTD) {
 | |
|     USRSet.insert(getUSRForDecl(VTD));
 | |
|     USRSet.insert(getUSRForDecl(VTD->getTemplatedDecl()));
 | |
|     llvm::for_each(VTD->specializations(), [&](const auto *Spec) {
 | |
|       USRSet.insert(getUSRForDecl(Spec));
 | |
|     });
 | |
|     SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;
 | |
|     VTD->getPartialSpecializations(PartialSpecs);
 | |
|     llvm::for_each(PartialSpecs, [&](const auto *Spec) {
 | |
|       USRSet.insert(getUSRForDecl(Spec));
 | |
|     });
 | |
|   }
 | |
| 
 | |
|   void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {
 | |
|     const auto* RecordDecl = RD->getDefinition();
 | |
| 
 | |
|     // Skip if the CXXRecordDecl doesn't have definition.
 | |
|     if (!RecordDecl) {
 | |
|       USRSet.insert(getUSRForDecl(RD));
 | |
|       return;
 | |
|     }
 | |
| 
 | |
|     for (const auto *CtorDecl : RecordDecl->ctors())
 | |
|       USRSet.insert(getUSRForDecl(CtorDecl));
 | |
|     // Add template constructor decls, they are not in ctors() unfortunately.
 | |
|     if (RecordDecl->hasUserDeclaredConstructor())
 | |
|       for (const auto *D : RecordDecl->decls())
 | |
|         if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))
 | |
|           if (const auto *Ctor =
 | |
|                   dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))
 | |
|             USRSet.insert(getUSRForDecl(Ctor));
 | |
| 
 | |
|     USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));
 | |
|     USRSet.insert(getUSRForDecl(RecordDecl));
 | |
|   }
 | |
| 
 | |
|   void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {
 | |
|     USRSet.insert(getUSRForDecl(MethodDecl));
 | |
|     // Recursively visit each OverridenMethod.
 | |
|     for (const auto &OverriddenMethod : MethodDecl->overridden_methods())
 | |
|       addUSRsOfOverridenFunctions(OverriddenMethod);
 | |
|   }
 | |
| 
 | |
|   void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {
 | |
|     // For renaming a class template method, all references of the instantiated
 | |
|     // member methods should be renamed too, so add USRs of the instantiated
 | |
|     // methods to the USR set.
 | |
|     USRSet.insert(getUSRForDecl(MethodDecl));
 | |
|     if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())
 | |
|       USRSet.insert(getUSRForDecl(FT));
 | |
|     for (const auto *Method : InstantiatedMethods) {
 | |
|       if (USRSet.find(getUSRForDecl(
 | |
|               Method->getInstantiatedFromMemberFunction())) != USRSet.end())
 | |
|         USRSet.insert(getUSRForDecl(Method));
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {
 | |
|     for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {
 | |
|       if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())
 | |
|         return true;
 | |
|       return checkIfOverriddenFunctionAscends(OverriddenMethod);
 | |
|     }
 | |
|     return false;
 | |
|   }
 | |
| 
 | |
|   const Decl *FoundDecl;
 | |
|   ASTContext &Context;
 | |
|   std::set<std::string> USRSet;
 | |
|   std::vector<const CXXMethodDecl *> OverriddenMethods;
 | |
|   std::vector<const CXXMethodDecl *> InstantiatedMethods;
 | |
| };
 | |
| } // namespace
 | |
| 
 | |
| std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,
 | |
|                                                ASTContext &Context) {
 | |
|   AdditionalUSRFinder Finder(ND, Context);
 | |
|   return Finder.Find();
 | |
| }
 | |
| 
 | |
| class NamedDeclFindingConsumer : public ASTConsumer {
 | |
| public:
 | |
|   NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,
 | |
|                            ArrayRef<std::string> QualifiedNames,
 | |
|                            std::vector<std::string> &SpellingNames,
 | |
|                            std::vector<std::vector<std::string>> &USRList,
 | |
|                            bool Force, bool &ErrorOccurred)
 | |
|       : SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),
 | |
|         SpellingNames(SpellingNames), USRList(USRList), Force(Force),
 | |
|         ErrorOccurred(ErrorOccurred) {}
 | |
| 
 | |
| private:
 | |
|   bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,
 | |
|                   unsigned SymbolOffset, const std::string &QualifiedName) {
 | |
|     DiagnosticsEngine &Engine = Context.getDiagnostics();
 | |
|     const FileID MainFileID = SourceMgr.getMainFileID();
 | |
| 
 | |
|     if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {
 | |
|       ErrorOccurred = true;
 | |
|       unsigned InvalidOffset = Engine.getCustomDiagID(
 | |
|           DiagnosticsEngine::Error,
 | |
|           "SourceLocation in file %0 at offset %1 is invalid");
 | |
|       Engine.Report(SourceLocation(), InvalidOffset)
 | |
|           << SourceMgr.getFileEntryForID(MainFileID)->getName() << SymbolOffset;
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)
 | |
|                                      .getLocWithOffset(SymbolOffset);
 | |
|     const NamedDecl *FoundDecl = QualifiedName.empty()
 | |
|                                      ? getNamedDeclAt(Context, Point)
 | |
|                                      : getNamedDeclFor(Context, QualifiedName);
 | |
| 
 | |
|     if (FoundDecl == nullptr) {
 | |
|       if (QualifiedName.empty()) {
 | |
|         FullSourceLoc FullLoc(Point, SourceMgr);
 | |
|         unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(
 | |
|             DiagnosticsEngine::Error,
 | |
|             "clang-rename could not find symbol (offset %0)");
 | |
|         Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;
 | |
|         ErrorOccurred = true;
 | |
|         return false;
 | |
|       }
 | |
| 
 | |
|       if (Force) {
 | |
|         SpellingNames.push_back(std::string());
 | |
|         USRList.push_back(std::vector<std::string>());
 | |
|         return true;
 | |
|       }
 | |
| 
 | |
|       unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(
 | |
|           DiagnosticsEngine::Error, "clang-rename could not find symbol %0");
 | |
|       Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;
 | |
|       ErrorOccurred = true;
 | |
|       return false;
 | |
|     }
 | |
| 
 | |
|     FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);
 | |
|     SpellingNames.push_back(FoundDecl->getNameAsString());
 | |
|     AdditionalUSRFinder Finder(FoundDecl, Context);
 | |
|     USRList.push_back(Finder.Find());
 | |
|     return true;
 | |
|   }
 | |
| 
 | |
|   void HandleTranslationUnit(ASTContext &Context) override {
 | |
|     const SourceManager &SourceMgr = Context.getSourceManager();
 | |
|     for (unsigned Offset : SymbolOffsets) {
 | |
|       if (!FindSymbol(Context, SourceMgr, Offset, ""))
 | |
|         return;
 | |
|     }
 | |
|     for (const std::string &QualifiedName : QualifiedNames) {
 | |
|       if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))
 | |
|         return;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   ArrayRef<unsigned> SymbolOffsets;
 | |
|   ArrayRef<std::string> QualifiedNames;
 | |
|   std::vector<std::string> &SpellingNames;
 | |
|   std::vector<std::vector<std::string>> &USRList;
 | |
|   bool Force;
 | |
|   bool &ErrorOccurred;
 | |
| };
 | |
| 
 | |
| std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {
 | |
|   return std::make_unique<NamedDeclFindingConsumer>(
 | |
|       SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,
 | |
|       ErrorOccurred);
 | |
| }
 | |
| 
 | |
| } // end namespace tooling
 | |
| } // end namespace clang
 |