xref: /llvm-project/clang-tools-extra/clangd/IncludeFixer.cpp (revision d5953e3e3092f7142a07aa012fc9665ede09e53b)
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