clang-tools 8.0.0

IncludeFixer.cpp
Go to the documentation of this file.
1 //===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "IncludeFixer.h"
11 #include "clang/Format/Format.h"
12 #include "clang/Frontend/CompilerInstance.h"
13 #include "clang/Lex/HeaderSearch.h"
14 #include "clang/Lex/Preprocessor.h"
15 #include "clang/Parse/ParseAST.h"
16 #include "clang/Sema/Sema.h"
17 #include "llvm/Support/Debug.h"
18 #include "llvm/Support/raw_ostream.h"
19
20 #define DEBUG_TYPE "include-fixer"
21
22 using namespace clang;
23
24 namespace clang {
25 namespace include_fixer {
26 namespace {
27 /// Manages the parse, gathers include suggestions.
28 class Action : public clang::ASTFrontendAction {
29 public:
30 explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths)
31 : SemaSource(SymbolIndexMgr, MinimizeIncludePaths,
32 /*GenerateDiagnostics=*/false) {}
33
34 std::unique_ptr<clang::ASTConsumer>
35 CreateASTConsumer(clang::CompilerInstance &Compiler,
36 StringRef InFile) override {
37 SemaSource.setFilePath(InFile);
38 return llvm::make_unique<clang::ASTConsumer>();
39 }
40
41 void ExecuteAction() override {
42 clang::CompilerInstance *Compiler = &getCompilerInstance();
43 assert(!Compiler->hasSema() && "CI already has Sema");
44
45 // Set up our hooks into sema and parse the AST.
46 if (hasCodeCompletionSupport() &&
47 !Compiler->getFrontendOpts().CodeCompletionAt.FileName.empty())
48 Compiler->createCodeCompletionConsumer();
49
50 clang::CodeCompleteConsumer *CompletionConsumer = nullptr;
51 if (Compiler->hasCodeCompletionConsumer())
52 CompletionConsumer = &Compiler->getCodeCompletionConsumer();
53
54 Compiler->createSema(getTranslationUnitKind(), CompletionConsumer);
55 SemaSource.setCompilerInstance(Compiler);
56 Compiler->getSema().addExternalSource(&SemaSource);
57
58 clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
59 Compiler->getFrontendOpts().SkipFunctionBodies);
60 }
61
62 IncludeFixerContext
63 getIncludeFixerContext(const clang::SourceManager &SourceManager,
64 clang::HeaderSearch &HeaderSearch) const {
65 return SemaSource.getIncludeFixerContext(SourceManager, HeaderSearch,
66 SemaSource.getMatchedSymbols());
67 }
68
69 private:
70 IncludeFixerSemaSource SemaSource;
71 };
72
73 } // namespace
74
75 IncludeFixerActionFactory::IncludeFixerActionFactory(
76 SymbolIndexManager &SymbolIndexMgr,
77 std::vector<IncludeFixerContext> &Contexts, StringRef StyleName,
78 bool MinimizeIncludePaths)
79 : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts),
80 MinimizeIncludePaths(MinimizeIncludePaths) {}
81
82 IncludeFixerActionFactory::~IncludeFixerActionFactory() = default;
83
84 bool IncludeFixerActionFactory::runInvocation(
85 std::shared_ptr<clang::CompilerInvocation> Invocation,
86 clang::FileManager *Files,
87 std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps,
88 clang::DiagnosticConsumer *Diagnostics) {
89 assert(Invocation->getFrontendOpts().Inputs.size() == 1);
90
91 // Set up Clang.
92 clang::CompilerInstance Compiler(PCHContainerOps);
93 Compiler.setInvocation(std::move(Invocation));
94 Compiler.setFileManager(Files);
95
96 // Create the compiler's actual diagnostics engine. We want to drop all
97 // diagnostics here.
98 Compiler.createDiagnostics(new clang::IgnoringDiagConsumer,
99 /*ShouldOwnClient=*/true);
100 Compiler.createSourceManager(*Files);
101
102 // We abort on fatal errors so don't let a large number of errors become
103 // fatal. A missing #include can cause thousands of errors.
104 Compiler.getDiagnostics().setErrorLimit(0);
105
106 // Run the parser, gather missing includes.
107 auto ScopedToolAction =
108 llvm::make_unique<Action>(SymbolIndexMgr, MinimizeIncludePaths);
109 Compiler.ExecuteAction(*ScopedToolAction);
110
111 Contexts.push_back(ScopedToolAction->getIncludeFixerContext(
112 Compiler.getSourceManager(),
113 Compiler.getPreprocessor().getHeaderSearchInfo()));
114
115 // Technically this should only return true if we're sure that we have a
116 // parseable file. We don't know that though. Only inform users of fatal
117 // errors.
118 return !Compiler.getDiagnostics().hasFatalErrorOccurred();
119 }
120
121 static bool addDiagnosticsForContext(TypoCorrection &Correction,
122 const IncludeFixerContext &Context,
123 StringRef Code, SourceLocation StartOfFile,
124 ASTContext &Ctx) {
125 auto Reps = createIncludeFixerReplacements(
126 Code, Context, format::getLLVMStyle(), /*AddQualifiers=*/false);
127 if (!Reps || Reps->size() != 1)
128 return false;
129
130 unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
131 DiagnosticsEngine::Note, "Add '#include %0' to provide the missing "
132 "declaration [clang-include-fixer]");
133
134 // FIXME: Currently we only generate a diagnostic for the first header. Give
135 // the user choices.
136 const tooling::Replacement &Placed = *Reps->begin();
137
138 auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset());
139 auto End = Begin.getLocWithOffset(std::max(0, (int)Placed.getLength() - 1));
140 PartialDiagnostic PD(DiagID, Ctx.getDiagAllocator());
141 PD << Context.getHeaderInfos().front().Header
142 << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Begin, End),
143 Placed.getReplacementText());
144 Correction.addExtraDiagnostic(std::move(PD));
145 return true;
146 }
147
148 /// Callback for incomplete types. If we encounter a forward declaration we
149 /// have the fully qualified name ready. Just query that.
150 bool IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType(
151 clang::SourceLocation Loc, clang::QualType T) {
152 // Ignore spurious callbacks from SFINAE contexts.
153 if (CI->getSema().isSFINAEContext())
154 return false;
155
156 clang::ASTContext &context = CI->getASTContext();
157 std::string QueryString = QualType(T->getUnqualifiedDesugaredType(), 0)
158 .getAsString(context.getPrintingPolicy());
159 LLVM_DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString
160 << "'");
161 // Pass an empty range here since we don't add qualifier in this case.
162 std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
163 query(QueryString, "", tooling::Range());
164
165 if (!MatchedSymbols.empty() && GenerateDiagnostics) {
166 TypoCorrection Correction;
167 FileID FID = CI->getSourceManager().getFileID(Loc);
168 StringRef Code = CI->getSourceManager().getBufferData(FID);
169 SourceLocation StartOfFile =
170 CI->getSourceManager().getLocForStartOfFile(FID);
171 addDiagnosticsForContext(
172 Correction,
173 getIncludeFixerContext(CI->getSourceManager(),
174 CI->getPreprocessor().getHeaderSearchInfo(),
175 MatchedSymbols),
176 Code, StartOfFile, CI->getASTContext());
177 for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics())
178 CI->getSema().Diag(Loc, PD);
179 }
180 return true;
181 }
182
183 /// Callback for unknown identifiers. Try to piece together as much
184 /// qualification as we can get and do a query.
185 clang::TypoCorrection IncludeFixerSemaSource::CorrectTypo(
186 const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS,
187 CorrectionCandidateCallback &CCC, DeclContext *MemberContext,
188 bool EnteringContext, const ObjCObjectPointerType *OPT) {
189 // Ignore spurious callbacks from SFINAE contexts.
190 if (CI->getSema().isSFINAEContext())
191 return clang::TypoCorrection();
192
193 // We currently ignore the unidentified symbol which is not from the
194 // main file.
195 //
196 // However, this is not always true due to templates in a non-self contained
197 // header, consider the case:
198 //
199 // // header.h
200 // template <typename T>
201 // class Foo {
202 // T t;
203 // };
204 //
205 // // test.cc
206 // // We need to add <bar.h> in test.cc instead of header.h.
207 // class Bar;
208 // Foo<Bar> foo;
209 //
210 // FIXME: Add the missing header to the header file where the symbol comes
211 // from.
212 if (!CI->getSourceManager().isWrittenInMainFile(Typo.getLoc()))
213 return clang::TypoCorrection();
214
215 std::string TypoScopeString;
216 if (S) {
217 // FIXME: Currently we only use namespace contexts. Use other context
218 // types for query.
219 for (const auto *Context = S->getEntity(); Context;
220 Context = Context->getParent()) {
221 if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) {
222 if (!ND->getName().empty())
223 TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString;
224 }
225 }
226 }
227
228 auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) {
229 StringRef Source =
230 Lexer::getSourceText(Range, CI->getSourceManager(), CI->getLangOpts());
231
232 // Skip forward until we find a character that's neither identifier nor
233 // colon. This is a bit of a hack around the fact that we will only get a
234 // single callback for a long nested name if a part of the beginning is
235 // unknown. For example:
236 //
237 // llvm::sys::path::parent_path(...)
238 // ^~~~ ^~~
239 // known
240 // ^~~~
241 // unknown, last callback
242 // ^~~~~~~~~~~
243 // no callback
244 //
245 // With the extension we get the full nested name specifier including
246 // parent_path.
247 // FIXME: Don't rely on source text.
248 const char *End = Source.end();
249 while (isIdentifierBody(*End) || *End == ':')
250 ++End;
251
252 return std::string(Source.begin(), End);
253 };
254
255 /// If we have a scope specification, use that to get more precise results.
256 std::string QueryString;
257 tooling::Range SymbolRange;
258 const auto &SM = CI->getSourceManager();
259 auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) {
260 return tooling::Range(SM.getDecomposedLoc(BeginLoc).second,
261 QueryString.size());
262 };
263 if (SS && SS->getRange().isValid()) {
264 auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
265 Typo.getLoc());
266
267 QueryString = ExtendNestedNameSpecifier(Range);
268 SymbolRange = CreateToolingRange(Range.getBegin());
269 } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) {
270 auto Range =
271 CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc());
272
273 QueryString = ExtendNestedNameSpecifier(Range);
274 SymbolRange = CreateToolingRange(Range.getBegin());
275 } else {
276 QueryString = Typo.getAsString();
277 SymbolRange = CreateToolingRange(Typo.getLoc());
278 }
279
280 LLVM_DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString
281 << "\n");
282 std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
283 query(QueryString, TypoScopeString, SymbolRange);
284
285 if (!MatchedSymbols.empty() && GenerateDiagnostics) {
286 TypoCorrection Correction(Typo.getName());
287 Correction.setCorrectionRange(SS, Typo);
288 FileID FID = SM.getFileID(Typo.getLoc());
289 StringRef Code = SM.getBufferData(FID);
290 SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
291 if (addDiagnosticsForContext(
292 Correction, getIncludeFixerContext(
293 SM, CI->getPreprocessor().getHeaderSearchInfo(),
294 MatchedSymbols),
295 Code, StartOfFile, CI->getASTContext()))
296 return Correction;
297 }
298 return TypoCorrection();
299 }
300
301 /// Get the minimal include for a given path.
302 std::string IncludeFixerSemaSource::minimizeInclude(
303 StringRef Include, const clang::SourceManager &SourceManager,
304 clang::HeaderSearch &HeaderSearch) const {
305 if (!MinimizeIncludePaths)
306 return Include;
307
308 // Get the FileEntry for the include.
309 StringRef StrippedInclude = Include.trim("\"<>");
310 const FileEntry *Entry =
311 SourceManager.getFileManager().getFile(StrippedInclude);
312
313 // If the file doesn't exist return the path from the database.
314 // FIXME: This should never happen.
315 if (!Entry)
316 return Include;
317
318 bool IsSystem;
319 std::string Suggestion =
320 HeaderSearch.suggestPathToFileForDiagnostics(Entry, &IsSystem);
321
322 return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
323 }
324
325 /// Get the include fixer context for the queried symbol.
326 IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext(
327 const clang::SourceManager &SourceManager,
328 clang::HeaderSearch &HeaderSearch,
329 ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const {
330 std::vector<find_all_symbols::SymbolInfo> SymbolCandidates;
331 for (const auto &Symbol : MatchedSymbols) {
332 std::string FilePath = Symbol.getFilePath().str();
333 std::string MinimizedFilePath = minimizeInclude(
334 ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath
335 : "\"" + FilePath + "\""),
336 SourceManager, HeaderSearch);
337 SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
338 MinimizedFilePath, Symbol.getContexts());
339 }
340 return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
341 }
342
343 std::vector<find_all_symbols::SymbolInfo>
344 IncludeFixerSemaSource::query(StringRef Query, StringRef ScopedQualifiers,
345 tooling::Range Range) {
346 assert(!Query.empty() && "Empty query!");
347
348 // Save all instances of an unidentified symbol.
349 //
350 // We use conservative behavior for detecting the same unidentified symbol
351 // here. The symbols which have the same ScopedQualifier and RawIdentifier
352 // are considered equal. So that include-fixer avoids false positives, and
353 // always adds missing qualifiers to correct symbols.
354 if (!GenerateDiagnostics && !QuerySymbolInfos.empty()) {
355 if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers &&
356 Query == QuerySymbolInfos.front().RawIdentifier) {
357 QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
358 }
359 return {};
360 }
361
362 LLVM_DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
363 LLVM_DEBUG(CI->getSourceManager()
364 .getLocForStartOfFile(CI->getSourceManager().getMainFileID())
365 .getLocWithOffset(Range.getOffset())
366 .print(llvm::dbgs(), CI->getSourceManager()));
367 LLVM_DEBUG(llvm::dbgs() << " ...");
368 llvm::StringRef FileName = CI->getSourceManager().getFilename(
369 CI->getSourceManager().getLocForStartOfFile(
370 CI->getSourceManager().getMainFileID()));
371
372 QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
373
374 // Query the symbol based on C++ name Lookup rules.
375 // Firstly, lookup the identifier with scoped namespace contexts;
376 // If that fails, falls back to look up the identifier directly.
377 //
378 // For example:
379 //
380 // namespace a {
381 // b::foo f;
382 // }
383 //
384 // 1. lookup a::b::foo.
385 // 2. lookup b::foo.
386 std::string QueryString = ScopedQualifiers.str() + Query.str();
387 // It's unsafe to do nested search for the identifier with scoped namespace
388 // context, it might treat the identifier as a nested class of the scoped
389 // namespace.
390 std::vector<find_all_symbols::SymbolInfo> MatchedSymbols =
391 SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false, FileName);
392 if (MatchedSymbols.empty())
393 MatchedSymbols =
394 SymbolIndexMgr.search(Query, /*IsNestedSearch=*/true, FileName);
395 LLVM_DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
396 << " symbols\n");
397 // We store a copy of MatchedSymbols in a place where it's globally reachable.
398 // This is used by the standalone version of the tool.
399 this->MatchedSymbols = MatchedSymbols;
400 return MatchedSymbols;
401 }
402
403 llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
404 StringRef Code, const IncludeFixerContext &Context,
405 const clang::format::FormatStyle &Style, bool AddQualifiers) {
406 if (Context.getHeaderInfos().empty())
407 return tooling::Replacements();
408 StringRef FilePath = Context.getFilePath();
409 std::string IncludeName =
410 "#include " + Context.getHeaderInfos().front().Header + "\n";
411 // Create replacements for the new header.
412 clang::tooling::Replacements Insertions;
413 auto Err =
414 Insertions.add(tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName));
415 if (Err)
416 return std::move(Err);
417
418 auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style);
419 if (!CleanReplaces)
420 return CleanReplaces;
421
422 auto Replaces = std::move(*CleanReplaces);
423 if (AddQualifiers) {
424 for (const auto &Info : Context.getQuerySymbolInfos()) {
425 // Ignore the empty range.
426 if (Info.Range.getLength() > 0) {
427 auto R = tooling::Replacement(
428 {FilePath, Info.Range.getOffset(), Info.Range.getLength(),
429 Context.getHeaderInfos().front().QualifiedName});
430 auto Err = Replaces.add(R);
431 if (Err) {
432 llvm::consumeError(std::move(Err));
433 R = tooling::Replacement(
434 R.getFilePath(), Replaces.getShiftedCodePosition(R.getOffset()),
435 R.getLength(), R.getReplacementText());
436 Replaces = Replaces.merge(tooling::Replacements(R));
437 }
438 }
439 }
440 }
441 return formatReplacements(Code, Replaces, Style);
442 }
443
444 } // namespace include_fixer
445 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:37
clang::include_fixer::createIncludeFixerReplacements
llvm::Expected< tooling::Replacements > createIncludeFixerReplacements(StringRef Code, const IncludeFixerContext &Context, const clang::format::FormatStyle &Style, bool AddQualifiers)
Definition: IncludeFixer.cpp:403
clang::include_fixer::IncludeFixerContext::getQuerySymbolInfos
const std::vector< QuerySymbolInfo > & getQuerySymbolInfos() const
Get information of symbols being querid.
Definition: IncludeFixerContext.h:71
clang::include_fixer::IncludeFixerContext::getFilePath
StringRef getFilePath() const
Get the file path to the file being processed.
Definition: IncludeFixerContext.h:65
clang::include_fixer::IncludeFixerContext
A context for a file being processed.
Definition: IncludeFixerContext.h:23
clang::include_fixer::IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType
bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc, clang::QualType T) override
Callback for incomplete types.
Definition: IncludeFixer.cpp:150
clang::include_fixer::IncludeFixerContext::getHeaderInfos
const std::vector< HeaderInfo > & getHeaderInfos() const
Get header information.
Definition: IncludeFixerContext.h:68
clang::include_fixer::IncludeFixerSemaSource::getIncludeFixerContext
IncludeFixerContext getIncludeFixerContext(const clang::SourceManager &SourceManager, clang::HeaderSearch &HeaderSearch, ArrayRef< find_all_symbols::SymbolInfo > MatchedSymbols) const
Get the include fixer context for the queried symbol.
Definition: IncludeFixer.cpp:326
clang::include_fixer::IncludeFixerSemaSource::CorrectTypo
clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS, CorrectionCandidateCallback &CCC, DeclContext *MemberContext, bool EnteringContext, const ObjCObjectPointerType *OPT) override
Callback for unknown identifiers.
Definition: IncludeFixer.cpp:185
clang::include_fixer::IncludeFixerActionFactory::runInvocation
bool runInvocation(std::shared_ptr< clang::CompilerInvocation > Invocation, clang::FileManager *Files, std::shared_ptr< clang::PCHContainerOperations > PCHContainerOps, clang::DiagnosticConsumer *Diagnostics) override
Definition: IncludeFixer.cpp:84
Ctx
Context Ctx
Definition: TUScheduler.cpp:224
clang::include_fixer::addDiagnosticsForContext
static bool addDiagnosticsForContext(TypoCorrection &Correction, const IncludeFixerContext &Context, StringRef Code, SourceLocation StartOfFile, ASTContext &Ctx)
Definition: IncludeFixer.cpp:121
Action
llvm::unique_function< void()> Action
Definition: TUScheduler.cpp:221
FileName
PathRef FileName
Definition: CodeComplete.cpp:1004
clang::include_fixer::SymbolIndexManager
This class provides an interface for finding the header files corresponding to an identifier in the s...
Definition: SymbolIndexManager.h:34
clang::include_fixer::IncludeFixerActionFactory::~IncludeFixerActionFactory
~IncludeFixerActionFactory() override
clang::include_fixer::IncludeFixerActionFactory::IncludeFixerActionFactory
IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr, std::vector< IncludeFixerContext > &Contexts, StringRef StyleName, bool MinimizeIncludePaths=true)
Definition: IncludeFixer.cpp:75
Info
FunctionInfo Info
Definition: FunctionSizeCheck.cpp:121
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ChangeNamespace.cpp:18
Range
CharSourceRange Range
SourceRange for the file name.
Definition: IncludeOrderCheck.cpp:38
clang::include_fixer::IncludeFixerSemaSource::minimizeInclude
std::string minimizeInclude(StringRef Include, const clang::SourceManager &SourceManager, clang::HeaderSearch &HeaderSearch) const
Get the minimal include for a given path.
Definition: IncludeFixer.cpp:302
IncludeFixer.h
clang::include_fixer::SymbolIndexManager::search
std::vector< find_all_symbols::SymbolInfo > search(llvm::StringRef Identifier, bool IsNestedSearch=true, llvm::StringRef FileName="") const
Search for header files to be included for an identifier.
Definition: SymbolIndexManager.cpp:73
Entry
Definition: Modularize.cpp:430
FormatStyle
static cl::opt< std::string > FormatStyle("format-style", cl::desc(R"( Style for formatting code around applied fixes: - 'none' (default) turns off formatting - 'file' (literally 'file', not a placeholder) uses .clang-format file in the closest parent directory - '{ <json> }' specifies options inline, e.g. -format-style='{BasedOnStyle: llvm, IndentWidth: 8}' - 'llvm', 'google', 'webkit', 'mozilla' See clang-format documentation for the up-to-date information about formatting styles and options. This option overrides the 'FormatStyle` option in .clang-tidy file, if any. )"), cl::init("none"), cl::cat(ClangTidyCategory))