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..5b4f0c855970f --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.cpp @@ -0,0 +1,183 @@ +//===----------------------------------------------------------------------===// +// +// 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 { + +AST_MATCHER(clang::QualType, isBuiltinInt) { + const auto *BT = Node->getAs(); + if (!BT) + return false; + + // BT->isInteger() would detect char and bool + 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; + + return BT->isFloatingPoint(); +} + +} // 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()) { + if (!WarnOnFloats && !WarnOnInts && !WarnOnChars) + this->configurationDiag( + "The check 'portability-avoid-platform-specific-fundamental-types' " + "will not perform any analysis because 'WarnOnFloats', 'WarnOnInts' " + "and 'WarnOnChars' are all false."); +} + +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 ? isChar() : unless(anything()), + WarnOnChars ? isWideChar() : unless(anything())))); + + if (!WarnOnInts && !WarnOnFloats && !WarnOnChars) + return; + + Finder->addMatcher(typeLoc(loc(PlatformSpecificFundamentalType)).bind("type"), + this); +} + +void AvoidPlatformSpecificFundamentalTypesCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *TL = Result.Nodes.getNodeAs("type"); + if (!TL) + return; + + SourceLocation Loc = TL->getBeginLoc(); + QualType QT = TL->getType(); + SourceRange TypeRange = TL->getSourceRange(); + + // Skip implicit type locations, such as literals + if (!Loc.isValid() || !TypeRange.isValid()) + return; + + const std::string TypeName = QT.getUnqualifiedType().getAsString(); + + const auto *BT = QT->getAs(); + + assert(BT); + 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 type alias or fixed-width type instead") + << TypeName; + } + } else if (QT->isCharType() || QT->isWideCharType()) { + 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..6fe6ffef1e9f9 --- /dev/null +++ b/clang-tools-extra/clang-tidy/portability/AvoidPlatformSpecificFundamentalTypesCheck.h @@ -0,0 +1,47 @@ +//===----------------------------------------------------------------------===// +// +// 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 { + +/// 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 8b63601882930..a52300830659b 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -124,6 +124,14 @@ New checks Checks for uses of MLIR's old/to be deprecated ``OpBuilder::create`` form and suggests using ``T::create`` instead. +- New :doc:`portability-avoid-platform-specific-fundamental-types + ` + check. + + Detects fundamental types (``int``, ``short``, ``long``, ``long long``, + ``char``, ``float``, etc) and warns against their use due to + platform-dependent behavior. + New check aliases ^^^^^^^^^^^^^^^^^ diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index b6444eb3c9aec..ed5f51f987881 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -356,6 +356,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..a849abfa639ad --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/portability/avoid-platform-specific-fundamental-types.rst @@ -0,0 +1,147 @@ +.. 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; + // unsigned long is 32 bits on Windows and 64 bits on Mac/Linux, so this will + // overflow depending on platform. + unsigned long global_unsigned_long = 1 << 36; + // On many systems, loading into a register must be done at the processor's + // word size. On a 64-bit system with 32-bit integers, loading an element from + // slowVec could take multiple instructions. The first will load two elements, + // and additional instructions will delete the unneeded element. + std::vector slowVec; + // This could overflow and cause undefined behaviour if slowVec is too big. + for(int i = 0U; i + + int32_t global_int32 = 42; + uint64_t global_uint64 = 100UL; + // On a 64-bit system, int_fast32_t will probably be 64 bits in size, + // potentially allowing faster accesses. + std::vector fastVec; + // A size_t can hold any index into an array or vector on a given platform, + // because it can hold the maximum size of any theoretical object, so we will + // never overflow fastVec's size. + for(size_t i = 0U; i struct TemplateStruct { + typedef char char_type; + // CHECK-MESSAGES: :[[@LINE-1]]:11: 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] + + char template_field; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + + char create_char() { return char('x'); } + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + // CHECK-MESSAGES: :[[@LINE-2]]:31: 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] +}; + +// Test namespace usage +namespace ns_chars { + char ns_char; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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 test_comprehensive_cases() { + // Test with spacing + char spaced_char; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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 spaced_signed_char; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + + // Test pointers + char *char_ptr; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + + unsigned char *unsigned_char_ptr; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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 static declarations + static char static_char; + // CHECK-MESSAGES: :[[@LINE-1]]:10: 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] + + // Test cv-qualifiers + const char const_char = 'a'; + // CHECK-MESSAGES: :[[@LINE-1]]:9: 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] + + volatile char volatile_char; + // CHECK-MESSAGES: :[[@LINE-1]]:12: 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] + + const volatile char const_volatile_char = '\0'; + // CHECK-MESSAGES: :[[@LINE-1]]:18: 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] + + // Test auto with initializer + auto auto_char = char{'x'}; + // CHECK-MESSAGES: :[[@LINE-1]]:20: 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] + + char brace_init_char{}; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + + // Test arrays + char char_array[10]; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + + unsigned char unsigned_char_array[] = {'a', 'b', 'c'}; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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 multiple declarations + char multi_char1, multi_char2, multi_char3; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] +} + +// Test class with character type members +class CharacterClass { +public: + char &get_char_ref(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] + +private: + char private_char; + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] +}; + +// Test template instantiation +void test_template_instantiation() { + TemplateStruct template_instance; + char template_test = template_instance.create_char(); + // CHECK-MESSAGES: :[[@LINE-1]]:3: 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] +} + +// 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; + +// Test that unicode characters are not flagged +char8_t utf8 = u8'a'; +char16_t utf16 = u'a'; +char32_t utf32 = U'a'; +std::byte null{0}; 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..c1d802fbacaff --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-floats.cpp @@ -0,0 +1,112 @@ +// RUN: %check_clang_tidy -std=c++23-or-later %s \ +// RUN: portability-avoid-platform-specific-fundamental-types %t -- \ +// RUN: -config="{CheckOptions: \ +// RUN: [{key: portability-avoid-platform-specific-fundamental-types.WarnOnInts, \ +// RUN: value: false}, \ +// RUN: {key: portability-avoid-platform-specific-fundamental-types.WarnOnChars, \ +// RUN: 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]]:1: 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]]:1: 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]]:32: 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]]:33: 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]]:1: 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]]:1: 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]]:5: 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]]:5: 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]]:5: 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]]:5: 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]]:5: 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]]:5: 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]]:9: 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]]:18: 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]]:24: warning: avoid using platform-dependent floating point type 'float'; consider using 'float32_t' instead [portability-avoid-platform-specific-fundamental-types] +// CHECK-MESSAGES: :[[@LINE-2]]:31: 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]]:24: warning: avoid using platform-dependent floating point type 'double'; consider using 'float64_t' instead [portability-avoid-platform-specific-fundamental-types] +// CHECK-MESSAGES: :[[@LINE-2]]:32: 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..fef9d6373531e --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/portability/avoid-platform-specific-fundamental-types-ints.cpp @@ -0,0 +1,154 @@ +// RUN: %check_clang_tidy -std=c++11-or-later %s \ +// RUN: portability-avoid-platform-specific-fundamental-types %t -- \ +// RUN: -config="{CheckOptions: \ +// RUN: [{key: portability-avoid-platform-specific-fundamental-types.WarnOnChars, \ +// RUN: value: false}, \ +// RUN: {key: portability-avoid-platform-specific-fundamental-types.WarnOnFloats, \ +// RUN: 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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]]:1: 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]]:30: 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]]:32: 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]]:1: 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]]:1: 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]]: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 local_short = 5; + // CHECK-MESSAGES: :[[@LINE-1]]:5: 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]]:5: 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]]: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] + + long member_long; + // CHECK-MESSAGES: :[[@LINE-1]]:5: 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]]:5: 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]]:5: 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]]: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] + +using MyLong = long; +// CHECK-MESSAGES: :[[@LINE-1]]:16: 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]]:9: 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]]:24: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a 'typedef' or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] +// CHECK-MESSAGES: :[[@LINE-2]]:29: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a 'typedef' or fixed-width type instead [portability-avoid-platform-specific-fundamental-types] +} diff --git a/clang/include/clang/ASTMatchers/ASTMatchers.h b/clang/include/clang/ASTMatchers/ASTMatchers.h index cbd931cabd806..2d0937000300a 100644 --- a/clang/include/clang/ASTMatchers/ASTMatchers.h +++ b/clang/include/clang/ASTMatchers/ASTMatchers.h @@ -6597,16 +6597,43 @@ AST_MATCHER(QualType, isSignedInteger) { return Node->isSignedIntegerType(); } -/// Matches QualType nodes that are of character type. +/// Matches QualType nodes that are of char type. /// /// Given /// \code /// void a(char); /// void b(wchar_t); -/// void c(double); +/// void c(char8_t); +/// void d(double); +/// \endcode +/// functionDecl(hasAnyParameter(hasType(isChar()))) +/// matches "a(char)", but not "b(wchar_t)", "c(char8_t)", or "d(double)" +AST_MATCHER(QualType, isChar) { return Node->isCharType(); } + +/// Matches QualType nodes that are of wide char type. +/// +/// Given +/// \code +/// void a(char); +/// void b(wchar_t); +/// void c(char8_t); +/// void d(double); +/// \endcode +/// functionDecl(hasAnyParameter(hasType(isWideChar()))) +/// matches "b(wchar_t)", but not "a(char)", "c(char8_t)", or "d(double)" +AST_MATCHER(QualType, isWideChar) { return Node->isWideCharType(); } + +/// Matches QualType nodes that are of any character type. +/// +/// Given +/// \code +/// void a(char); +/// void b(wchar_t); +/// void c(char8_t); +/// void d(double); /// \endcode /// functionDecl(hasAnyParameter(hasType(isAnyCharacter()))) -/// matches "a(char)", "b(wchar_t)", but not "c(double)". +/// matches "a(char)", "b(wchar_t)", "c(char8_t)", but not "d(double)". AST_MATCHER(QualType, isAnyCharacter) { return Node->isAnyCharacterType(); }