forked from OSchip/llvm-project
				
			
		
			
				
	
	
		
			224 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			224 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			C++
		
	
	
	
| //===--- 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/Core/Lookup.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);
 | |
| }
 |