brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.3 KiB · 31c5413 Raw
178 lines · cpp
1//===----------------------------------------------------------------------===//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-exception6//7//===----------------------------------------------------------------------===//8 9#include "SignedCharMisuseCheck.h"10#include "../utils/OptionsUtils.h"11#include "clang/AST/ASTContext.h"12#include "clang/ASTMatchers/ASTMatchFinder.h"13 14using namespace clang::ast_matchers;15using namespace clang::ast_matchers::internal;16 17namespace clang::tidy::bugprone {18 19static constexpr int UnsignedASCIIUpperBound = 127;20 21SignedCharMisuseCheck::SignedCharMisuseCheck(StringRef Name,22                                             ClangTidyContext *Context)23    : ClangTidyCheck(Name, Context),24      CharTypedefsToIgnoreList(Options.get("CharTypedefsToIgnore", "")),25      DiagnoseSignedUnsignedCharComparisons(26          Options.get("DiagnoseSignedUnsignedCharComparisons", true)) {}27 28void SignedCharMisuseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {29  Options.store(Opts, "CharTypedefsToIgnore", CharTypedefsToIgnoreList);30  Options.store(Opts, "DiagnoseSignedUnsignedCharComparisons",31                DiagnoseSignedUnsignedCharComparisons);32}33 34// Create a matcher for char -> integer cast.35BindableMatcher<clang::Stmt> SignedCharMisuseCheck::charCastExpression(36    bool IsSigned, const Matcher<clang::QualType> &IntegerType,37    const std::string &CastBindName) const {38  // We can ignore typedefs which are some kind of integer types39  // (e.g. typedef char sal_Int8). In this case, we don't need to40  // worry about the misinterpretation of char values.41  const auto IntTypedef = qualType(hasDeclaration(typedefDecl(42      hasAnyName(utils::options::parseStringList(CharTypedefsToIgnoreList)))));43 44  auto CharTypeExpr = expr();45  if (IsSigned) {46    CharTypeExpr = expr(hasType(47        qualType(isAnyCharacter(), isSignedInteger(), unless(IntTypedef))));48  } else {49    CharTypeExpr = expr(hasType(qualType(50        isAnyCharacter(), unless(isSignedInteger()), unless(IntTypedef))));51  }52 53  const auto ImplicitCastExpr =54      implicitCastExpr(hasSourceExpression(CharTypeExpr),55                       hasImplicitDestinationType(IntegerType))56          .bind(CastBindName);57 58  const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr));59  const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr));60  const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr));61 62  // We catch any type of casts to an integer. We need to have these cast63  // expressions explicitly to catch only those casts which are direct children64  // of the checked expressions. (e.g. assignment, declaration).65  return traverse(TK_AsIs, expr(anyOf(ImplicitCastExpr, CStyleCastExpr,66                                      StaticCastExpr, FunctionalCastExpr)));67}68 69void SignedCharMisuseCheck::registerMatchers(MatchFinder *Finder) {70  const auto IntegerType =71      qualType(isInteger(), unless(isAnyCharacter()), unless(booleanType()))72          .bind("integerType");73  const auto SignedCharCastExpr =74      charCastExpression(true, IntegerType, "signedCastExpression");75  const auto UnSignedCharCastExpr =76      charCastExpression(false, IntegerType, "unsignedCastExpression");77  const bool IsC23 = getLangOpts().C23;78 79  // Catch assignments with signed char -> integer conversion. Ignore false80  // positives on C23 enums with the fixed underlying type of signed char.81  const auto AssignmentOperatorExpr =82      expr(binaryOperator(hasOperatorName("="), hasLHS(hasType(IntegerType)),83                          hasRHS(SignedCharCastExpr)),84           IsC23 ? unless(binaryOperator(85                       hasLHS(hasType(hasCanonicalType(enumType())))))86                 : Matcher<Stmt>(anything()));87 88  Finder->addMatcher(AssignmentOperatorExpr, this);89 90  // Catch declarations with signed char -> integer conversion. Ignore false91  // positives on C23 enums with the fixed underlying type of signed char.92  const auto Declaration = varDecl(93      isDefinition(), hasType(IntegerType), hasInitializer(SignedCharCastExpr),94      IsC23 ? unless(hasType(hasCanonicalType(enumType())))95            : Matcher<VarDecl>(anything()));96 97  Finder->addMatcher(Declaration, this);98 99  if (DiagnoseSignedUnsignedCharComparisons) {100    // Catch signed char/unsigned char comparison.101    const auto CompareOperator =102        expr(binaryOperator(hasAnyOperatorName("==", "!="),103                            anyOf(allOf(hasLHS(SignedCharCastExpr),104                                        hasRHS(UnSignedCharCastExpr)),105                                  allOf(hasLHS(UnSignedCharCastExpr),106                                        hasRHS(SignedCharCastExpr)))))107            .bind("comparison");108 109    Finder->addMatcher(CompareOperator, this);110  }111 112  // Catch array subscripts with signed char -> integer conversion.113  // Matcher for C arrays.114  const auto CArraySubscript =115      arraySubscriptExpr(hasIndex(SignedCharCastExpr)).bind("arraySubscript");116 117  Finder->addMatcher(CArraySubscript, this);118 119  // Matcher for std arrays.120  const auto STDArraySubscript =121      cxxOperatorCallExpr(122          hasOverloadedOperatorName("[]"),123          hasArgument(0, hasType(cxxRecordDecl(hasName("::std::array")))),124          hasArgument(1, SignedCharCastExpr))125          .bind("arraySubscript");126 127  Finder->addMatcher(STDArraySubscript, this);128}129 130void SignedCharMisuseCheck::check(const MatchFinder::MatchResult &Result) {131  const auto *SignedCastExpression =132      Result.Nodes.getNodeAs<ImplicitCastExpr>("signedCastExpression");133  const auto *IntegerType = Result.Nodes.getNodeAs<QualType>("integerType");134  assert(SignedCastExpression);135  assert(IntegerType);136 137  // Ignore the match if we know that the signed char's value is not negative.138  // The potential misinterpretation happens for negative values only.139  Expr::EvalResult EVResult;140  if (!SignedCastExpression->isValueDependent() &&141      SignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,142                                                        *Result.Context)) {143    const llvm::APSInt Value = EVResult.Val.getInt();144    if (Value.isNonNegative())145      return;146  }147 148  if (const auto *Comparison = Result.Nodes.getNodeAs<Expr>("comparison")) {149    const auto *UnSignedCastExpression =150        Result.Nodes.getNodeAs<ImplicitCastExpr>("unsignedCastExpression");151 152    // We can ignore the ASCII value range also for unsigned char.153    Expr::EvalResult EVResult;154    if (!UnSignedCastExpression->isValueDependent() &&155        UnSignedCastExpression->getSubExpr()->EvaluateAsInt(EVResult,156                                                            *Result.Context)) {157      const llvm::APSInt Value = EVResult.Val.getInt();158      if (Value <= UnsignedASCIIUpperBound)159        return;160    }161 162    diag(Comparison->getBeginLoc(),163         "comparison between 'signed char' and 'unsigned char'");164  } else if (Result.Nodes.getNodeAs<Expr>("arraySubscript")) {165    diag(SignedCastExpression->getBeginLoc(),166         "'signed char' to %0 conversion in array subscript; "167         "consider casting to 'unsigned char' first.")168        << *IntegerType;169  } else {170    diag(SignedCastExpression->getBeginLoc(),171         "'signed char' to %0 conversion; "172         "consider casting to 'unsigned char' first.")173        << *IntegerType;174  }175}176 177} // namespace clang::tidy::bugprone178