clang-tools 6.0.0

MacroParenthesesCheck.cpp
Go to the documentation of this file.
1 //===--- MacroParenthesesCheck.cpp - clang-tidy----------------------------===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9
10 #include "MacroParenthesesCheck.h"
11 #include "clang/Frontend/CompilerInstance.h"
12 #include "clang/Lex/PPCallbacks.h"
13 #include "clang/Lex/Preprocessor.h"
14
15 namespace clang {
16 namespace tidy {
17 namespace misc {
18
19 namespace {
20 class MacroParenthesesPPCallbacks : public PPCallbacks {
21 public:
22 MacroParenthesesPPCallbacks(Preprocessor *PP, MacroParenthesesCheck *Check)
23 : PP(PP), Check(Check) {}
24
25 void MacroDefined(const Token &MacroNameTok,
26 const MacroDirective *MD) override {
27 replacementList(MacroNameTok, MD->getMacroInfo());
28 argument(MacroNameTok, MD->getMacroInfo());
29 }
30
31 private:
32 /// Replacement list with calculations should be enclosed in parentheses.
33 void replacementList(const Token &MacroNameTok, const MacroInfo *MI);
34
35 /// Arguments should be enclosed in parentheses.
36 void argument(const Token &MacroNameTok, const MacroInfo *MI);
37
38 Preprocessor *PP;
39 MacroParenthesesCheck *Check;
40 };
41 } // namespace
42
43 /// Is argument surrounded properly with parentheses/braces/squares/commas?
44 static bool isSurroundedLeft(const Token &T) {
45 return T.isOneOf(tok::l_paren, tok::l_brace, tok::l_square, tok::comma,
46 tok::semi);
47 }
48
49 /// Is argument surrounded properly with parentheses/braces/squares/commas?
50 static bool isSurroundedRight(const Token &T) {
51 return T.isOneOf(tok::r_paren, tok::r_brace, tok::r_square, tok::comma,
52 tok::semi);
53 }
54
55 /// Is given TokenKind a keyword?
56 static bool isKeyword(const Token &T) {
57 // FIXME: better matching of keywords to avoid false positives.
58 return T.isOneOf(tok::kw_case, tok::kw_const, tok::kw_struct);
59 }
60
61 /// Warning is written when one of these operators are not within parentheses.
62 static bool isWarnOp(const Token &T) {
63 // FIXME: This is an initial list of operators. It can be tweaked later to
64 // get more positives or perhaps avoid some false positive.
65 return T.isOneOf(tok::plus, tok::minus, tok::star, tok::slash, tok::percent,
66 tok::amp, tok::pipe, tok::caret);
67 }
68
69 /// Is given Token a keyword that is used in variable declarations?
70 static bool isVarDeclKeyword(const Token &T) {
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);
75 }
76
77 /// Is there a possible variable declaration at Tok?
78 static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok) {
79 if (Tok == MI->tokens_end())
80 return false;
81
82 // If we see int/short/struct/etc., just assume this is a variable
83 // declaration.
84 if (isVarDeclKeyword(*Tok))
85 return true;
86
87 // Variable declarations start with identifier or coloncolon.
88 if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon))
89 return false;
90
91 // Skip possible types, etc
92 while (Tok != MI->tokens_end() &&
93 Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon,
94 tok::star, tok::amp, tok::ampamp, tok::less,
95 tok::greater))
96 Tok++;
97
98 // Return true for possible variable declarations.
99 return Tok == MI->tokens_end() ||
100 Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) ||
101 isVarDeclKeyword(*Tok);
102 }
103
104 void MacroParenthesesPPCallbacks::replacementList(const Token &MacroNameTok,
105 const MacroInfo *MI) {
106 // Make sure macro replacement isn't a variable declaration.
107 if (possibleVarDecl(MI, MI->tokens_begin()))
108 return;
109
110 // Count how deep we are in parentheses/braces/squares.
111 int Count = 0;
112
113 // SourceLocation for error
114 SourceLocation Loc;
115
116 for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
117 const Token &Tok = *TI;
118 // Replacement list contains keywords, don't warn about it.
119 if (isKeyword(Tok))
120 return;
121 // When replacement list contains comma/semi don't warn about it.
122 if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
123 return;
124 if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
125 ++Count;
126 } else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
127 --Count;
128 // If there are unbalanced parentheses don't write any warning
129 if (Count < 0)
130 return;
131 } else if (Count == 0 && isWarnOp(Tok)) {
132 // Heuristic for macros that are clearly not intended to be enclosed in
133 // parentheses, macro starts with operator. For example:
134 // #define X *10
135 if (TI == MI->tokens_begin() && (TI + 1) != TE &&
136 !Tok.isOneOf(tok::plus, tok::minus))
137 return;
138 // Don't warn about this macro if the last token is a star. For example:
139 // #define X void *
140 if ((TE - 1)->is(tok::star))
141 return;
142
143 Loc = Tok.getLocation();
144 }
145 }
146 if (Loc.isValid()) {
147 const Token &Last = *(MI->tokens_end() - 1);
148 Check->diag(Loc, "macro replacement list should be enclosed in parentheses")
149 << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(), "(")
150 << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
151 PP->getSpelling(Last).length()),
152 ")");
153 }
154 }
155
156 void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok,
157 const MacroInfo *MI) {
158
159 // Skip variable declaration.
160 bool VarDecl = possibleVarDecl(MI, MI->tokens_begin());
161
162 for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
163 // First token.
164 if (TI == MI->tokens_begin())
165 continue;
166
167 // Last token.
168 if ((TI + 1) == MI->tokens_end())
169 continue;
170
171 const Token &Prev = *(TI - 1);
172 const Token &Next = *(TI + 1);
173
174 const Token &Tok = *TI;
175
176 // There should not be extra parentheses in possible variable declaration.
177 if (VarDecl) {
178 if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren))
179 VarDecl = false;
180 continue;
181 }
182
183 // Only interested in identifiers.
184 if (!Tok.isOneOf(tok::identifier, tok::raw_identifier))
185 continue;
186
187 // Only interested in macro arguments.
188 if (MI->getParameterNum(Tok.getIdentifierInfo()) < 0)
189 continue;
190
191 // Argument is surrounded with parentheses/squares/braces/commas.
192 if (isSurroundedLeft(Prev) && isSurroundedRight(Next))
193 continue;
194
195 // Don't warn after hash/hashhash or before hashhash.
196 if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
197 continue;
198
199 // Argument is a struct member.
200 if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
201 tok::periodstar))
202 continue;
203
204 // Argument is a namespace or class.
205 if (Next.is(tok::coloncolon))
206 continue;
207
208 // String concatenation.
209 if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
210 continue;
211
212 // Type/Var.
213 if (isAnyIdentifier(Prev.getKind()) || isKeyword(Prev) ||
214 isAnyIdentifier(Next.getKind()) || isKeyword(Next))
215 continue;
216
217 // Initialization.
218 if (Next.is(tok::l_paren))
219 continue;
220
221 // Cast.
222 if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
223 TI + 2 != MI->tokens_end() && (TI + 2)->is(tok::r_paren))
224 continue;
225
226 // Assignment/return, i.e. '=x;' or 'return x;'.
227 if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
228 continue;
229
230 // C++ template parameters.
231 if (PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less) &&
232 Next.isOneOf(tok::comma, tok::greater))
233 continue;
234
235 // Namespaces.
236 if (Prev.is(tok::kw_namespace))
237 continue;
238
239 // Variadic templates
240 if (MI->isVariadic())
241 continue;
242
243 Check->diag(Tok.getLocation(), "macro argument should be enclosed in "
244 "parentheses")
245 << FixItHint::CreateInsertion(Tok.getLocation(), "(")
246 << FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
247 PP->getSpelling(Tok).length()),
248 ")");
249 }
250 }
251
252 void MacroParenthesesCheck::registerPPCallbacks(CompilerInstance &Compiler) {
253 Compiler.getPreprocessor().addPPCallbacks(
254 llvm::make_unique<MacroParenthesesPPCallbacks>(
255 &Compiler.getPreprocessor(), this));
256 }
257
258 } // namespace misc
259 } // namespace tidy
260 } // namespace clang
Loc
SourceLocation Loc
&#39;#&#39; location in the include directive
Definition: IncludeOrderCheck.cpp:36
clang::tidy::misc::isSurroundedLeft
static bool isSurroundedLeft(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
Definition: MacroParenthesesCheck.cpp:44
clang::tidy::misc::isKeyword
static bool isKeyword(const Token &T)
Is given TokenKind a keyword?
Definition: MacroParenthesesCheck.cpp:56
MacroParenthesesCheck.h
clang::tidy::misc::isVarDeclKeyword
static bool isVarDeclKeyword(const Token &T)
Is given Token a keyword that is used in variable declarations?
Definition: MacroParenthesesCheck.cpp:70
clang::tidy::misc::isWarnOp
static bool isWarnOp(const Token &T)
Warning is written when one of these operators are not within parentheses.
Definition: MacroParenthesesCheck.cpp:62
clang::tidy::misc::possibleVarDecl
static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok)
Is there a possible variable declaration at Tok?
Definition: MacroParenthesesCheck.cpp:78
PPCallbacks
clang::tidy::misc::MacroParenthesesCheck::registerPPCallbacks
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.
Definition: MacroParenthesesCheck.cpp:252
clang
Definition: AndroidTidyModule.cpp:28
clang::tidy::misc::isSurroundedRight
static bool isSurroundedRight(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
Definition: MacroParenthesesCheck.cpp:50