1414
1515#include " common/ast/metadata.h"
1616
17+ #include < cstddef>
1718#include < memory>
19+ #include < string>
20+ #include < utility>
21+ #include < variant>
1822#include < vector>
1923
2024#include " absl/base/no_destructor.h"
25+ #include " absl/base/nullability.h"
2126#include " absl/functional/overload.h"
27+ #include " absl/log/absl_check.h"
28+ #include " absl/strings/str_cat.h"
2229#include " absl/types/variant.h"
2330
2431namespace cel {
@@ -30,6 +37,96 @@ const TypeSpec& DefaultTypeSpec() {
3037 return *type;
3138}
3239
40+ std::string FormatPrimitive (PrimitiveType t) {
41+ switch (t) {
42+ case PrimitiveType::kBool :
43+ return " bool" ;
44+ case PrimitiveType::kInt64 :
45+ return " int" ;
46+ case PrimitiveType::kUint64 :
47+ return " uint" ;
48+ case PrimitiveType::kDouble :
49+ return " double" ;
50+ case PrimitiveType::kString :
51+ return " string" ;
52+ case PrimitiveType::kBytes :
53+ return " bytes" ;
54+ default :
55+ return " *unspecified primitive*" ;
56+ }
57+ }
58+
59+ std::string FormatWellKnown (WellKnownTypeSpec t) {
60+ switch (t) {
61+ case WellKnownTypeSpec::kAny :
62+ return " google.protobuf.Any" ;
63+ case WellKnownTypeSpec::kDuration :
64+ return " google.protobuf.Duration" ;
65+ case WellKnownTypeSpec::kTimestamp :
66+ return " google.protobuf.Timestamp" ;
67+ default :
68+ return " *unspecified well known*" ;
69+ }
70+ }
71+
72+ using FormatIns = std::variant<const TypeSpec* absl_nonnull, std::string>;
73+ using FormatStack = std::vector<FormatIns>;
74+
75+ void HandleFormatTypeSpec (const TypeSpec& t, FormatStack& stack,
76+ std::string* out) {
77+ if (t.has_dyn ()) {
78+ absl::StrAppend (out, " dyn" );
79+ } else if (t.has_null ()) {
80+ absl::StrAppend (out, " null" );
81+ } else if (t.has_primitive ()) {
82+ absl::StrAppend (out, FormatPrimitive (t.primitive ()));
83+ } else if (t.has_wrapper ()) {
84+ absl::StrAppend (out, " wrapper(" , FormatPrimitive (t.wrapper ()), " )" );
85+ } else if (t.has_well_known ()) {
86+ absl::StrAppend (out, FormatWellKnown (t.well_known ()));
87+ return ;
88+ } else if (t.has_abstract_type ()) {
89+ const auto & abs_type = t.abstract_type ();
90+ if (abs_type.parameter_types ().empty ()) {
91+ absl::StrAppend (out, abs_type.name ());
92+ return ;
93+ }
94+ absl::StrAppend (out, abs_type.name (), " (" );
95+ stack.push_back (" )" );
96+ for (size_t i = abs_type.parameter_types ().size (); i > 0 ; --i) {
97+ stack.push_back (&abs_type.parameter_types ()[i - 1 ]);
98+ if (i > 1 ) {
99+ stack.push_back (" , " );
100+ }
101+ }
102+
103+ } else if (t.has_type ()) {
104+ if (t.type () == TypeSpec ()) {
105+ absl::StrAppend (out, " type" );
106+ return ;
107+ }
108+ absl::StrAppend (out, " type(" );
109+ stack.push_back (" )" );
110+ stack.push_back (&t.type ());
111+ } else if (t.has_message_type ()) {
112+ absl::StrAppend (out, t.message_type ().type ());
113+ } else if (t.has_type_param ()) {
114+ absl::StrAppend (out, t.type_param ().type ());
115+ } else if (t.has_list_type ()) {
116+ absl::StrAppend (out, " list(" );
117+ stack.push_back (" )" );
118+ stack.push_back (&t.list_type ().elem_type ());
119+ } else if (t.has_map_type ()) {
120+ absl::StrAppend (out, " map(" );
121+ stack.push_back (" )" );
122+ stack.push_back (&t.map_type ().value_type ());
123+ stack.push_back (" , " );
124+ stack.push_back (&t.map_type ().key_type ());
125+ } else {
126+ absl::StrAppend (out, " *error*" );
127+ }
128+ }
129+
33130TypeSpecKind CopyImpl (const TypeSpecKind& other) {
34131 return absl::visit (
35132 absl::Overload (
@@ -142,4 +239,24 @@ FunctionTypeSpec& FunctionTypeSpec::operator=(const FunctionTypeSpec& other) {
142239 return *this ;
143240}
144241
242+ std::string FormatTypeSpec (const TypeSpec& t) {
243+ // Use a stack to avoid recursion.
244+ // Probably overly defensive, but fuzzers will often notice the recursion
245+ // and try to trigger it.
246+ std::string out;
247+ FormatStack seq;
248+ seq.push_back (&t);
249+ while (!seq.empty ()) {
250+ FormatIns ins = std::move (seq.back ());
251+ seq.pop_back ();
252+ if (std::holds_alternative<std::string>(ins)) {
253+ absl::StrAppend (&out, std::get<std::string>(ins));
254+ continue ;
255+ }
256+ ABSL_DCHECK (std::holds_alternative<const TypeSpec*>(ins));
257+ HandleFormatTypeSpec (*std::get<const TypeSpec*>(ins), seq, &out);
258+ }
259+ return out;
260+ }
261+
145262} // namespace cel
0 commit comments