Path: blob/main/contrib/llvm-project/clang/lib/Tooling/Refactoring/Lookup.cpp
35271 views
//===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//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// This file defines helper methods for clang tools performing name lookup.9//10//===----------------------------------------------------------------------===//1112#include "clang/Tooling/Refactoring/Lookup.h"13#include "clang/AST/ASTContext.h"14#include "clang/AST/Decl.h"15#include "clang/AST/DeclCXX.h"16#include "clang/AST/DeclarationName.h"17#include "clang/Basic/SourceLocation.h"18#include "clang/Basic/SourceManager.h"19#include "llvm/ADT/SmallVector.h"20using namespace clang;21using namespace clang::tooling;2223// Gets all namespaces that \p Context is in as a vector (ignoring anonymous24// namespaces). The inner namespaces come before outer namespaces in the vector.25// For example, if the context is in the following namespace:26// `namespace a { namespace b { namespace c ( ... ) } }`,27// the vector will be `{c, b, a}`.28static llvm::SmallVector<const NamespaceDecl *, 4>29getAllNamedNamespaces(const DeclContext *Context) {30llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;31auto GetNextNamedNamespace = [](const DeclContext *Context) {32// Look past non-namespaces and anonymous namespaces on FromContext.33while (Context && (!isa<NamespaceDecl>(Context) ||34cast<NamespaceDecl>(Context)->isAnonymousNamespace()))35Context = Context->getParent();36return Context;37};38for (Context = GetNextNamedNamespace(Context); Context != nullptr;39Context = GetNextNamedNamespace(Context->getParent()))40Namespaces.push_back(cast<NamespaceDecl>(Context));41return Namespaces;42}4344// Returns true if the context in which the type is used and the context in45// which the type is declared are the same semantical namespace but different46// lexical namespaces.47static bool48usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,49const DeclContext *UseContext) {50// We can skip anonymous namespace because:51// 1. `FromContext` and `UseContext` must be in the same anonymous namespaces52// since referencing across anonymous namespaces is not possible.53// 2. If `FromContext` and `UseContext` are in the same anonymous namespace,54// the function will still return `false` as expected.55llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces =56getAllNamedNamespaces(FromContext);57llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =58getAllNamedNamespaces(UseContext);59// If `UseContext` has fewer level of nested namespaces, it cannot be in the60// same canonical namespace as the `FromContext`.61if (UseNamespaces.size() < FromNamespaces.size())62return false;63unsigned Diff = UseNamespaces.size() - FromNamespaces.size();64auto FromIter = FromNamespaces.begin();65// Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can66// collide, i.e. the top N namespaces where N is the number of namespaces in67// `FromNamespaces`.68auto UseIter = UseNamespaces.begin() + Diff;69for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();70++FromIter, ++UseIter) {71// Literally the same namespace, not a collision.72if (*FromIter == *UseIter)73return false;74// Now check the names. If they match we have a different canonical75// namespace with the same name.76if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==77cast<NamespaceDecl>(*UseIter)->getDeclName())78return true;79}80assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());81return false;82}8384static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,85StringRef NewName,86bool HadLeadingColonColon) {87while (true) {88while (DeclA && !isa<NamespaceDecl>(DeclA))89DeclA = DeclA->getParent();9091// Fully qualified it is! Leave :: in place if it's there already.92if (!DeclA)93return HadLeadingColonColon ? NewName : NewName.substr(2);9495// Otherwise strip off redundant namespace qualifications from the new name.96// We use the fully qualified name of the namespace and remove that part97// from NewName if it has an identical prefix.98std::string NS =99"::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";100if (NewName.consume_front(NS))101return NewName;102103// No match yet. Strip of a namespace from the end of the chain and try104// again. This allows to get optimal qualifications even if the old and new105// decl only share common namespaces at a higher level.106DeclA = DeclA->getParent();107}108}109110/// Check if the name specifier begins with a written "::".111static bool isFullyQualified(const NestedNameSpecifier *NNS) {112while (NNS) {113if (NNS->getKind() == NestedNameSpecifier::Global)114return true;115NNS = NNS->getPrefix();116}117return false;118}119120// Adds more scope specifier to the spelled name until the spelling is not121// ambiguous. A spelling is ambiguous if the resolution of the symbol is122// ambiguous. For example, if QName is "::y::bar", the spelling is "y::bar", and123// context contains a nested namespace "a::y", then "y::bar" can be resolved to124// ::a::y::bar in the context, which can cause compile error.125// FIXME: consider using namespaces.126static std::string disambiguateSpellingInScope(StringRef Spelling,127StringRef QName,128const DeclContext &UseContext,129SourceLocation UseLoc) {130assert(QName.starts_with("::"));131assert(QName.ends_with(Spelling));132if (Spelling.starts_with("::"))133return std::string(Spelling);134135auto UnspelledSpecifier = QName.drop_back(Spelling.size());136llvm::SmallVector<llvm::StringRef, 2> UnspelledScopes;137UnspelledSpecifier.split(UnspelledScopes, "::", /*MaxSplit=*/-1,138/*KeepEmpty=*/false);139140llvm::SmallVector<const NamespaceDecl *, 4> EnclosingNamespaces =141getAllNamedNamespaces(&UseContext);142auto &AST = UseContext.getParentASTContext();143StringRef TrimmedQName = QName.substr(2);144const auto &SM = UseContext.getParentASTContext().getSourceManager();145UseLoc = SM.getSpellingLoc(UseLoc);146147auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) {148if (CurSpelling.starts_with("::"))149return false;150// Lookup the first component of Spelling in all enclosing namespaces151// and check if there is any existing symbols with the same name but in152// different scope.153StringRef Head = CurSpelling.split("::").first;154for (const auto *NS : EnclosingNamespaces) {155auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));156if (!LookupRes.empty()) {157for (const NamedDecl *Res : LookupRes)158// If `Res` is not visible in `UseLoc`, we don't consider it159// ambiguous. For example, a reference in a header file should not be160// affected by a potentially ambiguous name in some file that includes161// the header.162if (!TrimmedQName.starts_with(Res->getQualifiedNameAsString()) &&163SM.isBeforeInTranslationUnit(164SM.getSpellingLoc(Res->getLocation()), UseLoc))165return true;166}167}168return false;169};170171// Add more qualifiers until the spelling is not ambiguous.172std::string Disambiguated = std::string(Spelling);173while (IsAmbiguousSpelling(Disambiguated)) {174if (UnspelledScopes.empty()) {175Disambiguated = "::" + Disambiguated;176} else {177Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str();178UnspelledScopes.pop_back();179}180}181return Disambiguated;182}183184std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,185SourceLocation UseLoc,186const DeclContext *UseContext,187const NamedDecl *FromDecl,188StringRef ReplacementString) {189assert(ReplacementString.starts_with("::") &&190"Expected fully-qualified name!");191192// We can do a raw name replacement when we are not inside the namespace for193// the original class/function and it is not in the global namespace. The194// assumption is that outside the original namespace we must have a using195// statement that makes this work out and that other parts of this refactor196// will automatically fix using statements to point to the new class/function.197// However, if the `FromDecl` is a class forward declaration, the reference is198// still considered as referring to the original definition, so we can't do a199// raw name replacement in this case.200const bool class_name_only = !Use;201const bool in_global_namespace =202isa<TranslationUnitDecl>(FromDecl->getDeclContext());203const bool is_class_forward_decl =204isa<CXXRecordDecl>(FromDecl) &&205!cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();206if (class_name_only && !in_global_namespace && !is_class_forward_decl &&207!usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),208UseContext)) {209auto Pos = ReplacementString.rfind("::");210return std::string(Pos != StringRef::npos211? ReplacementString.substr(Pos + 2)212: ReplacementString);213}214// We did not match this because of a using statement, so we will need to215// figure out how good a namespace match we have with our destination type.216// We work backwards (from most specific possible namespace to least217// specific).218StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,219isFullyQualified(Use));220221return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext,222UseLoc);223}224225226