127 lines · c
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/// \file10/// Declaration of the FormatStringConverter class which is used to convert11/// printf format strings to C++ std::formatter format strings.12///13//===----------------------------------------------------------------------===//14 15#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FORMATSTRINGCONVERTER_H16#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FORMATSTRINGCONVERTER_H17 18#include "clang/AST/ASTContext.h"19#include "clang/AST/FormatString.h"20#include "clang/ASTMatchers/ASTMatchers.h"21#include <string>22 23namespace clang::tidy::utils {24 25/// Convert a printf-style format string to a std::formatter-style one, and26/// prepare any casts that are required to wrap the arguments to retain printf27/// compatibility. This class is expecting to work on the already-cooked format28/// string (i.e. all the escapes have been converted) so we have to convert them29/// back. This means that we might not convert them back using the same form.30class FormatStringConverter31 : public clang::analyze_format_string::FormatStringHandler {32public:33 using ConversionSpecifier = clang::analyze_format_string::ConversionSpecifier;34 using PrintfSpecifier = analyze_printf::PrintfSpecifier;35 36 struct Configuration {37 bool StrictMode = false;38 bool AllowTrailingNewlineRemoval = false;39 };40 41 FormatStringConverter(ASTContext *Context, const CallExpr *Call,42 unsigned FormatArgOffset, Configuration Config,43 const LangOptions &LO, SourceManager &SM,44 Preprocessor &PP);45 46 bool canApply() const { return ConversionNotPossibleReason.empty(); }47 const std::string &conversionNotPossibleReason() const {48 return ConversionNotPossibleReason;49 }50 void applyFixes(DiagnosticBuilder &Diag, SourceManager &SM);51 bool usePrintNewlineFunction() const { return UsePrintNewlineFunction; }52 53private:54 ASTContext *Context;55 const Configuration Config;56 const bool CastMismatchedIntegerTypes;57 const Expr *const *Args;58 const unsigned NumArgs;59 unsigned ArgsOffset;60 const LangOptions &LangOpts;61 std::string ConversionNotPossibleReason;62 bool FormatStringNeededRewriting = false;63 bool UsePrintNewlineFunction = false;64 size_t PrintfFormatStringPos = 0U;65 StringRef PrintfFormatString;66 67 /// Lazily-created c_str() call matcher68 std::optional<clang::ast_matchers::StatementMatcher>69 StringCStrCallExprMatcher;70 71 const StringLiteral *FormatExpr;72 std::string StandardFormatString;73 74 /// Casts to be used to wrap arguments to retain printf compatibility.75 struct ArgumentFix {76 unsigned ArgIndex;77 std::string Fix;78 79 // We currently need this for emplace_back. Roll on C++20.80 explicit ArgumentFix(unsigned ArgIndex, std::string Fix)81 : ArgIndex(ArgIndex), Fix(std::move(Fix)) {}82 };83 84 std::vector<ArgumentFix> ArgFixes;85 std::vector<clang::ast_matchers::BoundNodes> ArgCStrRemovals;86 87 // Argument rotations to cope with the fact that std::print puts the value to88 // be formatted first and the width and precision afterwards whereas printf89 // puts the width and preicision first.90 std::vector<std::tuple<unsigned, unsigned>> ArgRotates;91 92 void emitAlignment(const PrintfSpecifier &FS, std::string &FormatSpec);93 void emitSign(const PrintfSpecifier &FS, std::string &FormatSpec);94 void emitAlternativeForm(const PrintfSpecifier &FS, std::string &FormatSpec);95 void emitFieldWidth(const PrintfSpecifier &FS, std::string &FormatSpec);96 void emitPrecision(const PrintfSpecifier &FS, std::string &FormatSpec);97 void emitStringArgument(unsigned ArgIndex, const Expr *Arg);98 bool emitIntegerArgument(ConversionSpecifier::Kind ArgKind, const Expr *Arg,99 unsigned ArgIndex, std::string &FormatSpec);100 101 bool emitType(const PrintfSpecifier &FS, const Expr *Arg,102 std::string &FormatSpec);103 bool convertArgument(const PrintfSpecifier &FS, const Expr *Arg,104 std::string &StandardFormatString);105 106 void maybeRotateArguments(const PrintfSpecifier &FS);107 108 bool HandlePrintfSpecifier(const PrintfSpecifier &FS,109 const char *StartSpecifier, unsigned SpecifierLen,110 const TargetInfo &Target) override;111 112 void appendFormatText(StringRef Text);113 void finalizeFormatText();114 static std::optional<StringRef>115 formatStringContainsUnreplaceableMacro(const CallExpr *CallExpr,116 const StringLiteral *FormatExpr,117 SourceManager &SM, Preprocessor &PP);118 bool conversionNotPossible(std::string Reason) {119 ConversionNotPossibleReason = std::move(Reason);120 return false;121 }122};123 124} // namespace clang::tidy::utils125 126#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FORMATSTRINGCONVERTER_H127