1 //===--- IncludeFixer.cpp ----------------------------------------*- C++-*-===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "IncludeFixer.h" 10 #include "AST.h" 11 #include "Diagnostics.h" 12 #include "SourceCode.h" 13 #include "index/Index.h" 14 #include "index/Symbol.h" 15 #include "support/Logger.h" 16 #include "support/Trace.h" 17 #include "clang/AST/Decl.h" 18 #include "clang/AST/DeclBase.h" 19 #include "clang/AST/DeclarationName.h" 20 #include "clang/AST/NestedNameSpecifier.h" 21 #include "clang/AST/Type.h" 22 #include "clang/Basic/Diagnostic.h" 23 #include "clang/Basic/DiagnosticSema.h" 24 #include "clang/Basic/LangOptions.h" 25 #include "clang/Basic/SourceLocation.h" 26 #include "clang/Basic/SourceManager.h" 27 #include "clang/Basic/TokenKinds.h" 28 #include "clang/Lex/Lexer.h" 29 #include "clang/Sema/DeclSpec.h" 30 #include "clang/Sema/Lookup.h" 31 #include "clang/Sema/Scope.h" 32 #include "clang/Sema/Sema.h" 33 #include "clang/Sema/TypoCorrection.h" 34 #include "llvm/ADT/DenseMap.h" 35 #include "llvm/ADT/None.h" 36 #include "llvm/ADT/Optional.h" 37 #include "llvm/ADT/StringExtras.h" 38 #include "llvm/ADT/StringRef.h" 39 #include "llvm/ADT/StringSet.h" 40 #include "llvm/Support/Error.h" 41 #include "llvm/Support/FormatVariadic.h" 42 #include <algorithm> 43 #include <set> 44 #include <string> 45 #include <vector> 46 47 namespace clang { 48 namespace clangd { 49 namespace { 50 51 llvm::Optional<llvm::StringRef> getArgStr(const clang::Diagnostic &Info, 52 unsigned Index) { 53 switch (Info.getArgKind(Index)) { 54 case DiagnosticsEngine::ak_c_string: 55 return llvm::StringRef(Info.getArgCStr(Index)); 56 case DiagnosticsEngine::ak_std_string: 57 return llvm::StringRef(Info.getArgStdStr(Index)); 58 default: 59 return llvm::None; 60 } 61 } 62 63 std::vector<Fix> only(llvm::Optional<Fix> F) { 64 if (F) 65 return {std::move(*F)}; 66 return {}; 67 } 68 69 } // namespace 70 71 std::vector<Fix> IncludeFixer::fix(DiagnosticsEngine::Level DiagLevel, 72 const clang::Diagnostic &Info) const { 73 switch (Info.getID()) { 74 /* 75 There are many "incomplete type" diagnostics! 76 They are almost all Sema diagnostics with "incomplete" in the name. 77 78 sed -n '/CLASS_NOTE/! s/DIAG(\\([^,]*\\).*)/ case diag::\\1:/p' \ 79 tools/clang/include/clang/Basic/DiagnosticSemaKinds.inc | grep incomplete 80 */ 81 // clang-format off 82 //case diag::err_alignof_member_of_incomplete_type: 83 case diag::err_array_incomplete_or_sizeless_type: 84 case diag::err_array_size_incomplete_type: 85 case diag::err_asm_incomplete_type: 86 case diag::err_assoc_type_incomplete: 87 case diag::err_bad_cast_incomplete: 88 case diag::err_call_function_incomplete_return: 89 case diag::err_call_incomplete_argument: 90 case diag::err_call_incomplete_return: 91 case diag::err_capture_of_incomplete_or_sizeless_type: 92 case diag::err_catch_incomplete: 93 case diag::err_catch_incomplete_ptr: 94 case diag::err_catch_incomplete_ref: 95 case diag::err_cconv_incomplete_param_type: 96 case diag::err_coroutine_promise_type_incomplete: 97 case diag::err_covariant_return_incomplete: 98 //case diag::err_deduced_class_template_incomplete: 99 case diag::err_delete_incomplete_class_type: 100 case diag::err_dereference_incomplete_type: 101 case diag::err_exception_spec_incomplete_type: 102 case diag::err_field_incomplete_or_sizeless: 103 case diag::err_for_range_incomplete_type: 104 case diag::err_func_def_incomplete_result: 105 case diag::err_ice_incomplete_type: 106 case diag::err_illegal_message_expr_incomplete_type: 107 case diag::err_incomplete_base_class: 108 case diag::err_incomplete_enum: 109 case diag::err_incomplete_in_exception_spec: 110 case diag::err_incomplete_member_access: 111 case diag::err_incomplete_nested_name_spec: 112 case diag::err_incomplete_object_call: 113 case diag::err_incomplete_receiver_type: 114 case diag::err_incomplete_synthesized_property: 115 case diag::err_incomplete_type: 116 case diag::err_incomplete_type_objc_at_encode: 117 case diag::err_incomplete_type_used_in_type_trait_expr: 118 case diag::err_incomplete_typeid: 119 case diag::err_init_incomplete_type: 120 case diag::err_invalid_incomplete_type_use: 121 case diag::err_lambda_incomplete_result: 122 //case diag::err_matrix_incomplete_index: 123 //case diag::err_matrix_separate_incomplete_index: 124 case diag::err_memptr_incomplete: 125 case diag::err_new_incomplete_or_sizeless_type: 126 case diag::err_objc_incomplete_boxed_expression_type: 127 case diag::err_objc_index_incomplete_class_type: 128 case diag::err_offsetof_incomplete_type: 129 case diag::err_omp_firstprivate_incomplete_type: 130 case diag::err_omp_incomplete_type: 131 case diag::err_omp_lastprivate_incomplete_type: 132 case diag::err_omp_linear_incomplete_type: 133 case diag::err_omp_private_incomplete_type: 134 case diag::err_omp_reduction_incomplete_type: 135 case diag::err_omp_section_incomplete_type: 136 case diag::err_omp_threadprivate_incomplete_type: 137 case diag::err_second_parameter_to_va_arg_incomplete: 138 case diag::err_sizeof_alignof_incomplete_or_sizeless_type: 139 case diag::err_subscript_incomplete_or_sizeless_type: 140 case diag::err_switch_incomplete_class_type: 141 case diag::err_temp_copy_incomplete: 142 //case diag::err_template_arg_deduced_incomplete_pack: 143 case diag::err_template_nontype_parm_incomplete: 144 //case diag::err_tentative_def_incomplete_type: 145 case diag::err_throw_incomplete: 146 case diag::err_throw_incomplete_ptr: 147 case diag::err_typecheck_arithmetic_incomplete_or_sizeless_type: 148 case diag::err_typecheck_cast_to_incomplete: 149 case diag::err_typecheck_decl_incomplete_type: 150 //case diag::err_typecheck_incomplete_array_needs_initializer: 151 case diag::err_typecheck_incomplete_tag: 152 case diag::err_typecheck_incomplete_type_not_modifiable_lvalue: 153 case diag::err_typecheck_nonviable_condition_incomplete: 154 case diag::err_underlying_type_of_incomplete_enum: 155 case diag::ext_incomplete_in_exception_spec: 156 //case diag::ext_typecheck_compare_complete_incomplete_pointers: 157 case diag::ext_typecheck_decl_incomplete_type: 158 case diag::warn_delete_incomplete: 159 case diag::warn_incomplete_encoded_type: 160 //case diag::warn_printf_incomplete_specifier: 161 case diag::warn_return_value_udt_incomplete: 162 //case diag::warn_scanf_scanlist_incomplete: 163 //case diag::warn_tentative_incomplete_array: 164 // clang-format on 165 // Incomplete type diagnostics should have a QualType argument for the 166 // incomplete type. 167 for (unsigned Idx = 0; Idx < Info.getNumArgs(); ++Idx) { 168 if (Info.getArgKind(Idx) == DiagnosticsEngine::ak_qualtype) { 169 auto QT = QualType::getFromOpaquePtr((void *)Info.getRawArg(Idx)); 170 if (const Type *T = QT.getTypePtrOrNull()) { 171 if (T->isIncompleteType()) 172 return fixIncompleteType(*T); 173 // `enum x : int;' is not formally an incomplete type. 174 // We may need a full definition anyway. 175 if (auto * ET = llvm::dyn_cast<EnumType>(T)) 176 if (!ET->getDecl()->getDefinition()) 177 return fixIncompleteType(*T); 178 } 179 } 180 } 181 break; 182 183 case diag::err_unknown_typename: 184 case diag::err_unknown_typename_suggest: 185 case diag::err_typename_nested_not_found: 186 case diag::err_no_template: 187 case diag::err_no_template_suggest: 188 case diag::err_undeclared_use: 189 case diag::err_undeclared_use_suggest: 190 case diag::err_undeclared_var_use: 191 case diag::err_undeclared_var_use_suggest: 192 case diag::err_no_member: // Could be no member in namespace. 193 case diag::err_no_member_suggest: 194 case diag::err_no_member_template: 195 case diag::err_no_member_template_suggest: 196 case diag::warn_implicit_function_decl: 197 case diag::ext_implicit_function_decl: 198 case diag::err_opencl_implicit_function_decl: 199 dlog("Unresolved name at {0}, last typo was {1}", 200 Info.getLocation().printToString(Info.getSourceManager()), 201 LastUnresolvedName 202 ? LastUnresolvedName->Loc.printToString(Info.getSourceManager()) 203 : "none"); 204 if (LastUnresolvedName) { 205 // Try to fix unresolved name caused by missing declaration. 206 // E.g. 207 // clang::SourceManager SM; 208 // ~~~~~~~~~~~~~ 209 // UnresolvedName 210 // or 211 // namespace clang { SourceManager SM; } 212 // ~~~~~~~~~~~~~ 213 // UnresolvedName 214 // We only attempt to recover a diagnostic if it has the same location as 215 // the last seen unresolved name. 216 if (LastUnresolvedName->Loc == Info.getLocation()) 217 return fixUnresolvedName(); 218 } 219 break; 220 221 // Cases where clang explicitly knows which header to include. 222 // (There's no fix provided for boring formatting reasons). 223 case diag::err_implied_std_initializer_list_not_found: 224 return only(insertHeader("<initializer_list>")); 225 case diag::err_need_header_before_typeid: 226 return only(insertHeader("<typeid>")); 227 case diag::err_need_header_before_ms_uuidof: 228 return only(insertHeader("<guiddef.h>")); 229 case diag::err_need_header_before_placement_new: 230 case diag::err_implicit_coroutine_std_nothrow_type_not_found: 231 return only(insertHeader("<new>")); 232 case diag::err_omp_implied_type_not_found: 233 case diag::err_omp_interop_type_not_found: 234 return only(insertHeader("<omp.h>")); 235 case diag::err_implied_coroutine_type_not_found: 236 return only(insertHeader("<coroutine>")); 237 case diag::err_implied_comparison_category_type_not_found: 238 return only(insertHeader("<compare>")); 239 case diag::note_include_header_or_declare: 240 if (Info.getNumArgs() > 0) 241 if (auto Header = getArgStr(Info, 0)) 242 return only(insertHeader(("<" + *Header + ">").str(), 243 getArgStr(Info, 1).getValueOr(""))); 244 break; 245 } 246 247 return {}; 248 } 249 250 llvm::Optional<Fix> IncludeFixer::insertHeader(llvm::StringRef Spelled, 251 llvm::StringRef Symbol) const { 252 Fix F; 253 254 if (auto Edit = Inserter->insert(Spelled)) 255 F.Edits.push_back(std::move(*Edit)); 256 else 257 return llvm::None; 258 259 if (Symbol.empty()) 260 F.Message = llvm::formatv("Include {0}", Spelled); 261 else 262 F.Message = llvm::formatv("Include {0} for symbol {1}", Spelled, Symbol); 263 264 return F; 265 } 266 267 std::vector<Fix> IncludeFixer::fixIncompleteType(const Type &T) const { 268 // Only handle incomplete TagDecl type. 269 const TagDecl *TD = T.getAsTagDecl(); 270 if (!TD) 271 return {}; 272 std::string TypeName = printQualifiedName(*TD); 273 trace::Span Tracer("Fix include for incomplete type"); 274 SPAN_ATTACH(Tracer, "type", TypeName); 275 vlog("Trying to fix include for incomplete type {0}", TypeName); 276 277 auto ID = getSymbolID(TD); 278 if (!ID) 279 return {}; 280 llvm::Optional<const SymbolSlab *> Symbols = lookupCached(ID); 281 if (!Symbols) 282 return {}; 283 const SymbolSlab &Syms = **Symbols; 284 std::vector<Fix> Fixes; 285 if (!Syms.empty()) { 286 auto &Matched = *Syms.begin(); 287 if (!Matched.IncludeHeaders.empty() && Matched.Definition && 288 Matched.CanonicalDeclaration.FileURI == Matched.Definition.FileURI) 289 Fixes = fixesForSymbols(Syms); 290 } 291 return Fixes; 292 } 293 294 std::vector<Fix> IncludeFixer::fixesForSymbols(const SymbolSlab &Syms) const { 295 auto Inserted = [&](const Symbol &Sym, llvm::StringRef Header) 296 -> llvm::Expected<std::pair<std::string, bool>> { 297 auto ResolvedDeclaring = 298 URI::resolve(Sym.CanonicalDeclaration.FileURI, File); 299 if (!ResolvedDeclaring) 300 return ResolvedDeclaring.takeError(); 301 auto ResolvedInserted = toHeaderFile(Header, File); 302 if (!ResolvedInserted) 303 return ResolvedInserted.takeError(); 304 auto Spelled = Inserter->calculateIncludePath(*ResolvedInserted, File); 305 if (!Spelled) 306 return error("Header not on include path"); 307 return std::make_pair( 308 std::move(*Spelled), 309 Inserter->shouldInsertInclude(*ResolvedDeclaring, *ResolvedInserted)); 310 }; 311 312 std::vector<Fix> Fixes; 313 // Deduplicate fixes by include headers. This doesn't distinguish symbols in 314 // different scopes from the same header, but this case should be rare and is 315 // thus ignored. 316 llvm::StringSet<> InsertedHeaders; 317 for (const auto &Sym : Syms) { 318 for (const auto &Inc : getRankedIncludes(Sym)) { 319 if (auto ToInclude = Inserted(Sym, Inc)) { 320 if (ToInclude->second) { 321 if (!InsertedHeaders.try_emplace(ToInclude->first).second) 322 continue; 323 if (auto Fix = 324 insertHeader(ToInclude->first, (Sym.Scope + Sym.Name).str())) 325 Fixes.push_back(std::move(*Fix)); 326 } 327 } else { 328 vlog("Failed to calculate include insertion for {0} into {1}: {2}", Inc, 329 File, ToInclude.takeError()); 330 } 331 } 332 } 333 return Fixes; 334 } 335 336 // Returns the identifiers qualified by an unresolved name. \p Loc is the 337 // start location of the unresolved name. For the example below, this returns 338 // "::X::Y" that is qualified by unresolved name "clangd": 339 // clang::clangd::X::Y 340 // ~ 341 llvm::Optional<std::string> qualifiedByUnresolved(const SourceManager &SM, 342 SourceLocation Loc, 343 const LangOptions &LangOpts) { 344 std::string Result; 345 346 SourceLocation NextLoc = Loc; 347 while (auto CCTok = Lexer::findNextToken(NextLoc, SM, LangOpts)) { 348 if (!CCTok->is(tok::coloncolon)) 349 break; 350 auto IDTok = Lexer::findNextToken(CCTok->getLocation(), SM, LangOpts); 351 if (!IDTok || !IDTok->is(tok::raw_identifier)) 352 break; 353 Result.append(("::" + IDTok->getRawIdentifier()).str()); 354 NextLoc = IDTok->getLocation(); 355 } 356 if (Result.empty()) 357 return llvm::None; 358 return Result; 359 } 360 361 // An unresolved name and its scope information that can be extracted cheaply. 362 struct CheapUnresolvedName { 363 std::string Name; 364 // This is the part of what was typed that was resolved, and it's in its 365 // resolved form not its typed form (think `namespace clang { clangd::x }` --> 366 // `clang::clangd::`). 367 llvm::Optional<std::string> ResolvedScope; 368 369 // Unresolved part of the scope. When the unresolved name is a specifier, we 370 // use the name that comes after it as the alternative name to resolve and use 371 // the specifier as the extra scope in the accessible scopes. 372 llvm::Optional<std::string> UnresolvedScope; 373 }; 374 375 // Extracts unresolved name and scope information around \p Unresolved. 376 // FIXME: try to merge this with the scope-wrangling code in CodeComplete. 377 llvm::Optional<CheapUnresolvedName> extractUnresolvedNameCheaply( 378 const SourceManager &SM, const DeclarationNameInfo &Unresolved, 379 CXXScopeSpec *SS, const LangOptions &LangOpts, bool UnresolvedIsSpecifier) { 380 bool Invalid = false; 381 llvm::StringRef Code = SM.getBufferData( 382 SM.getDecomposedLoc(Unresolved.getBeginLoc()).first, &Invalid); 383 if (Invalid) 384 return llvm::None; 385 CheapUnresolvedName Result; 386 Result.Name = Unresolved.getAsString(); 387 if (SS && SS->isNotEmpty()) { // "::" or "ns::" 388 if (auto *Nested = SS->getScopeRep()) { 389 if (Nested->getKind() == NestedNameSpecifier::Global) 390 Result.ResolvedScope = ""; 391 else if (const auto *NS = Nested->getAsNamespace()) { 392 auto SpecifiedNS = printNamespaceScope(*NS); 393 394 // Check the specifier spelled in the source. 395 // If the resolved scope doesn't end with the spelled scope. The 396 // resolved scope can come from a sema typo correction. For example, 397 // sema assumes that "clangd::" is a typo of "clang::" and uses 398 // "clang::" as the specified scope in: 399 // namespace clang { clangd::X; } 400 // In this case, we use the "typo" specifier as extra scope instead 401 // of using the scope assumed by sema. 402 auto B = SM.getFileOffset(SS->getBeginLoc()); 403 auto E = SM.getFileOffset(SS->getEndLoc()); 404 std::string Spelling = (Code.substr(B, E - B) + "::").str(); 405 if (llvm::StringRef(SpecifiedNS).endswith(Spelling)) 406 Result.ResolvedScope = SpecifiedNS; 407 else 408 Result.UnresolvedScope = Spelling; 409 } else if (const auto *ANS = Nested->getAsNamespaceAlias()) { 410 Result.ResolvedScope = printNamespaceScope(*ANS->getNamespace()); 411 } else { 412 // We don't fix symbols in scopes that are not top-level e.g. class 413 // members, as we don't collect includes for them. 414 return llvm::None; 415 } 416 } 417 } 418 419 if (UnresolvedIsSpecifier) { 420 // If the unresolved name is a specifier e.g. 421 // clang::clangd::X 422 // ~~~~~~ 423 // We try to resolve clang::clangd::X instead of clang::clangd. 424 // FIXME: We won't be able to fix include if the specifier is what we 425 // should resolve (e.g. it's a class scope specifier). Collecting include 426 // headers for nested types could make this work. 427 428 // Not using the end location as it doesn't always point to the end of 429 // identifier. 430 if (auto QualifiedByUnresolved = 431 qualifiedByUnresolved(SM, Unresolved.getBeginLoc(), LangOpts)) { 432 auto Split = splitQualifiedName(*QualifiedByUnresolved); 433 if (!Result.UnresolvedScope) 434 Result.UnresolvedScope.emplace(); 435 // If UnresolvedSpecifiedScope is already set, we simply append the 436 // extra scope. Suppose the unresolved name is "index" in the following 437 // example: 438 // namespace clang { clangd::index::X; } 439 // ~~~~~~ ~~~~~ 440 // "clangd::" is assumed to be clang:: by Sema, and we would have used 441 // it as extra scope. With "index" being a specifier, we append "index::" 442 // to the extra scope. 443 Result.UnresolvedScope->append((Result.Name + Split.first).str()); 444 Result.Name = std::string(Split.second); 445 } 446 } 447 return Result; 448 } 449 450 /// Returns all namespace scopes that the unqualified lookup would visit. 451 std::vector<std::string> 452 collectAccessibleScopes(Sema &Sem, const DeclarationNameInfo &Typo, Scope *S, 453 Sema::LookupNameKind LookupKind) { 454 // Collects contexts visited during a Sema name lookup. 455 struct VisitedContextCollector : public VisibleDeclConsumer { 456 VisitedContextCollector(std::vector<std::string> &Out) : Out(Out) {} 457 void EnteredContext(DeclContext *Ctx) override { 458 if (llvm::isa<NamespaceDecl>(Ctx)) 459 Out.push_back(printNamespaceScope(*Ctx)); 460 } 461 void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx, 462 bool InBaseClass) override {} 463 std::vector<std::string> &Out; 464 }; 465 466 std::vector<std::string> Scopes; 467 Scopes.push_back(""); 468 VisitedContextCollector Collector(Scopes); 469 Sem.LookupVisibleDecls(S, LookupKind, Collector, 470 /*IncludeGlobalScope=*/false, 471 /*LoadExternal=*/false); 472 std::sort(Scopes.begin(), Scopes.end()); 473 Scopes.erase(std::unique(Scopes.begin(), Scopes.end()), Scopes.end()); 474 return Scopes; 475 } 476 477 class IncludeFixer::UnresolvedNameRecorder : public ExternalSemaSource { 478 public: 479 UnresolvedNameRecorder(llvm::Optional<UnresolvedName> &LastUnresolvedName) 480 : LastUnresolvedName(LastUnresolvedName) {} 481 482 void InitializeSema(Sema &S) override { this->SemaPtr = &S; } 483 484 // Captures the latest typo and treat it as an unresolved name that can 485 // potentially be fixed by adding #includes. 486 TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind, 487 Scope *S, CXXScopeSpec *SS, 488 CorrectionCandidateCallback &CCC, 489 DeclContext *MemberContext, bool EnteringContext, 490 const ObjCObjectPointerType *OPT) override { 491 dlog("CorrectTypo: {0}", Typo.getAsString()); 492 assert(SemaPtr && "Sema must have been set."); 493 if (SemaPtr->isSFINAEContext()) 494 return TypoCorrection(); 495 if (!isInsideMainFile(Typo.getLoc(), SemaPtr->SourceMgr)) 496 return clang::TypoCorrection(); 497 498 auto Extracted = extractUnresolvedNameCheaply( 499 SemaPtr->SourceMgr, Typo, SS, SemaPtr->LangOpts, 500 static_cast<Sema::LookupNameKind>(LookupKind) == 501 Sema::LookupNameKind::LookupNestedNameSpecifierName); 502 if (!Extracted) 503 return TypoCorrection(); 504 505 UnresolvedName Unresolved; 506 Unresolved.Name = Extracted->Name; 507 Unresolved.Loc = Typo.getBeginLoc(); 508 if (!Extracted->ResolvedScope && !S) // Give up if no scope available. 509 return TypoCorrection(); 510 511 if (Extracted->ResolvedScope) 512 Unresolved.Scopes.push_back(*Extracted->ResolvedScope); 513 else // no qualifier or qualifier is unresolved. 514 Unresolved.Scopes = collectAccessibleScopes( 515 *SemaPtr, Typo, S, static_cast<Sema::LookupNameKind>(LookupKind)); 516 517 if (Extracted->UnresolvedScope) { 518 for (std::string &Scope : Unresolved.Scopes) 519 Scope += *Extracted->UnresolvedScope; 520 } 521 522 LastUnresolvedName = std::move(Unresolved); 523 524 // Never return a valid correction to try to recover. Our suggested fixes 525 // always require a rebuild. 526 return TypoCorrection(); 527 } 528 529 private: 530 Sema *SemaPtr = nullptr; 531 532 llvm::Optional<UnresolvedName> &LastUnresolvedName; 533 }; 534 535 llvm::IntrusiveRefCntPtr<ExternalSemaSource> 536 IncludeFixer::unresolvedNameRecorder() { 537 return new UnresolvedNameRecorder(LastUnresolvedName); 538 } 539 540 std::vector<Fix> IncludeFixer::fixUnresolvedName() const { 541 assert(LastUnresolvedName.hasValue()); 542 auto &Unresolved = *LastUnresolvedName; 543 vlog("Trying to fix unresolved name \"{0}\" in scopes: [{1}]", 544 Unresolved.Name, llvm::join(Unresolved.Scopes, ", ")); 545 546 FuzzyFindRequest Req; 547 Req.AnyScope = false; 548 Req.Query = Unresolved.Name; 549 Req.Scopes = Unresolved.Scopes; 550 Req.RestrictForCodeCompletion = true; 551 Req.Limit = 100; 552 553 if (llvm::Optional<const SymbolSlab *> Syms = fuzzyFindCached(Req)) 554 return fixesForSymbols(**Syms); 555 556 return {}; 557 } 558 559 llvm::Optional<const SymbolSlab *> 560 IncludeFixer::fuzzyFindCached(const FuzzyFindRequest &Req) const { 561 auto ReqStr = llvm::formatv("{0}", toJSON(Req)).str(); 562 auto I = FuzzyFindCache.find(ReqStr); 563 if (I != FuzzyFindCache.end()) 564 return &I->second; 565 566 if (IndexRequestCount >= IndexRequestLimit) 567 return llvm::None; 568 IndexRequestCount++; 569 570 SymbolSlab::Builder Matches; 571 Index.fuzzyFind(Req, [&](const Symbol &Sym) { 572 if (Sym.Name != Req.Query) 573 return; 574 if (!Sym.IncludeHeaders.empty()) 575 Matches.insert(Sym); 576 }); 577 auto Syms = std::move(Matches).build(); 578 auto E = FuzzyFindCache.try_emplace(ReqStr, std::move(Syms)); 579 return &E.first->second; 580 } 581 582 llvm::Optional<const SymbolSlab *> 583 IncludeFixer::lookupCached(const SymbolID &ID) const { 584 LookupRequest Req; 585 Req.IDs.insert(ID); 586 587 auto I = LookupCache.find(ID); 588 if (I != LookupCache.end()) 589 return &I->second; 590 591 if (IndexRequestCount >= IndexRequestLimit) 592 return llvm::None; 593 IndexRequestCount++; 594 595 // FIXME: consider batching the requests for all diagnostics. 596 SymbolSlab::Builder Matches; 597 Index.lookup(Req, [&](const Symbol &Sym) { Matches.insert(Sym); }); 598 auto Syms = std::move(Matches).build(); 599 600 std::vector<Fix> Fixes; 601 if (!Syms.empty()) { 602 auto &Matched = *Syms.begin(); 603 if (!Matched.IncludeHeaders.empty() && Matched.Definition && 604 Matched.CanonicalDeclaration.FileURI == Matched.Definition.FileURI) 605 Fixes = fixesForSymbols(Syms); 606 } 607 auto E = LookupCache.try_emplace(ID, std::move(Syms)); 608 return &E.first->second; 609 } 610 611 } // namespace clangd 612 } // namespace clang 613