]> git.ipfire.org Git - thirdparty/vala.git/commitdiff
parser: Properly handle chained equality expressions
authorwszqkzqk <wszqkzqk@qq.com>
Tue, 13 Dec 2022 13:56:53 +0000 (21:56 +0800)
committerRico Tzschichholz <ricotz@ubuntu.com>
Thu, 22 Dec 2022 16:13:52 +0000 (17:13 +0100)
Fixes https://gitlab.gnome.org/GNOME/vala/issues/1385

codegen/valaccodebasemodule.vala
tests/Makefile.am
tests/parser/chained-equality-type-invalid.test [new file with mode: 0644]
tests/parser/chained-equality.c-expected [new file with mode: 0644]
tests/parser/chained-equality.vala [new file with mode: 0644]
vala/valabinaryexpression.vala
vala/valaparser.vala

index 2969679c927f24e45a85bf364bd500c1e1035a4c..c8301c82141af3a09bbafde45917e09a26d8034c 100644 (file)
@@ -5842,8 +5842,17 @@ public abstract class Vala.CCodeBaseModule : CodeGenerator {
                        }
                }
 
-               bool is_string_comparison = !(expr.left.value_type is NullType) && expr.left.value_type.compatible (string_type)
-                       && !(expr.right.value_type is NullType) && expr.right.value_type.compatible (string_type);
+               bool is_string_comparison = false;
+               if (!(expr.right.value_type is NullType) && expr.right.value_type.compatible (string_type)) {
+                       if (!(expr.left.value_type is NullType) && expr.left.value_type.compatible (string_type)) {
+                               is_string_comparison = true;
+                       } else if (expr.is_chained) {
+                               unowned BinaryExpression lbe = (BinaryExpression) expr.left;
+                               if (!(lbe.right.value_type is NullType) && lbe.right.value_type.compatible (string_type)) {
+                                       is_string_comparison = true;
+                               }
+                       }
+               }
                bool has_string_literal = (expr.left is StringLiteral || expr.right is StringLiteral);
 
                if (is_string_comparison || (has_string_literal && expr.operator != BinaryOperator.PLUS)) {
index 94346acf3fe4f46c158ee6e08d07b44818646d72..407bffc5bbea938f0324b71d657548c85afbf330 100644 (file)
@@ -931,6 +931,8 @@ TESTS = \
        parser/attribute-missing-literal.test \
        parser/attribute-wrong-number.test \
        parser/block-delimiter-missing.test \
+       parser/chained-equality-type-invalid.test \
+       parser/chained-equality.vala \
        parser/constant-owned.test \
        parser/constant-local-owned.test \
        parser/constructor-class-exists.test \
diff --git a/tests/parser/chained-equality-type-invalid.test b/tests/parser/chained-equality-type-invalid.test
new file mode 100644 (file)
index 0000000..e280245
--- /dev/null
@@ -0,0 +1,8 @@
+Invalid Code
+
+void main () {
+    var foo = "world";
+    var bar = 0;
+    if (foo == "world" != 0) {
+    }
+}
diff --git a/tests/parser/chained-equality.c-expected b/tests/parser/chained-equality.c-expected
new file mode 100644 (file)
index 0000000..3e59b5d
--- /dev/null
@@ -0,0 +1,212 @@
+/* parser_chained_equality.c generated by valac, the Vala compiler
+ * generated from parser_chained_equality.vala, do not modify */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define _g_free0(var) (var = (g_free (var), NULL))
+#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
+#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; }
+#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; }
+#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg);
+
+static void _vala_main (void);
+
+static void
+_vala_main (void)
+{
+       {
+               gint foo = 0;
+               gint bar = 0;
+               gint _tmp0_;
+               foo = 123;
+               bar = 123;
+               _tmp0_ = bar;
+               _vala_assert ((foo == _tmp0_) && (_tmp0_ == 123), "foo == bar == 123");
+       }
+       {
+               gint foo = 0;
+               gint bar = 0;
+               gint _tmp1_;
+               foo = 111;
+               bar = 222;
+               _tmp1_ = bar;
+               _vala_assert ((foo != _tmp1_) && (_tmp1_ == 222), "foo != bar == 222");
+       }
+       {
+               gint foo = 0;
+               gint bar = 0;
+               gint _tmp2_;
+               foo = 111;
+               bar = 111;
+               _tmp2_ = bar;
+               _vala_assert ((foo == _tmp2_) && (_tmp2_ != 222), "foo == bar != 222");
+       }
+       {
+               gint foo = 0;
+               gint bar = 0;
+               gint _tmp3_;
+               gint _tmp4_;
+               foo = 111;
+               bar = 111;
+               _tmp3_ = foo;
+               _tmp4_ = bar;
+               _vala_assert (((0 < _tmp3_) && (_tmp3_ == _tmp4_)) && (_tmp4_ < 222), "0 < foo == bar < 222");
+       }
+       {
+               gint a = 0;
+               gint b = 0;
+               gint c = 0;
+               gint _tmp5_;
+               gint _tmp6_;
+               a = 111;
+               b = 111;
+               c = 111;
+               _tmp5_ = b;
+               _tmp6_ = c;
+               _vala_assert (((a == _tmp5_) && (_tmp5_ == _tmp6_)) && (_tmp6_ == 111), "a == b == c == 111");
+       }
+       {
+               gint a = 0;
+               gint b = 0;
+               gint c = 0;
+               gint _tmp7_;
+               gint _tmp8_;
+               a = 111;
+               b = 222;
+               c = 333;
+               _tmp7_ = b;
+               _tmp8_ = c;
+               _vala_assert (((a != _tmp7_) && (_tmp7_ != _tmp8_)) && (_tmp8_ != 123), "a != b != c != 123");
+       }
+       {
+               gchar* foo = NULL;
+               gchar* _tmp9_;
+               gchar* bar = NULL;
+               gchar* _tmp10_;
+               gchar* _tmp11_;
+               _tmp9_ = g_strdup ("world");
+               foo = _tmp9_;
+               _tmp10_ = g_strdup ("hello");
+               bar = _tmp10_;
+               _tmp11_ = bar;
+               _vala_assert ((g_strcmp0 (foo, _tmp11_) > 0) && (g_strcmp0 (_tmp11_, "hello") == 0), "foo > bar == \"hello\"");
+               _g_free0 (bar);
+               _g_free0 (foo);
+       }
+       {
+               gchar* a = NULL;
+               gchar* _tmp12_;
+               gchar* b = NULL;
+               gchar* _tmp13_;
+               gchar* c = NULL;
+               gchar* _tmp14_;
+               gchar* _tmp15_;
+               gchar* _tmp16_;
+               gchar* _tmp17_;
+               gchar* _tmp18_;
+               _tmp12_ = g_strdup ("hello");
+               a = _tmp12_;
+               _tmp13_ = g_strdup ("hello");
+               b = _tmp13_;
+               _tmp14_ = g_strdup ("hello");
+               c = _tmp14_;
+               _tmp15_ = a;
+               _tmp16_ = b;
+               _tmp17_ = c;
+               _tmp18_ = "hello";
+               _vala_assert (((((g_strcmp0 ("hello", _tmp15_) == 0) && (g_strcmp0 (_tmp15_, _tmp16_) <= 0)) && (g_strcmp0 (_tmp16_, _tmp17_) == 0)) && (g_strcmp0 (_tmp17_, _tmp18_) == 0)) && (g_strcmp0 (_tmp18_, "hello") <= 0), "\"hello\" == a <= b == c == \"hello\" <= \"hello\"");
+               _g_free0 (c);
+               _g_free0 (b);
+               _g_free0 (a);
+       }
+       {
+               gchar* foo = NULL;
+               gchar* _tmp19_;
+               gchar* bar = NULL;
+               gchar* _tmp20_;
+               gchar* _tmp21_;
+               _tmp19_ = g_strdup ("h");
+               foo = _tmp19_;
+               _tmp20_ = g_strdup ("h");
+               bar = _tmp20_;
+               _tmp21_ = bar;
+               _vala_assert ((g_strcmp0 (foo, _tmp21_) == 0) && (g_strcmp0 (_tmp21_, "hello") < 0), "foo == bar < \"hello\"");
+               _g_free0 (bar);
+               _g_free0 (foo);
+       }
+       {
+               gchar* foo = NULL;
+               gchar* _tmp22_;
+               gchar* bar = NULL;
+               gchar* _tmp23_;
+               gchar* _tmp24_;
+               _tmp22_ = g_strdup ("hello");
+               foo = _tmp22_;
+               _tmp23_ = g_strdup ("world");
+               bar = _tmp23_;
+               _tmp24_ = bar;
+               _vala_assert ((g_strcmp0 (foo, _tmp24_) != 0) && (g_strcmp0 (_tmp24_, "world") == 0), "foo != bar == \"world\"");
+               _g_free0 (bar);
+               _g_free0 (foo);
+       }
+       {
+               gchar* foo = NULL;
+               gchar* _tmp25_;
+               gchar* bar = NULL;
+               gchar* _tmp26_;
+               gchar* _tmp27_;
+               _tmp25_ = g_strdup ("world");
+               foo = _tmp25_;
+               _tmp26_ = g_strdup ("world");
+               bar = _tmp26_;
+               _tmp27_ = bar;
+               _vala_assert ((g_strcmp0 (foo, _tmp27_) == 0) && (g_strcmp0 (_tmp27_, "hello") != 0), "foo == bar != \"hello\"");
+               _g_free0 (bar);
+               _g_free0 (foo);
+       }
+       {
+               gchar* foo = NULL;
+               gchar* _tmp28_;
+               gchar* bar = NULL;
+               gchar* _tmp29_;
+               gchar* _tmp30_;
+               gint _tmp31_;
+               _tmp28_ = g_strdup ("world");
+               foo = _tmp28_;
+               _tmp29_ = g_strdup ("world");
+               bar = _tmp29_;
+               _tmp30_ = bar;
+               _tmp31_ = 0;
+               _vala_assert (((g_strcmp0 (foo, _tmp30_) == 0) && (g_strcmp0 (_tmp30_, "hello") != 0)) == ((0 == _tmp31_) && (_tmp31_ <= 1)), "(foo == bar != \"hello\") == (0 == 0 <= 1)");
+               _g_free0 (bar);
+               _g_free0 (foo);
+       }
+       {
+               gchar* foo = NULL;
+               gchar* _tmp32_;
+               gchar* bar = NULL;
+               gchar* _tmp33_;
+               gchar* _tmp34_;
+               gint _tmp35_;
+               _tmp32_ = g_strdup ("world");
+               foo = _tmp32_;
+               _tmp33_ = g_strdup ("world");
+               bar = _tmp33_;
+               _tmp34_ = bar;
+               _tmp35_ = 1;
+               _vala_assert (((g_strcmp0 (foo, _tmp34_) == 0) && (g_strcmp0 (_tmp34_, "hello") == 0)) == ((1 == _tmp35_) && (_tmp35_ <= 0)), "(foo == bar == \"hello\") == (1 == 1 <= 0)");
+               _g_free0 (bar);
+               _g_free0 (foo);
+       }
+}
+
+int
+main (int argc,
+      char ** argv)
+{
+       _vala_main ();
+       return 0;
+}
+
diff --git a/tests/parser/chained-equality.vala b/tests/parser/chained-equality.vala
new file mode 100644 (file)
index 0000000..ce734af
--- /dev/null
@@ -0,0 +1,70 @@
+void main () {
+    {
+        var foo = 123;
+        var bar = 123;
+        assert (foo == bar == 123);
+    }
+    {
+        var foo = 111;
+        var bar = 222;
+        assert (foo != bar == 222);
+    }
+    {
+        var foo = 111;
+        var bar = 111;
+        assert (foo == bar != 222);
+    }
+    {
+        var foo = 111;
+        var bar = 111;
+        assert (0 < foo == bar < 222);
+    }
+    {
+        var a = 111;
+        var b = 111;
+        var c = 111;
+        assert (a == b == c == 111);
+    }
+    {
+        var a = 111;
+        var b = 222;
+        var c = 333;
+        assert (a != b != c != 123);
+    }
+    {
+        var foo = "world";
+        var bar = "hello";
+        assert (foo > bar == "hello");
+    }
+    {
+        var a = "hello";
+        var b = "hello";
+        var c = "hello";
+        assert ("hello" == a <= b == c == "hello" <= "hello");
+    }
+    {
+        var foo = "h";
+        var bar = "h";
+        assert (foo == bar < "hello");
+    }
+    {
+        var foo = "hello";
+        var bar = "world";
+        assert (foo != bar == "world");
+    }
+    {
+        var foo = "world";
+        var bar = "world";
+        assert (foo == bar != "hello");
+    }
+    {
+        var foo = "world";
+        var bar = "world";
+        assert ((foo == bar != "hello") == (0 == 0 <= 1)); // true == true
+    }
+    {
+        var foo = "world";
+        var bar = "world";
+        assert ((foo == bar == "hello") == (1 == 1 <= 0)); // false == false
+    }
+}
index 6545e1cff1e86fbc6fcde21345ea429bbf789a28..d627e2942707c61ec1221a34b81e819f886f77ea 100644 (file)
@@ -467,7 +467,12 @@ public class Vala.BinaryExpression : Expression {
                                DataType resulting_type;
 
                                if (is_chained) {
-                                       var lbe = (BinaryExpression) left;
+                                       unowned BinaryExpression lbe = (BinaryExpression) left;
+                                       if (lbe.right.value_type.compatible (context.analyzer.string_type)
+                                           && right.value_type.compatible (context.analyzer.string_type)) {
+                                               value_type = context.analyzer.bool_type;
+                                               break;
+                                       }
                                        resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
                                } else {
                                        resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
@@ -475,7 +480,14 @@ public class Vala.BinaryExpression : Expression {
 
                                if (resulting_type == null) {
                                        error = true;
-                                       Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'", left.value_type.to_string (), right.value_type.to_string ());
+                                       unowned DataType left_type;
+                                       if (is_chained) {
+                                               unowned BinaryExpression lbe = (BinaryExpression) left;
+                                               left_type = lbe.right.value_type;
+                                       } else {
+                                               left_type = left.value_type;
+                                       }
+                                       Report.error (source_reference, "Relational operation not supported for types `%s' and `%s'", left_type.to_string (), right.value_type.to_string ());
                                        return false;
                                }
 
@@ -515,17 +527,31 @@ public class Vala.BinaryExpression : Expression {
                                }
                        }
 
-                       if (!right.value_type.compatible (left.value_type)
-                           && !left.value_type.compatible (right.value_type)) {
-                               Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), left.value_type.to_string ());
-                               error = true;
-                               return false;
+                       DataType resulting_type;
+                       if (is_chained) {
+                               unowned BinaryExpression lbe = (BinaryExpression) left;
+                               resulting_type = context.analyzer.get_arithmetic_result_type (lbe.right.target_type, right.target_type);
+                               if (!right.value_type.compatible (lbe.right.value_type)
+                                   && !lbe.right.value_type.compatible (right.value_type)) {
+                                       Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), lbe.right.value_type.to_string ());
+                                       error = true;
+                                       return false;
+                               }
+                       } else {
+                               resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
+                               if (!right.value_type.compatible (left.value_type)
+                                   && !left.value_type.compatible (right.value_type)) {
+                                       Report.error (source_reference, "Equality operation: `%s' and `%s' are incompatible", right.value_type.to_string (), left.value_type.to_string ());
+                                       error = true;
+                                       return false;
+                               }
                        }
 
-                       var resulting_type = context.analyzer.get_arithmetic_result_type (left.target_type, right.target_type);
                        if (resulting_type != null) {
                                // numeric operation
-                               left.target_type = resulting_type.copy ();
+                               if (!is_chained) {
+                                       left.target_type = resulting_type.copy ();
+                               }
                                right.target_type = resulting_type.copy ();
                        }
 
index d932b8b2de4bb828c5ca95e519e12729a057b012..25769ec54a2dfdd14dec968e68b83ea12e273479 100644 (file)
@@ -1452,6 +1452,8 @@ public class Vala.Parser : CodeVisitor {
                        case BinaryOperator.LESS_THAN:
                        case BinaryOperator.LESS_THAN_OR_EQUAL:
                        case BinaryOperator.GREATER_THAN_OR_EQUAL:
+                       case BinaryOperator.EQUALITY:
+                       case BinaryOperator.INEQUALITY:
                                next ();
                                var right = parse_type_check_expression ();
                                if (first) {
@@ -1485,32 +1487,11 @@ public class Vala.Parser : CodeVisitor {
                return left;
        }
 
-       Expression parse_equality_expression () throws ParseError {
-               var begin = get_location ();
-               var left = parse_relational_expression ();
-               bool found = true;
-               while (found) {
-                       var operator = get_binary_operator (current ());
-                       switch (operator) {
-                       case BinaryOperator.EQUALITY:
-                       case BinaryOperator.INEQUALITY:
-                               next ();
-                               var right = parse_relational_expression ();
-                               left = new BinaryExpression (operator, left, right, get_src (begin));
-                               break;
-                       default:
-                               found = false;
-                               break;
-                       }
-               }
-               return left;
-       }
-
        Expression parse_and_expression () throws ParseError {
                var begin = get_location ();
-               var left = parse_equality_expression ();
+               var left = parse_relational_expression ();
                while (accept (TokenType.BITWISE_AND)) {
-                       var right = parse_equality_expression ();
+                       var right = parse_relational_expression ();
                        left = new BinaryExpression (BinaryOperator.BITWISE_AND, left, right, get_src (begin));
                }
                return left;