clang 20.0.0git
RewriteMacros.cpp
Go to the documentation of this file.
1//===--- RewriteMacros.cpp - Rewrite macros into their expansions ---------===//
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// This code rewrites macro invocations into their expansions. This gives you
10// a macro expanded file that retains comments and #includes.
11//
12//===----------------------------------------------------------------------===//
13
18#include "llvm/ADT/RewriteBuffer.h"
19#include "llvm/Support/Path.h"
20#include "llvm/Support/raw_ostream.h"
21#include <cstdio>
22#include <memory>
23
24using namespace clang;
25using llvm::RewriteBuffer;
26
27/// isSameToken - Return true if the two specified tokens start have the same
28/// content.
29static bool isSameToken(Token &RawTok, Token &PPTok) {
30 // If two tokens have the same kind and the same identifier info, they are
31 // obviously the same.
32 if (PPTok.getKind() == RawTok.getKind() &&
33 PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo())
34 return true;
35
36 // Otherwise, if they are different but have the same identifier info, they
37 // are also considered to be the same. This allows keywords and raw lexed
38 // identifiers with the same name to be treated the same.
39 if (PPTok.getIdentifierInfo() &&
40 PPTok.getIdentifierInfo() == RawTok.getIdentifierInfo())
41 return true;
42
43 return false;
44}
45
46
47/// GetNextRawTok - Return the next raw token in the stream, skipping over
48/// comments if ReturnComment is false.
49static const Token &GetNextRawTok(const std::vector<Token> &RawTokens,
50 unsigned &CurTok, bool ReturnComment) {
51 assert(CurTok < RawTokens.size() && "Overran eof!");
52
53 // If the client doesn't want comments and we have one, skip it.
54 if (!ReturnComment && RawTokens[CurTok].is(tok::comment))
55 ++CurTok;
56
57 return RawTokens[CurTok++];
58}
59
60
61/// LexRawTokensFromMainFile - Lets all the raw tokens from the main file into
62/// the specified vector.
64 std::vector<Token> &RawTokens) {
66
67 // Create a lexer to lex all the tokens of the main file in raw mode. Even
68 // though it is in raw mode, it will not return comments.
69 llvm::MemoryBufferRef FromFile = SM.getBufferOrFake(SM.getMainFileID());
70 Lexer RawLex(SM.getMainFileID(), FromFile, SM, PP.getLangOpts());
71
72 // Switch on comment lexing because we really do want them.
73 RawLex.SetCommentRetentionState(true);
74
75 Token RawTok;
76 do {
77 RawLex.LexFromRawLexer(RawTok);
78
79 // If we have an identifier with no identifier info for our raw token, look
80 // up the identifier info. This is important for equality comparison of
81 // identifier tokens.
82 if (RawTok.is(tok::raw_identifier))
83 PP.LookUpIdentifierInfo(RawTok);
84
85 RawTokens.push_back(RawTok);
86 } while (RawTok.isNot(tok::eof));
87}
88
89
90/// RewriteMacrosInInput - Implement -rewrite-macros mode.
91void clang::RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS) {
93
95 Rewrite.setSourceMgr(SM, PP.getLangOpts());
96 RewriteBuffer &RB = Rewrite.getEditBuffer(SM.getMainFileID());
97
98 std::vector<Token> RawTokens;
99 LexRawTokensFromMainFile(PP, RawTokens);
100 unsigned CurRawTok = 0;
101 Token RawTok = GetNextRawTok(RawTokens, CurRawTok, false);
102
103
104 // Get the first preprocessing token.
106 Token PPTok;
107 PP.Lex(PPTok);
108
109 // Preprocess the input file in parallel with raw lexing the main file. Ignore
110 // all tokens that are preprocessed from a file other than the main file (e.g.
111 // a header). If we see tokens that are in the preprocessed file but not the
112 // lexed file, we have a macro expansion. If we see tokens in the lexed file
113 // that aren't in the preprocessed view, we have macros that expand to no
114 // tokens, or macro arguments etc.
115 while (RawTok.isNot(tok::eof) || PPTok.isNot(tok::eof)) {
116 SourceLocation PPLoc = SM.getExpansionLoc(PPTok.getLocation());
117
118 // If PPTok is from a different source file, ignore it.
119 if (!SM.isWrittenInMainFile(PPLoc)) {
120 PP.Lex(PPTok);
121 continue;
122 }
123
124 // If the raw file hits a preprocessor directive, they will be extra tokens
125 // in the raw file that don't exist in the preprocsesed file. However, we
126 // choose to preserve them in the output file and otherwise handle them
127 // specially.
128 if (RawTok.is(tok::hash) && RawTok.isAtStartOfLine()) {
129 // If this is a #warning directive or #pragma mark (GNU extensions),
130 // comment the line out.
131 if (RawTokens[CurRawTok].is(tok::identifier)) {
132 const IdentifierInfo *II = RawTokens[CurRawTok].getIdentifierInfo();
133 if (II->getName() == "warning") {
134 // Comment out #warning.
135 RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//");
136 } else if (II->getName() == "pragma" &&
137 RawTokens[CurRawTok+1].is(tok::identifier) &&
138 (RawTokens[CurRawTok+1].getIdentifierInfo()->getName() ==
139 "mark")) {
140 // Comment out #pragma mark.
141 RB.InsertTextAfter(SM.getFileOffset(RawTok.getLocation()), "//");
142 }
143 }
144
145 // Otherwise, if this is a #include or some other directive, just leave it
146 // in the file by skipping over the line.
147 RawTok = GetNextRawTok(RawTokens, CurRawTok, false);
148 while (!RawTok.isAtStartOfLine() && RawTok.isNot(tok::eof))
149 RawTok = GetNextRawTok(RawTokens, CurRawTok, false);
150 continue;
151 }
152
153 // Okay, both tokens are from the same file. Get their offsets from the
154 // start of the file.
155 unsigned PPOffs = SM.getFileOffset(PPLoc);
156 unsigned RawOffs = SM.getFileOffset(RawTok.getLocation());
157
158 // If the offsets are the same and the token kind is the same, ignore them.
159 if (PPOffs == RawOffs && isSameToken(RawTok, PPTok)) {
160 RawTok = GetNextRawTok(RawTokens, CurRawTok, false);
161 PP.Lex(PPTok);
162 continue;
163 }
164
165 // If the PP token is farther along than the raw token, something was
166 // deleted. Comment out the raw token.
167 if (RawOffs <= PPOffs) {
168 // Comment out a whole run of tokens instead of bracketing each one with
169 // comments. Add a leading space if RawTok didn't have one.
170 bool HasSpace = RawTok.hasLeadingSpace();
171 RB.InsertTextAfter(RawOffs, &" /*"[HasSpace]);
172 unsigned EndPos;
173
174 do {
175 EndPos = RawOffs+RawTok.getLength();
176
177 RawTok = GetNextRawTok(RawTokens, CurRawTok, true);
178 RawOffs = SM.getFileOffset(RawTok.getLocation());
179
180 if (RawTok.is(tok::comment)) {
181 // Skip past the comment.
182 RawTok = GetNextRawTok(RawTokens, CurRawTok, false);
183 break;
184 }
185
186 } while (RawOffs <= PPOffs && !RawTok.isAtStartOfLine() &&
187 (PPOffs != RawOffs || !isSameToken(RawTok, PPTok)));
188
189 RB.InsertTextBefore(EndPos, "*/");
190 continue;
191 }
192
193 // Otherwise, there was a replacement an expansion. Insert the new token
194 // in the output buffer. Insert the whole run of new tokens at once to get
195 // them in the right order.
196 unsigned InsertPos = PPOffs;
197 std::string Expansion;
198 while (PPOffs < RawOffs) {
199 Expansion += ' ' + PP.getSpelling(PPTok);
200 PP.Lex(PPTok);
201 PPLoc = SM.getExpansionLoc(PPTok.getLocation());
202 PPOffs = SM.getFileOffset(PPLoc);
203 }
204 Expansion += ' ';
205 RB.InsertTextBefore(InsertPos, Expansion);
206 }
207
208 // Get the buffer corresponding to MainFileID. If we haven't changed it, then
209 // we are done.
210 if (const RewriteBuffer *RewriteBuf =
211 Rewrite.getRewriteBufferFor(SM.getMainFileID())) {
212 //printf("Changed:\n");
213 *OS << std::string(RewriteBuf->begin(), RewriteBuf->end());
214 } else {
215 fprintf(stderr, "No changes\n");
216 }
217 OS->flush();
218}
#define SM(sm)
Definition: Cuda.cpp:84
Defines the clang::Preprocessor interface.
static bool isSameToken(Token &RawTok, Token &PPTok)
isSameToken - Return true if the two specified tokens start have the same content.
static const Token & GetNextRawTok(const std::vector< Token > &RawTokens, unsigned &CurTok, bool ReturnComment)
GetNextRawTok - Return the next raw token in the stream, skipping over comments if ReturnComment is f...
static void LexRawTokensFromMainFile(Preprocessor &PP, std::vector< Token > &RawTokens)
LexRawTokensFromMainFile - Lets all the raw tokens from the main file into the specified vector.
Defines the SourceManager interface.
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
Lexer - This provides a simple interface that turns a text buffer into a stream of tokens.
Definition: Lexer.h:78
bool LexFromRawLexer(Token &Result)
LexFromRawLexer - Lex a token from a designated raw lexer (one with no associated preprocessor object...
Definition: Lexer.h:236
void SetCommentRetentionState(bool Mode)
SetCommentRetentionMode - Change the comment retention mode of the lexer to the specified mode.
Definition: Lexer.h:269
Engages in a tight little dance with the lexer to efficiently preprocess tokens.
Definition: Preprocessor.h:138
IdentifierInfo * LookUpIdentifierInfo(Token &Identifier) const
Given a tok::raw_identifier token, look up the identifier information for the token and install it in...
void Lex(Token &Result)
Lex the next token for this preprocessor.
void EnterMainSourceFile()
Enter the specified FileID as the main source file, which implicitly adds the builtin defines etc.
SourceManager & getSourceManager() const
StringRef getSpelling(SourceLocation loc, SmallVectorImpl< char > &buffer, bool *invalid=nullptr) const
Return the 'spelling' of the token at the given location; does not go up to the spelling location or ...
const LangOptions & getLangOpts() const
Rewriter - This is the main interface to the rewrite buffers.
Definition: Rewriter.h:32
Encodes a location in the source.
This class handles loading and caching of source files into memory.
Token - This structure provides full information about a lexed token.
Definition: Token.h:36
IdentifierInfo * getIdentifierInfo() const
Definition: Token.h:187
SourceLocation getLocation() const
Return a source location identifier for the specified offset in the current file.
Definition: Token.h:132
unsigned getLength() const
Definition: Token.h:135
bool is(tok::TokenKind K) const
is/isNot - Predicates to check if this token is a specific kind, as in "if (Tok.is(tok::l_brace)) {....
Definition: Token.h:99
tok::TokenKind getKind() const
Definition: Token.h:94
bool isAtStartOfLine() const
isAtStartOfLine - Return true if this token is at the start of a line.
Definition: Token.h:276
bool hasLeadingSpace() const
Return true if this token has whitespace before it.
Definition: Token.h:280
bool isNot(tok::TokenKind K) const
Definition: Token.h:100
The JSON file list parser is used to communicate input to InstallAPI.
@ Rewrite
We are substituting template parameters for (typically) other template parameters in order to rewrite...
void RewriteMacrosInInput(Preprocessor &PP, raw_ostream *OS)
RewriteMacrosInInput - Implement -rewrite-macros mode.