[clang-tools-extra] r287228 - [include-fixer] Refactor include fixer to be usable as a plugin
Benjamin Kramer via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 17 07:16:06 PST 2016
Author: d0k
Date: Thu Nov 17 09:16:05 2016
New Revision: 287228
URL: http://llvm.org/viewvc/llvm-project?rev=287228&view=rev
Log:
[include-fixer] Refactor include fixer to be usable as a plugin
- Refactor the external sema source into a visible class
- Add support for emitting FixIts
- Wrap up include fixer as a plugin as I did with clang-tidy
Test case will follow as soon as I wire this up in libclang.
Differential Revision: https://reviews.llvm.org/D26752
Added:
clang-tools-extra/trunk/include-fixer/plugin/
clang-tools-extra/trunk/include-fixer/plugin/CMakeLists.txt
clang-tools-extra/trunk/include-fixer/plugin/IncludeFixerPlugin.cpp
Modified:
clang-tools-extra/trunk/include-fixer/CMakeLists.txt
clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
clang-tools-extra/trunk/include-fixer/IncludeFixer.h
Modified: clang-tools-extra/trunk/include-fixer/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/CMakeLists.txt?rev=287228&r1=287227&r2=287228&view=diff
==============================================================================
--- clang-tools-extra/trunk/include-fixer/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/include-fixer/CMakeLists.txt Thu Nov 17 09:16:05 2016
@@ -22,5 +22,6 @@ add_clang_library(clangIncludeFixer
findAllSymbols
)
+add_subdirectory(plugin)
add_subdirectory(tool)
add_subdirectory(find-all-symbols)
Modified: clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp?rev=287228&r1=287227&r2=287228&view=diff
==============================================================================
--- clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp (original)
+++ clang-tools-extra/trunk/include-fixer/IncludeFixer.cpp Thu Nov 17 09:16:05 2016
@@ -13,7 +13,6 @@
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Parse/ParseAST.h"
-#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Sema/Sema.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
@@ -25,19 +24,17 @@ using namespace clang;
namespace clang {
namespace include_fixer {
namespace {
-
/// Manages the parse, gathers include suggestions.
-class Action : public clang::ASTFrontendAction,
- public clang::ExternalSemaSource {
+class Action : public clang::ASTFrontendAction {
public:
explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths)
- : SymbolIndexMgr(SymbolIndexMgr),
- MinimizeIncludePaths(MinimizeIncludePaths) {}
+ : SemaSource(SymbolIndexMgr, MinimizeIncludePaths,
+ /*GenerateDiagnostics=*/false) {}
std::unique_ptr<clang::ASTConsumer>
CreateASTConsumer(clang::CompilerInstance &Compiler,
StringRef InFile) override {
- FilePath = InFile;
+ SemaSource.setFilePath(InFile);
return llvm::make_unique<clang::ASTConsumer>();
}
@@ -55,254 +52,21 @@ public:
CompletionConsumer = &Compiler->getCodeCompletionConsumer();
Compiler->createSema(getTranslationUnitKind(), CompletionConsumer);
- Compiler->getSema().addExternalSource(this);
+ SemaSource.setCompilerInstance(Compiler);
+ Compiler->getSema().addExternalSource(&SemaSource);
clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats,
Compiler->getFrontendOpts().SkipFunctionBodies);
}
- /// Callback for incomplete types. If we encounter a forward declaration we
- /// have the fully qualified name ready. Just query that.
- bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
- clang::QualType T) override {
- // Ignore spurious callbacks from SFINAE contexts.
- if (getCompilerInstance().getSema().isSFINAEContext())
- return false;
-
- clang::ASTContext &context = getCompilerInstance().getASTContext();
- std::string QueryString =
- T.getUnqualifiedType().getAsString(context.getPrintingPolicy());
- DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString
- << "'");
- // Pass an empty range here since we don't add qualifier in this case.
- query(QueryString, "", tooling::Range());
- return false;
- }
-
- /// Callback for unknown identifiers. Try to piece together as much
- /// qualification as we can get and do a query.
- clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
- int LookupKind, Scope *S, CXXScopeSpec *SS,
- CorrectionCandidateCallback &CCC,
- DeclContext *MemberContext,
- bool EnteringContext,
- const ObjCObjectPointerType *OPT) override {
- // Ignore spurious callbacks from SFINAE contexts.
- if (getCompilerInstance().getSema().isSFINAEContext())
- return clang::TypoCorrection();
-
- // We currently ignore the unidentified symbol which is not from the
- // main file.
- //
- // However, this is not always true due to templates in a non-self contained
- // header, consider the case:
- //
- // // header.h
- // template <typename T>
- // class Foo {
- // T t;
- // };
- //
- // // test.cc
- // // We need to add <bar.h> in test.cc instead of header.h.
- // class Bar;
- // Foo<Bar> foo;
- //
- // FIXME: Add the missing header to the header file where the symbol comes
- // from.
- if (!getCompilerInstance().getSourceManager().isWrittenInMainFile(
- Typo.getLoc()))
- return clang::TypoCorrection();
-
- std::string TypoScopeString;
- if (S) {
- // FIXME: Currently we only use namespace contexts. Use other context
- // types for query.
- for (const auto *Context = S->getEntity(); Context;
- Context = Context->getParent()) {
- if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) {
- if (!ND->getName().empty())
- TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString;
- }
- }
- }
-
- auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) {
- StringRef Source =
- Lexer::getSourceText(Range, getCompilerInstance().getSourceManager(),
- getCompilerInstance().getLangOpts());
-
- // Skip forward until we find a character that's neither identifier nor
- // colon. This is a bit of a hack around the fact that we will only get a
- // single callback for a long nested name if a part of the beginning is
- // unknown. For example:
- //
- // llvm::sys::path::parent_path(...)
- // ^~~~ ^~~
- // known
- // ^~~~
- // unknown, last callback
- // ^~~~~~~~~~~
- // no callback
- //
- // With the extension we get the full nested name specifier including
- // parent_path.
- // FIXME: Don't rely on source text.
- const char *End = Source.end();
- while (isIdentifierBody(*End) || *End == ':')
- ++End;
-
- return std::string(Source.begin(), End);
- };
-
- /// If we have a scope specification, use that to get more precise results.
- std::string QueryString;
- tooling::Range SymbolRange;
- const auto &SM = getCompilerInstance().getSourceManager();
- auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) {
- return tooling::Range(SM.getDecomposedLoc(BeginLoc).second,
- QueryString.size());
- };
- if (SS && SS->getRange().isValid()) {
- auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
- Typo.getLoc());
-
- QueryString = ExtendNestedNameSpecifier(Range);
- SymbolRange = CreateToolingRange(Range.getBegin());
- } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) {
- auto Range =
- CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc());
-
- QueryString = ExtendNestedNameSpecifier(Range);
- SymbolRange = CreateToolingRange(Range.getBegin());
- } else {
- QueryString = Typo.getAsString();
- SymbolRange = CreateToolingRange(Typo.getLoc());
- }
-
- DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString << "\n");
- query(QueryString, TypoScopeString, SymbolRange);
-
- // FIXME: We should just return the name we got as input here and prevent
- // clang from trying to correct the typo by itself. That may change the
- // identifier to something that's not wanted by the user.
- return clang::TypoCorrection();
- }
-
- /// Get the minimal include for a given path.
- std::string minimizeInclude(StringRef Include,
- const clang::SourceManager &SourceManager,
- clang::HeaderSearch &HeaderSearch) {
- if (!MinimizeIncludePaths)
- return Include;
-
- // Get the FileEntry for the include.
- StringRef StrippedInclude = Include.trim("\"<>");
- const FileEntry *Entry =
- SourceManager.getFileManager().getFile(StrippedInclude);
-
- // If the file doesn't exist return the path from the database.
- // FIXME: This should never happen.
- if (!Entry)
- return Include;
-
- bool IsSystem;
- std::string Suggestion =
- HeaderSearch.suggestPathToFileForDiagnostics(Entry, &IsSystem);
-
- return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
- }
-
- /// Get the include fixer context for the queried symbol.
IncludeFixerContext
getIncludeFixerContext(const clang::SourceManager &SourceManager,
- clang::HeaderSearch &HeaderSearch) {
- std::vector<find_all_symbols::SymbolInfo> SymbolCandidates;
- for (const auto &Symbol : MatchedSymbols) {
- std::string FilePath = Symbol.getFilePath().str();
- std::string MinimizedFilePath = minimizeInclude(
- ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath
- : "\"" + FilePath + "\""),
- SourceManager, HeaderSearch);
- SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
- MinimizedFilePath, Symbol.getLineNumber(),
- Symbol.getContexts(),
- Symbol.getNumOccurrences());
- }
- return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
+ clang::HeaderSearch &HeaderSearch) const {
+ return SemaSource.getIncludeFixerContext(SourceManager, HeaderSearch);
}
private:
- /// Query the database for a given identifier.
- bool query(StringRef Query, StringRef ScopedQualifiers,
- tooling::Range Range) {
- assert(!Query.empty() && "Empty query!");
-
- // Save all instances of an unidentified symbol.
- //
- // We use conservative behavior for detecting the same unidentified symbol
- // here. The symbols which have the same ScopedQualifier and RawIdentifier
- // are considered equal. So that include-fixer avoids false positives, and
- // always adds missing qualifiers to correct symbols.
- if (!QuerySymbolInfos.empty()) {
- if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers &&
- Query == QuerySymbolInfos.front().RawIdentifier) {
- QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
- }
- return false;
- }
-
- DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
- DEBUG(getCompilerInstance()
- .getSourceManager()
- .getLocForStartOfFile(
- getCompilerInstance().getSourceManager().getMainFileID())
- .getLocWithOffset(Range.getOffset())
- .print(llvm::dbgs(), getCompilerInstance().getSourceManager()));
- DEBUG(llvm::dbgs() << " ...");
-
- QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
-
- // Query the symbol based on C++ name Lookup rules.
- // Firstly, lookup the identifier with scoped namespace contexts;
- // If that fails, falls back to look up the identifier directly.
- //
- // For example:
- //
- // namespace a {
- // b::foo f;
- // }
- //
- // 1. lookup a::b::foo.
- // 2. lookup b::foo.
- std::string QueryString = ScopedQualifiers.str() + Query.str();
- // It's unsafe to do nested search for the identifier with scoped namespace
- // context, it might treat the identifier as a nested class of the scoped
- // namespace.
- MatchedSymbols = SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false);
- if (MatchedSymbols.empty())
- MatchedSymbols = SymbolIndexMgr.search(Query);
- DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
- << " symbols\n");
- return !MatchedSymbols.empty();
- }
-
- /// The client to use to find cross-references.
- SymbolIndexManager &SymbolIndexMgr;
-
- /// The information of the symbols being queried.
- std::vector<IncludeFixerContext::QuerySymbolInfo> QuerySymbolInfos;
-
- /// All symbol candidates which match QuerySymbol. We only include the first
- /// discovered identifier to avoid getting caught in results from error
- /// recovery.
- std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
-
- /// The file path to the file being processed.
- std::string FilePath;
-
- /// Whether we should use the smallest possible include path.
- bool MinimizeIncludePaths = true;
+ IncludeFixerSemaSource SemaSource;
};
} // namespace
@@ -352,6 +116,273 @@ bool IncludeFixerActionFactory::runInvoc
return !Compiler.getDiagnostics().hasFatalErrorOccurred();
}
+static void addDiagnosticsForContext(TypoCorrection &Correction,
+ const IncludeFixerContext &Context,
+ StringRef Code, SourceLocation StartOfFile,
+ ASTContext &Ctx) {
+ auto Reps = createIncludeFixerReplacements(
+ Code, Context, format::getLLVMStyle(), /*AddQualifiers=*/false);
+ if (!Reps)
+ return;
+
+ unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
+ DiagnosticsEngine::Note, "Add '#include %0' to provide the missing "
+ "declaration [clang-include-fixer]");
+
+ // FIXME: Currently we only generate a diagnostic for the first header. Give
+ // the user choices.
+ assert(Reps->size() == 1 && "Expected exactly one replacement");
+ const tooling::Replacement &Placed = *Reps->begin();
+
+ auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset());
+ auto End = Begin.getLocWithOffset(Placed.getLength());
+ PartialDiagnostic PD(DiagID, Ctx.getDiagAllocator());
+ PD << Context.getHeaderInfos().front().Header
+ << FixItHint::CreateReplacement(SourceRange(Begin, End),
+ Placed.getReplacementText());
+ Correction.addExtraDiagnostic(std::move(PD));
+}
+
+/// Callback for incomplete types. If we encounter a forward declaration we
+/// have the fully qualified name ready. Just query that.
+bool IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType(
+ clang::SourceLocation Loc, clang::QualType T) {
+ // Ignore spurious callbacks from SFINAE contexts.
+ if (CI->getSema().isSFINAEContext())
+ return false;
+
+ clang::ASTContext &context = CI->getASTContext();
+ std::string QueryString =
+ T.getUnqualifiedType().getAsString(context.getPrintingPolicy());
+ DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString << "'");
+ // Pass an empty range here since we don't add qualifier in this case.
+ query(QueryString, "", tooling::Range());
+
+ if (GenerateDiagnostics) {
+ TypoCorrection Correction;
+ FileID FID = CI->getSourceManager().getFileID(Loc);
+ StringRef Code = CI->getSourceManager().getBufferData(FID);
+ SourceLocation StartOfFile =
+ CI->getSourceManager().getLocForStartOfFile(FID);
+ addDiagnosticsForContext(
+ Correction,
+ getIncludeFixerContext(CI->getSourceManager(),
+ CI->getPreprocessor().getHeaderSearchInfo()),
+ Code, StartOfFile, CI->getASTContext());
+ for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics())
+ CI->getSema().Diag(Loc, PD);
+ }
+ return true;
+}
+
+/// Callback for unknown identifiers. Try to piece together as much
+/// qualification as we can get and do a query.
+clang::TypoCorrection IncludeFixerSemaSource::CorrectTypo(
+ const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC, DeclContext *MemberContext,
+ bool EnteringContext, const ObjCObjectPointerType *OPT) {
+ // Ignore spurious callbacks from SFINAE contexts.
+ if (CI->getSema().isSFINAEContext())
+ return clang::TypoCorrection();
+
+ // We currently ignore the unidentified symbol which is not from the
+ // main file.
+ //
+ // However, this is not always true due to templates in a non-self contained
+ // header, consider the case:
+ //
+ // // header.h
+ // template <typename T>
+ // class Foo {
+ // T t;
+ // };
+ //
+ // // test.cc
+ // // We need to add <bar.h> in test.cc instead of header.h.
+ // class Bar;
+ // Foo<Bar> foo;
+ //
+ // FIXME: Add the missing header to the header file where the symbol comes
+ // from.
+ if (!CI->getSourceManager().isWrittenInMainFile(Typo.getLoc()))
+ return clang::TypoCorrection();
+
+ std::string TypoScopeString;
+ if (S) {
+ // FIXME: Currently we only use namespace contexts. Use other context
+ // types for query.
+ for (const auto *Context = S->getEntity(); Context;
+ Context = Context->getParent()) {
+ if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) {
+ if (!ND->getName().empty())
+ TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString;
+ }
+ }
+ }
+
+ auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) {
+ StringRef Source =
+ Lexer::getSourceText(Range, CI->getSourceManager(), CI->getLangOpts());
+
+ // Skip forward until we find a character that's neither identifier nor
+ // colon. This is a bit of a hack around the fact that we will only get a
+ // single callback for a long nested name if a part of the beginning is
+ // unknown. For example:
+ //
+ // llvm::sys::path::parent_path(...)
+ // ^~~~ ^~~
+ // known
+ // ^~~~
+ // unknown, last callback
+ // ^~~~~~~~~~~
+ // no callback
+ //
+ // With the extension we get the full nested name specifier including
+ // parent_path.
+ // FIXME: Don't rely on source text.
+ const char *End = Source.end();
+ while (isIdentifierBody(*End) || *End == ':')
+ ++End;
+
+ return std::string(Source.begin(), End);
+ };
+
+ /// If we have a scope specification, use that to get more precise results.
+ std::string QueryString;
+ tooling::Range SymbolRange;
+ const auto &SM = CI->getSourceManager();
+ auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) {
+ return tooling::Range(SM.getDecomposedLoc(BeginLoc).second,
+ QueryString.size());
+ };
+ if (SS && SS->getRange().isValid()) {
+ auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(),
+ Typo.getLoc());
+
+ QueryString = ExtendNestedNameSpecifier(Range);
+ SymbolRange = CreateToolingRange(Range.getBegin());
+ } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) {
+ auto Range =
+ CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc());
+
+ QueryString = ExtendNestedNameSpecifier(Range);
+ SymbolRange = CreateToolingRange(Range.getBegin());
+ } else {
+ QueryString = Typo.getAsString();
+ SymbolRange = CreateToolingRange(Typo.getLoc());
+ }
+
+ DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString << "\n");
+ query(QueryString, TypoScopeString, SymbolRange);
+
+ clang::TypoCorrection Correction(Typo.getName());
+ Correction.setCorrectionRange(SS, Typo);
+ if (GenerateDiagnostics) {
+ FileID FID = SM.getFileID(Typo.getLoc());
+ StringRef Code = SM.getBufferData(FID);
+ SourceLocation StartOfFile = SM.getLocForStartOfFile(FID);
+ addDiagnosticsForContext(
+ Correction,
+ getIncludeFixerContext(SM, CI->getPreprocessor().getHeaderSearchInfo()),
+ Code, StartOfFile, CI->getASTContext());
+ }
+ return Correction;
+}
+
+/// Get the minimal include for a given path.
+std::string IncludeFixerSemaSource::minimizeInclude(
+ StringRef Include, const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const {
+ if (!MinimizeIncludePaths)
+ return Include;
+
+ // Get the FileEntry for the include.
+ StringRef StrippedInclude = Include.trim("\"<>");
+ const FileEntry *Entry =
+ SourceManager.getFileManager().getFile(StrippedInclude);
+
+ // If the file doesn't exist return the path from the database.
+ // FIXME: This should never happen.
+ if (!Entry)
+ return Include;
+
+ bool IsSystem;
+ std::string Suggestion =
+ HeaderSearch.suggestPathToFileForDiagnostics(Entry, &IsSystem);
+
+ return IsSystem ? '<' + Suggestion + '>' : '"' + Suggestion + '"';
+}
+
+/// Get the include fixer context for the queried symbol.
+IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext(
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const {
+ std::vector<find_all_symbols::SymbolInfo> SymbolCandidates;
+ for (const auto &Symbol : MatchedSymbols) {
+ std::string FilePath = Symbol.getFilePath().str();
+ std::string MinimizedFilePath = minimizeInclude(
+ ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath
+ : "\"" + FilePath + "\""),
+ SourceManager, HeaderSearch);
+ SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(),
+ MinimizedFilePath, Symbol.getLineNumber(),
+ Symbol.getContexts(),
+ Symbol.getNumOccurrences());
+ }
+ return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates);
+}
+
+bool IncludeFixerSemaSource::query(StringRef Query, StringRef ScopedQualifiers,
+ tooling::Range Range) {
+ assert(!Query.empty() && "Empty query!");
+
+ // Save all instances of an unidentified symbol.
+ //
+ // We use conservative behavior for detecting the same unidentified symbol
+ // here. The symbols which have the same ScopedQualifier and RawIdentifier
+ // are considered equal. So that include-fixer avoids false positives, and
+ // always adds missing qualifiers to correct symbols.
+ if (!QuerySymbolInfos.empty()) {
+ if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers &&
+ Query == QuerySymbolInfos.front().RawIdentifier) {
+ QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+ }
+ return false;
+ }
+
+ DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at ");
+ DEBUG(CI->getSourceManager()
+ .getLocForStartOfFile(CI->getSourceManager().getMainFileID())
+ .getLocWithOffset(Range.getOffset())
+ .print(llvm::dbgs(), CI->getSourceManager()));
+ DEBUG(llvm::dbgs() << " ...");
+
+ QuerySymbolInfos.push_back({Query.str(), ScopedQualifiers, Range});
+
+ // Query the symbol based on C++ name Lookup rules.
+ // Firstly, lookup the identifier with scoped namespace contexts;
+ // If that fails, falls back to look up the identifier directly.
+ //
+ // For example:
+ //
+ // namespace a {
+ // b::foo f;
+ // }
+ //
+ // 1. lookup a::b::foo.
+ // 2. lookup b::foo.
+ std::string QueryString = ScopedQualifiers.str() + Query.str();
+ // It's unsafe to do nested search for the identifier with scoped namespace
+ // context, it might treat the identifier as a nested class of the scoped
+ // namespace.
+ MatchedSymbols = SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false);
+ if (MatchedSymbols.empty())
+ MatchedSymbols = SymbolIndexMgr.search(Query);
+ DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size()
+ << " symbols\n");
+ return !MatchedSymbols.empty();
+}
+
llvm::Expected<tooling::Replacements> createIncludeFixerReplacements(
StringRef Code, const IncludeFixerContext &Context,
const clang::format::FormatStyle &Style, bool AddQualifiers) {
Modified: clang-tools-extra/trunk/include-fixer/IncludeFixer.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/IncludeFixer.h?rev=287228&r1=287227&r2=287228&view=diff
==============================================================================
--- clang-tools-extra/trunk/include-fixer/IncludeFixer.h (original)
+++ clang-tools-extra/trunk/include-fixer/IncludeFixer.h Thu Nov 17 09:16:05 2016
@@ -13,6 +13,7 @@
#include "IncludeFixerContext.h"
#include "SymbolIndexManager.h"
#include "clang/Format/Format.h"
+#include "clang/Sema/ExternalSemaSource.h"
#include "clang/Tooling/Core/Replacement.h"
#include "clang/Tooling/Tooling.h"
#include <memory>
@@ -80,6 +81,70 @@ llvm::Expected<tooling::Replacements> cr
const format::FormatStyle &Style = format::getLLVMStyle(),
bool AddQualifiers = true);
+/// Handles callbacks from sema, does the include lookup and turns it into an
+/// IncludeFixerContext.
+class IncludeFixerSemaSource : public clang::ExternalSemaSource {
+public:
+ explicit IncludeFixerSemaSource(SymbolIndexManager &SymbolIndexMgr,
+ bool MinimizeIncludePaths,
+ bool GenerateDiagnostics)
+ : SymbolIndexMgr(SymbolIndexMgr),
+ MinimizeIncludePaths(MinimizeIncludePaths),
+ GenerateDiagnostics(GenerateDiagnostics) {}
+
+ void setCompilerInstance(CompilerInstance *CI) { this->CI = CI; }
+ void setFilePath(StringRef FilePath) { this->FilePath = FilePath; }
+
+ /// Callback for incomplete types. If we encounter a forward declaration we
+ /// have the fully qualified name ready. Just query that.
+ bool MaybeDiagnoseMissingCompleteType(clang::SourceLocation Loc,
+ clang::QualType T) override;
+
+ /// Callback for unknown identifiers. Try to piece together as much
+ /// qualification as we can get and do a query.
+ clang::TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
+ int LookupKind, Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC,
+ DeclContext *MemberContext,
+ bool EnteringContext,
+ const ObjCObjectPointerType *OPT) override;
+
+ /// Get the minimal include for a given path.
+ std::string minimizeInclude(StringRef Include,
+ const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const;
+
+ /// Get the include fixer context for the queried symbol.
+ IncludeFixerContext
+ getIncludeFixerContext(const clang::SourceManager &SourceManager,
+ clang::HeaderSearch &HeaderSearch) const;
+
+private:
+ /// Query the database for a given identifier.
+ bool query(StringRef Query, StringRef ScopedQualifiers, tooling::Range Range);
+
+ CompilerInstance *CI;
+
+ /// The client to use to find cross-references.
+ SymbolIndexManager &SymbolIndexMgr;
+
+ /// The information of the symbols being queried.
+ std::vector<IncludeFixerContext::QuerySymbolInfo> QuerySymbolInfos;
+
+ /// All symbol candidates which match QuerySymbol. We only include the first
+ /// discovered identifier to avoid getting caught in results from error
+ /// recovery.
+ std::vector<find_all_symbols::SymbolInfo> MatchedSymbols;
+
+ /// The file path to the file being processed.
+ std::string FilePath;
+
+ /// Whether we should use the smallest possible include path.
+ bool MinimizeIncludePaths = true;
+
+ /// Whether we should generate diagnostics with fixits for missing symbols.
+ bool GenerateDiagnostics = false;
+};
} // namespace include_fixer
} // namespace clang
Added: clang-tools-extra/trunk/include-fixer/plugin/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/plugin/CMakeLists.txt?rev=287228&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/plugin/CMakeLists.txt (added)
+++ clang-tools-extra/trunk/include-fixer/plugin/CMakeLists.txt Thu Nov 17 09:16:05 2016
@@ -0,0 +1,12 @@
+add_clang_library(clangIncludeFixerPlugin
+ IncludeFixerPlugin.cpp
+
+ LINK_LIBS
+ clangAST
+ clangBasic
+ clangFrontend
+ clangIncludeFixer
+ clangParse
+ clangSema
+ clangTooling
+ )
Added: clang-tools-extra/trunk/include-fixer/plugin/IncludeFixerPlugin.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/include-fixer/plugin/IncludeFixerPlugin.cpp?rev=287228&view=auto
==============================================================================
--- clang-tools-extra/trunk/include-fixer/plugin/IncludeFixerPlugin.cpp (added)
+++ clang-tools-extra/trunk/include-fixer/plugin/IncludeFixerPlugin.cpp Thu Nov 17 09:16:05 2016
@@ -0,0 +1,97 @@
+//===- IncludeFixerPlugin.cpp - clang-include-fixer as a clang plugin -----===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "../IncludeFixer.h"
+#include "../YamlSymbolIndex.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Frontend/FrontendPluginRegistry.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/Sema.h"
+#include "llvm/Support/Path.h"
+
+namespace clang {
+namespace include_fixer {
+
+/// The core include fixer plugin action. This just provides the AST consumer
+/// and command line flag parsing for using include fixer as a clang plugin.
+class ClangIncludeFixerPluginAction : public PluginASTAction {
+ /// ASTConsumer to keep the symbol index alive. We don't really need an
+ /// ASTConsumer for this plugin (everything is funneled on the side through
+ /// Sema) but we have to keep the symbol index alive until sema is done.
+ struct ASTConsumerManagerWrapper : public ASTConsumer {
+ ASTConsumerManagerWrapper(std::shared_ptr<SymbolIndexManager> SIM)
+ : SymbolIndexMgr(std::move(SIM)) {}
+ std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+ };
+
+public:
+ explicit ClangIncludeFixerPluginAction()
+ : SymbolIndexMgr(std::make_shared<SymbolIndexManager>()),
+ SemaSource(new IncludeFixerSemaSource(*SymbolIndexMgr,
+ /*MinimizeIncludePaths=*/true,
+ /*GenerateDiagnostics=*/true)) {}
+
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &CI, StringRef InFile) override {
+ CI.setExternalSemaSource(SemaSource);
+ SemaSource->setFilePath(InFile);
+ SemaSource->setCompilerInstance(&CI);
+ return llvm::make_unique<ASTConsumerManagerWrapper>(SymbolIndexMgr);
+ }
+
+ void ExecuteAction() override {} // Do nothing.
+
+ bool ParseArgs(const CompilerInstance &CI,
+ const std::vector<std::string> &Args) override {
+ StringRef DB = "yaml";
+ StringRef Input;
+
+ // Parse the extra command line args.
+ // FIXME: This is very limited at the moment.
+ for (StringRef Arg : Args) {
+ if (Arg.startswith("-db="))
+ DB = Arg.substr(strlen("-db="));
+ else if (Arg.startswith("-input="))
+ Input = Arg.substr(strlen("-input="));
+ }
+
+ llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> SymbolIdx(
+ nullptr);
+ if (DB == "yaml") {
+ if (!Input.empty()) {
+ SymbolIdx = include_fixer::YamlSymbolIndex::createFromFile(Input);
+ } else {
+ // If we don't have any input file, look in the directory of the first
+ // file and its parents.
+ const FrontendOptions &FO = CI.getFrontendOpts();
+ SmallString<128> AbsolutePath(
+ tooling::getAbsolutePath(FO.Inputs[0].getFile()));
+ StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
+ SymbolIdx = include_fixer::YamlSymbolIndex::createFromDirectory(
+ Directory, "find_all_symbols_db.yaml");
+ }
+ }
+ SymbolIndexMgr->addSymbolIndex(std::move(*SymbolIdx));
+ return true;
+ }
+
+private:
+ std::shared_ptr<SymbolIndexManager> SymbolIndexMgr;
+ IntrusiveRefCntPtr<IncludeFixerSemaSource> SemaSource;
+};
+} // namespace include_fixer
+} // namespace clang
+
+// This anchor is used to force the linker to link in the generated object file
+// and thus register the include fixer plugin.
+volatile int ClangIncludeFixerPluginAnchorSource = 0;
+
+static clang::FrontendPluginRegistry::Add<
+ clang::include_fixer::ClangIncludeFixerPluginAction>
+ X("clang-include-fixer", "clang-include-fixer");
More information about the cfe-commits
mailing list