6
6
// ===----------------------------------------------------------------------===//
7
7
8
8
#include " llvm/Support/FormatVariadic.h"
9
+ #include " llvm/ADT/SmallSet.h"
9
10
#include < cassert>
10
11
#include < optional>
12
+ #include < variant>
11
13
12
14
using namespace llvm ;
13
15
@@ -25,8 +27,8 @@ static std::optional<AlignStyle> translateLocChar(char C) {
25
27
LLVM_BUILTIN_UNREACHABLE;
26
28
}
27
29
28
- bool formatv_object_base:: consumeFieldLayout (StringRef &Spec, AlignStyle &Where,
29
- size_t &Align, char &Pad) {
30
+ static bool consumeFieldLayout (StringRef &Spec, AlignStyle &Where,
31
+ size_t &Align, char &Pad) {
30
32
Where = AlignStyle::Right;
31
33
Align = 0 ;
32
34
Pad = ' ' ;
@@ -35,8 +37,7 @@ bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
35
37
36
38
if (Spec.size () > 1 ) {
37
39
// A maximum of 2 characters at the beginning can be used for something
38
- // other
39
- // than the width.
40
+ // other than the width.
40
41
// If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
41
42
// contains the width.
42
43
// Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
@@ -55,8 +56,8 @@ bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
55
56
return !Failed;
56
57
}
57
58
58
- std::optional <ReplacementItem>
59
- formatv_object_base:: parseReplacementItem (StringRef Spec) {
59
+ static std::variant <ReplacementItem, StringRef >
60
+ parseReplacementItem (StringRef Spec) {
60
61
StringRef RepString = Spec.trim (" {}" );
61
62
62
63
// If the replacement sequence does not start with a non-negative integer,
@@ -67,92 +68,123 @@ formatv_object_base::parseReplacementItem(StringRef Spec) {
67
68
StringRef Options;
68
69
size_t Index = 0 ;
69
70
RepString = RepString.trim ();
70
- if (RepString.consumeInteger (0 , Index)) {
71
- assert (false && " Invalid replacement sequence index!" );
72
- return ReplacementItem{};
73
- }
71
+ if (RepString.consumeInteger (0 , Index))
72
+ return " Invalid replacement sequence index!" ;
74
73
RepString = RepString.trim ();
75
74
if (RepString.consume_front (" ," )) {
76
75
if (!consumeFieldLayout (RepString, Where, Align, Pad))
77
- assert ( false && " Invalid replacement field layout specification!" ) ;
76
+ return " Invalid replacement field layout specification!" ;
78
77
}
79
78
RepString = RepString.trim ();
80
79
if (RepString.consume_front (" :" )) {
81
80
Options = RepString.trim ();
82
81
RepString = StringRef ();
83
82
}
84
83
RepString = RepString.trim ();
85
- if (!RepString.empty ()) {
86
- assert (false && " Unexpected characters found in replacement string!" );
87
- }
88
-
84
+ if (!RepString.empty ())
85
+ return " Unexpected character found in replacement string!" ;
89
86
return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
90
87
}
91
88
92
- std::pair<ReplacementItem, StringRef>
93
- formatv_object_base::splitLiteralAndReplacement (StringRef Fmt) {
94
- while (!Fmt.empty ()) {
95
- // Everything up until the first brace is a literal.
96
- if (Fmt.front () != ' {' ) {
97
- std::size_t BO = Fmt.find_first_of (' {' );
98
- return std::make_pair (ReplacementItem{Fmt.substr (0 , BO)}, Fmt.substr (BO));
99
- }
100
-
101
- StringRef Braces = Fmt.take_while ([](char C) { return C == ' {' ; });
102
- // If there is more than one brace, then some of them are escaped. Treat
103
- // these as replacements.
104
- if (Braces.size () > 1 ) {
105
- size_t NumEscapedBraces = Braces.size () / 2 ;
106
- StringRef Middle = Fmt.take_front (NumEscapedBraces);
107
- StringRef Right = Fmt.drop_front (NumEscapedBraces * 2 );
108
- return std::make_pair (ReplacementItem{Middle}, Right);
109
- }
110
- // An unterminated open brace is undefined. Assert to indicate that this is
111
- // undefined and that we consider it an error. When asserts are disabled,
112
- // build a replacement item with an error message.
113
- std::size_t BC = Fmt.find_first_of (' }' );
114
- if (BC == StringRef::npos) {
115
- assert (
116
- false &&
117
- " Unterminated brace sequence. Escape with {{ for a literal brace." );
118
- return std::make_pair (
119
- ReplacementItem{" Unterminated brace sequence. Escape with {{ for a "
120
- " literal brace." },
121
- StringRef ());
122
- }
123
-
124
- // Even if there is a closing brace, if there is another open brace before
125
- // this closing brace, treat this portion as literal, and try again with the
126
- // next one.
127
- std::size_t BO2 = Fmt.find_first_of (' {' , 1 );
128
- if (BO2 < BC)
129
- return std::make_pair (ReplacementItem{Fmt.substr (0 , BO2)},
130
- Fmt.substr (BO2));
131
-
132
- StringRef Spec = Fmt.slice (1 , BC);
133
- StringRef Right = Fmt.substr (BC + 1 );
134
-
135
- auto RI = parseReplacementItem (Spec);
136
- if (RI)
137
- return std::make_pair (*RI, Right);
89
+ static std::variant<std::pair<ReplacementItem, StringRef>, StringRef>
90
+ splitLiteralAndReplacement (StringRef Fmt) {
91
+ // Everything up until the first brace is a literal.
92
+ if (Fmt.front () != ' {' ) {
93
+ std::size_t BO = Fmt.find_first_of (' {' );
94
+ return std::make_pair (ReplacementItem (Fmt.substr (0 , BO)), Fmt.substr (BO));
95
+ }
138
96
139
- // If there was an error parsing the replacement item, treat it as an
140
- // invalid replacement spec, and just continue.
141
- Fmt = Fmt.drop_front (BC + 1 );
97
+ StringRef Braces = Fmt.take_while ([](char C) { return C == ' {' ; });
98
+ // If there is more than one brace, then some of them are escaped. Treat
99
+ // these as replacements.
100
+ if (Braces.size () > 1 ) {
101
+ size_t NumEscapedBraces = Braces.size () / 2 ;
102
+ StringRef Middle = Fmt.take_front (NumEscapedBraces);
103
+ StringRef Right = Fmt.drop_front (NumEscapedBraces * 2 );
104
+ return std::make_pair (ReplacementItem (Middle), Right);
142
105
}
143
- return std::make_pair (ReplacementItem{Fmt}, StringRef ());
106
+ // An unterminated open brace is undefined. Assert to indicate that this is
107
+ // undefined and that we consider it an error. When asserts are disabled,
108
+ // build a replacement item with an error message.
109
+ std::size_t BC = Fmt.find_first_of (' }' );
110
+ if (BC == StringRef::npos)
111
+ return " Unterminated brace sequence. Escape with {{ for a literal brace." ;
112
+
113
+ // Even if there is a closing brace, if there is another open brace before
114
+ // this closing brace, treat this portion as literal, and try again with the
115
+ // next one.
116
+ std::size_t BO2 = Fmt.find_first_of (' {' , 1 );
117
+ if (BO2 < BC)
118
+ return std::make_pair (ReplacementItem{Fmt.substr (0 , BO2)}, Fmt.substr (BO2));
119
+
120
+ StringRef Spec = Fmt.slice (1 , BC);
121
+ StringRef Right = Fmt.substr (BC + 1 );
122
+
123
+ auto RI = parseReplacementItem (Spec);
124
+ if (const StringRef *ErrMsg = std::get_if<1 >(&RI))
125
+ return *ErrMsg;
126
+
127
+ return std::make_pair (std::get<0 >(RI), Right);
144
128
}
145
129
146
- SmallVector<ReplacementItem, 2 >
147
- formatv_object_base::parseFormatString (StringRef Fmt) {
130
+ std::pair<SmallVector<ReplacementItem, 2 >, bool >
131
+ formatv_object_base::parseFormatString (raw_ostream &S, const StringRef Fmt,
132
+ size_t NumArgs, bool Validate) {
148
133
SmallVector<ReplacementItem, 2 > Replacements;
149
134
ReplacementItem I;
150
- while (!Fmt.empty ()) {
151
- std::tie (I, Fmt) = splitLiteralAndReplacement (Fmt);
135
+ size_t NumExpectedArgs = 0 ;
136
+
137
+ // Make a copy for pasring as it updates it.
138
+ StringRef ParseFmt = Fmt;
139
+ while (!ParseFmt.empty ()) {
140
+ auto RI = splitLiteralAndReplacement (ParseFmt);
141
+ if (const StringRef *ErrMsg = std::get_if<1 >(&RI)) {
142
+ // If there was an error parsing the format string, write the error to the
143
+ // stream, and return false as second member of the pair.
144
+ errs () << " Invalid format string: " << Fmt << " \n " ;
145
+ assert (0 && " Invalid format string" );
146
+ S << *ErrMsg;
147
+ return {{}, false };
148
+ }
149
+ std::tie (I, ParseFmt) = std::get<0 >(RI);
152
150
if (I.Type != ReplacementType::Empty)
153
151
Replacements.push_back (I);
152
+ if (I.Type == ReplacementType::Format)
153
+ NumExpectedArgs = std::max (NumExpectedArgs, I.Index + 1 );
154
154
}
155
- return Replacements;
155
+ if (!Validate)
156
+ return {Replacements, true };
157
+
158
+ // Perform additional validation. Verify that the number of arguments matches
159
+ // the number of replacement indices and that there are no holes in the
160
+ // replacement indexes.
161
+ if (NumExpectedArgs != NumArgs) {
162
+ errs () << " Expected " << NumExpectedArgs << " Args, but got " << NumArgs
163
+ << " for format string '" << Fmt << " '\n " ;
164
+ assert (0 && " Invalid formatv() call" );
165
+ S << " Expected " << NumExpectedArgs << " Args, but got " << NumArgs
166
+ << " for format string '" << Fmt << " '\n " ;
167
+ return {{}, false };
168
+ }
169
+
170
+ SmallSet<size_t , 2 > Indices;
171
+ for (const ReplacementItem &R : Replacements) {
172
+ if (R.Type != ReplacementType::Format)
173
+ continue ;
174
+ Indices.insert (R.Index );
175
+ }
176
+
177
+ if (Indices.size () != NumExpectedArgs) {
178
+ errs () << " Invalid format string: Replacement field indices "
179
+ " cannot have holes for format string '"
180
+ << Fmt << " '\n " ;
181
+ assert (0 && " Invalid format string" );
182
+ S << " Replacement field indices cannot have holes for format string '"
183
+ << Fmt << " '\n " ;
184
+ return {{}, false };
185
+ }
186
+
187
+ return {Replacements, true };
156
188
}
157
189
158
190
void support::detail::format_adapter::anchor () {}
0 commit comments