clang-tools  11.0.0
SignedCharMisuseCheck.cpp
Go to the documentation of this file.
1 //===--- SignedCharMisuseCheck.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 
10 #include "../utils/OptionsUtils.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/ASTMatchers/ASTMatchFinder.h"
13 
14 using namespace clang::ast_matchers;
15 using namespace clang::ast_matchers::internal;
16 
17 namespace clang {
18 namespace tidy {
19 namespace bugprone {
20 
21 static constexpr int UnsignedASCIIUpperBound = 127;
22 
23 static Matcher<TypedefDecl> hasAnyListedName(const std::string &Names) {
24  const std::vector<std::string> NameList =
26  return hasAnyName(std::vector<StringRef>(NameList.begin(), NameList.end()));
27 }
28 
29 SignedCharMisuseCheck::SignedCharMisuseCheck(StringRef Name,
30  ClangTidyContext *Context)
31  : ClangTidyCheck(Name, Context),
32  CharTypdefsToIgnoreList(Options.get("CharTypdefsToIgnore", "")),
33  DiagnoseSignedUnsignedCharComparisons(
34  Options.get("DiagnoseSignedUnsignedCharComparisons", true)) {}
35 
37  Options.store(Opts, "CharTypdefsToIgnore", CharTypdefsToIgnoreList);
38  Options.store(Opts, "DiagnoseSignedUnsignedCharComparisons",
39  DiagnoseSignedUnsignedCharComparisons);
40 }
41 
42 // Create a matcher for char -> integer cast.
43 BindableMatcher<clang::Stmt> SignedCharMisuseCheck::charCastExpression(
44  bool IsSigned, const Matcher<clang::QualType> &IntegerType,
45  const std::string &CastBindName) const {
46  // We can ignore typedefs which are some kind of integer types
47  // (e.g. typedef char sal_Int8). In this case, we don't need to
48  // worry about the misinterpretation of char values.
49  const auto IntTypedef = qualType(
50  hasDeclaration(typedefDecl(hasAnyListedName(CharTypdefsToIgnoreList))));
51 
52  auto CharTypeExpr = expr();
53  if (IsSigned) {
54  CharTypeExpr = expr(hasType(
55  qualType(isAnyCharacter(), isSignedInteger(), unless(IntTypedef))));
56  } else {
57  CharTypeExpr = expr(hasType(qualType(
58  isAnyCharacter(), unless(isSignedInteger()), unless(IntTypedef))));
59  }
60 
61  const auto ImplicitCastExpr =
62  implicitCastExpr(hasSourceExpression(CharTypeExpr),
63  hasImplicitDestinationType(IntegerType))
64  .bind(CastBindName);
65 
66  const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr));
67  const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr));
68  const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr));
69 
70  // We catch any type of casts to an integer. We need to have these cast
71  // expressions explicitly to catch only those casts which are direct children
72  // of the checked expressions. (e.g. assignment, declaration).
73  return traverse(ast_type_traits::TK_AsIs,
74  expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr,
75  FunctionalCastExpr)));
76 }
77 
78 void SignedCharMisuseCheck::registerMatchers(MatchFinder *Finder) {
79  const auto IntegerType =
80  qualType(isInteger(), unless(isAnyCharacter()), unless(booleanType()))
81  .bind("integerType");
82  const auto SignedCharCastExpr =
83  charCastExpression(true, IntegerType, "signedCastExpression");
84  const auto UnSignedCharCastExpr =
85  charCastExpression(false, IntegerType, "unsignedCastExpression");
86 
87  // Catch assignments with singed char -> integer conversion.
88  const auto AssignmentOperatorExpr =
89  expr(binaryOperator(hasOperatorName("="), hasLHS(hasType(IntegerType)),
90  hasRHS(SignedCharCastExpr)));
91 
92  Finder->addMatcher(AssignmentOperatorExpr, this);
93 
94  // Catch declarations with singed char -> integer conversion.
95  const auto Declaration = varDecl(isDefinition(), hasType(IntegerType),
96  hasInitializer(SignedCharCastExpr));
97 
98  Finder->addMatcher(Declaration, this);
99 
100  if (DiagnoseSignedUnsignedCharComparisons) {
101  // Catch signed char/unsigned char comparison.
102  const auto CompareOperator =
103  expr(binaryOperator(hasAnyOperatorName("==", "!="),
104  anyOf(allOf(hasLHS(SignedCharCastExpr),
105  hasRHS(UnSignedCharCastExpr)),
106  allOf(hasLHS(UnSignedCharCastExpr),
107  hasRHS(SignedCharCastExpr)))))
108  .bind("comparison");
109 
110  Finder->addMatcher(CompareOperator, this);
111  }
112 
113  // Catch array subscripts with signed char -> integer conversion.
114  // Matcher for C arrays.
115  const auto CArraySubscript =
116  arraySubscriptExpr(hasIndex(SignedCharCastExpr)).bind("arraySubscript");
117 
118  Finder->addMatcher(CArraySubscript, this);
119 
120  // Matcher for std arrays.
121  const auto STDArraySubscript =
122  cxxOperatorCallExpr(
123  hasOverloadedOperatorName("[]"),
124  hasArgument(0, hasType(cxxRecordDecl(hasName("::std::array")))),
125  hasArgument(1, SignedCharCastExpr))
126  .bind("arraySubscript");
127 
128  Finder->addMatcher(STDArraySubscript, this);
129 }
130 
131 void SignedCharMisuseCheck::check(const MatchFinder::MatchResult &Result) {
132  const auto *SignedCastExpression =
133  Result.Nodes.getNodeAs<ImplicitCastExpr>("signedCastExpression");
134  const auto *IntegerType = Result.Nodes.getNodeAs<QualType>("integerType");
135  assert(SignedCastExpression);
136  assert(IntegerType);
137 
138  // Ignore the match if we know that the signed char's value is not negative.
139  // The potential misinterpretation happens for negative values only.
140  Expr::EvalResult EVResult;
141  if (!SignedCastExpression->isValueDependent() &&
142  SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,
143  *Result.Context)) {
144  llvm::APSInt Value = EVResult.Val.getInt();
145  if (Value.isNonNegative())
146  return;
147  }
148 
149  if (const auto *Comparison = Result.Nodes.getNodeAs<Expr>("comparison")) {
150  const auto *UnSignedCastExpression =
151  Result.Nodes.getNodeAs<ImplicitCastExpr>("unsignedCastExpression");
152 
153  // We can ignore the ASCII value range also for unsigned char.
154  Expr::EvalResult EVResult;
155  if (!UnSignedCastExpression->isValueDependent() &&
156  UnSignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,
157  *Result.Context)) {
158  llvm::APSInt Value = EVResult.Val.getInt();
159  if (Value <= UnsignedASCIIUpperBound)
160  return;
161  }
162 
163  diag(Comparison->getBeginLoc(),
164  "comparison between 'signed char' and 'unsigned char'");
165  } else if (Result.Nodes.getNodeAs<Expr>("arraySubscript")) {
166  diag(SignedCastExpression->getBeginLoc(),
167  "'signed char' to %0 conversion in array subscript; "
168  "consider casting to 'unsigned char' first.")
169  << *IntegerType;
170  } else {
171  diag(SignedCastExpression->getBeginLoc(),
172  "'signed char' to %0 conversion; "
173  "consider casting to 'unsigned char' first.")
174  << *IntegerType;
175  }
176 }
177 
178 } // namespace bugprone
179 } // namespace tidy
180 } // namespace clang
clang::tidy::ClangTidyCheck
Base class for all clang-tidy checks.
Definition: ClangTidyCheck.h:114
clang::tidy::bugprone::SignedCharMisuseCheck::storeOptions
void storeOptions(ClangTidyOptions::OptionMap &Opts) override
Should store all options supported by this check with their current values or default values for opti...
Definition: SignedCharMisuseCheck.cpp:36
clang::tidy::bugprone::hasAnyListedName
static Matcher< TypedefDecl > hasAnyListedName(const std::string &Names)
Definition: SignedCharMisuseCheck.cpp:23
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::utils::options::parseStringList
std::vector< std::string > parseStringList(StringRef Option)
Parse a semicolon separated list of strings.
Definition: OptionsUtils.cpp:18
clang::tidy::bugprone::SignedCharMisuseCheck::registerMatchers
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
Definition: SignedCharMisuseCheck.cpp:78
clang::tidy::ClangTidyCheck::Options
OptionsView Options
Definition: ClangTidyCheck.h:471
clang::tidy::ClangTidyContext
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
Definition: ClangTidyDiagnosticConsumer.h:76
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
SignedCharMisuseCheck.h
clang::tidy::ClangTidyCheck::diag
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.
Definition: ClangTidyCheck.cpp:55
clang::tidy::bugprone::SignedCharMisuseCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: SignedCharMisuseCheck.cpp:131
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::bugprone::UnsignedASCIIUpperBound
static constexpr int UnsignedASCIIUpperBound
Definition: SignedCharMisuseCheck.cpp:21
clang::tidy::ClangTidyCheck::OptionsView::store
void store(ClangTidyOptions::OptionMap &Options, StringRef LocalName, StringRef Value) const
Stores an option with the check-local name LocalName with string value Value to Options.
Definition: ClangTidyCheck.cpp:152
clang::tidy::ClangTidyOptions::OptionMap
std::map< std::string, ClangTidyValue > OptionMap
Definition: ClangTidyOptions.h:111