Clang Project

clang_source_code/lib/Tooling/Core/Lookup.cpp
1//===--- Lookup.cpp - Framework for clang refactoring tools ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9//  This file defines helper methods for clang tools performing name lookup.
10//
11//===----------------------------------------------------------------------===//
12
13#include "clang/Tooling/Core/Lookup.h"
14#include "clang/AST/Decl.h"
15#include "clang/AST/DeclCXX.h"
16#include "clang/AST/DeclarationName.h"
17#include "llvm/ADT/SmallVector.h"
18using namespace clang;
19using namespace clang::tooling;
20
21// Gets all namespaces that \p Context is in as a vector (ignoring anonymous
22// namespaces). The inner namespaces come before outer namespaces in the vector.
23// For example, if the context is in the following namespace:
24//    `namespace a { namespace b { namespace c ( ... ) } }`,
25// the vector will be `{c, b, a}`.
26static llvm::SmallVector<const NamespaceDecl *, 4>
27getAllNamedNamespaces(const DeclContext *Context) {
28  llvm::SmallVector<const NamespaceDecl *, 4Namespaces;
29  auto GetNextNamedNamespace = [](const DeclContext *Context) {
30    // Look past non-namespaces and anonymous namespaces on FromContext.
31    while (Context && (!isa<NamespaceDecl>(Context) ||
32                       cast<NamespaceDecl>(Context)->isAnonymousNamespace()))
33      Context = Context->getParent();
34    return Context;
35  };
36  for (Context = GetNextNamedNamespace(Context); Context != nullptr;
37       Context = GetNextNamedNamespace(Context->getParent()))
38    Namespaces.push_back(cast<NamespaceDecl>(Context));
39  return Namespaces;
40}
41
42// Returns true if the context in which the type is used and the context in
43// which the type is declared are the same semantical namespace but different
44// lexical namespaces.
45static bool
46usingFromDifferentCanonicalNamespace(const DeclContext *FromContext,
47                                     const DeclContext *UseContext) {
48  // We can skip anonymous namespace because:
49  // 1. `FromContext` and `UseContext` must be in the same anonymous namespaces
50  // since referencing across anonymous namespaces is not possible.
51  // 2. If `FromContext` and `UseContext` are in the same anonymous namespace,
52  // the function will still return `false` as expected.
53  llvm::SmallVector<const NamespaceDecl *, 4FromNamespaces =
54      getAllNamedNamespaces(FromContext);
55  llvm::SmallVector<const NamespaceDecl *, 4UseNamespaces =
56      getAllNamedNamespaces(UseContext);
57  // If `UseContext` has fewer level of nested namespaces, it cannot be in the
58  // same canonical namespace as the `FromContext`.
59  if (UseNamespaces.size() < FromNamespaces.size())
60    return false;
61  unsigned Diff = UseNamespaces.size() - FromNamespaces.size();
62  auto FromIter = FromNamespaces.begin();
63  // Only compare `FromNamespaces` with namespaces in `UseNamespaces` that can
64  // collide, i.e. the top N namespaces where N is the number of namespaces in
65  // `FromNamespaces`.
66  auto UseIter = UseNamespaces.begin() + Diff;
67  for (; FromIter != FromNamespaces.end() && UseIter != UseNamespaces.end();
68       ++FromIter, ++UseIter) {
69    // Literally the same namespace, not a collision.
70    if (*FromIter == *UseIter)
71      return false;
72    // Now check the names. If they match we have a different canonical
73    // namespace with the same name.
74    if (cast<NamespaceDecl>(*FromIter)->getDeclName() ==
75        cast<NamespaceDecl>(*UseIter)->getDeclName())
76      return true;
77  }
78  assert(FromIter == FromNamespaces.end() && UseIter == UseNamespaces.end());
79  return false;
80}
81
82static StringRef getBestNamespaceSubstr(const DeclContext *DeclA,
83                                        StringRef NewName,
84                                        bool HadLeadingColonColon) {
85  while (true) {
86    while (DeclA && !isa<NamespaceDecl>(DeclA))
87      DeclA = DeclA->getParent();
88
89    // Fully qualified it is! Leave :: in place if it's there already.
90    if (!DeclA)
91      return HadLeadingColonColon ? NewName : NewName.substr(2);
92
93    // Otherwise strip off redundant namespace qualifications from the new name.
94    // We use the fully qualified name of the namespace and remove that part
95    // from NewName if it has an identical prefix.
96    std::string NS =
97        "::" + cast<NamespaceDecl>(DeclA)->getQualifiedNameAsString() + "::";
98    if (NewName.startswith(NS))
99      return NewName.substr(NS.size());
100
101    // No match yet. Strip of a namespace from the end of the chain and try
102    // again. This allows to get optimal qualifications even if the old and new
103    // decl only share common namespaces at a higher level.
104    DeclA = DeclA->getParent();
105  }
106}
107
108/// Check if the name specifier begins with a written "::".
109static bool isFullyQualified(const NestedNameSpecifier *NNS) {
110  while (NNS) {
111    if (NNS->getKind() == NestedNameSpecifier::Global)
112      return true;
113    NNS = NNS->getPrefix();
114  }
115  return false;
116}
117
118// Adds more scope specifier to the spelled name until the spelling is not
119// ambiguous. A spelling is ambiguous if the resolution of the symbol is
120// ambiguous. For example, if QName is "::y::bar", the spelling is "y::bar", and
121// context contains a nested namespace "a::y", then "y::bar" can be resolved to
122// ::a::y::bar in the context, which can cause compile error.
123// FIXME: consider using namespaces.
124static std::string disambiguateSpellingInScope(StringRef Spelling,
125                                               StringRef QName,
126                                               const DeclContext &UseContext) {
127   (0) . __assert_fail ("QName.startswith(\"..\")", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Core/Lookup.cpp", 127, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(QName.startswith("::"));
128  assert(QName.endswith(Spelling));
129  if (Spelling.startswith("::"))
130    return Spelling;
131
132  auto UnspelledSpecifier = QName.drop_back(Spelling.size());
133  llvm::SmallVector<llvm::StringRef2UnspelledScopes;
134  UnspelledSpecifier.split(UnspelledScopes, "::"/*MaxSplit=*/-1,
135                           /*KeepEmpty=*/false);
136
137  llvm::SmallVector<const NamespaceDecl *, 4EnclosingNamespaces =
138      getAllNamedNamespaces(&UseContext);
139  auto &AST = UseContext.getParentASTContext();
140  StringRef TrimmedQName = QName.substr(2);
141
142  auto IsAmbiguousSpelling = [&EnclosingNamespaces, &AST, &TrimmedQName](
143                                 const llvm::StringRef CurSpelling) {
144    if (CurSpelling.startswith("::"))
145      return false;
146    // Lookup the first component of Spelling in all enclosing namespaces
147    // and check if there is any existing symbols with the same name but in
148    // different scope.
149    StringRef Head = CurSpelling.split("::").first;
150    for (const auto *NS : EnclosingNamespaces) {
151      auto LookupRes = NS->lookup(DeclarationName(&AST.Idents.get(Head)));
152      if (!LookupRes.empty()) {
153        for (const NamedDecl *Res : LookupRes)
154          if (!TrimmedQName.startswith(Res->getQualifiedNameAsString()))
155            return true;
156      }
157    }
158    return false;
159  };
160
161  // Add more qualifiers until the spelling is not ambiguous.
162  std::string Disambiguated = Spelling;
163  while (IsAmbiguousSpelling(Disambiguated)) {
164    if (UnspelledScopes.empty()) {
165      Disambiguated = "::" + Disambiguated;
166    } else {
167      Disambiguated = (UnspelledScopes.back() + "::" + Disambiguated).str();
168      UnspelledScopes.pop_back();
169    }
170  }
171  return Disambiguated;
172}
173
174std::string tooling::replaceNestedName(const NestedNameSpecifier *Use,
175                                       const DeclContext *UseContext,
176                                       const NamedDecl *FromDecl,
177                                       StringRef ReplacementString) {
178   (0) . __assert_fail ("ReplacementString.startswith(\"..\") && \"Expected fully-qualified name!\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Core/Lookup.cpp", 179, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">assert(ReplacementString.startswith("::") &&
179 (0) . __assert_fail ("ReplacementString.startswith(\"..\") && \"Expected fully-qualified name!\"", "/home/seafit/code_projects/clang_source/clang/lib/Tooling/Core/Lookup.cpp", 179, __PRETTY_FUNCTION__))" file_link="../../../../include/assert.h.html#88" macro="true">         "Expected fully-qualified name!");
180
181  // We can do a raw name replacement when we are not inside the namespace for
182  // the original class/function and it is not in the global namespace.  The
183  // assumption is that outside the original namespace we must have a using
184  // statement that makes this work out and that other parts of this refactor
185  // will automatically fix using statements to point to the new class/function.
186  // However, if the `FromDecl` is a class forward declaration, the reference is
187  // still considered as referring to the original definition, so we can't do a
188  // raw name replacement in this case.
189  const bool class_name_only = !Use;
190  const bool in_global_namespace =
191      isa<TranslationUnitDecl>(FromDecl->getDeclContext());
192  const bool is_class_forward_decl =
193      isa<CXXRecordDecl>(FromDecl) &&
194      !cast<CXXRecordDecl>(FromDecl)->isCompleteDefinition();
195  if (class_name_only && !in_global_namespace && !is_class_forward_decl &&
196      !usingFromDifferentCanonicalNamespace(FromDecl->getDeclContext(),
197                                            UseContext)) {
198    auto Pos = ReplacementString.rfind("::");
199    return Pos != StringRef::npos ? ReplacementString.substr(Pos + 2)
200                                  : ReplacementString;
201  }
202  // We did not match this because of a using statement, so we will need to
203  // figure out how good a namespace match we have with our destination type.
204  // We work backwards (from most specific possible namespace to least
205  // specific).
206  StringRef Suggested = getBestNamespaceSubstr(UseContext, ReplacementString,
207                                               isFullyQualified(Use));
208
209  return disambiguateSpellingInScope(Suggested, ReplacementString, *UseContext);
210}
211