#pragma once #ifdef __GNUC__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wunused-parameter" #endif //===- MacroExpansionContext.h - Macro expansion information ----*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #ifndef LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H #define LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Lex/Preprocessor.h" #include "llvm/ADT/DenseMap.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include namespace clang { namespace detail { class MacroExpansionRangeRecorder; } // namespace detail /// MacroExpansionContext tracks the macro expansions processed by the /// Preprocessor. It means that it can track source locations from a single /// translation unit. For every macro expansion it can tell you what text will /// be substituted. /// /// It was designed to deal with: /// - regular macros /// - macro functions /// - variadic macros /// - transitive macro expansions /// - macro redefinition /// - unbalanced parenthesis /// /// \code{.c} /// void bar(); /// #define retArg(x) x /// #define retArgUnclosed retArg(bar() /// #define BB CC /// #define applyInt BB(int) /// #define CC(x) retArgUnclosed /// /// void unbalancedMacros() { /// applyInt ); /// //^~~~~~~~~~^ is the substituted range /// // Substituted text is "applyInt )" /// // Expanded text is "bar()" /// } /// /// #define expandArgUnclosedCommaExpr(x) (x, bar(), 1 /// #define f expandArgUnclosedCommaExpr /// /// void unbalancedMacros2() { /// int x = f(f(1)) )); // Look at the parenthesis! /// // ^~~~~~^ is the substituted range /// // Substituted text is "f(f(1))" /// // Expanded text is "((1,bar(),1,bar(),1" /// } /// \endcode /// \remark Currently we don't respect the whitespaces between expanded tokens, /// so the output for this example might differ from the -E compiler /// invocation. /// \remark All whitespaces are consumed while constructing the expansion. /// After all identifier a single space inserted to produce a valid C /// code even if identifier follows an other identifiers such as /// variable declarations. /// \remark MacroExpansionContext object must outlive the Preprocessor /// parameter. class MacroExpansionContext { public: /// Creates a MacroExpansionContext. /// \remark You must call registerForPreprocessor to set the required /// onTokenLexed callback and the PPCallbacks. explicit MacroExpansionContext(const LangOptions &LangOpts); /// Register the necessary callbacks to the Preprocessor to record the /// expansion events and the generated tokens. Must ensure that this object /// outlives the given Preprocessor. void registerForPreprocessor(Preprocessor &PP); /// \param MacroExpansionLoc Must be the expansion location of a macro. /// \return The textual representation of the token sequence which was /// substituted in place of the macro after the preprocessing. /// If no macro was expanded at that location, returns std::nullopt. std::optional getExpandedText(SourceLocation MacroExpansionLoc) const; /// \param MacroExpansionLoc Must be the expansion location of a macro. /// \return The text from the original source code which were substituted by /// the macro expansion chain from the given location. /// If no macro was expanded at that location, returns std::nullopt. std::optional getOriginalText(SourceLocation MacroExpansionLoc) const; LLVM_DUMP_METHOD void dumpExpansionRangesToStream(raw_ostream &OS) const; LLVM_DUMP_METHOD void dumpExpandedTextsToStream(raw_ostream &OS) const; LLVM_DUMP_METHOD void dumpExpansionRanges() const; LLVM_DUMP_METHOD void dumpExpandedTexts() const; private: friend class detail::MacroExpansionRangeRecorder; using MacroExpansionText = SmallString<40>; using ExpansionMap = llvm::DenseMap; using ExpansionRangeMap = llvm::DenseMap; /// Associates the textual representation of the expanded tokens at the given /// macro expansion location. ExpansionMap ExpandedTokens; /// Tracks which source location was the last affected by any macro /// substitution starting from a given macro expansion location. ExpansionRangeMap ExpansionRanges; Preprocessor *PP = nullptr; SourceManager *SM = nullptr; const LangOptions &LangOpts; /// This callback is called by the preprocessor. /// It stores the textual representation of the expanded token sequence for a /// macro expansion location. void onTokenLexed(const Token &Tok); }; } // end namespace clang #endif // LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H #ifdef __GNUC__ #pragma GCC diagnostic pop #endif