Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line | 
|---|---|---|---|
| 14 | pmbaty | 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 |