Index: clang-tidy/modernize/CMakeLists.txt =================================================================== --- clang-tidy/modernize/CMakeLists.txt +++ clang-tidy/modernize/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyModernizeModule AvoidBindCheck.cpp DeprecatedHeadersCheck.cpp + ExplicitOperatorBoolCheck.cpp LoopConvertCheck.cpp LoopConvertUtils.cpp MakeSmartPtrCheck.cpp Index: clang-tidy/modernize/ExplicitOperatorBoolCheck.h =================================================================== --- /dev/null +++ clang-tidy/modernize/ExplicitOperatorBoolCheck.h @@ -0,0 +1,41 @@ +//===--- ExplicitOperatorBoolCheck.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_EXPLICIT_OPERATOR_BOOL_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_EXPLICIT_OPERATOR_BOOL_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace modernize { + +/// This check finds implicit operator bool overloads and inserts the explicit +/// keyword, which is available since C++11. +/// It also finds implicit operator const void* overloads, which should often be +/// explicit operator bool overloads. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/modernize-explicit-operator-bool.html +class ExplicitOperatorBoolCheck : public ClangTidyCheck { +public: + ExplicitOperatorBoolCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opt) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + const bool WarnOnOperatorVoidPointer; +}; + +} // namespace modernize +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_EXPLICIT_OPERATOR_BOOL_H Index: clang-tidy/modernize/ExplicitOperatorBoolCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/modernize/ExplicitOperatorBoolCheck.cpp @@ -0,0 +1,83 @@ +//===--- ExplicitOperatorBoolCheck.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 "ExplicitOperatorBoolCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace modernize { + +ExplicitOperatorBoolCheck::ExplicitOperatorBoolCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WarnOnOperatorVoidPointer(Options.get("WarnOnOperatorVoidPointer", 1) != + 0) {} + +void ExplicitOperatorBoolCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "WarnOnOperatorVoidPointer", WarnOnOperatorVoidPointer); +} + +void ExplicitOperatorBoolCheck::registerMatchers(MatchFinder *Finder) { + // Use of the explicit keyword with operator overloads requires C++11 or + // later. + if (!getLangOpts().CPlusPlus11) + return; + + Finder->addMatcher( + cxxConversionDecl(returns(booleanType()), unless(isExplicit())) + .bind("operator-bool"), + this); + + if (WarnOnOperatorVoidPointer) { + Finder->addMatcher(cxxConversionDecl(returns(pointerType(pointee( + isConstQualified(), voidType()))), + unless(isExplicit())) + .bind("operator-void-pointer"), + this); + } +} + +void ExplicitOperatorBoolCheck::check(const MatchFinder::MatchResult &Result) { + if (const auto *MatchedDecl = + Result.Nodes.getNodeAs("operator-bool")) { + diag(MatchedDecl->getLocation(), + "operator bool declaration is not explicit") + << FixItHint::CreateInsertion(MatchedDecl->getLocation(), "explicit "); + } else if (const auto *MatchedDecl = + Result.Nodes.getNodeAs( + "operator-void-pointer")) { + diag(MatchedDecl->getLocation(), + "implicit operator const void* declaration should " + "probably be explicit operator bool"); + + // FIXME: This tries to change the type and add explicit, but + // MatchedDecl->getTypeSpecStartLoc() gets the start of void, not the start + // of const, in const void*. We would need something like + // getConversionTypeStartLoc(). + // const auto Type = MatchedDecl->getConversionType(). + // const auto OldRange = CharSourceRange::getTokenRange( + // MatchedDecl->getTypeSpecStartLoc(), + // MatchedDecl->getTypeSpecStartLoc().getLocWithOffset(Type.getAsString().size())); + // diag(MatchedDecl->getLocation(), + // "implicit operator void* declaration should probably be explicit + // operator + // bool") + // << FixItHint::CreateReplacement(OldRange, "bool") + // << FixItHint::CreateInsertion(MatchedDecl->getLocStart(), "explicit "); + } +} + +} // namespace modernize +} // namespace tidy +} // namespace clang Index: clang-tidy/modernize/ModernizeTidyModule.cpp =================================================================== --- clang-tidy/modernize/ModernizeTidyModule.cpp +++ clang-tidy/modernize/ModernizeTidyModule.cpp @@ -12,6 +12,7 @@ #include "../ClangTidyModuleRegistry.h" #include "AvoidBindCheck.h" #include "DeprecatedHeadersCheck.h" +#include "ExplicitOperatorBoolCheck.h" #include "LoopConvertCheck.h" #include "MakeSharedCheck.h" #include "MakeUniqueCheck.h" @@ -41,6 +42,8 @@ "modernize-avoid-bind"); CheckFactories.registerCheck( "modernize-deprecated-headers"); + CheckFactories.registerCheck( + "modernize-explicit-operator-bool"); CheckFactories.registerCheck("modernize-loop-convert"); CheckFactories.registerCheck("modernize-make-shared"); CheckFactories.registerCheck("modernize-make-unique"); Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -204,6 +204,13 @@ Replaces C standard library headers with their C++ alternatives. +- New `modernize-explicit-operator-bool + `_ check + + Adds the ``explicit`` keyword to ``operator bool`` overloads. + Also finds ``operator const void*`` overloads, which should often be + ``explicit operator bool`` overloads. + - New `modernize-make-shared `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -93,6 +93,7 @@ misc-virtual-near-miss modernize-avoid-bind modernize-deprecated-headers + modernize-explicit-operator-bool modernize-loop-convert modernize-make-shared modernize-make-unique Index: docs/clang-tidy/checks/modernize-explicit-operator-bool.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/modernize-explicit-operator-bool.rst @@ -0,0 +1,28 @@ +.. title:: clang-tidy - modernize-explicit-operator-bool + +modernize-explicit-operator-bool +================================ + +This check finds implicit ``operator bool`` overloads and inserts the +``explicit`` keyword, which is available since C++11. + +Without the ``explicit`` keyword, the implicit ``bool`` overload can allow +objects to be compared accidentally. For instance, even when objects `a` and +`b` have no ``operator ==`` overloads, an implicit ``operator bool`` would allow +`a == b` to compile because both `a` and `b` can be implictly converted to +``bool``. + +This check also finds implicit ``operator const void*`` overloads. These were +often used before C++11 to avoid implicit conversions to ``bool`` when providing +an ``operator bool`` overload. + +To disable the check for ``operator const void*`` overloads, you may set +The :option:`WarnOnOperatorVoidPointer` option to 1. + +.. code-block:: c++ + + operator bool () const; + + // becomes + + explicit operator bool () const; Index: test/clang-tidy/modernize-explicit-operator-bool-void-pointer.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-explicit-operator-bool-void-pointer.cpp @@ -0,0 +1,46 @@ +// RUN: %check_clang_tidy %s modernize-explicit-operator-bool %t -- -- -std=c++11 + +// This should trigger the check: +class SomethingBad { + operator const void *() const { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: implicit operator const void* declaration should probably be explicit operator bool [modernize-explicit-operator-bool] + return reinterpret_cast(something != 0); + } + + int something = 0; +}; + +class SomethingGood { + // Note: Use modernize-explicit-operator-bool to check for implicit operator bool. + explicit operator bool() const { + return something != 0; + } + + int something = 0; +}; + +class SomethingGoodExplicitConstVoidPtr { + explicit operator const void *() const { + return &something; + } + + const int something = 0; +}; + +class SomethingGoodExplicitNonConstVoidPtr { + explicit operator void *() { + return &something; + } + + int something = 0; +}; + +class SomethingGoodNonConstVoidPtr { + // A non-const void* is unlikely to to be meant as operator bool before C++11 + // let us use explicit. + operator void *() { + return &something; + } + + int something = 0; +}; Index: test/clang-tidy/modernize-explicit-operator-bool.cpp =================================================================== --- /dev/null +++ test/clang-tidy/modernize-explicit-operator-bool.cpp @@ -0,0 +1,19 @@ +// RUN: %check_clang_tidy %s modernize-explicit-operator-bool %t -- -- -std=c++11 + +// This should trigger the check: +class SomethingBad { + operator bool() const { + // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: operator bool declaration is not explicit [modernize-explicit-operator-bool] + return something != 0; + } + + int something = 0; +}; + +class SomethingGood { + explicit operator bool() const { + return something != 0; + } + + int something = 0; +};