Index: clang-tidy/misc/CMakeLists.txt =================================================================== --- clang-tidy/misc/CMakeLists.txt +++ clang-tidy/misc/CMakeLists.txt @@ -3,6 +3,7 @@ add_clang_library(clangTidyMiscModule ArgumentCommentCheck.cpp AssertSideEffectCheck.cpp + DefaultNumericsCheck.cpp ForwardingReferenceOverloadCheck.cpp LambdaFunctionNameCheck.cpp MisplacedConstCheck.cpp Index: clang-tidy/misc/DefaultNumericsCheck.h =================================================================== --- /dev/null +++ clang-tidy/misc/DefaultNumericsCheck.h @@ -0,0 +1,37 @@ +//===--- DefaultNumericsCheck.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_DEFAULT_NUMERICS_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFAULT_NUMERICS_H + +#include "../ClangTidy.h" + +namespace clang { +namespace tidy { +namespace misc { + +/// This check flags usages of ``std::numeric_limits::{min,max}()`` for +/// unspecialized types. It is dangerous because it returns T(), which rarely +/// might be the minimum or the maximum for this type. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc-default-numerics.html +class DefaultNumericsCheck : public ClangTidyCheck { +public: + DefaultNumericsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace misc +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_DEFAULT_NUMERICS_H Index: clang-tidy/misc/DefaultNumericsCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/misc/DefaultNumericsCheck.cpp @@ -0,0 +1,51 @@ +//===--- DefaultNumericsCheck.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 "DefaultNumericsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace misc { + +void DefaultNumericsCheck::registerMatchers(MatchFinder *Finder) { + if (!getLangOpts().CPlusPlus) + return; + // FIXME: We should also warn in template intantiations, but it would require + // printing backtrace. + Finder->addMatcher( + callExpr( + callee(cxxMethodDecl( + hasAnyName("min", "max"), + ofClass(classTemplateSpecializationDecl( + hasName("::std::numeric_limits"), + unless(isExplicitTemplateSpecialization()), + hasTemplateArgument(0, templateArgument().bind("type")))))), + unless(isInTemplateInstantiation())) + .bind("call"), + this); +} + +void DefaultNumericsCheck::check(const MatchFinder::MatchResult &Result) { + const auto &MatchedCall = *Result.Nodes.getNodeAs("call"); + const auto &NumericType = *Result.Nodes.getNodeAs("type"); + bool IsMax = MatchedCall.getCalleeDecl()->getAsFunction()->getName() == "max"; + + diag(MatchedCall.getLocStart(), + "'std::numeric_limits::%select{min|max}0' called with type %1; no such " + "specialization exists, so the default value for that type is returned") + << IsMax << NumericType; +} + +} // namespace misc +} // namespace tidy +} // namespace clang Index: clang-tidy/misc/MiscTidyModule.cpp =================================================================== --- clang-tidy/misc/MiscTidyModule.cpp +++ clang-tidy/misc/MiscTidyModule.cpp @@ -14,6 +14,7 @@ #include "AssertSideEffectCheck.h" #include "BoolPointerImplicitConversionCheck.h" #include "DanglingHandleCheck.h" +#include "DefaultNumericsCheck.h" #include "DefinitionsInHeadersCheck.h" #include "FoldInitTypeCheck.h" #include "ForwardDeclarationNamespaceCheck.h" @@ -67,6 +68,7 @@ CheckFactories.registerCheck("misc-argument-comment"); CheckFactories.registerCheck( "misc-assert-side-effect"); + CheckFactories.registerCheck("misc-default-numerics"); CheckFactories.registerCheck( "misc-forwarding-reference-overload"); CheckFactories.registerCheck( Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -78,6 +78,10 @@ Allow custom memory management functions to be considered as well. +- New `misc-default-numerics + `_ check + Finds uses of ``std::numeric_limits`` for unspecialized types + - New `misc-forwarding-reference-overload `_ check Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -76,6 +76,7 @@ misc-assert-side-effect misc-bool-pointer-implicit-conversion misc-dangling-handle + misc-default-numerics misc-definitions-in-headers misc-fold-init-type misc-forward-declaration-namespace Index: docs/clang-tidy/checks/misc-default-numerics.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/misc-default-numerics.rst @@ -0,0 +1,23 @@ +.. title:: clang-tidy - misc-default-numerics + +misc-default-numerics +===================== + +This check flags usages of ``std::numeric_limits::{min,max}()`` for +unspecialized types. It is dangerous because the calls return ``T()`` +in this case, which is unlikely to represent the minimum or maximum value for +the type. + +Consider scenario: +.. code-block:: c++ + + // 1. Have a: + typedef long long BigInt + + // 2. Use + std::numeric_limits::min() + + // 3. Replace the BigInt typedef with class implementing BigIntegers + class BigInt { ;;; }; + + // 4. Your code continues to compile, but the call to min() returns BigInt{} Index: test/clang-tidy/misc-default-numerics.cpp =================================================================== --- /dev/null +++ test/clang-tidy/misc-default-numerics.cpp @@ -0,0 +1,56 @@ +// RUN: %check_clang_tidy %s misc-default-numerics %t + +namespace std { + +template +struct numeric_limits { + static T min() { return T(); } + static T max() { return T(); } +}; + +template <> +struct numeric_limits { + static int min() { return -1; } + static int max() { return 1; } +}; + +} // namespace std + +class MyType {}; +template +class MyTemplate {}; + +class SpecializedType {}; + +namespace std { +template<> +struct numeric_limits { + static int min() { return -1; } + static SpecializedType max() { return SpecializedType(); } + +}; +} + +void test() { + auto x = std::numeric_limits::min(); + + auto y = std::numeric_limits::min(); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: 'std::numeric_limits::min' called with type 'MyType'; no such specialization exists, so the default value for that type is returned + + auto z = std::numeric_limits>::max(); + // CHECK-MESSAGES: [[@LINE-1]]:12: warning: 'std::numeric_limits::max' called with type 'MyTemplate'; + + auto a = std::numeric_limits::min(); + auto b = std::numeric_limits::max(); +} + +template +void fun() { + auto x = std::numeric_limits::min(); +} + +void testTemplate() { + fun(); + // FIXME: This should generate warning with backtrace. + fun; +}