143356f56SNico Weber //===-- IncludeFixer.cpp - Include inserter based on sema callbacks -------===// 243356f56SNico Weber // 343356f56SNico Weber // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 443356f56SNico Weber // See https://llvm.org/LICENSE.txt for license information. 543356f56SNico Weber // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 643356f56SNico Weber // 743356f56SNico Weber //===----------------------------------------------------------------------===// 843356f56SNico Weber 943356f56SNico Weber #include "IncludeFixer.h" 1043356f56SNico Weber #include "clang/Format/Format.h" 1143356f56SNico Weber #include "clang/Frontend/CompilerInstance.h" 1243356f56SNico Weber #include "clang/Lex/HeaderSearch.h" 1343356f56SNico Weber #include "clang/Lex/Preprocessor.h" 1443356f56SNico Weber #include "clang/Parse/ParseAST.h" 1543356f56SNico Weber #include "clang/Sema/Sema.h" 1643356f56SNico Weber #include "llvm/Support/Debug.h" 1743356f56SNico Weber #include "llvm/Support/raw_ostream.h" 1843356f56SNico Weber 1943356f56SNico Weber #define DEBUG_TYPE "clang-include-fixer" 2043356f56SNico Weber 2143356f56SNico Weber using namespace clang; 2243356f56SNico Weber 2343356f56SNico Weber namespace clang { 2443356f56SNico Weber namespace include_fixer { 2543356f56SNico Weber namespace { 2643356f56SNico Weber /// Manages the parse, gathers include suggestions. 2743356f56SNico Weber class Action : public clang::ASTFrontendAction { 2843356f56SNico Weber public: 2943356f56SNico Weber explicit Action(SymbolIndexManager &SymbolIndexMgr, bool MinimizeIncludePaths) 305b5329bdSChris Bieneman : SemaSource(new IncludeFixerSemaSource(SymbolIndexMgr, 315b5329bdSChris Bieneman MinimizeIncludePaths, 325b5329bdSChris Bieneman /*GenerateDiagnostics=*/false)) {} 3343356f56SNico Weber 3443356f56SNico Weber std::unique_ptr<clang::ASTConsumer> 3543356f56SNico Weber CreateASTConsumer(clang::CompilerInstance &Compiler, 3643356f56SNico Weber StringRef InFile) override { 375b5329bdSChris Bieneman SemaSource->setFilePath(InFile); 381c705d9cSJonas Devlieghere return std::make_unique<clang::ASTConsumer>(); 3943356f56SNico Weber } 4043356f56SNico Weber 4143356f56SNico Weber void ExecuteAction() override { 4243356f56SNico Weber clang::CompilerInstance *Compiler = &getCompilerInstance(); 4343356f56SNico Weber assert(!Compiler->hasSema() && "CI already has Sema"); 4443356f56SNico Weber 4543356f56SNico Weber // Set up our hooks into sema and parse the AST. 4643356f56SNico Weber if (hasCodeCompletionSupport() && 4743356f56SNico Weber !Compiler->getFrontendOpts().CodeCompletionAt.FileName.empty()) 4843356f56SNico Weber Compiler->createCodeCompletionConsumer(); 4943356f56SNico Weber 5043356f56SNico Weber clang::CodeCompleteConsumer *CompletionConsumer = nullptr; 5143356f56SNico Weber if (Compiler->hasCodeCompletionConsumer()) 5243356f56SNico Weber CompletionConsumer = &Compiler->getCodeCompletionConsumer(); 5343356f56SNico Weber 5443356f56SNico Weber Compiler->createSema(getTranslationUnitKind(), CompletionConsumer); 555b5329bdSChris Bieneman SemaSource->setCompilerInstance(Compiler); 565b5329bdSChris Bieneman Compiler->getSema().addExternalSource(SemaSource.get()); 5743356f56SNico Weber 5843356f56SNico Weber clang::ParseAST(Compiler->getSema(), Compiler->getFrontendOpts().ShowStats, 5943356f56SNico Weber Compiler->getFrontendOpts().SkipFunctionBodies); 6043356f56SNico Weber } 6143356f56SNico Weber 6243356f56SNico Weber IncludeFixerContext 6343356f56SNico Weber getIncludeFixerContext(const clang::SourceManager &SourceManager, 6443356f56SNico Weber clang::HeaderSearch &HeaderSearch) const { 655b5329bdSChris Bieneman return SemaSource->getIncludeFixerContext(SourceManager, HeaderSearch, 665b5329bdSChris Bieneman SemaSource->getMatchedSymbols()); 6743356f56SNico Weber } 6843356f56SNico Weber 6943356f56SNico Weber private: 705b5329bdSChris Bieneman IntrusiveRefCntPtr<IncludeFixerSemaSource> SemaSource; 7143356f56SNico Weber }; 7243356f56SNico Weber 7343356f56SNico Weber } // namespace 7443356f56SNico Weber 7543356f56SNico Weber IncludeFixerActionFactory::IncludeFixerActionFactory( 7643356f56SNico Weber SymbolIndexManager &SymbolIndexMgr, 7743356f56SNico Weber std::vector<IncludeFixerContext> &Contexts, StringRef StyleName, 7843356f56SNico Weber bool MinimizeIncludePaths) 7943356f56SNico Weber : SymbolIndexMgr(SymbolIndexMgr), Contexts(Contexts), 8043356f56SNico Weber MinimizeIncludePaths(MinimizeIncludePaths) {} 8143356f56SNico Weber 8243356f56SNico Weber IncludeFixerActionFactory::~IncludeFixerActionFactory() = default; 8343356f56SNico Weber 8443356f56SNico Weber bool IncludeFixerActionFactory::runInvocation( 8543356f56SNico Weber std::shared_ptr<clang::CompilerInvocation> Invocation, 8643356f56SNico Weber clang::FileManager *Files, 8743356f56SNico Weber std::shared_ptr<clang::PCHContainerOperations> PCHContainerOps, 8843356f56SNico Weber clang::DiagnosticConsumer *Diagnostics) { 8943356f56SNico Weber assert(Invocation->getFrontendOpts().Inputs.size() == 1); 9043356f56SNico Weber 9143356f56SNico Weber // Set up Clang. 9243356f56SNico Weber clang::CompilerInstance Compiler(PCHContainerOps); 9343356f56SNico Weber Compiler.setInvocation(std::move(Invocation)); 9443356f56SNico Weber Compiler.setFileManager(Files); 9543356f56SNico Weber 9643356f56SNico Weber // Create the compiler's actual diagnostics engine. We want to drop all 9743356f56SNico Weber // diagnostics here. 98*df9a14d7SKadir Cetinkaya Compiler.createDiagnostics(Files->getVirtualFileSystem(), 99*df9a14d7SKadir Cetinkaya new clang::IgnoringDiagConsumer, 10043356f56SNico Weber /*ShouldOwnClient=*/true); 10143356f56SNico Weber Compiler.createSourceManager(*Files); 10243356f56SNico Weber 10343356f56SNico Weber // We abort on fatal errors so don't let a large number of errors become 10443356f56SNico Weber // fatal. A missing #include can cause thousands of errors. 10543356f56SNico Weber Compiler.getDiagnostics().setErrorLimit(0); 10643356f56SNico Weber 10743356f56SNico Weber // Run the parser, gather missing includes. 10843356f56SNico Weber auto ScopedToolAction = 1091c705d9cSJonas Devlieghere std::make_unique<Action>(SymbolIndexMgr, MinimizeIncludePaths); 11043356f56SNico Weber Compiler.ExecuteAction(*ScopedToolAction); 11143356f56SNico Weber 11243356f56SNico Weber Contexts.push_back(ScopedToolAction->getIncludeFixerContext( 11343356f56SNico Weber Compiler.getSourceManager(), 11443356f56SNico Weber Compiler.getPreprocessor().getHeaderSearchInfo())); 11543356f56SNico Weber 11643356f56SNico Weber // Technically this should only return true if we're sure that we have a 11743356f56SNico Weber // parseable file. We don't know that though. Only inform users of fatal 11843356f56SNico Weber // errors. 11943356f56SNico Weber return !Compiler.getDiagnostics().hasFatalErrorOccurred(); 12043356f56SNico Weber } 12143356f56SNico Weber 12243356f56SNico Weber static bool addDiagnosticsForContext(TypoCorrection &Correction, 12343356f56SNico Weber const IncludeFixerContext &Context, 12443356f56SNico Weber StringRef Code, SourceLocation StartOfFile, 12543356f56SNico Weber ASTContext &Ctx) { 12643356f56SNico Weber auto Reps = createIncludeFixerReplacements( 12743356f56SNico Weber Code, Context, format::getLLVMStyle(), /*AddQualifiers=*/false); 12843356f56SNico Weber if (!Reps || Reps->size() != 1) 12943356f56SNico Weber return false; 13043356f56SNico Weber 13143356f56SNico Weber unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID( 13243356f56SNico Weber DiagnosticsEngine::Note, "Add '#include %0' to provide the missing " 13343356f56SNico Weber "declaration [clang-include-fixer]"); 13443356f56SNico Weber 13543356f56SNico Weber // FIXME: Currently we only generate a diagnostic for the first header. Give 13643356f56SNico Weber // the user choices. 13743356f56SNico Weber const tooling::Replacement &Placed = *Reps->begin(); 13843356f56SNico Weber 13943356f56SNico Weber auto Begin = StartOfFile.getLocWithOffset(Placed.getOffset()); 14043356f56SNico Weber auto End = Begin.getLocWithOffset(std::max(0, (int)Placed.getLength() - 1)); 14143356f56SNico Weber PartialDiagnostic PD(DiagID, Ctx.getDiagAllocator()); 14243356f56SNico Weber PD << Context.getHeaderInfos().front().Header 14343356f56SNico Weber << FixItHint::CreateReplacement(CharSourceRange::getCharRange(Begin, End), 14443356f56SNico Weber Placed.getReplacementText()); 14543356f56SNico Weber Correction.addExtraDiagnostic(std::move(PD)); 14643356f56SNico Weber return true; 14743356f56SNico Weber } 14843356f56SNico Weber 14943356f56SNico Weber /// Callback for incomplete types. If we encounter a forward declaration we 15043356f56SNico Weber /// have the fully qualified name ready. Just query that. 15143356f56SNico Weber bool IncludeFixerSemaSource::MaybeDiagnoseMissingCompleteType( 15243356f56SNico Weber clang::SourceLocation Loc, clang::QualType T) { 15343356f56SNico Weber // Ignore spurious callbacks from SFINAE contexts. 15443356f56SNico Weber if (CI->getSema().isSFINAEContext()) 15543356f56SNico Weber return false; 15643356f56SNico Weber 15743356f56SNico Weber clang::ASTContext &context = CI->getASTContext(); 15843356f56SNico Weber std::string QueryString = QualType(T->getUnqualifiedDesugaredType(), 0) 15943356f56SNico Weber .getAsString(context.getPrintingPolicy()); 16043356f56SNico Weber LLVM_DEBUG(llvm::dbgs() << "Query missing complete type '" << QueryString 16143356f56SNico Weber << "'"); 16243356f56SNico Weber // Pass an empty range here since we don't add qualifier in this case. 16343356f56SNico Weber std::vector<find_all_symbols::SymbolInfo> MatchedSymbols = 16443356f56SNico Weber query(QueryString, "", tooling::Range()); 16543356f56SNico Weber 16643356f56SNico Weber if (!MatchedSymbols.empty() && GenerateDiagnostics) { 16743356f56SNico Weber TypoCorrection Correction; 16843356f56SNico Weber FileID FID = CI->getSourceManager().getFileID(Loc); 16943356f56SNico Weber StringRef Code = CI->getSourceManager().getBufferData(FID); 17043356f56SNico Weber SourceLocation StartOfFile = 17143356f56SNico Weber CI->getSourceManager().getLocForStartOfFile(FID); 17243356f56SNico Weber addDiagnosticsForContext( 17343356f56SNico Weber Correction, 17443356f56SNico Weber getIncludeFixerContext(CI->getSourceManager(), 17543356f56SNico Weber CI->getPreprocessor().getHeaderSearchInfo(), 17643356f56SNico Weber MatchedSymbols), 17743356f56SNico Weber Code, StartOfFile, CI->getASTContext()); 17843356f56SNico Weber for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics()) 17943356f56SNico Weber CI->getSema().Diag(Loc, PD); 18043356f56SNico Weber } 18143356f56SNico Weber return true; 18243356f56SNico Weber } 18343356f56SNico Weber 18443356f56SNico Weber /// Callback for unknown identifiers. Try to piece together as much 18543356f56SNico Weber /// qualification as we can get and do a query. 18643356f56SNico Weber clang::TypoCorrection IncludeFixerSemaSource::CorrectTypo( 18743356f56SNico Weber const DeclarationNameInfo &Typo, int LookupKind, Scope *S, CXXScopeSpec *SS, 18843356f56SNico Weber CorrectionCandidateCallback &CCC, DeclContext *MemberContext, 18943356f56SNico Weber bool EnteringContext, const ObjCObjectPointerType *OPT) { 19043356f56SNico Weber // Ignore spurious callbacks from SFINAE contexts. 19143356f56SNico Weber if (CI->getSema().isSFINAEContext()) 19243356f56SNico Weber return clang::TypoCorrection(); 19343356f56SNico Weber 19443356f56SNico Weber // We currently ignore the unidentified symbol which is not from the 19543356f56SNico Weber // main file. 19643356f56SNico Weber // 19743356f56SNico Weber // However, this is not always true due to templates in a non-self contained 19843356f56SNico Weber // header, consider the case: 19943356f56SNico Weber // 20043356f56SNico Weber // // header.h 20143356f56SNico Weber // template <typename T> 20243356f56SNico Weber // class Foo { 20343356f56SNico Weber // T t; 20443356f56SNico Weber // }; 20543356f56SNico Weber // 20643356f56SNico Weber // // test.cc 20743356f56SNico Weber // // We need to add <bar.h> in test.cc instead of header.h. 20843356f56SNico Weber // class Bar; 20943356f56SNico Weber // Foo<Bar> foo; 21043356f56SNico Weber // 21143356f56SNico Weber // FIXME: Add the missing header to the header file where the symbol comes 21243356f56SNico Weber // from. 21343356f56SNico Weber if (!CI->getSourceManager().isWrittenInMainFile(Typo.getLoc())) 21443356f56SNico Weber return clang::TypoCorrection(); 21543356f56SNico Weber 21643356f56SNico Weber std::string TypoScopeString; 21743356f56SNico Weber if (S) { 21843356f56SNico Weber // FIXME: Currently we only use namespace contexts. Use other context 21943356f56SNico Weber // types for query. 22043356f56SNico Weber for (const auto *Context = S->getEntity(); Context; 22143356f56SNico Weber Context = Context->getParent()) { 22243356f56SNico Weber if (const auto *ND = dyn_cast<NamespaceDecl>(Context)) { 22343356f56SNico Weber if (!ND->getName().empty()) 22443356f56SNico Weber TypoScopeString = ND->getNameAsString() + "::" + TypoScopeString; 22543356f56SNico Weber } 22643356f56SNico Weber } 22743356f56SNico Weber } 22843356f56SNico Weber 22943356f56SNico Weber auto ExtendNestedNameSpecifier = [this](CharSourceRange Range) { 23043356f56SNico Weber StringRef Source = 23143356f56SNico Weber Lexer::getSourceText(Range, CI->getSourceManager(), CI->getLangOpts()); 23243356f56SNico Weber 23343356f56SNico Weber // Skip forward until we find a character that's neither identifier nor 23443356f56SNico Weber // colon. This is a bit of a hack around the fact that we will only get a 23543356f56SNico Weber // single callback for a long nested name if a part of the beginning is 23643356f56SNico Weber // unknown. For example: 23743356f56SNico Weber // 23843356f56SNico Weber // llvm::sys::path::parent_path(...) 23943356f56SNico Weber // ^~~~ ^~~ 24043356f56SNico Weber // known 24143356f56SNico Weber // ^~~~ 24243356f56SNico Weber // unknown, last callback 24343356f56SNico Weber // ^~~~~~~~~~~ 24443356f56SNico Weber // no callback 24543356f56SNico Weber // 24643356f56SNico Weber // With the extension we get the full nested name specifier including 24743356f56SNico Weber // parent_path. 24843356f56SNico Weber // FIXME: Don't rely on source text. 24943356f56SNico Weber const char *End = Source.end(); 250601102d2SCorentin Jabot while (isAsciiIdentifierContinue(*End) || *End == ':') 25143356f56SNico Weber ++End; 25243356f56SNico Weber 25343356f56SNico Weber return std::string(Source.begin(), End); 25443356f56SNico Weber }; 25543356f56SNico Weber 25643356f56SNico Weber /// If we have a scope specification, use that to get more precise results. 25743356f56SNico Weber std::string QueryString; 25843356f56SNico Weber tooling::Range SymbolRange; 25943356f56SNico Weber const auto &SM = CI->getSourceManager(); 26043356f56SNico Weber auto CreateToolingRange = [&QueryString, &SM](SourceLocation BeginLoc) { 26143356f56SNico Weber return tooling::Range(SM.getDecomposedLoc(BeginLoc).second, 26243356f56SNico Weber QueryString.size()); 26343356f56SNico Weber }; 26443356f56SNico Weber if (SS && SS->getRange().isValid()) { 26543356f56SNico Weber auto Range = CharSourceRange::getTokenRange(SS->getRange().getBegin(), 26643356f56SNico Weber Typo.getLoc()); 26743356f56SNico Weber 26843356f56SNico Weber QueryString = ExtendNestedNameSpecifier(Range); 26943356f56SNico Weber SymbolRange = CreateToolingRange(Range.getBegin()); 27043356f56SNico Weber } else if (Typo.getName().isIdentifier() && !Typo.getLoc().isMacroID()) { 27143356f56SNico Weber auto Range = 27243356f56SNico Weber CharSourceRange::getTokenRange(Typo.getBeginLoc(), Typo.getEndLoc()); 27343356f56SNico Weber 27443356f56SNico Weber QueryString = ExtendNestedNameSpecifier(Range); 27543356f56SNico Weber SymbolRange = CreateToolingRange(Range.getBegin()); 27643356f56SNico Weber } else { 27743356f56SNico Weber QueryString = Typo.getAsString(); 27843356f56SNico Weber SymbolRange = CreateToolingRange(Typo.getLoc()); 27943356f56SNico Weber } 28043356f56SNico Weber 28143356f56SNico Weber LLVM_DEBUG(llvm::dbgs() << "TypoScopeQualifiers: " << TypoScopeString 28243356f56SNico Weber << "\n"); 28343356f56SNico Weber std::vector<find_all_symbols::SymbolInfo> MatchedSymbols = 28443356f56SNico Weber query(QueryString, TypoScopeString, SymbolRange); 28543356f56SNico Weber 28643356f56SNico Weber if (!MatchedSymbols.empty() && GenerateDiagnostics) { 28743356f56SNico Weber TypoCorrection Correction(Typo.getName()); 28843356f56SNico Weber Correction.setCorrectionRange(SS, Typo); 28943356f56SNico Weber FileID FID = SM.getFileID(Typo.getLoc()); 29043356f56SNico Weber StringRef Code = SM.getBufferData(FID); 29143356f56SNico Weber SourceLocation StartOfFile = SM.getLocForStartOfFile(FID); 29243356f56SNico Weber if (addDiagnosticsForContext( 29343356f56SNico Weber Correction, getIncludeFixerContext( 29443356f56SNico Weber SM, CI->getPreprocessor().getHeaderSearchInfo(), 29543356f56SNico Weber MatchedSymbols), 29643356f56SNico Weber Code, StartOfFile, CI->getASTContext())) 29743356f56SNico Weber return Correction; 29843356f56SNico Weber } 29943356f56SNico Weber return TypoCorrection(); 30043356f56SNico Weber } 30143356f56SNico Weber 30243356f56SNico Weber /// Get the minimal include for a given path. 30343356f56SNico Weber std::string IncludeFixerSemaSource::minimizeInclude( 30443356f56SNico Weber StringRef Include, const clang::SourceManager &SourceManager, 30543356f56SNico Weber clang::HeaderSearch &HeaderSearch) const { 30643356f56SNico Weber if (!MinimizeIncludePaths) 307adcd0268SBenjamin Kramer return std::string(Include); 30843356f56SNico Weber 30943356f56SNico Weber // Get the FileEntry for the include. 31043356f56SNico Weber StringRef StrippedInclude = Include.trim("\"<>"); 3116966c06bSJan Svoboda auto Entry = 3126966c06bSJan Svoboda SourceManager.getFileManager().getOptionalFileRef(StrippedInclude); 31343356f56SNico Weber 31443356f56SNico Weber // If the file doesn't exist return the path from the database. 31543356f56SNico Weber // FIXME: This should never happen. 31643356f56SNico Weber if (!Entry) 317adcd0268SBenjamin Kramer return std::string(Include); 31843356f56SNico Weber 3199fe632baSDavid Goldman bool IsAngled = false; 32043356f56SNico Weber std::string Suggestion = 3219fe632baSDavid Goldman HeaderSearch.suggestPathToFileForDiagnostics(*Entry, "", &IsAngled); 32243356f56SNico Weber 3239fe632baSDavid Goldman return IsAngled ? '<' + Suggestion + '>' : '"' + Suggestion + '"'; 32443356f56SNico Weber } 32543356f56SNico Weber 32643356f56SNico Weber /// Get the include fixer context for the queried symbol. 32743356f56SNico Weber IncludeFixerContext IncludeFixerSemaSource::getIncludeFixerContext( 32843356f56SNico Weber const clang::SourceManager &SourceManager, 32943356f56SNico Weber clang::HeaderSearch &HeaderSearch, 33043356f56SNico Weber ArrayRef<find_all_symbols::SymbolInfo> MatchedSymbols) const { 33143356f56SNico Weber std::vector<find_all_symbols::SymbolInfo> SymbolCandidates; 33243356f56SNico Weber for (const auto &Symbol : MatchedSymbols) { 33343356f56SNico Weber std::string FilePath = Symbol.getFilePath().str(); 33443356f56SNico Weber std::string MinimizedFilePath = minimizeInclude( 33543356f56SNico Weber ((FilePath[0] == '"' || FilePath[0] == '<') ? FilePath 33643356f56SNico Weber : "\"" + FilePath + "\""), 33743356f56SNico Weber SourceManager, HeaderSearch); 33843356f56SNico Weber SymbolCandidates.emplace_back(Symbol.getName(), Symbol.getSymbolKind(), 33943356f56SNico Weber MinimizedFilePath, Symbol.getContexts()); 34043356f56SNico Weber } 34143356f56SNico Weber return IncludeFixerContext(FilePath, QuerySymbolInfos, SymbolCandidates); 34243356f56SNico Weber } 34343356f56SNico Weber 34443356f56SNico Weber std::vector<find_all_symbols::SymbolInfo> 34543356f56SNico Weber IncludeFixerSemaSource::query(StringRef Query, StringRef ScopedQualifiers, 34643356f56SNico Weber tooling::Range Range) { 34743356f56SNico Weber assert(!Query.empty() && "Empty query!"); 34843356f56SNico Weber 34943356f56SNico Weber // Save all instances of an unidentified symbol. 35043356f56SNico Weber // 35143356f56SNico Weber // We use conservative behavior for detecting the same unidentified symbol 35243356f56SNico Weber // here. The symbols which have the same ScopedQualifier and RawIdentifier 35343356f56SNico Weber // are considered equal. So that clang-include-fixer avoids false positives, 35443356f56SNico Weber // and always adds missing qualifiers to correct symbols. 35543356f56SNico Weber if (!GenerateDiagnostics && !QuerySymbolInfos.empty()) { 35643356f56SNico Weber if (ScopedQualifiers == QuerySymbolInfos.front().ScopedQualifiers && 35743356f56SNico Weber Query == QuerySymbolInfos.front().RawIdentifier) { 358adcd0268SBenjamin Kramer QuerySymbolInfos.push_back( 359adcd0268SBenjamin Kramer {Query.str(), std::string(ScopedQualifiers), Range}); 36043356f56SNico Weber } 36143356f56SNico Weber return {}; 36243356f56SNico Weber } 36343356f56SNico Weber 36443356f56SNico Weber LLVM_DEBUG(llvm::dbgs() << "Looking up '" << Query << "' at "); 36543356f56SNico Weber LLVM_DEBUG(CI->getSourceManager() 36643356f56SNico Weber .getLocForStartOfFile(CI->getSourceManager().getMainFileID()) 36743356f56SNico Weber .getLocWithOffset(Range.getOffset()) 36843356f56SNico Weber .print(llvm::dbgs(), CI->getSourceManager())); 36943356f56SNico Weber LLVM_DEBUG(llvm::dbgs() << " ..."); 37043356f56SNico Weber llvm::StringRef FileName = CI->getSourceManager().getFilename( 37143356f56SNico Weber CI->getSourceManager().getLocForStartOfFile( 37243356f56SNico Weber CI->getSourceManager().getMainFileID())); 37343356f56SNico Weber 374adcd0268SBenjamin Kramer QuerySymbolInfos.push_back( 375adcd0268SBenjamin Kramer {Query.str(), std::string(ScopedQualifiers), Range}); 37643356f56SNico Weber 37743356f56SNico Weber // Query the symbol based on C++ name Lookup rules. 37843356f56SNico Weber // Firstly, lookup the identifier with scoped namespace contexts; 37943356f56SNico Weber // If that fails, falls back to look up the identifier directly. 38043356f56SNico Weber // 38143356f56SNico Weber // For example: 38243356f56SNico Weber // 38343356f56SNico Weber // namespace a { 38443356f56SNico Weber // b::foo f; 38543356f56SNico Weber // } 38643356f56SNico Weber // 38743356f56SNico Weber // 1. lookup a::b::foo. 38843356f56SNico Weber // 2. lookup b::foo. 38943356f56SNico Weber std::string QueryString = ScopedQualifiers.str() + Query.str(); 39043356f56SNico Weber // It's unsafe to do nested search for the identifier with scoped namespace 39143356f56SNico Weber // context, it might treat the identifier as a nested class of the scoped 39243356f56SNico Weber // namespace. 39343356f56SNico Weber std::vector<find_all_symbols::SymbolInfo> MatchedSymbols = 39443356f56SNico Weber SymbolIndexMgr.search(QueryString, /*IsNestedSearch=*/false, FileName); 39543356f56SNico Weber if (MatchedSymbols.empty()) 39643356f56SNico Weber MatchedSymbols = 39743356f56SNico Weber SymbolIndexMgr.search(Query, /*IsNestedSearch=*/true, FileName); 39843356f56SNico Weber LLVM_DEBUG(llvm::dbgs() << "Having found " << MatchedSymbols.size() 39943356f56SNico Weber << " symbols\n"); 40043356f56SNico Weber // We store a copy of MatchedSymbols in a place where it's globally reachable. 40143356f56SNico Weber // This is used by the standalone version of the tool. 40243356f56SNico Weber this->MatchedSymbols = MatchedSymbols; 40343356f56SNico Weber return MatchedSymbols; 40443356f56SNico Weber } 40543356f56SNico Weber 40643356f56SNico Weber llvm::Expected<tooling::Replacements> createIncludeFixerReplacements( 40743356f56SNico Weber StringRef Code, const IncludeFixerContext &Context, 40843356f56SNico Weber const clang::format::FormatStyle &Style, bool AddQualifiers) { 40943356f56SNico Weber if (Context.getHeaderInfos().empty()) 41043356f56SNico Weber return tooling::Replacements(); 41143356f56SNico Weber StringRef FilePath = Context.getFilePath(); 41243356f56SNico Weber std::string IncludeName = 41343356f56SNico Weber "#include " + Context.getHeaderInfos().front().Header + "\n"; 41443356f56SNico Weber // Create replacements for the new header. 41543356f56SNico Weber clang::tooling::Replacements Insertions; 41643356f56SNico Weber auto Err = 41743356f56SNico Weber Insertions.add(tooling::Replacement(FilePath, UINT_MAX, 0, IncludeName)); 41843356f56SNico Weber if (Err) 41943356f56SNico Weber return std::move(Err); 42043356f56SNico Weber 42143356f56SNico Weber auto CleanReplaces = cleanupAroundReplacements(Code, Insertions, Style); 42243356f56SNico Weber if (!CleanReplaces) 42343356f56SNico Weber return CleanReplaces; 42443356f56SNico Weber 42543356f56SNico Weber auto Replaces = std::move(*CleanReplaces); 42643356f56SNico Weber if (AddQualifiers) { 42743356f56SNico Weber for (const auto &Info : Context.getQuerySymbolInfos()) { 42843356f56SNico Weber // Ignore the empty range. 42943356f56SNico Weber if (Info.Range.getLength() > 0) { 43043356f56SNico Weber auto R = tooling::Replacement( 43143356f56SNico Weber {FilePath, Info.Range.getOffset(), Info.Range.getLength(), 43243356f56SNico Weber Context.getHeaderInfos().front().QualifiedName}); 43343356f56SNico Weber auto Err = Replaces.add(R); 43443356f56SNico Weber if (Err) { 43543356f56SNico Weber llvm::consumeError(std::move(Err)); 43643356f56SNico Weber R = tooling::Replacement( 43743356f56SNico Weber R.getFilePath(), Replaces.getShiftedCodePosition(R.getOffset()), 43843356f56SNico Weber R.getLength(), R.getReplacementText()); 43943356f56SNico Weber Replaces = Replaces.merge(tooling::Replacements(R)); 44043356f56SNico Weber } 44143356f56SNico Weber } 44243356f56SNico Weber } 44343356f56SNico Weber } 44443356f56SNico Weber return formatReplacements(Code, Replaces, Style); 44543356f56SNico Weber } 44643356f56SNico Weber 44743356f56SNico Weber } // namespace include_fixer 44843356f56SNico Weber } // namespace clang 449