diff --git a/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.cpp b/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.cpp new file mode 100644 index 0000000000000..3543abb5604f1 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.cpp @@ -0,0 +1,254 @@ +//===--- AvoidPlatformSpecificFundamentalTypesCheck.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 "AvoidPlatformSpecificFundamentalTypesCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/TargetInfo.h" + +using namespace clang::ast_matchers; + +namespace { + +static bool isCharType(const clang::BuiltinType *BT) { + using clang::BuiltinType; + switch (BT->getKind()) { + case BuiltinType::Char_S: + case BuiltinType::Char_U: + case BuiltinType::SChar: + case BuiltinType::UChar: + return true; + default: + return false; + } +} + +AST_MATCHER(clang::QualType, isBuiltinInt) { + const auto *BT = Node->getAs(); + if (!BT) + return false; + + switch (BT->getKind()) { + case clang::BuiltinType::Short: + case clang::BuiltinType::UShort: + case clang::BuiltinType::Int: + case clang::BuiltinType::UInt: + case clang::BuiltinType::Long: + case clang::BuiltinType::ULong: + case clang::BuiltinType::LongLong: + case clang::BuiltinType::ULongLong: + return true; + default: + return false; + } +} + +AST_MATCHER(clang::QualType, isBuiltinFloat) { + const auto *BT = Node->getAs(); + if (!BT) + return false; + + switch (BT->getKind()) { + case clang::BuiltinType::Half: + case clang::BuiltinType::BFloat16: + case clang::BuiltinType::Float: + case clang::BuiltinType::Double: + case clang::BuiltinType::LongDouble: + return true; + default: + return false; + } +} + +AST_MATCHER(clang::QualType, isBuiltinChar) { + const auto *BT = Node->getAs(); + if (!BT) + return false; + + return isCharType(BT); +} +} // namespace + +namespace clang::tidy::portability { + +AvoidPlatformSpecificFundamentalTypesCheck:: + AvoidPlatformSpecificFundamentalTypesCheck(StringRef Name, + ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + WarnOnFloats(Options.get("WarnOnFloats", true)), + WarnOnInts(Options.get("WarnOnInts", true)), + WarnOnChars(Options.get("WarnOnChars", true)), + IncludeInserter(Options.getLocalOrGlobal("IncludeStyle", + utils::IncludeSorter::IS_LLVM), + areDiagsSelfContained()) {} + +void AvoidPlatformSpecificFundamentalTypesCheck::registerPPCallbacks( + const SourceManager &SM, Preprocessor *PP, Preprocessor *ModuleExpanderPP) { + IncludeInserter.registerPreprocessor(PP); +} + +void AvoidPlatformSpecificFundamentalTypesCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "WarnOnFloats", WarnOnFloats); + Options.store(Opts, "WarnOnInts", WarnOnInts); + Options.store(Opts, "WarnOnChars", WarnOnChars); + Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle()); +} + +static std::string getFloatReplacement(const BuiltinType *BT, + ASTContext &Context) { + const TargetInfo &Target = Context.getTargetInfo(); + + auto GetReplacementType = [](unsigned Width) { + switch (Width) { + // This is ambiguous by default since it could be bfloat16 or float16 + case 16U: + return ""; + case 32U: + return "float32_t"; + case 64U: + return "float64_t"; + case 128U: + return "float128_t"; + default: + return ""; + } + }; + + switch (BT->getKind()) { + // Not an ambiguous type + case BuiltinType::BFloat16: + return "bfloat16_t"; + case BuiltinType::Half: + return GetReplacementType(Target.getHalfWidth()); + case BuiltinType::Float: + return GetReplacementType(Target.getFloatWidth()); + case BuiltinType::Double: + return GetReplacementType(Target.getDoubleWidth()); + default: + return ""; + } +} + +void AvoidPlatformSpecificFundamentalTypesCheck::registerMatchers( + MatchFinder *Finder) { + auto PlatformSpecificFundamentalType = qualType( + allOf(builtinType(), + anyOf(WarnOnInts ? isBuiltinInt() : unless(anything()), + WarnOnFloats ? isBuiltinFloat() : unless(anything()), + WarnOnChars ? isBuiltinChar() : unless(anything())))); + + if (!WarnOnInts && !WarnOnFloats && !WarnOnChars) + return; + + Finder->addMatcher( + varDecl(hasType(PlatformSpecificFundamentalType)).bind("var_decl"), this); + + Finder->addMatcher( + functionDecl(returns(PlatformSpecificFundamentalType)).bind("func_decl"), + this); + + Finder->addMatcher( + parmVarDecl(hasType(PlatformSpecificFundamentalType)).bind("param_decl"), + this); + + Finder->addMatcher( + fieldDecl(hasType(PlatformSpecificFundamentalType)).bind("field_decl"), + this); + + Finder->addMatcher( + typedefDecl(hasUnderlyingType(PlatformSpecificFundamentalType)) + .bind("typedef_decl"), + this); + + Finder->addMatcher(typeAliasDecl(hasType(PlatformSpecificFundamentalType)) + .bind("alias_decl"), + this); +} + +void AvoidPlatformSpecificFundamentalTypesCheck::check( + const MatchFinder::MatchResult &Result) { + SourceLocation Loc; + QualType QT; + SourceRange TypeRange; + + auto SetTypeRange = [&TypeRange](auto Decl) { + if (Decl->getTypeSourceInfo()) + TypeRange = Decl->getTypeSourceInfo()->getTypeLoc().getSourceRange(); + }; + + if (const auto *VD = Result.Nodes.getNodeAs("var_decl")) { + Loc = VD->getLocation(); + QT = VD->getType(); + SetTypeRange(VD); + } else if (const auto *FD = + Result.Nodes.getNodeAs("func_decl")) { + Loc = FD->getLocation(); + QT = FD->getReturnType(); + SetTypeRange(FD); + } else if (const auto *PD = + Result.Nodes.getNodeAs("param_decl")) { + Loc = PD->getLocation(); + QT = PD->getType(); + SetTypeRange(PD); + } else if (const auto *FD = Result.Nodes.getNodeAs("field_decl")) { + Loc = FD->getLocation(); + QT = FD->getType(); + SetTypeRange(FD); + } else if (const auto *TD = + Result.Nodes.getNodeAs("typedef_decl")) { + Loc = TD->getLocation(); + QT = TD->getUnderlyingType(); + SetTypeRange(TD); + } else if (const auto *AD = + Result.Nodes.getNodeAs("alias_decl")) { + Loc = AD->getLocation(); + QT = AD->getUnderlyingType(); + SetTypeRange(AD); + } else { + return; + } + + const std::string TypeName = QT.getAsString(); + + const auto *BT = QT->getAs(); + + if (BT->isFloatingPoint()) { + const std::string Replacement = getFloatReplacement(BT, *Result.Context); + if (!Replacement.empty()) { + auto Diag = + diag(Loc, "avoid using platform-dependent floating point type '%0'; " + "consider using '%1' instead") + << TypeName << Replacement; + + if (TypeRange.isValid()) + Diag << FixItHint::CreateReplacement(TypeRange, Replacement); + + if (auto IncludeFixit = IncludeInserter.createIncludeInsertion( + Result.SourceManager->getFileID(Loc), "")) { + Diag << *IncludeFixit; + } + } else { + diag(Loc, "avoid using platform-dependent floating point type '%0'; " + "consider using a typedef or fixed-width type instead") + << TypeName; + } + } else if (isCharType(BT)) { + diag(Loc, "avoid using platform-dependent character type '%0'; " + "consider using char8_t for text or std::byte for bytes") + << TypeName; + } else { + diag(Loc, "avoid using platform-dependent fundamental integer type '%0'; " + "consider using a typedef or fixed-width type instead") + << TypeName; + } +} + +} // namespace clang::tidy::portability diff --git a/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.h b/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.h new file mode 100644 index 0000000000000..95e62d983f65d --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.h @@ -0,0 +1,50 @@ +//==-- AvoidPlatformSpecificFundamentalTypesCheck.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_AVOIDPLATFORMSPECIFICFUNDAMENTALTYPESCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPLATFORMSPECIFICFUNDAMENTALTYPESCHECK_H + +#include "../ClangTidyCheck.h" +#include "../utils/IncludeInserter.h" + +namespace clang::tidy::portability { + +/// Find fundamental platform-dependent types and recommend using typedefs or +/// fixed-width types. +/// +/// Detects fundamental types (int, short, long, long long, char, float, etc) +/// and warns against their use due to platform-dependent behavior. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/portability/avoid-platform-specific-fundamental-types.html +class AvoidPlatformSpecificFundamentalTypesCheck : public ClangTidyCheck { +public: + AvoidPlatformSpecificFundamentalTypesCheck(StringRef Name, + ClangTidyContext *Context); + void registerPPCallbacks(const SourceManager &SM, Preprocessor *PP, + Preprocessor *ModuleExpanderPP) override; + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override { + return LangOpts.CPlusPlus11; + } + std::optional getCheckTraversalKind() const override { + return TK_IgnoreUnlessSpelledInSource; + } + +private: + const bool WarnOnFloats; + const bool WarnOnInts; + const bool WarnOnChars; + utils::IncludeInserter IncludeInserter; +}; + +} // namespace clang::tidy::portability + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_PORTABILITY_AVOIDPLATFORMSPECIFICFUNDAMENTALTYPESCHECK_H diff --git a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt index 73d74a550afc0..014c1ea883547 100644 --- a/clang-tools-extra/clang-tidy/portability/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/portability/CMakeLists.txt @@ -5,6 +5,7 @@ set(LLVM_LINK_COMPONENTS ) add_clang_library(clangTidyPortabilityModule STATIC + AvoidPlatformSpecificFundamentalTypesCheck.cpp AvoidPragmaOnceCheck.cpp PortabilityTidyModule.cpp RestrictSystemIncludesCheck.cpp diff --git a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp index 98853556588b3..2e2ddb91934f3 100644 --- a/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/portability/PortabilityTidyModule.cpp @@ -9,6 +9,7 @@ #include "../ClangTidy.h" #include "../ClangTidyModule.h" #include "../ClangTidyModuleRegistry.h" +#include "AvoidPlatformSpecificFundamentalTypesCheck.h" #include "AvoidPragmaOnceCheck.h" #include "RestrictSystemIncludesCheck.h" #include "SIMDIntrinsicsCheck.h" @@ -21,6 +22,8 @@ namespace portability { class PortabilityModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override { + CheckFactories.registerCheck( + "portability-avoid-platform-specific-fundamental-types"); CheckFactories.registerCheck( "portability-avoid-pragma-once"); CheckFactories.registerCheck( diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 198efee7754de..0815b7f844c0b 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -148,6 +148,13 @@ New checks Finds uses of ``std::lock_guard`` and suggests replacing them with C++17's alternative ``std::scoped_lock``. +- New :doc:`portability-avoid-platform-specific-fundamental-types + ` + check. + + Finds fundamental types (e.g. `int`, `float`) and recommends using typedefs + or fixed-width types instead to improve portability across different platforms. + - New :doc:`portability-avoid-pragma-once ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 5098582d0c42b..73d9ef7039ac4 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -353,6 +353,7 @@ Clang-Tidy Checks :doc:`performance-type-promotion-in-math-fn `, "Yes" :doc:`performance-unnecessary-copy-initialization `, "Yes" :doc:`performance-unnecessary-value-param `, "Yes" + :doc:`portability-avoid-platform-specific-fundamental-types `, :doc:`portability-avoid-pragma-once `, :doc:`portability-restrict-system-includes `, "Yes" :doc:`portability-simd-intrinsics `, diff --git a/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-platform-specific-fundamental-types.rst b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-platform-specific-fundamental-types.rst new file mode 100644 index 0000000000000..28b29544724fd --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-platform-specific-fundamental-types.rst @@ -0,0 +1,227 @@ +.. title:: clang-tidy - portability-avoid-platform-specific-fundamental-types + +portability-avoid-platform-specific-fundamental-types +===================================================== + +Finds fundamental types (e.g. `int`, `float`) and recommends using typedefs +or fixed-width types instead to improve portability across different platforms. + +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 + + 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 + +The C++ specification does not define these types beyond their minimum sizes. +That means they can 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 implementation-defined types. + +Options +------- + +.. option:: WarnOnInts + + When `true` (default), 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. + + Example with ``WarnOnInts`` enabled (default): + + .. code-block:: c++ + + // Bad: platform-dependent integer types + #include + + int counter = 0; + long timestamp = 12345L; + unsigned short port = 8080; + + std::vector vec; + // If int is 32 bits and (vec.size > 2^31 - 1), this overflows + for(int i = 0; i + #include + + int32_t counter = 0; // When you need exactly 32 bits + int64_t timestamp = 12345L; // When you need exactly 64 bits + uint16_t port = 8080; // When you need exactly 16 unsigned bits + std::vector vec; + // A size_t is the maximum size of an object on a given platform + for(size_t i = 0U; i // C++23 + + float32_t pi = 3.14f; + float64_t e = 2.71828; + +.. option:: WarnOnChars + + When `true` (default), the check will warn about character types (``char``, ``signed char``, and ``unsigned char``). + When `false`, character types are not flagged. + + Character types can have platform-dependent behavior: + + - ``char`` can be either signed or unsigned depending on the platform (signed on ARM, unsigned on x86) + - The signedness of ``char`` affects comparisons and arithmetic operations + + When this option is enabled, the check will suggest using explicit signedness or typedefs + to make the intent clear and ensure consistent behavior across platforms. + + Example with ``WarnOnChars`` enabled: + + .. code-block:: c++ + + // Bad: platform-dependent character types + char buffer[256]; + signed char byte_value = -1; + unsigned char raw_byte = 255; + + .. code-block:: c++ + + // Good: use explicit types or typedefs + using byte_t = unsigned char; // For raw byte data + using text_char_t = char; // For text (when signedness doesn't matter) + + text_char_t buffer[256]; // For text storage + int8_t signed_byte = -1; // For signed 8-bit values + uint8_t raw_byte = 255; // For unsigned 8-bit values diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-chars.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-chars.cpp new file mode 100644 index 0000000000000..9648a84bf64cb --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-chars.cpp @@ -0,0 +1,69 @@ +// RUN: %check_clang_tidy -std=c++11-or-later %s portability-avoid-platform-specific-fundamental-types %t -- -config="{CheckOptions: [{key: portability-avoid-platform-specific-fundamental-types.WarnOnInts, value: false}, {key: portability-avoid-platform-specific-fundamental-types.WarnOnFloats, value: false}]}" + +// Test character types that should trigger warnings when WarnOnChars is enabled +char global_char = 'a'; +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid using platform-dependent character type 'char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +signed char global_signed_char = 'b'; +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: avoid using platform-dependent character type 'signed char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +unsigned char global_unsigned_char = 'c'; +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid using platform-dependent character type 'unsigned char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +// Function parameters +void func_with_char_param(char param) {} +// CHECK-MESSAGES: :[[@LINE-1]]:32: warning: avoid using platform-dependent character type 'char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +void func_with_signed_char_param(signed char param) {} +// CHECK-MESSAGES: :[[@LINE-1]]:46: warning: avoid using platform-dependent character type 'signed char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +void func_with_unsigned_char_param(unsigned char param) {} +// CHECK-MESSAGES: :[[@LINE-1]]:50: warning: avoid using platform-dependent character type 'unsigned char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +// Function return types +char func_returning_char() { return 'a'; } +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid using platform-dependent character type 'char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +signed char func_returning_signed_char() { return 'b'; } +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: avoid using platform-dependent character type 'signed char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +unsigned char func_returning_unsigned_char() { return 'c'; } +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid using platform-dependent character type 'unsigned char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +// Struct fields +struct TestStruct { + char field_char; + // CHECK-MESSAGES: :[[@LINE-1]]:8: warning: avoid using platform-dependent character type 'char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + + signed char field_signed_char; + // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid using platform-dependent character type 'signed char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + + unsigned char field_unsigned_char; + // CHECK-MESSAGES: :[[@LINE-1]]:17: warning: avoid using platform-dependent character type 'unsigned char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] +}; + +// Typedefs +typedef char char_typedef; +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using platform-dependent character type 'char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +typedef signed char signed_char_typedef; +// CHECK-MESSAGES: :[[@LINE-1]]:21: warning: avoid using platform-dependent character type 'signed char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +typedef unsigned char unsigned_char_typedef; +// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: avoid using platform-dependent character type 'unsigned char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +// Type aliases (C++11) +using char_alias = char; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent character type 'char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +using signed_char_alias = signed char; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent character type 'signed char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +using unsigned_char_alias = unsigned char; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent character type 'unsigned char'; consider using char8_t for text or std::byte for bytes [portability-avoid-platform-specific-fundamental-types] + +// Test that integer and float types are NOT flagged when their options are disabled +int should_not_warn_int = 42; +long should_not_warn_long = 100L; +float should_not_warn_float = 3.14f; +double should_not_warn_double = 2.71828; diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-floats.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-floats.cpp new file mode 100644 index 0000000000000..9e4605aacec67 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-floats.cpp @@ -0,0 +1,104 @@ +// RUN: %check_clang_tidy -std=c++23-or-later %s portability-avoid-platform-specific-fundamental-types %t -- -config="{CheckOptions: [{key: portability-avoid-platform-specific-fundamental-types.WarnOnInts, value: false}, {key: portability-avoid-platform-specific-fundamental-types.WarnOnChars, value: false}]}" + +// Mock fixed-width float types +// In reality, these types are aliases to "extended floating point types", and +// are not typedefs. However, there isn't a good way to mock extended floats as +// they are not fundamental types. +// NOLINTBEGIN(portability-avoid-platform-specific-fundamental-types) +typedef float float32_t; +typedef double float64_t; +// NOLINTEND(portability-avoid-platform-specific-fundamental-types) + +// Test floating point types that should trigger warnings when WarnOnFloats is enabled +float global_float = 3.14f; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] +// CHECK-FIXES: float32_t global_float = 3.14f; + +double global_double = 3.14159; +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] +// CHECK-FIXES: float64_t global_double = 3.14159; + + +// Test function parameters with float types +void function_with_float_param(float param) { +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] +} + +void function_with_double_param(double param) { +// CHECK-MESSAGES: :[[@LINE-1]]:40: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] +} + +// Test function return types with float types +float function_returning_float() { +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] + return 3.14f; +} + +double function_returning_double() { +// CHECK-MESSAGES: :[[@LINE-1]]:8: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] + return 3.14159; +} + +// Test local variables with float types +void test_local_float_variables() { + float local_float = 2.71f; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] + + double local_double = 2.71828; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] + + // Fixed-width types should not trigger warnings + float32_t local_float32 = 3.14f; + float64_t local_float64 = 3.14159; +} + +// Test struct/class members with float types +struct TestFloatStruct { + float member_float; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] + + double member_double; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] + + // Fixed-width types should not trigger warnings + float32_t member_float32; + float64_t member_float64; +}; + +class TestFloatClass { +public: + float public_float_member; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] + +private: + double private_double_member; + // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] +}; + +// Test typedefs and type aliases with float types +typedef float MyFloat; +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] + +using MyDouble = double; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] + +// Test template specializations with float types +template +void template_function(T param) {} + +template<> +void template_function(float param) { +// CHECK-MESSAGES: :[[@LINE-1]]:37: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] +} + +template<> +void template_function(double param) { +// CHECK-MESSAGES: :[[@LINE-1]]:39: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] +} + +// Test that integer and char types are NOT flagged when their options are disabled +int should_not_warn_int = 42; +long should_not_warn_long = 100L; +char should_not_warn_char = 'a'; +signed char should_not_warn_signed_char = 'b'; +unsigned char should_not_warn_unsigned_char = 'c'; diff --git a/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-ints.cpp b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-ints.cpp new file mode 100644 index 0000000000000..a4b40595245ce --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-ints.cpp @@ -0,0 +1,147 @@ +// RUN: %check_clang_tidy -std=c++11-or-later %s portability-avoid-platform-specific-fundamental-types %t -- -config="{CheckOptions: [{key: portability-avoid-platform-specific-fundamental-types.WarnOnChars, value: false}, {key: portability-avoid-platform-specific-fundamental-types.WarnOnFloats, value: false}]}" + +// Mock fixed-width integer types +// NOLINTBEGIN(portability-avoid-platform-specific-fundamental-types) +typedef unsigned int uint32_t; +typedef int int32_t; +typedef unsigned long long uint64_t; +typedef long long int64_t; + +// Mock standard library semantic types +typedef long ptrdiff_t; +// MSVC defines size_t automatically +#ifndef _MSC_VER +typedef unsigned long size_t; +#endif +// NOLINTEND(portability-avoid-platform-specific-fundamental-types) + +// Test fundamental integer types that should trigger warnings +int global_int = 42; +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +short global_short = 10; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +long global_long = 100L; +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +long long global_long_long = 1000LL; +// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent fundamental integer type 'long long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +unsigned int global_unsigned_int = 42U; +// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using platform-dependent fundamental integer type 'unsigned int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +unsigned short global_unsigned_short = 10U; +// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: avoid using platform-dependent fundamental integer type 'unsigned short'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +unsigned long global_unsigned_long = 100UL; +// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid using platform-dependent fundamental integer type 'unsigned long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +unsigned long long global_unsigned_long_long = 1000ULL; +// CHECK-MESSAGES: :[[@LINE-1]]:20: warning: avoid using platform-dependent fundamental integer type 'unsigned long long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +// Test integer that should NEVER trigger warnings +bool global_bool = true; + +// Test that char and float types are NOT flagged when their options are disabled +float should_not_warn_float = 3.14f; +double should_not_warn_double = 2.71828; +char should_not_warn_char = 'a'; +signed char should_not_warn_signed_char = 'b'; +unsigned char should_not_warn_unsigned_char = 'c'; + +// Test fixed-width types that should NOT trigger warnings +uint32_t global_uint32 = 42U; +int32_t global_int32 = 42; +uint64_t global_uint64 = 100ULL; +int64_t global_int64 = 100LL; + +// Test semantic standard library types that should NOT trigger warnings +size_t global_size = 100; +ptrdiff_t global_ptrdiff = 50; + +// Test function parameters +void function_with_int_param(int param) { +// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] +} + +void function_with_short_param(short param) { +// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] +} + +// Test function return types +int function_returning_int() { +// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + return 42; +} + +long function_returning_long() { +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + return 100L; +} + +// Test local variables +void test_local_variables() { + int local_int = 10; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + + short local_short = 5; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + + unsigned long local_unsigned_long = 200UL; + // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: avoid using platform-dependent fundamental integer type 'unsigned long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + + // These should not trigger warnings + char local_char = 'x'; + bool local_bool = false; + + // Fixed-width types should not trigger warnings + uint32_t local_uint32 = 42U; + int64_t local_int64 = 100LL; + + // Standard library semantic types should not trigger warnings + size_t local_size = 200; + ptrdiff_t local_ptrdiff = 10; +} + +// Test struct/class members +struct TestStruct { + int member_int; + // CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + + long member_long; + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + + // These should not trigger warnings + char member_char; + bool member_bool; +}; + +class TestClass { +public: + unsigned int public_member; + // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: avoid using platform-dependent fundamental integer type 'unsigned int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +private: + short private_member; + // CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] +}; + +// Test typedefs and type aliases +typedef int MyInt; +// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +using MyLong = long; +// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +typedef long long customType; +// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: avoid using platform-dependent fundamental integer type 'long long'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] + +// Test template parameters +template +void template_function(T param) {} + +template<> +void template_function(int param) { +// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] +}