10#include "clang/Frontend/CompilerInstance.h"
11#include "clang/Lex/PPCallbacks.h"
12#include "clang/Lex/Preprocessor.h"
17class MacroParenthesesPPCallbacks :
public PPCallbacks {
19 MacroParenthesesPPCallbacks(Preprocessor *PP, MacroParenthesesCheck *Check)
20 : PP(PP), Check(Check) {}
22 void MacroDefined(
const Token &MacroNameTok,
23 const MacroDirective *MD)
override {
24 replacementList(MacroNameTok, MD->getMacroInfo());
25 argument(MacroNameTok, MD->getMacroInfo());
30 void replacementList(
const Token &MacroNameTok,
const MacroInfo *MI);
33 void argument(
const Token &MacroNameTok,
const MacroInfo *MI);
36 MacroParenthesesCheck *Check;
42 return T.isOneOf(tok::l_paren, tok::l_brace, tok::l_square, tok::comma,
48 return T.isOneOf(tok::r_paren, tok::r_brace, tok::r_square, tok::comma,
55 return T.isOneOf(tok::kw_if, tok::kw_case, tok::kw_const, tok::kw_volatile,
63 return T.isOneOf(tok::plus, tok::minus, tok::star, tok::slash, tok::percent,
64 tok::amp, tok::pipe, tok::caret);
69 return T.isOneOf(tok::kw_bool, tok::kw_char, tok::kw_short, tok::kw_int,
70 tok::kw_long, tok::kw_float, tok::kw_double, tok::kw_const,
71 tok::kw_enum, tok::kw_inline, tok::kw_static, tok::kw_struct,
72 tok::kw_signed, tok::kw_unsigned);
77 if (Tok == MI->tokens_end())
86 if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon))
90 while (Tok != MI->tokens_end() &&
91 Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon,
92 tok::star, tok::amp, tok::ampamp, tok::less,
97 return Tok == MI->tokens_end() ||
98 Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) ||
103 if (MI->tokens_empty())
105 return Lexer::getSourceText(
106 CharSourceRange::getTokenRange(MI->tokens_begin()->getLocation(),
107 MI->tokens().back().getLocation()),
108 PP->getSourceManager(),
PP->getLangOpts());
111void MacroParenthesesPPCallbacks::replacementList(
const Token &MacroNameTok,
112 const MacroInfo *MI) {
114 if (possibleVarDecl(MI, MI->tokens_begin()))
123 for (
auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
124 const Token &Tok = *TI;
129 if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
131 if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
133 }
else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
138 }
else if (Count == 0 && isWarnOp(Tok)) {
142 if (TI == MI->tokens_begin() && std::next(TI) != TE &&
143 !Tok.isOneOf(tok::plus, tok::minus))
147 if (std::prev(TE)->is(tok::star))
150 Loc = Tok.getLocation();
154 const Token &Last = *std::prev(MI->tokens_end());
155 if (PP->getSourceManager().isWrittenInCommandLineFile(Loc)) {
156 Check->diag(Loc,
"macro replacement list should be enclosed in "
157 "parentheses; macro '%0' defined as '%1'")
158 << PP->getSpelling(MacroNameTok) <<
getMacroText(MI, PP)
159 << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(),
"(")
160 << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
161 PP->getSpelling(Last).length()),
165 "macro replacement list should be enclosed in parentheses")
166 << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(),
"(")
167 << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
168 PP->getSpelling(Last).length()),
174void MacroParenthesesPPCallbacks::argument(
const Token &MacroNameTok,
175 const MacroInfo *MI) {
180 bool FoundGoto =
false;
182 for (
auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
184 if (TI == MI->tokens_begin())
188 if (std::next(TI) == MI->tokens_end())
191 const Token &Prev = *std::prev(TI);
192 const Token &Next = *std::next(TI);
194 const Token &Tok = *TI;
198 if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren))
204 if (Tok.is(tok::kw_goto)) {
210 if (!Tok.isOneOf(tok::identifier, tok::raw_identifier)) {
216 if (MI->getParameterNum(Tok.getIdentifierInfo()) < 0)
224 if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
228 if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
233 if (Next.is(tok::coloncolon))
237 if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
241 if (isAnyIdentifier(Prev.getKind()) ||
isKeyword(Prev) ||
242 isAnyIdentifier(Next.getKind()) ||
isKeyword(Next))
246 if (Next.is(tok::l_paren))
250 if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
251 std::next(TI, 2) != MI->tokens_end() &&
252 std::next(TI, 2)->is(tok::r_paren))
256 if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
260 if (PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less)) {
262 std::find_if_not(std::next(TI), MI->tokens_end(), [](
const Token &T) {
263 return T.isOneOf(tok::star, tok::amp, tok::ampamp, tok::kw_const,
267 if (NextIt != MI->tokens_end() &&
268 NextIt->isOneOf(tok::comma, tok::greater))
273 if (Prev.is(tok::kw_namespace))
277 if (MI->isVariadic())
281 Check->diag(Tok.getLocation(),
"macro argument should be enclosed in "
283 << FixItHint::CreateInsertion(Tok.getLocation(),
"(")
284 << FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
285 PP->getSpelling(Tok).length()),
294 const SourceManager &SM, Preprocessor *
PP, Preprocessor *ModuleExpanderPP) {
295 PP->addPPCallbacks(std::make_unique<MacroParenthesesPPCallbacks>(
PP,
this));
void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) override
static bool isKeyword(const Token &T)
Is given TokenKind a keyword?
static bool isSurroundedRight(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
static StringRef getMacroText(const MacroInfo *MI, Preprocessor *PP)
static bool isWarnOp(const Token &T)
Warning is written when one of these operators are not within parentheses.
static bool isVarDeclKeyword(const Token &T)
Is given Token a keyword that is used in variable declarations?
static bool isSurroundedLeft(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok)
Is there a possible variable declaration at Tok?