From: Douglas Gregor Date: Tue, 12 Oct 2010 00:50:20 +0000 (+0000) Subject: When we load an ASTUnit from command-line arguments, hold on to the X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=4cd912aa94656697a44c3ebb159f05060300524e;p=clang When we load an ASTUnit from command-line arguments, hold on to the diagnostics produced by the driver itself. Previously, we were allowing these to either be dropped or to slip through to stderr. Fixes . git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@116285 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Frontend/ASTUnit.h b/include/clang/Frontend/ASTUnit.h index e3fd4b37ed..1e08c3a92d 100644 --- a/include/clang/Frontend/ASTUnit.h +++ b/include/clang/Frontend/ASTUnit.h @@ -115,6 +115,13 @@ private: /// translation unit. llvm::SmallVector StoredDiagnostics; + /// \brief The number of stored diagnostics that come from the driver + /// itself. + /// + /// Diagnostics that come from the driver are retained from one parse to + /// the next. + unsigned NumStoredDiagnosticsFromDriver; + /// \brief Temporary files that should be removed when the ASTUnit is /// destroyed. llvm::SmallVector TemporaryFiles; @@ -483,6 +490,19 @@ public: unsigned NumRemappedFiles = 0, bool CaptureDiagnostics = false); +private: + /// \brief Helper function for \c LoadFromCompilerInvocation() and + /// \c LoadFromCommandLine(), which loads an AST from a compiler invocation. + /// + /// \param PrecompilePreamble Whether to precompile the preamble of this + /// translation unit, to improve the performance of reparsing. + /// + /// \returns \c true if a catastrophic failure occurred (which means that the + /// \c ASTUnit itself is invalid), or \c false otherwise. + bool LoadFromCompilerInvocation(bool PrecompilePreamble); + +public: + /// LoadFromCompilerInvocation - Create an ASTUnit from a source file, via a /// CompilerInvocation object. /// diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp index bf66b85473..4319df3e92 100644 --- a/lib/Frontend/ASTUnit.cpp +++ b/lib/Frontend/ASTUnit.cpp @@ -52,7 +52,8 @@ const unsigned DefaultPreambleRebuildInterval = 5; ASTUnit::ASTUnit(bool _MainFileIsAST) : CaptureDiagnostics(false), MainFileIsAST(_MainFileIsAST), - CompleteTranslationUnit(true), ConcurrencyCheckValue(CheckUnlocked), + CompleteTranslationUnit(true), NumStoredDiagnosticsFromDriver(0), + ConcurrencyCheckValue(CheckUnlocked), PreambleRebuildCounter(0), SavedMainFileBuffer(0), PreambleBuffer(0), ShouldCacheCodeCompletionResults(false), NumTopLevelDeclsAtLastCompletionCache(0), @@ -714,7 +715,9 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { PreprocessedEntitiesByFile.clear(); if (!OverrideMainBuffer) { - StoredDiagnostics.clear(); + StoredDiagnostics.erase( + StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver, + StoredDiagnostics.end()); TopLevelDeclsInPreamble.clear(); } @@ -737,19 +740,21 @@ bool ASTUnit::Parse(llvm::MemoryBuffer *OverrideMainBuffer) { PreprocessorOpts.ImplicitPCHInclude = PreambleFile; PreprocessorOpts.DisablePCHValidation = true; - // Keep track of the override buffer; - SavedMainFileBuffer = OverrideMainBuffer; - // The stored diagnostic has the old source manager in it; update // the locations to refer into the new source manager. Since we've // been careful to make sure that the source manager's state // before and after are identical, so that we can reuse the source // location itself. - for (unsigned I = 0, N = StoredDiagnostics.size(); I != N; ++I) { + for (unsigned I = NumStoredDiagnosticsFromDriver, + N = StoredDiagnostics.size(); + I < N; ++I) { FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), getSourceManager()); StoredDiagnostics[I].setLocation(Loc); } + + // Keep track of the override buffer; + SavedMainFileBuffer = OverrideMainBuffer; } else { PreprocessorOpts.PrecompiledPreambleBytes.first = 0; PreprocessorOpts.PrecompiledPreambleBytes.second = false; @@ -1201,7 +1206,9 @@ llvm::MemoryBuffer *ASTUnit::getMainBufferWithPrecompiledPreamble( // Clear out old caches and data. getDiagnostics().Reset(); ProcessWarningOptions(getDiagnostics(), Clang.getDiagnosticOpts()); - StoredDiagnostics.clear(); + StoredDiagnostics.erase( + StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver, + StoredDiagnostics.end()); TopLevelDecls.clear(); TopLevelDeclsInPreamble.clear(); @@ -1306,6 +1313,41 @@ unsigned ASTUnit::getMaxPCHLevel() const { return 0; } +bool ASTUnit::LoadFromCompilerInvocation(bool PrecompilePreamble) { + if (!Invocation) + return true; + + // We'll manage file buffers ourselves. + Invocation->getPreprocessorOpts().RetainRemappedFileBuffers = true; + Invocation->getFrontendOpts().DisableFree = false; + + if (getenv("LIBCLANG_TIMING")) + TimerGroup.reset( + new llvm::TimerGroup(Invocation->getFrontendOpts().Inputs[0].second)); + + + llvm::MemoryBuffer *OverrideMainBuffer = 0; + // FIXME: When C++ PCH is ready, allow use of it for a precompiled preamble. + if (PrecompilePreamble && !Invocation->getLangOpts().CPlusPlus) { + PreambleRebuildCounter = 1; + OverrideMainBuffer + = getMainBufferWithPrecompiledPreamble(*Invocation); + } + + llvm::Timer *ParsingTimer = 0; + if (TimerGroup.get()) { + ParsingTimer = new llvm::Timer("Initial parse", *TimerGroup); + ParsingTimer->startTimer(); + Timers.push_back(ParsingTimer); + } + + bool Failed = Parse(OverrideMainBuffer); + if (ParsingTimer) + ParsingTimer->stopTimer(); + + return Failed; +} + ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, llvm::IntrusiveRefCntPtr Diags, bool OnlyLocalDecls, @@ -1329,33 +1371,8 @@ ASTUnit *ASTUnit::LoadFromCompilerInvocation(CompilerInvocation *CI, AST->CompleteTranslationUnit = CompleteTranslationUnit; AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; AST->Invocation.reset(CI); - CI->getPreprocessorOpts().RetainRemappedFileBuffers = true; - - if (getenv("LIBCLANG_TIMING")) - AST->TimerGroup.reset( - new llvm::TimerGroup(CI->getFrontendOpts().Inputs[0].second)); - - llvm::MemoryBuffer *OverrideMainBuffer = 0; - // FIXME: When C++ PCH is ready, allow use of it for a precompiled preamble. - if (PrecompilePreamble && !CI->getLangOpts().CPlusPlus) { - AST->PreambleRebuildCounter = 1; - OverrideMainBuffer - = AST->getMainBufferWithPrecompiledPreamble(*AST->Invocation); - } - - llvm::Timer *ParsingTimer = 0; - if (AST->TimerGroup.get()) { - ParsingTimer = new llvm::Timer("Initial parse", *AST->TimerGroup); - ParsingTimer->startTimer(); - AST->Timers.push_back(ParsingTimer); - } - - bool Failed = AST->Parse(OverrideMainBuffer); - if (ParsingTimer) - ParsingTimer->stopTimer(); - - return Failed? 0 : AST.take(); + return AST->LoadFromCompilerInvocation(PrecompilePreamble)? 0 : AST.take(); } ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, @@ -1387,41 +1404,50 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, // also want to force it to use clang. Args.push_back("-fsyntax-only"); - // FIXME: We shouldn't have to pass in the path info. - driver::Driver TheDriver("clang", llvm::sys::getHostTriple(), - "a.out", false, false, *Diags); - - // Don't check that inputs exist, they have been remapped. - TheDriver.setCheckInputsExist(false); - - llvm::OwningPtr C( - TheDriver.BuildCompilation(Args.size(), Args.data())); + llvm::SmallVector StoredDiagnostics; + + llvm::OwningPtr CI; + { + CaptureDroppedDiagnostics Capture(CaptureDiagnostics, + *Diags, + StoredDiagnostics); + + // FIXME: We shouldn't have to pass in the path info. + driver::Driver TheDriver("clang", llvm::sys::getHostTriple(), + "a.out", false, false, *Diags); + + // Don't check that inputs exist, they have been remapped. + TheDriver.setCheckInputsExist(false); + + llvm::OwningPtr C( + TheDriver.BuildCompilation(Args.size(), Args.data())); + + // We expect to get back exactly one command job, if we didn't something + // failed. + const driver::JobList &Jobs = C->getJobs(); + if (Jobs.size() != 1 || !isa(Jobs.begin())) { + llvm::SmallString<256> Msg; + llvm::raw_svector_ostream OS(Msg); + C->PrintJob(OS, C->getJobs(), "; ", true); + Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); + return 0; + } - // We expect to get back exactly one command job, if we didn't something - // failed. - const driver::JobList &Jobs = C->getJobs(); - if (Jobs.size() != 1 || !isa(Jobs.begin())) { - llvm::SmallString<256> Msg; - llvm::raw_svector_ostream OS(Msg); - C->PrintJob(OS, C->getJobs(), "; ", true); - Diags->Report(diag::err_fe_expected_compiler_job) << OS.str(); - return 0; - } + const driver::Command *Cmd = cast(*Jobs.begin()); + if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { + Diags->Report(diag::err_fe_expected_clang_command); + return 0; + } - const driver::Command *Cmd = cast(*Jobs.begin()); - if (llvm::StringRef(Cmd->getCreator().getName()) != "clang") { - Diags->Report(diag::err_fe_expected_clang_command); - return 0; + const driver::ArgStringList &CCArgs = Cmd->getArguments(); + CI.reset(new CompilerInvocation); + CompilerInvocation::CreateFromArgs(*CI, + const_cast(CCArgs.data()), + const_cast(CCArgs.data()) + + CCArgs.size(), + *Diags); } - - const driver::ArgStringList &CCArgs = Cmd->getArguments(); - llvm::OwningPtr CI(new CompilerInvocation); - CompilerInvocation::CreateFromArgs(*CI, - const_cast(CCArgs.data()), - const_cast(CCArgs.data()) + - CCArgs.size(), - *Diags); - + // Override any files that need remapping for (unsigned I = 0; I != NumRemappedFiles; ++I) CI->getPreprocessorOpts().addRemappedFile(RemappedFiles[I].first, @@ -1430,11 +1456,19 @@ ASTUnit *ASTUnit::LoadFromCommandLine(const char **ArgBegin, // Override the resources path. CI->getHeaderSearchOpts().ResourceDir = ResourceFilesPath; - CI->getFrontendOpts().DisableFree = false; - return LoadFromCompilerInvocation(CI.take(), Diags, OnlyLocalDecls, - CaptureDiagnostics, PrecompilePreamble, - CompleteTranslationUnit, - CacheCodeCompletionResults); + // Create the AST unit. + llvm::OwningPtr AST; + AST.reset(new ASTUnit(false)); + AST->Diagnostics = Diags; + AST->CaptureDiagnostics = CaptureDiagnostics; + AST->OnlyLocalDecls = OnlyLocalDecls; + AST->CompleteTranslationUnit = CompleteTranslationUnit; + AST->ShouldCacheCodeCompletionResults = CacheCodeCompletionResults; + AST->NumStoredDiagnosticsFromDriver = StoredDiagnostics.size(); + AST->NumStoredDiagnosticsInPreamble = StoredDiagnostics.size(); + AST->StoredDiagnostics.swap(StoredDiagnostics); + AST->Invocation.reset(CI.take()); + return AST->LoadFromCompilerInvocation(PrecompilePreamble)? 0 : AST.take(); } bool ASTUnit::Reparse(RemappedFile *RemappedFiles, unsigned NumRemappedFiles) { @@ -1832,6 +1866,9 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, // If the main file has been overridden due to the use of a preamble, // make that override happen and introduce the preamble. + StoredDiagnostics.insert(StoredDiagnostics.end(), + this->StoredDiagnostics.begin(), + this->StoredDiagnostics.begin() + NumStoredDiagnosticsFromDriver); if (OverrideMainBuffer) { PreprocessorOpts.addRemappedFile(OriginalSourceFile, OverrideMainBuffer); PreprocessorOpts.PrecompiledPreambleBytes.first = Preamble.size(); @@ -1843,12 +1880,14 @@ void ASTUnit::CodeComplete(llvm::StringRef File, unsigned Line, unsigned Column, // The stored diagnostics have the old source manager. Copy them // to our output set of stored diagnostics, updating the source // manager to the one we were given. - for (unsigned I = 0, N = this->StoredDiagnostics.size(); I != N; ++I) { + for (unsigned I = NumStoredDiagnosticsFromDriver, + N = this->StoredDiagnostics.size(); + I < N; ++I) { StoredDiagnostics.push_back(this->StoredDiagnostics[I]); FullSourceLoc Loc(StoredDiagnostics[I].getLocation(), SourceMgr); StoredDiagnostics[I].setLocation(Loc); } - + OwnedBuffers.push_back(OverrideMainBuffer); } else { PreprocessorOpts.PrecompiledPreambleBytes.first = 0; diff --git a/test/Index/complete-driver-errors.c b/test/Index/complete-driver-errors.c new file mode 100644 index 0000000000..0d2ceb94d4 --- /dev/null +++ b/test/Index/complete-driver-errors.c @@ -0,0 +1,23 @@ +int *blah = 1; + +int + +// Test driver errors with code completion +// RUN: c-index-test -code-completion-at=%s:4:1 -std= %s 2> %t | FileCheck -check-prefix=CHECK-RESULTS %s +// RUN: FileCheck -check-prefix=CHECK-DIAGS %s < %t +// CHECK-RESULTS: NotImplemented:{TypedText const} (30) +// CHECK-RESULTS: NotImplemented:{TypedText restrict} (30) +// CHECK-RESULTS: NotImplemented:{TypedText volatile} (30) + +// Test driver errors with parsing +// RUN: c-index-test -test-load-source all -std= %s 2> %t | FileCheck -check-prefix=CHECK-LOAD %s +// RUN: FileCheck -check-prefix=CHECK-DIAGS %s < %t +// CHECK-LOAD: complete-driver-errors.c:1:6: VarDecl=blah:1:6 + +// CHECK-DIAGS: error: invalid value '' in '-std=' +// CHECK-DIAGS: complete-driver-errors.c:1:6:{1:13-1:14}: warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' +// Test driver errors with code completion and precompiled preamble +// RUN: env CINDEXTEST_EDITING=1 c-index-test -code-completion-at=%s:4:1 -std= %s 2> %t | FileCheck -check-prefix=CHECK-RESULTS %s +// RUN: FileCheck -check-prefix=CHECK-DIAGS %s < %t +// RUN: env CINDEXTEST_EDITING=1 c-index-test -test-load-source all -std= %s 2> %t | FileCheck -check-prefix=CHECK-LOAD %s +// RUN: FileCheck -check-prefix=CHECK-DIAGS %s < %t diff --git a/tools/libclang/CIndexDiagnostic.cpp b/tools/libclang/CIndexDiagnostic.cpp index 85f451e855..0766548418 100644 --- a/tools/libclang/CIndexDiagnostic.cpp +++ b/tools/libclang/CIndexDiagnostic.cpp @@ -56,10 +56,6 @@ CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { CXDiagnosticSeverity Severity = clang_getDiagnosticSeverity(Diagnostic); - // Ignore diagnostics that should be ignored. - if (Severity == CXDiagnostic_Ignored) - return createCXString(""); - llvm::SmallString<256> Str; llvm::raw_svector_ostream Out(Str); @@ -101,9 +97,9 @@ CXString clang_formatDiagnostic(CXDiagnostic Diagnostic, unsigned Options) { if (PrintedRange) Out << ":"; } + + Out << " "; } - - Out << " "; } /* Print warning/error/etc. */