Index: clang-tools-extra/trunk/clang-tidy/misc/MacroParenthesesCheck.cpp =================================================================== --- clang-tools-extra/trunk/clang-tidy/misc/MacroParenthesesCheck.cpp +++ clang-tools-extra/trunk/clang-tidy/misc/MacroParenthesesCheck.cpp @@ -19,8 +19,7 @@ namespace { class MacroParenthesesPPCallbacks : public PPCallbacks { public: - MacroParenthesesPPCallbacks(Preprocessor *PP, - MacroParenthesesCheck *Check) + MacroParenthesesPPCallbacks(Preprocessor *PP, MacroParenthesesCheck *Check) : PP(PP), Check(Check) {} void MacroDefined(const Token &MacroNameTok, @@ -67,8 +66,46 @@ tok::amp, tok::pipe, tok::caret); } +/// Is given Token a keyword that is used in variable declarations? +static bool isVarDeclKeyword(const Token &T) { + return T.isOneOf(tok::kw_bool, tok::kw_char, tok::kw_short, tok::kw_int, + tok::kw_long, tok::kw_float, tok::kw_double, tok::kw_const, + tok::kw_enum, tok::kw_inline, tok::kw_static, tok::kw_struct, + tok::kw_signed, tok::kw_unsigned); +} + +/// Is there a possible variable declaration at Tok? +static bool possibleVarDecl(const MacroInfo *MI, const Token *Tok) { + if (Tok == MI->tokens_end()) + return false; + + // If we see int/short/struct/etc., just assume this is a variable declaration. + if (isVarDeclKeyword(*Tok)) + return true; + + // Variable declarations start with identifier or coloncolon. + if (!Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon)) + return false; + + // Skip possible types, etc + while ( + Tok != MI->tokens_end() && + Tok->isOneOf(tok::identifier, tok::raw_identifier, tok::coloncolon, + tok::star, tok::amp, tok::ampamp, tok::less, tok::greater)) + Tok++; + + // Return true for possible variable declarations. + return Tok == MI->tokens_end() || + Tok->isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren) || + isVarDeclKeyword(*Tok); +} + void MacroParenthesesPPCallbacks::replacementList(const Token &MacroNameTok, const MacroInfo *MI) { + // Make sure macro replacement isn't a variable declaration. + if (possibleVarDecl(MI, MI->tokens_begin())) + return; + // Count how deep we are in parentheses/braces/squares. int Count = 0; @@ -117,6 +154,9 @@ void MacroParenthesesPPCallbacks::argument(const Token &MacroNameTok, const MacroInfo *MI) { + + // Skip variable declaration. + bool VarDecl = possibleVarDecl(MI, MI->tokens_begin()); for (auto TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; ++TI) { // First token. @@ -132,6 +172,13 @@ const Token &Tok = *TI; + // There should not be extra parentheses in possible variable declaration. + if (VarDecl) { + if (Tok.isOneOf(tok::equal, tok::semi, tok::l_square, tok::l_paren)) + VarDecl = false; + continue; + } + // Only interested in identifiers. if (!Tok.isOneOf(tok::identifier, tok::raw_identifier)) continue; Index: clang-tools-extra/trunk/test/clang-tidy/misc-macro-parentheses.cpp =================================================================== --- clang-tools-extra/trunk/test/clang-tidy/misc-macro-parentheses.cpp +++ clang-tools-extra/trunk/test/clang-tidy/misc-macro-parentheses.cpp @@ -8,6 +8,8 @@ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses] #define BAD4(x) ((unsigned char)(x & 0xff)) // CHECK-MESSAGES: :[[@LINE-1]]:44: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses] +#define BAD5(X) A*B=(C*)X+2 +// CHECK-MESSAGES: :[[@LINE-1]]:35: warning: macro argument should be enclosed in parentheses [misc-macro-parentheses] #define GOOD1 1 #define GOOD2 (1+2) @@ -39,6 +41,8 @@ #define GOOD28(x) namespace x {int b;} #define GOOD29(...) std::cout << __VA_ARGS__; #define GOOD30(args...) std::cout << args; +#define GOOD31(X) A*X=2 +#define GOOD32(X) std::vector // These are allowed for now.. #define MAYBE1 *12.34