forked from OSchip/llvm-project
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:
parent
5c7cd6043e
commit
1630c3c4f0
|
@ -13,6 +13,7 @@
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
#include "clang.h"
|
#include "clang.h"
|
||||||
|
#include "clang/Lex/MacroInfo.h"
|
||||||
#include "clang/Lex/PPCallbacks.h"
|
#include "clang/Lex/PPCallbacks.h"
|
||||||
#include "clang/Lex/Preprocessor.h"
|
#include "clang/Lex/Preprocessor.h"
|
||||||
#include "clang/Lex/Pragma.h"
|
#include "clang/Lex/Pragma.h"
|
||||||
|
@ -39,6 +40,10 @@ static llvm::cl::opt<bool>
|
||||||
EnableMacroCommentOutput("CC",
|
EnableMacroCommentOutput("CC",
|
||||||
llvm::cl::desc("Enable comment output in -E mode, "
|
llvm::cl::desc("Enable comment output in -E mode, "
|
||||||
"even from macro expansions"));
|
"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 {
|
namespace {
|
||||||
class PrintPPOutputPPCallbacks : public PPCallbacks {
|
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.
|
/// DoPrintPreprocessedInput - This implements -E mode.
|
||||||
///
|
///
|
||||||
|
@ -564,31 +612,45 @@ void clang::DoPrintPreprocessedInput(Preprocessor &PP,
|
||||||
|
|
||||||
OS.SetBufferSize(64*1024);
|
OS.SetBufferSize(64*1024);
|
||||||
|
|
||||||
Token Tok;
|
if (DumpMacros) {
|
||||||
PrintPPOutputPPCallbacks *Callbacks;
|
// -dM mode just scans and ignores all tokens in the files, then dumps out
|
||||||
Callbacks = new PrintPPOutputPPCallbacks(PP, OS);
|
// the macro table at the end.
|
||||||
PP.AddPragmaHandler(0, new UnknownPragmaHandler("#pragma", Callbacks));
|
PP.EnterMainSourceFile();
|
||||||
PP.AddPragmaHandler("GCC", new UnknownPragmaHandler("#pragma GCC",Callbacks));
|
|
||||||
|
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.
|
// After we have configured the preprocessor, enter the main file.
|
||||||
|
PP.EnterMainSourceFile();
|
||||||
// Start parsing the specified input file.
|
|
||||||
PP.EnterMainSourceFile();
|
|
||||||
|
|
||||||
// Consume all of the tokens that come from the predefines buffer. Those
|
// 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
|
// should not be emitted into the output and are guaranteed to be at the
|
||||||
// start.
|
// start.
|
||||||
const SourceManager &SourceMgr = PP.getSourceManager();
|
const SourceManager &SourceMgr = PP.getSourceManager();
|
||||||
do PP.Lex(Tok);
|
Token Tok;
|
||||||
while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() &&
|
do PP.Lex(Tok);
|
||||||
!strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(),
|
while (Tok.isNot(tok::eof) && Tok.getLocation().isFileID() &&
|
||||||
"<predefines>"));
|
!strcmp(SourceMgr.getPresumedLoc(Tok.getLocation()).getFilename(),
|
||||||
|
"<predefines>"));
|
||||||
|
|
||||||
// Read all the preprocessed tokens, printing them out to the stream.
|
// Read all the preprocessed tokens, printing them out to the stream.
|
||||||
PrintPreprocessedTokens(PP, Tok, Callbacks, OS);
|
PrintPreprocessedTokens(PP, Tok, Callbacks, OS);
|
||||||
OS << '\n';
|
OS << '\n';
|
||||||
|
}
|
||||||
|
|
||||||
// Flush the ostream.
|
// Flush the ostream.
|
||||||
OS.flush();
|
OS.flush();
|
||||||
|
|
|
@ -116,6 +116,7 @@ public:
|
||||||
/// Arguments - The list of arguments for a function-like macro. This can be
|
/// Arguments - The list of arguments for a function-like macro. This can be
|
||||||
/// empty, for, e.g. "#define X()".
|
/// empty, for, e.g. "#define X()".
|
||||||
typedef IdentifierInfo* const *arg_iterator;
|
typedef IdentifierInfo* const *arg_iterator;
|
||||||
|
bool arg_empty() const { return NumArguments == 0; }
|
||||||
arg_iterator arg_begin() const { return ArgumentList; }
|
arg_iterator arg_begin() const { return ArgumentList; }
|
||||||
arg_iterator arg_end() const { return ArgumentList+NumArguments; }
|
arg_iterator arg_end() const { return ArgumentList+NumArguments; }
|
||||||
unsigned getNumArgs() const { return NumArguments; }
|
unsigned getNumArgs() const { return NumArguments; }
|
||||||
|
@ -163,6 +164,7 @@ public:
|
||||||
typedef llvm::SmallVector<Token, 8>::const_iterator tokens_iterator;
|
typedef llvm::SmallVector<Token, 8>::const_iterator tokens_iterator;
|
||||||
tokens_iterator tokens_begin() const { return ReplacementTokens.begin(); }
|
tokens_iterator tokens_begin() const { return ReplacementTokens.begin(); }
|
||||||
tokens_iterator tokens_end() const { return ReplacementTokens.end(); }
|
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
|
/// AddTokenToBody - Add the specified token to the replacement text for the
|
||||||
/// macro.
|
/// macro.
|
||||||
|
|
|
@ -13,7 +13,7 @@
|
||||||
//
|
//
|
||||||
// Options to support:
|
// Options to support:
|
||||||
// -H - Print the name of each header file used.
|
// -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.
|
// -fworking-directory - #line's with preprocessor's working dir.
|
||||||
// -fpreprocessed
|
// -fpreprocessed
|
||||||
// -dependency-file,-M,-MM,-MF,-MG,-MP,-MT,-MQ,-MD,-MMD
|
// -dependency-file,-M,-MM,-MF,-MG,-MP,-MT,-MQ,-MD,-MMD
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue