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

llvm-svn: 63926
This commit is contained in:
Chris Lattner 2009-02-06 06:45:26 +00:00
parent 5c7cd6043e
commit 1630c3c4f0
4 changed files with 117 additions and 22 deletions

View File

@ -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<bool>
EnableMacroCommentOutput("CC",
llvm::cl::desc("Enable comment output in -E mode, "
"even from macro expansions"));
static llvm::cl::opt<bool>
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));
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);
PP.setPPCallbacks(Callbacks);
// After we have configured the preprocessor, enter the main file.
// Start parsing the specified input file.
PP.EnterMainSourceFile();
// 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();
do PP.Lex(Tok);
while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() &&
!strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(),
"<predefines>"));
// 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(),
"<predefines>"));
// Read all the preprocessed tokens, printing them out to the stream.
PrintPreprocessedTokens(PP, Tok, Callbacks, OS);
OS << '\n';
// Read all the preprocessed tokens, printing them out to the stream.
PrintPreprocessedTokens(PP, Tok, Callbacks, OS);
OS << '\n';
}
// Flush the ostream.
OS.flush();

View File

@ -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<Token, 8>::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.

View File

@ -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

View File

@ -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