From 4a0372aacca5a4d31d209c87b798537d15500e56 Mon Sep 17 00:00:00 2001 From: Olly Betts Date: Fri, 28 Feb 2025 14:32:28 +1300 Subject: [PATCH] Improve parsing multi-line expr via skip_balanced() Fix bad generated code in some cases when a constant expression is split over multiple lines and used as part of a type. This manifested in cases where SWIG's parser calls skip_balanced('(', ')') then grabs the skipped expression's program text from scanner_ccode. Fixes #3127 --- CHANGES.current | 7 ++++ Examples/test-suite/cpp11_constexpr.i | 18 +++++++++ Source/CParse/parser.y | 57 ++++++++++++++++++--------- 3 files changed, 63 insertions(+), 19 deletions(-) diff --git a/CHANGES.current b/CHANGES.current index 0a7f52664..4b7c0ab18 100644 --- a/CHANGES.current +++ b/CHANGES.current @@ -7,6 +7,13 @@ the issue number to the end of the URL: https://github.com/swig/swig/issues/ Version 4.4.0 (in progress) =========================== +2025-02-28: olly + #3127 Fix bad generated code in some cases when a constant + expression is split over multiple lines and used as part of a type. + This manifested in cases where SWIG's parser gets the expression + text by skipping to the matching closing parenthesis and grabbing + the skipped program text. + 2025-02-19: wsfulton Add support for $n special variable expansion in the names of typemap local variables, such as: diff --git a/Examples/test-suite/cpp11_constexpr.i b/Examples/test-suite/cpp11_constexpr.i index 5fdfa2868..134ed4c3c 100644 --- a/Examples/test-suite/cpp11_constexpr.i +++ b/Examples/test-suite/cpp11_constexpr.i @@ -74,4 +74,22 @@ struct A { }; constexpr A a{42}; constexpr int N = a.i; + +// Regression test for https://github.com/swig/swig/issues/3127 fixed in 4.4.0: +#include + +constexpr std::size_t my_enum_size = + sizeof( + decltype( + 42 + ) + ) ? 1 + static_cast( + 4 + ) : alignof( + std::size_t + ); + +std::array do_something() { + return std::array{true,true,true,true,true}; +} %} diff --git a/Source/CParse/parser.y b/Source/CParse/parser.y index f3ed4040a..c27b632ac 100644 --- a/Source/CParse/parser.y +++ b/Source/CParse/parser.y @@ -1913,6 +1913,25 @@ static SwigType *deduce_type(const struct Define *dtype) { return NULL; } +// Append scanner_ccode to expr, normalising runs of whitespace to a single +// space (in particular newlines are problematic in the generated +// swig_type_info). +static void append_expr_from_scanner(String *expr) { + int len = Len(scanner_ccode); + int in_space = 0; + for (int i = 0; i < len; ++i) { + char ch = Char(scanner_ccode)[i]; + if (isspace((unsigned char)ch)) { + if (!in_space) Putc(' ', expr); + in_space = 1; + } else { + Putc(ch, expr); + in_space = 0; + } + } + Clear(scanner_ccode); +} + static Node *new_enum_node(SwigType *enum_base_type) { Node *n = new_node("enum"); if (enum_base_type) { @@ -6701,8 +6720,8 @@ exprmem : ID[lhs] ARROW ID[rhs] { | ID[lhs] ARROW ID[rhs] LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = default_dtype; - $$.val = NewStringf("%s->%s%s", $lhs, $rhs, scanner_ccode); - Clear(scanner_ccode); + $$.val = NewStringf("%s->%s", $lhs, $rhs); + append_expr_from_scanner($$.val); } | exprmem[in] ARROW ID { $$ = $in; @@ -6711,8 +6730,8 @@ exprmem : ID[lhs] ARROW ID[rhs] { | exprmem[in] ARROW ID LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = $in; - Printf($$.val, "->%s%s", $ID, scanner_ccode); - Clear(scanner_ccode); + Printf($$.val, "->%s", $ID); + append_expr_from_scanner($$.val); } | ID[lhs] PERIOD ID[rhs] { $$ = default_dtype; @@ -6721,8 +6740,8 @@ exprmem : ID[lhs] ARROW ID[rhs] { | ID[lhs] PERIOD ID[rhs] LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = default_dtype; - $$.val = NewStringf("%s.%s%s", $lhs, $rhs, scanner_ccode); - Clear(scanner_ccode); + $$.val = NewStringf("%s.%s", $lhs, $rhs); + append_expr_from_scanner($$.val); } | exprmem[in] PERIOD ID { $$ = $in; @@ -6731,8 +6750,8 @@ exprmem : ID[lhs] ARROW ID[rhs] { | exprmem[in] PERIOD ID LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = $in; - Printf($$.val, ".%s%s", $ID, scanner_ccode); - Clear(scanner_ccode); + Printf($$.val, ".%s", $ID); + append_expr_from_scanner($$.val); } ; @@ -6772,24 +6791,24 @@ exprsimple : exprnum | SIZEOF LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = default_dtype; - $$.val = NewStringf("sizeof%s", scanner_ccode); - Clear(scanner_ccode); + $$.val = NewString("sizeof"); + append_expr_from_scanner($$.val); $$.type = T_ULONG; } /* alignof(T) always has type size_t. */ | ALIGNOF LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = default_dtype; - $$.val = NewStringf("alignof%s", scanner_ccode); - Clear(scanner_ccode); + $$.val = NewString("alignof"); + append_expr_from_scanner($$.val); $$.type = T_ULONG; } /* noexcept(X) always has type bool. */ | NOEXCEPT LPAREN { if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); $$ = default_dtype; - $$.val = NewStringf("noexcept%s", scanner_ccode); - Clear(scanner_ccode); + $$.val = NewString("noexcept"); + append_expr_from_scanner($$.val); $$.type = T_BOOL; } | SIZEOF ELLIPSIS LPAREN identifier RPAREN { @@ -7253,16 +7272,14 @@ exprcompound : expr[lhs] PLUS expr[rhs] { } | type LPAREN { $$ = default_dtype; - String *qty; if (skip_balanced('(',')') < 0) Exit(EXIT_FAILURE); - qty = Swig_symbol_type_qualify($type,0); + + String *qty = Swig_symbol_type_qualify($type, 0); if (SwigType_istemplate(qty)) { String *nstr = SwigType_namestr(qty); Delete(qty); qty = nstr; } - $$.val = NewStringf("%s%s",qty,scanner_ccode); - Clear(scanner_ccode); /* Try to deduce the type - this could be a C++ "constructor * cast" such as `double(4)` or a function call such as * `some_func()`. In the latter case we get T_USER, but that @@ -7274,7 +7291,9 @@ exprcompound : expr[lhs] PLUS expr[rhs] { $$.type = SwigType_type(qty); if ($$.type == T_USER) $$.type = T_UNKNOWN; $$.unary_arg_type = 0; - Delete(qty); + + $$.val = qty; + append_expr_from_scanner($$.val); } ;