diff options
Diffstat (limited to 'Driver')
-rw-r--r-- | Driver/ASTStreamers.cpp | 109 | ||||
-rw-r--r-- | Driver/ASTStreamers.h | 30 | ||||
-rw-r--r-- | Driver/DiagChecker.cpp | 230 | ||||
-rw-r--r-- | Driver/LLVMCodegen.cpp | 68 | ||||
-rw-r--r-- | Driver/Makefile | 8 | ||||
-rw-r--r-- | Driver/PPCBuiltins.def | 24 | ||||
-rw-r--r-- | Driver/PrintParserCallbacks.cpp | 55 | ||||
-rw-r--r-- | Driver/PrintPreprocessedOutput.cpp | 436 | ||||
-rw-r--r-- | Driver/Targets.cpp | 443 | ||||
-rw-r--r-- | Driver/TextDiagnosticBuffer.cpp | 38 | ||||
-rw-r--r-- | Driver/TextDiagnosticBuffer.h | 51 | ||||
-rw-r--r-- | Driver/TextDiagnosticPrinter.cpp | 225 | ||||
-rw-r--r-- | Driver/TextDiagnosticPrinter.h | 46 | ||||
-rw-r--r-- | Driver/TextDiagnostics.cpp | 60 | ||||
-rw-r--r-- | Driver/TextDiagnostics.h | 53 | ||||
-rw-r--r-- | Driver/X86Builtins.def | 420 | ||||
-rw-r--r-- | Driver/clang.cpp | 914 | ||||
-rw-r--r-- | Driver/clang.h | 45 |
18 files changed, 3255 insertions, 0 deletions
diff --git a/Driver/ASTStreamers.cpp b/Driver/ASTStreamers.cpp new file mode 100644 index 0000000000..19e12bd69e --- /dev/null +++ b/Driver/ASTStreamers.cpp @@ -0,0 +1,109 @@ +//===--- ASTStreamers.cpp - ASTStreamer Drivers ---------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Bill Wendling and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// ASTStreamer drivers. +// +//===----------------------------------------------------------------------===// + +#include "ASTStreamers.h" +#include "clang/AST/AST.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Sema/ASTStreamer.h" + +void clang::BuildASTs(Preprocessor &PP, unsigned MainFileID, bool Stats) { + // collect global stats on Decls/Stmts (until we have a module streamer) + if (Stats) { + Decl::CollectingStats(true); + Stmt::CollectingStats(true); + } + + ASTContext Context(PP.getTargetInfo(), PP.getIdentifierTable()); + ASTStreamerTy *Streamer = ASTStreamer_Init(PP, Context, MainFileID); + + while (ASTStreamer_ReadTopLevelDecl(Streamer)) + /* keep reading */; + + if (Stats) { + fprintf(stderr, "\nSTATISTICS:\n"); + ASTStreamer_PrintStats(Streamer); + Context.PrintStats(); + Decl::PrintStats(); + Stmt::PrintStats(); + } + + ASTStreamer_Terminate(Streamer); +} + +void clang::PrintFunctionDecl(FunctionDecl *FD) { + bool HasBody = FD->getBody(); + + std::string Proto = FD->getName(); + FunctionType *AFT = cast<FunctionType>(FD->getType()); + + if (FunctionTypeProto *FT = dyn_cast<FunctionTypeProto>(AFT)) { + Proto += "("; + for (unsigned i = 0, e = FD->getNumParams(); i != e; ++i) { + if (i) Proto += ", "; + std::string ParamStr; + if (HasBody) ParamStr = FD->getParamDecl(i)->getName(); + + FT->getArgType(i).getAsStringInternal(ParamStr); + Proto += ParamStr; + } + + if (FT->isVariadic()) { + if (FD->getNumParams()) Proto += ", "; + Proto += "..."; + } + Proto += ")"; + } else { + assert(isa<FunctionTypeNoProto>(AFT)); + Proto += "()"; + } + + AFT->getResultType().getAsStringInternal(Proto); + fprintf(stderr, "\n%s", Proto.c_str()); + + if (FD->getBody()) { + fprintf(stderr, " "); + FD->getBody()->dump(); + fprintf(stderr, "\n"); + } else { + fprintf(stderr, ";\n"); + } +} + +void clang::PrintTypeDefDecl(TypedefDecl *TD) { + std::string S = TD->getName(); + TD->getUnderlyingType().getAsStringInternal(S); + fprintf(stderr, "typedef %s;\n", S.c_str()); +} + +void clang::PrintASTs(Preprocessor &PP, unsigned MainFileID, bool Stats) { + ASTContext Context(PP.getTargetInfo(), PP.getIdentifierTable()); + ASTStreamerTy *Streamer = ASTStreamer_Init(PP, Context, MainFileID); + + while (Decl *D = ASTStreamer_ReadTopLevelDecl(Streamer)) { + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + PrintFunctionDecl(FD); + } else if (TypedefDecl *TD = dyn_cast<TypedefDecl>(D)) { + PrintTypeDefDecl(TD); + } else { + fprintf(stderr, "Read top-level variable decl: '%s'\n", D->getName()); + } + } + + if (Stats) { + fprintf(stderr, "\nSTATISTICS:\n"); + ASTStreamer_PrintStats(Streamer); + Context.PrintStats(); + } + + ASTStreamer_Terminate(Streamer); +} diff --git a/Driver/ASTStreamers.h b/Driver/ASTStreamers.h new file mode 100644 index 0000000000..2cce217ce6 --- /dev/null +++ b/Driver/ASTStreamers.h @@ -0,0 +1,30 @@ +//===--- ASTStreamers.h - ASTStreamer Drivers -------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Bill Wendling and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// AST Streamers. +// +//===----------------------------------------------------------------------===// + +#ifndef DRIVER_ASTSTREAMERS_H_ +#define DRIVER_ASTSTREAMERS_H_ + +namespace clang { + +class Preprocessor; +class FunctionDecl; +class TypedefDecl; + +void BuildASTs(Preprocessor &PP, unsigned MainFileID, bool Stats); +void PrintASTs(Preprocessor &PP, unsigned MainFileID, bool Stats); +void PrintFunctionDecl(FunctionDecl *FD); +void PrintTypeDefDecl(TypedefDecl *TD); + +} // end clang namespace + +#endif diff --git a/Driver/DiagChecker.cpp b/Driver/DiagChecker.cpp new file mode 100644 index 0000000000..76b0526d4e --- /dev/null +++ b/Driver/DiagChecker.cpp @@ -0,0 +1,230 @@ +//===--- DiagChecker.cpp - Diagnostic Checking Functions ------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Bill Wendling and is distributed under the +// University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// Process the input files and check that the diagnostic messages are expected. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "ASTStreamers.h" +#include "TextDiagnosticBuffer.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Preprocessor.h" +using namespace clang; + +typedef TextDiagnosticBuffer::DiagList DiagList; +typedef TextDiagnosticBuffer::const_iterator const_diag_iterator; + +// USING THE DIAGNOSTIC CHECKER: +// +// Indicating that a line expects an error or a warning is simple. Put a comment +// on the line that has the diagnostic, use "expected-{error,warning}" to tag +// if it's an expected error or warning, and place the expected text between {{ +// and }} markers. The full text doesn't have to be included, only enough to +// ensure that the correct diagnostic was emitted. +// +// Here's an example: +// +// int A = B; // expected-error {{use of undeclared identifier 'B'}} +// +// You can place as many diagnostics on one line as you wish. To make the code +// more readable, you can use slash-newline to separate out the diagnostics. + +static const char * const ExpectedErrStr = "expected-error"; +static const char * const ExpectedWarnStr = "expected-warning"; + +/// FindDiagnostics - Go through the comment and see if it indicates expected +/// diagnostics. If so, then put them in a diagnostic list. +/// +static void FindDiagnostics(const std::string &Comment, + DiagList &ExpectedDiags, + SourceManager &SourceMgr, + SourceLocation Pos, + const char * const ExpectedStr) { + // Find all expected diagnostics + typedef std::string::size_type size_type; + size_type ColNo = std::string::npos; + + for (;;) { + ColNo = Comment.find(ExpectedStr, ColNo); + if (ColNo == std::string::npos) break; + + size_type OpenDiag = Comment.find_first_of("{{", ColNo); + + if (OpenDiag == std::string::npos) { + fprintf(stderr, + "oops:%d: Cannot find beginning of expected error string\n", + SourceMgr.getLineNumber(Pos)); + break; + } + + OpenDiag += 2; + size_type CloseDiag = Comment.find_first_of("}}", OpenDiag); + + if (CloseDiag == std::string::npos) { + fprintf(stderr, + "oops:%d: Cannot find end of expected error string\n", + SourceMgr.getLineNumber(Pos)); + break; + } + + std::string Msg(Comment.substr(OpenDiag, CloseDiag - OpenDiag)); + ExpectedDiags.push_back(std::make_pair(Pos, Msg)); + ColNo = CloseDiag + 2; + } +} + +/// FindExpectedDiags - Lex the file to finds all of the expected errors and +/// warnings. +static void FindExpectedDiags(Preprocessor &PP, unsigned MainFileID, + DiagList &ExpectedErrors, + DiagList &ExpectedWarnings) { + // Return comments as tokens, this is how we find expected diagnostics. + PP.SetCommentRetentionState(true, true); + + // Enter the cave. + PP.EnterSourceFile(MainFileID, 0, true); + + LexerToken Tok; + do { + PP.Lex(Tok); + + if (Tok.getKind() == tok::comment) { + std::string Comment = PP.getSpelling(Tok); + + // Find all expected errors + FindDiagnostics(Comment, ExpectedErrors,PP.getSourceManager(), + Tok.getLocation(), ExpectedErrStr); + + // Find all expected warnings + FindDiagnostics(Comment, ExpectedWarnings, PP.getSourceManager(), + Tok.getLocation(), ExpectedWarnStr); + } + } while (Tok.getKind() != tok::eof); + + PP.SetCommentRetentionState(false, false); +} + +/// PrintProblem - This takes a diagnostic map of the delta between expected and +/// seen diagnostics. If there's anything in it, then something unexpected +/// happened. Print the map out in a nice format and return "true". If the map +/// is empty and we're not going to print things, then return "false". +/// +static bool PrintProblem(SourceManager &SourceMgr, + const_diag_iterator diag_begin, + const_diag_iterator diag_end, + const char *Msg) { + if (diag_begin == diag_end) return false; + + fprintf(stderr, "%s\n", Msg); + + for (const_diag_iterator I = diag_begin, E = diag_end; I != E; ++I) + fprintf(stderr, " Line %d: %s\n", + SourceMgr.getLineNumber(I->first), + I->second.c_str()); + + return true; +} + +/// CompareDiagLists - Compare two diangnostic lists and return the difference +/// between them. +/// +static bool CompareDiagLists(SourceManager &SourceMgr, + const_diag_iterator d1_begin, + const_diag_iterator d1_end, + const_diag_iterator d2_begin, + const_diag_iterator d2_end, + const char *Msg) { + DiagList DiffList; + + for (const_diag_iterator I = d1_begin, E = d1_end; I != E; ++I) { + unsigned LineNo1 = SourceMgr.getLineNumber(I->first); + const std::string &Diag1 = I->second; + bool Found = false; + + for (const_diag_iterator II = d2_begin, IE = d2_end; II != IE; ++II) { + unsigned LineNo2 = SourceMgr.getLineNumber(II->first); + if (LineNo1 != LineNo2) continue; + + const std::string &Diag2 = II->second; + if (Diag2.find(Diag1) != std::string::npos || + Diag1.find(Diag2) != std::string::npos) { + Found = true; + break; + } + } + + if (!Found) + DiffList.push_back(std::make_pair(I->first, Diag1)); + } + + return PrintProblem(SourceMgr, DiffList.begin(), DiffList.end(), Msg); +} + +/// CheckResults - This compares the expected results to those that +/// were actually reported. It emits any discrepencies. Return "true" if there +/// were problems. Return "false" otherwise. +/// +static bool CheckResults(Preprocessor &PP, + const DiagList &ExpectedErrors, + const DiagList &ExpectedWarnings) { + const TextDiagnosticBuffer &Diags = + static_cast<const TextDiagnosticBuffer&>(PP.getDiagnostics().getClient()); + SourceManager &SourceMgr = PP.getSourceManager(); + + // We want to capture the delta between what was expected and what was + // seen. + // + // Expected \ Seen - set expected but not seen + // Seen \ Expected - set seen but not expected + bool HadProblem = false; + + // See if there were errors that were expected but not seen. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedErrors.begin(), ExpectedErrors.end(), + Diags.err_begin(), Diags.err_end(), + "Errors expected but not seen:"); + + // See if there were errors that were seen but not expected. + HadProblem |= CompareDiagLists(SourceMgr, + Diags.err_begin(), Diags.err_end(), + ExpectedErrors.begin(), ExpectedErrors.end(), + "Errors seen but not expected:"); + + // See if there were warnings that were expected but not seen. + HadProblem |= CompareDiagLists(SourceMgr, + ExpectedWarnings.begin(), + ExpectedWarnings.end(), + Diags.warn_begin(), Diags.warn_end(), + "Warnings expected but not seen:"); + + // See if there were warnings that were seen but not expected. + HadProblem |= CompareDiagLists(SourceMgr, + Diags.warn_begin(), Diags.warn_end(), + ExpectedWarnings.begin(), + ExpectedWarnings.end(), + "Warnings seen but not expected:"); + + return HadProblem; +} + +/// CheckDiagnostics - Implement the -parse-ast-check diagnostic verifier. +bool clang::CheckDiagnostics(Preprocessor &PP, unsigned MainFileID) { + // Gather the set of expected diagnostics. + DiagList ExpectedErrors, ExpectedWarnings; + FindExpectedDiags(PP, MainFileID, ExpectedErrors, ExpectedWarnings); + + // Parse the specified input file. + BuildASTs(PP, MainFileID, false); + + // Check that the expected diagnostics occurred. + return CheckResults(PP, ExpectedErrors, ExpectedWarnings); +} + + diff --git a/Driver/LLVMCodegen.cpp b/Driver/LLVMCodegen.cpp new file mode 100644 index 0000000000..e593b66cfd --- /dev/null +++ b/Driver/LLVMCodegen.cpp @@ -0,0 +1,68 @@ +//===--- LLVMCodegen.cpp - Emit LLVM Code from ASTs -----------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This builds an AST and converts it to LLVM Code. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/CodeGen/ModuleBuilder.h" +#include "clang/Sema/ASTStreamer.h" +#include "clang/AST/AST.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Basic/Diagnostic.h" +#include "llvm/Module.h" +#include <iostream> +using namespace clang; + +//===----------------------------------------------------------------------===// +// LLVM Emission +//===----------------------------------------------------------------------===// + +void clang::EmitLLVMFromASTs(Preprocessor &PP, unsigned MainFileID, + bool PrintStats) { + Diagnostic &Diags = PP.getDiagnostics(); + // Create the streamer to read the file. + ASTContext Context(PP.getTargetInfo(), PP.getIdentifierTable()); + ASTStreamerTy *Streamer = ASTStreamer_Init(PP, Context, MainFileID); + + // Create the module to codegen into. + llvm::Module M("foo"); + + CodeGen::BuilderTy *Builder = CodeGen::Init(Context, M); + + while (Decl *D = ASTStreamer_ReadTopLevelDecl(Streamer)) { + // If an error occurred, stop code generation, but continue parsing and + // semantic analysis (to ensure all warnings and errors are emitted). + if (Diags.hasErrorOccurred()) + continue; + + if (FunctionDecl *FD = dyn_cast<FunctionDecl>(D)) { + CodeGen::CodeGenFunction(Builder, FD); + } else if (isa<TypedefDecl>(D)) { + std::cerr << "Read top-level typedef decl: '" << D->getName() << "'\n"; + } else { + std::cerr << "Read top-level variable decl: '" << D->getName() << "'\n"; + } + } + + if (PrintStats) { + std::cerr << "\nSTATISTICS:\n"; + CodeGen::PrintStats(Builder); + ASTStreamer_PrintStats(Streamer); + Context.PrintStats(); + } + + CodeGen::Terminate(Builder); + ASTStreamer_Terminate(Streamer); + + // Print the generated code. + M.print(std::cout); +} + diff --git a/Driver/Makefile b/Driver/Makefile new file mode 100644 index 0000000000..4c9db0dc2d --- /dev/null +++ b/Driver/Makefile @@ -0,0 +1,8 @@ +LEVEL = ../../.. +CPPFLAGS += -I$(PROJ_SRC_DIR)/../include +CXXFLAGS = -fno-rtti + +TOOLNAME = clang +USEDLIBS = clangCodeGen.a clangSEMA.a clangAST.a clangParse.a clangLex.a clangBasic.a LLVMCore.a LLVMSupport.a LLVMSystem.a + +include $(LEVEL)/Makefile.common diff --git a/Driver/PPCBuiltins.def b/Driver/PPCBuiltins.def new file mode 100644 index 0000000000..6aed2caa4d --- /dev/null +++ b/Driver/PPCBuiltins.def @@ -0,0 +1,24 @@ +//===--- PPCBuiltins.def - PowerPC Builtin function database ----*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines the PowerPC-specific builtin function database. Users of +// this file must define the BUILTIN macro to make use of this information. +// +//===----------------------------------------------------------------------===// + +// FIXME: this needs to be the full list supported by GCC. Right now, I'm just +// adding stuff on demand. + +// The format of this database matches clang/AST/Builtins.def. + +// This is just a placeholder, the types and attributes are wrong. +BUILTIN(__builtin_altivec_abs_v4sf , "ii" , "nc") +// FIXME: Obviously incomplete. + +#undef BUILTIN diff --git a/Driver/PrintParserCallbacks.cpp b/Driver/PrintParserCallbacks.cpp new file mode 100644 index 0000000000..3730d19a7d --- /dev/null +++ b/Driver/PrintParserCallbacks.cpp @@ -0,0 +1,55 @@ +//===--- PrintParserActions.cpp - Implement -parse-print-callbacks mode ---===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code simply runs the preprocessor on the input file and prints out the +// result. This is the traditional behavior of the -E option. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Lex/IdentifierTable.h" +#include "clang/Parse/Action.h" +#include "clang/Parse/DeclSpec.h" +#include <iostream> +using namespace clang; + +namespace { + class ParserPrintActions : public MinimalAction { + + /// ParseDeclarator - This callback is invoked when a declarator is parsed + /// and 'Init' specifies the initializer if any. This is for things like: + /// "int X = 4" or "typedef int foo". + virtual DeclTy *ParseDeclarator(Scope *S, Declarator &D, ExprTy *Init, + DeclTy *LastInGroup) { + std::cout << "ParseDeclarator "; + if (IdentifierInfo *II = D.getIdentifier()) { + std::cout << "'" << II->getName() << "'"; + } else { + std::cout << "<anon>"; + } + std::cout << "\n"; + + // Pass up to EmptyActions so that the symbol table is maintained right. + return MinimalAction::ParseDeclarator(S, D, Init, LastInGroup); + } + + /// PopScope - This callback is called immediately before the specified scope + /// is popped and deleted. + virtual void PopScope(SourceLocation Loc, Scope *S) { + std::cout << "PopScope\n"; + + // Pass up to EmptyActions so that the symbol table is maintained right. + MinimalAction::PopScope(Loc, S); + } + }; +} + +MinimalAction *clang::CreatePrintParserActionsAction() { + return new ParserPrintActions(); +} diff --git a/Driver/PrintPreprocessedOutput.cpp b/Driver/PrintPreprocessedOutput.cpp new file mode 100644 index 0000000000..4a4f6783da --- /dev/null +++ b/Driver/PrintPreprocessedOutput.cpp @@ -0,0 +1,436 @@ +//===--- PrintPreprocessedOutput.cpp - Implement the -E mode --------------===// +// +// The LLVM Compiler Infrastructure +// +// This file was developed by Chris Lattner and is distributed under +// the University of Illinois Open Source License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This code simply runs the preprocessor on the input file and prints out the +// result. This is the traditional behavior of the -E option. +// +//===----------------------------------------------------------------------===// + +#include "clang.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/Pragma.h" +#include "clang/Basic/SourceManager.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/ADT/StringExtras.h" +#include "llvm/Config/config.h" +#include <cstdio> +using namespace clang; + +//===----------------------------------------------------------------------===// +// Simple buffered I/O +//===----------------------------------------------------------------------===// +// +// Empirically, iostream is over 30% slower than stdio for this workload, and +// stdio itself isn't very well suited. The problem with stdio is use of +// putchar_unlocked. We have many newline characters that need to be emitted, +// but stdio needs to do extra checks to handle line buffering mode. These +// extra checks make putchar_unlocked fall off its inlined code path, hitting +// slow system code. In practice, using 'write' directly makes 'clang -E -P' +// about 10% faster than using the stdio path on darwin. + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#else +#define USE_STDIO 1 +#endif + +static char *OutBufStart = 0, *OutBufEnd, *OutBufCur; + +/// InitOutputBuffer - Initialize our output buffer. +/// +static void InitOutputBuffer() { +#ifndef USE_STDIO + OutBufStart = new char[64*1024]; + OutBufEnd = OutBufStart+64*1024; + OutBufCur = OutBufStart; +#endif +} + +/// FlushBuffer - Write the accumulated bytes to the output stream. +/// +static void FlushBuffer() { +#ifndef USE_STDIO + write(STDOUT_FILENO, OutBufStart, OutBufCur-OutBufStart); + OutBufCur = OutBufStart; +#endif +} + +/// CleanupOutputBuffer - Finish up output. +/// +static void CleanupOutputBuffer() { +#ifndef USE_STDIO + FlushBuffer(); + delete [] OutBufStart; +#endif +} + +static void OutputChar(char c) { +#ifdef USE_STDIO + putchar_unlocked(c); +#else + if (OutBufCur >= OutBufEnd) + FlushBuffer(); + *OutBufCur++ = c; +#endif +} + +static void OutputString(const char *Ptr, unsigned Size) { +#ifdef USE_STDIO + fwrite(Ptr, Size, 1, stdout); +#else + if (OutBufCur+Size >= OutBufEnd) + FlushBuffer(); + memcpy(OutBufCur, Ptr, Size); + OutBufCur += Size; +#endif +} + + +//===----------------------------------------------------------------------===// +// Preprocessed token printer +//===----------------------------------------------------------------------===// + +static llvm::cl::opt<bool> +DisableLineMarkers("P", llvm::cl::desc("Disable linemarker output in -E mode")); +static llvm::cl::opt<bool> +EnableCommentOutput("C", llvm::cl::desc("Enable comment output in -E mode")); +static llvm::cl::opt<bool> +EnableMacroCommentOutput("CC", + llvm::cl::desc("Enable comment output in -E mode, " + "even from macro expansions")); + +namespace { +class PrintPPOutputPPCallbacks : public PPCallbacks { + Preprocessor &PP; + unsigned CurLine; + std::string CurFilename; + bool EmittedTokensOnThisLine; + DirectoryLookup::DirType FileType; +public: + PrintPPOutputPPCallbacks(Preprocessor &pp) : PP(pp) { + CurLine = 0; + CurFilename = "\"<uninit>\""; + EmittedTokensOnThisLine = false; + FileType = DirectoryLookup::NormalHeaderDir; + } + + void SetEmittedTokensOnThisLine() { EmittedTokensOnThisLine = true; } + + virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason, + DirectoryLookup::DirType FileType); + virtual void Ident(SourceLocation Loc, const std::string &str); + + + void HandleFirstTokOnLine(LexerToken &Tok); + void MoveToLine(SourceLocation Loc); + bool AvoidConcat(const LexerToken &PrevTok, const LexerToken &Tok); +}; +} + +/// MoveToLine - Move the output to the source line specified by the location +/// object. We can do this by emitting some number of \n's, or be emitting a +/// #line directive. +void PrintPPOutputPPCallbacks::MoveToLine(SourceLocation Loc) { + if (DisableLineMarkers) { + if (EmittedTokensOnThisLine) { + OutputChar('\n'); + EmittedTokensOnThisLine = false; + } + return; + } + + unsigned LineNo = PP.getSourceManager().getLineNumber(Loc); + + // If this line is "close enough" to the original line, just print newlines, + // otherwise print a #line directive. + if (LineNo-CurLine < 8) { + unsigned Line = CurLine; + for (; Line != LineNo; ++Line) + OutputChar('\n'); + CurLine = Line; + } else { + if (EmittedTokensOnThisLine) { + OutputChar('\n'); + EmittedTokensOnThisLine = false; + } + + CurLine = LineNo; + + OutputChar('#'); + OutputChar(' '); + std::string Num = llvm::utostr_32(LineNo); + OutputString(&Num[0], Num.size()); + OutputChar(' '); + OutputString(&CurFilename[0], CurFilename.size()); + + if (FileType == DirectoryLookup::SystemHeaderDir) + OutputString(" 3", 2); + else if (FileType == DirectoryLookup::ExternCSystemHeaderDir) + OutputString(" 3 4", 4); + OutputChar('\n'); + } +} + + +/// FileChanged - Whenever the preprocessor enters or exits a #include file +/// it invokes this handler. Update our conception of the current source +/// position. +void PrintPPOutputPPCallbacks::FileChanged(SourceLocation Loc, + FileChangeReason Reason, + DirectoryLookup::DirType FileType) { + if (DisableLineMarkers) return; + + // Unless we are exiting a #include, make sure to skip ahead to the line the + // #include directive was at. + SourceManager &SourceMgr = PP.getSourceManager(); + if (Reason == PPCallbacks::EnterFile) { + MoveToLine(SourceMgr.getIncludeLoc(Loc.getFileID())); + } else if (Reason == PPCallbacks::SystemHeaderPragma) { + MoveToLine(Loc); + + // TODO GCC emits the # directive for this directive on the line AFTER the + // directive and emits a bunch of spaces that aren't needed. Emulate this + // strange behavior. + } + + CurLine = SourceMgr.getLineNumber(Loc); + CurFilename = '"' + Lexer::Stringify(SourceMgr.getSourceName(Loc)) + '"'; + FileType = FileType; + + if (EmittedTokensOnThisLine) { + OutputChar('\n'); + EmittedTokensOnThisLine = false; + } + + if (DisableLineMarkers) return; + + OutputChar('#'); + OutputChar(' '); + std::string Num = llvm::utostr_32(CurLine); + OutputString(&Num[0], Num.size()); + OutputChar(' '); + OutputString(&CurFilename[0], CurFilename.size()); + + switch (Reason) { + case PPCallbacks::EnterFile: + OutputString(" 1", 2); + break; + case PPCallbacks::ExitFile: + OutputString(" 2", 2); + break; + case PPCallbacks::SystemHeaderPragma: break; + case PPCallbacks::RenameFile: break; + } + + if (FileType == DirectoryLookup::SystemHeaderDir) + OutputString(" 3", 2); + else if (FileType == DirectoryLookup::ExternCSystemHeaderDir) + OutputString(" 3 4", 4); + + OutputChar('\n'); +} + +/// HandleIdent - Handle #ident directives when read by the preprocessor. +/// +void PrintPPOutputPPCallbacks::Ident(SourceLocation Loc, const std::string &S) { + MoveToLine(Loc); + + OutputString("#ident ", strlen("#ident ")); + OutputString(&S[0], S.size()); + EmittedTokensOnThisLine = true; +} + +/// HandleFirstTokOnLine - When emitting a preprocessed file in -E mode, this +/// is called for the first token on each new line. +void PrintPPOutputPPCallbacks::HandleFirstTokOnLine(LexerToken &Tok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + MoveToLine(Tok.getLocation()); + + // Print out space characters so that the first token on a line is + // indented for easy reading. + unsigned ColNo = + PP.getSourceManager().getColumnNumber(Tok.getLocation()); + + // This hack prevents stuff like: + // #define HASH # + // HASH define foo bar + // From having the # character end up at column 1, which makes it so it + // is not handled as a #define next time through the preprocessor if in + // -fpreprocessed mode. + if (ColNo <= 1 && Tok.getKind() == tok::hash) + OutputChar(' '); + + // Otherwise, indent the appropriate number of spaces. + for (; ColNo > 1; --ColNo) + OutputChar(' '); +} + +namespace { +struct UnknownPragmaHandler : public PragmaHandler { + const char *Prefix; + PrintPPOutputPPCallbacks *Callbacks; + + UnknownPragmaHandler(const char *prefix, PrintPPOutputPPCallbacks *callbacks) + : PragmaHandler(0), Prefix(prefix), Callbacks(callbacks) {} + virtual void HandlePragma(Preprocessor &PP, LexerToken &PragmaTok) { + // Figure out what line we went to and insert the appropriate number of + // newline characters. + Callbacks->MoveToLine(PragmaTok.getLocation()); + OutputString(Prefix, strlen(Prefix)); + + // Read and print all of the pragma tokens. + while (PragmaTok.getKind() != tok::eom) { + if (PragmaTok.hasLeadingSpace()) + OutputChar(' '); + std::string TokSpell = PP.getSpelling(PragmaTok); + OutputString(&TokSpell[0], TokSpell.size()); + PP.LexUnexpandedToken(PragmaTok); + } + OutputChar('\n'); + } +}; +} // end anonymous namespace + +/// AvoidConcat - If printing PrevTok immediately followed by Tok would cause +/// the two individual tokens to be lexed as a single token, return true (which +/// causes a space to be printed between them). This allows the output of -E +/// mode to be lexed to the same token stream as lexing the input directly +/// would. +/// +/// This code must conservatively return true if it doesn't want to be 100% +/// accurate. This will cause the output to include extra space characters, but +/// the resulting output won't have incorrect concatenations going on. Examples +/// include "..", which we print with a space between, because we don't want to +/// track enough to tell "x.." from "...". +bool PrintPPOutputPPCallbacks::AvoidConcat(const LexerToken &PrevTok, + const LexerToken &Tok) { + char Buffer[256]; + + // If we haven't emitted a token on this line yet, PrevTok isn't useful to + // look at and no concatenation could happen anyway. + if (!EmittedTokensOnThisLine) + return false; + + // Basic algorithm: we look at the first character of the second token, and + // determine whether it, if appended to the first token, would form (or would + // contribute) to a larger token if concatenated. + char FirstChar; + if (IdentifierInfo *II = Tok.getIdentifierInfo()) { + // Avoid spelling identifiers, the most common form of token. + FirstChar = II->getName()[0]; + } else if (Tok.getLength() < 256) { + const char *TokPtr = Buffer; + PP.getSpelling(Tok, TokPtr); + FirstChar = TokPtr[0]; + } else { + FirstChar = PP.getSpelling(Tok)[0]; + } + + tok::TokenKind PrevKind = PrevTok.getKind(); + if (PrevTok.getIdentifierInfo()) // Language keyword or named operator. + PrevKind = tok::identifier; + + switch (PrevKind) { + default: return false; + case tok::identifier: // id+id or id+number or id+L"foo". + return isalnum(FirstChar) || FirstChar == '_'; + case tok::numeric_constant: |