@@ -18,6 +18,8 @@ namespace clang {
18
18
namespace tidy {
19
19
namespace bugprone {
20
20
21
+ static constexpr int UnsignedASCIIUpperBound = 127 ;
22
+
21
23
static Matcher<TypedefDecl> hasAnyListedName (const std::string &Names) {
22
24
const std::vector<std::string> NameList =
23
25
utils::options::parseStringList (Names);
@@ -33,70 +35,114 @@ void SignedCharMisuseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {
33
35
Options.store (Opts, " CharTypdefsToIgnore" , CharTypdefsToIgnoreList);
34
36
}
35
37
36
- void SignedCharMisuseCheck::registerMatchers (MatchFinder *Finder) {
38
+ // Create a matcher for char -> integer cast.
39
+ BindableMatcher<clang::Stmt> SignedCharMisuseCheck::charCastExpression (
40
+ bool IsSigned, const Matcher<clang::QualType> &IntegerType,
41
+ const std::string &CastBindName) const {
37
42
// We can ignore typedefs which are some kind of integer types
38
43
// (e.g. typedef char sal_Int8). In this case, we don't need to
39
44
// worry about the misinterpretation of char values.
40
45
const auto IntTypedef = qualType (
41
46
hasDeclaration (typedefDecl (hasAnyListedName (CharTypdefsToIgnoreList))));
42
47
43
- const auto SignedCharType = expr (hasType (qualType (
44
- allOf (isAnyCharacter (), isSignedInteger (), unless (IntTypedef)))));
45
-
46
- const auto IntegerType = qualType (allOf (isInteger (), unless (isAnyCharacter ()),
47
- unless (booleanType ())))
48
- .bind (" integerType" );
48
+ auto CharTypeExpr = expr ();
49
+ if (IsSigned) {
50
+ CharTypeExpr = expr (hasType (
51
+ qualType (isAnyCharacter (), isSignedInteger (), unless (IntTypedef))));
52
+ } else {
53
+ CharTypeExpr = expr (hasType (qualType (
54
+ isAnyCharacter (), unless (isSignedInteger ()), unless (IntTypedef))));
55
+ }
49
56
50
- // We are interested in signed char -> integer conversion.
51
57
const auto ImplicitCastExpr =
52
- implicitCastExpr (hasSourceExpression (SignedCharType ),
58
+ implicitCastExpr (hasSourceExpression (CharTypeExpr ),
53
59
hasImplicitDestinationType (IntegerType))
54
- .bind (" castExpression " );
60
+ .bind (CastBindName );
55
61
56
62
const auto CStyleCastExpr = cStyleCastExpr (has (ImplicitCastExpr));
57
63
const auto StaticCastExpr = cxxStaticCastExpr (has (ImplicitCastExpr));
58
64
const auto FunctionalCastExpr = cxxFunctionalCastExpr (has (ImplicitCastExpr));
59
65
60
66
// We catch any type of casts to an integer. We need to have these cast
61
67
// expressions explicitly to catch only those casts which are direct children
62
- // of an assignment/declaration.
63
- const auto CastExpr = expr (anyOf (ImplicitCastExpr, CStyleCastExpr,
64
- StaticCastExpr, FunctionalCastExpr));
68
+ // of the checked expressions. (e.g. assignment, declaration).
69
+ return expr (anyOf (ImplicitCastExpr, CStyleCastExpr, StaticCastExpr,
70
+ FunctionalCastExpr));
71
+ }
65
72
66
- // Catch assignments with the suspicious type conversion.
67
- const auto AssignmentOperatorExpr = expr (binaryOperator (
68
- hasOperatorName (" =" ), hasLHS (hasType (IntegerType)), hasRHS (CastExpr)));
73
+ void SignedCharMisuseCheck::registerMatchers (MatchFinder *Finder) {
74
+ const auto IntegerType =
75
+ qualType (isInteger (), unless (isAnyCharacter ()), unless (booleanType ()))
76
+ .bind (" integerType" );
77
+ const auto SignedCharCastExpr =
78
+ charCastExpression (true , IntegerType, " signedCastExpression" );
79
+ const auto UnSignedCharCastExpr =
80
+ charCastExpression (false , IntegerType, " unsignedCastExpression" );
81
+
82
+ // Catch assignments with singed char -> integer conversion.
83
+ const auto AssignmentOperatorExpr =
84
+ expr (binaryOperator (hasOperatorName (" =" ), hasLHS (hasType (IntegerType)),
85
+ hasRHS (SignedCharCastExpr)));
69
86
70
87
Finder->addMatcher (AssignmentOperatorExpr, this );
71
88
72
- // Catch declarations with the suspicious type conversion.
73
- const auto Declaration =
74
- varDecl ( isDefinition (), hasType (IntegerType), hasInitializer (CastExpr ));
89
+ // Catch declarations with singed char -> integer conversion.
90
+ const auto Declaration = varDecl ( isDefinition (), hasType (IntegerType),
91
+ hasInitializer (SignedCharCastExpr ));
75
92
76
93
Finder->addMatcher (Declaration, this );
94
+
95
+ // Catch signed char/unsigned char comparison.
96
+ const auto CompareOperator =
97
+ expr (binaryOperator (hasAnyOperatorName (" ==" , " !=" ),
98
+ anyOf (allOf (hasLHS (SignedCharCastExpr),
99
+ hasRHS (UnSignedCharCastExpr)),
100
+ allOf (hasLHS (UnSignedCharCastExpr),
101
+ hasRHS (SignedCharCastExpr)))))
102
+ .bind (" comparison" );
103
+
104
+ Finder->addMatcher (CompareOperator, this );
77
105
}
78
106
79
107
void SignedCharMisuseCheck::check (const MatchFinder::MatchResult &Result) {
80
- const auto *CastExpression =
81
- Result.Nodes .getNodeAs <ImplicitCastExpr>(" castExpression" );
82
- const auto *IntegerType = Result.Nodes .getNodeAs <QualType>(" integerType" );
83
- assert (CastExpression);
84
- assert (IntegerType);
108
+ const auto *SignedCastExpression =
109
+ Result.Nodes .getNodeAs <ImplicitCastExpr>(" signedCastExpression" );
85
110
86
- // Ignore the match if we know that the value is not negative.
111
+ // Ignore the match if we know that the signed char's value is not negative.
87
112
// The potential misinterpretation happens for negative values only.
88
113
Expr::EvalResult EVResult;
89
- if (!CastExpression->isValueDependent () &&
90
- CastExpression->getSubExpr ()->EvaluateAsInt (EVResult, *Result.Context )) {
91
- llvm::APSInt Value1 = EVResult.Val .getInt ();
92
- if (Value1.isNonNegative ())
114
+ if (!SignedCastExpression->isValueDependent () &&
115
+ SignedCastExpression->getSubExpr ()->EvaluateAsInt (EVResult,
116
+ *Result.Context )) {
117
+ llvm::APSInt Value = EVResult.Val .getInt ();
118
+ if (Value.isNonNegative ())
93
119
return ;
94
120
}
95
121
96
- diag (CastExpression->getBeginLoc (),
97
- " 'signed char' to %0 conversion; "
98
- " consider casting to 'unsigned char' first." )
99
- << *IntegerType;
122
+ if (const auto *Comparison = Result.Nodes .getNodeAs <Expr>(" comparison" )) {
123
+ const auto *UnSignedCastExpression =
124
+ Result.Nodes .getNodeAs <ImplicitCastExpr>(" unsignedCastExpression" );
125
+
126
+ // We can ignore the ASCII value range also for unsigned char.
127
+ Expr::EvalResult EVResult;
128
+ if (!UnSignedCastExpression->isValueDependent () &&
129
+ UnSignedCastExpression->getSubExpr ()->EvaluateAsInt (EVResult,
130
+ *Result.Context )) {
131
+ llvm::APSInt Value = EVResult.Val .getInt ();
132
+ if (Value <= UnsignedASCIIUpperBound)
133
+ return ;
134
+ }
135
+
136
+ diag (Comparison->getBeginLoc (),
137
+ " comparison between 'signed char' and 'unsigned char'" );
138
+ } else if (const auto *IntegerType =
139
+ Result.Nodes .getNodeAs <QualType>(" integerType" )) {
140
+ diag (SignedCastExpression->getBeginLoc (),
141
+ " 'signed char' to %0 conversion; "
142
+ " consider casting to 'unsigned char' first." )
143
+ << *IntegerType;
144
+ } else
145
+ llvm_unreachable (" Unexpected match" );
100
146
}
101
147
102
148
} // namespace bugprone
0 commit comments