objects/bug795225-3.test \
objects/bug795225-4.test \
objects/bug795521.vala \
+ objects/with-expression.vala \
+ objects/with-instance.vala \
+ objects/with-nested.vala \
errors/catch-error-code.vala \
errors/catch-in-finally.vala \
errors/default-gtype.vala \
parser/using-ambiguous-reference.test \
parser/using-directive.vala \
parser/using-invalid-namespace.test \
+ parser/with-embedded.vala \
+ parser/with-empty.vala \
+ parser/with-invalid-declaration.test \
+ parser/with-invalid-expression.test \
+ parser/with-no-block.test \
+ parser/with-no-expression.test \
parser/yield-method.test \
parser/yield-return.test \
parser/yield-return.vala \
semantic/unary-unsupported-increment.test \
semantic/unary-unsupported-minus.test \
semantic/unary-unsupported-negation.test \
+ semantic/with-array.test \
+ semantic/with-buildin.vala \
+ semantic/with-class.test \
+ semantic/with-compact.vala \
+ semantic/with-declaration-cast-type.vala \
+ semantic/with-declaration-wrong-type.test \
+ semantic/with-declaration.vala \
+ semantic/with-dereferenced-pointer.vala \
+ semantic/with-enum.test \
+ semantic/with-enum-member.vala \
+ semantic/with-error.test \
+ semantic/with-error-member.test \
+ semantic/with-pointer.test \
+ semantic/with-namespace.test \
+ semantic/with-no-declaration.test \
+ semantic/with-no-such-member.test \
+ semantic/with-null.vala \
+ semantic/with-string.vala \
+ semantic/with-outside-declaration.test \
+ semantic/with-value.vala \
semantic/yield-call-requires-async-context.test \
semantic/yield-call-requires-async-method.test \
semantic/yield-creation-requires-async-context.test \
nullability/member-access-nullable-instance.test \
nullability/method-parameter-invalid-convert.test \
nullability/method-return-invalid-convert.test \
+ nullability/with-non-null.test \
$(NULL)
LINUX_TESTS = \
--- /dev/null
+Invalid Code
+
+class Foo {
+ public int i;
+}
+
+void main () {
+ Foo? f = new Foo ();
+ with (f) {
+ i = 7;
+ }
+}
--- /dev/null
+class Foo {
+ public static int factory_called = 0;
+ public static Foo factory () {
+ factory_called++;
+ return new Foo ();
+ }
+
+ public static int method_called = 0;
+ public void method () {
+ method_called++;
+ }
+}
+
+void test () {
+ var foo = new Foo ();
+ with (foo)
+ method ();
+
+ with (new Foo ())
+ method ();
+
+ with (Foo.factory ()) {
+ method ();
+ method ();
+ }
+
+ Foo[] arr = {foo, foo};
+ with (arr[0]) {
+ method ();
+ }
+
+ assert (Foo.method_called == 5);
+ assert (Foo.factory_called == 1);
+}
+
+void main () {
+ test ();
+}
--- /dev/null
+class Foo {
+ public int field;
+ public string prop { get; set; }
+
+ public bool method_called = false;
+ public void method () {
+ method_called = true;
+ }
+}
+
+class Bar : Foo { }
+
+class TestFoo {
+ public static int class_field;
+ public int instance_field;
+
+ public void test () {
+ var foo = new Foo ();
+ var local_field = 0;
+
+ with (foo) {
+ field = 10;
+ prop = "prop";
+ method ();
+
+ local_field = 20;
+ class_field = 30;
+ instance_field = 40;
+ }
+
+ assert (foo.field == 10);
+ assert (foo.prop == "prop");
+ assert (foo.method_called);
+
+ assert (local_field == 20);
+ assert (class_field == 30);
+ assert (instance_field == 40);
+ }
+}
+
+// Copy and paste TestFoo, change Foo to Bar
+class TestBar {
+ public static int class_field;
+ public int instance_field;
+
+ public void test () {
+ var foo = new Bar ();
+ var local_field = 0;
+
+ with (foo) {
+ field = 10;
+ prop = "prop";
+ method ();
+
+ local_field = 20;
+ class_field = 30;
+ instance_field = 40;
+ }
+
+ assert (foo.field == 10);
+ assert (foo.prop == "prop");
+ assert (foo.method_called);
+
+ assert (local_field == 20);
+ assert (class_field == 30);
+ assert (instance_field == 40);
+ }
+}
+
+void main () {
+ new TestFoo ().test ();
+ new TestBar ().test ();
+}
--- /dev/null
+class Foo {
+ public int field;
+}
+
+class Bar {
+ public int field;
+}
+
+class Test {
+ public int field;
+
+ void nested () {
+ var foo = new Foo ();
+ var bar = new Bar ();
+
+ with (var f = foo) {
+ field = 100;
+ with (bar) {
+ field = 200;
+ f.field = 300;
+ this.field = 400;
+ }
+ }
+
+ assert (foo.field == 300);
+ assert (bar.field == 200);
+ assert (this.field == 400);
+ }
+
+ static int main () {
+ new Test ().nested ();
+ return 0;
+ }
+}
--- /dev/null
+void main () {
+ with (10)
+ assert (to_string () == "10");
+}
--- /dev/null
+void main () {
+ with (10);
+}
--- /dev/null
+Invalid Code
+
+void main () {
+ with (var f foo) {
+ }
+}
--- /dev/null
+Invalid Code
+
+void main () {
+ with (f foo) {
+ }
+}
--- /dev/null
+Invalid Code
+
+void main () {
+ with (10)
+}
\ No newline at end of file
--- /dev/null
+Invalid Code
+
+void main () {
+ with () {
+ }
+}
--- /dev/null
+Invalid Code
+
+void main () {
+ int[] arr = { 1, 2, 3 };
+ with (arr) {
+ }
+}
--- /dev/null
+void main () {
+ with (stdout) {
+ assert (!eof ());
+ }
+}
--- /dev/null
+Invalid Code
+
+class Foo {
+}
+
+void main () {
+ with (Foo) {
+ }
+}
--- /dev/null
+[Compact]
+class Foo {
+ public int i;
+}
+
+void main () {
+ var foo = new Foo ();
+ with (foo) {
+ i = 13;
+ }
+
+ assert (foo.i == 13);
+}
--- /dev/null
+class Foo {
+}
+
+class Bar : Foo {
+}
+
+void main () {
+ with (Foo f = new Bar ()) {
+ }
+}
--- /dev/null
+Invalid Code
+
+class Foo {
+}
+
+class Bar {
+}
+
+void main () {
+ with (Bar f = new Foo ()) {
+ }
+}
--- /dev/null
+class Foo {
+ public int i;
+ public int j;
+}
+
+void main () {
+ var foo = new Foo ();
+ with (foo) {
+ i = 10;
+ }
+
+ assert (foo.i == 10);
+ with (var f = foo) {
+ i = 100;
+ f.j = 200;
+ }
+
+ assert (foo.i == 100);
+ assert (foo.j == 200);
+ with (Foo f = foo) {
+ i = 1000;
+ f.j = 2000;
+ }
+
+ assert (foo.i == 1000);
+ assert (foo.j == 2000);
+ Foo f;
+ with (f = foo) {
+ i = 10000;
+ f.j = 20000;
+ }
+
+ assert (f.i == 10000);
+ assert (foo.j == 20000);
+}
--- /dev/null
+class Foo {
+ public int i;
+}
+
+void main () {
+ var f = new Foo ();
+ var p = &f;
+
+ with (*p) {
+ i = 13;
+ }
+
+ assert (f.i == 13);
+}
--- /dev/null
+enum FooEnum {
+ FIRST
+}
+
+void main () {
+ with (FooEnum.FIRST) {
+ }
+}
--- /dev/null
+Invalid Code
+
+enum FooEnum {
+ FIRST
+}
+
+void main () {
+ with (FooEnum) {
+ }
+}
--- /dev/null
+Invalid Code
+
+errordomain FooError {
+ FAIL
+}
+
+void main () {
+ var e = new FooError.FAIL ("foo");
+ with (e) {
+ }
+}
--- /dev/null
+Invalid Code
+
+errordomain FooError {
+ FAIL
+}
+
+void main () {
+ with (FooError) {
+ }
+}
--- /dev/null
+Invalid Code
+
+namespace Foo {
+}
+
+void main () {
+ with (Foo) {
+ }
+}
--- /dev/null
+Invalid Code
+
+class Foo {
+}
+
+void main () {
+ with (f = new Foo ()) {
+ }
+}
--- /dev/null
+Invalid Code
+
+class Foo {
+}
+
+void main () {
+ with (new Foo ()) {
+ field = 100;
+ }
+}
--- /dev/null
+class Foo {
+ public int i;
+}
+
+void main () {
+ Foo? f = null;
+ Process.exit (0);
+
+ with (f) {
+ i = 7;
+ }
+}
--- /dev/null
+Invalid Code
+
+class Foo {
+ public int i;
+}
+
+void main () {
+ with (var f = new Foo ()) {
+ i = 10;
+ }
+
+ f.i = 100;
+}
--- /dev/null
+Invalid Code
+
+void main () {
+ int i = 0;
+ with (&i) {
+ }
+}
--- /dev/null
+void main () {
+ with ("string ") {
+ assert (chomp () == "string");
+ }
+}
--- /dev/null
+void main () {
+ int i = 0;
+ string s;
+
+ with (i) {
+ s = i.to_string ();
+ }
+
+ assert (s == "0");
+}
valaversionattribute.vala \
valavoidtype.vala \
valawhilestatement.vala \
+ valawithstatement.vala \
valayieldstatement.vala \
$(NULL)
public virtual void visit_unlock_statement (UnlockStatement stmt) {
}
+ /**
+ * Visit operation called for with statements.
+ *
+ * @param stmt a with statement
+ */
+ public virtual void visit_with_statement (WithStatement stmt) {
+ }
+
/**
* Visit operation called for delete statements.
*
write_newline ();
}
+ public override void visit_with_statement (WithStatement stmt) {
+ write_indent ();
+ write_string ("with (");
+ stmt.expression.accept (this);
+ write_string (")");
+ stmt.body.accept (this);
+ write_string (";");
+ write_newline ();
+ }
+
public override void visit_yield_statement (YieldStatement y) {
write_indent ();
write_string ("yield");
}
}
+ public override void visit_with_statement (WithStatement stmt) {
+ if (unreachable (stmt)) {
+ return;
+ }
+
+ current_block.add_node (stmt.expression);
+
+ handle_errors (stmt.expression);
+
+ stmt.body.accept_children (this);
+ }
+
public override void visit_if_statement (IfStatement stmt) {
if (unreachable (stmt)) {
return;
bool may_access_instance_members = false;
bool may_access_klass_members = false;
+ var visited_types = new ArrayList<DataType> ();
+
symbol_reference = null;
if (qualified) {
symbol_reference = SemanticAnalyzer.symbol_lookup_inherited (sym, member_name);
+ if (symbol_reference == null && sym is WithStatement) {
+ unowned WithStatement w = (WithStatement) sym;
+
+ var variable_type = w.with_variable.variable_type;
+ if (variable_type is PointerType) {
+ variable_type = ((PointerType) variable_type).base_type;
+ }
+ visited_types.add (variable_type);
+
+ symbol_reference = variable_type.get_member (member_name);
+ if (symbol_reference != null) {
+ inner = new MemberAccess (null, w.with_variable.name, source_reference);
+ inner.check (context);
+ may_access_instance_members = true;
+ }
+ }
+
if (symbol_reference == null && sym is TypeSymbol && may_access_instance_members) {
// used for generated to_string methods in enums
symbol_reference = this_parameter.variable_type.get_member (member_name);
}
}
- Report.error (source_reference, "The name `%s' does not exist in the context of `%s'%s".printf (member_name, base_type_name, base_type_package));
+ string visited_types_string = "";
+ foreach (var type in visited_types) {
+ visited_types_string += " or `%s'".printf (type.to_string ());
+ }
+
+ Report.error (source_reference, "The name `%s' does not exist in the context of `%s'%s%s".printf (member_name, base_type_name, base_type_package, visited_types_string));
value_type = new InvalidType ();
return false;
} else if (symbol_reference.error) {
case TokenType.VOLATILE:
case TokenType.WEAK:
case TokenType.WHILE:
+ case TokenType.WITH:
case TokenType.YIELD:
next ();
return;
case TokenType.DELETE:
stmt = parse_delete_statement ();
break;
+ case TokenType.WITH:
+ stmt = parse_with_statement ();
+ break;
case TokenType.VAR:
is_decl = true;
parse_local_variable_declarations (block);
expect (TokenType.SEMICOLON);
}
- LocalVariable parse_local_variable (DataType? variable_type) throws ParseError {
+ LocalVariable parse_local_variable (DataType? variable_type, bool expect_initializer = false) throws ParseError {
var begin = get_location ();
string id = parse_identifier ();
var type = parse_inline_array_type (variable_type);
Expression initializer = null;
if (accept (TokenType.ASSIGN)) {
initializer = parse_expression ();
+ } else if (expect_initializer) {
+ report_parse_error (new ParseError.SYNTAX ("expected initializer"));
+ prev ();
+ initializer = new InvalidExpression ();
}
return new LocalVariable (type, id, initializer, src);
}
return new DeleteStatement (expr, src);
}
+ Statement? parse_with_statement () throws ParseError {
+ var begin = get_location ();
+ expect (TokenType.WITH);
+ expect (TokenType.OPEN_PARENS);
+ var expr_or_decl = get_location ();
+
+ LocalVariable? local = null;
+
+ // Try "with (expr)"
+ Expression expr = parse_expression ();
+ if (!accept (TokenType.CLOSE_PARENS)) {
+ // Try "with (var identifier = expr)"
+ rollback (expr_or_decl);
+ DataType variable_type;
+ if (accept (TokenType.UNOWNED) && accept (TokenType.VAR)) {
+ variable_type = new VarType (false);
+ } else {
+ rollback (expr_or_decl);
+ if (accept (TokenType.VAR)) {
+ variable_type = new VarType ();
+ } else {
+ variable_type = parse_type (true, true);
+ }
+ }
+ local = parse_local_variable (variable_type, true);
+ expr = local.initializer;
+ expect (TokenType.CLOSE_PARENS);
+ }
+
+ var src = get_src (begin);
+ var body = parse_embedded_statement ("with", false);
+ return new WithStatement (local, expr, body, src);
+ }
+
string parse_attribute_value () throws ParseError {
switch (current ()) {
case TokenType.NULL:
if (matches (begin, "void")) return TokenType.VOID;
break;
case 'w':
- if (matches (begin, "weak")) return TokenType.WEAK;
+ switch (begin[1]) {
+ case 'e':
+ if (matches (begin, "weak")) return TokenType.WEAK;
+ break;
+ case 'i':
+ if (matches (begin, "with")) return TokenType.WITH;
+ break;
+ }
break;
}
break;
list.accept_children (this);
}
+ public override void visit_with_statement (WithStatement stmt) {
+ stmt.accept_children (this);
+ }
+
public override void visit_expression_statement (ExpressionStatement stmt) {
if (stmt.checked) {
return;
VOLATILE,
WEAK,
WHILE,
+ WITH,
YIELD;
public unowned string to_string () {
case VOLATILE: return "`volatile'";
case WEAK: return "`weak'";
case WHILE: return "`while'";
+ case WITH: return "`with'";
case YIELD: return "`yield'";
default: return "unknown token";
}
--- /dev/null
+/* valawithtatement.vala
+ *
+ * Copyright (C) 2020 Nick Schrader
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Authors:
+ * Nick Schrader <nick.schrader@mailbox.org>
+ */
+
+public class Vala.WithStatement : Block {
+ /**
+ * Expression representing the type of body's dominant scope.
+ */
+ public Expression expression {
+ get { return _expression; }
+ private set {
+ _expression = value;
+ _expression.parent_node = this;
+ }
+ }
+
+ /**
+ * Specifies the with-variable.
+ */
+ public LocalVariable? with_variable { get; private set; }
+
+ /**
+ * The block which dominant scope is type of expression.
+ */
+ public Block body {
+ get { return _body; }
+ private set {
+ _body = value;
+ _body.parent_node = this;
+ }
+ }
+
+ Expression _expression;
+ Block _body;
+
+ public WithStatement (LocalVariable? variable, Expression expression, Block body, SourceReference? source_reference = null) {
+ base (source_reference);
+ this.with_variable = variable;
+ this.expression = expression;
+ this.body = body;
+ }
+
+ public override void accept (CodeVisitor visitor) {
+ visitor.visit_with_statement (this);
+ }
+
+ public override void accept_children (CodeVisitor visitor) {
+ if (expression.symbol_reference == with_variable) {
+ expression.accept (visitor);
+ }
+
+ if (with_variable != null) {
+ with_variable.accept (visitor);
+ }
+
+ body.accept (visitor);
+ }
+
+ bool is_object_or_value_type (DataType? type) {
+ if (type == null) {
+ return false;
+ } else if (type is PointerType) {
+ var pointer_type = (PointerType) type;
+ return is_object_or_value_type (pointer_type.base_type) && expression is PointerIndirection;
+ } else {
+ return type is ObjectType || type is ValueType;
+ }
+ }
+
+ public override bool check (CodeContext context) {
+ if (checked) {
+ return !error;
+ }
+
+ checked = true;
+
+ if (!expression.check (context)) {
+ error = true;
+ return false;
+ }
+
+ if (!is_object_or_value_type (expression.value_type)) {
+ error = true;
+ Report.error (expression.source_reference, "with statement expects an object or basic type");
+ return false;
+ }
+
+ var local_var = expression.symbol_reference as LocalVariable;
+ if (with_variable != null || local_var == null) {
+ if (with_variable == null) {
+ local_var = new LocalVariable (expression.value_type.copy (), get_temp_name (), expression, source_reference);
+ } else {
+ local_var = with_variable;
+ }
+ body.insert_statement (0, new DeclarationStatement (local_var, source_reference));
+ }
+ with_variable = local_var;
+
+ var old_symbol = context.analyzer.current_symbol;
+ owner = context.analyzer.current_symbol.scope;
+ context.analyzer.current_symbol = this;
+
+ if (!body.check (context)) {
+ error = true;
+ }
+
+ context.analyzer.current_symbol = old_symbol;
+
+ return !error;
+ }
+
+ public override void emit (CodeGenerator codegen) {
+ if (expression.symbol_reference == with_variable) {
+ expression.emit (codegen);
+ }
+ body.emit (codegen);
+ }
+
+ public override void get_error_types (Collection<DataType> collection, SourceReference? source_reference = null) {
+ if (source_reference == null) {
+ source_reference = this.source_reference;
+ }
+ expression.get_error_types (collection, source_reference);
+ body.get_error_types (collection, source_reference);
+ }
+
+ public override void get_defined_variables (Collection<Variable> collection) {
+ if (expression.symbol_reference != with_variable) {
+ collection.add (with_variable);
+ }
+ }
+}