Index: clang-tidy/readability/CMakeLists.txt =================================================================== --- clang-tidy/readability/CMakeLists.txt +++ clang-tidy/readability/CMakeLists.txt @@ -11,6 +11,7 @@ IdentifierNamingCheck.cpp ImplicitBoolConversionCheck.cpp InconsistentDeclarationParameterNameCheck.cpp + MagicNumbersCheck.cpp MisleadingIndentationCheck.cpp MisplacedArrayIndexCheck.cpp NamedParameterCheck.cpp Index: clang-tidy/readability/MagicNumbersCheck.h =================================================================== --- /dev/null +++ clang-tidy/readability/MagicNumbersCheck.h @@ -0,0 +1,62 @@ +//===--- MagicNumbersCheck.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_READABILITY_MAGICNUMBERSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H + +#include "../ClangTidy.h" + +#include + +namespace clang { +namespace tidy { +namespace readability { + +/// Detects magic numbers, integer and floating point literals embedded in code. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/readability-magic-numbers.html +class MagicNumbersCheck : public ClangTidyCheck { +public: + MagicNumbersCheck(StringRef Name, ClangTidyContext *Context); + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + +private: + void checkIntegerMatch(const ast_matchers::MatchFinder::MatchResult &Result); + void checkFloatMatch(const ast_matchers::MatchFinder::MatchResult &Result); + + bool isIgnoredIntegerValue(const llvm::APInt &IntValue) const; + bool isIgnoredFloatValue(const llvm::APFloat &FloatValue) const; + + bool isConstantDefinition(const ast_type_traits::DynTypedNode &Parent) const; + bool + isConstantFieldDefinition(const ast_type_traits::DynTypedNode &Parent) const; + bool isTemplateArgumentAtSourceLocation( + const ast_type_traits::DynTypedNode &Parent) const; + bool isInitializerListForConstant( + const ast_matchers::MatchFinder::MatchResult &Result, + const ast_type_traits::DynTypedNode &Parent) const; + bool + isEnumerationDefinition(const ast_matchers::MatchFinder::MatchResult &Result, + const ast_type_traits::DynTypedNode &Parent) const; + + bool isConstant(const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &MatchResult) const; + + const std::vector IngnoredValuesInput; + std::vector IngnoredValues; +}; + +} // namespace readability +} // namespace tidy +} // namespace clang + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_MAGICNUMBERSCHECK_H Index: clang-tidy/readability/MagicNumbersCheck.cpp =================================================================== --- /dev/null +++ clang-tidy/readability/MagicNumbersCheck.cpp @@ -0,0 +1,214 @@ +//===--- MagicNumbersCheck.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 "MagicNumbersCheck.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" + +#include + +using namespace clang::ast_matchers; + +namespace clang { +namespace tidy { +namespace readability { + +const char DefaultIgnoredValues[] = "0;1;2;10;100;"; + +MagicNumbersCheck::MagicNumbersCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IngnoredValuesInput(utils::options::parseStringList( + Options.get("IgnoredValues", DefaultIgnoredValues))) { + IngnoredValues.reserve(IngnoredValuesInput.size()); + for (const std::string &IgnoredValue : IngnoredValuesInput) { + IngnoredValues.push_back(std::stoll(IgnoredValue)); + } + std::sort(IngnoredValues.begin(), IngnoredValues.end()); +} + +void MagicNumbersCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IgnoredValues", DefaultIgnoredValues); +} + +void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(integerLiteral().bind("ii"), this); + Finder->addMatcher(floatLiteral().bind("ff"), this); +} + +void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) { + checkIntegerMatch(Result); + checkFloatMatch(Result); +} + +void MagicNumbersCheck::checkIntegerMatch( + const MatchFinder::MatchResult &Result) { + const IntegerLiteral *MatchedInteger = + Result.Nodes.getNodeAs("ii"); + if (MatchedInteger) { + + if (Result.SourceManager->isMacroBodyExpansion( + MatchedInteger->getLocation())) + return; + + llvm::APInt Value = MatchedInteger->getValue(); + if (isIgnoredIntegerValue(Value)) + return; + + if (isConstant(Result, *MatchedInteger)) + return; + + SmallVector Str(32, '\0'); + Str.resize(0); + + Value.toString(Str, 10, true); + + diag(MatchedInteger->getLocation(), "magic number integer literal %0") + << Str.data(); + } +} + +void MagicNumbersCheck::checkFloatMatch( + const MatchFinder::MatchResult &Result) { + + const FloatingLiteral *MatchedFloat = + Result.Nodes.getNodeAs("ff"); + if (MatchedFloat) { + + if (Result.SourceManager->isMacroBodyExpansion(MatchedFloat->getLocation())) + return; + + llvm::APFloat Value = MatchedFloat->getValue(); + if (isIgnoredFloatValue(Value)) + return; + + if (isConstant(Result, *MatchedFloat)) + return; + + SmallVector Str(32, '\0'); + Str.resize(0); + + Value.toString(Str, 10, true); + + diag(MatchedFloat->getLocation(), "magic number float literal %0") + << Str.data(); + } +} + +bool MagicNumbersCheck::isIgnoredIntegerValue( + const llvm::APInt &IntValue) const { + int64_t Value = IntValue.getZExtValue(); + return std::binary_search(IngnoredValues.begin(), IngnoredValues.end(), + Value); +} + +bool MagicNumbersCheck::isIgnoredFloatValue( + const llvm::APFloat &FloatValue) const { + return FloatValue.isZero(); +} + +bool MagicNumbersCheck::isConstantDefinition( + const ast_type_traits::DynTypedNode &Parent) const { + const VarDecl *AsVarDecl = Parent.get(); + if (AsVarDecl) { + if (AsVarDecl->getType().isConstQualified()) { + return true; + } + } + return false; +} + +bool MagicNumbersCheck::isConstantFieldDefinition( + const ast_type_traits::DynTypedNode &Parent) const { + const FieldDecl *AsFieldDecl = Parent.get(); + if (AsFieldDecl) { + if (AsFieldDecl->getType().isConstQualified()) { + return true; + } + } + return false; +} + +bool MagicNumbersCheck::isTemplateArgumentAtSourceLocation( + const ast_type_traits::DynTypedNode &Parent) const { + const SubstNonTypeTemplateParmExpr *AsTemplateArgument = + Parent.get(); + if (AsTemplateArgument) { + return true; + } + return false; +} + +bool MagicNumbersCheck::isInitializerListForConstant( + const MatchFinder::MatchResult &Result, + const ast_type_traits::DynTypedNode &Parent) const { + const InitListExpr *AsInitListExpr = Parent.get(); + if (AsInitListExpr) { + for (const ast_type_traits::DynTypedNode &InitListParent : + Result.Context->getParents(*AsInitListExpr)) { + const VarDecl *AsVarDecl = InitListParent.get(); + if (AsVarDecl) { + if (AsVarDecl->getType().isConstQualified()) { + // good const definition + return true; + } + } + + if (isInitializerListForConstant(Result, InitListParent)) + return true; + } + } + + return false; +} + +bool MagicNumbersCheck::isEnumerationDefinition( + const MatchFinder::MatchResult &Result, + const ast_type_traits::DynTypedNode &Parent) const { + const EnumConstantDecl *AsEnumConstantDecl = Parent.get(); + if (AsEnumConstantDecl) + return true; + + for (const ast_type_traits::DynTypedNode &GrandParent : + Result.Context->getParents(Parent)) { + if (isEnumerationDefinition(Result, GrandParent)) + return true; + } + + return false; +} + +bool MagicNumbersCheck::isConstant( + const ast_matchers::MatchFinder::MatchResult &Result, + const Expr &MatchResult) const { + for (const ast_type_traits::DynTypedNode &Parent : + Result.Context->getParents(MatchResult)) { + + if (isConstantDefinition(Parent)) + return true; + + if (isConstantFieldDefinition(Parent)) + return true; + + if (isTemplateArgumentAtSourceLocation(Parent)) + return true; + + if (isInitializerListForConstant(Result, Parent)) + return true; + + if (isEnumerationDefinition(Result, Parent)) + return true; + } + + return false; +} + +} // namespace readability +} // namespace tidy +} // namespace clang Index: clang-tidy/readability/ReadabilityTidyModule.cpp =================================================================== --- clang-tidy/readability/ReadabilityTidyModule.cpp +++ clang-tidy/readability/ReadabilityTidyModule.cpp @@ -20,6 +20,7 @@ #include "IdentifierNamingCheck.h" #include "ImplicitBoolConversionCheck.h" #include "InconsistentDeclarationParameterNameCheck.h" +#include "MagicNumbersCheck.h" #include "MisleadingIndentationCheck.h" #include "MisplacedArrayIndexCheck.h" #include "NamedParameterCheck.h" @@ -65,6 +66,8 @@ "readability-implicit-bool-conversion"); CheckFactories.registerCheck( "readability-inconsistent-declaration-parameter-name"); + CheckFactories.registerCheck( + "readability-magic-numbers"); CheckFactories.registerCheck( "readability-misleading-indentation"); CheckFactories.registerCheck( Index: docs/ReleaseNotes.rst =================================================================== --- docs/ReleaseNotes.rst +++ docs/ReleaseNotes.rst @@ -154,6 +154,12 @@ Warns or suggests alternatives if SIMD intrinsics are used which can be replaced by ``std::experimental::simd`` operations. +- New :doc:`readability-magic-numbers + ` check. + + Detects usage of magic numbers, numbers that are used as literals instead of + introduced via constants or symbols. + - New :doc:`readability-simplify-subscript-expr ` check. Index: docs/clang-tidy/checks/list.rst =================================================================== --- docs/clang-tidy/checks/list.rst +++ docs/clang-tidy/checks/list.rst @@ -217,6 +217,7 @@ readability-identifier-naming readability-implicit-bool-conversion readability-inconsistent-declaration-parameter-name + readability-magic-numbers readability-misleading-indentation readability-misplaced-array-index readability-named-parameter Index: docs/clang-tidy/checks/readability-magic-numbers.rst =================================================================== --- /dev/null +++ docs/clang-tidy/checks/readability-magic-numbers.rst @@ -0,0 +1,19 @@ +.. title:: clang-tidy - readability-magic-numbers + +readability-magic-numbers +========================== + +Detects magic numbers, integer or floating point literal that are embedded in +code and not introduced via constants or symbols. + +Bad example: + +.. code-block:: c++ + + double circleArea = 3.1415926535 * radius * radius; + +Good example: + +.. code-block:: c++ + + double circleArea = M_PI * radius * radius; Index: test/clang-tidy/readability-magic-numbers.cpp =================================================================== --- /dev/null +++ test/clang-tidy/readability-magic-numbers.cpp @@ -0,0 +1,107 @@ +// RUN: %check_clang_tidy %s readability-magic-numbers %t + +template +struct ValueBucket { + T value[V]; +}; + +int BadGlobalInt = 5; +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: magic number integer literal 5 [readability-magic-numbers] + +int IntSquarer(int param) { + return param * param; +} + +void BuggyFunction() { + int BadLocalInt = 6; + // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: magic number integer literal 6 [readability-magic-numbers] + + (void)IntSquarer(7); + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: magic number integer literal 7 [readability-magic-numbers] + + int LocalArray[8]; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: magic number integer literal 8 [readability-magic-numbers] + + for (int ii = 0; ii < 8; ++ii) + // CHECK-MESSAGES: :[[@LINE-1]]:25: warning: magic number integer literal 8 [readability-magic-numbers] + { + LocalArray[ii] = 3 * ii; + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: magic number integer literal 3 [readability-magic-numbers] + } + + ValueBucket Bucket; + // CHECK-MESSAGES: :[[@LINE-1]]:20: warning: magic number integer literal 4 [readability-magic-numbers] +} + +class TwoIntContainer { +public: + TwoIntContainer(int val) : anotherMember(val * val), yetAnotherMember(6), anotherConstant(val + val) {} + // CHECK-MESSAGES: :[[@LINE-1]]:73: warning: magic number integer literal 6 [readability-magic-numbers] + + int getValue() const; + +private: + int oneMember = 9; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: magic number integer literal 9 [readability-magic-numbers] + + int anotherMember; + + int yetAnotherMember; + + const int oneConstant = 2; + + const int anotherConstant; +}; + +int ValueArray[] = {3, 5}; +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: magic number integer literal 3 [readability-magic-numbers] +// CHECK-MESSAGES: :[[@LINE-2]]:24: warning: magic number integer literal 5 [readability-magic-numbers] + +float FloatPiVariable = 3.1415926535f; +// CHECK-MESSAGES: :[[@LINE-1]]:25: warning: magic number float literal 3.141592741 [readability-magic-numbers] +double DoublePiVariable = 6.283185307; +// CHECK-MESSAGES: :[[@LINE-1]]:27: warning: magic number float literal 6.283185307 [readability-magic-numbers] + +/* + * Clean code + */ + +#define INT_MACRO 5 + +const int GoodGlobalIntContant = 42; + +constexpr int AlsoGoodGlobalIntContant = 42; + +void SolidFunction() { + const int GoodLocalIntContant = 43; + + (void)IntSquarer(GoodLocalIntContant); + + int LocalArray[INT_MACRO]; + + ValueBucket Bucket; +} + +const int ConstValueArray[] = {7, 9}; + +const int ConstValueArray2D[2][2] = {{7, 9}, {13, 15}}; + +/* + * no warnings for grandfathered values + */ +int GrandfatheredValues[] = {0, 1, 2, 10, 100, -1, -10, -100}; + +/* + * no warnings for enums + */ +enum Smorgasbord { + STARTER, + ALPHA = 3, + BETA = 1 << 5, +}; + +const float FloatPiConstant = 3.1415926535f; +const double DoublePiConstant = 6.283185307; + +double DoubleZeroIsAccepted = 0.0; +float FloatZeroIsAccepted = 0.0f;