brintos

brintos / llvm-project-archived public Read only

0
0
Text · 4.3 KiB · c95834f Raw
108 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 "UseStdFormatCheck.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 24UseStdFormatCheck::UseStdFormatCheck(StringRef Name, ClangTidyContext *Context)25    : ClangTidyCheck(Name, Context),26      StrictMode(Options.get("StrictMode", false)),27      StrFormatLikeFunctions(utils::options::parseStringList(28          Options.get("StrFormatLikeFunctions", ""))),29      ReplacementFormatFunction(30          Options.get("ReplacementFormatFunction", "std::format")),31      IncludeInserter(Options.getLocalOrGlobal("IncludeStyle",32                                               utils::IncludeSorter::IS_LLVM),33                      areDiagsSelfContained()),34      MaybeHeaderToInclude(Options.get("FormatHeader")) {35  if (StrFormatLikeFunctions.empty())36    StrFormatLikeFunctions.emplace_back("absl::StrFormat");37 38  if (!MaybeHeaderToInclude && ReplacementFormatFunction == "std::format")39    MaybeHeaderToInclude = "<format>";40}41 42void UseStdFormatCheck::registerPPCallbacks(const SourceManager &SM,43                                            Preprocessor *PP,44                                            Preprocessor *ModuleExpanderPP) {45  IncludeInserter.registerPreprocessor(PP);46  this->PP = PP;47}48 49void UseStdFormatCheck::registerMatchers(MatchFinder *Finder) {50  Finder->addMatcher(51      callExpr(argumentCountAtLeast(1),52               hasArgument(0, stringLiteral(isOrdinary())),53               callee(functionDecl(matchers::matchesAnyListedName(54                                       StrFormatLikeFunctions))55                          .bind("func_decl")))56          .bind("strformat"),57      this);58}59 60void UseStdFormatCheck::storeOptions(ClangTidyOptions::OptionMap &Opts) {61  using utils::options::serializeStringList;62  Options.store(Opts, "StrictMode", StrictMode);63  Options.store(Opts, "StrFormatLikeFunctions",64                serializeStringList(StrFormatLikeFunctions));65  Options.store(Opts, "ReplacementFormatFunction", ReplacementFormatFunction);66  Options.store(Opts, "IncludeStyle", IncludeInserter.getStyle());67  if (MaybeHeaderToInclude)68    Options.store(Opts, "FormatHeader", *MaybeHeaderToInclude);69}70 71void UseStdFormatCheck::check(const MatchFinder::MatchResult &Result) {72  const unsigned FormatArgOffset = 0;73  const auto *OldFunction = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");74  const auto *StrFormat = Result.Nodes.getNodeAs<CallExpr>("strformat");75 76  utils::FormatStringConverter::Configuration ConverterConfig;77  ConverterConfig.StrictMode = StrictMode;78  utils::FormatStringConverter Converter(79      Result.Context, StrFormat, FormatArgOffset, ConverterConfig,80      getLangOpts(), *Result.SourceManager, *PP);81  const Expr *StrFormatCall = StrFormat->getCallee();82  if (!Converter.canApply()) {83    diag(StrFormat->getBeginLoc(),84         "unable to use '%0' instead of %1 because %2")85        << StrFormatCall->getSourceRange() << ReplacementFormatFunction86        << OldFunction->getIdentifier()87        << Converter.conversionNotPossibleReason();88    return;89  }90 91  DiagnosticBuilder Diag =92      diag(StrFormatCall->getBeginLoc(), "use '%0' instead of %1")93      << ReplacementFormatFunction << OldFunction->getIdentifier();94  Diag << FixItHint::CreateReplacement(95      CharSourceRange::getTokenRange(StrFormatCall->getExprLoc(),96                                     StrFormatCall->getEndLoc()),97      ReplacementFormatFunction);98  Converter.applyFixes(Diag, *Result.SourceManager);99 100  if (MaybeHeaderToInclude)101    Diag << IncludeInserter.createIncludeInsertion(102        Result.Context->getSourceManager().getFileID(103            StrFormatCall->getBeginLoc()),104        *MaybeHeaderToInclude);105}106 107} // namespace clang::tidy::modernize108