123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- //===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//
- //
- // 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
- //
- //===----------------------------------------------------------------------===//
- //
- // This file defines helper methods for clang tools performing name lookup.
- //
- //===----------------------------------------------------------------------===//
- #include "clang/Tooling/Refactoring/Lookup.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/AST/Decl.h"
- #include "clang/AST/DeclCXX.h"
- #include "clang/AST/DeclarationName.h"
- #include "clang/Basic/SourceLocation.h"
- #include "clang/Basic/SourceManager.h"
- #include "llvm/ADT/SmallVector.h"
- using namespace clang;
- using namespace clang::tooling;
- // Gets all namespaces that \p Context is in as a vector (ignoring anonymous
- // namespaces). The inner namespaces come before outer namespaces in the vector.
- // For example, if the context is in the following namespace:
- // `namespace a { namespace b { namespace c ( ... ) } }`,
- // the vector will be `{c, b, a}`.
- static llvm::SmallVector<const NamespaceDecl *, 4>
- getAllNamedNamespaces(const DeclContext *Context) {
- llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;
- auto GetNextNamedNamespace = [](const DeclContext *Context) {
- // Look past non-namespaces and anonymous namespaces on FromContext.
- while (Context && (!isa<NamespaceDecl>(Context) ||
- cast<NamespaceDecl>(Context)->isAnonymousNamespace()))
- Context = Context->getParent();
- return Context;
- };
- for (Context = GetNextNamedNamespace(Context); Context != nullptr;
- Context = GetNextNamedNamespace(Context->getParent()))
- Namespaces.push_back(cast<NamespaceDecl>(Context));
- return Namespaces;
- }
- // Returns true if the context in which the type is used and the context in
- // which the type is declared are the same semantical namespace but different
- // lexical namespaces.
- static bool
- usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
- const DeclContext *UseContext) {
- // We can skip anonymous namespace because:
- // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces
- // since referencing across anonymous namespaces is not possible.
- // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
- // the function will still return `false` as expected.
- llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces =
- getAllNamedNamespaces(FromContext);
- llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
- getAllNamedNamespaces(UseContext);
- // If `UseContext` has fewer level of nested namespaces, it cannot be in the
- // same canonical namespace as the `FromContext`.
- if (UseNamespaces.size() < FromNamespaces.size())
- return false;
- unsigned Diff = UseNamespaces.size() - FromNamespaces.size();
- auto FromIter = FromNamespaces.begin();
- // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can
- // collide, i.e. the top N namespaces where N is the number of namespaces in
- // `FromNamespaces`.
- auto UseIter = UseNamespaces.begin() + Diff;
- for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();
- ++FromIter, ++UseIter) {
- // Literally the same namespace, not a collision.
- if (*FromIter == *UseIter)
- return false;
- // Now check the names. If they match we have a different canonical
- // namespace with the same name.
- if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==
- cast<NamespaceDecl>(*UseIter)->getDeclName())
- return true;
- }
- assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
- return false;
- }
- static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
- StringRef NewName,
- bool HadLeadingColonColon) {
- while (true) {
- while (DeclA && !isa<NamespaceDecl>(DeclA))
- DeclA = DeclA->getParent();
- // Fully qualified it is! Leave :: in place if it's there already.
- if (!DeclA)
- return HadLeadingColonColon ? NewName : NewName.substr(2);
- // Otherwise strip off redundant namespace qualifications from the new name.
- // We use the fully qualified name of the namespace and remove that part
- // from NewName if it has an identical prefix.
- std::string NS =
- "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
- if (NewName.startswith(NS))
- return NewName.substr(NS.size());
- // No match yet. Strip of a namespace from the end of the chain and try
- // again. This allows to get optimal qualifications even if the old and new
- // decl only share common namespaces at a higher level.
- DeclA = DeclA->getParent();
- }
- }
- /// Check if the name specifier begins with a written "::".
- static bool isFullyQualified(const NestedNameSpecifier *NNS) {
- while (NNS) {
- if (NNS->getKind() == NestedNameSpecifier::Global)
- return true;
- NNS = NNS->getPrefix();
- }
- return false;
- }
- // Adds more scope specifier to the spelled name until the spelling is not
- // ambiguous. A spelling is ambiguous if the resolution of the symbol is
- // ambiguous. For example, if QName is "::y::bar", the spelling is "y::bar", and
- // context contains a nested namespace "a::y", then "y::bar" can be resolved to
- // ::a::y::bar in the context, which can cause compile error.
- // FIXME: consider using namespaces.
- static std::string disambiguateSpellingInScope(StringRef Spelling,
- StringRef QName,
- const DeclContext &UseContext,
- SourceLocation UseLoc) {
- assert(QName.startswith("::"));
- assert(QName.endswith(Spelling));
- if (Spelling.startswith("::"))
- return std::string(Spelling);
- auto UnspelledSpecifier = QName.drop_back(Spelling.size());
- llvm::SmallVector<llvm::StringRef, 2> UnspelledScopes;
- UnspelledSpecifier.split(UnspelledScopes, "::", /*MaxSplit=*/-1,
- /*KeepEmpty=*/false);
- llvm::SmallVector<const NamespaceDecl *, 4> EnclosingNamespaces =
- getAllNamedNamespaces(&UseContext);
- auto &AST = UseContext.getParentASTContext();
- StringRef TrimmedQName = QName.substr(2);
- const auto &SM = UseContext.getParentASTContext().getSourceManager();
- UseLoc = SM.getSpellingLoc(UseLoc);
- auto IsAmbiguousSpelling = [&](const llvm::StringRef CurSpelling) {
- if (CurSpelling.startswith("::"))
- return false;
- // Lookup the first component of Spelling in all enclosing namespaces
- // and check if there is any existing symbols with the same name but in
- // different scope.
- StringRef Head = CurSpelling.split("::").first;
- for (const auto *NS : EnclosingNamespaces) {
- auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
- if (!LookupRes.empty()) {
- for (const NamedDecl *Res : LookupRes)
- // If `Res` is not visible in `UseLoc`, we don't consider it
- // ambiguous. For example, a reference in a header file should not be
- // affected by a potentially ambiguous name in some file that includes
- // the header.
- if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()) &&
- SM.isBeforeInTranslationUnit(
- SM.getSpellingLoc(Res->getLocation()), UseLoc))
- return true;
- }
- }
- return false;
- };
- // Add more qualifiers until the spelling is not ambiguous.
- std::string Disambiguated = std::string(Spelling);
- while (IsAmbiguousSpelling(Disambiguated)) {
- if (UnspelledScopes.empty()) {
- Disambiguated = "::" + Disambiguated;
- } else {
- Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str();
- UnspelledScopes.pop_back();
- }
- }
- return Disambiguated;
- }
- std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
- SourceLocation UseLoc,
- const DeclContext *UseContext,
- const NamedDecl *FromDecl,
- StringRef ReplacementString) {
- assert(ReplacementString.startswith("::") &&
- "Expected fully-qualified name!");
- // We can do a raw name replacement when we are not inside the namespace for
- // the original class/function and it is not in the global namespace. The
- // assumption is that outside the original namespace we must have a using
- // statement that makes this work out and that other parts of this refactor
- // will automatically fix using statements to point to the new class/function.
- // However, if the `FromDecl` is a class forward declaration, the reference is
- // still considered as referring to the original definition, so we can't do a
- // raw name replacement in this case.
- const bool class_name_only = !Use;
- const bool in_global_namespace =
- isa<TranslationUnitDecl>(FromDecl->getDeclContext());
- const bool is_class_forward_decl =
- isa<CXXRecordDecl>(FromDecl) &&
- !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
- if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
- !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
- UseContext)) {
- auto Pos = ReplacementString.rfind("::");
- return std::string(Pos != StringRef::npos
- ? ReplacementString.substr(Pos + 2)
- : ReplacementString);
- }
- // We did not match this because of a using statement, so we will need to
- // figure out how good a namespace match we have with our destination type.
- // We work backwards (from most specific possible namespace to least
- // specific).
- StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,
- isFullyQualified(Use));
- return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext,
- UseLoc);
- }
|