From: Rico Tzschichholz Date: Tue, 16 Feb 2021 21:42:00 +0000 (+0100) Subject: vala: Properly parse and handle chained initialization of members X-Git-Tag: 0.51.2~1 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=86bc23f2d7295ae3b2f5f79449d2c5a4a7a93a58;p=thirdparty%2Fvala.git vala: Properly parse and handle chained initialization of members Make MemberInitializer an Expression, so it is possible to descibe nested/chained member initializers in the AST. Fixes https://gitlab.gnome.org/GNOME/vala/issues/1137 --- diff --git a/tests/Makefile.am b/tests/Makefile.am index 3294f9a03..612808724 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -451,6 +451,8 @@ TESTS = \ objects/interface-property-override.vala \ objects/interface-virtual-override.vala \ objects/member-initializer-base-properties.vala \ + objects/member-initializer-chained.vala \ + objects/member-initializer-chained-2.vala \ objects/member-initializer-property.vala \ objects/member-initializer-property-owned-setter.vala \ objects/methods.vala \ diff --git a/tests/objects/member-initializer-chained-2.vala b/tests/objects/member-initializer-chained-2.vala new file mode 100644 index 000000000..b8b12d616 --- /dev/null +++ b/tests/objects/member-initializer-chained-2.vala @@ -0,0 +1,18 @@ +class Foo : Object { +} + +class Bar { + public Foo a; + public Foo b { get; set; } + public Foo c; +} + +void main () { + var bar = new Bar () { + a = b = c = new Foo () + }; + assert (bar.a != null); + assert (bar.a == bar.b); + assert (bar.a == bar.c); + assert (bar.a.ref_count == 4); +} diff --git a/tests/objects/member-initializer-chained.vala b/tests/objects/member-initializer-chained.vala new file mode 100644 index 000000000..106076a6f --- /dev/null +++ b/tests/objects/member-initializer-chained.vala @@ -0,0 +1,61 @@ +class Foo { + public int x; + public int y { get; set; } + public int z; +} + +class Bar : Foo { + public Bar () { + x = 4711; + y = 4711; + z = 4711; + { + var foo = new Foo () { + x = y = z = 23 + }; + assert (foo.x == 23); + assert (foo.y == 23); + assert (foo.y == 23); + } + { + var foo2 = new Foo () { + z = 42, + y = z, + x = y + }; + assert (foo2.x == 4711); + assert (foo2.y == 4711); + assert (foo2.z == 42); + } + assert (x == 4711); + assert (y == 4711); + assert (z == 4711); + } +} + +int i = 67; + +int get_int () { + return i++; +} + +void main () { + { + var bar = new Bar () { + x = y = z = get_int () + }; + assert (bar.x == 67); + assert (bar.y == 67); + assert (bar.z == 67); + } + { + var bar = new Bar () { + x = 23, + y = 42, + z = 67 + }; + assert (bar.x == 23); + assert (bar.y == 42); + assert (bar.z == 67); + } +} diff --git a/vala/valagenieparser.vala b/vala/valagenieparser.vala index 2b3537cf6..43ff863de 100644 --- a/vala/valagenieparser.vala +++ b/vala/valagenieparser.vala @@ -1179,8 +1179,16 @@ public class Vala.Genie.Parser : CodeVisitor { var begin = get_location (); string id = parse_identifier (); expect (TokenType.ASSIGN); - var expr = parse_expression (); + var inner = get_location (); + Expression expr; + try { + // chained member initializer + expr = parse_member_initializer (); + } catch { + rollback (inner); + expr = parse_expression (); + } return new MemberInitializer (id, expr, get_src (begin)); } diff --git a/vala/valamemberinitializer.vala b/vala/valamemberinitializer.vala index 270e4204d..3f693120e 100644 --- a/vala/valamemberinitializer.vala +++ b/vala/valamemberinitializer.vala @@ -26,7 +26,7 @@ using GLib; * Represents a member initializer, i.e. an element of an object initializer, in * the source code. */ -public class Vala.MemberInitializer : CodeNode { +public class Vala.MemberInitializer : Expression { /** * Member name. */ @@ -43,11 +43,6 @@ public class Vala.MemberInitializer : CodeNode { } } - /** - * The symbol this expression refers to. - */ - public weak Symbol symbol_reference { get; set; } - Expression _initializer; /** @@ -64,6 +59,10 @@ public class Vala.MemberInitializer : CodeNode { this.name = name; } + public override bool is_pure () { + return false; + } + public override void accept (CodeVisitor visitor) { initializer.accept (visitor); } diff --git a/vala/valaobjectcreationexpression.vala b/vala/valaobjectcreationexpression.vala index 1126288b8..ca2731258 100644 --- a/vala/valaobjectcreationexpression.vala +++ b/vala/valaobjectcreationexpression.vala @@ -507,7 +507,34 @@ public class Vala.ObjectCreationExpression : Expression, CallableExpression { context.analyzer.check_type (type_reference); } + // Unwrap chained member initializers foreach (MemberInitializer init in get_object_initializer ()) { + if (!(init.initializer is MemberInitializer)) { + continue; + } + + int index = object_initializer.index_of (init); + object_initializer.remove_at (index); + assert (index >= 0); + + unowned MemberInitializer? inner_mi = (MemberInitializer) init.initializer; + while (inner_mi.initializer is MemberInitializer) { + inner_mi = (MemberInitializer) inner_mi.initializer; + } + + var local = new LocalVariable (null, get_temp_name (), inner_mi.initializer, inner_mi.initializer.source_reference); + var decl = new DeclarationStatement (local, inner_mi.initializer.source_reference); + decl.check (context); + insert_statement (context.analyzer.insert_block, decl); + + do { + var member_init = new MemberInitializer (inner_mi.name, new MemberAccess (null, local.name, inner_mi.source_reference), inner_mi.source_reference); + object_initializer.insert (index++, member_init); + inner_mi = inner_mi.parent_node as MemberInitializer; + } while (inner_mi != null); + } + foreach (MemberInitializer init in get_object_initializer ()) { + init.parent_node = this; init.check (context); } diff --git a/vala/valaparser.vala b/vala/valaparser.vala index 2c2db2d3a..7236d5c7b 100644 --- a/vala/valaparser.vala +++ b/vala/valaparser.vala @@ -1064,8 +1064,16 @@ public class Vala.Parser : CodeVisitor { var begin = get_location (); string id = parse_identifier (); expect (TokenType.ASSIGN); - var expr = parse_expression (); + var inner = get_location (); + Expression expr; + try { + // chained member initializer + expr = parse_member_initializer (); + } catch { + rollback (inner); + expr = parse_expression (); + } return new MemberInitializer (id, expr, get_src (begin)); }