Discookie created this revision.
Discookie added reviewers: aaron.ballman, njames93, carlosgalvezp.
Discookie added a project: clang-tools-extra.
Herald added subscribers: PiotrZSL, ChuanqiXu, xazax.hun.
Herald added a project: All.
Discookie requested review of this revision.

This check finds integer literals that are being used in a non-portable manner.

Currently, the check detects cases where maximum or minimum values should be 
used
instead, as well as error-prone integer literals having leading zeroes, or
relying on most significant bits.


Repository:
  rG LLVM Github Monorepo

https://reviews.llvm.org/D146712

Files:
  clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
  clang-tools-extra/clang-tidy/cert/CMakeLists.txt
  clang-tools-extra/clang-tidy/portability/CMakeLists.txt
  clang-tools-extra/clang-tidy/portability/NonPortableIntegerConstantCheck.cpp
  clang-tools-extra/clang-tidy/portability/NonPortableIntegerConstantCheck.h
  clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
  clang-tools-extra/docs/ReleaseNotes.rst
  clang-tools-extra/docs/clang-tidy/checks/cert/int17-c.rst
  clang-tools-extra/docs/clang-tidy/checks/list.rst
  
clang-tools-extra/docs/clang-tidy/checks/portability/non-portable-integer-constant.rst
  
clang-tools-extra/test/clang-tidy/checkers/portability/non-portable-integer-constant.cpp

Index: clang-tools-extra/test/clang-tidy/checkers/portability/non-portable-integer-constant.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/checkers/portability/non-portable-integer-constant.cpp
@@ -0,0 +1,258 @@
+// RUN: %check_clang_tidy %s -std=c++17-or-later portability-non-portable-integer-constant %t
+
+using int32_t = decltype(42);
+
+void regular() {
+  // no-warnings
+  0;
+  00;
+  0x0;
+  0x00;
+  0b0;
+  0b00;
+  0b0'0'0;
+
+  -1;
+  -0X1;
+
+  127;
+  0x7'f;
+
+  -128;
+  -0x80;
+
+  256;
+  0X100;
+
+  42;
+  0x2A;
+
+  180079837;
+  0xabbccdd;
+
+  // FIXME: (only 31 bits) False positive, reported as max signed int.
+  0b1111111111111111111111111111111;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  // FIXME: Large numbers represented as hex are the most common false positive,
+  // eg. the following literal is a 64-bit prime.
+  0xff51afd7ed558ccd;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: should not rely on the most significant bit [portability-non-portable-integer-constant]
+
+  // FIXME: Fixed-size integer literals are a common false positive as well.
+  int32_t literal = 0x40000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:21: warning: non-portable integer literal: should not rely on bits of most significant byte [portability-non-portable-integer-constant]
+
+  // FIXME: According to the standard, the type of the integer literal is the
+  // smallest type it can fit into. While technically a false positive, it could
+  // signal literal width confusion regardless.
+  long long int long_literal = 0x7fffffff;
+  // CHECK-MESSAGES: :[[@LINE-1]]:32: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+}
+
+enum LiteralClamp {
+
+};
+
+// INT_MIN, INT_MAX, UINT_MAX, UINT_MAX-1
+// All binary literals are 32 bits long
+void limits_int() {
+  // FIXME: not recognize as Min
+  -214748'3'648;
+  // --CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  -0x80'00'00'00;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  -020000000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  -0b10000000000000000000000000000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+
+
+  21474'83647;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0x7FFFFFFF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  017777777777;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  429'4'96'7295u;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0xFFFFFFFF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  037777777777;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0B11111111111111111111111111111111;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  4294967294;  // recognized as long ( Not UINT_MAX-1)
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0XFFFFFFFE;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  037777777776;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0B11111111111111111111111111111110;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+}
+
+// LLONG_MIN, LLONG_MAX, ULLONG_MAX, ULLONG_MAX-1
+// Most binary literals are 64 bits long
+void limits_llong() {
+  -9'22337203'6854775808LL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  0x8000000000000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  01000000000000000000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  0b1000000000000000000000000000000000000000000000000000000000000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+
+  9223372036854775807;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0x7FFFFFFFFFFFFFFF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0777777777777777777777;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  // 63 bits
+  0b111111111111111111111111111111111111111111111111111111111111111;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  18446744073709551615llU;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0xFFFFFFFFFFFFFFFF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  01777777777777777777777;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0b1111111111111111111111111111111111111111111111111111111111111111;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  18446744073709551614llU;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0xFFFFFFFFFFFFFFFe;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  01777777777777777777776;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+  0b1111111111111111111111111111111111111111111111111111111111111110;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+}
+
+void full_patterns() {
+  0x7F'FF'FF'FF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+  -0x80'00'00'00;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+  0xFFFFFFFFu;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+  0xFF'FF'FF'Fe;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+
+
+  0x7F'FF'FFFFFFFFFFFF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+  -0x80'0000000000000'0LL;
+  // CHECK-MESSAGES: :[[@LINE-1]]:4: warning: non-portable integer literal: hardcoded platform-specific minimum value [portability-non-portable-integer-constant]
+  0XFFffFFFffFFFFFFFllU;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: hardcoded platform-specific maximum value [portability-non-portable-integer-constant]
+}
+
+void most_significant_bits() {
+  0x80004000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: should not rely on the most significant bit [portability-non-portable-integer-constant]
+
+  0xAFFFFFFF;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: should not rely on the most significant bit [portability-non-portable-integer-constant]
+
+  0x10000000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: should not rely on bits of most significant byte [portability-non-portable-integer-constant]
+
+  0x3000200010004000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: should not rely on bits of most significant byte [portability-non-portable-integer-constant]
+
+  0x00001000;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: integer literal with leading zeroes [portability-non-portable-integer-constant]
+
+  // In some cases, an integer literal represents a smaller type than its
+  // bitcount, which caused issues with MSBBit detection.
+  0x0000000000000001;
+  // CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal: integer literal with leading zeroes [portability-non-portable-integer-constant]
+}
+
+template <int I> bool template_unused_reported() { return I < 0x7FFFFFFF; }
+// CHECK-MESSAGES: :[[@LINE-1]]:63: warning: non-portable integer literal
+
+template <int I> bool template_arg_not_reported() { return I < 0x7FFFFFFF; }
+// CHECK-MESSAGES: :[[@LINE-1]]:64: warning: non-portable integer literal
+// CHECK-MESSAGES-NOT: :[[@LINE-2]]:60: warning: non-portable integer literal
+
+template <int I = 0x7FFFFFFF > bool template_default_reported() { return I < 0; }
+// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: non-portable integer literal
+// CHECK-MESSAGES-NOT: :[[@LINE-2]]:74: warning: non-portable integer literal
+
+void templates() {
+  // Should be reported only at the callsite.
+  template_arg_not_reported<0x7FFFFFFF>();
+  // CHECK-MESSAGES: :[[@LINE-1]]:29: warning: non-portable integer literal
+
+  template_default_reported();
+
+  // Do not report same literal twice.
+  template_default_reported<0>();
+}
+
+// FIXME: Macros are currently not distinguished from the template arguments.
+void macros() {
+#define FULL_LITERAL 0x0100000000000000
+  FULL_LITERAL;
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal
+  // --CHECK-MESSAGES: :[[@LINE-3]]:22: note: expanded from macro
+
+#define PARTIAL_LITERAL(start) start##00000000
+  PARTIAL_LITERAL(0x01000000);
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal
+  // --CHECK-MESSAGES: :[[@LINE-3]]:32: note: expanded from macro
+
+  // FIXME: In this case, the integer literal token is 0 long due to a tokenizer issue.
+#define EMPTY_ARGUMENT(start) start##0x0100000000000000
+  EMPTY_ARGUMENT();
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal
+  // --CHECK-MESSAGES: :[[@LINE-3]]:38: note: expanded from macro
+
+  // The following literals could be detected without reading the text representation.
+#define MAX_VALUE_LITERAL 0x7FFFFFFF
+  MAX_VALUE_LITERAL;
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal
+  // --CHECK-MESSAGES: :[[@LINE-3]]:22: note: expanded from macro
+
+#define MAX_VALUE_PARTIAL_LITERAL(start) start##FFFF
+  MAX_VALUE_PARTIAL_LITERAL(0x7FFF);
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal
+  // --CHECK-MESSAGES: :[[@LINE-3]]:32: note: expanded from macro
+
+#define MAX_VALUE_EMPTY_ARGUMENT(start) start##0x7FFFFFFF
+  MAX_VALUE_EMPTY_ARGUMENT();
+  // --CHECK-MESSAGES: :[[@LINE-1]]:3: warning: non-portable integer literal
+  // --CHECK-MESSAGES: :[[@LINE-3]]:38: note: expanded from macro
+}
Index: clang-tools-extra/docs/clang-tidy/checks/portability/non-portable-integer-constant.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/portability/non-portable-integer-constant.rst
@@ -0,0 +1,40 @@
+.. title:: clang-tidy - portability-non-portable-integer-constant
+
+portability-non-portable-integer-constant
+======================================
+
+`cert-int17-c` redirects here as an alias for this check.
+
+Finds integer literals that are being used in a non-portable manner.
+
+Currently the check detects cases where maximum or minimum values should be used
+instead, as well as error-prone integer literals having leading zeroes, or
+relying on the most significant bits.
+
+This check corresponds to CERT C Coding Standard rule `INT17-C. Define integer 
+constants in an implementation-independent manner
+<https://wiki.sei.cmu.edu/confluence/display/c/INT17-C.+Define+integer+constants+in+an+implementation-independent+manner>`_.
+
+.. code-block:: c
+    
+    unsigned long flip_bits(unsigned long x) {
+      return x ^ 0xFFFFFFFF;
+      // The right way to write this would be ULONG_MAX, or -1.
+    }
+
+.. code-block:: c++
+
+    const unsigned long mask = 0b1000'0000'0000'0000'0000'0000'0000'0000; // warn
+    // The right way to write this would be ~(ULONG_MAX >> 1).
+    unsigned long x;
+
+    x &= mask;
+
+.. code-block:: c++
+
+    const unsigned long bit_to_set = 0x00010000; // warn
+    // Incorrectly assumes that long is 4 bytes.
+
+    unsigned long set_bit(unsigned long x) {
+      return x | bit_to_set;
+    }
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -152,6 +152,7 @@
    `cert-err58-cpp <cert/err58-cpp.html>`_,
    `cert-err60-cpp <cert/err60-cpp.html>`_,
    `cert-flp30-c <cert/flp30-c.html>`_,
+   `cert-int17-c <cert/int17-c.html>`_,
    `cert-mem57-cpp <cert/mem57-cpp.html>`_,
    `cert-msc50-cpp <cert/msc50-cpp.html>`_,
    `cert-msc51-cpp <cert/msc51-cpp.html>`_,
@@ -327,6 +328,7 @@
    `performance-type-promotion-in-math-fn <performance/type-promotion-in-math-fn.html>`_, "Yes"
    `performance-unnecessary-copy-initialization <performance/unnecessary-copy-initialization.html>`_, "Yes"
    `performance-unnecessary-value-param <performance/unnecessary-value-param.html>`_, "Yes"
+   `portability-non-portable-integer-constant <portability/non-portable-integer-constant.html>`_,
    `portability-restrict-system-includes <portability/restrict-system-includes.html>`_, "Yes"
    `portability-simd-intrinsics <portability/simd-intrinsics.html>`_,
    `portability-std-allocator-const <portability/std-allocator-const.html>`_,
Index: clang-tools-extra/docs/clang-tidy/checks/cert/int17-c.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/cert/int17-c.rst
@@ -0,0 +1,10 @@
+.. title:: clang-tidy - cert-int17-c
+.. meta::
+   :http-equiv=refresh: 5;URL=../portability/non-portable-integer-constant.html
+
+cert-int17-c
+============
+
+The cert-int17-c check is an alias, please see
+`portability-non-portable-integer-constant <../portability/non-portable-integer-constant.html>`_
+for more information.
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -133,6 +133,16 @@
   Checks that all implicit and explicit inline functions in header files are
   tagged with the ``LIBC_INLINE`` macro.
 
+- New :doc:`portability-non-portable-integer-constant
+  <clang-tidy/checks/portability-non-portable-integer-constant>` check.
+
+  Finds masks that are being used in a non-portable manner.
+
+- New :doc:`portability-non-portable-integer-constant
+  <clang-tidy/checks/portability/non-portable-integer-constant>` check.
+
+  Finds integer literals that are being used in a non-portable manner.
+
 New check aliases
 ^^^^^^^^^^^^^^^^^
 
Index: clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
+++ clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp
@@ -9,6 +9,7 @@
 #include "../ClangTidy.h"
 #include "../ClangTidyModule.h"
 #include "../ClangTidyModuleRegistry.h"
+#include "NonPortableIntegerConstantCheck.h"
 #include "RestrictSystemIncludesCheck.h"
 #include "SIMDIntrinsicsCheck.h"
 #include "StdAllocatorConstCheck.h"
@@ -19,6 +20,8 @@
 class PortabilityModule : public ClangTidyModule {
 public:
   void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+    CheckFactories.registerCheck<NonPortableIntegerConstantCheck>(
+        "portability-non-portable-integer-constant");
     CheckFactories.registerCheck<RestrictSystemIncludesCheck>(
         "portability-restrict-system-includes");
     CheckFactories.registerCheck<SIMDIntrinsicsCheck>(
Index: clang-tools-extra/clang-tidy/portability/NonPortableIntegerConstantCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/portability/NonPortableIntegerConstantCheck.h
@@ -0,0 +1,30 @@
+//===--- NonPortableIntegerConstantCheck.h - clang-tidy ---------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_NON_PORTABLE_INTEGER_CONSTANT_CHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_NON_PORTABLE_INTEGER_CONSTANT_CHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::portability {
+
+/// Finds integer literals that are being used in a non-portable manner.
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/portability/non-portable-integer-constant.html
+class NonPortableIntegerConstantCheck : public ClangTidyCheck {
+public:
+  NonPortableIntegerConstantCheck(StringRef Name, ClangTidyContext *Context)
+      : ClangTidyCheck(Name, Context) {}
+  void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+  void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace clang::tidy::portability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_NON_PORTABLE_INTEGER_CONSTANT_CHECK_H
Index: clang-tools-extra/clang-tidy/portability/NonPortableIntegerConstantCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/portability/NonPortableIntegerConstantCheck.cpp
@@ -0,0 +1,166 @@
+//===--- NonPortableIntegerConstantCheck.cpp - clang-tidy -----------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "NonPortableIntegerConstantCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::portability {
+namespace {
+
+struct SanitizedLiteralType {
+  StringRef StrippedLiteral;
+  // The exact bit value of the MSB.
+  std::size_t MSBBit;
+  // The bit value of MSB rounded up to the nearest byte.
+  std::size_t MSBByte;
+  std::size_t Radix;
+};
+
+} // namespace
+
+// Does not calculate true MSB - only takes the value of the first digit into
+// account alongside the total digit count. Returns MSB zero if radix is 10, and
+// MSBBit zero if first digit is 0.
+static SanitizedLiteralType sanitizeAndCountBits(std::string &IntegerLiteral) {
+  llvm::erase_value(IntegerLiteral, '\''); // Skip digit separators.
+  StringRef StrippedLiteral{IntegerLiteral};
+
+  const auto MSBBit = [&StrippedLiteral](std::size_t RemainingBitCount)
+                        -> std::size_t {
+    char FirstDigit = StrippedLiteral.front();
+
+    if (FirstDigit == '0') {
+      return 0;
+    } else if (FirstDigit <= '1') {
+      return RemainingBitCount + 1;
+    } else if (FirstDigit <= '3') {
+      return RemainingBitCount + 2;
+    } else if (FirstDigit <= '7') {
+      return RemainingBitCount + 3;
+    } else {
+      return RemainingBitCount + 4;
+    }
+  };
+
+  if(StrippedLiteral.consume_front("0b"))
+  {
+    StrippedLiteral = StrippedLiteral.take_while(
+      [](char c){return c == '0' || c == '1'; });
+    assert(!StrippedLiteral.empty());
+
+    return { StrippedLiteral,
+        MSBBit(StrippedLiteral.size() - 1), 
+        StrippedLiteral.size(), 
+        2 };
+  }
+  else if(StrippedLiteral.consume_front("0x"))
+  {
+    StrippedLiteral = StrippedLiteral.take_while(llvm::isHexDigit);
+    assert(!StrippedLiteral.empty());
+
+    return { StrippedLiteral,
+      MSBBit((StrippedLiteral.size() - 1) * 4),
+      StrippedLiteral.size() * 4,
+      16 };
+  }
+  else if(StrippedLiteral != "0" && StrippedLiteral.consume_front("0"))
+  {
+    StrippedLiteral = StrippedLiteral.take_while(
+      [](char c){ return c >= '0' || c <= '7'; });
+    assert(!StrippedLiteral.empty());
+
+    return { StrippedLiteral,
+        MSBBit((StrippedLiteral.size() - 1) * 3),
+        StrippedLiteral.size() * 3,
+        8 };
+  } else {
+    return { StrippedLiteral, 0, 0, 10 };
+  }
+}
+
+void NonPortableIntegerConstantCheck::registerMatchers(MatchFinder *Finder) {
+  Finder->addMatcher(integerLiteral().bind("integer"), this);
+}
+
+
+void NonPortableIntegerConstantCheck::check(
+    const MatchFinder::MatchResult &Result) {
+  const auto *MatchedInt = Result.Nodes.getNodeAs<IntegerLiteral>("integer");
+
+  assert(MatchedInt);
+
+  QualType IntegerLiteralType = MatchedInt->getType();
+  auto LiteralBitWidth = Result.Context->getTypeSize( IntegerLiteralType );
+
+  llvm::APInt LiteralValue = MatchedInt->getValue();
+
+  std::string LiteralStr =
+      Lexer::getSourceText(
+          CharSourceRange::getTokenRange(MatchedInt->getSourceRange()),
+          *Result.SourceManager, Result.Context->getLangOpts(), nullptr)
+          .lower();
+
+  // FIXME: There are two problematic cases where we cannot read the character.
+  // With macros, in some cases (such as when not passing an argument) the
+  // integer literal's token range will be 0 long.
+  if (LiteralStr.empty())
+    return;
+  // A template function with an integer literal template argument will warn in
+  // both the argument, and the function body. In the instantiated body, the
+  // source range will contain the argument name, not the literal.
+  // FIXME: This disables checking macro literals entirely.
+  if (!llvm::isDigit(LiteralStr[0]))
+    return;
+
+  const SanitizedLiteralType SanitizedLiteral = sanitizeAndCountBits(LiteralStr);
+
+  // Only potential edge case is "0", handled by sanitizeAndCountBits.
+  assert(!SanitizedLiteral.StrippedLiteral.empty() &&
+         "integer literal should not be empty");
+
+  assert(SanitizedLiteral.MSBBit <= LiteralBitWidth &&
+         "integer literal has more bits set than its bit width");
+
+  bool IsMax = LiteralValue.isMaxValue() || LiteralValue.isMaxSignedValue();
+  bool IsUnsignedMaxMinusOne = (LiteralValue + 1).isMaxValue();
+  bool IsMin = LiteralValue.isMinValue() || LiteralValue.isMinSignedValue();
+  bool RepresentsZero = LiteralValue.isNullValue();
+
+  bool IsMSBBitUsed = SanitizedLiteral.MSBBit == LiteralBitWidth;
+  // Can be greater, eg. an 8-bit UCHAR_MAX byte value represented by 377 octal
+  bool IsMSBByteUsed = SanitizedLiteral.MSBByte >= LiteralBitWidth;
+  bool HasLeadingZeroes = SanitizedLiteral.StrippedLiteral[0] == '0';
+
+  if (IsMax || IsUnsignedMaxMinusOne) {
+    diag(MatchedInt->getBeginLoc(),
+         "non-portable integer literal: hardcoded platform-specific maximum value");
+  } else if (IsMin && !RepresentsZero) {
+    diag(MatchedInt->getBeginLoc(),
+         "non-portable integer literal: hardcoded platform-specific minimum value");
+  } else if (HasLeadingZeroes && !RepresentsZero) {
+    diag(MatchedInt->getBeginLoc(),
+         "non-portable integer literal: integer literal with leading zeroes");
+  // Matches only the most significant bit,
+  // eg. unsigned value 0x80000000.
+  } else if (IsMSBBitUsed) {
+    diag(MatchedInt->getBeginLoc(),
+         "non-portable integer literal: should not rely on the most significant bit");
+  // Matches the most significant byte,
+  // eg. literals like 0x30000000.
+  } else if (IsMSBByteUsed) {
+    diag(MatchedInt->getBeginLoc(),
+         "non-portable integer literal: should not rely on bits of most significant byte");
+  }
+}
+
+} // namespace clang::tidy::portability
Index: clang-tools-extra/clang-tidy/portability/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/portability/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/portability/CMakeLists.txt
@@ -5,6 +5,7 @@
   )
 
 add_clang_library(clangTidyPortabilityModule
+  NonPortableIntegerConstantCheck.cpp
   PortabilityTidyModule.cpp
   RestrictSystemIncludesCheck.cpp
   SIMDIntrinsicsCheck.cpp
Index: clang-tools-extra/clang-tidy/cert/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/cert/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/cert/CMakeLists.txt
@@ -27,6 +27,7 @@
   clangTidyGoogleModule
   clangTidyMiscModule
   clangTidyPerformanceModule
+  clangTidyPortabilityModule
   clangTidyReadabilityModule
   clangTidyUtils
 
Index: clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
+++ clang-tools-extra/clang-tidy/cert/CERTTidyModule.cpp
@@ -25,6 +25,7 @@
 #include "../misc/StaticAssertCheck.h"
 #include "../misc/ThrowByValueCatchByReferenceCheck.h"
 #include "../performance/MoveConstructorInitCheck.h"
+#include "../portability/NonPortableIntegerConstantCheck.h"
 #include "../readability/UppercaseLiteralSuffixCheck.h"
 #include "CommandProcessorCheck.h"
 #include "DefaultOperatorNewAlignmentCheck.h"
@@ -302,6 +303,9 @@
         "cert-flp37-c");
     // FIO
     CheckFactories.registerCheck<misc::NonCopyableObjectsCheck>("cert-fio38-c");
+    // INT
+    CheckFactories.registerCheck<portability::NonPortableIntegerConstantCheck>(
+        "cert-int17-c");
     // MSC
     CheckFactories.registerCheck<bugprone::UnsafeFunctionsCheck>(
         "cert-msc24-c");
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to