clang-cc: Switch to using FrontendAction. Whee.

Please report any discrepancies you see in clang-cc, I'm not confident that our regression tests cover all the fun ways one can use clang-cc.

llvm-svn: 88776
This commit is contained in:
Daniel Dunbar
2009-11-14 10:53:49 +00:00
parent 883578060f
commit ea956ddf0b

View File

@@ -8,50 +8,32 @@
//===----------------------------------------------------------------------===//
//
// This utility may be invoked in the following manner:
// clang --help - Output help info.
// clang [options] - Read from stdin.
// clang [options] file - Read from "file".
// clang [options] file1 file2 - Read these files.
// clang-cc --help - Output help info.
// clang-cc [options] - Read from stdin.
// clang-cc [options] file - Read from "file".
// clang-cc [options] file1 file2 - Read these files.
//
//===----------------------------------------------------------------------===//
#include "Options.h"
#include "clang/AST/ASTConsumer.h"
#include "clang/AST/ASTContext.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
#include "clang/Basic/Version.h"
#include "clang/CodeGen/ModuleBuilder.h"
#include "clang/Frontend/ASTConsumers.h"
#include "clang/Frontend/ASTUnit.h"
#include "clang/Frontend/AnalysisConsumer.h"
#include "clang/Frontend/CommandLineSourceLoc.h"
#include "clang/Frontend/CompilerInstance.h"
#include "clang/Frontend/CompilerInvocation.h"
#include "clang/Frontend/DependencyOutputOptions.h"
#include "clang/Frontend/FixItRewriter.h"
#include "clang/Frontend/FrontendActions.h"
#include "clang/Frontend/FrontendDiagnostic.h"
#include "clang/Frontend/PathDiagnosticClients.h"
#include "clang/Frontend/PreprocessorOptions.h"
#include "clang/Frontend/PreprocessorOutputOptions.h"
#include "clang/Frontend/Utils.h"
#include "clang/Frontend/VerifyDiagnosticsClient.h"
#include "clang/Lex/HeaderSearch.h"
#include "clang/Lex/LexDiagnostic.h"
#include "clang/Parse/Parser.h"
#include "clang/Sema/CodeCompleteConsumer.h"
#include "clang/Sema/ParseAST.h"
#include "clang/Sema/SemaDiagnostic.h"
#include "llvm/LLVMContext.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/StringExtras.h"
#include "llvm/Config/config.h"
#include "llvm/LLVMContext.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/ManagedStatic.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/PluginLoader.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/Timer.h"
@@ -183,19 +165,6 @@ std::string GetBuiltinIncludePath(const char *Argv0) {
return P.str();
}
//===----------------------------------------------------------------------===//
// Basic Parser driver
//===----------------------------------------------------------------------===//
static void ParseFile(Preprocessor &PP, MinimalAction *PA) {
Parser P(PP, *PA);
PP.EnterMainSourceFile();
// Parsing the specified input file.
P.ParseTranslationUnit();
delete PA;
}
//===----------------------------------------------------------------------===//
// Main driver
//===----------------------------------------------------------------------===//
@@ -205,359 +174,37 @@ static void ParseFile(Preprocessor &PP, MinimalAction *PA) {
/// anything.
llvm::Timer *ClangFrontendTimer = 0;
/// AddFixItLocations - Add any individual user specified "fix-it" locations,
/// and return true on success.
static bool AddFixItLocations(CompilerInstance &CI,
FixItRewriter &FixItRewrite) {
const std::vector<ParsedSourceLocation> &Locs =
CI.getFrontendOpts().FixItLocations;
for (unsigned i = 0, e = Locs.size(); i != e; ++i) {
const FileEntry *File = CI.getFileManager().getFile(Locs[i].FileName);
if (!File) {
CI.getDiagnostics().Report(diag::err_fe_unable_to_find_fixit_file)
<< Locs[i].FileName;
return false;
}
RequestedSourceLocation Requested;
Requested.File = File;
Requested.Line = Locs[i].Line;
Requested.Column = Locs[i].Column;
FixItRewrite.addFixItLocation(Requested);
}
return true;
}
static ASTConsumer *CreateConsumerAction(CompilerInstance &CI,
Preprocessor &PP,
const std::string &InFile,
ProgActions PA) {
const FrontendOptions &FEOpts = CI.getFrontendOpts();
static FrontendAction *CreateFrontendAction(ProgActions PA) {
switch (PA) {
default:
return 0;
case ASTPrint:
return CreateASTPrinter(CI.createDefaultOutputFile(false, InFile));
case ASTPrintXML:
return CreateASTPrinterXML(CI.createDefaultOutputFile(false, InFile,
"xml"));
case ASTDump:
return CreateASTDumper();
case ASTView:
return CreateASTViewer();
case DumpRecordLayouts:
return CreateRecordLayoutDumper();
case InheritanceView:
return CreateInheritanceViewer(FEOpts.ViewClassInheritance);
case EmitAssembly:
case EmitLLVM:
case EmitBC:
case EmitLLVMOnly: {
BackendAction Act;
llvm::OwningPtr<llvm::raw_ostream> OS;
if (ProgAction == EmitAssembly) {
Act = Backend_EmitAssembly;
OS.reset(CI.createDefaultOutputFile(false, InFile, "s"));
} else if (ProgAction == EmitLLVM) {
Act = Backend_EmitLL;
OS.reset(CI.createDefaultOutputFile(false, InFile, "ll"));
} else if (ProgAction == EmitLLVMOnly) {
Act = Backend_EmitNothing;
} else {
Act = Backend_EmitBC;
OS.reset(CI.createDefaultOutputFile(true, InFile, "bc"));
}
return CreateBackendConsumer(Act, PP.getDiagnostics(), PP.getLangOptions(),
CI.getCodeGenOpts(), InFile, OS.take(),
CI.getLLVMContext());
default: return 0;
case ASTDump: return new ASTDumpAction();
case ASTPrint: return new ASTPrintAction();
case ASTPrintXML: return new ASTPrintXMLAction();
case ASTView: return new ASTViewAction();
case DumpRawTokens: return new DumpRawTokensAction();
case DumpRecordLayouts: return new DumpRecordAction();
case DumpTokens: return new DumpTokensAction();
case EmitAssembly: return new EmitAssemblyAction();
case EmitBC: return new EmitBCAction();
case EmitHTML: return new HTMLPrintAction();
case EmitLLVM: return new EmitLLVMAction();
case EmitLLVMOnly: return new EmitLLVMOnlyAction();
case FixIt: return new FixItAction();
case GeneratePCH: return new GeneratePCHAction();
case GeneratePTH: return new GeneratePTHAction();
case InheritanceView: return new InheritanceViewAction();
case ParseNoop: return new ParseOnlyAction();
case ParsePrintCallbacks: return new PrintParseAction();
case ParseSyntaxOnly: return new SyntaxOnlyAction();
case PrintDeclContext: return new DeclContextPrintAction();
case PrintPreprocessedInput: return new PrintPreprocessedAction();
case RewriteBlocks: return new RewriteBlocksAction();
case RewriteMacros: return new RewriteMacrosAction();
case RewriteObjC: return new RewriteObjCAction();
case RewriteTest: return new RewriteTestAction();
case RunAnalysis: return new AnalysisAction();
case RunPreprocessorOnly: return new PreprocessOnlyAction();
}
case RewriteObjC:
return CreateObjCRewriter(InFile,
CI.createDefaultOutputFile(true, InFile, "cpp"),
PP.getDiagnostics(), PP.getLangOptions(),
CI.getDiagnosticOpts().NoRewriteMacros);
case RewriteBlocks:
return CreateBlockRewriter(InFile, PP.getDiagnostics(),
PP.getLangOptions());
case FixIt:
return new ASTConsumer();
case ParseSyntaxOnly:
return new ASTConsumer();
case PrintDeclContext:
return CreateDeclContextPrinter();
}
}
/// ProcessInputFile - Process a single input file with the specified state.
static void ProcessInputFile(CompilerInstance &CI, const std::string &InFile,
ProgActions PA) {
Preprocessor &PP = CI.getPreprocessor();
const FrontendOptions &FEOpts = CI.getFrontendOpts();
llvm::OwningPtr<ASTConsumer> Consumer;
llvm::OwningPtr<FixItRewriter> FixItRewrite;
bool CompleteTranslationUnit = true;
switch (PA) {
default:
Consumer.reset(CreateConsumerAction(CI, PP, InFile, PA));
if (!Consumer.get()) {
PP.getDiagnostics().Report(diag::err_fe_invalid_ast_action);
return;
}
break;
case EmitHTML:
Consumer.reset(CreateHTMLPrinter(CI.createDefaultOutputFile(false, InFile),
PP));
break;
case RunAnalysis:
Consumer.reset(CreateAnalysisConsumer(PP, FEOpts.OutputFile,
CI.getAnalyzerOpts()));
break;
case GeneratePCH: {
const std::string &Sysroot = CI.getHeaderSearchOpts().Sysroot;
bool Relocatable = FEOpts.RelocatablePCH;
if (Relocatable && Sysroot.empty()) {
PP.Diag(SourceLocation(), diag::err_relocatable_without_without_isysroot);
Relocatable = false;
}
llvm::raw_ostream *OS = CI.createDefaultOutputFile(true, InFile);
if (Relocatable)
Consumer.reset(CreatePCHGenerator(PP, OS, Sysroot.c_str()));
else
Consumer.reset(CreatePCHGenerator(PP, OS));
CompleteTranslationUnit = false;
break;
}
// Do any necessary set up for non-consumer actions.
case DumpRawTokens:
case DumpTokens:
case RunPreprocessorOnly:
case ParseNoop:
case GeneratePTH:
case PrintPreprocessedInput:
case ParsePrintCallbacks:
case RewriteMacros:
case RewriteTest:
break; // No setup.
}
// Check if we want a fix-it rewriter.
if (PA == FixIt) {
FixItRewrite.reset(new FixItRewriter(PP.getDiagnostics(),
PP.getSourceManager(),
PP.getLangOptions()));
if (!AddFixItLocations(CI, *FixItRewrite))
return;
}
if (Consumer) {
// Create the ASTContext.
CI.createASTContext();
// Create the external AST source when using PCH.
const std::string &ImplicitPCHInclude =
CI.getPreprocessorOpts().getImplicitPCHInclude();
if (!ImplicitPCHInclude.empty()) {
CI.createPCHExternalASTSource(ImplicitPCHInclude);
if (!CI.getASTContext().getExternalSource())
return;
}
}
// Initialize builtin info as long as we aren't using an external AST
// source.
if (!CI.hasASTContext() || !CI.getASTContext().getExternalSource())
PP.getBuiltinInfo().InitializeBuiltins(PP.getIdentifierTable(),
PP.getLangOptions().NoBuiltin);
// Initialize the main file entry. This needs to be delayed until after PCH
// has loaded.
if (!CI.InitializeSourceManager(InFile))
return;
if (Consumer) {
// FIXME: Move the truncation aspect of this into Sema.
if (!FEOpts.CodeCompletionAt.FileName.empty())
CI.createCodeCompletionConsumer();
// Run the AST consumer action.
CodeCompleteConsumer *CompletionConsumer =
CI.hasCodeCompletionConsumer() ? &CI.getCodeCompletionConsumer() : 0;
ParseAST(PP, Consumer.get(), CI.getASTContext(), FEOpts.ShowStats,
CompleteTranslationUnit, CompletionConsumer);
} else {
// Run the preprocessor actions.
llvm::TimeRegion Timer(ClangFrontendTimer);
switch (PA) {
default:
assert(0 && "unexpected program action");
case DumpRawTokens: {
SourceManager &SM = PP.getSourceManager();
// Start lexing the specified input file.
Lexer RawLex(SM.getMainFileID(), SM, PP.getLangOptions());
RawLex.SetKeepWhitespaceMode(true);
Token RawTok;
RawLex.LexFromRawLexer(RawTok);
while (RawTok.isNot(tok::eof)) {
PP.DumpToken(RawTok, true);
fprintf(stderr, "\n");
RawLex.LexFromRawLexer(RawTok);
}
break;
}
case DumpTokens: {
Token Tok;
// Start preprocessing the specified input file.
PP.EnterMainSourceFile();
do {
PP.Lex(Tok);
PP.DumpToken(Tok, true);
fprintf(stderr, "\n");
} while (Tok.isNot(tok::eof));
break;
}
case GeneratePTH:
if (FEOpts.OutputFile.empty() || FEOpts.OutputFile == "-") {
// FIXME: Don't fail this way.
// FIXME: Verify that we can actually seek in the given file.
llvm::errs() << "ERROR: PTH requires an seekable file for output!\n";
::exit(1);
}
CacheTokens(PP, CI.createDefaultOutputFile(true, InFile));
break;
case ParseNoop:
ParseFile(PP, new MinimalAction(PP));
break;
case ParsePrintCallbacks: {
llvm::raw_ostream *OS = CI.createDefaultOutputFile(false, InFile);
ParseFile(PP, CreatePrintParserActionsAction(PP, OS));
break;
}
case PrintPreprocessedInput:
DoPrintPreprocessedInput(PP, CI.createDefaultOutputFile(false, InFile),
CI.getPreprocessorOutputOpts());
break;
case RewriteMacros:
RewriteMacrosInInput(PP, CI.createDefaultOutputFile(true, InFile));
break;
case RewriteTest:
DoRewriteTest(PP, CI.createDefaultOutputFile(false, InFile));
break;
case RunPreprocessorOnly: { // Just lex as fast as we can, no output.
Token Tok;
// Start parsing the specified input file.
PP.EnterMainSourceFile();
do {
PP.Lex(Tok);
} while (Tok.isNot(tok::eof));
break;
}
}
}
if (FixItRewrite)
FixItRewrite->WriteFixedFile(InFile, FEOpts.OutputFile);
// Release the consumer and the AST, in that order since the consumer may
// perform actions in its destructor which require the context.
if (FEOpts.DisableFree) {
Consumer.take();
CI.takeASTContext();
} else {
Consumer.reset();
CI.setASTContext(0);
}
if (FEOpts.ShowStats) {
fprintf(stderr, "\nSTATISTICS FOR '%s':\n", InFile.c_str());
PP.PrintStats();
PP.getIdentifierTable().PrintStats();
PP.getHeaderSearchInfo().PrintStats();
PP.getSourceManager().PrintStats();
fprintf(stderr, "\n");
}
// Cleanup the output streams, and erase the output files if we encountered an
// error.
CI.ClearOutputFiles(/*EraseFiles=*/PP.getDiagnostics().getNumErrors());
}
/// ProcessASTInputFile - Process a single AST input file with the specified
/// state.
static void ProcessASTInputFile(CompilerInstance &CI, const std::string &InFile,
ProgActions PA) {
std::string Error;
llvm::OwningPtr<ASTUnit> AST(ASTUnit::LoadFromPCHFile(InFile, &Error));
if (!AST) {
CI.getDiagnostics().Report(diag::err_fe_invalid_ast_file) << Error;
return;
}
Preprocessor &PP = AST->getPreprocessor();
llvm::OwningPtr<ASTConsumer> Consumer(CreateConsumerAction(CI, PP,
InFile, PA));
if (!Consumer.get()) {
CI.getDiagnostics().Report(diag::err_fe_invalid_ast_action);
return;
}
// Set the main file ID to an empty file.
//
// FIXME: We probably shouldn't need this, but for now this is the simplest
// way to reuse the logic in ParseAST.
const char *EmptyStr = "";
llvm::MemoryBuffer *SB =
llvm::MemoryBuffer::getMemBuffer(EmptyStr, EmptyStr, "<dummy input>");
AST->getSourceManager().createMainFileIDForMemBuffer(SB);
// Stream the input AST to the consumer.
CI.getDiagnostics().getClient()->BeginSourceFile(PP.getLangOptions(), &PP);
ParseAST(PP, Consumer.get(), AST->getASTContext(),
CI.getFrontendOpts().ShowStats);
CI.getDiagnostics().getClient()->EndSourceFile();
// Release the consumer and the AST, in that order since the consumer may
// perform actions in its destructor which require the context.
if (CI.getFrontendOpts().DisableFree) {
Consumer.take();
AST.take();
} else {
Consumer.reset();
AST.reset();
}
// Cleanup the output streams, and erase the output files if we encountered an
// error.
CI.ClearOutputFiles(/*EraseFiles=*/PP.getDiagnostics().getNumErrors());
}
static void LLVMErrorHandler(void *UserData, const std::string &Message) {
@@ -701,33 +348,35 @@ int main(int argc, char **argv) {
if (!Clang.getFrontendOpts().FixItLocations.empty())
ProgAction = FixIt;
// Create the source manager.
Clang.createSourceManager();
// Create a file manager object to provide access to and cache the filesystem.
Clang.createFileManager();
for (unsigned i = 0, e = Clang.getFrontendOpts().Inputs.size(); i != e; ++i) {
const std::string &InFile = Clang.getFrontendOpts().Inputs[i].second;
// AST inputs are handled specially.
if (IsAST) {
ProcessASTInputFile(Clang, InFile, ProgAction);
continue;
// If we aren't using an AST file, setup the file and source managers and
// the preprocessor.
if (!IsAST) {
if (!i) {
// Create a file manager object to provide access to and cache the
// filesystem.
Clang.createFileManager();
// Create the source manager.
Clang.createSourceManager();
} else {
// Reset the ID tables if we are reusing the SourceManager.
Clang.getSourceManager().clearIDTables();
}
// Create the preprocessor.
Clang.createPreprocessor();
}
// Reset the ID tables if we are reusing the SourceManager.
if (i)
Clang.getSourceManager().clearIDTables();
// Create the preprocessor.
Clang.createPreprocessor();
// Process the source file.
Clang.getDiagnostics().getClient()->BeginSourceFile(Clang.getLangOpts(),
&Clang.getPreprocessor());
ProcessInputFile(Clang, InFile, ProgAction);
Clang.getDiagnostics().getClient()->EndSourceFile();
llvm::OwningPtr<FrontendAction> Act(CreateFrontendAction(ProgAction));
assert(Act && "Invalid program action!");
Act->setCurrentTimer(ClangFrontendTimer);
if (Act->BeginSourceFile(Clang, InFile, IsAST)) {
Act->Execute();
Act->EndSourceFile();
}
}
if (Clang.getDiagnosticOpts().ShowCarets)