brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.0 KiB · 1ab9576 Raw
166 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 "UseStdPrintCheck.h"10#include "../utils/FormatStringConverter.h"11#include "../utils/Matchers.h"12#include "../utils/OptionsUtils.h"13#include "clang/ASTMatchers/ASTMatchFinder.h"14#include "clang/Lex/Lexer.h"15 16using namespace clang::ast_matchers;17 18namespace clang::tidy::modernize {19 20namespace {21AST_MATCHER(StringLiteral, isOrdinary) { return Node.isOrdinary(); }22} // namespace23 24UseStdPrintCheck::UseStdPrintCheck(StringRef Name, ClangTidyContext *Context)25    : ClangTidyCheck(Name, Context), PP(nullptr),26      StrictMode(Options.get("StrictMode", false)),27      PrintfLikeFunctions(utils::options::parseStringList(28          Options.get("PrintfLikeFunctions", ""))),29      FprintfLikeFunctions(utils::options::parseStringList(30          Options.get("FprintfLikeFunctions", ""))),31      ReplacementPrintFunction(32          Options.get("ReplacementPrintFunction", "std::print")),33      ReplacementPrintlnFunction(34          Options.get("ReplacementPrintlnFunction", "std::println")),35      IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",36                                               utils::IncludeSorter::IS_LLVM),37                      areDiagsSelfContained()),38      MaybeHeaderToInclude(Options.get("PrintHeader")) {39  if (PrintfLikeFunctions.empty() && FprintfLikeFunctions.empty()) {40    PrintfLikeFunctions.emplace_back("::printf");41    PrintfLikeFunctions.emplace_back("absl::PrintF");42    FprintfLikeFunctions.emplace_back("::fprintf");43    FprintfLikeFunctions.emplace_back("absl::FPrintF");44  }45 46  if (!MaybeHeaderToInclude && (ReplacementPrintFunction == "std::print" ||47                                ReplacementPrintlnFunction == "std::println"))48    MaybeHeaderToInclude = "<print>";49}50 51void UseStdPrintCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {52  using utils::options::serializeStringList;53  Options.store(Opts, "StrictMode", StrictMode);54  Options.store(Opts, "PrintfLikeFunctions",55                serializeStringList(PrintfLikeFunctions));56  Options.store(Opts, "FprintfLikeFunctions",57                serializeStringList(FprintfLikeFunctions));58  Options.store(Opts, "ReplacementPrintFunction", ReplacementPrintFunction);59  Options.store(Opts, "ReplacementPrintlnFunction", ReplacementPrintlnFunction);60  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());61  if (MaybeHeaderToInclude)62    Options.store(Opts, "PrintHeader", *MaybeHeaderToInclude);63}64 65void UseStdPrintCheck::registerPPCallbacks(const SourceManager &SM,66                                           Preprocessor *PP,67                                           Preprocessor *ModuleExpanderPP) {68  IncludeInserter.registerPreprocessor(PP);69  this->PP = PP;70}71 72static clang::ast_matchers::StatementMatcher unusedReturnValue(73    const clang::ast_matchers::StatementMatcher &MatchedCallExpr) {74  auto UnusedInCompoundStmt =75      compoundStmt(forEach(MatchedCallExpr),76                   // The checker can't currently differentiate between the77                   // return statement and other statements inside GNU statement78                   // expressions, so disable the checker inside them to avoid79                   // false positives.80                   unless(hasParent(stmtExpr())));81  auto UnusedInIfStmt =82      ifStmt(eachOf(hasThen(MatchedCallExpr), hasElse(MatchedCallExpr)));83  auto UnusedInWhileStmt = whileStmt(hasBody(MatchedCallExpr));84  auto UnusedInDoStmt = doStmt(hasBody(MatchedCallExpr));85  auto UnusedInForStmt =86      forStmt(eachOf(hasLoopInit(MatchedCallExpr),87                     hasIncrement(MatchedCallExpr), hasBody(MatchedCallExpr)));88  auto UnusedInRangeForStmt = cxxForRangeStmt(hasBody(MatchedCallExpr));89  auto UnusedInCaseStmt = switchCase(forEach(MatchedCallExpr));90 91  return stmt(anyOf(UnusedInCompoundStmt, UnusedInIfStmt, UnusedInWhileStmt,92                    UnusedInDoStmt, UnusedInForStmt, UnusedInRangeForStmt,93                    UnusedInCaseStmt));94}95 96void UseStdPrintCheck::registerMatchers(MatchFinder *Finder) {97  if (!PrintfLikeFunctions.empty())98    Finder->addMatcher(99        unusedReturnValue(100            callExpr(argumentCountAtLeast(1),101                     hasArgument(0, stringLiteral(isOrdinary())),102                     callee(functionDecl(matchers::matchesAnyListedName(103                                             PrintfLikeFunctions))104                                .bind("func_decl")))105                .bind("printf")),106        this);107 108  if (!FprintfLikeFunctions.empty())109    Finder->addMatcher(110        unusedReturnValue(111            callExpr(argumentCountAtLeast(2),112                     hasArgument(1, stringLiteral(isOrdinary())),113                     callee(functionDecl(matchers::matchesAnyListedName(114                                             FprintfLikeFunctions))115                                .bind("func_decl")))116                .bind("fprintf")),117        this);118}119 120void UseStdPrintCheck::check(const MatchFinder::MatchResult &Result) {121  unsigned FormatArgOffset = 0;122  const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");123  const auto *Printf = Result.Nodes.getNodeAs<CallExpr>("printf");124  if (!Printf) {125    Printf = Result.Nodes.getNodeAs<CallExpr>("fprintf");126    FormatArgOffset = 1;127  }128 129  utils::FormatStringConverter::Configuration ConverterConfig;130  ConverterConfig.StrictMode = StrictMode;131  ConverterConfig.AllowTrailingNewlineRemoval = true;132  assert(PP && "Preprocessor should be set by registerPPCallbacks");133  utils::FormatStringConverter Converter(134      Result.Context, Printf, FormatArgOffset, ConverterConfig, getLangOpts(),135      *Result.SourceManager, *PP);136  const Expr *PrintfCall = Printf->getCallee();137  const StringRef ReplacementFunction = Converter.usePrintNewlineFunction()138                                            ? ReplacementPrintlnFunction139                                            : ReplacementPrintFunction;140  if (!Converter.canApply()) {141    diag(PrintfCall->getBeginLoc(),142         "unable to use '%0' instead of %1 because %2")143        << PrintfCall->getSourceRange() << ReplacementFunction144        << OldFunction->getIdentifier()145        << Converter.conversionNotPossibleReason();146    return;147  }148 149  DiagnosticBuilder Diag =150      diag(PrintfCall->getBeginLoc(), "use '%0' instead of %1")151      << ReplacementFunction << OldFunction->getIdentifier();152 153  Diag << FixItHint::CreateReplacement(154      CharSourceRange::getTokenRange(PrintfCall->getExprLoc(),155                                     PrintfCall->getEndLoc()),156      ReplacementFunction);157  Converter.applyFixes(Diag, *Result.SourceManager);158 159  if (MaybeHeaderToInclude)160    Diag << IncludeInserter.createIncludeInsertion(161        Result.Context->getSourceManager().getFileID(PrintfCall->getBeginLoc()),162        *MaybeHeaderToInclude);163}164 165} // namespace clang::tidy::modernize166