669 lines · cpp
1//===--- PPCallbacksTracker.cpp - Preprocessor tracker -*--*---------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8///9/// \file10/// Implementations for preprocessor tracking.11///12/// See the header for details.13///14//===----------------------------------------------------------------------===//15 16#include "PPCallbacksTracker.h"17#include "clang/Basic/FileManager.h"18#include "clang/Lex/MacroArgs.h"19#include "llvm/Support/raw_ostream.h"20 21namespace clang {22namespace pp_trace {23 24// Get a "file:line:column" source location string.25static std::string getSourceLocationString(Preprocessor &PP,26 SourceLocation Loc) {27 if (Loc.isInvalid())28 return std::string("(none)");29 30 if (Loc.isFileID()) {31 PresumedLoc PLoc = PP.getSourceManager().getPresumedLoc(Loc);32 33 if (PLoc.isInvalid()) {34 return std::string("(invalid)");35 }36 37 std::string Str;38 llvm::raw_string_ostream SS(Str);39 40 // The macro expansion and spelling pos is identical for file locs.41 SS << "\"" << PLoc.getFilename() << ':' << PLoc.getLine() << ':'42 << PLoc.getColumn() << "\"";43 44 std::string Result = SS.str();45 46 // YAML treats backslash as escape, so use forward slashes.47 llvm::replace(Result, '\\', '/');48 49 return Result;50 }51 52 return std::string("(nonfile)");53}54 55// Enum string tables.56 57// FileChangeReason strings.58static const char *const FileChangeReasonStrings[] = {59 "EnterFile", "ExitFile", "SystemHeaderPragma", "RenameFile"60};61 62// CharacteristicKind strings.63static const char *const CharacteristicKindStrings[] = { "C_User", "C_System",64 "C_ExternCSystem" };65 66// MacroDirective::Kind strings.67static const char *const MacroDirectiveKindStrings[] = {68 "MD_Define","MD_Undefine", "MD_Visibility"69};70 71// PragmaIntroducerKind strings.72static const char *const PragmaIntroducerKindStrings[] = { "PIK_HashPragma",73 "PIK__Pragma",74 "PIK___pragma" };75 76// PragmaMessageKind strings.77static const char *const PragmaMessageKindStrings[] = {78 "PMK_Message", "PMK_Warning", "PMK_Error"79};80 81// PragmaWarningSpecifier strings.82static const char *const PragmaWarningSpecifierStrings[] = {83 "PWS_Default", "PWS_Disable", "PWS_Error", "PWS_Once", "PWS_Suppress",84 "PWS_Level1", "PWS_Level2", "PWS_Level3", "PWS_Level4",85};86 87// ConditionValueKind strings.88static const char *const ConditionValueKindStrings[] = {89 "CVK_NotEvaluated", "CVK_False", "CVK_True"90};91 92// Mapping strings.93static const char *const MappingStrings[] = { "0", "MAP_IGNORE",94 "MAP_REMARK", "MAP_WARNING",95 "MAP_ERROR", "MAP_FATAL" };96 97// PPCallbacksTracker functions.98 99PPCallbacksTracker::PPCallbacksTracker(const FilterType &Filters,100 std::vector<CallbackCall> &CallbackCalls,101 Preprocessor &PP)102 : CallbackCalls(CallbackCalls), Filters(Filters), PP(PP) {}103 104PPCallbacksTracker::~PPCallbacksTracker() {}105 106// Callback functions.107 108// Callback invoked whenever a source file is entered or exited.109void PPCallbacksTracker::FileChanged(SourceLocation Loc,110 PPCallbacks::FileChangeReason Reason,111 SrcMgr::CharacteristicKind FileType,112 FileID PrevFID) {113 beginCallback("FileChanged");114 appendArgument("Loc", Loc);115 appendArgument("Reason", Reason, FileChangeReasonStrings);116 appendArgument("FileType", FileType, CharacteristicKindStrings);117 appendArgument("PrevFID", PrevFID);118}119 120// Callback invoked whenever a source file is skipped as the result121// of header guard optimization.122void PPCallbacksTracker::FileSkipped(const FileEntryRef &SkippedFile,123 const Token &FilenameTok,124 SrcMgr::CharacteristicKind FileType) {125 beginCallback("FileSkipped");126 appendArgument("ParentFile", SkippedFile);127 appendArgument("FilenameTok", FilenameTok);128 appendArgument("FileType", FileType, CharacteristicKindStrings);129}130 131// Callback invoked whenever an inclusion directive of132// any kind (#include, #import, etc.) has been processed, regardless133// of whether the inclusion will actually result in an inclusion.134void PPCallbacksTracker::InclusionDirective(135 SourceLocation HashLoc, const Token &IncludeTok, llvm::StringRef FileName,136 bool IsAngled, CharSourceRange FilenameRange, OptionalFileEntryRef File,137 llvm::StringRef SearchPath, llvm::StringRef RelativePath,138 const Module *SuggestedModule, bool ModuleImported,139 SrcMgr::CharacteristicKind FileType) {140 beginCallback("InclusionDirective");141 appendArgument("HashLoc", HashLoc);142 appendArgument("IncludeTok", IncludeTok);143 appendFilePathArgument("FileName", FileName);144 appendArgument("IsAngled", IsAngled);145 appendArgument("FilenameRange", FilenameRange);146 appendArgument("File", File);147 appendFilePathArgument("SearchPath", SearchPath);148 appendFilePathArgument("RelativePath", RelativePath);149 appendArgument("SuggestedModule", SuggestedModule);150 appendArgument("ModuleImported", ModuleImported);151}152 153// Callback invoked whenever there was an explicit module-import154// syntax.155void PPCallbacksTracker::moduleImport(SourceLocation ImportLoc,156 ModuleIdPath Path,157 const Module *Imported) {158 beginCallback("moduleImport");159 appendArgument("ImportLoc", ImportLoc);160 appendArgument("Path", Path);161 appendArgument("Imported", Imported);162}163 164// Callback invoked when the end of the main file is reached.165// No subsequent callbacks will be made.166void PPCallbacksTracker::EndOfMainFile() { beginCallback("EndOfMainFile"); }167 168// Callback invoked when a #ident or #sccs directive is read.169void PPCallbacksTracker::Ident(SourceLocation Loc, llvm::StringRef Str) {170 beginCallback("Ident");171 appendArgument("Loc", Loc);172 appendArgument("Str", Str);173}174 175// Callback invoked when start reading any pragma directive.176void PPCallbacksTracker::PragmaDirective(SourceLocation Loc,177 PragmaIntroducerKind Introducer) {178 beginCallback("PragmaDirective");179 appendArgument("Loc", Loc);180 appendArgument("Introducer", Introducer, PragmaIntroducerKindStrings);181}182 183// Callback invoked when a #pragma comment directive is read.184void PPCallbacksTracker::PragmaComment(SourceLocation Loc,185 const IdentifierInfo *Kind,186 llvm::StringRef Str) {187 beginCallback("PragmaComment");188 appendArgument("Loc", Loc);189 appendArgument("Kind", Kind);190 appendArgument("Str", Str);191}192 193// Callback invoked when a #pragma detect_mismatch directive is194// read.195void PPCallbacksTracker::PragmaDetectMismatch(SourceLocation Loc,196 llvm::StringRef Name,197 llvm::StringRef Value) {198 beginCallback("PragmaDetectMismatch");199 appendArgument("Loc", Loc);200 appendArgument("Name", Name);201 appendArgument("Value", Value);202}203 204// Callback invoked when a #pragma clang __debug directive is read.205void PPCallbacksTracker::PragmaDebug(SourceLocation Loc,206 llvm::StringRef DebugType) {207 beginCallback("PragmaDebug");208 appendArgument("Loc", Loc);209 appendArgument("DebugType", DebugType);210}211 212// Callback invoked when a #pragma message directive is read.213void PPCallbacksTracker::PragmaMessage(SourceLocation Loc,214 llvm::StringRef Namespace,215 PPCallbacks::PragmaMessageKind Kind,216 llvm::StringRef Str) {217 beginCallback("PragmaMessage");218 appendArgument("Loc", Loc);219 appendArgument("Namespace", Namespace);220 appendArgument("Kind", Kind, PragmaMessageKindStrings);221 appendArgument("Str", Str);222}223 224// Callback invoked when a #pragma gcc diagnostic push directive225// is read.226void PPCallbacksTracker::PragmaDiagnosticPush(SourceLocation Loc,227 llvm::StringRef Namespace) {228 beginCallback("PragmaDiagnosticPush");229 appendArgument("Loc", Loc);230 appendArgument("Namespace", Namespace);231}232 233// Callback invoked when a #pragma gcc diagnostic pop directive234// is read.235void PPCallbacksTracker::PragmaDiagnosticPop(SourceLocation Loc,236 llvm::StringRef Namespace) {237 beginCallback("PragmaDiagnosticPop");238 appendArgument("Loc", Loc);239 appendArgument("Namespace", Namespace);240}241 242// Callback invoked when a #pragma gcc diagnostic directive is read.243void PPCallbacksTracker::PragmaDiagnostic(SourceLocation Loc,244 llvm::StringRef Namespace,245 diag::Severity Mapping,246 llvm::StringRef Str) {247 beginCallback("PragmaDiagnostic");248 appendArgument("Loc", Loc);249 appendArgument("Namespace", Namespace);250 appendArgument("Mapping", (unsigned)Mapping, MappingStrings);251 appendArgument("Str", Str);252}253 254// Called when an OpenCL extension is either disabled or255// enabled with a pragma.256void PPCallbacksTracker::PragmaOpenCLExtension(SourceLocation NameLoc,257 const IdentifierInfo *Name,258 SourceLocation StateLoc,259 unsigned State) {260 beginCallback("PragmaOpenCLExtension");261 appendArgument("NameLoc", NameLoc);262 appendArgument("Name", Name);263 appendArgument("StateLoc", StateLoc);264 appendArgument("State", (int)State);265}266 267// Callback invoked when a #pragma warning directive is read.268void PPCallbacksTracker::PragmaWarning(SourceLocation Loc,269 PragmaWarningSpecifier WarningSpec,270 llvm::ArrayRef<int> Ids) {271 beginCallback("PragmaWarning");272 appendArgument("Loc", Loc);273 appendArgument("WarningSpec", WarningSpec, PragmaWarningSpecifierStrings);274 275 std::string Str;276 llvm::raw_string_ostream SS(Str);277 SS << "[";278 for (int i = 0, e = Ids.size(); i != e; ++i) {279 if (i)280 SS << ", ";281 SS << Ids[i];282 }283 SS << "]";284 appendArgument("Ids", SS.str());285}286 287// Callback invoked when a #pragma warning(push) directive is read.288void PPCallbacksTracker::PragmaWarningPush(SourceLocation Loc, int Level) {289 beginCallback("PragmaWarningPush");290 appendArgument("Loc", Loc);291 appendArgument("Level", Level);292}293 294// Callback invoked when a #pragma warning(pop) directive is read.295void PPCallbacksTracker::PragmaWarningPop(SourceLocation Loc) {296 beginCallback("PragmaWarningPop");297 appendArgument("Loc", Loc);298}299 300// Callback invoked when a #pragma execution_character_set(push) directive301// is read.302void PPCallbacksTracker::PragmaExecCharsetPush(SourceLocation Loc,303 StringRef Str) {304 beginCallback("PragmaExecCharsetPush");305 appendArgument("Loc", Loc);306 appendArgument("Charset", Str);307}308 309// Callback invoked when a #pragma execution_character_set(pop) directive310// is read.311void PPCallbacksTracker::PragmaExecCharsetPop(SourceLocation Loc) {312 beginCallback("PragmaExecCharsetPop");313 appendArgument("Loc", Loc);314}315 316// Called by Preprocessor::HandleMacroExpandedIdentifier when a317// macro invocation is found.318void PPCallbacksTracker::MacroExpands(const Token &MacroNameTok,319 const MacroDefinition &MacroDefinition,320 SourceRange Range,321 const MacroArgs *Args) {322 beginCallback("MacroExpands");323 appendArgument("MacroNameTok", MacroNameTok);324 appendArgument("MacroDefinition", MacroDefinition);325 appendArgument("Range", Range);326 appendArgument("Args", Args);327}328 329// Hook called whenever a macro definition is seen.330void PPCallbacksTracker::MacroDefined(const Token &MacroNameTok,331 const MacroDirective *MacroDirective) {332 beginCallback("MacroDefined");333 appendArgument("MacroNameTok", MacroNameTok);334 appendArgument("MacroDirective", MacroDirective);335}336 337// Hook called whenever a macro #undef is seen.338void PPCallbacksTracker::MacroUndefined(const Token &MacroNameTok,339 const MacroDefinition &MacroDefinition,340 const MacroDirective *Undef) {341 beginCallback("MacroUndefined");342 appendArgument("MacroNameTok", MacroNameTok);343 appendArgument("MacroDefinition", MacroDefinition);344}345 346// Hook called whenever the 'defined' operator is seen.347void PPCallbacksTracker::Defined(const Token &MacroNameTok,348 const MacroDefinition &MacroDefinition,349 SourceRange Range) {350 beginCallback("Defined");351 appendArgument("MacroNameTok", MacroNameTok);352 appendArgument("MacroDefinition", MacroDefinition);353 appendArgument("Range", Range);354}355 356// Hook called when a source range is skipped.357void PPCallbacksTracker::SourceRangeSkipped(SourceRange Range,358 SourceLocation EndifLoc) {359 beginCallback("SourceRangeSkipped");360 appendArgument("Range", SourceRange(Range.getBegin(), EndifLoc));361}362 363// Hook called whenever an #if is seen.364void PPCallbacksTracker::If(SourceLocation Loc, SourceRange ConditionRange,365 ConditionValueKind ConditionValue) {366 beginCallback("If");367 appendArgument("Loc", Loc);368 appendArgument("ConditionRange", ConditionRange);369 appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings);370}371 372// Hook called whenever an #elif is seen.373void PPCallbacksTracker::Elif(SourceLocation Loc, SourceRange ConditionRange,374 ConditionValueKind ConditionValue,375 SourceLocation IfLoc) {376 beginCallback("Elif");377 appendArgument("Loc", Loc);378 appendArgument("ConditionRange", ConditionRange);379 appendArgument("ConditionValue", ConditionValue, ConditionValueKindStrings);380 appendArgument("IfLoc", IfLoc);381}382 383// Hook called whenever an #ifdef is seen.384void PPCallbacksTracker::Ifdef(SourceLocation Loc, const Token &MacroNameTok,385 const MacroDefinition &MacroDefinition) {386 beginCallback("Ifdef");387 appendArgument("Loc", Loc);388 appendArgument("MacroNameTok", MacroNameTok);389 appendArgument("MacroDefinition", MacroDefinition);390}391 392// Hook called whenever an #ifndef is seen.393void PPCallbacksTracker::Ifndef(SourceLocation Loc, const Token &MacroNameTok,394 const MacroDefinition &MacroDefinition) {395 beginCallback("Ifndef");396 appendArgument("Loc", Loc);397 appendArgument("MacroNameTok", MacroNameTok);398 appendArgument("MacroDefinition", MacroDefinition);399}400 401// Hook called whenever an #else is seen.402void PPCallbacksTracker::Else(SourceLocation Loc, SourceLocation IfLoc) {403 beginCallback("Else");404 appendArgument("Loc", Loc);405 appendArgument("IfLoc", IfLoc);406}407 408// Hook called whenever an #endif is seen.409void PPCallbacksTracker::Endif(SourceLocation Loc, SourceLocation IfLoc) {410 beginCallback("Endif");411 appendArgument("Loc", Loc);412 appendArgument("IfLoc", IfLoc);413}414 415// Helper functions.416 417// Start a new callback.418void PPCallbacksTracker::beginCallback(const char *Name) {419 auto R = CallbackIsEnabled.try_emplace(Name, false);420 if (R.second) {421 llvm::StringRef N(Name);422 for (const std::pair<llvm::GlobPattern, bool> &Filter : Filters)423 if (Filter.first.match(N))424 R.first->second = Filter.second;425 }426 DisableTrace = !R.first->second;427 if (DisableTrace)428 return;429 CallbackCalls.push_back(CallbackCall(Name));430}431 432// Append a bool argument to the top trace item.433void PPCallbacksTracker::appendArgument(const char *Name, bool Value) {434 appendArgument(Name, (Value ? "true" : "false"));435}436 437// Append an int argument to the top trace item.438void PPCallbacksTracker::appendArgument(const char *Name, int Value) {439 std::string Str;440 llvm::raw_string_ostream SS(Str);441 SS << Value;442 appendArgument(Name, SS.str());443}444 445// Append a string argument to the top trace item.446void PPCallbacksTracker::appendArgument(const char *Name, const char *Value) {447 if (DisableTrace)448 return;449 CallbackCalls.back().Arguments.push_back(Argument{Name, Value});450}451 452// Append a string object argument to the top trace item.453void PPCallbacksTracker::appendArgument(const char *Name,454 llvm::StringRef Value) {455 appendArgument(Name, Value.str());456}457 458// Append a string object argument to the top trace item.459void PPCallbacksTracker::appendArgument(const char *Name,460 const std::string &Value) {461 appendArgument(Name, Value.c_str());462}463 464// Append a token argument to the top trace item.465void PPCallbacksTracker::appendArgument(const char *Name, const Token &Value) {466 appendArgument(Name, PP.getSpelling(Value));467}468 469// Append an enum argument to the top trace item.470void PPCallbacksTracker::appendArgument(const char *Name, int Value,471 const char *const Strings[]) {472 appendArgument(Name, Strings[Value]);473}474 475// Append a FileID argument to the top trace item.476void PPCallbacksTracker::appendArgument(const char *Name, FileID Value) {477 if (Value.isInvalid()) {478 appendArgument(Name, "(invalid)");479 return;480 }481 OptionalFileEntryRef FileEntry =482 PP.getSourceManager().getFileEntryRefForID(Value);483 if (!FileEntry) {484 appendArgument(Name, "(getFileEntryForID failed)");485 return;486 }487 appendFilePathArgument(Name, FileEntry->getName());488}489 490// Append a FileEntry argument to the top trace item.491void PPCallbacksTracker::appendArgument(const char *Name,492 OptionalFileEntryRef Value) {493 if (!Value) {494 appendArgument(Name, "(null)");495 return;496 }497 appendArgument(Name, *Value);498}499 500void PPCallbacksTracker::appendArgument(const char *Name, FileEntryRef Value) {501 appendFilePathArgument(Name, Value.getName());502}503 504// Append a SourceLocation argument to the top trace item.505void PPCallbacksTracker::appendArgument(const char *Name,506 SourceLocation Value) {507 if (Value.isInvalid()) {508 appendArgument(Name, "(invalid)");509 return;510 }511 appendArgument(Name, getSourceLocationString(PP, Value).c_str());512}513 514// Append a SourceRange argument to the top trace item.515void PPCallbacksTracker::appendArgument(const char *Name, SourceRange Value) {516 if (DisableTrace)517 return;518 if (Value.isInvalid()) {519 appendArgument(Name, "(invalid)");520 return;521 }522 std::string Str;523 llvm::raw_string_ostream SS(Str);524 SS << "[" << getSourceLocationString(PP, Value.getBegin()) << ", "525 << getSourceLocationString(PP, Value.getEnd()) << "]";526 appendArgument(Name, SS.str());527}528 529// Append a CharSourceRange argument to the top trace item.530void PPCallbacksTracker::appendArgument(const char *Name,531 CharSourceRange Value) {532 if (Value.isInvalid()) {533 appendArgument(Name, "(invalid)");534 return;535 }536 appendArgument(Name, getSourceString(Value).str().c_str());537}538 539// Append a SourceLocation argument to the top trace item.540void PPCallbacksTracker::appendArgument(const char *Name, ModuleIdPath Value) {541 if (DisableTrace)542 return;543 std::string Str;544 llvm::raw_string_ostream SS(Str);545 SS << "[";546 for (int I = 0, E = Value.size(); I != E; ++I) {547 if (I)548 SS << ", ";549 SS << "{"550 << "Name: " << Value[I].getIdentifierInfo()->getName() << ", "551 << "Loc: " << getSourceLocationString(PP, Value[I].getLoc()) << "}";552 }553 SS << "]";554 appendArgument(Name, SS.str());555}556 557// Append an IdentifierInfo argument to the top trace item.558void PPCallbacksTracker::appendArgument(const char *Name,559 const IdentifierInfo *Value) {560 if (!Value) {561 appendArgument(Name, "(null)");562 return;563 }564 appendArgument(Name, Value->getName().str().c_str());565}566 567// Append a MacroDirective argument to the top trace item.568void PPCallbacksTracker::appendArgument(const char *Name,569 const MacroDirective *Value) {570 if (!Value) {571 appendArgument(Name, "(null)");572 return;573 }574 appendArgument(Name, MacroDirectiveKindStrings[Value->getKind()]);575}576 577// Append a MacroDefinition argument to the top trace item.578void PPCallbacksTracker::appendArgument(const char *Name,579 const MacroDefinition &Value) {580 std::string Str;581 llvm::raw_string_ostream SS(Str);582 SS << "[";583 bool Any = false;584 if (Value.getLocalDirective()) {585 SS << "(local)";586 Any = true;587 }588 for (auto *MM : Value.getModuleMacros()) {589 if (Any) SS << ", ";590 SS << MM->getOwningModule()->getFullModuleName();591 }592 SS << "]";593 appendArgument(Name, SS.str());594}595 596// Append a MacroArgs argument to the top trace item.597void PPCallbacksTracker::appendArgument(const char *Name,598 const MacroArgs *Value) {599 if (!Value) {600 appendArgument(Name, "(null)");601 return;602 }603 std::string Str;604 llvm::raw_string_ostream SS(Str);605 SS << "[";606 607 // Each argument is a series of contiguous Tokens, terminated by a eof.608 // Go through each argument printing tokens until we reach eof.609 for (unsigned I = 0; I < Value->getNumMacroArguments(); ++I) {610 const Token *Current = Value->getUnexpArgument(I);611 if (I)612 SS << ", ";613 bool First = true;614 while (Current->isNot(tok::eof)) {615 if (!First)616 SS << " ";617 // We need to be careful here because the arguments might not be legal in618 // YAML, so we use the token name for anything but identifiers and619 // numeric literals.620 if (Current->isAnyIdentifier() || Current->is(tok::numeric_constant)) {621 SS << PP.getSpelling(*Current);622 } else {623 SS << "<" << Current->getName() << ">";624 }625 ++Current;626 First = false;627 }628 }629 SS << "]";630 appendArgument(Name, SS.str());631}632 633// Append a Module argument to the top trace item.634void PPCallbacksTracker::appendArgument(const char *Name, const Module *Value) {635 if (!Value) {636 appendArgument(Name, "(null)");637 return;638 }639 appendArgument(Name, Value->Name.c_str());640}641 642// Append a double-quoted argument to the top trace item.643void PPCallbacksTracker::appendQuotedArgument(const char *Name,644 const std::string &Value) {645 std::string Str;646 llvm::raw_string_ostream SS(Str);647 SS << "\"" << Value << "\"";648 appendArgument(Name, SS.str());649}650 651// Append a double-quoted file path argument to the top trace item.652void PPCallbacksTracker::appendFilePathArgument(const char *Name,653 llvm::StringRef Value) {654 std::string Path(Value);655 // YAML treats backslash as escape, so use forward slashes.656 llvm::replace(Path, '\\', '/');657 appendQuotedArgument(Name, Path);658}659 660// Get the raw source string of the range.661llvm::StringRef PPCallbacksTracker::getSourceString(CharSourceRange Range) {662 const char *B = PP.getSourceManager().getCharacterData(Range.getBegin());663 const char *E = PP.getSourceManager().getCharacterData(Range.getEnd());664 return llvm::StringRef(B, E - B);665}666 667} // namespace pp_trace668} // namespace clang669