-
Notifications
You must be signed in to change notification settings - Fork 14.4k
[clang-tidy] Add portability-avoid-platform-specific-fundamental-types #146970
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
3ef4feb
48598ef
524fdd8
d5fd2e7
25425cc
86787ec
c66668e
1b314bb
411d598
b427df7
5dd5c72
7c2031b
475f1b2
20e43d8
20e9b32
f49a289
143fcad
3c2ccd5
40b8be3
93c8489
91eb765
723a76e
a30bedf
76125bb
8258324
8dd606b
90c9887
c487bf3
09a762d
a53b5bc
b525a7a
de662e8
d0012d3
304b7b0
e66cf7b
c2f53d9
32343ae
d37015e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -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) { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Place function outside of anon-namespace. |
||||||
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<clang::BuiltinType>(); | ||||||
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<clang::BuiltinType>(); | ||||||
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<clang::BuiltinType>(); | ||||||
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; | ||||||
Comment on lines
+148
to
+149
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should give configuration warning in ctor for this case, see how it is done in |
||||||
|
||||||
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); | ||||||
Comment on lines
+151
to
+164
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could simplify these 4 matchers into one. The only bind you need is bad fundamental type. Finder->addMatcher(typeLoc(loc(hasType(PlatformSpecificFundamentalType))).bind("type")); See how it is done in |
||||||
|
||||||
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<VarDecl>("var_decl")) { | ||||||
Loc = VD->getLocation(); | ||||||
QT = VD->getType(); | ||||||
SetTypeRange(VD); | ||||||
} else if (const auto *FD = | ||||||
Result.Nodes.getNodeAs<FunctionDecl>("func_decl")) { | ||||||
Loc = FD->getLocation(); | ||||||
QT = FD->getReturnType(); | ||||||
SetTypeRange(FD); | ||||||
} else if (const auto *PD = | ||||||
Result.Nodes.getNodeAs<ParmVarDecl>("param_decl")) { | ||||||
Loc = PD->getLocation(); | ||||||
QT = PD->getType(); | ||||||
SetTypeRange(PD); | ||||||
} else if (const auto *FD = Result.Nodes.getNodeAs<FieldDecl>("field_decl")) { | ||||||
Loc = FD->getLocation(); | ||||||
QT = FD->getType(); | ||||||
SetTypeRange(FD); | ||||||
} else if (const auto *TD = | ||||||
Result.Nodes.getNodeAs<TypedefDecl>("typedef_decl")) { | ||||||
Loc = TD->getLocation(); | ||||||
QT = TD->getUnderlyingType(); | ||||||
SetTypeRange(TD); | ||||||
} else if (const auto *AD = | ||||||
Result.Nodes.getNodeAs<TypeAliasDecl>("alias_decl")) { | ||||||
Loc = AD->getLocation(); | ||||||
QT = AD->getUnderlyingType(); | ||||||
SetTypeRange(AD); | ||||||
} else { | ||||||
return; | ||||||
jj-marr marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
|
||||||
const std::string TypeName = QT.getAsString(); | ||||||
|
||||||
const auto *BT = QT->getAs<BuiltinType>(); | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
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), "<stdfloat>")) { | ||||||
Diag << *IncludeFixit; | ||||||
} | ||||||
} else { | ||||||
diag(Loc, "avoid using platform-dependent floating point type '%0'; " | ||||||
"consider using a typedef or fixed-width type instead") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
<< 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") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
<< TypeName; | ||||||
} else { | ||||||
diag(Loc, "avoid using platform-dependent fundamental integer type '%0'; " | ||||||
"consider using a typedef or fixed-width type instead") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
<< TypeName; | ||||||
} | ||||||
} | ||||||
|
||||||
} // namespace clang::tidy::portability |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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. | ||
Comment on lines
+17
to
+21
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This description should be the same as in |
||
/// | ||
/// 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<TraversalKind> 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 |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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 | ||||||
<clang-tidy/checks/portability/avoid-platform-specific-fundamental-types>` | ||||||
check. | ||||||
|
||||||
Finds fundamental types (e.g. `int`, `float`) and recommends using typedefs | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
or fixed-width types instead to improve portability across different platforms. | ||||||
|
||||||
- New :doc:`portability-avoid-pragma-once | ||||||
<clang-tidy/checks/portability/avoid-pragma-once>` check. | ||||||
|
||||||
|
Uh oh!
There was an error while loading. Please reload this page.