mimic++ v10.0.1
Loading...
Searching...
No Matches
PrintVisitor.hpp
Go to the documentation of this file.
1// Copyright Dominic (DNKpp) Koepke 2026.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at
4// https://www.boost.org/LICENSE_1_0.txt)
5
6#ifndef MIMICPP_PRINTING_TYPE_PRINT_VISITOR_HPP
7#define MIMICPP_PRINTING_TYPE_PRINT_VISITOR_HPP
8
9#pragma once
10
16
17#ifndef MIMICPP_DETAIL_IS_MODULE
18 #include <algorithm>
19 #include <variant>
20#endif
21
23{
24 template <print_iterator OutIter>
26 {
27 public:
28 [[nodiscard]]
29 explicit constexpr PrintVisitor(OutIter out)
30 : m_OutIter{std::move(out)}
31 {
32 }
33
34 [[nodiscard]]
35 constexpr OutIter out() const
36 {
37 return m_OutIter;
38 }
39
40 constexpr void visit(state::TypeId const& typeId)
41 {
42 std::visit(
43 [&](auto& base) { visit(base); },
44 typeId.base);
45 visit(typeId.qualifications);
46 visit(typeId.declarator.root);
47 }
48
49 constexpr void visit(state::FunctionId const& functionId)
50 {
51 if (functionId.returnType)
52 {
53 visit(*functionId.returnType);
54 m_OutIter = format::format_to(std::move(m_OutIter), " ");
55 }
56
57 visit(functionId.identifier);
58 }
59
60 private:
61 OutIter m_OutIter;
62 int m_NestedDepth{0};
63
64 template <typename State>
65 constexpr void visit(state::Recursive<State> const& state)
66 {
67 visit(*state);
68 }
69
70 constexpr void visit(state::BuiltinType::SignedSpec const spec)
71 {
72 switch (spec)
73 {
75 case Spec::id_signed:
76 m_OutIter = format::format_to(std::move(m_OutIter), "signed");
77 break;
78 case Spec::id_unsigned:
79 m_OutIter = format::format_to(std::move(m_OutIter), "unsigned");
80 break;
81
82 // GCOVR_EXCL_START
83 default:
85 // GCOVR_EXCL_STOP
86 }
87 }
88
89 constexpr void visit(state::BuiltinType::SizeSpec const spec)
90 {
91 switch (spec)
92 {
94 case Spec::id_short:
95 m_OutIter = format::format_to(std::move(m_OutIter), "short");
96 break;
97 case Spec::id_long:
98 m_OutIter = format::format_to(std::move(m_OutIter), "long");
99 break;
100 case Spec::id_longlong:
101 m_OutIter = format::format_to(std::move(m_OutIter), "long long");
102 break;
103
104 // GCOVR_EXCL_START
105 default:
107 // GCOVR_EXCL_STOP
108 }
109 }
110
111 constexpr void visit(state::BuiltinType const& type)
112 {
113 bool first = true;
114
115 if (type.signedSpec)
116 {
117 visit(*type.signedSpec);
118 first = false;
119 }
120
121 if (type.sizeSpec)
122 {
123 if (!std::exchange(first, false))
124 {
125 m_OutIter = format::format_to(std::move(m_OutIter), " ");
126 }
127
128 visit(*type.sizeSpec);
129 }
130
131 if (type.base
132 // Do not print redundant `int` when a size-spec is applied.
133 && (!type.sizeSpec || lexing::keyword{"int"} != *type.base))
134 {
135 if (!std::exchange(first, false))
136 {
137 m_OutIter = format::format_to(std::move(m_OutIter), " ");
138 }
139
140 m_OutIter = format::format_to(std::move(m_OutIter), "{}", type.base->text());
141 }
142 }
143
144 constexpr void visit(state::RefQualifier const qualifier)
145 {
146 switch (qualifier)
147 {
148 using Qualifier = state::RefQualifier;
149 case Qualifier::id_ref:
150 m_OutIter = format::format_to(std::move(m_OutIter), "&");
151 break;
152 case Qualifier::id_refref:
153 m_OutIter = format::format_to(std::move(m_OutIter), "&&");
154 break;
155
156 // GCOVR_EXCL_START
157 default:
159 // GCOVR_EXCL_STOP
160 }
161 }
162
163 constexpr void visit(state::CVQualifierSeq const qualifiers)
164 {
165 if (qualifiers.isConst)
166 {
167 m_OutIter = format::format_to(m_OutIter, " const");
168 }
169
170 if (qualifiers.isVolatile)
171 {
172 m_OutIter = format::format_to(m_OutIter, " volatile");
173 }
174 }
175
176 constexpr void visit(state::ConstantExpression const& expression)
177 {
178 m_OutIter = format::format_to(m_OutIter, "{}", expression.content);
179 }
180
181 static constexpr std::array syntheticAliases = std::to_array<std::pair<std::string_view, std::string_view>>({
182 {"{anonymous}", "{anon-ns}" },
183 {"(anonymous namespace)", "{anon-ns}" },
184
185 {"`anonymous-namespace'", "{anon-ns}" }, // msvc
186 {"`anonymous namespace'", "{anon-ns}" }, // msvc
187 {"<unnamed-tag>", "<unnamed tag>"}, // msvc
188 });
189
190 constexpr void visit(state::Identifier const& id)
191 {
192 if (id.isSynthetic)
193 {
194 if (auto const iter = std::ranges::find(syntheticAliases, id.content, [](auto const& e) { return e.first; });
195 iter != syntheticAliases.cend())
196 {
197 m_OutIter = format::format_to(std::move(m_OutIter), "{}", iter->second);
198 return;
199 }
200
201 // These synthetic ids will be generated on clang:
202 if (constexpr std::string_view prefix{"(unnamed "}, suffix{")"};
203 id.content.starts_with(prefix)
204 && id.content.ends_with(suffix))
205 {
206 std::string_view classType = id.content;
207 classType.remove_prefix(prefix.size());
208 classType.remove_suffix(suffix.size());
209 classType = classType.substr(0, classType.find(" at "));
210
211 m_OutIter = format::format_to(std::move(m_OutIter), "<unnamed {}>", classType);
212 return;
213 }
214
215 // These synthetic ids will be generated on clang as well.
216 if (constexpr std::string_view prefix{"(anonymous "}, suffix{")"};
217 id.content.starts_with(prefix)
218 && id.content.ends_with(suffix))
219 {
220 std::string_view classType = id.content;
221 classType.remove_prefix(prefix.size());
222 classType.remove_suffix(suffix.size());
223 classType = classType.substr(0, classType.find(" at "));
224
225 m_OutIter = format::format_to(std::move(m_OutIter), "<unnamed {}>", classType);
226 return;
227 }
228
229 // These synthetic ids will be generated on msvc.
230 if (constexpr std::string_view prefix{"<unnamed-type-anon_"}, suffix{">"};
231 id.content.starts_with(prefix)
232 && id.content.ends_with(suffix))
233 {
234 std::string_view classType = id.content;
235 classType.remove_prefix(prefix.size());
236 classType.remove_suffix(suffix.size());
237
238 m_OutIter = format::format_to(std::move(m_OutIter), "<unnamed {}>", classType);
239 return;
240 }
241 }
242
243 m_OutIter = format::format_to(std::move(m_OutIter), "{}", id.content);
244 }
245
246 constexpr void visit(state::PointerDeclarator const& declarator)
247 {
248 if (declarator.scopes)
249 {
250 visit(*declarator.scopes);
251 }
252
253 m_OutIter = format::format_to(std::move(m_OutIter), "*");
254
255 if (declarator.qualifiers)
256 {
257 visit(*declarator.qualifiers);
258 }
259 }
260
261 constexpr void visit(state::ReferenceDeclarator const& declarator)
262 {
263 visit(declarator.qualifier);
264 }
265
266 constexpr void visit(state::ArrayDeclarator const& declarator)
267 {
268 m_OutIter = format::format_to(std::move(m_OutIter), "[");
269
270 if (declarator.size)
271 {
272 visit(*declarator.size);
273 }
274
275 m_OutIter = format::format_to(std::move(m_OutIter), "]");
276 }
277
278 MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES void visit(state::FunctionDeclarator const& declarator)
279 {
280 if (0 < m_NestedDepth)
281 {
282 return;
283 }
284
285 m_OutIter = format::format_to(std::move(m_OutIter), "(");
286
287 // Sometimes params just contain a single (`ret (void)`), which will be suppressed.
288 state::TypeId const voidTypeId{.base = state::BuiltinType{.base = lexing::keyword{"void"}}};
289 if (1u != declarator.params.size()
290 || voidTypeId != declarator.params.front())
291 {
292 ++m_NestedDepth;
293 join(
294 declarator.params,
295 ", ",
296 "",
297 [&](auto const& arg) { visit(arg); });
298 --m_NestedDepth;
299 }
300
301 m_OutIter = format::format_to(std::move(m_OutIter), ")");
302
303 visit(declarator.qualifiers);
304 if (declarator.refQualifier)
305 {
306 visit(*declarator.refQualifier);
307 }
308
309 if (declarator.isNoexcept)
310 {
311 m_OutIter = format::format_to(std::move(m_OutIter), " noexcept");
312 }
313 }
314
315 constexpr void visit(state::TemplateArgumentList const& args)
316 {
317 if (0 < m_NestedDepth)
318 {
319 return;
320 }
321
322 m_OutIter = format::format_to(std::move(m_OutIter), "<");
323
324 ++m_NestedDepth;
325 join(
326 args,
327 ", ",
328 "",
329 [this](auto const& arg) {
330 std::visit(
331 [&](auto const& inner) { visit(inner); },
332 arg);
333 });
334 --m_NestedDepth;
335
336 m_OutIter = format::format_to(std::move(m_OutIter), ">");
337 }
338
339 constexpr void visit(state::OperatorFunctionId const& id)
340 {
341 m_OutIter = format::format_to(std::move(m_OutIter), "operator");
342 std::visit(
344 [this](lexing::operator_or_punctuator const& op) {
345 m_OutIter = format::format_to(std::move(m_OutIter), "{}", op.text());
346 },
347 [this](std::pair<lexing::keyword const, bool> const& op) {
348 m_OutIter = format::format_to(std::move(m_OutIter), " {}", op.first.text());
349 if (op.second)
350 {
351 m_OutIter = format::format_to(std::move(m_OutIter), "[]");
352 }
353 },
354 [this](std::span<lexing::operator_or_punctuator const, 2u> const op) {
355 m_OutIter = format::format_to(
356 std::move(m_OutIter),
357 "{}{}",
358 op.front().text(),
359 op.back().text());
360 }},
361 id.symbol);
362 }
363
364 constexpr void visit(state::ConversionFunctionId const& id)
365 {
366 m_OutIter = format::format_to(std::move(m_OutIter), "operator ");
367 visit(id.target);
368 }
369
370 constexpr void visit(state::DestructorFunctionId const& id)
371 {
372 m_OutIter = format::format_to(std::move(m_OutIter), "~{}", id.name.content);
373 }
374
375 constexpr void visit(state::LambdaFunctionId const& id)
376 {
377 m_OutIter = format::format_to(std::move(m_OutIter), "<lambda>");
378
379 if (id.isMutable)
380 {
381 m_OutIter = format::format_to(std::move(m_OutIter), " mutable");
382 }
383 }
384
385 constexpr void visit(state::UnqualifiedId const& nested)
386 {
387 std::visit(
388 [&](auto const& inner) { visit(inner); },
389 nested.name);
390
391 if (nested.templateArgs)
392 {
393 visit(*nested.templateArgs);
394 }
395
396 if (nested.functionDeclarator)
397 {
398 visit(*nested.functionDeclarator);
399 }
400 }
401
402 constexpr void visit(std::span<state::UnqualifiedId const> scopes)
403 {
404 while (!scopes.empty())
405 {
406 auto const& current = scopes.front();
407 scopes = scopes.subspan(1u);
408
409 if (auto const* const identifier = std::get_if<state::Identifier>(&current.name);
410 identifier
411 && "__cxx11" == identifier->content)
412 {
413 continue;
414 }
415
416 visit(current);
417 m_OutIter = format::format_to(std::move(m_OutIter), "::");
418
419 // msvc appends an additional `()::` scope to lambdas, which the parser correctly identifies as `operator()::`.
420 // However, this is unnecessary noise, which I'd like to omit.
421 if (std::get_if<state::LambdaFunctionId>(&current.name)
422 && !scopes.empty())
423 {
424 if (auto const* const next = std::get_if<state::OperatorFunctionId>(&scopes.front().name);
425 next
426 && next->is_call())
427 {
428 scopes = scopes.subspan(1u);
429 }
430 }
431 // msvc does not emit `~id` for destructors, but a synthetic `{dtor}` identifier instead.
432 // So, let's rebuild the actual destructor identifier with the help of the previous identifier..
433 else if (auto const* const currentId = std::get_if<state::Identifier>(&current.name);
434 currentId
435 && !scopes.empty())
436 {
437 if (auto const* const next = std::get_if<state::Identifier>(&scopes.front().name);
438 next
439 && "{dtor}" == next->content)
440 {
441 visit(state::DestructorFunctionId{.name = *currentId});
442 m_OutIter = format::format_to(std::move(m_OutIter), "::");
443 scopes = scopes.subspan(1u);
444 }
445 }
446 }
447 }
448
449 constexpr void visit(state::ScopeSequence const& sequence)
450 {
451 if (sequence.explicitRoot)
452 {
453 m_OutIter = format::format_to(std::move(m_OutIter), "::");
454 }
455
456 ++m_NestedDepth;
457 visit(sequence.scopes);
458 --m_NestedDepth;
459 }
460
461 constexpr void visit(state::QualifiedId const& id)
462 {
463 visit(id.scopes);
464 visit(id.identifier);
465 }
466
467 constexpr void visit(state::AbstractDeclarator::Layer const& declarator)
468 {
469 for (auto const& decoration : declarator.decorations)
470 {
471 std::visit(
472 [&](auto const& inner) { visit(inner); },
473 decoration);
474 }
475
476 if (declarator.nested)
477 {
478 m_OutIter = format::format_to(std::move(m_OutIter), "(");
479 visit(*declarator.nested);
480 m_OutIter = format::format_to(std::move(m_OutIter), ")");
481 }
482
483 if (declarator.function)
484 {
485 visit(*declarator.function);
486 }
487
488 std::ranges::for_each(
489 declarator.arrays,
490 [&](auto const& arr) { visit(arr); });
491 }
492
493 template <std::ranges::forward_range Range>
494 constexpr void join(
495 Range&& range,
496 std::string_view const separator,
497 std::string_view const terminator,
498 std::invocable<std::ranges::range_reference_t<Range>> auto action)
499 {
500 if (std::ranges::subrange elements{range})
501 {
502 std::invoke(action, elements.front());
503 for (auto const& element : elements.advance(1u))
504 {
505 m_OutIter = format::format_to(std::move(m_OutIter), "{}", separator);
506 std::invoke(action, element);
507 }
508
509 m_OutIter = format::format_to(std::move(m_OutIter), "{}", terminator);
510 }
511 }
512 };
513}
514
515#endif
#define MIMICPP_DETAIL_CONSTEXPR_PRETTY_TYPES
Definition Config.hpp:93
constexpr OutIter out() const
Definition PrintVisitor.hpp:35
constexpr void visit(state::FunctionId const &functionId)
Definition PrintVisitor.hpp:49
constexpr void visit(state::TypeId const &typeId)
Definition PrintVisitor.hpp:40
constexpr PrintVisitor(OutIter out)
Definition PrintVisitor.hpp:29
constexpr auto arg(Matcher &&matcher, Projection &&projection={}) noexcept(std::is_nothrow_constructible_v< std::remove_cvref_t< Matcher >, Matcher > &&std::is_nothrow_constructible_v< std::remove_cvref_t< Projection >, Projection >)
Checks whether the selected argument satisfies the given matcher.
Definition RequirementPolicies.hpp:233
constexpr TypeMatcher< T > type
Matcher, which can be used as a last resort to disambiguate similar overloads.
Definition GeneralMatchers.hpp:480
constexpr detail::current_fn current
Function object, which generates the current-stacktrace.
Definition Stacktrace.hpp:473
OutIter format_to(OutIter out, format_string< Args... > const fmt, Args &&... args)
Definition Format.hpp:175
lexing::literal ConstantExpression
Definition ParserState.hpp:123
std::vector< TemplateArgument > TemplateArgumentList
Definition ParserState.hpp:149
RefQualifier
Definition ParserState.hpp:118
Definition Parser.hpp:27
Overloaded(Ts...) -> Overloaded< Ts... >
void unreachable()
Invokes undefined behavior.
Definition C++23Backports.hpp:40
QualifiedId identifier
Definition ParserState.hpp:430
std::optional< TypeId > returnType
Definition ParserState.hpp:428
CVQualifierSeq qualifications
Definition ParserState.hpp:418
AbstractDeclarator declarator
Definition ParserState.hpp:420
BaseType base
Definition ParserState.hpp:419