0x8000-0000 created this revision.
0x8000-0000 added reviewers: Wizard, aaron.ballman, alexfh, hokein.
Herald added subscribers: cfe-commits, mgorny.

Add a clang-tidy check for "magic numbers", integers and floating point values 
embedded in the code instead of using symbols or constants.

Bad example:

  double circleArea = 3.1415926535 * radius * radius;

Good example:

  double circleArea = M_PI * radius * radius;

This version detects and report integers only. If there is interest of merging 
the tool I can add the functionality for floats as well.


Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D49114

Files:
  clang-tidy/readability/CMakeLists.txt
  clang-tidy/readability/MagicNumbersCheck.cpp
  clang-tidy/readability/MagicNumbersCheck.h
  clang-tidy/readability/ReadabilityTidyModule.cpp
  docs/ReleaseNotes.rst
  docs/clang-tidy/checks/list.rst
  docs/clang-tidy/checks/readability-magic-numbers.rst
  test/clang-tidy/readability-magic-numbers.cpp

Index: test/clang-tidy/readability-magic-numbers.cpp
===================================================================
--- /dev/null
+++ test/clang-tidy/readability-magic-numbers.cpp
@@ -0,0 +1,80 @@
+// RUN: %check_clang_tidy %s readability-magic-numbers %t
+
+template <typename T, int V>
+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<int, 4> 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(2), anotherConstant(val + val) {}
+  // CHECK-MESSAGES: :[[@LINE-1]]:73: warning: magic number integer literal 2 [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]
+
+/*
+ * 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<int, INT_MACRO> Bucket;
+}
+
+const int ConstValueArray[] = {7, 9};
Index: docs/clang-tidy/checks/readability-magic-numbers.rst
===================================================================
--- /dev/null
+++ docs/clang-tidy/checks/readability-magic-numbers.rst
@@ -0,0 +1,15 @@
+.. 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:
+
+   double circleArea = 3.1415926535 * radius * radius;
+
+Good example:
+
+   double circleArea = M_PI * radius * radius;
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/ReleaseNotes.rst
===================================================================
--- docs/ReleaseNotes.rst
+++ docs/ReleaseNotes.rst
@@ -57,6 +57,12 @@
 Improvements to clang-tidy
 --------------------------
 
+- New `readability-magic-numbers
+  <http://clang.llvm.org/extra/clang-tidy/checks/readability-magic-numbers.html>`_ check
+
+  Detect usage of magic numbers, numbers that are used as literals instead of
+  introduced via constants or symbols.
+
 - The checks profiling info can now be stored as JSON files for futher
   post-processing and analysis.
 
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<InconsistentDeclarationParameterNameCheck>(
         "readability-inconsistent-declaration-parameter-name");
+    CheckFactories.registerCheck<MagicNumbersCheck>(
+        "readability-magic-numbers");
     CheckFactories.registerCheck<MisleadingIndentationCheck>(
         "readability-misleading-indentation");
     CheckFactories.registerCheck<MisplacedArrayIndexCheck>(
Index: clang-tidy/readability/MagicNumbersCheck.h
===================================================================
--- /dev/null
+++ clang-tidy/readability/MagicNumbersCheck.h
@@ -0,0 +1,35 @@
+//===--- 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"
+
+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)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // 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,108 @@
+//===--- 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 "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace readability {
+
+void MagicNumbersCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(integerLiteral().bind("x"), this);
+}
+
+void MagicNumbersCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *MatchedDecl = Result.Nodes.getNodeAs<IntegerLiteral>("x");
+  if (MatchedDecl) {
+
+    if (Result.SourceManager->isMacroBodyExpansion(
+            MatchedDecl->getLocation())) {
+      // this is not a magic value in the source file, it actually is defined
+      // via a macro - not as good as const, but still OK
+      return;
+    }
+
+    llvm::APInt Value = MatchedDecl->getValue();
+    if ((Value == 0) || (Value == 1)) {
+      // 0 and 1 are "given" in a binary computer; besides, every C-style loop
+      // will trigger on base and increment - which is too much noise
+      return;
+    }
+
+    const auto &Parents = Result.Context->getParents(*MatchedDecl);
+    for (const auto &Parent : Parents) {
+
+      // the definition of const values themselves, as global or local variables
+      {
+        const auto *AsVarDecl = Parent.get<clang::VarDecl>();
+        if (AsVarDecl) {
+          if (AsVarDecl->getType().isConstQualified()) {
+            // good const definition
+            return;
+          }
+        }
+      }
+
+      // const class/struct members
+      {
+        const auto *AsFieldDecl = Parent.get<clang::FieldDecl>();
+        if (AsFieldDecl) {
+          if (AsFieldDecl->getType().isConstQualified()) {
+            // good const definition
+            return;
+          }
+        }
+      }
+
+      // template-argument (at the definition location)
+      {
+        const auto *AsTemplateArgument =
+            Parent.get<clang::SubstNonTypeTemplateParmExpr>();
+        if (AsTemplateArgument) {
+          // no need to report here, it will be also reported at the
+          // instantiation location
+          return;
+        }
+      }
+
+      // local/global non-const array
+      {
+        const auto *AsInitListExpr = Parent.get<clang::InitListExpr>();
+        if (AsInitListExpr) {
+          const auto &InitListParents =
+              Result.Context->getParents(*AsInitListExpr);
+          for (const auto &InitListParent : InitListParents) {
+            const auto *AsVarDecl = InitListParent.get<clang::VarDecl>();
+            if (AsVarDecl) {
+              if (AsVarDecl->getType().isConstQualified()) {
+                // good const definition
+                return;
+              }
+            }
+          }
+        }
+      }
+    }
+
+    SmallVector<char, 32> Str(32, '\0');
+    Str.resize(0);
+
+    Value.toString(Str, 10, true);
+
+    diag(MatchedDecl->getLocation(), "magic number integer literal %0") << Str.data();
+  }
+}
+
+} // namespace readability
+} // namespace tidy
+} // namespace clang
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
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to