1 //===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//
3 // The LLVM Compiler Infrastructure
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
8 //===----------------------------------------------------------------------===//
10 // This file defines helper methods for clang tools performing name lookup.
12 //===----------------------------------------------------------------------===//
14 #include "clang/Tooling/Core/Lookup.h"
15 #include "clang/AST/Decl.h"
16 using namespace clang;
17 using namespace clang::tooling;
19 // Returns true if the context in which the type is used and the context in
20 // which the type is declared are the same semantical namespace but different
21 // lexical namespaces.
23 usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
24 const DeclContext *UseContext) {
26 // Look past non-namespaces and anonymous namespaces on FromContext.
27 // We can skip anonymous namespace because:
28 // 1. `FromContext` and `UseContext` must be in the same anonymous
29 // namespaces since referencing across anonymous namespaces is not possible.
30 // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
31 // the function will still return `false` as expected.
33 (!isa<NamespaceDecl>(FromContext) ||
34 cast<NamespaceDecl>(FromContext)->isAnonymousNamespace()))
35 FromContext = FromContext->getParent();
37 // Look past non-namespaces and anonymous namespaces on UseContext.
39 (!isa<NamespaceDecl>(UseContext) ||
40 cast<NamespaceDecl>(UseContext)->isAnonymousNamespace()))
41 UseContext = UseContext->getParent();
43 // We hit the root, no namespace collision.
44 if (!FromContext || !UseContext)
47 // Literally the same namespace, not a collision.
48 if (FromContext == UseContext)
51 // Now check the names. If they match we have a different namespace with the
53 if (cast<NamespaceDecl>(FromContext)->getDeclName() ==
54 cast<NamespaceDecl>(UseContext)->getDeclName())
57 FromContext = FromContext->getParent();
58 UseContext = UseContext->getParent();
62 static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
64 bool HadLeadingColonColon) {
66 while (DeclA && !isa<NamespaceDecl>(DeclA))
67 DeclA = DeclA->getParent();
69 // Fully qualified it is! Leave :: in place if it's there already.
71 return HadLeadingColonColon ? NewName : NewName.substr(2);
73 // Otherwise strip off redundant namespace qualifications from the new name.
74 // We use the fully qualified name of the namespace and remove that part
75 // from NewName if it has an identical prefix.
77 "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
78 if (NewName.startswith(NS))
79 return NewName.substr(NS.size());
81 // No match yet. Strip of a namespace from the end of the chain and try
82 // again. This allows to get optimal qualifications even if the old and new
83 // decl only share common namespaces at a higher level.
84 DeclA = DeclA->getParent();
88 /// Check if the name specifier begins with a written "::".
89 static bool isFullyQualified(const NestedNameSpecifier *NNS) {
91 if (NNS->getKind() == NestedNameSpecifier::Global)
93 NNS = NNS->getPrefix();
98 std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
99 const DeclContext *UseContext,
100 const NamedDecl *FromDecl,
101 StringRef ReplacementString) {
102 assert(ReplacementString.startswith("::") &&
103 "Expected fully-qualified name!");
105 // We can do a raw name replacement when we are not inside the namespace for
106 // the original function and it is not in the global namespace. The
107 // assumption is that outside the original namespace we must have a using
108 // statement that makes this work out and that other parts of this refactor
109 // will automatically fix using statements to point to the new function
110 const bool class_name_only = !Use;
111 const bool in_global_namespace =
112 isa<TranslationUnitDecl>(FromDecl->getDeclContext());
113 if (class_name_only && !in_global_namespace &&
114 !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
116 auto Pos = ReplacementString.rfind("::");
117 return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
120 // We did not match this because of a using statement, so we will need to
121 // figure out how good a namespace match we have with our destination type.
122 // We work backwards (from most specific possible namespace to least
124 return getBestNamespaceSubstr(UseContext, ReplacementString,
125 isFullyQualified(Use));