Index: CMakeLists.txt =================================================================== --- CMakeLists.txt +++ CMakeLists.txt @@ -2,6 +2,7 @@ add_subdirectory(clang-modernize) add_subdirectory(clang-tidy) add_subdirectory(modularize) +add_subdirectory(pp-trace) add_subdirectory(remove-cstr-calls) add_subdirectory(tool-template) Index: Makefile =================================================================== --- Makefile +++ Makefile @@ -11,7 +11,7 @@ include $(CLANG_LEVEL)/../../Makefile.config -PARALLEL_DIRS := remove-cstr-calls tool-template modularize +PARALLEL_DIRS := remove-cstr-calls tool-template modularize pp-trace DIRS := clang-apply-replacements clang-modernize clang-tidy unittests include $(CLANG_LEVEL)/Makefile Index: pp-trace/CMakeLists.txt =================================================================== --- pp-trace/CMakeLists.txt +++ pp-trace/CMakeLists.txt @@ -0,0 +1,18 @@ +set(LLVM_LINK_COMPONENTS + ${LLVM_TARGETS_TO_BUILD} + asmparser + support + mc + ) + +add_clang_executable(pp-trace + PPTrace.cpp + PPCallbacksTracker.cpp + ) + +target_link_libraries(pp-trace + clangLex + clangParse + clangSema + clangTooling + ) Index: pp-trace/Makefile =================================================================== --- pp-trace/Makefile +++ pp-trace/Makefile @@ -0,0 +1,22 @@ +##===- extra/pp-trace/Makefile --------------------------*- Makefile -*---===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===---------------------------------------------------------------------===## + +CLANG_LEVEL := ../../.. + +TOOLNAME = pp-trace + +# No plugins, optimize startup time. +TOOL_NO_EXPORTS = 1 + +LINK_COMPONENTS := mcparser bitreader support mc option TransformUtils +USEDLIBS = clangFrontend.a clangSerialization.a clangDriver.a \ + clangTooling.a clangParse.a clangSema.a clangAnalysis.a \ + clangEdit.a clangAST.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/Makefile Index: pp-trace/PPCallbacksTracker.h =================================================================== --- pp-trace/PPCallbacksTracker.h +++ pp-trace/PPCallbacksTracker.h @@ -0,0 +1,240 @@ +//===--- PPCallbacksTracker.h - Preprocessor tracking -*- C++ -*---------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +/// +/// \file +/// \brief Classes and definitions for preprocessor tracking. +/// +/// The core definition is the PPCallbacksTracker class, derived from Clang's +/// PPCallbacks class from the Lex library, which overrides all the callbacks +/// and collects information about each callback call, saving it in a +/// data structure built up of CallbackCall and Argument objects, which +/// record the preprocessor callback name and arguments in high-level string +/// form for later inspection. +/// +//===--------------------------------------------------------------------===// + +#ifndef PPTRACE_PPCALLBACKSTRACKER_H +#define PPTRACE_PPCALLBACKSTRACKER_H + +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" + +/// \brief This class represents one callback function argument by name +/// and value. +class Argument { +public: + Argument(llvm::StringRef Name, llvm::StringRef Value) + : Name(Name), Value(Value) {} + Argument() {} + + std::string Name; + std::string Value; +}; + +/// \brief This class represents one callback call by name and an array +/// of arguments. +class CallbackCall { +public: + CallbackCall(llvm::StringRef Name) : Name(Name) {} + CallbackCall() {} + + std::string Name; + std::vector Arguments; +}; + +/// \brief This class overrides the PPCallbacks class for tracking preprocessor +/// activity by means of its callback functions. +/// +/// This object is given a vector for storing the trace information, built up +/// of CallbackCall and subordinate Argument objects for representing the +/// callback calls and their arguments. It's a reference so the vector can +/// exist beyond the lifetime of this object, because it's deleted by the +/// preprocessor automatically in its destructor. +/// +/// This class supports a mechanism for inhibiting trace output for +/// specific callbacks by name, for the purpose of eliminating output for +/// callbacks of no interest that might clutter the output. +/// +/// Following the constructor and destructor function declarations, the +/// overidden callback functions are defined. The remaining functions are +/// helpers for recording the trace data, to reduce the coupling between it +/// and the recorded data structure. +class PPCallbacksTracker : public clang::PPCallbacks { +public: + /// \brief Note that all of the arguments are references, and owned + /// by the caller. + /// \param Ignore - Set of names of callbacks to ignore. + /// \param CallbackCalls - Trace buffer. + /// \param PP - The preprocessor. Needed for getting some argument strings. + PPCallbacksTracker(llvm::SmallSet &Ignore, + std::vector &CallbackCalls, + clang::Preprocessor &PP); + + virtual ~PPCallbacksTracker(); + + // Overidden callback functions. + + void FileChanged(clang::SourceLocation Loc, + clang::PPCallbacks::FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, + clang::FileID PrevFID = clang::FileID()) LLVM_OVERRIDE; + void FileSkipped(const clang::FileEntry &ParentFile, + const clang::Token &FilenameTok, + clang::SrcMgr::CharacteristicKind FileType) LLVM_OVERRIDE; + bool FileNotFound(llvm::StringRef FileName, + llvm::SmallVectorImpl &RecoveryPath) LLVM_OVERRIDE; + void InclusionDirective(clang::SourceLocation HashLoc, + const clang::Token &IncludeTok, + llvm::StringRef FileName, bool IsAngled, + clang::CharSourceRange FilenameRange, + const clang::FileEntry *File, + llvm::StringRef SearchPath, + llvm::StringRef RelativePath, + const clang::Module *Imported) LLVM_OVERRIDE; + void moduleImport(clang::SourceLocation ImportLoc, clang::ModuleIdPath Path, + const clang::Module *Imported) LLVM_OVERRIDE; + void EndOfMainFile() LLVM_OVERRIDE; + void Ident(clang::SourceLocation Loc, const std::string &str) LLVM_OVERRIDE; + void PragmaDirective(clang::SourceLocation Loc, + clang::PragmaIntroducerKind Introducer) LLVM_OVERRIDE; + void PragmaComment(clang::SourceLocation Loc, + const clang::IdentifierInfo *Kind, + const std::string &Str) LLVM_OVERRIDE; + void PragmaDetectMismatch(clang::SourceLocation Loc, const std::string &Name, + const std::string &Value) LLVM_OVERRIDE; + void PragmaDebug(clang::SourceLocation Loc, + llvm::StringRef DebugType) LLVM_OVERRIDE; + void PragmaMessage(clang::SourceLocation Loc, llvm::StringRef Namespace, + clang::PPCallbacks::PragmaMessageKind Kind, + llvm::StringRef Str) LLVM_OVERRIDE; + void PragmaDiagnosticPush(clang::SourceLocation Loc, + llvm::StringRef Namespace) LLVM_OVERRIDE; + void PragmaDiagnosticPop(clang::SourceLocation Loc, + llvm::StringRef Namespace) LLVM_OVERRIDE; + void PragmaDiagnostic(clang::SourceLocation Loc, llvm::StringRef Namespace, + clang::diag::Mapping mapping, + llvm::StringRef Str) LLVM_OVERRIDE; + void PragmaOpenCLExtension(clang::SourceLocation NameLoc, + const clang::IdentifierInfo *Name, + clang::SourceLocation StateLoc, + unsigned State) LLVM_OVERRIDE; + void PragmaWarning(clang::SourceLocation Loc, llvm::StringRef WarningSpec, + llvm::ArrayRef Ids) LLVM_OVERRIDE; + void PragmaWarningPush(clang::SourceLocation Loc, int Level) LLVM_OVERRIDE; + void PragmaWarningPop(clang::SourceLocation Loc) LLVM_OVERRIDE; + void MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, clang::SourceRange Range, + const clang::MacroArgs *Args) LLVM_OVERRIDE; + void MacroDefined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) LLVM_OVERRIDE; + void MacroUndefined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) LLVM_OVERRIDE; + void Defined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MD, + clang::SourceRange Range) LLVM_OVERRIDE; + void SourceRangeSkipped(clang::SourceRange Range) LLVM_OVERRIDE; + void If(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + bool ConditionValue) LLVM_OVERRIDE; + void Elif(clang::SourceLocation Loc, clang::SourceRange ConditionRange, + bool ConditionValue, clang::SourceLocation IfLoc) LLVM_OVERRIDE; + void Ifdef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) LLVM_OVERRIDE; + void Ifndef(clang::SourceLocation Loc, const clang::Token &MacroNameTok, + const clang::MacroDirective *MD) LLVM_OVERRIDE; + void Else(clang::SourceLocation Loc, + clang::SourceLocation IfLoc) LLVM_OVERRIDE; + void Endif(clang::SourceLocation Loc, + clang::SourceLocation IfLoc) LLVM_OVERRIDE; + + // Helper functions. + + /// \brief Start a new callback. + void beginCallback(const char *Name); + + /// \brief Append a string to the top trace item. + void append(const char *Str); + + /// \brief Format and append a string to the top trace item. + void appendFormatted(const char *Format, ...); + + /// \brief Append a bool argument to the top trace item. + void appendArgument(const char *Name, bool Value); + + /// \brief Append an int argument to the top trace item. + void appendArgument(const char *Name, int Value); + + /// \brief Append a string argument to the top trace item. + void appendArgument(const char *Name, const char *Value); + + /// \brief Append a string reference object argument to the top trace item. + void appendArgument(const char *Name, llvm::StringRef Value); + + /// \brief Append a string object argument to the top trace item. + void appendArgument(const char *Name, const std::string &Value); + + /// \brief Append a token argument to the top trace item. + void appendArgument(const char *Name, const clang::Token &Value); + + /// \brief Append an enum argument to the top trace item. + void appendArgument(const char *Name, int Value, const char *Strings[]); + + /// \brief Append a FileID argument to the top trace item. + void appendArgument(const char *Name, clang::FileID Value); + + /// \brief Append a FileEntry argument to the top trace item. + void appendArgument(const char *Name, const clang::FileEntry *Value); + + /// \brief Append a SourceLocation argument to the top trace item. + void appendArgument(const char *Name, clang::SourceLocation Value); + + /// \brief Append a SourceRange argument to the top trace item. + void appendArgument(const char *Name, clang::SourceRange Value); + + /// \brief Append a CharSourceRange argument to the top trace item. + void appendArgument(const char *Name, clang::CharSourceRange Value); + + /// \brief Append a ModuleIdPath argument to the top trace item. + void appendArgument(const char *Name, clang::ModuleIdPath Value); + + /// \brief Append an IdentifierInfo argument to the top trace item. + void appendArgument(const char *Name, const clang::IdentifierInfo *Value); + + /// \brief Append a MacroDirective argument to the top trace item. + void appendArgument(const char *Name, const clang::MacroDirective *Value); + + /// \brief Append a MacroArgs argument to the top trace item. + void appendArgument(const char *Name, const clang::MacroArgs *Value); + + /// \brief Append a Module argument to the top trace item. + void appendArgument(const char *Name, const clang::Module *Value); + + /// \brief Append a double-quoted argument to the top trace item. + void appendQuotedArgument(const char *Name, std::string &Value); + + /// \brief Append a double-quoted file path argument to the top trace item. + void appendFilePathArgument(const char *Name, llvm::StringRef Value); + + /// \brief Get the raw source string of the range. + llvm::StringRef getSourceString(clang::CharSourceRange Range); + + /// \brief Callback trace information. + /// We use a reference so the trace will be preserved for the caller + /// after this object is destructed. + std::vector &CallbackCalls; + + /// \brief Names of callbacks to ignore. + llvm::SmallSet &Ignore; + + /// \brief Inhibit trace while this is set. + bool DisableTrace; + + clang::Preprocessor &PP; +}; + +#endif // PPTRACE_PPCALLBACKSTRACKER_H Index: pp-trace/PPCallbacksTracker.cpp =================================================================== --- pp-trace/PPCallbacksTracker.cpp +++ pp-trace/PPCallbacksTracker.cpp @@ -0,0 +1,629 @@ +//===--- PPCallbacksTracker.cpp - Preprocessor tracker -*--*-------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===--------------------------------------------------------------------===// +/// +/// \file +/// \brief Implementations for preprocessor tracking. +/// +/// See the header for details. +/// +//===--------------------------------------------------------------------===// + +#include "PPCallbacksTracker.h" +#include "clang/Lex/MacroArgs.h" +#include "llvm/Support/raw_ostream.h" +#include +#include + +// Utility functions. + +// Get a "file:line:column" source location string. +static std::string getSourceLocationString(clang::Preprocessor &PP, + clang::SourceLocation Loc) { + if (Loc.isInvalid()) + return std::string("(none)"); + + if (Loc.isFileID()) { + clang::PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc); + + if (PLoc.isInvalid()) { + return std::string("(invalid)"); + } + + std::string Str; + llvm::raw_string_ostream SS(Str); + + // The macro expansion and spelling pos is identical for file locs. + SS << "\"" << PLoc.getFilename() << ':' << PLoc.getLine() << ':' + << PLoc.getColumn() << "\""; + + std::string Result = SS.str(); + + // YAML treats backslash as escape, so use forward slashes. + std::replace(Result.begin(), Result.end(), '\\', '/'); + + return Result; + } + + return std::string("(nonfile)"); +} + +// Enum string tables. + +// FileChangeReason strings. +static const char *FileChangeReasonStrings[] = { + "EnterFile", "ExitFile", "SystemHeaderPragma", "RenameFile" +}; + +// CharacteristicKind strings. +static const char *CharacteristicKindStrings[] = { "C_User", "C_System", + "C_ExternCSystem" }; + +// MacroDirective::Kind strings. +static const char *MacroDirectiveKindStrings[] = { "MD_Define", "MD_Undefine", + "MD_Visibility" }; + +// PragmaIntroducerKind strings. +static const char *PragmaIntroducerKindStrings[] = { "PIK_HashPragma", + "PIK__Pragma", + "PIK___pragma" }; + +// PragmaMessageKind strings. +static const char *PragmaMessageKindStrings[] = { "PMK_Message", "PMK_Warning", + "PMK_Error" }; + +// Mapping strings. +static const char *MappingStrings[] = { "0", "MAP_IGNORE", + "MAP_WARNING", "MAP_ERROR", + "MAP_FATAL" }; + +// PPCallbacksTracker functions. + +PPCallbacksTracker::PPCallbacksTracker(llvm::SmallSet &Ignore, + std::vector &CallbackCalls, + clang::Preprocessor &PP) + : CallbackCalls(CallbackCalls), Ignore(Ignore), PP(PP) {} + +PPCallbacksTracker::~PPCallbacksTracker() {} + +// Callback functions. + +// Callback invoked whenever a source file is entered or exited. +void PPCallbacksTracker::FileChanged( + clang::SourceLocation Loc, clang::PPCallbacks::FileChangeReason Reason, + clang::SrcMgr::CharacteristicKind FileType, clang::FileID PrevFID) { + beginCallback("FileChanged"); + appendArgument("Loc", Loc); + appendArgument("Reason", Reason, FileChangeReasonStrings); + appendArgument("FileType", FileType, CharacteristicKindStrings); + appendArgument("PrevFID", PrevFID); +} + +// Callback invoked whenever a source file is skipped as the result +// of header guard optimization. +void +PPCallbacksTracker::FileSkipped(const clang::FileEntry &ParentFile, + const clang::Token &FilenameTok, + clang::SrcMgr::CharacteristicKind FileType) { + beginCallback("FileSkipped"); + appendArgument("ParentFile", &ParentFile); + appendArgument("FilenameTok", FilenameTok); + appendArgument("FileType", FileType, CharacteristicKindStrings); +} + +// Callback invoked whenever an inclusion directive results in a +// file-not-found error. +bool +PPCallbacksTracker::FileNotFound(llvm::StringRef FileName, + llvm::SmallVectorImpl &RecoveryPath) { + beginCallback("FileNotFound"); + appendFilePathArgument("FileName", FileName); + return false; +} + +// Callback invoked whenever an inclusion directive of +// any kind (#include, #import, etc.) has been processed, regardless +// of whether the inclusion will actually result in an inclusion. +void PPCallbacksTracker::InclusionDirective( + clang::SourceLocation HashLoc, const clang::Token &IncludeTok, + llvm::StringRef FileName, bool IsAngled, + clang::CharSourceRange FilenameRange, const clang::FileEntry *File, + llvm::StringRef SearchPath, llvm::StringRef RelativePath, + const clang::Module *Imported) { + beginCallback("InclusionDirective"); + appendArgument("IncludeTok", IncludeTok); + appendFilePathArgument("FileName", FileName); + appendArgument("IsAngled", IsAngled); + appendArgument("FilenameRange", FilenameRange); + appendArgument("File", File); + appendFilePathArgument("SearchPath", SearchPath); + appendFilePathArgument("RelativePath", RelativePath); + appendArgument("Imported", Imported); +} + +// Callback invoked whenever there was an explicit module-import +// syntax. +void PPCallbacksTracker::moduleImport(clang::SourceLocation ImportLoc, + clang::ModuleIdPath Path, + const clang::Module *Imported) { + beginCallback("moduleImport"); + appendArgument("ImportLoc", ImportLoc); + appendArgument("Path", Path); + appendArgument("Imported", Imported); +} + +// Callback invoked when the end of the main file is reached. +// No subsequent callbacks will be made. +void PPCallbacksTracker::EndOfMainFile() { beginCallback("EndOfMainFile"); } + +// Callback invoked when a #ident or #sccs directive is read. +void PPCallbacksTracker::Ident(clang::SourceLocation Loc, + const std::string &Str) { + beginCallback("Ident"); + appendArgument("Loc", Loc); + appendArgument("Path", Str); +} + +// Callback invoked when start reading any pragma directive. +void +PPCallbacksTracker::PragmaDirective(clang::SourceLocation Loc, + clang::PragmaIntroducerKind Introducer) { + beginCallback("PragmaDirective"); + appendArgument("Loc", Loc); + appendArgument("Path", Introducer, PragmaIntroducerKindStrings); +} + +// Callback invoked when a #pragma comment directive is read. +void PPCallbacksTracker::PragmaComment(clang::SourceLocation Loc, + const clang::IdentifierInfo *Kind, + const std::string &Str) { + beginCallback("PragmaComment"); + appendArgument("Loc", Loc); + appendArgument("Kind", Kind); + appendArgument("Str", Str); +} + +// Callback invoked when a #pragma detect_mismatch directive is +// read. +void PPCallbacksTracker::PragmaDetectMismatch(clang::SourceLocation Loc, + const std::string &Name, + const std::string &Value) { + beginCallback("PragmaDetectMismatch"); + appendArgument("Loc", Loc); + appendArgument("Name", Name); + appendArgument("Value", Value); +} + +// Callback invoked when a #pragma clang __debug directive is read. +void PPCallbacksTracker::PragmaDebug(clang::SourceLocation Loc, + llvm::StringRef DebugType) { + beginCallback("PragmaDebug"); + appendArgument("Loc", Loc); + appendArgument("DebugType", DebugType); +} + +// Callback invoked when a #pragma message directive is read. +void PPCallbacksTracker::PragmaMessage( + clang::SourceLocation Loc, llvm::StringRef Namespace, + clang::PPCallbacks::PragmaMessageKind Kind, llvm::StringRef Str) { + beginCallback("PragmaMessage"); + appendArgument("Loc", Loc); + appendArgument("Namespace", Namespace); + appendArgument("Kind", Kind, PragmaMessageKindStrings); + appendArgument("Str", Str); +} + +// Callback invoked when a #pragma gcc dianostic push directive +// is read. +void PPCallbacksTracker::PragmaDiagnosticPush(clang::SourceLocation Loc, + llvm::StringRef Namespace) { + beginCallback("PragmaDiagnosticPush"); + appendArgument("Loc", Loc); + appendArgument("Namespace", Namespace); +} + +// Callback invoked when a #pragma gcc dianostic pop directive +// is read. +void PPCallbacksTracker::PragmaDiagnosticPop(clang::SourceLocation Loc, + llvm::StringRef Namespace) { + beginCallback("PragmaDiagnosticPop"); + appendArgument("Loc", Loc); + appendArgument("Namespace", Namespace); +} + +// Callback invoked when a #pragma gcc dianostic directive is read. +void PPCallbacksTracker::PragmaDiagnostic(clang::SourceLocation Loc, + llvm::StringRef Namespace, + clang::diag::Mapping Mapping, + llvm::StringRef Str) { + beginCallback("PragmaDiagnostic"); + appendArgument("Loc", Loc); + appendArgument("Namespace", Namespace); + appendArgument("Mapping", Mapping, MappingStrings); + appendArgument("Str", Str); +} + +// Called when an OpenCL extension is either disabled or +// enabled with a pragma. +void PPCallbacksTracker::PragmaOpenCLExtension( + clang::SourceLocation NameLoc, const clang::IdentifierInfo *Name, + clang::SourceLocation StateLoc, unsigned State) { + beginCallback("PragmaOpenCLExtension"); + appendArgument("NameLoc", NameLoc); + appendArgument("Name", Name); + appendArgument("StateLoc", StateLoc); + appendArgument("State", (int)State); +} + +// Callback invoked when a #pragma warning directive is read. +void PPCallbacksTracker::PragmaWarning(clang::SourceLocation Loc, + llvm::StringRef WarningSpec, + llvm::ArrayRef Ids) { + beginCallback("PragmaWarning"); + appendArgument("Loc", Loc); + appendArgument("WarningSpec", WarningSpec); + + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << "["; + for (int i = 0, e = Ids.size(); i != e; ++i) { + if (i) + SS << ", "; + SS << Ids[i]; + } + appendArgument("Ids", SS.str()); +} + +// Callback invoked when a #pragma warning(push) directive is read. +void PPCallbacksTracker::PragmaWarningPush(clang::SourceLocation Loc, + int Level) { + beginCallback("PragmaWarningPush"); + appendArgument("Loc", Loc); + appendArgument("Level", Level); +} + +// Callback invoked when a #pragma warning(pop) directive is read. +void PPCallbacksTracker::PragmaWarningPop(clang::SourceLocation Loc) { + beginCallback("PragmaWarningPop"); + appendArgument("Loc", Loc); +} + +// Called by Preprocessor::HandleMacroExpandedIdentifier when a +// macro invocation is found. +void +PPCallbacksTracker::MacroExpands(const clang::Token &MacroNameTok, + const clang::MacroDirective *MacroDirective, + clang::SourceRange Range, + const clang::MacroArgs *Args) { + beginCallback("MacroExpands"); + appendArgument("MacroNameTok", MacroNameTok); + appendArgument("MacroDirective", MacroDirective); + appendArgument("Range", Range); + appendArgument("Args", Args); +} + +// Hook called whenever a macro definition is seen. +void +PPCallbacksTracker::MacroDefined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MacroDirective) { + beginCallback("MacroDefined"); + appendArgument("MacroNameTok", MacroNameTok); + appendArgument("MacroDirective", MacroDirective); +} + +// Hook called whenever a macro #undef is seen. +void PPCallbacksTracker::MacroUndefined( + const clang::Token &MacroNameTok, + const clang::MacroDirective *MacroDirective) { + beginCallback("MacroUndefined"); + appendArgument("MacroNameTok", MacroNameTok); + appendArgument("MacroDirective", MacroDirective); +} + +// Hook called whenever the 'defined' operator is seen. +void PPCallbacksTracker::Defined(const clang::Token &MacroNameTok, + const clang::MacroDirective *MacroDirective, + clang::SourceRange Range) { + beginCallback("Defined"); + appendArgument("MacroNameTok", MacroNameTok); + appendArgument("MacroDirective", MacroDirective); + appendArgument("Range", Range); +} + +// Hook called when a source range is skipped. +void PPCallbacksTracker::SourceRangeSkipped(clang::SourceRange Range) { + beginCallback("SourceRangeSkipped"); + appendArgument("Range", Range); +} + +// Hook called whenever an #if is seen. +void PPCallbacksTracker::If(clang::SourceLocation Loc, + clang::SourceRange ConditionRange, + bool ConditionValue) { + beginCallback("If"); + appendArgument("Loc", Loc); + appendArgument("ConditionRange", ConditionRange); + appendArgument("ConditionValue", ConditionValue); +} + +// Hook called whenever an #elif is seen. +void PPCallbacksTracker::Elif(clang::SourceLocation Loc, + clang::SourceRange ConditionRange, + bool ConditionValue, + clang::SourceLocation IfLoc) { + beginCallback("Elif"); + appendArgument("Loc", Loc); + appendArgument("ConditionRange", ConditionRange); + appendArgument("ConditionValue", ConditionValue); + appendArgument("IfLoc", IfLoc); +} + +// Hook called whenever an #ifdef is seen. +void PPCallbacksTracker::Ifdef(clang::SourceLocation Loc, + const clang::Token &MacroNameTok, + const clang::MacroDirective *MacroDirective) { + beginCallback("Ifdef"); + appendArgument("Loc", Loc); + appendArgument("MacroNameTok", MacroNameTok); + appendArgument("MacroDirective", MacroDirective); +} + +// Hook called whenever an #ifndef is seen. +void PPCallbacksTracker::Ifndef(clang::SourceLocation Loc, + const clang::Token &MacroNameTok, + const clang::MacroDirective *MacroDirective) { + beginCallback("Ifndef"); + appendArgument("Loc", Loc); + appendArgument("MacroNameTok", MacroNameTok); + appendArgument("MacroDirective", MacroDirective); +} + +// Hook called whenever an #else is seen. +void PPCallbacksTracker::Else(clang::SourceLocation Loc, + clang::SourceLocation IfLoc) { + beginCallback("Else"); + appendArgument("Loc", Loc); + appendArgument("IfLoc", IfLoc); +} + +// Hook called whenever an #endif is seen. +void PPCallbacksTracker::Endif(clang::SourceLocation Loc, + clang::SourceLocation IfLoc) { + beginCallback("Endif"); + appendArgument("Loc", Loc); + appendArgument("IfLoc", IfLoc); +} + +// Helper functions. + +// Start a new callback. +void PPCallbacksTracker::beginCallback(const char *Name) { + DisableTrace = Ignore.count(std::string(Name)); + if (DisableTrace) + return; + CallbackCalls.push_back(CallbackCall(Name)); +} + +// Append a bool argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, bool Value) { + appendArgument(Name, (Value ? "true" : "false")); +} + +// Append an int argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, int Value) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << Value; + appendArgument(Name, SS.str()); +} + +// Append a string argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, const char *Value) { + if (DisableTrace) + return; + CallbackCalls.back().Arguments.push_back(Argument(Name, Value)); +} + +// Append a string object argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + llvm::StringRef Value) { + appendArgument(Name, Value.str()); +} + +// Append a string object argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const std::string &Value) { + appendArgument(Name, Value.c_str()); +} + +// Append a token argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const clang::Token &Value) { + appendArgument(Name, PP.getSpelling(Value)); +} + +// Append an enum argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, int Value, + const char *Strings[]) { + appendArgument(Name, Strings[Value]); +} + +// Append a FileID argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, clang::FileID Value) { + if (Value.isInvalid()) { + appendArgument(Name, "(invalid)"); + return; + } + const clang::FileEntry *FileEntry = + PP.getSourceManager().getFileEntryForID(Value); + if (FileEntry == 0) { + appendArgument(Name, "(getFileEntryForID failed)"); + return; + } + appendFilePathArgument(Name, FileEntry->getName()); +} + +// Append a FileEntry argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const clang::FileEntry *Value) { + if (Value == 0) { + appendArgument(Name, "(null)"); + return; + } + appendFilePathArgument(Name, Value->getName()); +} + +// Append a SourceLocation argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + clang::SourceLocation Value) { + if (Value.isInvalid()) { + appendArgument(Name, "(invalid)"); + return; + } + appendArgument(Name, getSourceLocationString(PP, Value).c_str()); +} + +// Append a SourceRange argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + clang::SourceRange Value) { + if (DisableTrace) + return; + if (Value.isInvalid()) { + appendArgument(Name, "(invalid)"); + return; + } + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << "[" << getSourceLocationString(PP, Value.getBegin()) << ", " + << getSourceLocationString(PP, Value.getEnd()) << "]"; + appendArgument(Name, SS.str()); +} + +// Append a CharSourceRange argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + clang::CharSourceRange Value) { + if (Value.isInvalid()) { + appendArgument(Name, "(invalid)"); + return; + } + appendArgument(Name, getSourceString(Value).str().c_str()); +} + +// Append a SourceLocation argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + clang::ModuleIdPath Value) { + if (DisableTrace) + return; + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << "["; + for (int I = 0, E = Value.size(); I != E; ++I) { + if (I) + SS << ", "; + SS << "{" + << "Name: " << Value[I].first->getName() << ", " + << "Loc:" << getSourceLocationString(PP, Value[I].second) << "}"; + } + SS << "]"; + appendArgument(Name, SS.str()); +} + +// Append an IdentifierInfo argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const clang::IdentifierInfo *Value) { + if (!Value) { + appendArgument(Name, "(null)"); + return; + } + appendArgument(Name, Value->getName().str().c_str()); +} + +// Append a MacroDirective argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const clang::MacroDirective *Value) { + if (!Value) { + appendArgument(Name, "(null)"); + return; + } + appendArgument(Name, MacroDirectiveKindStrings[Value->getKind()]); +} + +// Append a MacroArgs argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const clang::MacroArgs *Value) { + if (!Value) { + appendArgument(Name, "(null)"); + return; + } + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << "["; + // The argument tokens might include end tokens, so we reflect how + // how getUnexpArgument provides the arguments. + for (int I = 0, E = Value->getNumArguments(); I < E; ++I) { + const clang::Token *Current = Value->getUnexpArgument(I); + int TokenCount = Value->getArgLength(Current) + 1; // include EOF + E -= TokenCount; + if (I) + SS << ", "; + // We're assuming tokens are contiguous, as otherwise we have no + // other way to get at them. + --TokenCount; + for (int TokenIndex = 0; TokenIndex < TokenCount; ++TokenIndex, ++Current) { + if (TokenIndex) + SS << " "; + // We need to be careful here because the arguments might not be legal in + // YAML, so we use the token name for anything but identifiers and + // numeric literals. + if (Current->isAnyIdentifier() || + Current->is(clang::tok::numeric_constant)) { + SS << PP.getSpelling(*Current); + } else { + SS << "<" << Current->getName() << ">"; + } + } + } + SS << "]"; + appendArgument(Name, SS.str()); +} + +// Append a Module argument to the top trace item. +void PPCallbacksTracker::appendArgument(const char *Name, + const clang::Module *Value) { + if (!Value) { + appendArgument(Name, "(null)"); + return; + } + appendArgument(Name, Value->Name.c_str()); +} + +// Append a double-quoted argument to the top trace item. +void PPCallbacksTracker::appendQuotedArgument(const char *Name, + std::string &Value) { + std::string Str; + llvm::raw_string_ostream SS(Str); + SS << "\"" << Value << "\""; + appendArgument(Name, SS.str()); +} + +// Append a double-quoted file path argument to the top trace item. +void PPCallbacksTracker::appendFilePathArgument(const char *Name, + llvm::StringRef Value) { + std::string Path(Value); + // YAML treats backslash as escape, so use forward slashes. + std::replace(Path.begin(), Path.end(), '\\', '/'); + appendQuotedArgument(Name, Path); +} + +// Get the raw source string of the range. +llvm::StringRef +PPCallbacksTracker::getSourceString(clang::CharSourceRange Range) { + const char *B = PP.getSourceManager().getCharacterData(Range.getBegin()); + const char *E = PP.getSourceManager().getCharacterData(Range.getEnd()); + return llvm::StringRef(B, E - B); +} Index: pp-trace/PPTrace.cpp =================================================================== --- pp-trace/PPTrace.cpp +++ pp-trace/PPTrace.cpp @@ -0,0 +1,231 @@ +//===--- tools/pp-trace/PPTrace.cpp - Clang preprocessor tracer -----------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file implements pp-trace, a tool for displaying a textual trace +// of the Clang preprocessor activity. It's based on a derivation of the +// PPCallbacks class, that once registerd with Clang, receives callback calls +// to its virtual members, and outputs the information passed to the callbacks +// in a high-level YAML format. +// +// The pp-trace tool also serves as the basis for a test of the PPCallbacks +// mechanism. +// +// The pp-trace tool supports the following general command line format: +// +// pp-trace [pp-trace options] (source file(s)) [compiler options] +// +// Basically you put the pp-trace options first, then the source file or files, +// and then any options you want to pass to the compiler. +// +// These are the pp-trace options: +// +// -ignore (callback list) Don't display output for a comma-separated +// list of callbacks, i.e.: +// -ignore "FileChanged,InclusionDirective" +// +// -output (file|-) Output trace to the given file (or stdout +// for "-") in a YAML format, e.g.: +// +// --- +// - Callback: Name +// Argument1: Value1 +// Argument2: Value2 +// (etc.) +// ... +// +//===----------------------------------------------------------------------===// + +#include "PPCallbacksTracker.h" +#include "clang/AST/ASTConsumer.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Driver/Options.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Frontend/FrontendActions.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Tooling/CompilationDatabase.h" +#include "clang/Tooling/Tooling.h" +#include "llvm/ADT/OwningPtr.h" +#include "llvm/Config/config.h" +#include "llvm/Option/Arg.h" +#include "llvm/Option/ArgList.h" +#include "llvm/Option/OptTable.h" +#include "llvm/Option/Option.h" +#include "llvm/Support/CommandLine.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include "llvm/Support/ToolOutputFile.h" +#include +#include +#include +#include +#include + +using namespace clang; +using namespace clang::driver; +using namespace clang::driver::options; +using namespace clang::tooling; +using namespace llvm; +using namespace llvm::opt; + +// Options: + +// Collect the source files. +cl::list SourcePaths(cl::Positional, + cl::desc(" [... ]"), + cl::OneOrMore); + +// Option to specify a list or one or more callback names to ignore. +cl::opt IgnoreCallbacks( + "ignore", cl::init(""), + cl::desc("Ignore callbacks, i.e. \"Callback1, Callback2...\".")); + +// Option to specify the trace output file name. +cl::opt OutputFileName( + "output", cl::init(""), + cl::desc("Output trace to the given file name or '-' for stdout.")); + +// Collect all other arguments, which will be passed to the front end. +cl::list +CC1Arguments(cl::ConsumeAfter, + cl::desc("...")); + +// Frontend action stuff: + +// Consumer is responsible for setting up the callbacks. +class PPTraceConsumer : public ASTConsumer { +public: + PPTraceConsumer(SmallSet &Ignore, + std::vector &CallbackCalls, Preprocessor &PP) + : CallbacksTracker(new PPCallbacksTracker(Ignore, CallbackCalls, PP)) { + PP.addPPCallbacks(CallbacksTracker); // Takes ownership. + } + +private: + PPCallbacksTracker *CallbacksTracker; // Not owned here. +}; + +class PPTraceAction : public SyntaxOnlyAction { +public: + PPTraceAction(SmallSet &Ignore, + std::vector &CallbackCalls) + : Ignore(Ignore), CallbackCalls(CallbackCalls) {} + +protected: + virtual clang::ASTConsumer *CreateASTConsumer(CompilerInstance &CI, + StringRef InFile) { + return new PPTraceConsumer(Ignore, CallbackCalls, CI.getPreprocessor()); + } + +private: + SmallSet &Ignore; + std::vector &CallbackCalls; +}; + +class PPTraceFrontendActionFactory : public FrontendActionFactory { +public: + PPTraceFrontendActionFactory(SmallSet &Ignore, + std::vector &CallbackCalls) + : Ignore(Ignore), CallbackCalls(CallbackCalls) {} + + virtual PPTraceAction *create() { + return new PPTraceAction(Ignore, CallbackCalls); + } + +private: + SmallSet &Ignore; + std::vector &CallbackCalls; +}; + +// Output the trace given its data structure and a stream. +int outputPPTrace(std::vector &CallbackCalls, + llvm::raw_ostream &OS) { + // Mark start of document. + OS << "---\n"; + + for (std::vector::const_iterator I = CallbackCalls.begin(), + E = CallbackCalls.end(); + I != E; ++I) { + const CallbackCall &Callback = *I; + OS << "- Callback: " << Callback.Name << "\n"; + + for (std::vector::const_iterator AI = Callback.Arguments.begin(), + AE = Callback.Arguments.end(); + AI != AE; ++AI) { + const Argument &Arg = *AI; + OS << " " << Arg.Name << ": " << Arg.Value << "\n"; + } + } + + // Mark end of document. + OS << "...\n"; + + return 0; +} + +// Program entry point. +int main(int Argc, const char **Argv) { + + // Parse command line. + cl::ParseCommandLineOptions(Argc, Argv, "pp-trace.\n"); + + // Parse the IgnoreCallbacks list into strings. + SmallVector IgnoreCallbacksStrings; + StringRef(IgnoreCallbacks).split(IgnoreCallbacksStrings, ",", + /*MaxSplit=*/ -1, /*KeepEmpty=*/false); + SmallSet Ignore; + for (SmallVector::iterator I = IgnoreCallbacksStrings.begin(), + E = IgnoreCallbacksStrings.end(); + I != E; ++I) + Ignore.insert(*I); + + // Create the compilation database. + SmallString<256> PathBuf; + sys::fs::current_path(PathBuf); + OwningPtr Compilations; + Compilations.reset( + new FixedCompilationDatabase(Twine(PathBuf), CC1Arguments)); + + // Store the callback trace information here. + std::vector CallbackCalls; + + // Create the tool and run the compilation. + ClangTool Tool(*Compilations, SourcePaths); + int HadErrors = + Tool.run(new PPTraceFrontendActionFactory(Ignore, CallbackCalls)); + + // If we had errors, exit early. + if (HadErrors) + return HadErrors; + + // Do the output. + // If file name is "-", output to stdout. + if (!OutputFileName.size() || (OutputFileName == "-")) { + HadErrors = outputPPTrace(CallbackCalls, llvm::outs()); + } else { + // Set up output file. + std::string Error; + llvm::tool_output_file Out(OutputFileName.c_str(), Error); + if (!Error.empty()) { + llvm::errs() << "pp-trace: error creating " << OutputFileName << ":" + << Error << "\n"; + return 1; + } + + HadErrors = outputPPTrace(CallbackCalls, Out.os()); + + // Tell tool_output_file that we want to keep the file. + if (HadErrors == 0) + Out.keep(); + } + + return HadErrors; +} Index: test/pp-trace/Input/Level1A.h =================================================================== --- test/pp-trace/Input/Level1A.h +++ test/pp-trace/Input/Level1A.h @@ -0,0 +1,2 @@ +#include "Level2A.h" +#define MACRO_1A 1 Index: test/pp-trace/Input/Level1B.h =================================================================== --- test/pp-trace/Input/Level1B.h +++ test/pp-trace/Input/Level1B.h @@ -0,0 +1 @@ +#define MACRO_1B 1 Index: test/pp-trace/Input/Level2A.h =================================================================== --- test/pp-trace/Input/Level2A.h +++ test/pp-trace/Input/Level2A.h @@ -0,0 +1 @@ +#define MACRO_2A 1 Index: test/pp-trace/pp-trace-include.cpp =================================================================== --- test/pp-trace/pp-trace-include.cpp +++ test/pp-trace/pp-trace-include.cpp @@ -0,0 +1,119 @@ +// RUN: pp-trace %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s + +#include "Input/Level1A.h" +#include "Input/Level1B.h" + +// CHECK: --- +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:1:1" +// CHECK-NEXT: Reason: EnterFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: ":1:1" +// CHECK-NEXT: Reason: EnterFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: ":1:1" +// CHECK-NEXT: Reason: RenameFile +// CHECK-NEXT: FileType: C_System +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC_HOSTED__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __cplusplus +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC_UTF_16__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC_UTF_32__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: ":1:1" +// CHECK-NEXT: Reason: EnterFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: ":1:1" +// CHECK-NEXT: Reason: ExitFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:1:1" +// CHECK-NEXT: Reason: ExitFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (getFileEntryForID failed) +// CHECK-NEXT: - Callback: InclusionDirective +// CHECK-NEXT: IncludeTok: include +// CHECK-NEXT: FileName: "Input/Level1A.h" +// CHECK-NEXT: IsAngled: false +// CHECK-NEXT: FilenameRange: "Input/Level1A.h" +// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Input/Level1A.h" +// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}pp-trace" +// CHECK-NEXT: RelativePath: "Input/Level1A.h" +// CHECK-NEXT: Imported: (null) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Input/Level1A.h:1:1" +// CHECK-NEXT: Reason: EnterFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: InclusionDirective +// CHECK-NEXT: IncludeTok: include +// CHECK-NEXT: FileName: "Level2A.h" +// CHECK-NEXT: IsAngled: false +// CHECK-NEXT: FilenameRange: "Level2A.h" +// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Input/Level2A.h" +// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}Input" +// CHECK-NEXT: RelativePath: "Level2A.h" +// CHECK-NEXT: Imported: (null) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Input/Level2A.h:1:1" +// CHECK-NEXT: Reason: EnterFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: MACRO_2A +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Input/Level1A.h:2:1" +// CHECK-NEXT: Reason: ExitFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Input/Level2A.h" +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: MACRO_1A +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:4:1" +// CHECK-NEXT: Reason: ExitFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Input/Level1A.h" +// CHECK-NEXT: - Callback: InclusionDirective +// CHECK-NEXT: IncludeTok: include +// CHECK-NEXT: FileName: "Input/Level1B.h" +// CHECK-NEXT: IsAngled: false +// CHECK-NEXT: FilenameRange: "Input/Level1B.h" +// CHECK-NEXT: File: "{{.*}}{{[/\\]}}Input/Level1B.h" +// CHECK-NEXT: SearchPath: "{{.*}}{{[/\\]}}pp-trace" +// CHECK-NEXT: RelativePath: "Input/Level1B.h" +// CHECK-NEXT: Imported: (null) +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}Input/Level1B.h:1:1" +// CHECK-NEXT: Reason: EnterFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: (invalid) +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: MACRO_1B +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: FileChanged +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-include.cpp:5:1" +// CHECK-NEXT: Reason: ExitFile +// CHECK-NEXT: FileType: C_User +// CHECK-NEXT: PrevFID: "{{.*}}{{[/\\]}}Input/Level1B.h" +// CHECK-NEXT: - Callback: EndOfMainFile +// CHECK-NEXT: ... Index: test/pp-trace/pp-trace-macro.cpp =================================================================== --- test/pp-trace/pp-trace-macro.cpp +++ test/pp-trace/pp-trace-macro.cpp @@ -0,0 +1,101 @@ +// RUN: pp-trace -ignore FileChanged %s -undef -target x86_64 -std=c++11 | FileCheck --strict-whitespace %s + +#define MACRO 1 +int i = MACRO; +#if defined(MACRO) +#endif +#undef MACRO +#if defined(MACRO) +#endif +#define FUNCMACRO(ARG1) ARG1 +int j = FUNCMACRO(1); +#define X X_IMPL(a+y,b) X_IMPL2(c) +#define X_IMPL(p1,p2) +#define X_IMPL2(p1) +X + +// CHECK: --- +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC_HOSTED__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __cplusplus +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC_UTF_16__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: __STDC_UTF_32__ +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: MACRO +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroExpands +// CHECK-NEXT: MacroNameTok: MACRO +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:4:9", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:4:9"] +// CHECK-NEXT: Args: (null) +// CHECK-NEXT: - Callback: Defined +// CHECK-NEXT: MacroNameTok: MACRO +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:5", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:19"] +// CHECK-NEXT: - Callback: If +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:2" +// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:4", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:6:1"] +// CHECK-NEXT: ConditionValue: true +// CHECK-NEXT: - Callback: Endif +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:6:2" +// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:5:2" +// CHECK-NEXT: - Callback: MacroUndefined +// CHECK-NEXT: MacroNameTok: MACRO +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: Defined +// CHECK-NEXT: MacroNameTok: MACRO +// CHECK-NEXT: MacroDirective: (null) +// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:5", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:19"] +// CHECK-NEXT: - Callback: If +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2" +// CHECK-NEXT: ConditionRange: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:4", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:1"] +// CHECK-NEXT: ConditionValue: false +// CHECK-NEXT: - Callback: Endif +// CHECK-NEXT: Loc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:2" +// CHECK-NEXT: IfLoc: "{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2" +// CHECK-NEXT: - Callback: SourceRangeSkipped +// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:8:2", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:9:2"] +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: FUNCMACRO +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroExpands +// CHECK-NEXT: MacroNameTok: FUNCMACRO +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:11:9", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:11:20"] +// CHECK-NEXT: Args: [1] +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: X +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: X_IMPL +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroDefined +// CHECK-NEXT: MacroNameTok: X_IMPL2 +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: - Callback: MacroExpands +// CHECK-NEXT: MacroNameTok: X +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: Range: ["{{.*}}{{[/\\]}}pp-trace-macro.cpp:15:1", "{{.*}}{{[/\\]}}pp-trace-macro.cpp:15:1"] +// CHECK-NEXT: Args: (null) +// CHECK-NEXT: - Callback: MacroExpands +// CHECK-NEXT: MacroNameTok: X_IMPL +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: Range: [(nonfile), (nonfile)] +// CHECK-NEXT: Args: [a plus y, b] +// CHECK-NEXT: - Callback: MacroExpands +// CHECK-NEXT: MacroNameTok: X_IMPL2 +// CHECK-NEXT: MacroDirective: MD_Define +// CHECK-NEXT: Range: [(nonfile), (nonfile)] +// CHECK-NEXT: Args: [c] +// CHECK-NEXT: - Callback: EndOfMainFile +// CHECK-NEXT: ...