Skip to content

Commit 3ef4feb

Browse files
committed
AvoidFundamentalIntegerTypesCheck
1 parent e18c5de commit 3ef4feb

File tree

5 files changed

+341
-0
lines changed

5 files changed

+341
-0
lines changed
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
//===--- AvoidFundamentalIntegerTypesCheck.cpp - clang-tidy ---------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "AvoidFundamentalIntegerTypesCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/ASTMatchers/ASTMatchFinder.h"
12+
#include "clang/ASTMatchers/ASTMatchers.h"
13+
14+
using namespace clang::ast_matchers;
15+
16+
namespace clang::tidy::modernize {
17+
18+
namespace {
19+
20+
AST_MATCHER(clang::TypeLoc, hasValidBeginLoc) {
21+
return Node.getBeginLoc().isValid();
22+
}
23+
24+
AST_MATCHER_P(clang::TypeLoc, hasType,
25+
clang::ast_matchers::internal::Matcher<clang::Type>,
26+
InnerMatcher) {
27+
const clang::Type *TypeNode = Node.getTypePtr();
28+
return TypeNode != nullptr &&
29+
InnerMatcher.matches(*TypeNode, Finder, Builder);
30+
}
31+
32+
} // namespace
33+
34+
AvoidFundamentalIntegerTypesCheck::AvoidFundamentalIntegerTypesCheck(
35+
StringRef Name, ClangTidyContext *Context)
36+
: ClangTidyCheck(Name, Context),
37+
IgnoreTypedefs(Options.get("IgnoreTypedefs", false)) {}
38+
39+
void AvoidFundamentalIntegerTypesCheck::storeOptions(
40+
ClangTidyOptions::OptionMap &Opts) {
41+
Options.store(Opts, "IgnoreTypedefs", IgnoreTypedefs);
42+
}
43+
44+
bool AvoidFundamentalIntegerTypesCheck::isFundamentalIntegerType(
45+
const Type *T) const {
46+
if (!T->isBuiltinType())
47+
return false;
48+
49+
const auto *BT = T->getAs<BuiltinType>();
50+
if (!BT)
51+
return false;
52+
53+
switch (BT->getKind()) {
54+
case BuiltinType::Int:
55+
case BuiltinType::UInt:
56+
case BuiltinType::Short:
57+
case BuiltinType::UShort:
58+
case BuiltinType::Long:
59+
case BuiltinType::ULong:
60+
case BuiltinType::LongLong:
61+
case BuiltinType::ULongLong:
62+
return true;
63+
default:
64+
return false;
65+
}
66+
}
67+
68+
bool AvoidFundamentalIntegerTypesCheck::isSemanticType(const Type *T) const {
69+
if (!T->isBuiltinType())
70+
return false;
71+
72+
const auto *BT = T->getAs<BuiltinType>();
73+
if (!BT)
74+
return false;
75+
76+
switch (BT->getKind()) {
77+
case BuiltinType::Bool:
78+
case BuiltinType::Char_S:
79+
case BuiltinType::Char_U:
80+
case BuiltinType::SChar:
81+
case BuiltinType::UChar:
82+
case BuiltinType::WChar_S:
83+
case BuiltinType::WChar_U:
84+
case BuiltinType::Char8:
85+
case BuiltinType::Char16:
86+
case BuiltinType::Char32:
87+
return true;
88+
default:
89+
return false;
90+
}
91+
}
92+
93+
void AvoidFundamentalIntegerTypesCheck::registerMatchers(MatchFinder *Finder) {
94+
// Match variable declarations with fundamental integer types
95+
Finder->addMatcher(
96+
varDecl().bind("var_decl"),
97+
this);
98+
99+
// Match function declarations with fundamental integer return types
100+
Finder->addMatcher(
101+
functionDecl().bind("func_decl"),
102+
this);
103+
104+
// Match function parameters with fundamental integer types
105+
Finder->addMatcher(
106+
parmVarDecl().bind("param_decl"),
107+
this);
108+
109+
// Match field declarations with fundamental integer types
110+
Finder->addMatcher(
111+
fieldDecl().bind("field_decl"),
112+
this);
113+
114+
// Match typedef declarations if not ignoring them
115+
if (!IgnoreTypedefs) {
116+
Finder->addMatcher(
117+
typedefDecl().bind("typedef_decl"),
118+
this);
119+
120+
Finder->addMatcher(
121+
typeAliasDecl().bind("alias_decl"),
122+
this);
123+
}
124+
}
125+
126+
void AvoidFundamentalIntegerTypesCheck::check(
127+
const MatchFinder::MatchResult &Result) {
128+
SourceLocation Loc;
129+
QualType QT;
130+
std::string DeclType;
131+
132+
if (const auto *VD = Result.Nodes.getNodeAs<VarDecl>("var_decl")) {
133+
Loc = VD->getLocation();
134+
QT = VD->getType();
135+
DeclType = "variable";
136+
} else if (const auto *FD = Result.Nodes.getNodeAs<FunctionDecl>("func_decl")) {
137+
Loc = FD->getLocation();
138+
QT = FD->getReturnType();
139+
DeclType = "function return type";
140+
} else if (const auto *PD = Result.Nodes.getNodeAs<ParmVarDecl>("param_decl")) {
141+
Loc = PD->getLocation();
142+
QT = PD->getType();
143+
DeclType = "function parameter";
144+
} else if (const auto *FD = Result.Nodes.getNodeAs<FieldDecl>("field_decl")) {
145+
Loc = FD->getLocation();
146+
QT = FD->getType();
147+
DeclType = "field";
148+
} else if (const auto *TD = Result.Nodes.getNodeAs<TypedefDecl>("typedef_decl")) {
149+
Loc = TD->getLocation();
150+
QT = TD->getUnderlyingType();
151+
DeclType = "typedef";
152+
} else if (const auto *AD = Result.Nodes.getNodeAs<TypeAliasDecl>("alias_decl")) {
153+
Loc = AD->getLocation();
154+
QT = AD->getUnderlyingType();
155+
DeclType = "type alias";
156+
} else {
157+
return;
158+
}
159+
160+
if (Loc.isInvalid() || QT.isNull())
161+
return;
162+
163+
const Type *T = QT.getCanonicalType().getTypePtr();
164+
if (!T)
165+
return;
166+
167+
// Skip if not a fundamental integer type
168+
if (!isFundamentalIntegerType(T))
169+
return;
170+
171+
// Skip semantic types
172+
if (isSemanticType(T))
173+
return;
174+
175+
// Get the type name for the diagnostic
176+
std::string TypeName = QT.getAsString();
177+
178+
diag(Loc, "avoid using platform-dependent fundamental integer type '%0'; "
179+
"consider using a typedef or fixed-width type instead")
180+
<< TypeName;
181+
}
182+
183+
} // namespace clang::tidy::modernize
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
//===--- AvoidFundamentalIntegerTypesCheck.h - clang-tidy -------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOIDFUNDAMENTALINTEGERTYPESCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOIDFUNDAMENTALINTEGERTYPESCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::modernize {
15+
16+
/// Find fundamental integer types and recommend using typedefs or fixed-width types.
17+
///
18+
/// Detects fundamental integer types (int, short, long, long long, and their
19+
/// unsigned variants) and warns against their use due to platform-dependent
20+
/// behavior. Excludes semantic types like char, bool, wchar_t, char16_t,
21+
/// char32_t, size_t, and ptrdiff_t.
22+
///
23+
/// For the user-facing documentation see:
24+
/// http://clang.llvm.org/extra/clang-tidy/checks/modernize/avoid-fundamental-integer-types.html
25+
class AvoidFundamentalIntegerTypesCheck : public ClangTidyCheck {
26+
public:
27+
AvoidFundamentalIntegerTypesCheck(StringRef Name, ClangTidyContext *Context);
28+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
29+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
30+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
31+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override {
32+
return LangOpts.CPlusPlus11;
33+
}
34+
std::optional<TraversalKind> getCheckTraversalKind() const override {
35+
return TK_IgnoreUnlessSpelledInSource;
36+
}
37+
38+
private:
39+
const bool IgnoreTypedefs;
40+
bool isFundamentalIntegerType(const Type *T) const;
41+
bool isSemanticType(const Type *T) const;
42+
};
43+
44+
} // namespace clang::tidy::modernize
45+
46+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_AVOIDFUNDAMENTALINTEGERTYPESCHECK_H

clang-tools-extra/clang-tidy/modernize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ set(LLVM_LINK_COMPONENTS
66
add_clang_library(clangTidyModernizeModule STATIC
77
AvoidBindCheck.cpp
88
AvoidCArraysCheck.cpp
9+
AvoidFundamentalIntegerTypesCheck.cpp
910
ConcatNestedNamespacesCheck.cpp
1011
DeprecatedHeadersCheck.cpp
1112
DeprecatedIosBaseAliasesCheck.cpp

clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "../ClangTidyModuleRegistry.h"
1212
#include "AvoidBindCheck.h"
1313
#include "AvoidCArraysCheck.h"
14+
#include "AvoidFundamentalIntegerTypesCheck.h"
1415
#include "ConcatNestedNamespacesCheck.h"
1516
#include "DeprecatedHeadersCheck.h"
1617
#include "DeprecatedIosBaseAliasesCheck.h"
@@ -63,6 +64,8 @@ class ModernizeModule : public ClangTidyModule {
6364
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
6465
CheckFactories.registerCheck<AvoidBindCheck>("modernize-avoid-bind");
6566
CheckFactories.registerCheck<AvoidCArraysCheck>("modernize-avoid-c-arrays");
67+
CheckFactories.registerCheck<AvoidFundamentalIntegerTypesCheck>(
68+
"modernize-avoid-fundamental-integer-types");
6669
CheckFactories.registerCheck<ConcatNestedNamespacesCheck>(
6770
"modernize-concat-nested-namespaces");
6871
CheckFactories.registerCheck<DeprecatedHeadersCheck>(
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// RUN: %check_clang_tidy %s modernize-avoid-fundamental-integer-types %t
2+
3+
// Test fundamental integer types that should trigger warnings
4+
int global_int = 42;
5+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
6+
7+
short global_short = 10;
8+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
9+
10+
long global_long = 100L;
11+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
12+
13+
long long global_long_long = 1000LL;
14+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent fundamental integer type 'long long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
15+
16+
unsigned int global_unsigned_int = 42U;
17+
// CHECK-MESSAGES: :[[@LINE-1]]:14: warning: avoid using platform-dependent fundamental integer type 'unsigned int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
18+
19+
unsigned short global_unsigned_short = 10U;
20+
// CHECK-MESSAGES: :[[@LINE-1]]:16: warning: avoid using platform-dependent fundamental integer type 'unsigned short'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
21+
22+
unsigned long global_unsigned_long = 100UL;
23+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: avoid using platform-dependent fundamental integer type 'unsigned long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
24+
25+
unsigned long long global_unsigned_long_long = 1000ULL;
26+
// 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 [modernize-avoid-fundamental-integer-types]
27+
28+
// Test semantic types that should NOT trigger warnings
29+
char global_char = 'a';
30+
signed char global_signed_char = 'b';
31+
unsigned char global_unsigned_char = 'c';
32+
bool global_bool = true;
33+
wchar_t global_wchar = L'w';
34+
35+
// Test function parameters
36+
void function_with_int_param(int param) {
37+
// CHECK-MESSAGES: :[[@LINE-1]]:34: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
38+
}
39+
40+
void function_with_short_param(short param) {
41+
// CHECK-MESSAGES: :[[@LINE-1]]:38: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
42+
}
43+
44+
// Test function return types
45+
int function_returning_int() {
46+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
47+
return 42;
48+
}
49+
50+
long function_returning_long() {
51+
// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
52+
return 100L;
53+
}
54+
55+
// Test local variables
56+
void test_local_variables() {
57+
int local_int = 10;
58+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
59+
60+
short local_short = 5;
61+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
62+
63+
unsigned long local_unsigned_long = 200UL;
64+
// CHECK-MESSAGES: :[[@LINE-1]]:19: warning: avoid using platform-dependent fundamental integer type 'unsigned long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
65+
66+
// These should not trigger warnings
67+
char local_char = 'x';
68+
bool local_bool = false;
69+
}
70+
71+
// Test struct/class members
72+
struct TestStruct {
73+
int member_int;
74+
// CHECK-MESSAGES: :[[@LINE-1]]:9: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
75+
76+
long member_long;
77+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
78+
79+
// These should not trigger warnings
80+
char member_char;
81+
bool member_bool;
82+
};
83+
84+
class TestClass {
85+
public:
86+
unsigned int public_member;
87+
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: avoid using platform-dependent fundamental integer type 'unsigned int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
88+
89+
private:
90+
short private_member;
91+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: avoid using platform-dependent fundamental integer type 'short'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
92+
};
93+
94+
// Test typedefs and type aliases
95+
typedef int MyInt;
96+
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
97+
98+
using MyLong = long;
99+
// CHECK-MESSAGES: :[[@LINE-1]]:7: warning: avoid using platform-dependent fundamental integer type 'long'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
100+
101+
// Test template parameters
102+
template<typename T>
103+
void template_function(T param) {}
104+
105+
template<>
106+
void template_function<int>(int param) {
107+
// CHECK-MESSAGES: :[[@LINE-1]]:33: warning: avoid using platform-dependent fundamental integer type 'int'; consider using a typedef or fixed-width type instead [modernize-avoid-fundamental-integer-types]
108+
}

0 commit comments

Comments
 (0)