annotate clang-tools-extra/clangd/IncludeFixer.cpp @ 221:79ff65ed7e25

LLVM12 Original
author Shinji KONO <kono@ie.u-ryukyu.ac.jp>
date Tue, 15 Jun 2021 19:15:29 +0900
parents 0572611fdcc8
children c4bab56944e8
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
150
anatofuz
parents:
diff changeset
1 //===--- IncludeFixer.cpp ----------------------------------------*- C++-*-===//
anatofuz
parents:
diff changeset
2 //
anatofuz
parents:
diff changeset
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
anatofuz
parents:
diff changeset
4 // See https://llvm.org/LICENSE.txt for license information.
anatofuz
parents:
diff changeset
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
anatofuz
parents:
diff changeset
6 //
anatofuz
parents:
diff changeset
7 //===----------------------------------------------------------------------===//
anatofuz
parents:
diff changeset
8
anatofuz
parents:
diff changeset
9 #include "IncludeFixer.h"
anatofuz
parents:
diff changeset
10 #include "AST.h"
anatofuz
parents:
diff changeset
11 #include "Diagnostics.h"
anatofuz
parents:
diff changeset
12 #include "SourceCode.h"
anatofuz
parents:
diff changeset
13 #include "index/Index.h"
anatofuz
parents:
diff changeset
14 #include "index/Symbol.h"
173
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
15 #include "support/Logger.h"
0572611fdcc8 reorgnization done
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 150
diff changeset
16 #include "support/Trace.h"
150
anatofuz
parents:
diff changeset
17 #include "clang/AST/Decl.h"
anatofuz
parents:
diff changeset
18 #include "clang/AST/DeclBase.h"
anatofuz
parents:
diff changeset
19 #include "clang/AST/DeclarationName.h"
anatofuz
parents:
diff changeset
20 #include "clang/AST/NestedNameSpecifier.h"
anatofuz
parents:
diff changeset
21 #include "clang/AST/Type.h"
anatofuz
parents:
diff changeset
22 #include "clang/Basic/Diagnostic.h"
anatofuz
parents:
diff changeset
23 #include "clang/Basic/DiagnosticSema.h"
anatofuz
parents:
diff changeset
24 #include "clang/Basic/LangOptions.h"
anatofuz
parents:
diff changeset
25 #include "clang/Basic/SourceLocation.h"
anatofuz
parents:
diff changeset
26 #include "clang/Basic/SourceManager.h"
anatofuz
parents:
diff changeset
27 #include "clang/Basic/TokenKinds.h"
anatofuz
parents:
diff changeset
28 #include "clang/Lex/Lexer.h"
anatofuz
parents:
diff changeset
29 #include "clang/Sema/DeclSpec.h"
anatofuz
parents:
diff changeset
30 #include "clang/Sema/Lookup.h"
anatofuz
parents:
diff changeset
31 #include "clang/Sema/Scope.h"
anatofuz
parents:
diff changeset
32 #include "clang/Sema/Sema.h"
anatofuz
parents:
diff changeset
33 #include "clang/Sema/TypoCorrection.h"
anatofuz
parents:
diff changeset
34 #include "llvm/ADT/ArrayRef.h"
anatofuz
parents:
diff changeset
35 #include "llvm/ADT/DenseMap.h"
anatofuz
parents:
diff changeset
36 #include "llvm/ADT/None.h"
anatofuz
parents:
diff changeset
37 #include "llvm/ADT/Optional.h"
anatofuz
parents:
diff changeset
38 #include "llvm/ADT/StringExtras.h"
anatofuz
parents:
diff changeset
39 #include "llvm/ADT/StringRef.h"
anatofuz
parents:
diff changeset
40 #include "llvm/ADT/StringSet.h"
anatofuz
parents:
diff changeset
41 #include "llvm/Support/Error.h"
anatofuz
parents:
diff changeset
42 #include "llvm/Support/FormatVariadic.h"
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
43 #include <algorithm>
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
44 #include <set>
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
45 #include <string>
150
anatofuz
parents:
diff changeset
46 #include <vector>
anatofuz
parents:
diff changeset
47
anatofuz
parents:
diff changeset
48 namespace clang {
anatofuz
parents:
diff changeset
49 namespace clangd {
anatofuz
parents:
diff changeset
50
anatofuz
parents:
diff changeset
51 std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
anatofuz
parents:
diff changeset
52 const clang::Diagnostic &Info) const {
anatofuz
parents:
diff changeset
53 switch (Info.getID()) {
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
54 case diag::err_incomplete_nested_name_spec:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
55 case diag::err_incomplete_base_class:
150
anatofuz
parents:
diff changeset
56 case diag::err_incomplete_member_access:
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
57 case diag::err_incomplete_type:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
58 case diag::err_typecheck_decl_incomplete_type:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
59 case diag::err_typecheck_incomplete_tag:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
60 case diag::err_invalid_incomplete_type_use:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
61 case diag::err_sizeof_alignof_incomplete_or_sizeless_type:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
62 case diag::err_for_range_incomplete_type:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
63 case diag::err_func_def_incomplete_result:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
64 case diag::err_field_incomplete_or_sizeless:
150
anatofuz
parents:
diff changeset
65 // Incomplete type diagnostics should have a QualType argument for the
anatofuz
parents:
diff changeset
66 // incomplete type.
anatofuz
parents:
diff changeset
67 for (unsigned Idx = 0; Idx < Info.getNumArgs(); ++Idx) {
anatofuz
parents:
diff changeset
68 if (Info.getArgKind(Idx) == DiagnosticsEngine::ak_qualtype) {
anatofuz
parents:
diff changeset
69 auto QT = QualType::getFromOpaquePtr((void *)Info.getRawArg(Idx));
anatofuz
parents:
diff changeset
70 if (const Type *T = QT.getTypePtrOrNull())
anatofuz
parents:
diff changeset
71 if (T->isIncompleteType())
anatofuz
parents:
diff changeset
72 return fixIncompleteType(*T);
anatofuz
parents:
diff changeset
73 }
anatofuz
parents:
diff changeset
74 }
anatofuz
parents:
diff changeset
75 break;
anatofuz
parents:
diff changeset
76 case diag::err_unknown_typename:
anatofuz
parents:
diff changeset
77 case diag::err_unknown_typename_suggest:
anatofuz
parents:
diff changeset
78 case diag::err_typename_nested_not_found:
anatofuz
parents:
diff changeset
79 case diag::err_no_template:
anatofuz
parents:
diff changeset
80 case diag::err_no_template_suggest:
anatofuz
parents:
diff changeset
81 case diag::err_undeclared_use:
anatofuz
parents:
diff changeset
82 case diag::err_undeclared_use_suggest:
anatofuz
parents:
diff changeset
83 case diag::err_undeclared_var_use:
anatofuz
parents:
diff changeset
84 case diag::err_undeclared_var_use_suggest:
anatofuz
parents:
diff changeset
85 case diag::err_no_member: // Could be no member in namespace.
anatofuz
parents:
diff changeset
86 case diag::err_no_member_suggest:
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
87 case diag::err_no_member_template:
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
88 case diag::err_no_member_template_suggest:
150
anatofuz
parents:
diff changeset
89 if (LastUnresolvedName) {
anatofuz
parents:
diff changeset
90 // Try to fix unresolved name caused by missing declaration.
anatofuz
parents:
diff changeset
91 // E.g.
anatofuz
parents:
diff changeset
92 // clang::SourceManager SM;
anatofuz
parents:
diff changeset
93 // ~~~~~~~~~~~~~
anatofuz
parents:
diff changeset
94 // UnresolvedName
anatofuz
parents:
diff changeset
95 // or
anatofuz
parents:
diff changeset
96 // namespace clang { SourceManager SM; }
anatofuz
parents:
diff changeset
97 // ~~~~~~~~~~~~~
anatofuz
parents:
diff changeset
98 // UnresolvedName
anatofuz
parents:
diff changeset
99 // We only attempt to recover a diagnostic if it has the same location as
anatofuz
parents:
diff changeset
100 // the last seen unresolved name.
anatofuz
parents:
diff changeset
101 if (DiagLevel >= DiagnosticsEngine::Error &&
anatofuz
parents:
diff changeset
102 LastUnresolvedName->Loc == Info.getLocation())
anatofuz
parents:
diff changeset
103 return fixUnresolvedName();
anatofuz
parents:
diff changeset
104 }
anatofuz
parents:
diff changeset
105 }
anatofuz
parents:
diff changeset
106 return {};
anatofuz
parents:
diff changeset
107 }
anatofuz
parents:
diff changeset
108
anatofuz
parents:
diff changeset
109 std::vector<Fix> IncludeFixer::fixIncompleteType(const Type &T) const {
anatofuz
parents:
diff changeset
110 // Only handle incomplete TagDecl type.
anatofuz
parents:
diff changeset
111 const TagDecl *TD = T.getAsTagDecl();
anatofuz
parents:
diff changeset
112 if (!TD)
anatofuz
parents:
diff changeset
113 return {};
anatofuz
parents:
diff changeset
114 std::string TypeName = printQualifiedName(*TD);
anatofuz
parents:
diff changeset
115 trace::Span Tracer("Fix include for incomplete type");
anatofuz
parents:
diff changeset
116 SPAN_ATTACH(Tracer, "type", TypeName);
anatofuz
parents:
diff changeset
117 vlog("Trying to fix include for incomplete type {0}", TypeName);
anatofuz
parents:
diff changeset
118
anatofuz
parents:
diff changeset
119 auto ID = getSymbolID(TD);
anatofuz
parents:
diff changeset
120 if (!ID)
anatofuz
parents:
diff changeset
121 return {};
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
122 llvm::Optional<const SymbolSlab *> Symbols = lookupCached(ID);
150
anatofuz
parents:
diff changeset
123 if (!Symbols)
anatofuz
parents:
diff changeset
124 return {};
anatofuz
parents:
diff changeset
125 const SymbolSlab &Syms = **Symbols;
anatofuz
parents:
diff changeset
126 std::vector<Fix> Fixes;
anatofuz
parents:
diff changeset
127 if (!Syms.empty()) {
anatofuz
parents:
diff changeset
128 auto &Matched = *Syms.begin();
anatofuz
parents:
diff changeset
129 if (!Matched.IncludeHeaders.empty() && Matched.Definition &&
anatofuz
parents:
diff changeset
130 Matched.CanonicalDeclaration.FileURI == Matched.Definition.FileURI)
anatofuz
parents:
diff changeset
131 Fixes = fixesForSymbols(Syms);
anatofuz
parents:
diff changeset
132 }
anatofuz
parents:
diff changeset
133 return Fixes;
anatofuz
parents:
diff changeset
134 }
anatofuz
parents:
diff changeset
135
anatofuz
parents:
diff changeset
136 std::vector<Fix> IncludeFixer::fixesForSymbols(const SymbolSlab &Syms) const {
anatofuz
parents:
diff changeset
137 auto Inserted = [&](const Symbol &Sym, llvm::StringRef Header)
anatofuz
parents:
diff changeset
138 -> llvm::Expected<std::pair<std::string, bool>> {
anatofuz
parents:
diff changeset
139 auto ResolvedDeclaring =
anatofuz
parents:
diff changeset
140 URI::resolve(Sym.CanonicalDeclaration.FileURI, File);
anatofuz
parents:
diff changeset
141 if (!ResolvedDeclaring)
anatofuz
parents:
diff changeset
142 return ResolvedDeclaring.takeError();
anatofuz
parents:
diff changeset
143 auto ResolvedInserted = toHeaderFile(Header, File);
anatofuz
parents:
diff changeset
144 if (!ResolvedInserted)
anatofuz
parents:
diff changeset
145 return ResolvedInserted.takeError();
anatofuz
parents:
diff changeset
146 auto Spelled = Inserter->calculateIncludePath(*ResolvedInserted, File);
anatofuz
parents:
diff changeset
147 if (!Spelled)
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
148 return error("Header not on include path");
150
anatofuz
parents:
diff changeset
149 return std::make_pair(
anatofuz
parents:
diff changeset
150 std::move(*Spelled),
anatofuz
parents:
diff changeset
151 Inserter->shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted));
anatofuz
parents:
diff changeset
152 };
anatofuz
parents:
diff changeset
153
anatofuz
parents:
diff changeset
154 std::vector<Fix> Fixes;
anatofuz
parents:
diff changeset
155 // Deduplicate fixes by include headers. This doesn't distinguish symbols in
anatofuz
parents:
diff changeset
156 // different scopes from the same header, but this case should be rare and is
anatofuz
parents:
diff changeset
157 // thus ignored.
anatofuz
parents:
diff changeset
158 llvm::StringSet<> InsertedHeaders;
anatofuz
parents:
diff changeset
159 for (const auto &Sym : Syms) {
anatofuz
parents:
diff changeset
160 for (const auto &Inc : getRankedIncludes(Sym)) {
anatofuz
parents:
diff changeset
161 if (auto ToInclude = Inserted(Sym, Inc)) {
anatofuz
parents:
diff changeset
162 if (ToInclude->second) {
anatofuz
parents:
diff changeset
163 auto I = InsertedHeaders.try_emplace(ToInclude->first);
anatofuz
parents:
diff changeset
164 if (!I.second)
anatofuz
parents:
diff changeset
165 continue;
anatofuz
parents:
diff changeset
166 if (auto Edit = Inserter->insert(ToInclude->first))
anatofuz
parents:
diff changeset
167 Fixes.push_back(Fix{std::string(llvm::formatv(
anatofuz
parents:
diff changeset
168 "Add include {0} for symbol {1}{2}",
anatofuz
parents:
diff changeset
169 ToInclude->first, Sym.Scope, Sym.Name)),
anatofuz
parents:
diff changeset
170 {std::move(*Edit)}});
anatofuz
parents:
diff changeset
171 }
anatofuz
parents:
diff changeset
172 } else {
anatofuz
parents:
diff changeset
173 vlog("Failed to calculate include insertion for {0} into {1}: {2}", Inc,
anatofuz
parents:
diff changeset
174 File, ToInclude.takeError());
anatofuz
parents:
diff changeset
175 }
anatofuz
parents:
diff changeset
176 }
anatofuz
parents:
diff changeset
177 }
anatofuz
parents:
diff changeset
178 return Fixes;
anatofuz
parents:
diff changeset
179 }
anatofuz
parents:
diff changeset
180
anatofuz
parents:
diff changeset
181 // Returns the identifiers qualified by an unresolved name. \p Loc is the
anatofuz
parents:
diff changeset
182 // start location of the unresolved name. For the example below, this returns
anatofuz
parents:
diff changeset
183 // "::X::Y" that is qualified by unresolved name "clangd":
anatofuz
parents:
diff changeset
184 // clang::clangd::X::Y
anatofuz
parents:
diff changeset
185 // ~
anatofuz
parents:
diff changeset
186 llvm::Optional<std::string> qualifiedByUnresolved(const SourceManager &SM,
anatofuz
parents:
diff changeset
187 SourceLocation Loc,
anatofuz
parents:
diff changeset
188 const LangOptions &LangOpts) {
anatofuz
parents:
diff changeset
189 std::string Result;
anatofuz
parents:
diff changeset
190
anatofuz
parents:
diff changeset
191 SourceLocation NextLoc = Loc;
anatofuz
parents:
diff changeset
192 while (auto CCTok = Lexer::findNextToken(NextLoc, SM, LangOpts)) {
anatofuz
parents:
diff changeset
193 if (!CCTok->is(tok::coloncolon))
anatofuz
parents:
diff changeset
194 break;
anatofuz
parents:
diff changeset
195 auto IDTok = Lexer::findNextToken(CCTok->getLocation(), SM, LangOpts);
anatofuz
parents:
diff changeset
196 if (!IDTok || !IDTok->is(tok::raw_identifier))
anatofuz
parents:
diff changeset
197 break;
anatofuz
parents:
diff changeset
198 Result.append(("::" + IDTok->getRawIdentifier()).str());
anatofuz
parents:
diff changeset
199 NextLoc = IDTok->getLocation();
anatofuz
parents:
diff changeset
200 }
anatofuz
parents:
diff changeset
201 if (Result.empty())
anatofuz
parents:
diff changeset
202 return llvm::None;
anatofuz
parents:
diff changeset
203 return Result;
anatofuz
parents:
diff changeset
204 }
anatofuz
parents:
diff changeset
205
anatofuz
parents:
diff changeset
206 // An unresolved name and its scope information that can be extracted cheaply.
anatofuz
parents:
diff changeset
207 struct CheapUnresolvedName {
anatofuz
parents:
diff changeset
208 std::string Name;
anatofuz
parents:
diff changeset
209 // This is the part of what was typed that was resolved, and it's in its
anatofuz
parents:
diff changeset
210 // resolved form not its typed form (think `namespace clang { clangd::x }` -->
anatofuz
parents:
diff changeset
211 // `clang::clangd::`).
anatofuz
parents:
diff changeset
212 llvm::Optional<std::string> ResolvedScope;
anatofuz
parents:
diff changeset
213
anatofuz
parents:
diff changeset
214 // Unresolved part of the scope. When the unresolved name is a specifier, we
anatofuz
parents:
diff changeset
215 // use the name that comes after it as the alternative name to resolve and use
anatofuz
parents:
diff changeset
216 // the specifier as the extra scope in the accessible scopes.
anatofuz
parents:
diff changeset
217 llvm::Optional<std::string> UnresolvedScope;
anatofuz
parents:
diff changeset
218 };
anatofuz
parents:
diff changeset
219
anatofuz
parents:
diff changeset
220 // Extracts unresolved name and scope information around \p Unresolved.
anatofuz
parents:
diff changeset
221 // FIXME: try to merge this with the scope-wrangling code in CodeComplete.
anatofuz
parents:
diff changeset
222 llvm::Optional<CheapUnresolvedName> extractUnresolvedNameCheaply(
anatofuz
parents:
diff changeset
223 const SourceManager &SM, const DeclarationNameInfo &Unresolved,
anatofuz
parents:
diff changeset
224 CXXScopeSpec *SS, const LangOptions &LangOpts, bool UnresolvedIsSpecifier) {
anatofuz
parents:
diff changeset
225 bool Invalid = false;
anatofuz
parents:
diff changeset
226 llvm::StringRef Code = SM.getBufferData(
anatofuz
parents:
diff changeset
227 SM.getDecomposedLoc(Unresolved.getBeginLoc()).first, &Invalid);
anatofuz
parents:
diff changeset
228 if (Invalid)
anatofuz
parents:
diff changeset
229 return llvm::None;
anatofuz
parents:
diff changeset
230 CheapUnresolvedName Result;
anatofuz
parents:
diff changeset
231 Result.Name = Unresolved.getAsString();
anatofuz
parents:
diff changeset
232 if (SS && SS->isNotEmpty()) { // "::" or "ns::"
anatofuz
parents:
diff changeset
233 if (auto *Nested = SS->getScopeRep()) {
anatofuz
parents:
diff changeset
234 if (Nested->getKind() == NestedNameSpecifier::Global)
anatofuz
parents:
diff changeset
235 Result.ResolvedScope = "";
anatofuz
parents:
diff changeset
236 else if (const auto *NS = Nested->getAsNamespace()) {
anatofuz
parents:
diff changeset
237 auto SpecifiedNS = printNamespaceScope(*NS);
anatofuz
parents:
diff changeset
238
anatofuz
parents:
diff changeset
239 // Check the specifier spelled in the source.
anatofuz
parents:
diff changeset
240 // If the resolved scope doesn't end with the spelled scope. The
anatofuz
parents:
diff changeset
241 // resolved scope can come from a sema typo correction. For example,
anatofuz
parents:
diff changeset
242 // sema assumes that "clangd::" is a typo of "clang::" and uses
anatofuz
parents:
diff changeset
243 // "clang::" as the specified scope in:
anatofuz
parents:
diff changeset
244 // namespace clang { clangd::X; }
anatofuz
parents:
diff changeset
245 // In this case, we use the "typo" specifier as extra scope instead
anatofuz
parents:
diff changeset
246 // of using the scope assumed by sema.
anatofuz
parents:
diff changeset
247 auto B = SM.getFileOffset(SS->getBeginLoc());
anatofuz
parents:
diff changeset
248 auto E = SM.getFileOffset(SS->getEndLoc());
anatofuz
parents:
diff changeset
249 std::string Spelling = (Code.substr(B, E - B) + "::").str();
anatofuz
parents:
diff changeset
250 if (llvm::StringRef(SpecifiedNS).endswith(Spelling))
anatofuz
parents:
diff changeset
251 Result.ResolvedScope = SpecifiedNS;
anatofuz
parents:
diff changeset
252 else
anatofuz
parents:
diff changeset
253 Result.UnresolvedScope = Spelling;
anatofuz
parents:
diff changeset
254 } else if (const auto *ANS = Nested->getAsNamespaceAlias()) {
anatofuz
parents:
diff changeset
255 Result.ResolvedScope = printNamespaceScope(*ANS->getNamespace());
anatofuz
parents:
diff changeset
256 } else {
anatofuz
parents:
diff changeset
257 // We don't fix symbols in scopes that are not top-level e.g. class
anatofuz
parents:
diff changeset
258 // members, as we don't collect includes for them.
anatofuz
parents:
diff changeset
259 return llvm::None;
anatofuz
parents:
diff changeset
260 }
anatofuz
parents:
diff changeset
261 }
anatofuz
parents:
diff changeset
262 }
anatofuz
parents:
diff changeset
263
anatofuz
parents:
diff changeset
264 if (UnresolvedIsSpecifier) {
anatofuz
parents:
diff changeset
265 // If the unresolved name is a specifier e.g.
anatofuz
parents:
diff changeset
266 // clang::clangd::X
anatofuz
parents:
diff changeset
267 // ~~~~~~
anatofuz
parents:
diff changeset
268 // We try to resolve clang::clangd::X instead of clang::clangd.
anatofuz
parents:
diff changeset
269 // FIXME: We won't be able to fix include if the specifier is what we
anatofuz
parents:
diff changeset
270 // should resolve (e.g. it's a class scope specifier). Collecting include
anatofuz
parents:
diff changeset
271 // headers for nested types could make this work.
anatofuz
parents:
diff changeset
272
anatofuz
parents:
diff changeset
273 // Not using the end location as it doesn't always point to the end of
anatofuz
parents:
diff changeset
274 // identifier.
anatofuz
parents:
diff changeset
275 if (auto QualifiedByUnresolved =
anatofuz
parents:
diff changeset
276 qualifiedByUnresolved(SM, Unresolved.getBeginLoc(), LangOpts)) {
anatofuz
parents:
diff changeset
277 auto Split = splitQualifiedName(*QualifiedByUnresolved);
anatofuz
parents:
diff changeset
278 if (!Result.UnresolvedScope)
anatofuz
parents:
diff changeset
279 Result.UnresolvedScope.emplace();
anatofuz
parents:
diff changeset
280 // If UnresolvedSpecifiedScope is already set, we simply append the
anatofuz
parents:
diff changeset
281 // extra scope. Suppose the unresolved name is "index" in the following
anatofuz
parents:
diff changeset
282 // example:
anatofuz
parents:
diff changeset
283 // namespace clang { clangd::index::X; }
anatofuz
parents:
diff changeset
284 // ~~~~~~ ~~~~~
anatofuz
parents:
diff changeset
285 // "clangd::" is assumed to be clang:: by Sema, and we would have used
anatofuz
parents:
diff changeset
286 // it as extra scope. With "index" being a specifier, we append "index::"
anatofuz
parents:
diff changeset
287 // to the extra scope.
anatofuz
parents:
diff changeset
288 Result.UnresolvedScope->append((Result.Name + Split.first).str());
anatofuz
parents:
diff changeset
289 Result.Name = std::string(Split.second);
anatofuz
parents:
diff changeset
290 }
anatofuz
parents:
diff changeset
291 }
anatofuz
parents:
diff changeset
292 return Result;
anatofuz
parents:
diff changeset
293 }
anatofuz
parents:
diff changeset
294
anatofuz
parents:
diff changeset
295 /// Returns all namespace scopes that the unqualified lookup would visit.
anatofuz
parents:
diff changeset
296 std::vector<std::string>
anatofuz
parents:
diff changeset
297 collectAccessibleScopes(Sema &Sem, const DeclarationNameInfo &Typo, Scope *S,
anatofuz
parents:
diff changeset
298 Sema::LookupNameKind LookupKind) {
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
299 // Collects contexts visited during a Sema name lookup.
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
300 struct VisitedContextCollector : public VisibleDeclConsumer {
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
301 VisitedContextCollector(std::vector<std::string> &Out) : Out(Out) {}
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
302 void EnteredContext(DeclContext *Ctx) override {
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
303 if (llvm::isa<NamespaceDecl>(Ctx))
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
304 Out.push_back(printNamespaceScope(*Ctx));
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
305 }
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
306 void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
307 bool InBaseClass) override {}
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
308 std::vector<std::string> &Out;
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
309 };
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
310
150
anatofuz
parents:
diff changeset
311 std::vector<std::string> Scopes;
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
312 Scopes.push_back("");
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
313 VisitedContextCollector Collector(Scopes);
150
anatofuz
parents:
diff changeset
314 Sem.LookupVisibleDecls(S, LookupKind, Collector,
anatofuz
parents:
diff changeset
315 /*IncludeGlobalScope=*/false,
anatofuz
parents:
diff changeset
316 /*LoadExternal=*/false);
221
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
317 std::sort(Scopes.begin(), Scopes.end());
79ff65ed7e25 LLVM12 Original
Shinji KONO <kono@ie.u-ryukyu.ac.jp>
parents: 173
diff changeset
318 Scopes.erase(std::unique(Scopes.begin(), Scopes.end()), Scopes.end());
150
anatofuz
parents:
diff changeset
319 return Scopes;
anatofuz
parents:
diff changeset
320 }
anatofuz
parents:
diff changeset
321
anatofuz
parents:
diff changeset
322 class IncludeFixer::UnresolvedNameRecorder : public ExternalSemaSource {
anatofuz
parents:
diff changeset
323 public:
anatofuz
parents:
diff changeset
324 UnresolvedNameRecorder(llvm::Optional<UnresolvedName> &LastUnresolvedName)
anatofuz
parents:
diff changeset
325 : LastUnresolvedName(LastUnresolvedName) {}
anatofuz
parents:
diff changeset
326
anatofuz
parents:
diff changeset
327 void InitializeSema(Sema &S) override { this->SemaPtr = &S; }
anatofuz
parents:
diff changeset
328
anatofuz
parents:
diff changeset
329 // Captures the latest typo and treat it as an unresolved name that can
anatofuz
parents:
diff changeset
330 // potentially be fixed by adding #includes.
anatofuz
parents:
diff changeset
331 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
anatofuz
parents:
diff changeset
332 Scope *S, CXXScopeSpec *SS,
anatofuz
parents:
diff changeset
333 CorrectionCandidateCallback &CCC,
anatofuz
parents:
diff changeset
334 DeclContext *MemberContext, bool EnteringContext,
anatofuz
parents:
diff changeset
335 const ObjCObjectPointerType *OPT) override {
anatofuz
parents:
diff changeset
336 assert(SemaPtr && "Sema must have been set.");
anatofuz
parents:
diff changeset
337 if (SemaPtr->isSFINAEContext())
anatofuz
parents:
diff changeset
338 return TypoCorrection();
anatofuz
parents:
diff changeset
339 if (!isInsideMainFile(Typo.getLoc(), SemaPtr->SourceMgr))
anatofuz
parents:
diff changeset
340 return clang::TypoCorrection();
anatofuz
parents:
diff changeset
341
anatofuz
parents:
diff changeset
342 auto Extracted = extractUnresolvedNameCheaply(
anatofuz
parents:
diff changeset
343 SemaPtr->SourceMgr, Typo, SS, SemaPtr->LangOpts,
anatofuz
parents:
diff changeset
344 static_cast<Sema::LookupNameKind>(LookupKind) ==
anatofuz
parents:
diff changeset
345 Sema::LookupNameKind::LookupNestedNameSpecifierName);
anatofuz
parents:
diff changeset
346 if (!Extracted)
anatofuz
parents:
diff changeset
347 return TypoCorrection();
anatofuz
parents:
diff changeset
348
anatofuz
parents:
diff changeset
349 UnresolvedName Unresolved;
anatofuz
parents:
diff changeset
350 Unresolved.Name = Extracted->Name;
anatofuz
parents:
diff changeset
351 Unresolved.Loc = Typo.getBeginLoc();
anatofuz
parents:
diff changeset
352 if (!Extracted->ResolvedScope && !S) // Give up if no scope available.
anatofuz
parents:
diff changeset
353 return TypoCorrection();
anatofuz
parents:
diff changeset
354
anatofuz
parents:
diff changeset
355 if (Extracted->ResolvedScope)
anatofuz
parents:
diff changeset
356 Unresolved.Scopes.push_back(*Extracted->ResolvedScope);
anatofuz
parents:
diff changeset
357 else // no qualifier or qualifier is unresolved.
anatofuz
parents:
diff changeset
358 Unresolved.Scopes = collectAccessibleScopes(
anatofuz
parents:
diff changeset
359 *SemaPtr, Typo, S, static_cast<Sema::LookupNameKind>(LookupKind));
anatofuz
parents:
diff changeset
360
anatofuz
parents:
diff changeset
361 if (Extracted->UnresolvedScope) {
anatofuz
parents:
diff changeset
362 for (std::string &Scope : Unresolved.Scopes)
anatofuz
parents:
diff changeset
363 Scope += *Extracted->UnresolvedScope;
anatofuz
parents:
diff changeset
364 }
anatofuz
parents:
diff changeset
365
anatofuz
parents:
diff changeset
366 LastUnresolvedName = std::move(Unresolved);
anatofuz
parents:
diff changeset
367
anatofuz
parents:
diff changeset
368 // Never return a valid correction to try to recover. Our suggested fixes
anatofuz
parents:
diff changeset
369 // always require a rebuild.
anatofuz
parents:
diff changeset
370 return TypoCorrection();
anatofuz
parents:
diff changeset
371 }
anatofuz
parents:
diff changeset
372
anatofuz
parents:
diff changeset
373 private:
anatofuz
parents:
diff changeset
374 Sema *SemaPtr = nullptr;
anatofuz
parents:
diff changeset
375
anatofuz
parents:
diff changeset
376 llvm::Optional<UnresolvedName> &LastUnresolvedName;
anatofuz
parents:
diff changeset
377 };
anatofuz
parents:
diff changeset
378
anatofuz
parents:
diff changeset
379 llvm::IntrusiveRefCntPtr<ExternalSemaSource>
anatofuz
parents:
diff changeset
380 IncludeFixer::unresolvedNameRecorder() {
anatofuz
parents:
diff changeset
381 return new UnresolvedNameRecorder(LastUnresolvedName);
anatofuz
parents:
diff changeset
382 }
anatofuz
parents:
diff changeset
383
anatofuz
parents:
diff changeset
384 std::vector<Fix> IncludeFixer::fixUnresolvedName() const {
anatofuz
parents:
diff changeset
385 assert(LastUnresolvedName.hasValue());
anatofuz
parents:
diff changeset
386 auto &Unresolved = *LastUnresolvedName;
anatofuz
parents:
diff changeset
387 vlog("Trying to fix unresolved name \"{0}\" in scopes: [{1}]",
anatofuz
parents:
diff changeset
388 Unresolved.Name, llvm::join(Unresolved.Scopes, ", "));
anatofuz
parents:
diff changeset
389
anatofuz
parents:
diff changeset
390 FuzzyFindRequest Req;
anatofuz
parents:
diff changeset
391 Req.AnyScope = false;
anatofuz
parents:
diff changeset
392 Req.Query = Unresolved.Name;
anatofuz
parents:
diff changeset
393 Req.Scopes = Unresolved.Scopes;
anatofuz
parents:
diff changeset
394 Req.RestrictForCodeCompletion = true;
anatofuz
parents:
diff changeset
395 Req.Limit = 100;
anatofuz
parents:
diff changeset
396
anatofuz
parents:
diff changeset
397 if (llvm::Optional<const SymbolSlab *> Syms = fuzzyFindCached(Req))
anatofuz
parents:
diff changeset
398 return fixesForSymbols(**Syms);
anatofuz
parents:
diff changeset
399
anatofuz
parents:
diff changeset
400 return {};
anatofuz
parents:
diff changeset
401 }
anatofuz
parents:
diff changeset
402
anatofuz
parents:
diff changeset
403 llvm::Optional<const SymbolSlab *>
anatofuz
parents:
diff changeset
404 IncludeFixer::fuzzyFindCached(const FuzzyFindRequest &Req) const {
anatofuz
parents:
diff changeset
405 auto ReqStr = llvm::formatv("{0}", toJSON(Req)).str();
anatofuz
parents:
diff changeset
406 auto I = FuzzyFindCache.find(ReqStr);
anatofuz
parents:
diff changeset
407 if (I != FuzzyFindCache.end())
anatofuz
parents:
diff changeset
408 return &I->second;
anatofuz
parents:
diff changeset
409
anatofuz
parents:
diff changeset
410 if (IndexRequestCount >= IndexRequestLimit)
anatofuz
parents:
diff changeset
411 return llvm::None;
anatofuz
parents:
diff changeset
412 IndexRequestCount++;
anatofuz
parents:
diff changeset
413
anatofuz
parents:
diff changeset
414 SymbolSlab::Builder Matches;
anatofuz
parents:
diff changeset
415 Index.fuzzyFind(Req, [&](const Symbol &Sym) {
anatofuz
parents:
diff changeset
416 if (Sym.Name != Req.Query)
anatofuz
parents:
diff changeset
417 return;
anatofuz
parents:
diff changeset
418 if (!Sym.IncludeHeaders.empty())
anatofuz
parents:
diff changeset
419 Matches.insert(Sym);
anatofuz
parents:
diff changeset
420 });
anatofuz
parents:
diff changeset
421 auto Syms = std::move(Matches).build();
anatofuz
parents:
diff changeset
422 auto E = FuzzyFindCache.try_emplace(ReqStr, std::move(Syms));
anatofuz
parents:
diff changeset
423 return &E.first->second;
anatofuz
parents:
diff changeset
424 }
anatofuz
parents:
diff changeset
425
anatofuz
parents:
diff changeset
426 llvm::Optional<const SymbolSlab *>
anatofuz
parents:
diff changeset
427 IncludeFixer::lookupCached(const SymbolID &ID) const {
anatofuz
parents:
diff changeset
428 LookupRequest Req;
anatofuz
parents:
diff changeset
429 Req.IDs.insert(ID);
anatofuz
parents:
diff changeset
430
anatofuz
parents:
diff changeset
431 auto I = LookupCache.find(ID);
anatofuz
parents:
diff changeset
432 if (I != LookupCache.end())
anatofuz
parents:
diff changeset
433 return &I->second;
anatofuz
parents:
diff changeset
434
anatofuz
parents:
diff changeset
435 if (IndexRequestCount >= IndexRequestLimit)
anatofuz
parents:
diff changeset
436 return llvm::None;
anatofuz
parents:
diff changeset
437 IndexRequestCount++;
anatofuz
parents:
diff changeset
438
anatofuz
parents:
diff changeset
439 // FIXME: consider batching the requests for all diagnostics.
anatofuz
parents:
diff changeset
440 SymbolSlab::Builder Matches;
anatofuz
parents:
diff changeset
441 Index.lookup(Req, [&](const Symbol &Sym) { Matches.insert(Sym); });
anatofuz
parents:
diff changeset
442 auto Syms = std::move(Matches).build();
anatofuz
parents:
diff changeset
443
anatofuz
parents:
diff changeset
444 std::vector<Fix> Fixes;
anatofuz
parents:
diff changeset
445 if (!Syms.empty()) {
anatofuz
parents:
diff changeset
446 auto &Matched = *Syms.begin();
anatofuz
parents:
diff changeset
447 if (!Matched.IncludeHeaders.empty() && Matched.Definition &&
anatofuz
parents:
diff changeset
448 Matched.CanonicalDeclaration.FileURI == Matched.Definition.FileURI)
anatofuz
parents:
diff changeset
449 Fixes = fixesForSymbols(Syms);
anatofuz
parents:
diff changeset
450 }
anatofuz
parents:
diff changeset
451 auto E = LookupCache.try_emplace(ID, std::move(Syms));
anatofuz
parents:
diff changeset
452 return &E.first->second;
anatofuz
parents:
diff changeset
453 }
anatofuz
parents:
diff changeset
454
anatofuz
parents:
diff changeset
455 } // namespace clangd
anatofuz
parents:
diff changeset
456 } // namespace clang