Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -7,6 +7,7 @@ BoolPointerImplicitConversionCheck.cpp InaccurateEraseCheck.cpp InefficientAlgorithmCheck.cpp + MacroRepeatedSideEffectsCheck.cpp MiscTidyModule.cpp StaticAssertCheck.cpp SwappedArgumentsCheck.cpp Index: clang-tidy/misc/MacroRepeatedSideEffectsCheck.h =================================================================== --- clang-tidy/misc/MacroRepeatedSideEffectsCheck.h +++ clang-tidy/misc/MacroRepeatedSideEffectsCheck.h @@ -0,0 +1,32 @@ +//===--- MacroRepeatedSideEffectsCheck.h - clang-tidy -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// \brief Checks for repeated side effects in macros. +/// +class MacroRepeatedSideEffectsCheck : public ClangTidyCheck { +public: + MacroRepeatedSideEffectsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerPPCallbacks(CompilerInstance &Compiler) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_MACRO_REPEATED_SIDE_EFFECTS_CHECK_H Index: clang-tidy/misc/MacroRepeatedSideEffectsCheck.cpp =================================================================== --- clang-tidy/misc/MacroRepeatedSideEffectsCheck.cpp +++ clang-tidy/misc/MacroRepeatedSideEffectsCheck.cpp @@ -0,0 +1,100 @@ +//===--- MacroRepeatedSideEffectsCheck.cpp - clang-tidy--------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include "MacroRepeatedSideEffectsCheck.h" +#include "clang/Frontend/CompilerInstance.h" +#include "clang/Lex/PPCallbacks.h" +#include "clang/Lex/Preprocessor.h" +#include "clang/Lex/MacroArgs.h" + +namespace clang { +namespace tidy { +namespace misc { + +namespace { +class MacroRepeatedPPCallbacks : public PPCallbacks { +public: + MacroRepeatedPPCallbacks(ClangTidyCheck &Check, SourceManager &SM, + Preprocessor &PP) + : Check(Check), SM(SM), PP(PP) {} + + void MacroExpands(const Token &MacroNameTok, const MacroDefinition &MD, + SourceRange Range, const MacroArgs *Args) override; + +private: + ClangTidyCheck &Check; + SourceManager &SM; + Preprocessor &PP; +}; +} // namespace + +void MacroRepeatedPPCallbacks::MacroExpands(const Token &MacroNameTok, + const MacroDefinition &MD, + SourceRange Range, + const MacroArgs *Args) { + clang::SourceLocation Loc = Range.getBegin(); + // Ignore macro argument expansions. + if (!Loc.isFileID()) + return; + + const clang::MacroInfo *MI = MD.getMacroInfo(); + + typedef clang::MacroInfo::tokens_iterator TokIter; + typedef clang::MacroInfo::arg_iterator ArgIter; + for (ArgIter AI = MI->arg_begin(), AE = MI->arg_end(); AI != AE; ++AI) { + const clang::Token *ResultArgToks = + Args->getUnexpArgument(MI->getArgumentNum((*AI))); + int CountInMacro = 0; + for (TokIter TI = MI->tokens_begin(), TE = MI->tokens_end(); TI != TE; + ++TI) { + clang::IdentifierInfo *TII = TI->getIdentifierInfo(); + int ArgNo = (TII ? MI->getArgumentNum(TII) : -1); + if (ArgNo == -1) { + // This isn't an argument, continue. + continue; + } + const clang::Token *Res = Args->getUnexpArgument(ArgNo); + if (ResultArgToks == Res) + CountInMacro++; + } + + if (CountInMacro < 2) + continue; + + // If the arg token didn't expand into anything, ignore it. + if (ResultArgToks->is(clang::tok::eof)) + continue; + unsigned NumToks = clang::MacroArgs::getArgLength(ResultArgToks); + + // Check for sideeffects in the argument expansions. + for (unsigned ArgumentIndex = 0; ArgumentIndex < NumToks; ++ArgumentIndex) { + const clang::Token &AT = ResultArgToks[ArgumentIndex]; + + if (AT.is(clang::tok::plusplus) || AT.is(clang::tok::minusminus)) { + Check.diag( + ResultArgToks->getLocation(), + "side effects in macro argument is repeated in macro expansion"); + Check.diag(MI->getDefinitionLoc(), "%0 macro defined here", + DiagnosticIDs::Note) + << MacroNameTok.getIdentifierInfo(); + } + } + } +} + +void MacroRepeatedSideEffectsCheck::registerPPCallbacks( + CompilerInstance &Compiler) { + Compiler.getPreprocessor().addPPCallbacks( + ::llvm::make_unique( + *this, Compiler.getSourceManager(), Compiler.getPreprocessor())); +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -16,6 +16,7 @@ #include "BoolPointerImplicitConversionCheck.h" #include "InaccurateEraseCheck.h" #include "InefficientAlgorithmCheck.h" +#include "MacroRepeatedSideEffectsCheck.h" #include "StaticAssertCheck.h" #include "SwappedArgumentsCheck.h" #include "UndelegatedConstructor.h" @@ -41,6 +42,8 @@ "misc-inaccurate-erase"); CheckFactories.registerCheck( "misc-inefficient-algorithm"); + CheckFactories.registerCheck( + "misc-macro-repeated-side-effects"); CheckFactories.registerCheck( "misc-static-assert"); CheckFactories.registerCheck( Index: test/clang-tidy/misc-repeated-side-effects-in-macro.c =================================================================== --- test/clang-tidy/misc-repeated-side-effects-in-macro.c +++ test/clang-tidy/misc-repeated-side-effects-in-macro.c @@ -0,0 +1,32 @@ +// RUN: $(dirname %s)/check_clang_tidy.sh %s misc-macro-repeated-side-effects %t +// REQUIRES: shell + +#define MIN(A,B) (((A)<(B)) ? (A) : (B)) + +void plusplus1() { + int j = 0; + int i = 0; + int min = MIN(i++, j); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in macro argument is repeated in macro expansion [misc-macro-repeated-side-effects] +} + +void plusplus2() { + int j = 0; + int i = 0; + int min = MIN(++i, j); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in macro argument is repeated in macro expansion [misc-macro-repeated-side-effects] +} + +void minusminus1() { + int j = 0; + int i = 0; + int min = MIN(i--, j); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in macro argument is repeated in macro expansion [misc-macro-repeated-side-effects] +} + +void minusminus2() { + int j = 0; + int i = 0; + int min = MIN(--i, j); + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: side effects in macro argument is repeated in macro expansion [misc-macro-repeated-side-effects] +}