• 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 
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);
44 
canApply()45   bool canApply() const { return ConversionNotPossibleReason.empty(); }
conversionNotPossibleReason()46   const std::string &conversionNotPossibleReason() const {
47     return ConversionNotPossibleReason;
48   }
49   void applyFixes(DiagnosticBuilder &Diag, SourceManager &SM);
usePrintNewlineFunction()50   bool usePrintNewlineFunction() const { return UsePrintNewlineFunction; }
51 
52 private:
53   ASTContext *Context;
54   const Configuration Config;
55   const bool CastMismatchedIntegerTypes;
56   const Expr *const *Args;
57   const unsigned NumArgs;
58   unsigned ArgsOffset;
59   const LangOptions &LangOpts;
60   std::string ConversionNotPossibleReason;
61   bool FormatStringNeededRewriting = false;
62   bool UsePrintNewlineFunction = false;
63   size_t PrintfFormatStringPos = 0U;
64   StringRef PrintfFormatString;
65 
66   /// Lazily-created c_str() call matcher
67   std::optional<clang::ast_matchers::StatementMatcher>
68       StringCStrCallExprMatcher;
69 
70   const StringLiteral *FormatExpr;
71   std::string StandardFormatString;
72 
73   /// Casts to be used to wrap arguments to retain printf compatibility.
74   struct ArgumentFix {
75     unsigned ArgIndex;
76     std::string Fix;
77 
78     // We currently need this for emplace_back. Roll on C++20.
ArgumentFixArgumentFix79     explicit ArgumentFix(unsigned ArgIndex, std::string Fix)
80         : ArgIndex(ArgIndex), Fix(std::move(Fix)) {}
81   };
82 
83   std::vector<ArgumentFix> ArgFixes;
84   std::vector<clang::ast_matchers::BoundNodes> ArgCStrRemovals;
85 
86   // Argument rotations to cope with the fact that std::print puts the value to
87   // be formatted first and the width and precision afterwards whereas printf
88   // puts the width and preicision first.
89   std::vector<std::tuple<unsigned, unsigned>> ArgRotates;
90 
91   void emitAlignment(const PrintfSpecifier &FS, std::string &FormatSpec);
92   void emitSign(const PrintfSpecifier &FS, std::string &FormatSpec);
93   void emitAlternativeForm(const PrintfSpecifier &FS, std::string &FormatSpec);
94   void emitFieldWidth(const PrintfSpecifier &FS, std::string &FormatSpec);
95   void emitPrecision(const PrintfSpecifier &FS, std::string &FormatSpec);
96   void emitStringArgument(unsigned ArgIndex, const Expr *Arg);
97   bool emitIntegerArgument(ConversionSpecifier::Kind ArgKind, const Expr *Arg,
98                            unsigned ArgIndex, std::string &FormatSpec);
99 
100   bool emitType(const PrintfSpecifier &FS, const Expr *Arg,
101                 std::string &FormatSpec);
102   bool convertArgument(const PrintfSpecifier &FS, const Expr *Arg,
103                        std::string &StandardFormatString);
104 
105   void maybeRotateArguments(const PrintfSpecifier &FS);
106 
107   bool HandlePrintfSpecifier(const PrintfSpecifier &FS,
108                              const char *StartSpecifier, unsigned SpecifierLen,
109                              const TargetInfo &Target) override;
110 
111   void appendFormatText(StringRef Text);
112   void finalizeFormatText();
conversionNotPossible(std::string Reason)113   bool conversionNotPossible(std::string Reason) {
114     ConversionNotPossibleReason = std::move(Reason);
115     return false;
116   }
117 };
118 
119 } // namespace clang::tidy::utils
120 
121 #endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_UTILS_FORMATSTRINGCONVERTER_H
122