From: Chris Lattner Date: Fri, 6 Feb 2009 06:45:26 +0000 (+0000) Subject: Add an implementation of -dM that follows GCC closely enough to permit X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=f73903a1ded46748e1dfda151f5d037b7b3d31f9;p=clang Add an implementation of -dM that follows GCC closely enough to permit diffing the output of: clang -dM -o - -E -x c foo.c | sort git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@63926 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/Driver/PrintPreprocessedOutput.cpp b/Driver/PrintPreprocessedOutput.cpp index 8b73c14d4d..8bd4e96ae3 100644 --- a/Driver/PrintPreprocessedOutput.cpp +++ b/Driver/PrintPreprocessedOutput.cpp @@ -13,6 +13,7 @@ //===----------------------------------------------------------------------===// #include "clang.h" +#include "clang/Lex/MacroInfo.h" #include "clang/Lex/PPCallbacks.h" #include "clang/Lex/Preprocessor.h" #include "clang/Lex/Pragma.h" @@ -39,6 +40,10 @@ static llvm::cl::opt EnableMacroCommentOutput("CC", llvm::cl::desc("Enable comment output in -E mode, " "even from macro expansions")); +static llvm::cl::opt +DumpMacros("dM", llvm::cl::desc("Print macro definitions in -E mode instead of" + " normal output")); + namespace { class PrintPPOutputPPCallbacks : public PPCallbacks { @@ -543,6 +548,49 @@ static void PrintPreprocessedTokens(Preprocessor &PP, Token &Tok, } } +/// PrintMacroDefinition - Print a macro definition in a form that will be +/// properly accepted back as a definition. +static void PrintMacroDefinition(IdentifierInfo &II, const MacroInfo &MI, + Preprocessor &PP, llvm::raw_ostream &OS) { + // Ignore computed macros like __LINE__ and friends. + if (MI.isBuiltinMacro()) return; + OS << "#define " << II.getName(); + + if (MI.isFunctionLike()) { + OS << '('; + if (MI.arg_empty()) + ; + else if (MI.getNumArgs() == 1) + OS << (*MI.arg_begin())->getName(); + else { + MacroInfo::arg_iterator AI = MI.arg_begin(), E = MI.arg_end(); + OS << (*AI++)->getName(); + while (AI != E) + OS << ',' << (*AI++)->getName(); + } + + if (MI.isVariadic()) { + if (!MI.arg_empty()) + OS << ','; + OS << "..."; + } + OS << ')'; + } + + // GCC always emits a space, even if the macro body is empty. However, do not + // want to emit two spaces if the first token has a leading space. + if (MI.tokens_empty() || !MI.tokens_begin()->hasLeadingSpace()) + OS << ' '; + + for (MacroInfo::tokens_iterator I = MI.tokens_begin(), E = MI.tokens_end(); + I != E; ++I) { + if (I->hasLeadingSpace()) + OS << ' '; + OS << PP.getSpelling(*I); + } + OS << "\n"; +} + /// DoPrintPreprocessedInput - This implements -E mode. /// @@ -564,31 +612,45 @@ void clang::DoPrintPreprocessedInput(Preprocessor &PP, OS.SetBufferSize(64*1024); - Token Tok; - PrintPPOutputPPCallbacks *Callbacks; - Callbacks = new PrintPPOutputPPCallbacks(PP, OS); - PP.AddPragmaHandler(0, new UnknownPragmaHandler("#pragma", Callbacks)); - PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC",Callbacks)); - - PP.setPPCallbacks(Callbacks); - - // After we have configured the preprocessor, enter the main file. - - // Start parsing the specified input file. - PP.EnterMainSourceFile(); - - // Consume all of the tokens that come from the predefines buffer. Those - // should not be emitted into the output and are guaranteed to be at the - // start. - const SourceManager &SourceMgr = PP.getSourceManager(); - do PP.Lex(Tok); - while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() && - !strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(), - "")); - - // Read all the preprocessed tokens, printing them out to the stream. - PrintPreprocessedTokens(PP, Tok, Callbacks, OS); - OS << '\n'; + if (DumpMacros) { + // -dM mode just scans and ignores all tokens in the files, then dumps out + // the macro table at the end. + PP.EnterMainSourceFile(); + + Token Tok; + do PP.Lex(Tok); + while (Tok.isNot(tok::eof)); + + for (Preprocessor::macro_iterator I = PP.macro_begin(), E = PP.macro_end(); + I != E; ++I) + PrintMacroDefinition(*I->first, *I->second, PP, OS); + + } else { + PrintPPOutputPPCallbacks *Callbacks; + Callbacks = new PrintPPOutputPPCallbacks(PP, OS); + PP.AddPragmaHandler(0, new UnknownPragmaHandler("#pragma", Callbacks)); + PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC", + Callbacks)); + + PP.setPPCallbacks(Callbacks); + + // After we have configured the preprocessor, enter the main file. + PP.EnterMainSourceFile(); + + // Consume all of the tokens that come from the predefines buffer. Those + // should not be emitted into the output and are guaranteed to be at the + // start. + const SourceManager &SourceMgr = PP.getSourceManager(); + Token Tok; + do PP.Lex(Tok); + while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() && + !strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(), + "")); + + // Read all the preprocessed tokens, printing them out to the stream. + PrintPreprocessedTokens(PP, Tok, Callbacks, OS); + OS << '\n'; + } // Flush the ostream. OS.flush(); diff --git a/include/clang/Lex/MacroInfo.h b/include/clang/Lex/MacroInfo.h index c63b701a85..7e0125f496 100644 --- a/include/clang/Lex/MacroInfo.h +++ b/include/clang/Lex/MacroInfo.h @@ -116,6 +116,7 @@ public: /// Arguments - The list of arguments for a function-like macro. This can be /// empty, for, e.g. "#define X()". typedef IdentifierInfo* const *arg_iterator; + bool arg_empty() const { return NumArguments == 0; } arg_iterator arg_begin() const { return ArgumentList; } arg_iterator arg_end() const { return ArgumentList+NumArguments; } unsigned getNumArgs() const { return NumArguments; } @@ -163,6 +164,7 @@ public: typedef llvm::SmallVector::const_iterator tokens_iterator; tokens_iterator tokens_begin() const { return ReplacementTokens.begin(); } tokens_iterator tokens_end() const { return ReplacementTokens.end(); } + bool tokens_empty() const { return ReplacementTokens.empty(); } /// AddTokenToBody - Add the specified token to the replacement text for the /// macro. diff --git a/lib/Lex/Preprocessor.cpp b/lib/Lex/Preprocessor.cpp index 9b5965ccc5..984cf1aab8 100644 --- a/lib/Lex/Preprocessor.cpp +++ b/lib/Lex/Preprocessor.cpp @@ -13,7 +13,7 @@ // // Options to support: // -H - Print the name of each header file used. -// -d[MDNI] - Dump various things. +// -d[DNI] - Dump various things. // -fworking-directory - #line's with preprocessor's working dir. // -fpreprocessed // -dependency-file,-M,-MM,-MF,-MG,-MP,-MT,-MQ,-MD,-MMD diff --git a/test/Preprocessor/dump_macros.c b/test/Preprocessor/dump_macros.c new file mode 100644 index 0000000000..348257fb51 --- /dev/null +++ b/test/Preprocessor/dump_macros.c @@ -0,0 +1,31 @@ +// RUN: clang -E -dM %s -o %t && + +// Space even without expansion tokens +// RUN: grep "#define A(x) " %t && +#define A(x) + +// Space before expansion list. +// RUN: grep "#define B(x,y) x y" %t && +#define B(x,y)x y + +// No space in expansion list. +// RUN: grep "#define C(x,y) x y" %t && +#define C(x, y) x y + +// No paste avoidance. +// RUN: grep "#define X() .." %t && +#define X() .. + +// Simple test. +// RUN: grep "#define Y ." %t && +// RUN: grep "#define Z X()Y" %t && +#define Y . +#define Z X()Y + +// gcc prints macros at end of translation unit, so last one wins. +// RUN: grep "#define foo 2" %t && +// RUN: not grep "#define foo 1" %t +#define foo 1 +#undef foo +#define foo 2 +