• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DNKpp / mimicpp / 18446180060

12 Oct 2025 03:53PM UTC coverage: 97.974% (-0.1%) from 98.107%
18446180060

Pull #134

github

web-flow
Merge 946130c91 into 798c20ab0
Pull Request #134: Feature: New set of facade-macros

29 of 33 new or added lines in 2 files covered. (87.88%)

1 existing line in 1 file now uncovered.

2466 of 2517 relevant lines covered (97.97%)

12639.09 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

99.53
/include/mimic++/printing/type/NameParser.hpp
1
//          Copyright Dominic (DNKpp) Koepke 2024 - 2025.
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_NAME_PARSER_HPP
7
#define MIMICPP_PRINTING_TYPE_NAME_PARSER_HPP
8

9
#pragma once
10

11
#include "mimic++/Fwd.hpp"
12
#include "mimic++/config/Config.hpp"
13
#include "mimic++/printing/type/NameLexer.hpp"
14
#include "mimic++/printing/type/NameParserReductions.hpp"
15
#include "mimic++/printing/type/NameParserTokens.hpp"
16
#include "mimic++/utilities/C++23Backports.hpp"
17

18
#ifndef MIMICPP_DETAIL_IS_MODULE
19
    #include <array>
20
    #include <functional>
21
    #include <iterator>
22
    #include <type_traits>
23
    #include <variant>
24
#endif
25

26
namespace mimicpp::printing::type::parsing
27
{
28
    template <parser_visitor Visitor>
29
    class NameParser
30
    {
31
    public:
32
        [[nodiscard]]
33
        explicit constexpr NameParser(Visitor visitor, StringViewT const& content) noexcept(std::is_nothrow_move_constructible_v<Visitor>)
34
            : m_Visitor{std::move(visitor)},
6,308✔
35
              m_Content{content},
6,308✔
36
              m_Lexer{content}
6,308✔
37
        {
38
        }
6,308✔
39

40
        constexpr void parse_type()
41
        {
42
            parse();
3,209✔
43
            token::try_reduce_as_type(m_TokenStack);
3,209✔
44
            if (!finalize<token::Type>())
3,209✔
45
            {
46
                emit_unrecognized();
2✔
47
            }
48
        }
3,209✔
49

50
        constexpr void parse_function()
51
        {
52
            parse();
2,340✔
53

54
            if (m_HasConversionOperator)
2,340✔
55
            {
56
                token::reduce_as_conversion_operator_function_identifier(m_TokenStack);
8✔
57
            }
58
            else
59
            {
60
                is_suffix_of<token::FunctionIdentifier>(m_TokenStack)
4,664✔
61
                    || token::try_reduce_as_function_identifier(m_TokenStack);
2,332✔
62
            }
63

64
            token::try_reduce_as_function(m_TokenStack);
2,340✔
65
            if (!finalize<token::Function>())
2,340✔
66
            {
67
                // Well, this is a workaround to circumvent issues with lambdas on some environments.
68
                // gcc produces lambdas in form `<lambda()>` which are not recognized as actual functions.
69
                token::try_reduce_as_type(m_TokenStack);
322✔
70
                if (!finalize<token::Type>())
322✔
71
                {
72
                    emit_unrecognized();
13✔
73
                }
74
            }
75
        }
2,340✔
76

77
    private:
78
        static constexpr lexing::operator_or_punctuator openingParens{"("};
79
        static constexpr lexing::operator_or_punctuator closingParens{")"};
80
        static constexpr lexing::operator_or_punctuator openingAngle{"<"};
81
        static constexpr lexing::operator_or_punctuator closingAngle{">"};
82
        static constexpr lexing::operator_or_punctuator openingCurly{"{"};
83
        static constexpr lexing::operator_or_punctuator closingCurly{"}"};
84
        static constexpr lexing::operator_or_punctuator openingSquare{"["};
85
        static constexpr lexing::operator_or_punctuator closingSquare{"]"};
86
        static constexpr lexing::operator_or_punctuator backtick{"`"};
87
        static constexpr lexing::operator_or_punctuator singleQuote{"'"};
88
        static constexpr lexing::operator_or_punctuator scopeResolution{"::"};
89
        static constexpr lexing::operator_or_punctuator commaSeparator{","};
90
        static constexpr lexing::operator_or_punctuator pointer{"*"};
91
        static constexpr lexing::operator_or_punctuator lvalueRef{"&"};
92
        static constexpr lexing::operator_or_punctuator rvalueRef{"&&"};
93
        static constexpr lexing::operator_or_punctuator colon{":"};
94
        static constexpr lexing::operator_or_punctuator leftShift{"<<"};
95
        static constexpr lexing::operator_or_punctuator rightShift{">>"};
96
        static constexpr lexing::operator_or_punctuator plus{"+"};
97
        static constexpr lexing::operator_or_punctuator exclamationMark{"!"};
98
        static constexpr lexing::operator_or_punctuator tilde{"~"};
99
        static constexpr lexing::keyword operatorKeyword{"operator"};
100
        static constexpr lexing::keyword constKeyword{"const"};
101
        static constexpr lexing::keyword volatileKeyword{"volatile"};
102
        static constexpr lexing::keyword noexceptKeyword{"noexcept"};
103
        static constexpr lexing::keyword coAwaitKeyword{"co_await"};
104
        static constexpr lexing::keyword newKeyword{"new"};
105
        static constexpr lexing::keyword deleteKeyword{"delete"};
106
        static constexpr lexing::keyword classKeyword{"class"};
107
        static constexpr lexing::keyword structKeyword{"struct"};
108
        static constexpr lexing::keyword enumKeyword{"enum"};
109

110
        static constexpr std::array typeKeywordCollection = {
111
            lexing::keyword{"auto"},
112
            lexing::keyword{"void"},
113
            lexing::keyword{"bool"},
114
            lexing::keyword{"char"},
115
            lexing::keyword{"char8_t"},
116
            lexing::keyword{"char16_t"},
117
            lexing::keyword{"char32_t"},
118
            lexing::keyword{"wchar_t"},
119
            lexing::keyword{"double"},
120
            lexing::keyword{"float"},
121
            lexing::keyword{"int"},
122
            lexing::keyword{"__int64"},
123
            lexing::keyword{"long"},
124
            lexing::keyword{"short"},
125
            lexing::keyword{"signed"},
126
            lexing::keyword{"unsigned"}};
127

128
        Visitor m_Visitor;
129
        StringViewT m_Content;
130
        lexing::NameLexer m_Lexer;
131
        bool m_HasConversionOperator{false};
132

133
        std::vector<Token> m_TokenStack{};
134

135
        template <typename LexerTokenClass>
136
        constexpr LexerTokenClass const* peek_if() const noexcept
137
        {
138
            return std::get_if<LexerTokenClass>(&m_Lexer.peek().classification);
12,644✔
139
        }
140

141
        constexpr void parse()
142
        {
143
            for (lexing::token next = m_Lexer.next();
6,308✔
144
                 !std::holds_alternative<lexing::end>(next.classification);
48,195✔
145
                 next = m_Lexer.next())
41,887✔
146
            {
147
                std::visit(
41,887✔
148
                    [&](auto const& tokenClass) { handle_lexer_token(next.content, tokenClass); },
38,574✔
149
                    next.classification);
150
            }
151
        }
6,308✔
152

153
        template <token_type EndToken>
154
        constexpr bool finalize()
155
        {
156
            if (1u == m_TokenStack.size())
10,124✔
157
            {
158
                if (auto const* const end = std::get_if<EndToken>(&m_TokenStack.back()))
10,064✔
159
                {
160
                    auto& unwrapped = unwrap_visitor(m_Visitor);
9,454✔
161

162
                    unwrapped.begin();
9,454✔
163
                    std::invoke(*end, m_Visitor);
9,454✔
164
                    unwrapped.end();
9,454✔
165

166
                    return true;
9,454✔
167
                }
168
            }
169

170
            return false;
670✔
171
        }
172

173
        constexpr void emit_unrecognized()
174
        {
175
            auto& unwrapped = unwrap_visitor(m_Visitor);
16✔
176
            unwrapped.unrecognized(m_Content);
16✔
177
        }
16✔
178

179
        static constexpr void handle_lexer_token([[maybe_unused]] StringViewT const content, [[maybe_unused]] lexing::end const& end)
180
        {
181
            util::unreachable();
182
        }
183

184
        [[nodiscard]]
185
        constexpr bool merge_with_next_token() const noexcept
186
        {
187
            auto const* const keyword = peek_if<lexing::keyword>();
1,105✔
188

189
            return keyword
190
                && util::contains(typeKeywordCollection, *keyword);
1,105✔
191
        }
192

193
        constexpr void handle_lexer_token([[maybe_unused]] StringViewT const content, [[maybe_unused]] lexing::space const& space)
194
        {
195
            if (auto* const id = match_suffix<token::Identifier>(m_TokenStack))
4,501✔
196
            {
197
                // See, whether we need to merge the current builtin identifier with another one.
198
                // E.g. `long long` or `unsigned int`.
199
                if (id->is_builtin()
2,817✔
200
                    && merge_with_next_token())
2,817✔
201
                {
202
                    auto& curContent = std::get<StringViewT>(id->content);
151✔
203
                    auto const [nextContent, _] = m_Lexer.next();
151✔
204
                    // Merge both keywords by simply treating them as contiguous content.
205
                    MIMICPP_ASSERT(curContent.data() + curContent.size() == content.data(), "Violated expectation.");
206
                    MIMICPP_ASSERT(content.data() + content.size() = nextContent.data(), "Violated expectation.");
207
                    curContent = StringViewT{
151✔
208
                        curContent.data(),
209
                        nextContent.data() + nextContent.size()};
151✔
210

211
                    return;
151✔
212
                }
213

214
                token::try_reduce_as_type(m_TokenStack);
2,666✔
215
            }
216

217
            // In certain cases, a space after an identifier has semantic significance.
218
            // For example, consider the type names `void ()` and `foo()`:
219
            // - `void ()` represents a function type returning `void`.
220
            // - `foo()` represents a function named `foo`.
221
            if (auto const* const nextOp = peek_if<lexing::operator_or_punctuator>();
4,350✔
222
                nextOp
223
                && util::contains(std::array{openingAngle, openingParens, openingCurly, singleQuote, backtick}, *nextOp))
4,350✔
224
            {
225
                m_TokenStack.emplace_back(token::Space{});
545✔
226
            }
227
        }
228

229
        constexpr void handle_lexer_token([[maybe_unused]] StringViewT const content, lexing::identifier const& identifier)
230
        {
231
            m_TokenStack.emplace_back(
12,837✔
232
                token::Identifier{.content = identifier.content});
12,837✔
233
        }
25,674✔
234

235
        constexpr void handle_lexer_token([[maybe_unused]] StringViewT const content, lexing::keyword const& keyword)
236
        {
237
            if (constKeyword == keyword)
4,393✔
238
            {
239
                auto& specs = token::get_or_emplace_specs(m_TokenStack);
932✔
240
                MIMICPP_ASSERT(!specs.layers.empty(), "Zero spec layers detected.");
241
                auto& top = specs.layers.back();
932✔
242
                MIMICPP_ASSERT(!top.isConst, "Specs is already const.");
243
                top.isConst = true;
932✔
244
            }
245
            else if (volatileKeyword == keyword)
3,461✔
246
            {
247
                auto& specs = token::get_or_emplace_specs(m_TokenStack);
536✔
248
                MIMICPP_ASSERT(!specs.layers.empty(), "Zero spec layers detected.");
249
                auto& top = specs.layers.back();
536✔
250
                MIMICPP_ASSERT(!top.isVolatile, "Specs is already volatile.");
251
                top.isVolatile = true;
536✔
252
            }
253
            else if (noexceptKeyword == keyword)
2,925✔
254
            {
255
                auto& specs = token::get_or_emplace_specs(m_TokenStack);
266✔
256
                MIMICPP_ASSERT(!specs.isNoexcept, "Specs already is a noexcept.");
257
                specs.isNoexcept = true;
266✔
258
            }
259
            else if (operatorKeyword == keyword && !process_simple_operator())
2,659✔
260
            {
261
                // Conversion operators can not be part of a scope, thus they can not appear multiple times in a single type-name.
262
                MIMICPP_ASSERT(!m_HasConversionOperator, "Multiple conversion operators detected.");
263

264
                m_TokenStack.emplace_back(token::OperatorKeyword{});
8✔
265
                m_HasConversionOperator = true;
8✔
266
            }
267
            else if (constexpr std::array collection{classKeyword, structKeyword, enumKeyword};
5,302✔
268
                     util::contains(collection, keyword))
2,651✔
269
            {
270
                // This token is needed, so we do not accidentally treat e.g. `(anonymous class)` as function args,
271
                // because otherwise there would just be the `anonymous` identifier left.
272
                m_TokenStack.emplace_back(token::TypeContext{.content = content});
284✔
273
            }
274
            else if (util::contains(typeKeywordCollection, keyword))
2,367✔
275
            {
276
                m_TokenStack.emplace_back(
3,006✔
277
                    token::Identifier{
278
                        .isBuiltinType = true,
279
                        .content = content});
280
            }
281
        }
7,399✔
282

283
        constexpr bool process_simple_operator()
284
        {
285
            auto dropSpaceInput = [this] {
2,160✔
286
                if (std::holds_alternative<lexing::space>(m_Lexer.peek().classification))
880✔
287
                {
288
                    std::ignore = m_Lexer.next();
285✔
289
                }
290
            };
291

292
            dropSpaceInput();
762✔
293

294
            // As we assume valid input, we do not have to check for the actual symbol.
295
            if (auto const next = m_Lexer.peek();
1,524✔
296
                auto const* operatorToken = std::get_if<lexing::operator_or_punctuator>(&next.classification))
762✔
297
            {
298
                std::ignore = m_Lexer.next();
698✔
299

300
                auto const finishMultiOpOperator = [&, this]([[maybe_unused]] lexing::operator_or_punctuator const& expectedClosingOp) {
664✔
301
                    auto const [closingContent, classification] = m_Lexer.next();
72✔
302
                    MIMICPP_ASSERT(lexing::token_class{expectedClosingOp} == classification, "Invalid input.");
303

304
                    StringViewT const content{
72✔
305
                        next.content.data(),
72✔
306
                        next.content.size() + closingContent.size()};
72✔
307
                    m_TokenStack.emplace_back(
216✔
308
                        token::Identifier{
309
                            .content = token::Identifier::OperatorInfo{.symbol = content}});
310
                };
311

312
                if (openingParens == *operatorToken)
698✔
313
                {
314
                    finishMultiOpOperator(closingParens);
74✔
315
                }
316
                else if (openingSquare == *operatorToken)
624✔
317
                {
318
                    finishMultiOpOperator(closingSquare);
16✔
319
                }
320
                // `operator <` and `operator <<` needs to be handled carefully, as it may come in as a template:
321
                // `operator<<>` is actually `operator< <>`.
322
                // Note: No tested c++ compiler actually allows `operator<<>`, but some environments still procude this.
323
                else if (leftShift == *operatorToken)
608✔
324
                {
325
                    dropSpaceInput();
20✔
326

327
                    if (auto const* const nextOp = peek_if<lexing::operator_or_punctuator>();
20✔
328
                        nextOp
329
                        // When next token starts a function or template, we know it's actually `operator <<`.
330
                        && (openingParens == *nextOp || openingAngle == *nextOp))
20✔
331
                    {
332
                        m_TokenStack.emplace_back(
16✔
333
                            token::Identifier{
334
                                .content = token::Identifier::OperatorInfo{.symbol = next.content}});
335
                    }
336
                    // looks like an `operator< <>`, so just treat both `<` separately.
337
                    else
338
                    {
339
                        m_TokenStack.emplace_back(
4✔
340
                            token::Identifier{
UNCOV
341
                                .content = token::Identifier::OperatorInfo{.symbol = next.content.substr(0u, 1u)}});
×
342
                        handle_lexer_token(next.content.substr(1u, 1u), openingAngle);
4✔
343
                    }
344
                }
345
                else
346
                {
347
                    m_TokenStack.emplace_back(
588✔
348
                        token::Identifier{
349
                            .content = token::Identifier::OperatorInfo{.symbol = next.content}});
350
                }
351

352
                dropSpaceInput();
698✔
353

354
                return true;
698✔
355
            }
356
            else if (auto const* keywordToken = std::get_if<lexing::keyword>(&next.classification);
64✔
357
                     keywordToken
358
                     && util::contains(std::array{newKeyword, deleteKeyword, coAwaitKeyword}, *keywordToken))
64✔
359
            {
360
                std::ignore = m_Lexer.next();
56✔
361

362
                StringViewT content = next.content;
56✔
363

364
                if (newKeyword == *keywordToken || deleteKeyword == *keywordToken)
56✔
365
                {
366
                    dropSpaceInput();
48✔
367

368
                    if (auto const* const opAfter = peek_if<lexing::operator_or_punctuator>();
48✔
369
                        opAfter
370
                        && openingSquare == *opAfter)
48✔
371
                    {
372
                        // Strip `[]` or `[ ]` from the input.
373
                        std::ignore = m_Lexer.next();
32✔
374
                        dropSpaceInput();
32✔
375
                        auto const closing = m_Lexer.next();
32✔
376
                        MIMICPP_ASSERT(closingSquare == std::get<lexing::operator_or_punctuator>(closing.classification), "Invalid input.");
377

378
                        content = StringViewT{
32✔
379
                            next.content.data(),
380
                            closing.content.data() + closing.content.size()};
32✔
381
                    }
382
                }
383

384
                m_TokenStack.emplace_back(
56✔
385
                    token::Identifier{
386
                        .content = token::Identifier::OperatorInfo{.symbol = content}});
387

388
                dropSpaceInput();
56✔
389

390
                return true;
56✔
391
            }
392

393
            return false;
8✔
394
        }
1,328✔
395

396
        constexpr void handle_lexer_token(StringViewT const content, lexing::operator_or_punctuator const& token)
397
        {
398
            if (scopeResolution == token)
20,204✔
399
            {
400
                token::try_reduce_as_function_identifier(m_TokenStack);
5,893✔
401

402
                m_TokenStack.emplace_back(
5,893✔
403
                    std::in_place_type<token::ScopeResolution>,
404
                    content);
405
                token::try_reduce_as_scope_sequence(m_TokenStack);
5,893✔
406
            }
407
            else if (commaSeparator == token)
14,311✔
408
            {
409
                if (is_suffix_of<token::Type>(m_TokenStack)
178✔
410
                    || token::try_reduce_as_type(m_TokenStack))
178✔
411
                {
412
                    token::try_reduce_as_arg_sequence(m_TokenStack);
178✔
413
                }
414

415
                m_TokenStack.emplace_back(
178✔
416
                    std::in_place_type<token::ArgSeparator>,
417
                    content);
418
            }
419
            else if (lvalueRef == token)
14,133✔
420
            {
421
                auto& specs = token::get_or_emplace_specs(m_TokenStack);
648✔
422
                MIMICPP_ASSERT(token::Specs::Refness::none == specs.refness, "Specs already is a reference.");
423
                specs.refness = token::Specs::Refness::lvalue;
648✔
424
            }
425
            else if (rvalueRef == token)
13,485✔
426
            {
427
                auto& specs = token::get_or_emplace_specs(m_TokenStack);
452✔
428
                MIMICPP_ASSERT(token::Specs::Refness::none == specs.refness, "Specs already is a reference.");
429
                specs.refness = token::Specs::Refness::rvalue;
452✔
430
            }
431
            else if (pointer == token)
13,033✔
432
            {
433
                auto& specs = token::get_or_emplace_specs(m_TokenStack);
306✔
434
                specs.layers.emplace_back();
306✔
435
            }
436
            else if (openingAngle == token)
12,727✔
437
            {
438
                m_TokenStack.emplace_back(
1,218✔
439
                    std::in_place_type<token::OpeningAngle>,
440
                    content);
441
            }
442
            else if (closingAngle == token)
11,509✔
443
            {
444
                if (is_suffix_of<token::Type>(m_TokenStack)
1,218✔
445
                    || token::try_reduce_as_type(m_TokenStack))
1,218✔
446
                {
447
                    token::try_reduce_as_arg_sequence(m_TokenStack);
446✔
448
                }
449

450
                m_TokenStack.emplace_back(
1,218✔
451
                    std::in_place_type<token::ClosingAngle>,
452
                    content);
453
                token::try_reduce_as_template_identifier(m_TokenStack)
1,218✔
454
                    || token::try_reduce_as_placeholder_identifier_wrapped<token::OpeningAngle, token::ClosingAngle>(m_TokenStack);
1,218✔
455
            }
456
            else if (openingParens == token)
10,291✔
457
            {
458
                m_TokenStack.emplace_back(
4,300✔
459
                    std::in_place_type<token::OpeningParens>,
460
                    content);
461
            }
462
            else if (closingParens == token)
5,991✔
463
            {
464
                bool isNextOpeningParens{false};
4,300✔
465
                if (auto const* const nextOp = peek_if<lexing::operator_or_punctuator>())
4,300✔
466
                {
467
                    isNextOpeningParens = (openingParens == *nextOp);
1,727✔
468
                }
469

470
                // There can be no `(` directly after function-args, thus do not perform any reduction if such a token is found.
471
                // This helps when function-ptrs are given, so that we do not accidentally reduce something like `(__cdecl*)` as function-args.
472
                if (!isNextOpeningParens)
4,300✔
473
                {
474
                    if (is_suffix_of<token::Type>(m_TokenStack)
4,236✔
475
                        || token::try_reduce_as_type(m_TokenStack))
4,236✔
476
                    {
477
                        token::try_reduce_as_arg_sequence(m_TokenStack);
346✔
478
                    }
479
                }
480

481
                m_TokenStack.emplace_back(
4,300✔
482
                    std::in_place_type<token::ClosingParens>,
483
                    content);
484

485
                if (bool const result = isNextOpeningParens
8,600✔
486
                     ? token::try_reduce_as_function_ptr(m_TokenStack)
4,300✔
487
                     : token::try_reduce_as_function_context(m_TokenStack);
4,236✔
488
                     !result)
4,300✔
489
                {
490
                    token::try_reduce_as_placeholder_identifier_wrapped<token::OpeningParens, token::ClosingParens>(m_TokenStack);
690✔
491
                }
492
            }
493
            else if (openingCurly == token)
1,691✔
494
            {
495
                m_TokenStack.emplace_back(
217✔
496
                    std::in_place_type<token::OpeningCurly>,
497
                    content);
498
            }
499
            else if (closingCurly == token)
1,474✔
500
            {
501
                m_TokenStack.emplace_back(
217✔
502
                    std::in_place_type<token::ClosingCurly>,
503
                    content);
504
                token::try_reduce_as_placeholder_identifier_wrapped<token::OpeningCurly, token::ClosingCurly>(m_TokenStack);
217✔
505
            }
506
            else if (backtick == token)
1,257✔
507
            {
508
                m_TokenStack.emplace_back(
302✔
509
                    std::in_place_type<token::OpeningBacktick>,
510
                    content);
511
            }
512
            else if (singleQuote == token)
955✔
513
            {
514
                if (token::try_reduce_as_function_identifier(m_TokenStack))
642✔
515
                {
516
                    unwrap_msvc_like_function();
124✔
517
                }
518
                // Something like `id1::id2' should become id1::id2, so just remove the leading backtick.
519
                else if (is_suffix_of<token::OpeningBacktick, token::ScopeSequence, token::Identifier>(m_TokenStack))
518✔
520
                {
521
                    m_TokenStack.erase(m_TokenStack.cend() - 3u);
8✔
522
                }
523
                else
524
                {
525
                    m_TokenStack.emplace_back(
514✔
526
                        std::in_place_type<token::ClosingSingleQuote>,
527
                        content);
528
                    // Well, some environments wrap in `' (like msvc) and some wrap in '' (libc++).
529
                    token::try_reduce_as_placeholder_identifier_wrapped<token::OpeningBacktick, token::ClosingSingleQuote>(m_TokenStack)
514✔
530
                        || token::try_reduce_as_placeholder_identifier_wrapped<token::ClosingSingleQuote, token::ClosingSingleQuote>(m_TokenStack);
514✔
531
                }
532
            }
533
            // The current parsing process will never receive an `<<` or `>>` without a preceding `operator` keyword.
534
            // As the current `operator` parsing currently consumes the next op-symbol, we will never reach this point
535
            // with an actual left or right-shift. So, to make that easier, just split them.
536
            else if (leftShift == token)
313✔
537
            {
538
                handle_lexer_token(content.substr(0, 1u), openingAngle);
10✔
539
                handle_lexer_token(content.substr(1u, 1u), openingAngle);
10✔
540
            }
541
            else if (rightShift == token)
303✔
542
            {
543
                handle_lexer_token(content.substr(0, 1u), closingAngle);
12✔
544
                handle_lexer_token(content.substr(1u, 1u), closingAngle);
12✔
545
            }
546
            // A `~` without a preceding `operator` keyword must be followed by a type-name and forms a destructor.
547
            else if (tilde == token)
291✔
548
            {
549
                if (auto const* nextId = peek_if<lexing::identifier>())
1✔
550
                {
551
                    StringViewT const merged{
2✔
552
                        content.data(),
553
                        nextId->content.data() + nextId->content.size()};
1✔
554
                    m_TokenStack.emplace_back(token::Identifier{.content = merged});
1✔
555
                    std::ignore = m_Lexer.next();
1✔
556
                }
557
            }
558
            // The msvc c++23 `std::stacktrace` implementation adds `+0x\d+` to function identifiers.
559
            // The only reason to receive a `+`-token without an `operator`-token is exactly that case.
560
            // So, just ignore it and skip the next identifier.
561
            else if (plus == token)
290✔
562
            {
563
                if (auto const* const nextId = peek_if<lexing::identifier>();
2✔
564
                    nextId
565
                    && nextId->content.starts_with("0x"))
2✔
566
                {
567
                    std::ignore = m_Lexer.next();
2✔
568
                }
569
            }
570
            // The msvc c++23 `std::stacktrace` implementation seems to add something which looks like the executable-name as prefix.
571
            // The only reason to receive a `!`-token without an `operator`-token is exactly that case.
572
            // So, just ignore it and skip the previous identifier.
573
            else if (exclamationMark == token
288✔
574
                && is_suffix_of<token::Identifier>(m_TokenStack))
288✔
575
            {
576
                m_TokenStack.pop_back();
6✔
577
            }
578
        }
20,206✔
579

580
        void unwrap_msvc_like_function()
581
        {
582
            MIMICPP_ASSERT(is_suffix_of<token::FunctionIdentifier>(m_TokenStack), "Invalid state.");
583

584
            auto funIdentifier = std::get<token::FunctionIdentifier>(m_TokenStack.back());
124✔
585
            m_TokenStack.pop_back();
124✔
586

587
            std::optional<token::ScopeSequence> scopes{};
124✔
588
            if (auto* const scopeSeq = match_suffix<token::ScopeSequence>(m_TokenStack))
124✔
589
            {
590
                scopes = std::move(*scopeSeq);
12✔
591
                m_TokenStack.pop_back();
12✔
592
            }
593

594
            // Ignore return-types.
595
            if (is_suffix_of<token::Type>(m_TokenStack))
124✔
596
            {
597
                m_TokenStack.pop_back();
124✔
598
            }
599

600
            MIMICPP_ASSERT(match_suffix<token::OpeningBacktick>(m_TokenStack), "Invalid state.");
601
            m_TokenStack.pop_back();
124✔
602

603
            // As we gather spaces in front of backticks, there may be a space here, too.
604
            if (is_suffix_of<token::Space>(m_TokenStack))
124✔
605
            {
606
                m_TokenStack.pop_back();
116✔
607
            }
608

609
            MIMICPP_ASSERT(!is_suffix_of<token::ScopeSequence>(m_TokenStack), "Invlid state.");
610
            if (scopes)
124✔
611
            {
612
                m_TokenStack.emplace_back(*std::move(scopes));
24✔
613
            }
614

615
            m_TokenStack.emplace_back(std::move(funIdentifier));
248✔
616
        }
124✔
617
    };
618
}
619

620
#endif
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc