Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -2,6 +2,7 @@ add_clang_library(clangTidyModernizeModule DeprecatedHeadersCheck.cpp + IncrementBoolCheck.cpp LoopConvertCheck.cpp LoopConvertUtils.cpp MakeSmartPtrCheck.cpp Index: clang-tidy/modernize/IncrementBoolCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/IncrementBoolCheck.h @@ -0,0 +1,40 @@ +//===--- IncrementBoolCheck.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_MODERNIZE_INCREMENT_BOOL_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_INCREMENT_BOOL_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// This check replaces deprecated bool incrementation with truth assignment. +/// +/// Before: +/// bool x = false, y = ++x; +/// After: +/// bool x = false; y = (x = true); +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-increment-bool.html +class IncrementBoolCheck : public ClangTidyCheck { +public: + IncrementBoolCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_INCREMENT_BOOL_H Index: clang-tidy/modernize/IncrementBoolCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/IncrementBoolCheck.cpp @@ -0,0 +1,91 @@ +//===--- IncrementBoolCheck.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 "IncrementBoolCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +static const char ExprName[] = "expr"; +static const char IncrText[] = "++"; +static const char ParentName[] = "parent"; +static const char ParenExprName[] = "parenExpr"; +static const char UnaryOpName[] = "unaryOp"; + +void IncrementBoolCheck::registerMatchers(MatchFinder *Finder) { + const auto ExprMatcher = + allOf(has(expr(hasType(booleanType())).bind(ExprName)), + hasOperatorName(IncrText)); + + const auto ParentMatcher = hasParent(compoundStmt().bind(ParentName)); + const auto ParMatcher = hasDescendant(parenExpr().bind(ParenExprName)); + + // We want to match the operator, no matter if it is a whole statement or not; + // no matter if it is a parenthesized expression or not; we just might add + // additional bindings. + Finder->addMatcher(unaryOperator(ExprMatcher, + eachOf(ParentMatcher, unless(ParentMatcher)), + eachOf(ParMatcher, unless(ParMatcher)), + unless(isInTemplateInstantiation())) + .bind(UnaryOpName), + this); +} + +void IncrementBoolCheck::check(const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs(UnaryOpName); + const auto *MatchedExpr = Result.Nodes.getNodeAs(ExprName); + + // If declaration is inside macro, we don't want to fix it + if (MatchedDecl->getLocStart().isMacroID()) + return; + + SourceManager &SM = *Result.SourceManager; + + bool Invalid = false; + StringRef SubExpr = Lexer::getSourceText( + CharSourceRange::getTokenRange(MatchedExpr->getLocStart(), + MatchedExpr->getLocEnd()), + SM, LangOptions(), &Invalid); + assert(!Invalid); + + bool IsLastOperation = Result.Nodes.getNodeAs(ParentName); + + auto Diag = diag(MatchedDecl->getLocStart(), + "bool incrementation is deprecated, use assignment"); + + // Postfix operations are probably impossible to change automatically. + if (MatchedDecl->isPostfix() && !IsLastOperation) + return; + + bool UseInnerParentheses = Result.Nodes.getNodeAs(ParenExprName); + bool UseOuterParentheses = !IsLastOperation; + + std::string Replacement = SubExpr.str(); + if (UseInnerParentheses) + Replacement = "(" + Replacement + ")"; + + Replacement += " = true"; + if (UseOuterParentheses) + Replacement = "(" + Replacement + ")"; + + Diag << FixItHint::CreateReplacement( + CharSourceRange::getTokenRange(MatchedDecl->getLocStart(), + MatchedDecl->getLocEnd()), + Replacement); +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -11,6 +11,7 @@ #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" #include "DeprecatedHeadersCheck.h" +#include "IncrementBoolCheck.h" #include "LoopConvertCheck.h" #include "MakeSharedCheck.h" #include "MakeUniqueCheck.h" @@ -35,6 +36,8 @@ void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { CheckFactories.registerCheck( "modernize-deprecated-headers"); + CheckFactories.registerCheck( + "modernize-increment-bool"); CheckFactories.registerCheck("modernize-loop-convert"); CheckFactories.registerCheck("modernize-make-shared"); CheckFactories.registerCheck("modernize-make-unique"); Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -91,6 +91,7 @@ misc-unused-using-decls misc-virtual-near-miss modernize-deprecated-headers + modernize-increment-bool modernize-loop-convert modernize-make-shared modernize-make-unique Index: docs/clang-tidy/checks/modernize-increment-bool.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-increment-bool.rst @@ -0,0 +1,54 @@ +.. title:: clang-tidy - modernize-increment-bool + +modernize-increment-bool +======================== + +This check detects the deprecated boolean type incrementation, and wherever possible, +converts it into truth assignment. + +C++17 forbids this behavior. Before C++17 this was equivalent to setting the variable +to true. + + +Example +------- + +Original: + +.. code-block:: c++ + + bool variable = false; + variable++; + ++variable; + + bool another = ++variable; + bool third = ++variable && another; + +After applying the check: + +.. code-block:: c++ + + bool variable = false; + variable = true; + variable = true; /* Both postfix and prefix incrementations are supported. */ + + bool another = (variable = true); + bool third = (variable = true) && another; + + +Limitations +----------- + +When postfix boolean incrementation is not the outermost operation done in the instruction, +tool will not repair the problem (the fix would have to append some instructions after the +statement). For example: + +.. code-block:: c++ + + bool first = false, second; + second = first++; + + // Equivalent to: + second = false; + first = true; + Index: test/clang-tidy/modernize-increment-bool.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-increment-bool.cpp @@ -0,0 +1,59 @@ +// RUN: %check_clang_tidy %s modernize-increment-bool %t + +#define INCR(var) ((var)++) + +template +T &f(T &x) { return x; } + +template +void g() { + T x; + x++; + + bool y; + y++; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: y = true; +} + +int main() { + bool b1 = false, b2 = false, b3 = false; + + b1++; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: b1 = true; + + bool b4 = f(b1)++; + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: bool incrementation is deprecated, use assignment + + bool b5 = ++f(b1); + // CHECK-MESSAGES: :[[@LINE-1]]:13: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: bool b5 = (f(b1) = true); + + (b1 = false)++; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: (b1 = false) = true; + + (b1 = b2 = false)++; + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: (b1 = b2 = false) = true; + + ++(b1 = b2 = false); + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: (b1 = b2 = false) = true; + + b3 = b1++ && b2; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: bool incrementation is deprecated, use assignment + + b3 = ++(b1 |= b4) && b3; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: bool incrementation is deprecated, use assignment + // CHECK-FIXES: b3 = ((b1 |= b4) = true) && b3; + + int x = 8; + x++; + + INCR(b1); + + g(); + g(); +}