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 #include "clang/AST/DeclCXX.h"
17 using namespace clang;
18 using namespace clang::tooling;
20 // Gets all namespaces that \p Context is in as a vector (ignoring anonymous
21 // namespaces). The inner namespaces come before outer namespaces in the vector.
22 // For example, if the context is in the following namespace:
23 // `namespace a { namespace b { namespace c ( ... ) } }`,
24 // the vector will be `{c, b, a}`.
25 static llvm::SmallVector<const NamespaceDecl *, 4>
26 getAllNamedNamespaces(const DeclContext *Context) {
27 llvm::SmallVector<const NamespaceDecl *, 4> Namespaces;
28 auto GetNextNamedNamespace = [](const DeclContext *Context) {
29 // Look past non-namespaces and anonymous namespaces on FromContext.
30 while (Context && (!isa<NamespaceDecl>(Context) ||
31 cast<NamespaceDecl>(Context)->isAnonymousNamespace()))
32 Context = Context->getParent();
35 for (Context = GetNextNamedNamespace(Context); Context != nullptr;
36 Context = GetNextNamedNamespace(Context->getParent()))
37 Namespaces.push_back(cast<NamespaceDecl>(Context));
41 // Returns true if the context in which the type is used and the context in
42 // which the type is declared are the same semantical namespace but different
43 // lexical namespaces.
45 usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
46 const DeclContext *UseContext) {
47 // We can skip anonymous namespace because:
48 // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces
49 // since referencing across anonymous namespaces is not possible.
50 // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
51 // the function will still return `false` as expected.
52 llvm::SmallVector<const NamespaceDecl *, 4> FromNamespaces =
53 getAllNamedNamespaces(FromContext);
54 llvm::SmallVector<const NamespaceDecl *, 4> UseNamespaces =
55 getAllNamedNamespaces(UseContext);
56 // If `UseContext` has fewer level of nested namespaces, it cannot be in the
57 // same canonical namespace as the `FromContext`.
58 if (UseNamespaces.size() < FromNamespaces.size())
60 unsigned Diff = UseNamespaces.size() - FromNamespaces.size();
61 auto FromIter = FromNamespaces.begin();
62 // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can
63 // collide, i.e. the top N namespaces where N is the number of namespaces in
65 auto UseIter = UseNamespaces.begin() + Diff;
66 for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();
67 ++FromIter, ++UseIter) {
68 // Literally the same namespace, not a collision.
69 if (*FromIter == *UseIter)
71 // Now check the names. If they match we have a different canonical
72 // namespace with the same name.
73 if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==
74 cast<NamespaceDecl>(*UseIter)->getDeclName())
77 assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
81 static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
83 bool HadLeadingColonColon) {
85 while (DeclA && !isa<NamespaceDecl>(DeclA))
86 DeclA = DeclA->getParent();
88 // Fully qualified it is! Leave :: in place if it's there already.
90 return HadLeadingColonColon ? NewName : NewName.substr(2);
92 // Otherwise strip off redundant namespace qualifications from the new name.
93 // We use the fully qualified name of the namespace and remove that part
94 // from NewName if it has an identical prefix.
96 "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
97 if (NewName.startswith(NS))
98 return NewName.substr(NS.size());
100 // No match yet. Strip of a namespace from the end of the chain and try
101 // again. This allows to get optimal qualifications even if the old and new
102 // decl only share common namespaces at a higher level.
103 DeclA = DeclA->getParent();
107 /// Check if the name specifier begins with a written "::".
108 static bool isFullyQualified(const NestedNameSpecifier *NNS) {
110 if (NNS->getKind() == NestedNameSpecifier::Global)
112 NNS = NNS->getPrefix();
117 std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
118 const DeclContext *UseContext,
119 const NamedDecl *FromDecl,
120 StringRef ReplacementString) {
121 assert(ReplacementString.startswith("::") &&
122 "Expected fully-qualified name!");
124 // We can do a raw name replacement when we are not inside the namespace for
125 // the original class/function and it is not in the global namespace. The
126 // assumption is that outside the original namespace we must have a using
127 // statement that makes this work out and that other parts of this refactor
128 // will automatically fix using statements to point to the new class/function.
129 // However, if the `FromDecl` is a class forward declaration, the reference is
130 // still considered as referring to the original definition, so we can't do a
131 // raw name replacement in this case.
132 const bool class_name_only = !Use;
133 const bool in_global_namespace =
134 isa<TranslationUnitDecl>(FromDecl->getDeclContext());
135 const bool is_class_forward_decl =
136 isa<CXXRecordDecl>(FromDecl) &&
137 !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
138 if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
139 !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
141 auto Pos = ReplacementString.rfind("::");
142 return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
145 // We did not match this because of a using statement, so we will need to
146 // figure out how good a namespace match we have with our destination type.
147 // We work backwards (from most specific possible namespace to least
149 return getBestNamespaceSubstr(UseContext, ReplacementString,
150 isFullyQualified(Use));