Mercurial > hg > CbC > CbC_llvm
diff clang-tools-extra/clangd/ParsedAST.cpp @ 173:0572611fdcc8 llvm10 llvm12
reorgnization done
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Mon, 25 May 2020 11:55:54 +0900 |
parents | 1d019706d866 |
children | 2e18cbf3894f |
line wrap: on
line diff
--- a/clang-tools-extra/clangd/ParsedAST.cpp Mon May 25 11:50:15 2020 +0900 +++ b/clang-tools-extra/clangd/ParsedAST.cpp Mon May 25 11:55:54 2020 +0900 @@ -14,11 +14,11 @@ #include "Diagnostics.h" #include "Headers.h" #include "IncludeFixer.h" -#include "Logger.h" #include "SourceCode.h" -#include "Trace.h" #include "index/CanonicalIncludes.h" #include "index/Index.h" +#include "support/Logger.h" +#include "support/Trace.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/Basic/LangOptions.h" @@ -114,16 +114,16 @@ // Attach preprocessor hooks such that preamble events will be injected at // the appropriate time. // Events will be delivered to the *currently registered* PP callbacks. - static void attach(const IncludeStructure &Includes, - CompilerInstance &Clang) { + static void attach(const IncludeStructure &Includes, CompilerInstance &Clang, + const PreambleBounds &PB) { auto &PP = Clang.getPreprocessor(); auto *ExistingCallbacks = PP.getPPCallbacks(); // No need to replay events if nobody is listening. if (!ExistingCallbacks) return; - PP.addPPCallbacks(std::unique_ptr<PPCallbacks>( - new ReplayPreamble(Includes, ExistingCallbacks, - Clang.getSourceManager(), PP, Clang.getLangOpts()))); + PP.addPPCallbacks(std::unique_ptr<PPCallbacks>(new ReplayPreamble( + Includes, ExistingCallbacks, Clang.getSourceManager(), PP, + Clang.getLangOpts(), PB))); // We're relying on the fact that addPPCallbacks keeps the old PPCallbacks // around, creating a chaining wrapper. Guard against other implementations. assert(PP.getPPCallbacks() != ExistingCallbacks && @@ -133,9 +133,13 @@ private: ReplayPreamble(const IncludeStructure &Includes, PPCallbacks *Delegate, const SourceManager &SM, Preprocessor &PP, - const LangOptions &LangOpts) - : Includes(Includes), Delegate(Delegate), SM(SM), PP(PP), - LangOpts(LangOpts) {} + const LangOptions &LangOpts, const PreambleBounds &PB) + : Includes(Includes), Delegate(Delegate), SM(SM), PP(PP) { + // Only tokenize the preamble section of the main file, as we are not + // interested in the rest of the tokens. + MainFileTokens = syntax::tokenize( + syntax::FileRange(SM.getMainFileID(), 0, PB.Size), SM, LangOpts); + } // In a normal compile, the preamble traverses the following structure: // @@ -167,33 +171,53 @@ if (auto FE = SM.getFileManager().getFile(Inc.Resolved)) File = *FE; + // Re-lex the #include directive to find its interesting parts. + auto HashLoc = SM.getComposedLoc(SM.getMainFileID(), Inc.HashOffset); + auto HashTok = llvm::partition_point(MainFileTokens, + [&HashLoc](const syntax::Token &T) { + return T.location() < HashLoc; + }); + assert(HashTok != MainFileTokens.end() && HashTok->kind() == tok::hash); + + auto IncludeTok = std::next(HashTok); + assert(IncludeTok != MainFileTokens.end()); + + auto FileTok = std::next(IncludeTok); + assert(FileTok != MainFileTokens.end()); + + // Create a fake import/include token, none of the callers seem to care + // about clang::Token::Flags. + Token SynthesizedIncludeTok; + SynthesizedIncludeTok.startToken(); + SynthesizedIncludeTok.setLocation(IncludeTok->location()); + SynthesizedIncludeTok.setLength(IncludeTok->length()); + SynthesizedIncludeTok.setKind(tok::raw_identifier); + SynthesizedIncludeTok.setRawIdentifierData(IncludeTok->text(SM).data()); + PP.LookUpIdentifierInfo(SynthesizedIncludeTok); + + // Same here, create a fake one for Filename, including angles or quotes. + Token SynthesizedFilenameTok; + SynthesizedFilenameTok.startToken(); + SynthesizedFilenameTok.setLocation(FileTok->location()); + // Note that we can't make use of FileTok->length/text in here as in the + // case of angled includes this will contain tok::less instead of + // filename. Whereas Inc.Written contains the full header name including + // quotes/angles. + SynthesizedFilenameTok.setLength(Inc.Written.length()); + SynthesizedFilenameTok.setKind(tok::header_name); + SynthesizedFilenameTok.setLiteralData(Inc.Written.data()); + llvm::StringRef WrittenFilename = llvm::StringRef(Inc.Written).drop_front().drop_back(); - bool Angled = llvm::StringRef(Inc.Written).startswith("<"); - - // Re-lex the #include directive to find its interesting parts. - llvm::StringRef Src = SM.getBufferData(SM.getMainFileID()); - Lexer RawLexer(SM.getLocForStartOfFile(SM.getMainFileID()), LangOpts, - Src.begin(), Src.begin() + Inc.HashOffset, Src.end()); - Token HashTok, IncludeTok, FilenameTok; - RawLexer.LexFromRawLexer(HashTok); - assert(HashTok.getKind() == tok::hash); - RawLexer.setParsingPreprocessorDirective(true); - RawLexer.LexFromRawLexer(IncludeTok); - IdentifierInfo *II = PP.getIdentifierInfo(IncludeTok.getRawIdentifier()); - IncludeTok.setIdentifierInfo(II); - IncludeTok.setKind(II->getTokenID()); - RawLexer.LexIncludeFilename(FilenameTok); - - Delegate->InclusionDirective( - HashTok.getLocation(), IncludeTok, WrittenFilename, Angled, - CharSourceRange::getCharRange(FilenameTok.getLocation(), - FilenameTok.getEndLoc()), - File, "SearchPath", "RelPath", /*Imported=*/nullptr, Inc.FileKind); + Delegate->InclusionDirective(HashTok->location(), SynthesizedIncludeTok, + WrittenFilename, Inc.Written.front() == '<', + FileTok->range(SM).toCharRange(SM), File, + "SearchPath", "RelPath", + /*Imported=*/nullptr, Inc.FileKind); if (File) // FIXME: Use correctly named FileEntryRef. - Delegate->FileSkipped(FileEntryRef(File->getName(), *File), FilenameTok, - Inc.FileKind); + Delegate->FileSkipped(FileEntryRef(File->getName(), *File), + SynthesizedFilenameTok, Inc.FileKind); else { llvm::SmallString<1> UnusedRecovery; Delegate->FileNotFound(WrittenFilename, UnusedRecovery); @@ -205,7 +229,7 @@ PPCallbacks *Delegate; const SourceManager &SM; Preprocessor &PP; - const LangOptions &LangOpts; + std::vector<syntax::Token> MainFileTokens; }; } // namespace @@ -215,12 +239,22 @@ } llvm::Optional<ParsedAST> -ParsedAST::build(std::unique_ptr<clang::CompilerInvocation> CI, +ParsedAST::build(llvm::StringRef Filename, const ParseInputs &Inputs, + std::unique_ptr<clang::CompilerInvocation> CI, llvm::ArrayRef<Diag> CompilerInvocationDiags, - std::shared_ptr<const PreambleData> Preamble, - std::unique_ptr<llvm::MemoryBuffer> Buffer, - llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS, - const SymbolIndex *Index, const ParseOptions &Opts) { + std::shared_ptr<const PreambleData> Preamble) { + trace::Span Tracer("BuildAST"); + SPAN_ATTACH(Tracer, "File", Filename); + + auto VFS = Inputs.FS; + if (Preamble && Preamble->StatCache) + VFS = Preamble->StatCache->getConsumingFS(std::move(VFS)); + if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { + log("Couldn't set working directory when building the preamble."); + // We proceed anyway, our lit-tests rely on results for non-existing working + // dirs. + } + assert(CI); // Command-line parsing sets DisableFree to true by default, but we don't want // to leak memory in clangd. @@ -228,13 +262,16 @@ const PrecompiledPreamble *PreamblePCH = Preamble ? &Preamble->Preamble : nullptr; + // This is on-by-default in windows to allow parsing SDK headers, but it + // breaks many features. Disable it for the main-file (not preamble). + CI->getLangOpts()->DelayedTemplateParsing = false; + StoreDiags ASTDiags; - std::string Content = std::string(Buffer->getBuffer()); - std::string Filename = - std::string(Buffer->getBufferIdentifier()); // Absolute. - auto Clang = prepareCompilerInstance(std::move(CI), PreamblePCH, - std::move(Buffer), VFS, ASTDiags); + auto Clang = prepareCompilerInstance( + std::move(CI), PreamblePCH, + llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, Filename), VFS, + ASTDiags); if (!Clang) return None; @@ -247,7 +284,7 @@ } // Set up ClangTidy. Must happen after BeginSourceFile() so ASTContext exists. - // Clang-tidy has some limitiations to ensure reasonable performance: + // Clang-tidy has some limitations to ensure reasonable performance: // - checks don't see all preprocessor events in the preamble // - matchers run only over the main-file top-level decls (and can't see // ancestors outside this scope). @@ -258,12 +295,12 @@ { trace::Span Tracer("ClangTidyInit"); dlog("ClangTidy configuration for file {0}: {1}", Filename, - tidy::configurationAsText(Opts.ClangTidyOpts)); + tidy::configurationAsText(Inputs.Opts.ClangTidyOpts)); tidy::ClangTidyCheckFactories CTFactories; for (const auto &E : tidy::ClangTidyModuleRegistry::entries()) E.instantiate()->addCheckFactories(CTFactories); CTContext.emplace(std::make_unique<tidy::DefaultOptionsProvider>( - tidy::ClangTidyGlobalOptions(), Opts.ClangTidyOpts)); + tidy::ClangTidyGlobalOptions(), Inputs.Opts.ClangTidyOpts)); CTContext->setDiagnosticsEngine(&Clang->getDiagnostics()); CTContext->setASTContext(&Clang->getASTContext()); CTContext->setCurrentFile(Filename); @@ -274,33 +311,34 @@ std::string CheckName = CTContext->getCheckName(Info.getID()); bool IsClangTidyDiag = !CheckName.empty(); if (IsClangTidyDiag) { + // Check for suppression comment. Skip the check for diagnostics not + // in the main file, because we don't want that function to query the + // source buffer for preamble files. For the same reason, we ask + // shouldSuppressDiagnostic to avoid I/O. + // We let suppression comments take precedence over warning-as-error + // to match clang-tidy's behaviour. + bool IsInsideMainFile = + Info.hasSourceManager() && + isInsideMainFile(Info.getLocation(), Info.getSourceManager()); + if (IsInsideMainFile && + tidy::shouldSuppressDiagnostic(DiagLevel, Info, *CTContext, + /*AllowIO=*/false)) { + return DiagnosticsEngine::Ignored; + } + // Check for warning-as-error. - // We deliberately let this take precedence over suppression comments - // to match clang-tidy's behaviour. if (DiagLevel == DiagnosticsEngine::Warning && CTContext->treatAsError(CheckName)) { return DiagnosticsEngine::Error; } - - // Check for suppression comment. Skip the check for diagnostics not - // in the main file, because we don't want that function to query the - // source buffer for preamble files. For the same reason, we ask - // shouldSuppressDiagnostic not to follow macro expansions, since - // those might take us into a preamble file as well. - bool IsInsideMainFile = - Info.hasSourceManager() && - isInsideMainFile(Info.getLocation(), Info.getSourceManager()); - if (IsInsideMainFile && tidy::shouldSuppressDiagnostic( - DiagLevel, Info, *CTContext, - /* CheckMacroExpansion = */ false)) { - return DiagnosticsEngine::Ignored; - } } } return DiagLevel; }); Preprocessor *PP = &Clang->getPreprocessor(); for (const auto &Check : CTChecks) { + if (!Check->isLanguageVersionSupported(CTContext->getLangOpts())) + continue; // FIXME: the PP callbacks skip the entire preamble. // Checks that want to see #includes in the main file do not see them. Check->registerPPCallbacks(Clang->getSourceManager(), PP, PP); @@ -312,16 +350,17 @@ // (e.g. incomplete type) and attach include insertion fixes to diagnostics. llvm::Optional<IncludeFixer> FixIncludes; auto BuildDir = VFS->getCurrentWorkingDirectory(); - if (Opts.SuggestMissingIncludes && Index && !BuildDir.getError()) { - auto Style = getFormatStyleForFile(Filename, Content, VFS.get()); + if (Inputs.Opts.SuggestMissingIncludes && Inputs.Index && + !BuildDir.getError()) { + auto Style = getFormatStyleForFile(Filename, Inputs.Contents, VFS.get()); auto Inserter = std::make_shared<IncludeInserter>( - Filename, Content, Style, BuildDir.get(), + Filename, Inputs.Contents, Style, BuildDir.get(), &Clang->getPreprocessor().getHeaderSearchInfo()); if (Preamble) { for (const auto &Inc : Preamble->Includes.MainFileIncludes) Inserter->addExisting(Inc); } - FixIncludes.emplace(Filename, Inserter, *Index, + FixIncludes.emplace(Filename, Inserter, *Inputs.Index, /*IndexRequestLimit=*/5); ASTDiags.contributeFixes([&FixIncludes](DiagnosticsEngine::Level DiagLevl, const clang::Diagnostic &Info) { @@ -335,7 +374,7 @@ auto Includes = Preamble ? Preamble->Includes : IncludeStructure{}; // Replay the preamble includes so that clang-tidy checks can see them. if (Preamble) - ReplayPreamble::attach(Includes, *Clang); + ReplayPreamble::attach(Includes, *Clang, Preamble->Preamble.getBounds()); // Important: collectIncludeStructure is registered *after* ReplayPreamble! // Otherwise we would collect the replayed includes again... // (We can't *just* use the replayed includes, they don't have Resolved path). @@ -348,7 +387,7 @@ Macros = Preamble->Macros; Clang->getPreprocessor().addPPCallbacks( std::make_unique<CollectMainFileMacros>(Clang->getSourceManager(), - Clang->getLangOpts(), Macros)); + Macros)); // Copy over the includes from the preamble, then combine with the // non-preamble includes below. @@ -401,10 +440,10 @@ std::vector<Diag> D = ASTDiags.take(CTContext.getPointer()); Diags.insert(Diags.end(), D.begin(), D.end()); } - return ParsedAST(std::move(Preamble), std::move(Clang), std::move(Action), - std::move(Tokens), std::move(Macros), std::move(ParsedDecls), - std::move(Diags), std::move(Includes), - std::move(CanonIncludes)); + return ParsedAST(Inputs.Version, std::move(Preamble), std::move(Clang), + std::move(Action), std::move(Tokens), std::move(Macros), + std::move(ParsedDecls), std::move(Diags), + std::move(Includes), std::move(CanonIncludes)); } ParsedAST::ParsedAST(ParsedAST &&Other) = default; @@ -456,7 +495,7 @@ // FIXME: the rest of the function is almost a direct copy-paste from // libclang's clang_getCXTUResourceUsage. We could share the implementation. - // Sum up variaous allocators inside the ast context and the preprocessor. + // Sum up various allocators inside the ast context and the preprocessor. Total += AST.getASTAllocatedMemory(); Total += AST.getSideTableAllocatedMemory(); Total += AST.Idents.getAllocator().getTotalMemory(); @@ -486,14 +525,15 @@ return CanonIncludes; } -ParsedAST::ParsedAST(std::shared_ptr<const PreambleData> Preamble, +ParsedAST::ParsedAST(llvm::StringRef Version, + std::shared_ptr<const PreambleData> Preamble, std::unique_ptr<CompilerInstance> Clang, std::unique_ptr<FrontendAction> Action, syntax::TokenBuffer Tokens, MainFileMacros Macros, std::vector<Decl *> LocalTopLevelDecls, std::vector<Diag> Diags, IncludeStructure Includes, CanonicalIncludes CanonIncludes) - : Preamble(std::move(Preamble)), Clang(std::move(Clang)), + : Version(Version), Preamble(std::move(Preamble)), Clang(std::move(Clang)), Action(std::move(Action)), Tokens(std::move(Tokens)), Macros(std::move(Macros)), Diags(std::move(Diags)), LocalTopLevelDecls(std::move(LocalTopLevelDecls)), @@ -502,29 +542,5 @@ assert(this->Action); } -llvm::Optional<ParsedAST> -buildAST(PathRef FileName, std::unique_ptr<CompilerInvocation> Invocation, - llvm::ArrayRef<Diag> CompilerInvocationDiags, - const ParseInputs &Inputs, - std::shared_ptr<const PreambleData> Preamble) { - trace::Span Tracer("BuildAST"); - SPAN_ATTACH(Tracer, "File", FileName); - - auto VFS = Inputs.FS; - if (Preamble && Preamble->StatCache) - VFS = Preamble->StatCache->getConsumingFS(std::move(VFS)); - if (VFS->setCurrentWorkingDirectory(Inputs.CompileCommand.Directory)) { - log("Couldn't set working directory when building the preamble."); - // We proceed anyway, our lit-tests rely on results for non-existing working - // dirs. - } - - return ParsedAST::build( - std::make_unique<CompilerInvocation>(*Invocation), - CompilerInvocationDiags, Preamble, - llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents, FileName), - std::move(VFS), Inputs.Index, Inputs.Opts); -} - } // namespace clangd } // namespace clang