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