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 | |