[clang] [clang-tools-extra] [clang-tidy] Add portability-avoid-platform-specific-fundamental-types (PR #146970)
JJ Marr via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 24 13:28:03 PDT 2025
================
@@ -0,0 +1,241 @@
+.. title:: clang-tidy - portability-avoid-platform-specific-fundamental-types
+
+portability-avoid-platform-specific-fundamental-types
+=====================================================
+
+Detects fundamental types (``int``, ``short``, ``long``, ``long long``, ``char``
+, ``float``, etc) and warns against their use due to platform-dependent
+behavior.
+
+This check detects fundamental types (``int``, ``short``, ``long``, ``float``,
+``char`` and their ``unsigned`` or ``signed`` variants) and warns against their
+use due to non-standard platform-dependent behavior. For example, ``long`` is
+64 bits on Linux but 32 bits on Windows. There is no standard rationale or
+intent for the sizes of these types.
+
+Instead of fundamental types, use fixed-width types such as ``int32_t`` or
+implementation-defined types with standard semantics, e.g. ``int_fast32_t`` for
+the fastest integer type greater than or equal to 32 bits.
+
+Examples
+--------
+
+.. code-block:: c++
+
+ // Bad: platform-dependent fundamental types
+ int global_int = 42;
+ short global_short = 10;
+ long global_long = 100L;
+ unsigned long global_unsigned_long = 100UL;
+
+ void function_with_int_param(int param) {
+ // ...
+ }
+
+ int function_returning_int() {
+ return 42;
+ }
+
+ struct MyStruct {
+ int member_int;
+ long member_long;
+ };
+
+.. code-block:: c++
+
+ // Good: use fixed-width types or typedefs
+ #include <cstdint>
+
+ int32_t global_int32 = 42;
+ int16_t global_int16 = 10;
+ int64_t global_int64 = 100L;
+ uint64_t global_uint64 = 100UL;
+
+ void function_with_int32_param(int32_t param) {
+ // ...
+ }
+
+ int32_t function_returning_int32() {
+ return 42;
+ }
+
+ struct MyStruct {
+ int32_t member_int32;
+ int64_t member_int64;
+ };
+
+The check will also warn about typedef declarations that use fundamental types
+as their underlying type:
+
+.. code-block:: c++
+
+ // Bad: typedef using fundamental type
+ typedef long long MyLongType;
+ using MyIntType = int;
+
+.. code-block:: c++
+
+ // Good: use descriptive names or fixed-width types
+ typedef int64_t TimestampType;
+ using CounterType = uint32_t;
+
+Rationale
+---------
+
+Fundamental types have platform-dependent sizes and behavior:
+
+- ``int`` is typically 32 bits on modern platforms but is only guaranteed to be
+ 16 bits by the spec
+- ``long int`` is 32 bits on Windows but 64 bits on most Unix systems
+- ``double`` is typically 64-bit IEEE754, but on some microcontrollers without
+ a 64-bit FPU (e.g. certain Arduinos) it can be 32 bits
+- ``char`` is signed on ARM and unsigned on x86
+
+For historical reasons, the C++ standard allows the implementation to define
+the size and representation of these types. They communicate intent in
+non-standard ways and are often needlessly incompatible.
+
+For example, ``int`` was traditionally the word size of a given processor in
+16-bit and 32-bit computing and was a reasonable default for performance. This
+is no longer true on modern 64-bit computers, but the size of ``int`` remains
+fixed at 32 bits for backwards compatibility with code that relied on a 32-bit
+implementation of ``int``.
+
+If code is explicitly relying on the size of an ``int`` being 32 bits, it is
+better to say so in the typename with ``int32_t``. Otherwise, use an
+appropriate implementation-defined type such as ``fast_int32_t`` or
+``least_int32_t`` that communicates the appropriate time/space tradeoff.
+
+Likewise, ``float`` and ``double`` should be replaced by ``float32_t`` and
+``float64_t`` which are guaranteed to be standard IEEE754 floats for a given
+size.
+
+``char`` should be replaced by ``char8_t`` when used in the representation of
+Unicode text. When used to represent a byte on a given platform, ``std::byte``
+is an appropriate replacement.
+
+Types Not Flagged
+-----------------
+
+The following types are intentionally not flagged:
+
+- ``bool`` (boolean type)
+- Standard library typedefs like ``size_t``, ``ptrdiff_t``, or ``uint32_t``.
+- Already typedef'd types, though the check will flag the typedef itself
+
+``bool`` is excluded because it can only be true or false, and is not
+vulnerable to overflow or narrowing issues that occur as a result of using
+types of an implementation-defined size.
+
+Options
+-------
+
+.. option:: WarnOnInts
+
+ When `true`, the check will warn about fundamental integer types
+ (``short``, ``int``, ``long``, ``long long`` and their ``signed`` and
+ ``unsigned`` variants).
+ When `false`, integer types are not flagged. Default is `true`
+
+ Example with :option:`WarnOnInts` enabled:
----------------
jj-marr wrote:
@vbvictor The intent is to give examples of when you may choose different alternatives. For `float` and `char` it is obvious, honestly (`float`->`float32_t` on most platforms, `char`->`char8_t` unless doing bit-level stuff). For replacing `int` it is less obvious. In my own experience, I replace `int` with `size_t` in loop counters and the vast majority of "countable" uses, or `uint32_t` for when I need to do bit-level stuff.
I think if `int` is the only option we need examples for, it is superfluous to have that example in Options.
https://github.com/llvm/llvm-project/pull/146970
More information about the cfe-commits
mailing list