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 
76  SymbolIndexManager &SymbolIndexMgr,
77  std::vector<IncludeFixerContext> &Contexts, StringRef StyleName,
78  bool MinimizeIncludePaths)
79  : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts),
80  MinimizeIncludePaths(MinimizeIncludePaths) {}
81 
83 
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.
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);
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.
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);
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.
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.
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,
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
SourceLocation Loc
&#39;#&#39; location in the include directive
llvm::Expected< tooling::Replacements > createIncludeFixerReplacements(StringRef Code, const IncludeFixerContext &Context, const clang::format::FormatStyle &Style, bool AddQualifiers)
const std::vector< QuerySymbolInfo > & getQuerySymbolInfos() const
Get information of symbols being querid.
StringRef getFilePath() const
Get the file path to the file being processed.
A context for a file being processed.
bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc, clang::QualType T) override
Callback for incomplete types.
const std::vector< HeaderInfo > & getHeaderInfos() const
Get header information.
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.
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.
bool runInvocation(std::shared_ptr< clang::CompilerInvocation > Invocation, clang::FileManager *Files, std::shared_ptr< clang::PCHContainerOperations > PCHContainerOps, clang::DiagnosticConsumer *Diagnostics) override
Context Ctx
static bool addDiagnosticsForContext(TypoCorrection &Correction, const IncludeFixerContext &Context, StringRef Code, SourceLocation StartOfFile, ASTContext &Ctx)
llvm::unique_function< void()> Action
PathRef FileName
This class provides an interface for finding the header files corresponding to an identifier in the s...
IncludeFixerActionFactory(SymbolIndexManager &SymbolIndexMgr, std::vector< IncludeFixerContext > &Contexts, StringRef StyleName, bool MinimizeIncludePaths=true)
FunctionInfo Info
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
CharSourceRange Range
SourceRange for the file name.
std::string minimizeInclude(StringRef Include, const clang::SourceManager &SourceManager, clang::HeaderSearch &HeaderSearch) const
Get the minimal include for a given path.
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.
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))