clang-tools  3.8.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 
18 namespace {
19 class MacroParenthesesPPCallbacks : public PPCallbacks {
20 public:
21  explicit MacroParenthesesPPCallbacks(Preprocessor *PP,
22  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 void MacroParenthesesPPCallbacks::replacementList(const Token &MacroNameTok,
70  const MacroInfo *MI) {
71  // Count how deep we are in parentheses/braces/squares.
72  int Count = 0;
73 
74  // SourceLocation for error
75  SourceLocation Loc;
76 
77  for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
78  const Token &Tok = *TI;
79  // Replacement list contains keywords, don't warn about it.
80  if (isKeyword(Tok))
81  return;
82  // When replacement list contains comma/semi don't warn about it.
83  if (Count == 0 && Tok.isOneOf(tok::comma, tok::semi))
84  return;
85  if (Tok.isOneOf(tok::l_paren, tok::l_brace, tok::l_square)) {
86  ++Count;
87  } else if (Tok.isOneOf(tok::r_paren, tok::r_brace, tok::r_square)) {
88  --Count;
89  // If there are unbalanced parentheses don't write any warning
90  if (Count < 0)
91  return;
92  } else if (Count == 0 && isWarnOp(Tok)) {
93  // Heuristic for macros that are clearly not intended to be enclosed in
94  // parentheses, macro starts with operator. For example:
95  // #define X *10
96  if (TI == MI->tokens_begin() && (TI + 1) != TE &&
97  !Tok.isOneOf(tok::plus, tok::minus))
98  return;
99  // Don't warn about this macro if the last token is a star. For example:
100  // #define X void *
101  if ((TE - 1)->is(tok::star))
102  return;
103 
104  Loc = Tok.getLocation();
105  }
106  }
107  if (Loc.isValid()) {
108  const Token &Last = *(MI->tokens_end() - 1);
109  Check->diag(Loc, "macro replacement list should be enclosed in parentheses")
110  << FixItHint::CreateInsertion(MI->tokens_begin()->getLocation(), "(")
111  << FixItHint::CreateInsertion(Last.getLocation().getLocWithOffset(
112  PP->getSpelling(Last).length()),
113  ")");
114  }
115 }
116 
117 void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok,
118  const MacroInfo *MI) {
119 
120  for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) {
121  // First token.
122  if (TI == MI->tokens_begin())
123  continue;
124 
125  // Last token.
126  if ((TI + 1) == MI->tokens_end())
127  continue;
128 
129  const Token &Prev = *(TI - 1);
130  const Token &Next = *(TI + 1);
131 
132  const Token &Tok = *TI;
133 
134  // Only interested in identifiers.
135  if (!Tok.isOneOf(tok::identifier, tok::raw_identifier))
136  continue;
137 
138  // Only interested in macro arguments.
139  if (MI->getArgumentNum(Tok.getIdentifierInfo()) < 0)
140  continue;
141 
142  // Argument is surrounded with parentheses/squares/braces/commas.
143  if (isSurroundedLeft(Prev) && isSurroundedRight(Next))
144  continue;
145 
146  // Don't warn after hash/hashhash or before hashhash.
147  if (Prev.isOneOf(tok::hash, tok::hashhash) || Next.is(tok::hashhash))
148  continue;
149 
150  // Argument is a struct member.
151  if (Prev.isOneOf(tok::period, tok::arrow, tok::coloncolon, tok::arrowstar,
152  tok::periodstar))
153  continue;
154 
155  // Argument is a namespace or class.
156  if (Next.is(tok::coloncolon))
157  continue;
158 
159  // String concatenation.
160  if (isStringLiteral(Prev.getKind()) || isStringLiteral(Next.getKind()))
161  continue;
162 
163  // Type/Var.
164  if (isAnyIdentifier(Prev.getKind()) || isKeyword(Prev) ||
165  isAnyIdentifier(Next.getKind()) || isKeyword(Next))
166  continue;
167 
168  // Initialization.
169  if (Next.is(tok::l_paren))
170  continue;
171 
172  // Cast.
173  if (Prev.is(tok::l_paren) && Next.is(tok::star) &&
174  TI + 2 != MI->tokens_end() && (TI + 2)->is(tok::r_paren))
175  continue;
176 
177  // Assignment/return, i.e. '=x;' or 'return x;'.
178  if (Prev.isOneOf(tok::equal, tok::kw_return) && Next.is(tok::semi))
179  continue;
180 
181  // C++ template parameters.
182  if (PP->getLangOpts().CPlusPlus && Prev.isOneOf(tok::comma, tok::less) &&
183  Next.isOneOf(tok::comma, tok::greater))
184  continue;
185 
186  Check->diag(Tok.getLocation(), "macro argument should be enclosed in "
187  "parentheses")
188  << FixItHint::CreateInsertion(Tok.getLocation(), "(")
189  << FixItHint::CreateInsertion(Tok.getLocation().getLocWithOffset(
190  PP->getSpelling(Tok).length()),
191  ")");
192  }
193 }
194 
195 void MacroParenthesesCheck::registerPPCallbacks(CompilerInstance &Compiler) {
196  Compiler.getPreprocessor().addPPCallbacks(
197  llvm::make_unique<MacroParenthesesPPCallbacks>(
198  &Compiler.getPreprocessor(), this));
199 }
200 
201 } // namespace tidy
202 } // namespace clang
SourceLocation Loc
'#' location in the include directive
MacroParenthesesCheck * Check
static bool isSurroundedRight(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 isWarnOp(const Token &T)
Warning is written when one of these operators are not within parentheses.
Preprocessor * PP
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidy.cpp:323
static bool isSurroundedLeft(const Token &T)
Is argument surrounded properly with parentheses/braces/squares/commas?
void registerPPCallbacks(CompilerInstance &Compiler) override
Override this to register PPCallbacks with Compiler.