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/Optional.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/SmallVector.h"
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 llvm::None.
89   Optional<StringRef> getExpandedText(SourceLocation MacroExpansionLoc) const;
90 
91   /// \param MacroExpansionLoc Must be the expansion location of a macro.
92   /// \return The text from the original source code which were substituted by
93   ///         the macro expansion chain from the given location.
94   ///         If no macro was expanded at that location, returns llvm::None.
95   Optional<StringRef> getOriginalText(SourceLocation MacroExpansionLoc) const;
96 
97   LLVM_DUMP_METHOD void dumpExpansionRangesToStream(raw_ostream &OS) const;
98   LLVM_DUMP_METHOD void dumpExpandedTextsToStream(raw_ostream &OS) const;
99   LLVM_DUMP_METHOD void dumpExpansionRanges() const;
100   LLVM_DUMP_METHOD void dumpExpandedTexts() const;
101 
102 private:
103   friend class detail::MacroExpansionRangeRecorder;
104   using MacroExpansionText = SmallString<40>;
105   using ExpansionMap = llvm::DenseMap<SourceLocation, MacroExpansionText>;
106   using ExpansionRangeMap = llvm::DenseMap<SourceLocation, SourceLocation>;
107 
108   /// Associates the textual representation of the expanded tokens at the given
109   /// macro expansion location.
110   ExpansionMap ExpandedTokens;
111 
112   /// Tracks which source location was the last affected by any macro
113   /// substitution starting from a given macro expansion location.
114   ExpansionRangeMap ExpansionRanges;
115 
116   Preprocessor *PP = nullptr;
117   SourceManager *SM = nullptr;
118   const LangOptions &LangOpts;
119 
120   /// This callback is called by the preprocessor.
121   /// It stores the textual representation of the expanded token sequence for a
122   /// macro expansion location.
123   void onTokenLexed(const Token &Tok);
124 };
125 } // end namespace clang
126 
127 #endif // LLVM_CLANG_ANALYSIS_MACROEXPANSIONCONTEXT_H
128