Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Rename/USRFindingAction.cpp
35295 views
//===--- USRFindingAction.cpp - Clang refactoring library -----------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//7///8/// \file9/// Provides an action to find USR for the symbol at <offset>, as well as10/// all additional USRs.11///12//===----------------------------------------------------------------------===//1314#include "clang/Tooling/Refactoring/Rename/USRFindingAction.h"15#include "clang/AST/AST.h"16#include "clang/AST/ASTConsumer.h"17#include "clang/AST/ASTContext.h"18#include "clang/AST/Decl.h"19#include "clang/AST/RecursiveASTVisitor.h"20#include "clang/Basic/FileManager.h"21#include "clang/Frontend/CompilerInstance.h"22#include "clang/Frontend/FrontendAction.h"23#include "clang/Lex/Lexer.h"24#include "clang/Lex/Preprocessor.h"25#include "clang/Tooling/CommonOptionsParser.h"26#include "clang/Tooling/Refactoring.h"27#include "clang/Tooling/Refactoring/Rename/USRFinder.h"28#include "clang/Tooling/Tooling.h"2930#include <algorithm>31#include <set>32#include <string>33#include <vector>3435using namespace llvm;3637namespace clang {38namespace tooling {3940const NamedDecl *getCanonicalSymbolDeclaration(const NamedDecl *FoundDecl) {41if (!FoundDecl)42return nullptr;43// If FoundDecl is a constructor or destructor, we want to instead take44// the Decl of the corresponding class.45if (const auto *CtorDecl = dyn_cast<CXXConstructorDecl>(FoundDecl))46FoundDecl = CtorDecl->getParent();47else if (const auto *DtorDecl = dyn_cast<CXXDestructorDecl>(FoundDecl))48FoundDecl = DtorDecl->getParent();49// FIXME: (Alex L): Canonicalize implicit template instantions, just like50// the indexer does it.5152// Note: please update the declaration's doc comment every time the53// canonicalization rules are changed.54return FoundDecl;55}5657namespace {58// NamedDeclFindingConsumer should delegate finding USRs of given Decl to59// AdditionalUSRFinder. AdditionalUSRFinder adds USRs of ctor and dtor if given60// Decl refers to class and adds USRs of all overridden methods if Decl refers61// to virtual method.62class AdditionalUSRFinder : public RecursiveASTVisitor<AdditionalUSRFinder> {63public:64AdditionalUSRFinder(const Decl *FoundDecl, ASTContext &Context)65: FoundDecl(FoundDecl), Context(Context) {}6667std::vector<std::string> Find() {68// Fill OverriddenMethods and PartialSpecs storages.69TraverseAST(Context);70if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FoundDecl)) {71addUSRsOfOverridenFunctions(MethodDecl);72for (const auto &OverriddenMethod : OverriddenMethods) {73if (checkIfOverriddenFunctionAscends(OverriddenMethod))74USRSet.insert(getUSRForDecl(OverriddenMethod));75}76addUSRsOfInstantiatedMethods(MethodDecl);77} else if (const auto *RecordDecl = dyn_cast<CXXRecordDecl>(FoundDecl)) {78handleCXXRecordDecl(RecordDecl);79} else if (const auto *TemplateDecl =80dyn_cast<ClassTemplateDecl>(FoundDecl)) {81handleClassTemplateDecl(TemplateDecl);82} else if (const auto *FD = dyn_cast<FunctionDecl>(FoundDecl)) {83USRSet.insert(getUSRForDecl(FD));84if (const auto *FTD = FD->getPrimaryTemplate())85handleFunctionTemplateDecl(FTD);86} else if (const auto *FD = dyn_cast<FunctionTemplateDecl>(FoundDecl)) {87handleFunctionTemplateDecl(FD);88} else if (const auto *VTD = dyn_cast<VarTemplateDecl>(FoundDecl)) {89handleVarTemplateDecl(VTD);90} else if (const auto *VD =91dyn_cast<VarTemplateSpecializationDecl>(FoundDecl)) {92// FIXME: figure out why FoundDecl can be a VarTemplateSpecializationDecl.93handleVarTemplateDecl(VD->getSpecializedTemplate());94} else if (const auto *VD = dyn_cast<VarDecl>(FoundDecl)) {95USRSet.insert(getUSRForDecl(VD));96if (const auto *VTD = VD->getDescribedVarTemplate())97handleVarTemplateDecl(VTD);98} else {99USRSet.insert(getUSRForDecl(FoundDecl));100}101return std::vector<std::string>(USRSet.begin(), USRSet.end());102}103104bool shouldVisitTemplateInstantiations() const { return true; }105106bool VisitCXXMethodDecl(const CXXMethodDecl *MethodDecl) {107if (MethodDecl->isVirtual())108OverriddenMethods.push_back(MethodDecl);109if (MethodDecl->getInstantiatedFromMemberFunction())110InstantiatedMethods.push_back(MethodDecl);111return true;112}113114private:115void handleCXXRecordDecl(const CXXRecordDecl *RecordDecl) {116if (!RecordDecl->getDefinition()) {117USRSet.insert(getUSRForDecl(RecordDecl));118return;119}120RecordDecl = RecordDecl->getDefinition();121if (const auto *ClassTemplateSpecDecl =122dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl))123handleClassTemplateDecl(ClassTemplateSpecDecl->getSpecializedTemplate());124addUSRsOfCtorDtors(RecordDecl);125}126127void handleClassTemplateDecl(const ClassTemplateDecl *TemplateDecl) {128for (const auto *Specialization : TemplateDecl->specializations())129addUSRsOfCtorDtors(Specialization);130SmallVector<ClassTemplatePartialSpecializationDecl *, 4> PartialSpecs;131TemplateDecl->getPartialSpecializations(PartialSpecs);132for (const auto *Spec : PartialSpecs)133addUSRsOfCtorDtors(Spec);134addUSRsOfCtorDtors(TemplateDecl->getTemplatedDecl());135}136137void handleFunctionTemplateDecl(const FunctionTemplateDecl *FTD) {138USRSet.insert(getUSRForDecl(FTD));139USRSet.insert(getUSRForDecl(FTD->getTemplatedDecl()));140for (const auto *S : FTD->specializations())141USRSet.insert(getUSRForDecl(S));142}143144void handleVarTemplateDecl(const VarTemplateDecl *VTD) {145USRSet.insert(getUSRForDecl(VTD));146USRSet.insert(getUSRForDecl(VTD->getTemplatedDecl()));147for (const auto *Spec : VTD->specializations())148USRSet.insert(getUSRForDecl(Spec));149SmallVector<VarTemplatePartialSpecializationDecl *, 4> PartialSpecs;150VTD->getPartialSpecializations(PartialSpecs);151for (const auto *Spec : PartialSpecs)152USRSet.insert(getUSRForDecl(Spec));153}154155void addUSRsOfCtorDtors(const CXXRecordDecl *RD) {156const auto* RecordDecl = RD->getDefinition();157158// Skip if the CXXRecordDecl doesn't have definition.159if (!RecordDecl) {160USRSet.insert(getUSRForDecl(RD));161return;162}163164for (const auto *CtorDecl : RecordDecl->ctors())165USRSet.insert(getUSRForDecl(CtorDecl));166// Add template constructor decls, they are not in ctors() unfortunately.167if (RecordDecl->hasUserDeclaredConstructor())168for (const auto *D : RecordDecl->decls())169if (const auto *FTD = dyn_cast<FunctionTemplateDecl>(D))170if (const auto *Ctor =171dyn_cast<CXXConstructorDecl>(FTD->getTemplatedDecl()))172USRSet.insert(getUSRForDecl(Ctor));173174USRSet.insert(getUSRForDecl(RecordDecl->getDestructor()));175USRSet.insert(getUSRForDecl(RecordDecl));176}177178void addUSRsOfOverridenFunctions(const CXXMethodDecl *MethodDecl) {179USRSet.insert(getUSRForDecl(MethodDecl));180// Recursively visit each OverridenMethod.181for (const auto &OverriddenMethod : MethodDecl->overridden_methods())182addUSRsOfOverridenFunctions(OverriddenMethod);183}184185void addUSRsOfInstantiatedMethods(const CXXMethodDecl *MethodDecl) {186// For renaming a class template method, all references of the instantiated187// member methods should be renamed too, so add USRs of the instantiated188// methods to the USR set.189USRSet.insert(getUSRForDecl(MethodDecl));190if (const auto *FT = MethodDecl->getInstantiatedFromMemberFunction())191USRSet.insert(getUSRForDecl(FT));192for (const auto *Method : InstantiatedMethods) {193if (USRSet.find(getUSRForDecl(194Method->getInstantiatedFromMemberFunction())) != USRSet.end())195USRSet.insert(getUSRForDecl(Method));196}197}198199bool checkIfOverriddenFunctionAscends(const CXXMethodDecl *MethodDecl) {200for (const auto &OverriddenMethod : MethodDecl->overridden_methods()) {201if (USRSet.find(getUSRForDecl(OverriddenMethod)) != USRSet.end())202return true;203return checkIfOverriddenFunctionAscends(OverriddenMethod);204}205return false;206}207208const Decl *FoundDecl;209ASTContext &Context;210std::set<std::string> USRSet;211std::vector<const CXXMethodDecl *> OverriddenMethods;212std::vector<const CXXMethodDecl *> InstantiatedMethods;213};214} // namespace215216std::vector<std::string> getUSRsForDeclaration(const NamedDecl *ND,217ASTContext &Context) {218AdditionalUSRFinder Finder(ND, Context);219return Finder.Find();220}221222class NamedDeclFindingConsumer : public ASTConsumer {223public:224NamedDeclFindingConsumer(ArrayRef<unsigned> SymbolOffsets,225ArrayRef<std::string> QualifiedNames,226std::vector<std::string> &SpellingNames,227std::vector<std::vector<std::string>> &USRList,228bool Force, bool &ErrorOccurred)229: SymbolOffsets(SymbolOffsets), QualifiedNames(QualifiedNames),230SpellingNames(SpellingNames), USRList(USRList), Force(Force),231ErrorOccurred(ErrorOccurred) {}232233private:234bool FindSymbol(ASTContext &Context, const SourceManager &SourceMgr,235unsigned SymbolOffset, const std::string &QualifiedName) {236DiagnosticsEngine &Engine = Context.getDiagnostics();237const FileID MainFileID = SourceMgr.getMainFileID();238239if (SymbolOffset >= SourceMgr.getFileIDSize(MainFileID)) {240ErrorOccurred = true;241unsigned InvalidOffset = Engine.getCustomDiagID(242DiagnosticsEngine::Error,243"SourceLocation in file %0 at offset %1 is invalid");244Engine.Report(SourceLocation(), InvalidOffset)245<< SourceMgr.getFileEntryRefForID(MainFileID)->getName()246<< SymbolOffset;247return false;248}249250const SourceLocation Point = SourceMgr.getLocForStartOfFile(MainFileID)251.getLocWithOffset(SymbolOffset);252const NamedDecl *FoundDecl = QualifiedName.empty()253? getNamedDeclAt(Context, Point)254: getNamedDeclFor(Context, QualifiedName);255256if (FoundDecl == nullptr) {257if (QualifiedName.empty()) {258FullSourceLoc FullLoc(Point, SourceMgr);259unsigned CouldNotFindSymbolAt = Engine.getCustomDiagID(260DiagnosticsEngine::Error,261"clang-rename could not find symbol (offset %0)");262Engine.Report(Point, CouldNotFindSymbolAt) << SymbolOffset;263ErrorOccurred = true;264return false;265}266267if (Force) {268SpellingNames.push_back(std::string());269USRList.push_back(std::vector<std::string>());270return true;271}272273unsigned CouldNotFindSymbolNamed = Engine.getCustomDiagID(274DiagnosticsEngine::Error, "clang-rename could not find symbol %0");275Engine.Report(CouldNotFindSymbolNamed) << QualifiedName;276ErrorOccurred = true;277return false;278}279280FoundDecl = getCanonicalSymbolDeclaration(FoundDecl);281SpellingNames.push_back(FoundDecl->getNameAsString());282AdditionalUSRFinder Finder(FoundDecl, Context);283USRList.push_back(Finder.Find());284return true;285}286287void HandleTranslationUnit(ASTContext &Context) override {288const SourceManager &SourceMgr = Context.getSourceManager();289for (unsigned Offset : SymbolOffsets) {290if (!FindSymbol(Context, SourceMgr, Offset, ""))291return;292}293for (const std::string &QualifiedName : QualifiedNames) {294if (!FindSymbol(Context, SourceMgr, 0, QualifiedName))295return;296}297}298299ArrayRef<unsigned> SymbolOffsets;300ArrayRef<std::string> QualifiedNames;301std::vector<std::string> &SpellingNames;302std::vector<std::vector<std::string>> &USRList;303bool Force;304bool &ErrorOccurred;305};306307std::unique_ptr<ASTConsumer> USRFindingAction::newASTConsumer() {308return std::make_unique<NamedDeclFindingConsumer>(309SymbolOffsets, QualifiedNames, SpellingNames, USRList, Force,310ErrorOccurred);311}312313} // end namespace tooling314} // end namespace clang315316317