brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.1 KiB · e10b17c Raw
105 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 "UnintendedCharOstreamOutputCheck.h"10#include "../utils/Matchers.h"11#include "../utils/OptionsUtils.h"12#include "clang/AST/Type.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "clang/ASTMatchers/ASTMatchers.h"15#include "clang/Basic/Diagnostic.h"16#include "clang/Tooling/FixIt.h"17 18using namespace clang::ast_matchers;19 20namespace clang::tidy::bugprone {21 22namespace {23 24// check if the type is unsigned char or signed char25AST_MATCHER(Type, isNumericChar) {26  return Node.isSpecificBuiltinType(BuiltinType::SChar) ||27         Node.isSpecificBuiltinType(BuiltinType::UChar);28}29 30// check if the type is char31AST_MATCHER(Type, isChar) {32  return Node.isSpecificBuiltinType(BuiltinType::Char_S) ||33         Node.isSpecificBuiltinType(BuiltinType::Char_U);34}35 36} // namespace37 38UnintendedCharOstreamOutputCheck::UnintendedCharOstreamOutputCheck(39    StringRef Name, ClangTidyContext *Context)40    : ClangTidyCheck(Name, Context),41      AllowedTypes(utils::options::parseStringList(42          Options.get("AllowedTypes", "unsigned char;signed char"))),43      CastTypeName(Options.get("CastTypeName")) {}44void UnintendedCharOstreamOutputCheck::storeOptions(45    ClangTidyOptions::OptionMap &Opts) {46  Options.store(Opts, "AllowedTypes",47                utils::options::serializeStringList(AllowedTypes));48  if (CastTypeName.has_value())49    Options.store(Opts, "CastTypeName", CastTypeName.value());50}51 52void UnintendedCharOstreamOutputCheck::registerMatchers(MatchFinder *Finder) {53  auto BasicOstream =54      cxxRecordDecl(hasName("::std::basic_ostream"),55                    // only basic_ostream<char, Traits> has overload operator<<56                    // with char / unsigned char / signed char57                    classTemplateSpecializationDecl(58                        hasTemplateArgument(0, refersToType(isChar()))));59  auto IsDeclRefExprFromAllowedTypes = declRefExpr(to(varDecl(60      hasType(matchers::matchesAnyListedTypeName(AllowedTypes, false)))));61  auto IsExplicitCastExprFromAllowedTypes = explicitCastExpr(hasDestinationType(62      matchers::matchesAnyListedTypeName(AllowedTypes, false)));63  Finder->addMatcher(64      cxxOperatorCallExpr(65          hasOverloadedOperatorName("<<"),66          hasLHS(hasType(hasUnqualifiedDesugaredType(67              recordType(hasDeclaration(cxxRecordDecl(68                  anyOf(BasicOstream, isDerivedFrom(BasicOstream)))))))),69          hasRHS(expr(hasType(hasUnqualifiedDesugaredType(isNumericChar())),70                      unless(ignoringParenImpCasts(71                          anyOf(IsDeclRefExprFromAllowedTypes,72                                IsExplicitCastExprFromAllowedTypes))))))73          .bind("x"),74      this);75}76 77void UnintendedCharOstreamOutputCheck::check(78    const MatchFinder::MatchResult &Result) {79  const auto *Call = Result.Nodes.getNodeAs<CXXOperatorCallExpr>("x");80  const Expr *Value = Call->getArg(1);81  const SourceRange SourceRange = Value->getSourceRange();82 83  const DiagnosticBuilder Builder =84      diag(Call->getOperatorLoc(),85           "%0 passed to 'operator<<' outputs as character instead of integer. "86           "cast to 'unsigned int' to print numeric value or cast to 'char' to "87           "print as character")88      << Value->getType() << SourceRange;89 90  const QualType T = Value->getType();91  const Type *UnqualifiedDesugaredType = T->getUnqualifiedDesugaredType();92 93  const llvm::StringRef CastType = CastTypeName.value_or(94      UnqualifiedDesugaredType->isSpecificBuiltinType(BuiltinType::SChar)95          ? "int"96          : "unsigned int");97 98  Builder << FixItHint::CreateReplacement(99      SourceRange, ("static_cast<" + CastType + ">(" +100                    tooling::fixit::getText(*Value, *Result.Context) + ")")101                       .str());102}103 104} // namespace clang::tidy::bugprone105