xref: /llvm-project/clang-tools-extra/clangd/IncludeFixer.h (revision 042dd99484d6f393cc8a365def250e9d74c24d37)
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 LLVM_CLANG_TOOLS_EXTRA_CLANGD_INCLUDEFIXER_H
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:
IncludeFixer(llvm::StringRef File,std::shared_ptr<IncludeInserter> Inserter,const SymbolIndex & Index,unsigned IndexRequestLimit,Symbol::IncludeDirective Directive)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> insertHeader(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