• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Protocol Buffers - Google's data interchange format
2 // Copyright 2024 Google LLC.  All rights reserved.
3 //
4 // Use of this source code is governed by a BSD-style
5 // license that can be found in the LICENSE file or at
6 // https://developers.google.com/open-source/licenses/bsd
7 
8 // Author: kenton@google.com (Kenton Varda)
9 //  Based on original Protocol Buffers design by
10 //  Sanjay Ghemawat, Jeff Dean, and others.
11 
12 #include "google/protobuf/io/printer.h"
13 
14 #include <stdlib.h>
15 
16 #include <cstddef>
17 #include <functional>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
22 #include "absl/container/flat_hash_map.h"
23 #include "absl/log/absl_check.h"
24 #include "absl/log/absl_log.h"
25 #include "absl/strings/ascii.h"
26 #include "absl/strings/escaping.h"
27 #include "absl/strings/match.h"
28 #include "absl/strings/str_cat.h"
29 #include "absl/strings/str_format.h"
30 #include "absl/strings/str_split.h"
31 #include "absl/strings/string_view.h"
32 #include "absl/strings/strip.h"
33 #include "absl/types/optional.h"
34 #include "absl/types/span.h"
35 #include "absl/types/variant.h"
36 
37 namespace google {
38 namespace protobuf {
39 namespace io {
40 namespace {
41 template <typename T>
LookupInFrameStack(absl::string_view var,absl::Span<std::function<absl::optional<T> (absl::string_view)>> frames)42 absl::optional<T> LookupInFrameStack(
43     absl::string_view var,
44     absl::Span<std::function<absl::optional<T>(absl::string_view)>> frames) {
45   for (size_t i = frames.size(); i >= 1; --i) {
46     auto val = frames[i - 1](var);
47     if (val.has_value()) {
48       return val;
49     }
50   }
51   return absl::nullopt;
52 }
53 }  // namespace
54 
55 struct Printer::Format {
56   struct Chunk {
57     // The chunk's text; if this is a variable, it does not include the $...$.
58     absl::string_view text;
59 
60     // Whether or not this is a variable name, i.e., a $...$.
61     bool is_var;
62   };
63 
64   struct Line {
65     // Chunks to emit, split along $ and annotates as to whether it is a
66     // variable name.
67     std::vector<Chunk> chunks;
68 
69     // The indentation for this chunk.
70     size_t indent;
71   };
72 
73   std::vector<Line> lines;
74 
75   // Whether this is a multiline raw string, according to internal heuristics.
76   bool is_raw_string = false;
77 };
78 
TokenizeFormat(absl::string_view format_string,const PrintOptions & options)79 Printer::Format Printer::TokenizeFormat(absl::string_view format_string,
80                                         const PrintOptions& options) {
81   Format format;
82   size_t raw_string_indent = 0;
83   if (options.strip_raw_string_indentation) {
84     // We are processing a call that looks like
85     //
86     // p->Emit(R"cc(
87     //   class Foo {
88     //     int x, y, z;
89     //   };
90     // )cc");
91     //
92     // or
93     //
94     // p->Emit(R"cc(
95     //
96     //   class Foo {
97     //     int x, y, z;
98     //   };
99     // )cc");
100     //
101     // To compute the indent, we need:
102     //   1. Iterate over each line.
103     //   2. Find the first line that contains non-whitespace characters.
104     //   3. Count the number of leading spaces on that line.
105     //
106     // The following pairs of loops assume the current line is the first line
107     // with non-whitespace characters; if we consume all the spaces and
108     // then immediately hit a newline, this means this wasn't the right line and
109     // we should start over.
110     //
111     // Note that the very first character *must* be a newline; that is how we
112     // detect that this is a multi-line raw string template, and as such this is
113     // a while loop, not a do/while loop.
114 
115     absl::string_view orig = format_string;
116     while (absl::ConsumePrefix(&format_string, "\n")) {
117       raw_string_indent = 0;
118       format.is_raw_string = true;
119       while (absl::ConsumePrefix(&format_string, " ")) {
120         ++raw_string_indent;
121       }
122     }
123 
124     // If we consume the entire string, this probably wasn't a raw string and
125     // was probably something like a couple of explicit newlines.
126     if (format_string.empty()) {
127       format_string = orig;
128       format.is_raw_string = false;
129       raw_string_indent = 0;
130     }
131 
132     // This means we have a preprocessor directive and we should not have eaten
133     // the newline.
134     if (!at_start_of_line_ && absl::StartsWith(format_string, "#")) {
135       format_string = orig;
136     }
137   }
138 
139   // We now split the remaining format string into lines and discard:
140   //   1. A trailing Printer-discarded comment, if this is a raw string.
141   //
142   //   2. All leading spaces to compute that line's indent.
143   //      We do not do this for the first line, so that Emit("  ") works
144   //      correctly. We do this *regardless* of whether we are processing
145   //      a raw string, because existing non-raw-string calls to cpp::Formatter
146   //      rely on this. There is a test that validates this behavior.
147   //
148   //   3. Set the indent for that line to max(0, line_indent -
149   //      raw_string_indent), if this is not a raw string.
150   //
151   //   4. Trailing empty lines, if we know this is a raw string, except for
152   //      a single extra newline at the end.
153   //
154   // Each line is itself split into chunks along the variable delimiters, e.g.
155   // $...$.
156   bool is_first = true;
157   for (absl::string_view line_text : absl::StrSplit(format_string, '\n')) {
158     if (format.is_raw_string) {
159       size_t comment_index = line_text.find(options_.ignored_comment_start);
160       if (comment_index != absl::string_view::npos) {
161         line_text = line_text.substr(0, comment_index);
162         if (absl::StripLeadingAsciiWhitespace(line_text).empty()) {
163           continue;
164         }
165       }
166     }
167 
168     size_t line_indent = 0;
169     while (!is_first && absl::ConsumePrefix(&line_text, " ")) {
170       ++line_indent;
171     }
172     is_first = false;
173 
174     format.lines.emplace_back();
175     auto& line = format.lines.back();
176     line.indent =
177         line_indent > raw_string_indent ? line_indent - raw_string_indent : 0;
178 
179     bool is_var = false;
180     size_t total_len = 0;
181     for (absl::string_view chunk :
182          absl::StrSplit(line_text, options_.variable_delimiter)) {
183       // The special _start and _end variables should actually glom the next
184       // chunk into themselves, so as to be of the form _start$foo and _end$foo.
185       if (!line.chunks.empty() && !is_var) {
186         auto& prev = line.chunks.back();
187         if (prev.text == "_start" || prev.text == "_end") {
188           // The +1 below is to account for the $ in between them.
189           // This string is safe, because prev.text and chunk are contiguous
190           // by construction.
191           prev.text = absl::string_view(prev.text.data(),
192                                         prev.text.size() + 1 + chunk.size());
193 
194           // Account for the foo$ part of $_start$foo$.
195           total_len += chunk.size() + 1;
196           continue;
197         }
198       }
199 
200       if (is_var || !chunk.empty()) {
201         line.chunks.push_back(Format::Chunk{chunk, is_var});
202       }
203 
204       total_len += chunk.size();
205       if (is_var) {
206         // This accounts for the $s around a variable.
207         total_len += 2;
208       }
209 
210       is_var = !is_var;
211     }
212 
213     // To ensure there are no unclosed $...$, we check that the computed length
214     // above equals the actual length of the string. If it's off, that means
215     // that there are missing or extra $ characters.
216     Validate(total_len == line_text.size(), options, [&line] {
217       if (line.chunks.empty()) {
218         return std::string("wrong number of variable delimiters");
219       }
220 
221       return absl::StrFormat("unclosed variable name: `%s`",
222                              absl::CHexEscape(line.chunks.back().text));
223     });
224 
225     // Trim any empty, non-variable chunks.
226     while (!line.chunks.empty()) {
227       auto& last = line.chunks.back();
228       if (last.is_var || !last.text.empty()) {
229         break;
230       }
231 
232       line.chunks.pop_back();
233     }
234   }
235 
236   // Discard any trailing newlines (i.e., lines which contain no chunks.)
237   if (format.is_raw_string) {
238     while (!format.lines.empty() && format.lines.back().chunks.empty()) {
239       format.lines.pop_back();
240     }
241   }
242 
243 #if 0  // Use this to aid debugging tokenization.
244   LOG(INFO) << "--- " << format.lines.size() << " lines";
245   for (size_t i = 0; i < format.lines.size(); ++i) {
246     const auto& line = format.lines[i];
247 
248     auto log_line = absl::StrFormat("[\" \" x %d]", line.indent);
249     for (const auto& chunk : line.chunks) {
250       absl::StrAppendFormat(&log_line, " %s\"%s\"", chunk.is_var ? "$" : "",
251                             absl::CHexEscape(chunk.text));
252     }
253     LOG(INFO) << log_line;
254   }
255   LOG(INFO) << "---";
256 #endif
257 
258   return format;
259 }
260 
261 constexpr absl::string_view Printer::kProtocCodegenTrace;
262 
Printer(ZeroCopyOutputStream * output)263 Printer::Printer(ZeroCopyOutputStream* output) : Printer(output, Options{}) {}
264 
Printer(ZeroCopyOutputStream * output,Options options)265 Printer::Printer(ZeroCopyOutputStream* output, Options options)
266     : sink_(output), options_(options) {
267   if (!options_.enable_codegen_trace.has_value()) {
268     // Trace-by-default is threaded through via an env var, rather than a
269     // global, so that child processes can pick it up as well. The flag
270     // --enable_codegen_trace setenv()'s this in protoc's startup code.
271     static const bool kEnableCodegenTrace =
272         ::getenv(kProtocCodegenTrace.data()) != nullptr;
273     options_.enable_codegen_trace = kEnableCodegenTrace;
274   }
275 }
276 
Printer(ZeroCopyOutputStream * output,char variable_delimiter,AnnotationCollector * annotation_collector)277 Printer::Printer(ZeroCopyOutputStream* output, char variable_delimiter,
278                  AnnotationCollector* annotation_collector)
279     : Printer(output, Options{variable_delimiter, annotation_collector}) {}
280 
LookupVar(absl::string_view var)281 absl::string_view Printer::LookupVar(absl::string_view var) {
282   auto result = LookupInFrameStack(var, absl::MakeSpan(var_lookups_));
283   ABSL_CHECK(result.has_value()) << "could not find " << var;
284 
285   auto* view = result->AsString();
286   ABSL_CHECK(view != nullptr)
287       << "could not find " << var << "; found callback instead";
288 
289   return *view;
290 }
291 
Validate(bool cond,Printer::PrintOptions opts,absl::FunctionRef<std::string ()> message)292 bool Printer::Validate(bool cond, Printer::PrintOptions opts,
293                        absl::FunctionRef<std::string()> message) {
294   if (!cond) {
295     if (opts.checks_are_debug_only) {
296       ABSL_DLOG(FATAL) << message();
297     } else {
298       ABSL_LOG(FATAL) << message();
299     }
300   }
301   return cond;
302 }
303 
Validate(bool cond,Printer::PrintOptions opts,absl::string_view message)304 bool Printer::Validate(bool cond, Printer::PrintOptions opts,
305                        absl::string_view message) {
306   return Validate(cond, opts, [=] { return std::string(message); });
307 }
308 
309 // This function is outlined to isolate the use of
310 // ABSL_CHECK into the .cc file.
Outdent()311 void Printer::Outdent() {
312   PrintOptions opts;
313   opts.checks_are_debug_only = true;
314   if (!Validate(indent_ >= options_.spaces_per_indent, opts,
315                 "Outdent() without matching Indent()")) {
316     return;
317   }
318   indent_ -= options_.spaces_per_indent;
319 }
320 
Emit(absl::Span<const Sub> vars,absl::string_view format,SourceLocation loc)321 void Printer::Emit(absl::Span<const Sub> vars, absl::string_view format,
322                    SourceLocation loc) {
323   PrintOptions opts;
324   opts.strip_raw_string_indentation = true;
325   opts.loc = loc;
326 
327   auto defs = WithDefs(vars, /*allow_callbacks=*/true);
328 
329   PrintImpl(format, {}, opts);
330 }
331 
GetSubstitutionRange(absl::string_view varname,PrintOptions opts)332 absl::optional<std::pair<size_t, size_t>> Printer::GetSubstitutionRange(
333     absl::string_view varname, PrintOptions opts) {
334   auto it = substitutions_.find(varname);
335   if (!Validate(it != substitutions_.end(), opts, [varname] {
336         return absl::StrCat("undefined variable in annotation: ", varname);
337       })) {
338     return absl::nullopt;
339   }
340 
341   std::pair<size_t, size_t> range = it->second;
342   if (!Validate(range.first <= range.second, opts, [range, varname] {
343         return absl::StrFormat(
344             "variable used for annotation used multiple times: %s (%d..%d)",
345             varname, range.first, range.second);
346       })) {
347     return absl::nullopt;
348   }
349 
350   return range;
351 }
352 
Annotate(absl::string_view begin_varname,absl::string_view end_varname,absl::string_view file_path,const std::vector<int> & path,absl::optional<AnnotationCollector::Semantic> semantic)353 void Printer::Annotate(absl::string_view begin_varname,
354                        absl::string_view end_varname,
355                        absl::string_view file_path,
356                        const std::vector<int>& path,
357                        absl::optional<AnnotationCollector::Semantic> semantic) {
358   if (options_.annotation_collector == nullptr) {
359     return;
360   }
361 
362   PrintOptions opts;
363   opts.checks_are_debug_only = true;
364   auto begin = GetSubstitutionRange(begin_varname, opts);
365   auto end = GetSubstitutionRange(end_varname, opts);
366   if (!begin.has_value() || !end.has_value()) {
367     return;
368   }
369   if (begin->first > end->second) {
370     ABSL_DLOG(FATAL) << "annotation has negative length from " << begin_varname
371                      << " to " << end_varname;
372     return;
373   }
374   options_.annotation_collector->AddAnnotation(
375       begin->first, end->second, std::string(file_path), path, semantic);
376 }
377 
WriteRaw(const char * data,size_t size)378 void Printer::WriteRaw(const char* data, size_t size) {
379   if (failed_ || size == 0) {
380     return;
381   }
382 
383   if (at_start_of_line_ && data[0] != '\n') {
384     IndentIfAtStart();
385     if (failed_) {
386       return;
387     }
388 
389     // Fix up empty variables (e.g., "{") that should be annotated as
390     // coming after the indent.
391     for (const std::string& var : line_start_variables_) {
392       auto& pair = substitutions_[var];
393       pair.first += indent_;
394       pair.second += indent_;
395     }
396   }
397 
398   // If we're going to write any data, clear line_start_variables_, since
399   // we've either updated them in the block above or they no longer refer to
400   // the current line.
401   line_start_variables_.clear();
402 
403   if (paren_depth_to_omit_.empty()) {
404     sink_.Append(data, size);
405   } else {
406     for (size_t i = 0; i < size; ++i) {
407       char c = data[i];
408       switch (c) {
409         case '(':
410           paren_depth_++;
411           if (!paren_depth_to_omit_.empty() &&
412               paren_depth_to_omit_.back() == paren_depth_) {
413             break;
414           }
415 
416           sink_.Append(&c, 1);
417           break;
418         case ')':
419           if (!paren_depth_to_omit_.empty() &&
420               paren_depth_to_omit_.back() == paren_depth_) {
421             paren_depth_to_omit_.pop_back();
422             paren_depth_--;
423             break;
424           }
425 
426           paren_depth_--;
427           sink_.Append(&c, 1);
428           break;
429         default:
430           sink_.Append(&c, 1);
431           break;
432       }
433     }
434   }
435   failed_ |= sink_.failed();
436 }
437 
IndentIfAtStart()438 void Printer::IndentIfAtStart() {
439   if (!at_start_of_line_) {
440     return;
441   }
442 
443   for (size_t i = 0; i < indent_; ++i) {
444     sink_.Write(" ");
445   }
446   at_start_of_line_ = false;
447 }
448 
PrintCodegenTrace(absl::optional<SourceLocation> loc)449 void Printer::PrintCodegenTrace(absl::optional<SourceLocation> loc) {
450   if (!options_.enable_codegen_trace.value_or(false) || !loc.has_value()) {
451     return;
452   }
453 
454   if (!at_start_of_line_) {
455     at_start_of_line_ = true;
456     line_start_variables_.clear();
457     sink_.Write("\n");
458   }
459 
460   PrintRaw(absl::StrFormat("%s @%s:%d\n", options_.comment_start,
461                            loc->file_name(), loc->line()));
462   at_start_of_line_ = true;
463 }
464 
ValidateIndexLookupInBounds(size_t index,size_t current_arg_index,size_t args_len,PrintOptions opts)465 bool Printer::ValidateIndexLookupInBounds(size_t index,
466                                           size_t current_arg_index,
467                                           size_t args_len, PrintOptions opts) {
468   if (!Validate(index < args_len, opts, [this, index] {
469         return absl::StrFormat("annotation %c{%d%c is out of bounds",
470                                options_.variable_delimiter, index + 1,
471                                options_.variable_delimiter);
472       })) {
473     return false;
474   }
475   if (!Validate(
476           index <= current_arg_index, opts, [this, index, current_arg_index] {
477             return absl::StrFormat(
478                 "annotation arg must be in correct order as given; expected "
479                 "%c{%d%c but got %c{%d%c",
480                 options_.variable_delimiter, current_arg_index + 1,
481                 options_.variable_delimiter, options_.variable_delimiter,
482                 index + 1, options_.variable_delimiter);
483           })) {
484     return false;
485   }
486   return true;
487 }
488 
PrintImpl(absl::string_view format,absl::Span<const std::string> args,PrintOptions opts)489 void Printer::PrintImpl(absl::string_view format,
490                         absl::Span<const std::string> args, PrintOptions opts) {
491   // Inside of this function, we set indentation as we print new lines from
492   // the format string. No matter how we exit this function, we should fix up
493   // the indent to what it was before we entered; a cleanup makes it easy to
494   // avoid this mistake.
495   size_t original_indent = indent_;
496   auto unindent =
497       absl::MakeCleanup([this, original_indent] { indent_ = original_indent; });
498 
499   absl::string_view original = format;
500 
501   line_start_variables_.clear();
502 
503   if (opts.use_substitution_map) {
504     substitutions_.clear();
505   }
506 
507   auto fmt = TokenizeFormat(format, opts);
508   PrintCodegenTrace(opts.loc);
509 
510   size_t arg_index = 0;
511   bool skip_next_newline = false;
512   std::vector<AnnotationCollector::Annotation> annot_stack;
513   std::vector<std::pair<absl::string_view, size_t>> annot_records;
514   for (size_t line_idx = 0; line_idx < fmt.lines.size(); ++line_idx) {
515     const auto& line = fmt.lines[line_idx];
516 
517     // We only print a newline for lines that follow the first; a loop iteration
518     // can also hint that we should not emit another newline through the
519     // `skip_next_newline` variable.
520     //
521     // We also assume that double newlines are undesirable, so we
522     // do not emit a newline if we are at the beginning of a line, *unless* the
523     // previous format line is actually empty. This behavior is specific to
524     // raw strings.
525     if (line_idx > 0) {
526       bool prev_was_empty = fmt.lines[line_idx - 1].chunks.empty();
527       bool should_skip_newline =
528           skip_next_newline ||
529           (fmt.is_raw_string && (at_start_of_line_ && !prev_was_empty));
530       if (!should_skip_newline) {
531         line_start_variables_.clear();
532         sink_.Write("\n");
533         at_start_of_line_ = true;
534       }
535     }
536     skip_next_newline = false;
537 
538     indent_ = original_indent + line.indent;
539 
540     for (size_t chunk_idx = 0; chunk_idx < line.chunks.size(); ++chunk_idx) {
541       auto chunk = line.chunks[chunk_idx];
542 
543       if (!chunk.is_var) {
544         PrintRaw(chunk.text);
545         continue;
546       }
547 
548       if (chunk.text.empty()) {
549         // `$$` is an escape for just `$`.
550         WriteRaw(&options_.variable_delimiter, 1);
551         continue;
552       }
553 
554       // If we get this far, we can conclude the chunk is a substitution
555       // variable; we rename the `chunk` variable to make this clear below.
556       absl::string_view var = chunk.text;
557       if (substitution_listener_ != nullptr) {
558         substitution_listener_(var, opts.loc.value_or(SourceLocation()));
559       }
560       if (opts.use_curly_brace_substitutions &&
561           absl::ConsumePrefix(&var, "{")) {
562         if (!Validate(var.size() == 1u, opts,
563                       "expected single-digit variable")) {
564           continue;
565         }
566 
567         if (!Validate(absl::ascii_isdigit(var[0]), opts,
568                       "expected digit after {")) {
569           continue;
570         }
571 
572         size_t idx = var[0] - '1';
573         if (!ValidateIndexLookupInBounds(idx, arg_index, args.size(), opts)) {
574           continue;
575         }
576 
577         if (idx == arg_index) {
578           ++arg_index;
579         }
580 
581         IndentIfAtStart();
582         annot_stack.push_back({{sink_.bytes_written(), 0}, args[idx]});
583         continue;
584       }
585 
586       if (opts.use_curly_brace_substitutions &&
587           absl::ConsumePrefix(&var, "}")) {
588         // The rest of var is actually ignored, and this is apparently
589         // public API now. Oops?
590         if (!Validate(!annot_stack.empty(), opts,
591                       "unexpected end of annotation")) {
592           continue;
593         }
594 
595         annot_stack.back().first.second = sink_.bytes_written();
596         if (options_.annotation_collector != nullptr) {
597           options_.annotation_collector->AddAnnotationNew(annot_stack.back());
598         }
599         annot_stack.pop_back();
600         continue;
601       }
602 
603       absl::string_view prefix, suffix;
604       if (opts.strip_spaces_around_vars) {
605         var = absl::StripLeadingAsciiWhitespace(var);
606         prefix = chunk.text.substr(0, chunk.text.size() - var.size());
607         var = absl::StripTrailingAsciiWhitespace(var);
608         suffix = chunk.text.substr(prefix.size() + var.size());
609       }
610 
611       if (!Validate(!var.empty(), opts, "unexpected empty variable")) {
612         continue;
613       }
614 
615       bool is_start = absl::ConsumePrefix(&var, "_start$");
616       bool is_end = absl::ConsumePrefix(&var, "_end$");
617       if (opts.use_annotation_frames && (is_start || is_end)) {
618         if (is_start) {
619           IndentIfAtStart();
620           annot_records.push_back({var, sink_.bytes_written()});
621 
622           // Skip all whitespace immediately after a _start.
623           ++chunk_idx;
624           if (chunk_idx < line.chunks.size()) {
625             absl::string_view text = line.chunks[chunk_idx].text;
626             while (absl::ConsumePrefix(&text, " ")) {
627             }
628             PrintRaw(text);
629           }
630         } else {
631           // If a line consisted *only* of an _end, this will likely result in
632           // a blank line if we do not zap the newline after it, so we do that
633           // here.
634           if (line.chunks.size() == 1) {
635             skip_next_newline = true;
636           }
637 
638           auto record_var = annot_records.back();
639           annot_records.pop_back();
640 
641           if (!Validate(record_var.first == var, opts, [record_var, var] {
642                 return absl::StrFormat(
643                     "_start and _end variables must match, but got %s and %s, "
644                     "respectively",
645                     record_var.first, var);
646               })) {
647             continue;
648           }
649 
650           absl::optional<AnnotationRecord> record =
651               LookupInFrameStack(var, absl::MakeSpan(annotation_lookups_));
652 
653           if (!Validate(record.has_value(), opts, [var] {
654                 return absl::StrCat("undefined annotation variable: \"",
655                                     absl::CHexEscape(var), "\"");
656               })) {
657             continue;
658           }
659 
660           if (options_.annotation_collector != nullptr) {
661             options_.annotation_collector->AddAnnotation(
662                 record_var.second, sink_.bytes_written(), record->file_path,
663                 record->path, record->semantic);
664           }
665         }
666 
667         continue;
668       }
669 
670       absl::optional<ValueView> sub;
671       absl::optional<AnnotationRecord> same_name_record;
672       if (opts.allow_digit_substitutions && absl::ascii_isdigit(var[0])) {
673         if (!Validate(var.size() == 1u, opts,
674                       "expected single-digit variable")) {
675           continue;
676         }
677 
678         size_t idx = var[0] - '1';
679         if (!ValidateIndexLookupInBounds(idx, arg_index, args.size(), opts)) {
680           continue;
681         }
682         if (idx == arg_index) {
683           ++arg_index;
684         }
685         sub = args[idx];
686       } else {
687         sub = LookupInFrameStack(var, absl::MakeSpan(var_lookups_));
688 
689         if (opts.use_annotation_frames) {
690           same_name_record =
691               LookupInFrameStack(var, absl::MakeSpan(annotation_lookups_));
692         }
693       }
694 
695       // By returning here in case of empty we also skip possible spaces inside
696       // the $...$, i.e. "void$ dllexpor$ f();" -> "void f();" in the empty
697       // case.
698       if (!Validate(sub.has_value(), opts, [var] {
699             return absl::StrCat("undefined variable: \"", absl::CHexEscape(var),
700                                 "\"");
701           })) {
702         continue;
703       }
704 
705       size_t range_start = sink_.bytes_written();
706       size_t range_end = sink_.bytes_written();
707 
708       if (const absl::string_view* str = sub->AsString()) {
709         if (at_start_of_line_ && str->empty()) {
710           line_start_variables_.emplace_back(var);
711         }
712 
713         if (!str->empty()) {
714           // If `sub` is empty, we do not print the spaces around it.
715           PrintRaw(prefix);
716           PrintRaw(*str);
717           range_end = sink_.bytes_written();
718           range_start = range_end - str->size();
719           PrintRaw(suffix);
720         }
721       } else {
722         const ValueView::Callback* fnc = sub->AsCallback();
723         ABSL_CHECK(fnc != nullptr);
724 
725         Validate(
726             prefix.empty() && suffix.empty(), opts,
727             "substitution that resolves to callback cannot contain whitespace");
728 
729         range_start = sink_.bytes_written();
730         ABSL_CHECK((*fnc)())
731             << "recursive call encountered while evaluating \"" << var << "\"";
732         range_end = sink_.bytes_written();
733       }
734 
735       if (range_start == range_end && sub->consume_parens_if_empty) {
736         paren_depth_to_omit_.push_back(paren_depth_ + 1);
737       }
738 
739       // If we just evaluated a value which specifies end-of-line consume-after
740       // characters, and we're at the start of a line, that means we finished
741       // with a newline.
742       //
743       // We trim a single end-of-line `consume_after` character in this case.
744       //
745       // This helps callback formatting "work as expected" with respect to forms
746       // like
747       //
748       //   class Foo {
749       //     $methods$;
750       //   };
751       //
752       // Without this post-processing, it would turn into
753       //
754       //   class Foo {
755       //     void Bar() {};
756       //   };
757       //
758       // in many cases. Without the `;`, clang-format may format the template
759       // incorrectly.
760       auto next_idx = chunk_idx + 1;
761       if (!sub->consume_after.empty() && next_idx < line.chunks.size() &&
762           !line.chunks[next_idx].is_var) {
763         chunk_idx = next_idx;
764 
765         absl::string_view text = line.chunks[chunk_idx].text;
766         for (char c : sub->consume_after) {
767           if (absl::ConsumePrefix(&text, absl::string_view(&c, 1))) {
768             break;
769           }
770         }
771 
772         PrintRaw(text);
773       }
774 
775       if (same_name_record.has_value() &&
776           options_.annotation_collector != nullptr) {
777         options_.annotation_collector->AddAnnotation(
778             range_start, range_end, same_name_record->file_path,
779             same_name_record->path, same_name_record->semantic);
780       }
781 
782       if (opts.use_substitution_map) {
783         auto insertion =
784             substitutions_.emplace(var, std::make_pair(range_start, range_end));
785 
786         if (!insertion.second) {
787           // This variable was used multiple times.
788           // Make its span have negative length so
789           // we can detect it if it gets used in an
790           // annotation.
791           insertion.first->second = {1, 0};
792         }
793       }
794     }
795   }
796 
797   Validate(arg_index == args.size(), opts,
798            [original] { return absl::StrCat("unused args: ", original); });
799   Validate(annot_stack.empty(), opts, [this, original] {
800     return absl::StrFormat(
801         "annotation range was not closed; expected %c}%c: %s",
802         options_.variable_delimiter, options_.variable_delimiter, original);
803   });
804 
805   // For multiline raw strings, we always make sure to end on a newline.
806   if (fmt.is_raw_string && !at_start_of_line_) {
807     PrintRaw("\n");
808     at_start_of_line_ = true;
809   }
810 }
811 }  // namespace io
812 }  // namespace protobuf
813 }  // namespace google
814