11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Lex/PPCallbacks.h"
13 #include "clang/Lex/Preprocessor.h"
20 class MacroParenthesesPPCallbacks :
public PPCallbacks {
22 MacroParenthesesPPCallbacks(Preprocessor *
PP, MacroParenthesesCheck *
Check)
23 : PP(PP), Check(Check) {}
25 void MacroDefined(
const Token &MacroNameTok,
26 const MacroDirective *MD)
override {
27 replacementList(MacroNameTok, MD->getMacroInfo());
28 argument(MacroNameTok, MD->getMacroInfo());
33 void replacementList(
const Token &MacroNameTok,
const MacroInfo *MI);
36 void argument(
const Token &MacroNameTok,
const MacroInfo *MI);
45 return T.isOneOf(tok::l_paren, tok::l_brace, tok::l_square, tok::comma,
51 return T.isOneOf(tok::r_paren, tok::r_brace, tok::r_square, tok::comma,
58 return T.isOneOf(tok::kw_case, tok::kw_const, tok::kw_struct);
65 return T.isOneOf(tok::plus, tok::minus, tok::star, tok::slash, tok::percent,
66 tok::amp, tok::pipe, tok::caret);
71 return T.isOneOf(tok::kw_bool, tok::kw_char, tok::kw_short, tok::kw_int,
72 tok::kw_long, tok::kw_float, tok::kw_double, tok::kw_const,
73 tok::kw_enum, tok::kw_inline, tok::kw_static, tok::kw_struct,
74 tok::kw_signed, tok::kw_unsigned);
79 if (Tok == MI->tokens_end())
87 if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon))
92 Tok != MI->tokens_end() &&
93 Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon,
94 tok::star, tok::amp, tok::ampamp, tok::less, tok::greater))
98 return Tok == MI->tokens_end() ||
99 Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) ||
103 void MacroParenthesesPPCallbacks::replacementList(
const Token &MacroNameTok,
104 const MacroInfo *MI) {
115 for (
auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
116 const Token &Tok = *TI;
121 if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
123 if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
125 }
else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
130 }
else if (Count == 0 &&
isWarnOp(Tok)) {
134 if (TI == MI->tokens_begin() && (TI + 1) != TE &&
135 !Tok.isOneOf(tok::plus, tok::minus))
139 if ((TE - 1)->is(tok::star))
142 Loc = Tok.getLocation();
146 const Token &Last = *(MI->tokens_end() - 1);
147 Check->
diag(Loc,
"macro replacement list should be enclosed in parentheses")
148 << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(),
"(")
149 << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
150 PP->getSpelling(Last).length()),
155 void MacroParenthesesPPCallbacks::argument(
const Token &MacroNameTok,
156 const MacroInfo *MI) {
161 for (
auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
163 if (TI == MI->tokens_begin())
167 if ((TI + 1) == MI->tokens_end())
170 const Token &Prev = *(TI - 1);
171 const Token &Next = *(TI + 1);
173 const Token &Tok = *TI;
177 if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren))
183 if (!Tok.isOneOf(tok::identifier, tok::raw_identifier))
187 if (MI->getArgumentNum(Tok.getIdentifierInfo()) < 0)
195 if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
199 if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
204 if (Next.is(tok::coloncolon))
208 if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
212 if (isAnyIdentifier(Prev.getKind()) ||
isKeyword(Prev) ||
213 isAnyIdentifier(Next.getKind()) ||
isKeyword(Next))
217 if (Next.is(tok::l_paren))
221 if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
222 TI + 2 != MI->tokens_end() && (TI + 2)->is(tok::r_paren))
226 if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
230 if (
PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less) &&
231 Next.isOneOf(tok::comma, tok::greater))
235 if (Prev.is(tok::kw_namespace))
239 if (MI->isVariadic())
242 Check->
diag(Tok.getLocation(),
"macro argument should be enclosed in "
244 << FixItHint::CreateInsertion(Tok.getLocation(),
"(")
245 << FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
246 PP->getSpelling(Tok).length()),
252 Compiler.getPreprocessor().addPPCallbacks(
253 llvm::make_unique<MacroParenthesesPPCallbacks>(
254 &Compiler.getPreprocessor(),
this));
SourceLocation Loc
'#' location in the include directive
MacroParenthesesCheck * Check
static bool isSurroundedLeft(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
static bool isKeyword(const Token &T)
Is given TokenKind a keyword?
static bool isVarDeclKeyword(const Token &T)
Is given Token a keyword that is used in variable declarations?
static bool isWarnOp(const Token &T)
Warning is written when one of these operators are not within parentheses.
static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok)
Is there a possible variable declaration at Tok?
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.
static bool isSurroundedRight(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.