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
This commit is contained in:
Olly Betts 2025-02-28 14:32:28 +13:00
parent 1bff64d5e2
commit 4a0372aacc
3 changed files with 63 additions and 19 deletions

View File

@ -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) 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 2025-02-19: wsfulton
Add support for $n special variable expansion in the names of typemap Add support for $n special variable expansion in the names of typemap
local variables, such as: local variables, such as:

View File

@ -74,4 +74,22 @@ struct A {
}; };
constexpr A a{42}; constexpr A a{42};
constexpr int N = a.i; constexpr int N = a.i;
// Regression test for https://github.com/swig/swig/issues/3127 fixed in 4.4.0:
#include <array>
constexpr std::size_t my_enum_size =
sizeof(
decltype(
42
)
) ? 1 + static_cast<std::size_t>(
4
) : alignof(
std::size_t
);
std::array<bool, my_enum_size> do_something() {
return std::array<bool, my_enum_size>{true,true,true,true,true};
}
%} %}

View File

@ -1913,6 +1913,25 @@ static SwigType *deduce_type(const struct Define *dtype) {
return NULL; 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) { static Node *new_enum_node(SwigType *enum_base_type) {
Node *n = new_node("enum"); Node *n = new_node("enum");
if (enum_base_type) { if (enum_base_type) {
@ -6701,8 +6720,8 @@ exprmem : ID[lhs] ARROW ID[rhs] {
| ID[lhs] ARROW ID[rhs] LPAREN { | ID[lhs] ARROW ID[rhs] LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = default_dtype; $$ = default_dtype;
$$.val = NewStringf("%s->%s%s", $lhs, $rhs, scanner_ccode); $$.val = NewStringf("%s->%s", $lhs, $rhs);
Clear(scanner_ccode); append_expr_from_scanner($$.val);
} }
| exprmem[in] ARROW ID { | exprmem[in] ARROW ID {
$$ = $in; $$ = $in;
@ -6711,8 +6730,8 @@ exprmem : ID[lhs] ARROW ID[rhs] {
| exprmem[in] ARROW ID LPAREN { | exprmem[in] ARROW ID LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = $in; $$ = $in;
Printf($$.val, "->%s%s", $ID, scanner_ccode); Printf($$.val, "->%s", $ID);
Clear(scanner_ccode); append_expr_from_scanner($$.val);
} }
| ID[lhs] PERIOD ID[rhs] { | ID[lhs] PERIOD ID[rhs] {
$$ = default_dtype; $$ = default_dtype;
@ -6721,8 +6740,8 @@ exprmem : ID[lhs] ARROW ID[rhs] {
| ID[lhs] PERIOD ID[rhs] LPAREN { | ID[lhs] PERIOD ID[rhs] LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = default_dtype; $$ = default_dtype;
$$.val = NewStringf("%s.%s%s", $lhs, $rhs, scanner_ccode); $$.val = NewStringf("%s.%s", $lhs, $rhs);
Clear(scanner_ccode); append_expr_from_scanner($$.val);
} }
| exprmem[in] PERIOD ID { | exprmem[in] PERIOD ID {
$$ = $in; $$ = $in;
@ -6731,8 +6750,8 @@ exprmem : ID[lhs] ARROW ID[rhs] {
| exprmem[in] PERIOD ID LPAREN { | exprmem[in] PERIOD ID LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = $in; $$ = $in;
Printf($$.val, ".%s%s", $ID, scanner_ccode); Printf($$.val, ".%s", $ID);
Clear(scanner_ccode); append_expr_from_scanner($$.val);
} }
; ;
@ -6772,24 +6791,24 @@ exprsimple : exprnum
| SIZEOF LPAREN { | SIZEOF LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = default_dtype; $$ = default_dtype;
$$.val = NewStringf("sizeof%s", scanner_ccode); $$.val = NewString("sizeof");
Clear(scanner_ccode); append_expr_from_scanner($$.val);
$$.type = T_ULONG; $$.type = T_ULONG;
} }
/* alignof(T) always has type size_t. */ /* alignof(T) always has type size_t. */
| ALIGNOF LPAREN { | ALIGNOF LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = default_dtype; $$ = default_dtype;
$$.val = NewStringf("alignof%s", scanner_ccode); $$.val = NewString("alignof");
Clear(scanner_ccode); append_expr_from_scanner($$.val);
$$.type = T_ULONG; $$.type = T_ULONG;
} }
/* noexcept(X) always has type bool. */ /* noexcept(X) always has type bool. */
| NOEXCEPT LPAREN { | NOEXCEPT LPAREN {
if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE); if (skip_balanced('(', ')') < 0) Exit(EXIT_FAILURE);
$$ = default_dtype; $$ = default_dtype;
$$.val = NewStringf("noexcept%s", scanner_ccode); $$.val = NewString("noexcept");
Clear(scanner_ccode); append_expr_from_scanner($$.val);
$$.type = T_BOOL; $$.type = T_BOOL;
} }
| SIZEOF ELLIPSIS LPAREN identifier RPAREN { | SIZEOF ELLIPSIS LPAREN identifier RPAREN {
@ -7253,16 +7272,14 @@ exprcompound : expr[lhs] PLUS expr[rhs] {
} }
| type LPAREN { | type LPAREN {
$$ = default_dtype; $$ = default_dtype;
String *qty;
if (skip_balanced('(',')') < 0) Exit(EXIT_FAILURE); 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)) { if (SwigType_istemplate(qty)) {
String *nstr = SwigType_namestr(qty); String *nstr = SwigType_namestr(qty);
Delete(qty); Delete(qty);
qty = nstr; qty = nstr;
} }
$$.val = NewStringf("%s%s",qty,scanner_ccode);
Clear(scanner_ccode);
/* Try to deduce the type - this could be a C++ "constructor /* Try to deduce the type - this could be a C++ "constructor
* cast" such as `double(4)` or a function call such as * cast" such as `double(4)` or a function call such as
* `some_func()`. In the latter case we get T_USER, but that * `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); $$.type = SwigType_type(qty);
if ($$.type == T_USER) $$.type = T_UNKNOWN; if ($$.type == T_USER) $$.type = T_UNKNOWN;
$$.unary_arg_type = 0; $$.unary_arg_type = 0;
Delete(qty);
$$.val = qty;
append_expr_from_scanner($$.val);
} }
; ;