@@ -25,8 +25,8 @@ static std::optional<AlignStyle> translateLocChar(char C) {
2525 LLVM_BUILTIN_UNREACHABLE;
2626}
2727
28- bool formatv_object_base:: consumeFieldLayout (StringRef &Spec, AlignStyle &Where,
29- size_t &Align, char &Pad) {
28+ static bool consumeFieldLayout (StringRef &Spec, AlignStyle &Where,
29+ size_t &Align, char &Pad) {
3030 Where = AlignStyle::Right;
3131 Align = 0 ;
3232 Pad = ' ' ;
@@ -35,8 +35,7 @@ bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
3535
3636 if (Spec.size () > 1 ) {
3737 // A maximum of 2 characters at the beginning can be used for something
38- // other
39- // than the width.
38+ // other than the width.
4039 // If Spec[1] is a loc char, then Spec[0] is a pad char and Spec[2:...]
4140 // contains the width.
4241 // Otherwise, if Spec[0] is a loc char, then Spec[1:...] contains the width.
@@ -55,8 +54,7 @@ bool formatv_object_base::consumeFieldLayout(StringRef &Spec, AlignStyle &Where,
5554 return !Failed;
5655}
5756
58- std::optional<ReplacementItem>
59- formatv_object_base::parseReplacementItem (StringRef Spec) {
57+ static std::optional<ReplacementItem> parseReplacementItem (StringRef Spec) {
6058 StringRef RepString = Spec.trim (" {}" );
6159
6260 // If the replacement sequence does not start with a non-negative integer,
@@ -82,15 +80,14 @@ formatv_object_base::parseReplacementItem(StringRef Spec) {
8280 RepString = StringRef ();
8381 }
8482 RepString = RepString.trim ();
85- if (!RepString.empty ()) {
86- assert (false && " Unexpected characters found in replacement string!" );
87- }
83+ assert (RepString.empty () &&
84+ " Unexpected characters found in replacement string!" );
8885
8986 return ReplacementItem{Spec, Index, Align, Where, Pad, Options};
9087}
9188
92- std::pair<ReplacementItem, StringRef>
93- formatv_object_base:: splitLiteralAndReplacement (StringRef Fmt) {
89+ static std::pair<ReplacementItem, StringRef>
90+ splitLiteralAndReplacement (StringRef Fmt) {
9491 while (!Fmt.empty ()) {
9592 // Everything up until the first brace is a literal.
9693 if (Fmt.front () != ' {' ) {
@@ -143,15 +140,77 @@ formatv_object_base::splitLiteralAndReplacement(StringRef Fmt) {
143140 return std::make_pair (ReplacementItem{Fmt}, StringRef ());
144141}
145142
143+ #ifndef NDEBUG
144+ #define ENABLE_VALIDATION 1
145+ #else
146+ #define ENABLE_VALIDATION 0 // Conveniently enable validation in release mode.
147+ #endif
148+
146149SmallVector<ReplacementItem, 2 >
147- formatv_object_base::parseFormatString (StringRef Fmt) {
150+ formatv_object_base::parseFormatString (StringRef Fmt, size_t NumArgs,
151+ bool Validate) {
148152 SmallVector<ReplacementItem, 2 > Replacements;
149- ReplacementItem I;
153+
154+ #if ENABLE_VALIDATION
155+ const StringRef SavedFmtStr = Fmt;
156+ size_t NumExpectedArgs = 0 ;
157+ #endif
158+
150159 while (!Fmt.empty ()) {
160+ ReplacementItem I;
151161 std::tie (I, Fmt) = splitLiteralAndReplacement (Fmt);
152162 if (I.Type != ReplacementType::Empty)
153163 Replacements.push_back (I);
164+ #if ENABLE_VALIDATION
165+ if (I.Type == ReplacementType::Format)
166+ NumExpectedArgs = std::max (NumExpectedArgs, I.Index + 1 );
167+ #endif
168+ }
169+
170+ #if ENABLE_VALIDATION
171+ if (!Validate)
172+ return Replacements;
173+
174+ // Perform additional validation. Verify that the number of arguments matches
175+ // the number of replacement indices and that there are no holes in the
176+ // replacement indices.
177+
178+ // When validation fails, return an array of replacement items that
179+ // will print an error message as the outout of this formatv() (used when
180+ // validation is enabled in release mode).
181+ auto getErrorReplacements = [SavedFmtStr](StringLiteral ErrorMsg) {
182+ return SmallVector<ReplacementItem, 2 >{
183+ ReplacementItem (" Invalid formatv() call: " ), ReplacementItem (ErrorMsg),
184+ ReplacementItem (" for format string: " ), ReplacementItem (SavedFmtStr)};
185+ };
186+
187+ if (NumExpectedArgs != NumArgs) {
188+ errs () << formatv (
189+ " Expected {0} Args, but got {1} for format string '{2}'\n " ,
190+ NumExpectedArgs, NumArgs, SavedFmtStr);
191+ assert (0 && " Invalid formatv() call" );
192+ return getErrorReplacements (" Unexpected number of arguments" );
193+ }
194+
195+ // Find the number of unique indices seen. All replacement indices
196+ // are < NumExpectedArgs.
197+ SmallVector<bool > Indices (NumExpectedArgs);
198+ size_t Count = 0 ;
199+ for (const ReplacementItem &I : Replacements) {
200+ if (I.Type != ReplacementType::Format || Indices[I.Index ])
201+ continue ;
202+ Indices[I.Index ] = true ;
203+ ++Count;
204+ }
205+
206+ if (Count != NumExpectedArgs) {
207+ errs () << formatv (
208+ " Replacement field indices cannot have holes for format string '{0}'\n " ,
209+ SavedFmtStr);
210+ assert (0 && " Invalid format string" );
211+ return getErrorReplacements (" Replacement indices have holes" );
154212 }
213+ #endif // ENABLE_VALIDATION
155214 return Replacements;
156215}
157216
0 commit comments