1 //===--- IntegerTypesCheck.cpp - clang-tidy -------------------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "IntegerTypesCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchers.h"
13 #include "clang/Basic/AttrKinds.h"
14 #include "clang/Basic/CharInfo.h"
15 #include "clang/Basic/IdentifierTable.h"
16 #include "clang/Basic/TargetInfo.h"
17 #include "clang/Lex/Lexer.h"
21 using namespace ast_matchers
;
23 static Token
getTokenAtLoc(SourceLocation Loc
,
24 const MatchFinder::MatchResult
&MatchResult
,
25 IdentifierTable
&IdentTable
) {
27 if (Lexer::getRawToken(Loc
, Tok
, *MatchResult
.SourceManager
,
28 MatchResult
.Context
->getLangOpts(), false))
31 if (Tok
.is(tok::raw_identifier
)) {
32 IdentifierInfo
&Info
= IdentTable
.get(Tok
.getRawIdentifier());
33 Tok
.setIdentifierInfo(&Info
);
34 Tok
.setKind(Info
.getTokenID());
40 AST_MATCHER(FunctionDecl
, isUserDefineLiteral
) {
41 return Node
.getLiteralIdentifier() != nullptr;
44 AST_MATCHER(TypeLoc
, isValidAndNotInMacro
) {
45 const SourceLocation Loc
= Node
.getBeginLoc();
46 return Loc
.isValid() && !Loc
.isMacroID();
49 AST_MATCHER(TypeLoc
, isBuiltinType
) {
51 if (auto QualLoc
= Node
.getAs
<QualifiedTypeLoc
>())
52 TL
= QualLoc
.getUnqualifiedLoc();
54 const auto BuiltinLoc
= TL
.getAs
<BuiltinTypeLoc
>();
58 switch (BuiltinLoc
.getTypePtr()->getKind()) {
59 case BuiltinType::Short
:
60 case BuiltinType::Long
:
61 case BuiltinType::LongLong
:
62 case BuiltinType::UShort
:
63 case BuiltinType::ULong
:
64 case BuiltinType::ULongLong
:
73 namespace tidy::google::runtime
{
75 IntegerTypesCheck::IntegerTypesCheck(StringRef Name
, ClangTidyContext
*Context
)
76 : ClangTidyCheck(Name
, Context
),
77 UnsignedTypePrefix(Options
.get("UnsignedTypePrefix", "uint")),
78 SignedTypePrefix(Options
.get("SignedTypePrefix", "int")),
79 TypeSuffix(Options
.get("TypeSuffix", "")) {}
81 void IntegerTypesCheck::storeOptions(ClangTidyOptions::OptionMap
&Opts
) {
82 Options
.store(Opts
, "UnsignedTypePrefix", UnsignedTypePrefix
);
83 Options
.store(Opts
, "SignedTypePrefix", SignedTypePrefix
);
84 Options
.store(Opts
, "TypeSuffix", TypeSuffix
);
87 void IntegerTypesCheck::registerMatchers(MatchFinder
*Finder
) {
88 // Match any integer types, unless they are passed to a printf-based API:
90 // http://google.github.io/styleguide/cppguide.html#64-bit_Portability
91 // "Where possible, avoid passing arguments of types specified by
92 // bitwidth typedefs to printf-based APIs."
94 typeLoc(loc(isInteger()), isValidAndNotInMacro(), isBuiltinType(),
96 callExpr(callee(functionDecl(hasAttr(attr::Format
)))))),
97 unless(hasParent(parmVarDecl(
98 hasAncestor(functionDecl(isUserDefineLiteral()))))))
101 IdentTable
= std::make_unique
<IdentifierTable
>(getLangOpts());
104 void IntegerTypesCheck::check(const MatchFinder::MatchResult
&Result
) {
105 auto TL
= *Result
.Nodes
.getNodeAs
<TypeLoc
>("tl");
106 SourceLocation Loc
= TL
.getBeginLoc();
108 // Look through qualification.
109 if (auto QualLoc
= TL
.getAs
<QualifiedTypeLoc
>())
110 TL
= QualLoc
.getUnqualifiedLoc();
112 auto BuiltinLoc
= TL
.getAs
<BuiltinTypeLoc
>();
116 Token Tok
= getTokenAtLoc(Loc
, Result
, *IdentTable
);
117 // Ensure the location actually points to one of the builting integral type
118 // names we're interested in. Otherwise, we might be getting this match from
119 // implicit code (e.g. an implicit assignment operator of a class containing
120 // an array of non-POD types).
121 if (!Tok
.isOneOf(tok::kw_short
, tok::kw_long
, tok::kw_unsigned
,
125 bool IsSigned
= false;
127 const TargetInfo
&TargetInfo
= Result
.Context
->getTargetInfo();
129 // Look for uses of short, long, long long and their unsigned versions.
130 switch (BuiltinLoc
.getTypePtr()->getKind()) {
131 case BuiltinType::Short
:
132 Width
= TargetInfo
.getShortWidth();
135 case BuiltinType::Long
:
136 Width
= TargetInfo
.getLongWidth();
139 case BuiltinType::LongLong
:
140 Width
= TargetInfo
.getLongLongWidth();
143 case BuiltinType::UShort
:
144 Width
= TargetInfo
.getShortWidth();
147 case BuiltinType::ULong
:
148 Width
= TargetInfo
.getLongWidth();
151 case BuiltinType::ULongLong
:
152 Width
= TargetInfo
.getLongLongWidth();
159 // We allow "unsigned short port" as that's reasonably common and required by
161 const StringRef Port
= "unsigned short port";
162 const char *Data
= Result
.SourceManager
->getCharacterData(Loc
);
163 if (!std::strncmp(Data
, Port
.data(), Port
.size()) &&
164 !isAsciiIdentifierContinue(Data
[Port
.size()]))
167 std::string Replacement
=
168 ((IsSigned
? SignedTypePrefix
: UnsignedTypePrefix
) + Twine(Width
) +
172 // We don't add a fix-it as changing the type can easily break code,
173 // e.g. when a function requires a 'long' argument on all platforms.
174 // QualTypes are printed with implicit quotes.
175 diag(Loc
, "consider replacing %0 with '%1'") << BuiltinLoc
.getType()
179 } // namespace tidy::google::runtime