1dd66277cSEric Liu //===--- IncludeFixer.cpp ----------------------------------------*- C++-*-===//
2dd66277cSEric Liu //
3dd66277cSEric Liu // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4dd66277cSEric Liu // See https://llvm.org/LICENSE.txt for license information.
5dd66277cSEric Liu // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6dd66277cSEric Liu //
7dd66277cSEric Liu //===----------------------------------------------------------------------===//
8dd66277cSEric Liu
9dd66277cSEric Liu #include "IncludeFixer.h"
10dd66277cSEric Liu #include "AST.h"
11dd66277cSEric Liu #include "Diagnostics.h"
12dd66277cSEric Liu #include "SourceCode.h"
13dd66277cSEric Liu #include "index/Index.h"
1408b49b53SDmitri Gribenko #include "index/Symbol.h"
15ad97ccf6SSam McCall #include "support/Logger.h"
16ad97ccf6SSam McCall #include "support/Trace.h"
17dd66277cSEric Liu #include "clang/AST/Decl.h"
18a9e9c506SEric Liu #include "clang/AST/DeclBase.h"
1956bdb0c5SIlya Biryukov #include "clang/AST/DeclarationName.h"
20a9e9c506SEric Liu #include "clang/AST/NestedNameSpecifier.h"
21dd66277cSEric Liu #include "clang/AST/Type.h"
22dd66277cSEric Liu #include "clang/Basic/Diagnostic.h"
23671eab25SSam McCall #include "clang/Basic/DiagnosticParse.h"
24dd66277cSEric Liu #include "clang/Basic/DiagnosticSema.h"
254df070a5SEric Liu #include "clang/Basic/LangOptions.h"
264df070a5SEric Liu #include "clang/Basic/SourceLocation.h"
274df070a5SEric Liu #include "clang/Basic/SourceManager.h"
284df070a5SEric Liu #include "clang/Basic/TokenKinds.h"
29cb83ea62SDmitri Gribenko #include "clang/Lex/Lexer.h"
30a9e9c506SEric Liu #include "clang/Sema/DeclSpec.h"
31a9e9c506SEric Liu #include "clang/Sema/Lookup.h"
32a9e9c506SEric Liu #include "clang/Sema/Scope.h"
33a9e9c506SEric Liu #include "clang/Sema/Sema.h"
34a9e9c506SEric Liu #include "clang/Sema/TypoCorrection.h"
35a9e9c506SEric Liu #include "llvm/ADT/DenseMap.h"
36aba43035SDmitri Gribenko #include "llvm/ADT/STLExtras.h"
3756bdb0c5SIlya Biryukov #include "llvm/ADT/StringExtras.h"
384df070a5SEric Liu #include "llvm/ADT/StringRef.h"
39a9e9c506SEric Liu #include "llvm/ADT/StringSet.h"
40dd66277cSEric Liu #include "llvm/Support/Error.h"
41dd66277cSEric Liu #include "llvm/Support/FormatVariadic.h"
42d1978fa4SKadir Cetinkaya #include <algorithm>
4371f55735SKazu Hirata #include <optional>
44d1978fa4SKadir Cetinkaya #include <set>
45d1978fa4SKadir Cetinkaya #include <string>
46a9e9c506SEric Liu #include <vector>
47dd66277cSEric Liu
48dd66277cSEric Liu namespace clang {
49dd66277cSEric Liu namespace clangd {
502676759bSSam McCall namespace {
512676759bSSam McCall
getArgStr(const clang::Diagnostic & Info,unsigned Index)52f71ffd3bSKazu Hirata std::optional<llvm::StringRef> getArgStr(const clang::Diagnostic &Info,
532676759bSSam McCall unsigned Index) {
542676759bSSam McCall switch (Info.getArgKind(Index)) {
552676759bSSam McCall case DiagnosticsEngine::ak_c_string:
562676759bSSam McCall return llvm::StringRef(Info.getArgCStr(Index));
572676759bSSam McCall case DiagnosticsEngine::ak_std_string:
582676759bSSam McCall return llvm::StringRef(Info.getArgStdStr(Index));
592676759bSSam McCall default:
60059a23c0SKazu Hirata return std::nullopt;
612676759bSSam McCall }
622676759bSSam McCall }
632676759bSSam McCall
only(std::optional<Fix> F)64f71ffd3bSKazu Hirata std::vector<Fix> only(std::optional<Fix> F) {
652676759bSSam McCall if (F)
662676759bSSam McCall return {std::move(*F)};
672676759bSSam McCall return {};
682676759bSSam McCall }
692676759bSSam McCall
702676759bSSam McCall } // namespace
71dd66277cSEric Liu
fix(DiagnosticsEngine::Level DiagLevel,const clang::Diagnostic & Info) const72dd66277cSEric Liu std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel,
73dd66277cSEric Liu const clang::Diagnostic &Info) const {
74dd66277cSEric Liu switch (Info.getID()) {
75c25ea488SSam McCall /*
76c25ea488SSam McCall There are many "incomplete type" diagnostics!
77c25ea488SSam McCall They are almost all Sema diagnostics with "incomplete" in the name.
78c25ea488SSam McCall
79c25ea488SSam McCall sed -n '/CLASS_NOTE/! s/DIAG(\\([^,]*\\).*)/ case diag::\\1:/p' \
80c25ea488SSam McCall tools/clang/include/clang/Basic/DiagnosticSemaKinds.inc | grep incomplete
81c25ea488SSam McCall */
82c25ea488SSam McCall // clang-format off
83c25ea488SSam McCall //case diag::err_alignof_member_of_incomplete_type:
84c25ea488SSam McCall case diag::err_array_incomplete_or_sizeless_type:
85c25ea488SSam McCall case diag::err_array_size_incomplete_type:
86c25ea488SSam McCall case diag::err_asm_incomplete_type:
87c25ea488SSam McCall case diag::err_assoc_type_incomplete:
88c25ea488SSam McCall case diag::err_bad_cast_incomplete:
89c25ea488SSam McCall case diag::err_call_function_incomplete_return:
90c25ea488SSam McCall case diag::err_call_incomplete_argument:
91c25ea488SSam McCall case diag::err_call_incomplete_return:
92c25ea488SSam McCall case diag::err_capture_of_incomplete_or_sizeless_type:
93c25ea488SSam McCall case diag::err_catch_incomplete:
94c25ea488SSam McCall case diag::err_catch_incomplete_ptr:
95c25ea488SSam McCall case diag::err_catch_incomplete_ref:
96c25ea488SSam McCall case diag::err_cconv_incomplete_param_type:
97c25ea488SSam McCall case diag::err_coroutine_promise_type_incomplete:
98c25ea488SSam McCall case diag::err_covariant_return_incomplete:
99c25ea488SSam McCall //case diag::err_deduced_class_template_incomplete:
100c25ea488SSam McCall case diag::err_delete_incomplete_class_type:
101c25ea488SSam McCall case diag::err_dereference_incomplete_type:
102c25ea488SSam McCall case diag::err_exception_spec_incomplete_type:
103c25ea488SSam McCall case diag::err_field_incomplete_or_sizeless:
104efd8c9edSHaojian Wu case diag::err_for_range_incomplete_type:
105efd8c9edSHaojian Wu case diag::err_func_def_incomplete_result:
106c25ea488SSam McCall case diag::err_ice_incomplete_type:
107c25ea488SSam McCall case diag::err_illegal_message_expr_incomplete_type:
108c25ea488SSam McCall case diag::err_incomplete_base_class:
109c25ea488SSam McCall case diag::err_incomplete_enum:
110c25ea488SSam McCall case diag::err_incomplete_in_exception_spec:
111c25ea488SSam McCall case diag::err_incomplete_member_access:
112c25ea488SSam McCall case diag::err_incomplete_nested_name_spec:
113c25ea488SSam McCall case diag::err_incomplete_object_call:
114c25ea488SSam McCall case diag::err_incomplete_receiver_type:
115c25ea488SSam McCall case diag::err_incomplete_synthesized_property:
116c25ea488SSam McCall case diag::err_incomplete_type:
117c25ea488SSam McCall case diag::err_incomplete_type_objc_at_encode:
118c25ea488SSam McCall case diag::err_incomplete_type_used_in_type_trait_expr:
119c25ea488SSam McCall case diag::err_incomplete_typeid:
120c25ea488SSam McCall case diag::err_init_incomplete_type:
121c25ea488SSam McCall case diag::err_invalid_incomplete_type_use:
122c25ea488SSam McCall case diag::err_lambda_incomplete_result:
123c25ea488SSam McCall //case diag::err_matrix_incomplete_index:
124c25ea488SSam McCall //case diag::err_matrix_separate_incomplete_index:
125c25ea488SSam McCall case diag::err_memptr_incomplete:
126c25ea488SSam McCall case diag::err_new_incomplete_or_sizeless_type:
127c25ea488SSam McCall case diag::err_objc_incomplete_boxed_expression_type:
128c25ea488SSam McCall case diag::err_objc_index_incomplete_class_type:
129c25ea488SSam McCall case diag::err_offsetof_incomplete_type:
130c25ea488SSam McCall case diag::err_omp_firstprivate_incomplete_type:
131c25ea488SSam McCall case diag::err_omp_incomplete_type:
132c25ea488SSam McCall case diag::err_omp_lastprivate_incomplete_type:
133c25ea488SSam McCall case diag::err_omp_linear_incomplete_type:
134c25ea488SSam McCall case diag::err_omp_private_incomplete_type:
135c25ea488SSam McCall case diag::err_omp_reduction_incomplete_type:
136c25ea488SSam McCall case diag::err_omp_section_incomplete_type:
137c25ea488SSam McCall case diag::err_omp_threadprivate_incomplete_type:
138c25ea488SSam McCall case diag::err_second_parameter_to_va_arg_incomplete:
139c25ea488SSam McCall case diag::err_sizeof_alignof_incomplete_or_sizeless_type:
140c25ea488SSam McCall case diag::err_subscript_incomplete_or_sizeless_type:
141c25ea488SSam McCall case diag::err_switch_incomplete_class_type:
142c25ea488SSam McCall case diag::err_temp_copy_incomplete:
143c25ea488SSam McCall //case diag::err_template_arg_deduced_incomplete_pack:
144c25ea488SSam McCall case diag::err_template_nontype_parm_incomplete:
145c25ea488SSam McCall //case diag::err_tentative_def_incomplete_type:
146c25ea488SSam McCall case diag::err_throw_incomplete:
147c25ea488SSam McCall case diag::err_throw_incomplete_ptr:
148c25ea488SSam McCall case diag::err_typecheck_arithmetic_incomplete_or_sizeless_type:
149c25ea488SSam McCall case diag::err_typecheck_cast_to_incomplete:
150c25ea488SSam McCall case diag::err_typecheck_decl_incomplete_type:
151c25ea488SSam McCall //case diag::err_typecheck_incomplete_array_needs_initializer:
152c25ea488SSam McCall case diag::err_typecheck_incomplete_tag:
153c25ea488SSam McCall case diag::err_typecheck_incomplete_type_not_modifiable_lvalue:
154c25ea488SSam McCall case diag::err_typecheck_nonviable_condition_incomplete:
155c25ea488SSam McCall case diag::err_underlying_type_of_incomplete_enum:
156c25ea488SSam McCall case diag::ext_incomplete_in_exception_spec:
157c25ea488SSam McCall //case diag::ext_typecheck_compare_complete_incomplete_pointers:
158c25ea488SSam McCall case diag::ext_typecheck_decl_incomplete_type:
159c25ea488SSam McCall case diag::warn_delete_incomplete:
160c25ea488SSam McCall case diag::warn_incomplete_encoded_type:
161c25ea488SSam McCall //case diag::warn_printf_incomplete_specifier:
162c25ea488SSam McCall case diag::warn_return_value_udt_incomplete:
163c25ea488SSam McCall //case diag::warn_scanf_scanlist_incomplete:
164c25ea488SSam McCall //case diag::warn_tentative_incomplete_array:
165c25ea488SSam McCall // clang-format on
166dd66277cSEric Liu // Incomplete type diagnostics should have a QualType argument for the
167dd66277cSEric Liu // incomplete type.
168dd66277cSEric Liu for (unsigned Idx = 0; Idx < Info.getNumArgs(); ++Idx) {
169dd66277cSEric Liu if (Info.getArgKind(Idx) == DiagnosticsEngine::ak_qualtype) {
170dd66277cSEric Liu auto QT = QualType::getFromOpaquePtr((void *)Info.getRawArg(Idx));
171c25ea488SSam McCall if (const Type *T = QT.getTypePtrOrNull()) {
172dd66277cSEric Liu if (T->isIncompleteType())
173dd66277cSEric Liu return fixIncompleteType(*T);
174c25ea488SSam McCall // `enum x : int;' is not formally an incomplete type.
175c25ea488SSam McCall // We may need a full definition anyway.
176c25ea488SSam McCall if (auto * ET = llvm::dyn_cast<EnumType>(T))
177c25ea488SSam McCall if (!ET->getDecl()->getDefinition())
178c25ea488SSam McCall return fixIncompleteType(*T);
179dd66277cSEric Liu }
180dd66277cSEric Liu }
181c25ea488SSam McCall }
182a9e9c506SEric Liu break;
183c25ea488SSam McCall
184a9e9c506SEric Liu case diag::err_unknown_typename:
185a9e9c506SEric Liu case diag::err_unknown_typename_suggest:
186671eab25SSam McCall case diag::err_unknown_type_or_class_name_suggest:
187671eab25SSam McCall case diag::err_expected_class_name:
188a9e9c506SEric Liu case diag::err_typename_nested_not_found:
189a9e9c506SEric Liu case diag::err_no_template:
190a9e9c506SEric Liu case diag::err_no_template_suggest:
191da2ed56fSEric Liu case diag::err_undeclared_use:
192da2ed56fSEric Liu case diag::err_undeclared_use_suggest:
193da2ed56fSEric Liu case diag::err_undeclared_var_use:
194da2ed56fSEric Liu case diag::err_undeclared_var_use_suggest:
195da2ed56fSEric Liu case diag::err_no_member: // Could be no member in namespace.
196da2ed56fSEric Liu case diag::err_no_member_suggest:
19768dbd1aeSHaojian Wu case diag::err_no_member_template:
19868dbd1aeSHaojian Wu case diag::err_no_member_template_suggest:
1991ab13793SSam McCall case diag::warn_implicit_function_decl:
2007d644e12SAaron Ballman case diag::ext_implicit_function_decl_c99:
2011ab13793SSam McCall dlog("Unresolved name at {0}, last typo was {1}",
2021ab13793SSam McCall Info.getLocation().printToString(Info.getSourceManager()),
2031ab13793SSam McCall LastUnresolvedName
2041ab13793SSam McCall ? LastUnresolvedName->Loc.printToString(Info.getSourceManager())
2051ab13793SSam McCall : "none");
206a9e9c506SEric Liu if (LastUnresolvedName) {
207b7ecf1c1SKazuaki Ishizaki // Try to fix unresolved name caused by missing declaration.
208a9e9c506SEric Liu // E.g.
209a9e9c506SEric Liu // clang::SourceManager SM;
210a9e9c506SEric Liu // ~~~~~~~~~~~~~
211a9e9c506SEric Liu // UnresolvedName
212a9e9c506SEric Liu // or
213a9e9c506SEric Liu // namespace clang { SourceManager SM; }
214a9e9c506SEric Liu // ~~~~~~~~~~~~~
215a9e9c506SEric Liu // UnresolvedName
216a9e9c506SEric Liu // We only attempt to recover a diagnostic if it has the same location as
217a9e9c506SEric Liu // the last seen unresolved name.
2181ab13793SSam McCall if (LastUnresolvedName->Loc == Info.getLocation())
219a9e9c506SEric Liu return fixUnresolvedName();
220a9e9c506SEric Liu }
2212676759bSSam McCall break;
222c25ea488SSam McCall
2232676759bSSam McCall // Cases where clang explicitly knows which header to include.
2242676759bSSam McCall // (There's no fix provided for boring formatting reasons).
2252676759bSSam McCall case diag::err_implied_std_initializer_list_not_found:
2262676759bSSam McCall return only(insertHeader("<initializer_list>"));
2272676759bSSam McCall case diag::err_need_header_before_typeid:
228056d4dcaSNathan Ridge return only(insertHeader("<typeinfo>"));
2292676759bSSam McCall case diag::err_need_header_before_placement_new:
2302676759bSSam McCall case diag::err_implicit_coroutine_std_nothrow_type_not_found:
2312676759bSSam McCall return only(insertHeader("<new>"));
2322676759bSSam McCall case diag::err_omp_implied_type_not_found:
2332676759bSSam McCall case diag::err_omp_interop_type_not_found:
2342676759bSSam McCall return only(insertHeader("<omp.h>"));
2352676759bSSam McCall case diag::err_implied_coroutine_type_not_found:
2362676759bSSam McCall return only(insertHeader("<coroutine>"));
2372676759bSSam McCall case diag::err_implied_comparison_category_type_not_found:
2382676759bSSam McCall return only(insertHeader("<compare>"));
2392676759bSSam McCall case diag::note_include_header_or_declare:
2402676759bSSam McCall if (Info.getNumArgs() > 0)
2412676759bSSam McCall if (auto Header = getArgStr(Info, 0))
2422676759bSSam McCall return only(insertHeader(("<" + *Header + ">").str(),
2435dd171dcSKazu Hirata getArgStr(Info, 1).value_or("")));
2442676759bSSam McCall break;
245dd66277cSEric Liu }
2462676759bSSam McCall
247dd66277cSEric Liu return {};
248dd66277cSEric Liu }
249dd66277cSEric Liu
insertHeader(llvm::StringRef Spelled,llvm::StringRef Symbol,tooling::IncludeDirective Directive) const250f71ffd3bSKazu Hirata std::optional<Fix> IncludeFixer::insertHeader(llvm::StringRef Spelled,
251fc46d6e6SDavid Goldman llvm::StringRef Symbol,
252fc46d6e6SDavid Goldman tooling::IncludeDirective Directive) const {
2532676759bSSam McCall Fix F;
2542676759bSSam McCall
255fc46d6e6SDavid Goldman if (auto Edit = Inserter->insert(Spelled, Directive))
2562676759bSSam McCall F.Edits.push_back(std::move(*Edit));
2572676759bSSam McCall else
258059a23c0SKazu Hirata return std::nullopt;
2592676759bSSam McCall
260fc46d6e6SDavid Goldman llvm::StringRef DirectiveSpelling =
261fc46d6e6SDavid Goldman Directive == tooling::IncludeDirective::Include ? "Include" : "Import";
2622676759bSSam McCall if (Symbol.empty())
263fc46d6e6SDavid Goldman F.Message = llvm::formatv("{0} {1}", DirectiveSpelling, Spelled);
2642676759bSSam McCall else
265fc46d6e6SDavid Goldman F.Message = llvm::formatv("{0} {1} for symbol {2}",
266fc46d6e6SDavid Goldman DirectiveSpelling, Spelled, Symbol);
2672676759bSSam McCall
2682676759bSSam McCall return F;
2692676759bSSam McCall }
2702676759bSSam McCall
fixIncompleteType(const Type & T) const271dd66277cSEric Liu std::vector<Fix> IncludeFixer::fixIncompleteType(const Type &T) const {
272dd66277cSEric Liu // Only handle incomplete TagDecl type.
273dd66277cSEric Liu const TagDecl *TD = T.getAsTagDecl();
274dd66277cSEric Liu if (!TD)
275dd66277cSEric Liu return {};
276dd66277cSEric Liu std::string TypeName = printQualifiedName(*TD);
277dd66277cSEric Liu trace::Span Tracer("Fix include for incomplete type");
278dd66277cSEric Liu SPAN_ATTACH(Tracer, "type", TypeName);
279dd66277cSEric Liu vlog("Trying to fix include for incomplete type {0}", TypeName);
280dd66277cSEric Liu
281dd66277cSEric Liu auto ID = getSymbolID(TD);
282dd66277cSEric Liu if (!ID)
283dd66277cSEric Liu return {};
284f71ffd3bSKazu Hirata std::optional<const SymbolSlab *> Symbols = lookupCached(ID);
285b3558029SEric Liu if (!Symbols)
286dd66277cSEric Liu return {};
287b3558029SEric Liu const SymbolSlab &Syms = **Symbols;
288b3558029SEric Liu std::vector<Fix> Fixes;
289b3558029SEric Liu if (!Syms.empty()) {
290b3558029SEric Liu auto &Matched = *Syms.begin();
291b3558029SEric Liu if (!Matched.IncludeHeaders.empty() && Matched.Definition &&
292b3558029SEric Liu Matched.CanonicalDeclaration.FileURI == Matched.Definition.FileURI)
293b3558029SEric Liu Fixes = fixesForSymbols(Syms);
294b3558029SEric Liu }
295b3558029SEric Liu return Fixes;
296dd66277cSEric Liu }
297dd66277cSEric Liu
fixesForSymbols(const SymbolSlab & Syms) const298b3558029SEric Liu std::vector<Fix> IncludeFixer::fixesForSymbols(const SymbolSlab &Syms) const {
299a9e9c506SEric Liu auto Inserted = [&](const Symbol &Sym, llvm::StringRef Header)
300dd66277cSEric Liu -> llvm::Expected<std::pair<std::string, bool>> {
301b70323e5SHaojian Wu auto ResolvedDeclaring =
302b70323e5SHaojian Wu URI::resolve(Sym.CanonicalDeclaration.FileURI, File);
303dd66277cSEric Liu if (!ResolvedDeclaring)
304dd66277cSEric Liu return ResolvedDeclaring.takeError();
305dd66277cSEric Liu auto ResolvedInserted = toHeaderFile(Header, File);
306dd66277cSEric Liu if (!ResolvedInserted)
307dd66277cSEric Liu return ResolvedInserted.takeError();
308b324c64bSSam McCall auto Spelled = Inserter->calculateIncludePath(*ResolvedInserted, File);
309b324c64bSSam McCall if (!Spelled)
310687e1d71SSam McCall return error("Header not on include path");
311dd66277cSEric Liu return std::make_pair(
312b324c64bSSam McCall std::move(*Spelled),
313dd66277cSEric Liu Inserter->shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted));
314dd66277cSEric Liu };
315dd66277cSEric Liu
316dd66277cSEric Liu std::vector<Fix> Fixes;
317b7ecf1c1SKazuaki Ishizaki // Deduplicate fixes by include headers. This doesn't distinguish symbols in
318a9e9c506SEric Liu // different scopes from the same header, but this case should be rare and is
319a9e9c506SEric Liu // thus ignored.
320a9e9c506SEric Liu llvm::StringSet<> InsertedHeaders;
321a9e9c506SEric Liu for (const auto &Sym : Syms) {
322dd66277cSEric Liu for (const auto &Inc : getRankedIncludes(Sym)) {
323042dd994SDavid Goldman if ((Inc.Directive & Directive) == 0)
32451f1ae52SDavid Goldman continue;
32551f1ae52SDavid Goldman if (auto ToInclude = Inserted(Sym, Inc.Header)) {
326a9e9c506SEric Liu if (ToInclude->second) {
3272676759bSSam McCall if (!InsertedHeaders.try_emplace(ToInclude->first).second)
328a9e9c506SEric Liu continue;
3292676759bSSam McCall if (auto Fix =
330fc46d6e6SDavid Goldman insertHeader(ToInclude->first, (Sym.Scope + Sym.Name).str(),
331042dd994SDavid Goldman Directive == Symbol::Import
332042dd994SDavid Goldman ? tooling::IncludeDirective::Import
333042dd994SDavid Goldman : tooling::IncludeDirective::Include))
3342676759bSSam McCall Fixes.push_back(std::move(*Fix));
335a9e9c506SEric Liu }
336dd66277cSEric Liu } else {
33751f1ae52SDavid Goldman vlog("Failed to calculate include insertion for {0} into {1}: {2}",
33851f1ae52SDavid Goldman Inc.Header, File, ToInclude.takeError());
339a9e9c506SEric Liu }
340dd66277cSEric Liu }
341dd66277cSEric Liu }
342dd66277cSEric Liu return Fixes;
343dd66277cSEric Liu }
3444df070a5SEric Liu
3454df070a5SEric Liu // Returns the identifiers qualified by an unresolved name. \p Loc is the
3464df070a5SEric Liu // start location of the unresolved name. For the example below, this returns
3474df070a5SEric Liu // "::X::Y" that is qualified by unresolved name "clangd":
3484df070a5SEric Liu // clang::clangd::X::Y
3494df070a5SEric Liu // ~
qualifiedByUnresolved(const SourceManager & SM,SourceLocation Loc,const LangOptions & LangOpts)350f71ffd3bSKazu Hirata std::optional<std::string> qualifiedByUnresolved(const SourceManager &SM,
3514df070a5SEric Liu SourceLocation Loc,
3524df070a5SEric Liu const LangOptions &LangOpts) {
3534df070a5SEric Liu std::string Result;
354b857a509SSam McCall // Accept qualifier written within macro arguments, but not macro bodies.
355b857a509SSam McCall SourceLocation NextLoc = SM.getTopMacroCallerLoc(Loc);
3564df070a5SEric Liu while (auto CCTok = Lexer::findNextToken(NextLoc, SM, LangOpts)) {
3574df070a5SEric Liu if (!CCTok->is(tok::coloncolon))
3584df070a5SEric Liu break;
3594df070a5SEric Liu auto IDTok = Lexer::findNextToken(CCTok->getLocation(), SM, LangOpts);
3604df070a5SEric Liu if (!IDTok || !IDTok->is(tok::raw_identifier))
3614df070a5SEric Liu break;
3624df070a5SEric Liu Result.append(("::" + IDTok->getRawIdentifier()).str());
3634df070a5SEric Liu NextLoc = IDTok->getLocation();
3644df070a5SEric Liu }
3654df070a5SEric Liu if (Result.empty())
366059a23c0SKazu Hirata return std::nullopt;
3674df070a5SEric Liu return Result;
3684df070a5SEric Liu }
3694df070a5SEric Liu
3704df070a5SEric Liu // An unresolved name and its scope information that can be extracted cheaply.
3714df070a5SEric Liu struct CheapUnresolvedName {
3724df070a5SEric Liu std::string Name;
3734df070a5SEric Liu // This is the part of what was typed that was resolved, and it's in its
3744df070a5SEric Liu // resolved form not its typed form (think `namespace clang { clangd::x }` -->
3754df070a5SEric Liu // `clang::clangd::`).
376f71ffd3bSKazu Hirata std::optional<std::string> ResolvedScope;
3774df070a5SEric Liu
3784df070a5SEric Liu // Unresolved part of the scope. When the unresolved name is a specifier, we
3794df070a5SEric Liu // use the name that comes after it as the alternative name to resolve and use
3804df070a5SEric Liu // the specifier as the extra scope in the accessible scopes.
381f71ffd3bSKazu Hirata std::optional<std::string> UnresolvedScope;
3824df070a5SEric Liu };
3834df070a5SEric Liu
getSpelledSpecifier(const CXXScopeSpec & SS,const SourceManager & SM)384f71ffd3bSKazu Hirata std::optional<std::string> getSpelledSpecifier(const CXXScopeSpec &SS,
385b857a509SSam McCall const SourceManager &SM) {
386b857a509SSam McCall // Support specifiers written within a single macro argument.
387b857a509SSam McCall if (!SM.isWrittenInSameFile(SS.getBeginLoc(), SS.getEndLoc()))
388059a23c0SKazu Hirata return std::nullopt;
389b857a509SSam McCall SourceRange Range(SM.getTopMacroCallerLoc(SS.getBeginLoc()), SM.getTopMacroCallerLoc(SS.getEndLoc()));
390b857a509SSam McCall if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
391059a23c0SKazu Hirata return std::nullopt;
392b857a509SSam McCall
393b857a509SSam McCall return (toSourceCode(SM, Range) + "::").str();
394b857a509SSam McCall }
395b857a509SSam McCall
3964df070a5SEric Liu // Extracts unresolved name and scope information around \p Unresolved.
3974df070a5SEric Liu // FIXME: try to merge this with the scope-wrangling code in CodeComplete.
extractUnresolvedNameCheaply(const SourceManager & SM,const DeclarationNameInfo & Unresolved,CXXScopeSpec * SS,const LangOptions & LangOpts,bool UnresolvedIsSpecifier)398f71ffd3bSKazu Hirata std::optional<CheapUnresolvedName> extractUnresolvedNameCheaply(
3994df070a5SEric Liu const SourceManager &SM, const DeclarationNameInfo &Unresolved,
4004df070a5SEric Liu CXXScopeSpec *SS, const LangOptions &LangOpts, bool UnresolvedIsSpecifier) {
4014df070a5SEric Liu CheapUnresolvedName Result;
4024df070a5SEric Liu Result.Name = Unresolved.getAsString();
4034df070a5SEric Liu if (SS && SS->isNotEmpty()) { // "::" or "ns::"
4044df070a5SEric Liu if (auto *Nested = SS->getScopeRep()) {
405b857a509SSam McCall if (Nested->getKind() == NestedNameSpecifier::Global) {
4064df070a5SEric Liu Result.ResolvedScope = "";
407b857a509SSam McCall } else if (const auto *NS = Nested->getAsNamespace()) {
408b857a509SSam McCall std::string SpecifiedNS = printNamespaceScope(*NS);
409f71ffd3bSKazu Hirata std::optional<std::string> Spelling = getSpelledSpecifier(*SS, SM);
4104df070a5SEric Liu
4114df070a5SEric Liu // Check the specifier spelled in the source.
412b857a509SSam McCall // If the resolved scope doesn't end with the spelled scope, the
413b857a509SSam McCall // resolved scope may come from a sema typo correction. For example,
4144df070a5SEric Liu // sema assumes that "clangd::" is a typo of "clang::" and uses
4154df070a5SEric Liu // "clang::" as the specified scope in:
4164df070a5SEric Liu // namespace clang { clangd::X; }
4174df070a5SEric Liu // In this case, we use the "typo" specifier as extra scope instead
4184df070a5SEric Liu // of using the scope assumed by sema.
419*d5953e3eSKazu Hirata if (!Spelling || llvm::StringRef(SpecifiedNS).ends_with(*Spelling)) {
420b857a509SSam McCall Result.ResolvedScope = std::move(SpecifiedNS);
421b857a509SSam McCall } else {
422b857a509SSam McCall Result.UnresolvedScope = std::move(*Spelling);
423b857a509SSam McCall }
4244df070a5SEric Liu } else if (const auto *ANS = Nested->getAsNamespaceAlias()) {
4254df070a5SEric Liu Result.ResolvedScope = printNamespaceScope(*ANS->getNamespace());
4264df070a5SEric Liu } else {
4274df070a5SEric Liu // We don't fix symbols in scopes that are not top-level e.g. class
4284df070a5SEric Liu // members, as we don't collect includes for them.
429059a23c0SKazu Hirata return std::nullopt;
4304df070a5SEric Liu }
4314df070a5SEric Liu }
4324df070a5SEric Liu }
4334df070a5SEric Liu
4344df070a5SEric Liu if (UnresolvedIsSpecifier) {
4354df070a5SEric Liu // If the unresolved name is a specifier e.g.
4364df070a5SEric Liu // clang::clangd::X
4374df070a5SEric Liu // ~~~~~~
4384df070a5SEric Liu // We try to resolve clang::clangd::X instead of clang::clangd.
4394df070a5SEric Liu // FIXME: We won't be able to fix include if the specifier is what we
4404df070a5SEric Liu // should resolve (e.g. it's a class scope specifier). Collecting include
4414df070a5SEric Liu // headers for nested types could make this work.
4424df070a5SEric Liu
4434df070a5SEric Liu // Not using the end location as it doesn't always point to the end of
4444df070a5SEric Liu // identifier.
4454df070a5SEric Liu if (auto QualifiedByUnresolved =
4464df070a5SEric Liu qualifiedByUnresolved(SM, Unresolved.getBeginLoc(), LangOpts)) {
4474df070a5SEric Liu auto Split = splitQualifiedName(*QualifiedByUnresolved);
4484df070a5SEric Liu if (!Result.UnresolvedScope)
4494df070a5SEric Liu Result.UnresolvedScope.emplace();
4504df070a5SEric Liu // If UnresolvedSpecifiedScope is already set, we simply append the
4514df070a5SEric Liu // extra scope. Suppose the unresolved name is "index" in the following
4524df070a5SEric Liu // example:
4534df070a5SEric Liu // namespace clang { clangd::index::X; }
4544df070a5SEric Liu // ~~~~~~ ~~~~~
4554df070a5SEric Liu // "clangd::" is assumed to be clang:: by Sema, and we would have used
4564df070a5SEric Liu // it as extra scope. With "index" being a specifier, we append "index::"
4574df070a5SEric Liu // to the extra scope.
4584df070a5SEric Liu Result.UnresolvedScope->append((Result.Name + Split.first).str());
459adcd0268SBenjamin Kramer Result.Name = std::string(Split.second);
4604df070a5SEric Liu }
4614df070a5SEric Liu }
4624df070a5SEric Liu return Result;
4634df070a5SEric Liu }
4644df070a5SEric Liu
46556bdb0c5SIlya Biryukov /// Returns all namespace scopes that the unqualified lookup would visit.
46656bdb0c5SIlya Biryukov std::vector<std::string>
collectAccessibleScopes(Sema & Sem,const DeclarationNameInfo & Typo,Scope * S,Sema::LookupNameKind LookupKind)46756bdb0c5SIlya Biryukov collectAccessibleScopes(Sema &Sem, const DeclarationNameInfo &Typo, Scope *S,
46856bdb0c5SIlya Biryukov Sema::LookupNameKind LookupKind) {
469d1978fa4SKadir Cetinkaya // Collects contexts visited during a Sema name lookup.
470d1978fa4SKadir Cetinkaya struct VisitedContextCollector : public VisibleDeclConsumer {
471d1978fa4SKadir Cetinkaya VisitedContextCollector(std::vector<std::string> &Out) : Out(Out) {}
472d1978fa4SKadir Cetinkaya void EnteredContext(DeclContext *Ctx) override {
473d1978fa4SKadir Cetinkaya if (llvm::isa<NamespaceDecl>(Ctx))
474d1978fa4SKadir Cetinkaya Out.push_back(printNamespaceScope(*Ctx));
475d1978fa4SKadir Cetinkaya }
476d1978fa4SKadir Cetinkaya void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
477d1978fa4SKadir Cetinkaya bool InBaseClass) override {}
478d1978fa4SKadir Cetinkaya std::vector<std::string> &Out;
479d1978fa4SKadir Cetinkaya };
480d1978fa4SKadir Cetinkaya
48156bdb0c5SIlya Biryukov std::vector<std::string> Scopes;
482d1978fa4SKadir Cetinkaya Scopes.push_back("");
483d1978fa4SKadir Cetinkaya VisitedContextCollector Collector(Scopes);
48456bdb0c5SIlya Biryukov Sem.LookupVisibleDecls(S, LookupKind, Collector,
48556bdb0c5SIlya Biryukov /*IncludeGlobalScope=*/false,
48656bdb0c5SIlya Biryukov /*LoadExternal=*/false);
487aba43035SDmitri Gribenko llvm::sort(Scopes);
488d1978fa4SKadir Cetinkaya Scopes.erase(std::unique(Scopes.begin(), Scopes.end()), Scopes.end());
48956bdb0c5SIlya Biryukov return Scopes;
49056bdb0c5SIlya Biryukov }
49156bdb0c5SIlya Biryukov
492a9e9c506SEric Liu class IncludeFixer::UnresolvedNameRecorder : public ExternalSemaSource {
493a9e9c506SEric Liu public:
UnresolvedNameRecorder(std::optional<UnresolvedName> & LastUnresolvedName)494f71ffd3bSKazu Hirata UnresolvedNameRecorder(std::optional<UnresolvedName> &LastUnresolvedName)
495a9e9c506SEric Liu : LastUnresolvedName(LastUnresolvedName) {}
496a9e9c506SEric Liu
InitializeSema(Sema & S)497a9e9c506SEric Liu void InitializeSema(Sema &S) override { this->SemaPtr = &S; }
498a9e9c506SEric Liu
499a9e9c506SEric Liu // Captures the latest typo and treat it as an unresolved name that can
500a9e9c506SEric Liu // potentially be fixed by adding #includes.
CorrectTypo(const DeclarationNameInfo & Typo,int LookupKind,Scope * S,CXXScopeSpec * SS,CorrectionCandidateCallback & CCC,DeclContext * MemberContext,bool EnteringContext,const ObjCObjectPointerType * OPT)501a9e9c506SEric Liu TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
502a9e9c506SEric Liu Scope *S, CXXScopeSpec *SS,
503a9e9c506SEric Liu CorrectionCandidateCallback &CCC,
504a9e9c506SEric Liu DeclContext *MemberContext, bool EnteringContext,
505a9e9c506SEric Liu const ObjCObjectPointerType *OPT) override {
5061ab13793SSam McCall dlog("CorrectTypo: {0}", Typo.getAsString());
507a9e9c506SEric Liu assert(SemaPtr && "Sema must have been set.");
508a9e9c506SEric Liu if (SemaPtr->isSFINAEContext())
509a9e9c506SEric Liu return TypoCorrection();
5106ae86ea2SHaojian Wu if (!isInsideMainFile(Typo.getLoc(), SemaPtr->SourceMgr))
511a9e9c506SEric Liu return clang::TypoCorrection();
512a9e9c506SEric Liu
5134df070a5SEric Liu auto Extracted = extractUnresolvedNameCheaply(
5144df070a5SEric Liu SemaPtr->SourceMgr, Typo, SS, SemaPtr->LangOpts,
5154df070a5SEric Liu static_cast<Sema::LookupNameKind>(LookupKind) ==
5164df070a5SEric Liu Sema::LookupNameKind::LookupNestedNameSpecifierName);
5174df070a5SEric Liu if (!Extracted)
518a9e9c506SEric Liu return TypoCorrection();
519a9e9c506SEric Liu
52056bdb0c5SIlya Biryukov UnresolvedName Unresolved;
52156bdb0c5SIlya Biryukov Unresolved.Name = Extracted->Name;
52256bdb0c5SIlya Biryukov Unresolved.Loc = Typo.getBeginLoc();
52356bdb0c5SIlya Biryukov if (!Extracted->ResolvedScope && !S) // Give up if no scope available.
5244df070a5SEric Liu return TypoCorrection();
5254df070a5SEric Liu
52656bdb0c5SIlya Biryukov if (Extracted->ResolvedScope)
52756bdb0c5SIlya Biryukov Unresolved.Scopes.push_back(*Extracted->ResolvedScope);
52856bdb0c5SIlya Biryukov else // no qualifier or qualifier is unresolved.
52956bdb0c5SIlya Biryukov Unresolved.Scopes = collectAccessibleScopes(
53056bdb0c5SIlya Biryukov *SemaPtr, Typo, S, static_cast<Sema::LookupNameKind>(LookupKind));
531a9e9c506SEric Liu
53256bdb0c5SIlya Biryukov if (Extracted->UnresolvedScope) {
53356bdb0c5SIlya Biryukov for (std::string &Scope : Unresolved.Scopes)
53456bdb0c5SIlya Biryukov Scope += *Extracted->UnresolvedScope;
535a9e9c506SEric Liu }
5364df070a5SEric Liu
537a9e9c506SEric Liu LastUnresolvedName = std::move(Unresolved);
538a9e9c506SEric Liu
539a9e9c506SEric Liu // Never return a valid correction to try to recover. Our suggested fixes
540a9e9c506SEric Liu // always require a rebuild.
541a9e9c506SEric Liu return TypoCorrection();
542a9e9c506SEric Liu }
543a9e9c506SEric Liu
544a9e9c506SEric Liu private:
545a9e9c506SEric Liu Sema *SemaPtr = nullptr;
546a9e9c506SEric Liu
547f71ffd3bSKazu Hirata std::optional<UnresolvedName> &LastUnresolvedName;
548a9e9c506SEric Liu };
549a9e9c506SEric Liu
550a9e9c506SEric Liu llvm::IntrusiveRefCntPtr<ExternalSemaSource>
unresolvedNameRecorder()551a9e9c506SEric Liu IncludeFixer::unresolvedNameRecorder() {
552a9e9c506SEric Liu return new UnresolvedNameRecorder(LastUnresolvedName);
553a9e9c506SEric Liu }
554a9e9c506SEric Liu
fixUnresolvedName() const555a9e9c506SEric Liu std::vector<Fix> IncludeFixer::fixUnresolvedName() const {
5565413bf1bSKazu Hirata assert(LastUnresolvedName);
557a9e9c506SEric Liu auto &Unresolved = *LastUnresolvedName;
558a9e9c506SEric Liu vlog("Trying to fix unresolved name \"{0}\" in scopes: [{1}]",
55956bdb0c5SIlya Biryukov Unresolved.Name, llvm::join(Unresolved.Scopes, ", "));
560a9e9c506SEric Liu
561a9e9c506SEric Liu FuzzyFindRequest Req;
562a9e9c506SEric Liu Req.AnyScope = false;
563a9e9c506SEric Liu Req.Query = Unresolved.Name;
56456bdb0c5SIlya Biryukov Req.Scopes = Unresolved.Scopes;
565a9e9c506SEric Liu Req.RestrictForCodeCompletion = true;
566a9e9c506SEric Liu Req.Limit = 100;
567a9e9c506SEric Liu
568f71ffd3bSKazu Hirata if (std::optional<const SymbolSlab *> Syms = fuzzyFindCached(Req))
569b3558029SEric Liu return fixesForSymbols(**Syms);
570b3558029SEric Liu
571b3558029SEric Liu return {};
572b3558029SEric Liu }
573b3558029SEric Liu
574f71ffd3bSKazu Hirata std::optional<const SymbolSlab *>
fuzzyFindCached(const FuzzyFindRequest & Req) const575b3558029SEric Liu IncludeFixer::fuzzyFindCached(const FuzzyFindRequest &Req) const {
576b3558029SEric Liu auto ReqStr = llvm::formatv("{0}", toJSON(Req)).str();
577b3558029SEric Liu auto I = FuzzyFindCache.find(ReqStr);
578b3558029SEric Liu if (I != FuzzyFindCache.end())
579b3558029SEric Liu return &I->second;
580b3558029SEric Liu
581b3558029SEric Liu if (IndexRequestCount >= IndexRequestLimit)
582059a23c0SKazu Hirata return std::nullopt;
583b3558029SEric Liu IndexRequestCount++;
584b3558029SEric Liu
585a9e9c506SEric Liu SymbolSlab::Builder Matches;
586a9e9c506SEric Liu Index.fuzzyFind(Req, [&](const Symbol &Sym) {
587a9e9c506SEric Liu if (Sym.Name != Req.Query)
588a9e9c506SEric Liu return;
589a9e9c506SEric Liu if (!Sym.IncludeHeaders.empty())
590a9e9c506SEric Liu Matches.insert(Sym);
591a9e9c506SEric Liu });
592a9e9c506SEric Liu auto Syms = std::move(Matches).build();
593b3558029SEric Liu auto E = FuzzyFindCache.try_emplace(ReqStr, std::move(Syms));
594b3558029SEric Liu return &E.first->second;
595b3558029SEric Liu }
596b3558029SEric Liu
597f71ffd3bSKazu Hirata std::optional<const SymbolSlab *>
lookupCached(const SymbolID & ID) const598b3558029SEric Liu IncludeFixer::lookupCached(const SymbolID &ID) const {
599b3558029SEric Liu LookupRequest Req;
600b3558029SEric Liu Req.IDs.insert(ID);
601b3558029SEric Liu
602b3558029SEric Liu auto I = LookupCache.find(ID);
603b3558029SEric Liu if (I != LookupCache.end())
604b3558029SEric Liu return &I->second;
605b3558029SEric Liu
606b3558029SEric Liu if (IndexRequestCount >= IndexRequestLimit)
607059a23c0SKazu Hirata return std::nullopt;
608b3558029SEric Liu IndexRequestCount++;
609b3558029SEric Liu
610b3558029SEric Liu // FIXME: consider batching the requests for all diagnostics.
611b3558029SEric Liu SymbolSlab::Builder Matches;
612b3558029SEric Liu Index.lookup(Req, [&](const Symbol &Sym) { Matches.insert(Sym); });
613b3558029SEric Liu auto Syms = std::move(Matches).build();
614b3558029SEric Liu
615b3558029SEric Liu std::vector<Fix> Fixes;
616b3558029SEric Liu if (!Syms.empty()) {
617b3558029SEric Liu auto &Matched = *Syms.begin();
618b3558029SEric Liu if (!Matched.IncludeHeaders.empty() && Matched.Definition &&
619b3558029SEric Liu Matched.CanonicalDeclaration.FileURI == Matched.Definition.FileURI)
620b3558029SEric Liu Fixes = fixesForSymbols(Syms);
621b3558029SEric Liu }
622b3558029SEric Liu auto E = LookupCache.try_emplace(ID, std::move(Syms));
623b3558029SEric Liu return &E.first->second;
624a9e9c506SEric Liu }
625dd66277cSEric Liu
626dd66277cSEric Liu } // namespace clangd
627dd66277cSEric Liu } // namespace clang
628