//===--- SignedCharMisuseCheck.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 "SignedCharMisuseCheck.h" #include "../utils/OptionsUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" using namespace clang::ast_matchers; using namespace clang::ast_matchers::internal; namespace clang { namespace tidy { namespace bugprone { static Matcher hasAnyListedName(const std::string &Names) { const std::vector NameList = utils::options::parseStringList(Names); return hasAnyName(std::vector(NameList.begin(), NameList.end())); } SignedCharMisuseCheck::SignedCharMisuseCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), CharTypdefsToIgnoreList(Options.get("CharTypdefsToIgnore", "")) {} void SignedCharMisuseCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) { Options.store(Opts, "CharTypdefsToIgnore", CharTypdefsToIgnoreList); } void SignedCharMisuseCheck::registerMatchers(MatchFinder *Finder) { // We can ignore typedefs which are some kind of integer types // (e.g. typedef char sal_Int8). In this case, we don't need to // worry about the misinterpretation of char values. const auto IntTypedef = qualType( hasDeclaration(typedefDecl(hasAnyListedName(CharTypdefsToIgnoreList)))); const auto SignedCharType = expr(hasType(qualType( allOf(isAnyCharacter(), isSignedInteger(), unless(IntTypedef))))); const auto IntegerType = qualType(allOf(isInteger(), unless(isAnyCharacter()), unless(booleanType()))) .bind("integerType"); // We are interested in signed char -> integer conversion. const auto ImplicitCastExpr = implicitCastExpr(hasSourceExpression(SignedCharType), hasImplicitDestinationType(IntegerType)) .bind("castExpression"); const auto CStyleCastExpr = cStyleCastExpr(has(ImplicitCastExpr)); const auto StaticCastExpr = cxxStaticCastExpr(has(ImplicitCastExpr)); const auto FunctionalCastExpr = cxxFunctionalCastExpr(has(ImplicitCastExpr)); // We catch any type of casts to an integer. We need to have these cast // expressions explicitly to catch only those casts which are direct children // of an assignment/declaration. const auto CastExpr = expr(anyOf(ImplicitCastExpr, CStyleCastExpr, StaticCastExpr, FunctionalCastExpr)); // Catch assignments with the suspicious type conversion. const auto AssignmentOperatorExpr = expr(binaryOperator( hasOperatorName("="), hasLHS(hasType(IntegerType)), hasRHS(CastExpr))); Finder->addMatcher(AssignmentOperatorExpr, this); // Catch declarations with the suspicious type conversion. const auto Declaration = varDecl(isDefinition(), hasType(IntegerType), hasInitializer(CastExpr)); Finder->addMatcher(Declaration, this); } void SignedCharMisuseCheck::check(const MatchFinder::MatchResult &Result) { const auto *CastExpression = Result.Nodes.getNodeAs("castExpression"); const auto *IntegerType = Result.Nodes.getNodeAs("integerType"); assert(CastExpression); assert(IntegerType); // Ignore the match if we know that the value is not negative. // The potential misinterpretation happens for negative values only. Expr::EvalResult EVResult; if (!CastExpression->isValueDependent() && CastExpression->getSubExpr()->EvaluateAsInt(EVResult, *Result.Context)) { llvm::APSInt Value1 = EVResult.Val.getInt(); if (Value1.isNonNegative()) return; } diag(CastExpression->getBeginLoc(), "'signed char' to %0 conversion; " "consider casting to 'unsigned char' first.") << *IntegerType; } } // namespace bugprone } // namespace tidy } // namespace clang