| 1 | //===--- IncludeFixer.h ------------------------------------------*- 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 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDEFIXER_H |
| 10 | #define |
| 11 | |
| 12 | #include "Diagnostics.h" |
| 13 | #include "Headers.h" |
| 14 | #include "index/Index.h" |
| 15 | #include "index/Symbol.h" |
| 16 | #include "clang/AST/Type.h" |
| 17 | #include "clang/Basic/Diagnostic.h" |
| 18 | #include "clang/Basic/SourceLocation.h" |
| 19 | #include "clang/Sema/ExternalSemaSource.h" |
| 20 | #include "clang/Tooling/Inclusions/HeaderIncludes.h" |
| 21 | #include "llvm/ADT/DenseMap.h" |
| 22 | #include "llvm/ADT/IntrusiveRefCntPtr.h" |
| 23 | #include "llvm/ADT/StringMap.h" |
| 24 | #include "llvm/ADT/StringRef.h" |
| 25 | #include <memory> |
| 26 | #include <optional> |
| 27 | |
| 28 | namespace clang { |
| 29 | namespace clangd { |
| 30 | |
| 31 | /// Attempts to recover from error diagnostics by suggesting include insertion |
| 32 | /// fixes. For example, member access into incomplete type can be fixes by |
| 33 | /// include headers with the definition. |
| 34 | class IncludeFixer { |
| 35 | public: |
| 36 | IncludeFixer(llvm::StringRef File, std::shared_ptr<IncludeInserter> Inserter, |
| 37 | const SymbolIndex &Index, unsigned IndexRequestLimit, |
| 38 | Symbol::IncludeDirective Directive) |
| 39 | : File(File), Inserter(std::move(Inserter)), Index(Index), |
| 40 | IndexRequestLimit(IndexRequestLimit), Directive(Directive) {} |
| 41 | |
| 42 | /// Returns include insertions that can potentially recover the diagnostic. |
| 43 | /// If Info is a note and fixes are returned, they should *replace* the note. |
| 44 | std::vector<Fix> fix(DiagnosticsEngine::Level DiagLevel, |
| 45 | const clang::Diagnostic &Info) const; |
| 46 | |
| 47 | /// Returns an ExternalSemaSource that records failed name lookups in Sema. |
| 48 | /// This allows IncludeFixer to suggest inserting headers that define those |
| 49 | /// names. |
| 50 | llvm::IntrusiveRefCntPtr<ExternalSemaSource> unresolvedNameRecorder(); |
| 51 | |
| 52 | private: |
| 53 | /// Attempts to recover diagnostic caused by an incomplete type \p T. |
| 54 | std::vector<Fix> fixIncompleteType(const Type &T) const; |
| 55 | |
| 56 | /// Generates header insertion fixes for all symbols. Fixes are deduplicated. |
| 57 | std::vector<Fix> fixesForSymbols(const SymbolSlab &Syms) const; |
| 58 | |
| 59 | std::optional<Fix> (llvm::StringRef Name, |
| 60 | llvm::StringRef Symbol = "" , |
| 61 | tooling::IncludeDirective Directive = |
| 62 | tooling::IncludeDirective::Include) const; |
| 63 | |
| 64 | struct UnresolvedName { |
| 65 | std::string Name; // E.g. "X" in foo::X. |
| 66 | SourceLocation Loc; // Start location of the unresolved name. |
| 67 | std::vector<std::string> Scopes; // Namespace scopes we should search in. |
| 68 | }; |
| 69 | |
| 70 | /// Records the last unresolved name seen by Sema. |
| 71 | class UnresolvedNameRecorder; |
| 72 | |
| 73 | /// Attempts to fix the unresolved name associated with the current |
| 74 | /// diagnostic. We assume a diagnostic is caused by a unresolved name when |
| 75 | /// they have the same source location and the unresolved name is the last |
| 76 | /// one we've seen during the Sema run. |
| 77 | std::vector<Fix> fixUnresolvedName() const; |
| 78 | |
| 79 | std::string File; |
| 80 | std::shared_ptr<IncludeInserter> Inserter; |
| 81 | const SymbolIndex &Index; |
| 82 | const unsigned IndexRequestLimit; // Make at most 5 index requests. |
| 83 | mutable unsigned IndexRequestCount = 0; |
| 84 | const Symbol::IncludeDirective Directive; |
| 85 | |
| 86 | // These collect the last unresolved name so that we can associate it with the |
| 87 | // diagnostic. |
| 88 | std::optional<UnresolvedName> LastUnresolvedName; |
| 89 | |
| 90 | // There can be multiple diagnostics that are caused by the same unresolved |
| 91 | // name or incomplete type in one parse, especially when code is |
| 92 | // copy-and-pasted without #includes. We cache the index results based on |
| 93 | // index requests. |
| 94 | mutable llvm::StringMap<SymbolSlab> FuzzyFindCache; |
| 95 | mutable llvm::DenseMap<SymbolID, SymbolSlab> LookupCache; |
| 96 | // Returns std::nullopt if the number of index requests has reached the limit. |
| 97 | std::optional<const SymbolSlab *> |
| 98 | fuzzyFindCached(const FuzzyFindRequest &Req) const; |
| 99 | std::optional<const SymbolSlab *> lookupCached(const SymbolID &ID) const; |
| 100 | }; |
| 101 | |
| 102 | } // namespace clangd |
| 103 | } // namespace clang |
| 104 | |
| 105 | #endif // LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDEFIXER_H |
| 106 | |