1 //===- MacroExpansionContext.h - Macro expansion information ----*- C++ -*-===// 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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #ifndef LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H 10 #define LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H 11 12 #include "clang/Basic/LangOptions.h" 13 #include "clang/Basic/SourceLocation.h" 14 #include "clang/Lex/Preprocessor.h" 15 #include "llvm/ADT/DenseMap.h" 16 #include "llvm/ADT/SmallString.h" 17 #include "llvm/ADT/SmallVector.h" 18 #include <optional> 19 20 namespace clang { 21 22 namespace detail { 23 class MacroExpansionRangeRecorder; 24 } // namespace detail 25 26 /// MacroExpansionContext tracks the macro expansions processed by the 27 /// Preprocessor. It means that it can track source locations from a single 28 /// translation unit. For every macro expansion it can tell you what text will 29 /// be substituted. 30 /// 31 /// It was designed to deal with: 32 /// - regular macros 33 /// - macro functions 34 /// - variadic macros 35 /// - transitive macro expansions 36 /// - macro redefinition 37 /// - unbalanced parenthesis 38 /// 39 /// \code{.c} 40 /// void bar(); 41 /// #define retArg(x) x 42 /// #define retArgUnclosed retArg(bar() 43 /// #define BB CC 44 /// #define applyInt BB(int) 45 /// #define CC(x) retArgUnclosed 46 /// 47 /// void unbalancedMacros() { 48 /// applyInt ); 49 /// //^~~~~~~~~~^ is the substituted range 50 /// // Substituted text is "applyInt )" 51 /// // Expanded text is "bar()" 52 /// } 53 /// 54 /// #define expandArgUnclosedCommaExpr(x) (x, bar(), 1 55 /// #define f expandArgUnclosedCommaExpr 56 /// 57 /// void unbalancedMacros2() { 58 /// int x = f(f(1)) )); // Look at the parenthesis! 59 /// // ^~~~~~^ is the substituted range 60 /// // Substituted text is "f(f(1))" 61 /// // Expanded text is "((1,bar(),1,bar(),1" 62 /// } 63 /// \endcode 64 /// \remark Currently we don't respect the whitespaces between expanded tokens, 65 /// so the output for this example might differ from the -E compiler 66 /// invocation. 67 /// \remark All whitespaces are consumed while constructing the expansion. 68 /// After all identifier a single space inserted to produce a valid C 69 /// code even if identifier follows an other identifiers such as 70 /// variable declarations. 71 /// \remark MacroExpansionContext object must outlive the Preprocessor 72 /// parameter. 73 class MacroExpansionContext { 74 public: 75 /// Creates a MacroExpansionContext. 76 /// \remark You must call registerForPreprocessor to set the required 77 /// onTokenLexed callback and the PPCallbacks. 78 explicit MacroExpansionContext(const LangOptions &LangOpts); 79 80 /// Register the necessary callbacks to the Preprocessor to record the 81 /// expansion events and the generated tokens. Must ensure that this object 82 /// outlives the given Preprocessor. 83 void registerForPreprocessor(Preprocessor &PP); 84 85 /// \param MacroExpansionLoc Must be the expansion location of a macro. 86 /// \return The textual representation of the token sequence which was 87 /// substituted in place of the macro after the preprocessing. 88 /// If no macro was expanded at that location, returns std::nullopt. 89 std::optional<StringRef> 90 getExpandedText(SourceLocation MacroExpansionLoc) const; 91 92 /// \param MacroExpansionLoc Must be the expansion location of a macro. 93 /// \return The text from the original source code which were substituted by 94 /// the macro expansion chain from the given location. 95 /// If no macro was expanded at that location, returns std::nullopt. 96 std::optional<StringRef> 97 getOriginalText(SourceLocation MacroExpansionLoc) const; 98 99 LLVM_DUMP_METHOD void dumpExpansionRangesToStream(raw_ostream &OS) const; 100 LLVM_DUMP_METHOD void dumpExpandedTextsToStream(raw_ostream &OS) const; 101 LLVM_DUMP_METHOD void dumpExpansionRanges() const; 102 LLVM_DUMP_METHOD void dumpExpandedTexts() const; 103 104 private: 105 friend class detail::MacroExpansionRangeRecorder; 106 using MacroExpansionText = SmallString<40>; 107 using ExpansionMap = llvm::DenseMap<SourceLocation, MacroExpansionText>; 108 using ExpansionRangeMap = llvm::DenseMap<SourceLocation, SourceLocation>; 109 110 /// Associates the textual representation of the expanded tokens at the given 111 /// macro expansion location. 112 ExpansionMap ExpandedTokens; 113 114 /// Tracks which source location was the last affected by any macro 115 /// substitution starting from a given macro expansion location. 116 ExpansionRangeMap ExpansionRanges; 117 118 Preprocessor *PP = nullptr; 119 SourceManager *SM = nullptr; 120 const LangOptions &LangOpts; 121 122 /// This callback is called by the preprocessor. 123 /// It stores the textual representation of the expanded token sequence for a 124 /// macro expansion location. 125 void onTokenLexed(const Token &Tok); 126 }; 127 } // end namespace clang 128 129 #endif // LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H 130