Repository: VKCOM/noverify
Pull request #687 (2020-08-14 14:00:19)

ir/normalize: add list and class const normalization rules
* All list-assignments become keyed.
* Class constants are folded like normal constants.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>


Patch:
diff --git a/src/constfold/eval.go b/src/constfold/eval.go
index f91c334..dfd2185 100644
--- a/src/constfold/eval.go
+++ b/src/constfold/eval.go
@@ -19,6 +19,20 @@ func Eval(st *meta.ClassParseState, e ir.Node) meta.ConstantValue {
 	case *ir.ParenExpr:
 		return Eval(st, e.Expr)
 
+	case *ir.ClassConstFetchExpr:
+		if !meta.IsIndexingComplete() {
+			return meta.UnknownValue
+		}
+		className, ok := solver.GetClassName(st, e.Class)
+		if !ok {
+			return meta.UnknownValue
+		}
+		info, _, ok := solver.FindConstant(className, e.ConstantName.Value)
+		if !ok {
+			return meta.UnknownValue
+		}
+		return info.Value
+
 	case *ir.ConstFetchExpr:
 		switch e.Constant.Value {
 		case `true`:
diff --git a/src/ir/normalize/normalize.go b/src/ir/normalize/normalize.go
index b530162..d62ce30 100644
--- a/src/ir/normalize/normalize.go
+++ b/src/ir/normalize/normalize.go
@@ -3,6 +3,7 @@ package normalize
 import (
 	"fmt"
 	"sort"
+	"strconv"
 	"strings"
 
 	"github.com/VKCOM/noverify/src/constfold"
@@ -395,6 +396,19 @@ func (norm *normalizer) normalizedExpr(e ir.Node) ir.Node {
 	case *ir.ListExpr:
 		// `list(...)` => `[...]`
 		e.ShortSyntax = true
+		// `list($x, $y)` => `list(0 => $x, 1 => $y)`
+		// `list(, $x)` => `list(1 => $x)`,
+		items := e.Items[:0]
+		for i, item := range e.Items {
+			if item.Val == nil {
+				continue
+			}
+			if item.Key == nil {
+				item.Key = &ir.Lnumber{Value: strconv.Itoa(i)}
+			}
+			items = append(items, item)
+		}
+		e.Items = items
 	}
 
 	return e
@@ -480,6 +494,8 @@ func constToIR(v meta.ConstantValue) ir.Node {
 	switch v.Type {
 	case meta.Integer:
 		return &ir.Lnumber{Value: fmt.Sprint(value)}
+	case meta.Float:
+		return &ir.Dnumber{Value: fmt.Sprint(value)}
 	case meta.String:
 		return &ir.String{Value: value.(string)}
 	default:


Test patch:
diff --git a/src/ir/normalize/normalize_test.go b/src/ir/normalize/normalize_test.go
index 2943618..21fe231 100644
--- a/src/ir/normalize/normalize_test.go
+++ b/src/ir/normalize/normalize_test.go
@@ -69,12 +69,12 @@ func TestNormalizeStmtList(t *testing.T) {
 
 		{
 			`self::$x; self::myconst;`,
-			`Foo::$x; Foo::myconst;`,
+			`\Foo::$x; \Foo::myconst;`,
 		},
 	}
 
 	conf := Config{}
-	st := &meta.ClassParseState{CurrentClass: `Foo`}
+	st := &meta.ClassParseState{CurrentClass: `\Foo`}
 	for _, test := range tests {
 
 		list, err := parseStmtList(test.orig)
@@ -103,7 +103,7 @@ func runNormalizeTests(t *testing.T, tests []normalizationTest) {
 	t.Helper()
 
 	conf := Config{}
-	st := &meta.ClassParseState{CurrentClass: `Foo`}
+	st := &meta.ClassParseState{CurrentClass: `\Foo`}
 	for _, test := range tests {
 		n, _, err := parseutil.ParseStmt([]byte(test.orig))
 		if err != nil {
@@ -164,7 +164,7 @@ func TestNormalizeExpr(t *testing.T) {
 		{`$x = $x - 1`, `--$v0`},
 
 		{`array(1, 2)`, `[1, 2]`},
-		{`list($x, $y) = f()`, `[$v0, $v1] = f()`},
+		{`list($x, $y) = f()`, `[0 => $v0, 1 => $v1] = f()`},
 
 		{`$x ? $x : $y`, `$v0 ?: $v1`},
 		{`f() ? f() : $y`, `f() ? f() : $v0`},
@@ -174,6 +174,12 @@ func TestNormalizeExpr(t *testing.T) {
 		{`(1 + 5) + 1`, `7`},
 		{`1 + 2 * 4`, `9`},
 		{`(1 + 2) * 4`, `12`},
+
+		// List assignments.
+		{`list(, $x) = f()`, `[1 => $v0] = f()`},
+		{`list(, $x, , $y) = f()`, `[1 => $v0, 3 => $v1] = f()`},
+		{`[5 => $x] = f()`, `[5 => $v0] = f()`},
+		{`[$x, ] = f()`, `[0 => $v0] = f()`},
 	})
 }
 
@@ -182,6 +188,10 @@ func TestNormalizeExprAfterIndexing(t *testing.T) {
 const ZERO = 0;
 const HELLO_WORLD = 'hello, world';
 const LOCALHOST = "127.0.0.1";
+
+class Foo {
+  const FOO_VALUE = 53.001122334455665;
+}
 `)
 	meta.SetIndexingComplete(true)
 	defer meta.SetIndexingComplete(false)
@@ -190,6 +200,7 @@ const LOCALHOST = "127.0.0.1";
 		{`ZERO`, `0`},
 		{`HELLO_WORLD`, `'hello, world'`},
 		{`LOCALHOST`, `'127.0.0.1'`},
+		{`self::FOO_VALUE`, `53.001122334455665`},
 	})
 }