Repository: VKCOM/noverify
Pull request #1126 (2021-08-29 10:04:52)

linter: improved non-mixed mode types


Patch:
diff --git a/src/linter/utils.go b/src/linter/utils.go
index d611aae..522782f 100644
--- a/src/linter/utils.go
+++ b/src/linter/utils.go
@@ -257,7 +257,7 @@ func resolveMethodCall(sc *meta.Scope, st *meta.ClassParseState, customTypes []s
 	)
 
 	methodCallerType := solver.ExprTypeCustom(sc, st, e.Variable, customTypes)
-	if !strictMixed && isMixedLikeType(methodCallerType) {
+	if !strictMixed && isMixedLikeTypes(methodCallerType) {
 		return methodCallInfo{
 			canAnalyze:        true,
 			callerTypeIsMixed: true,
@@ -412,7 +412,7 @@ func resolvePropertyFetch(sc *meta.Scope, st *meta.ClassParseState, customTypes
 	var info meta.PropertyInfo
 
 	propertyFetchType := solver.ExprTypeCustom(sc, st, e.Variable, customTypes)
-	if !strictMixed && isMixedLikeType(propertyFetchType) {
+	if !strictMixed && isMixedLikeTypes(propertyFetchType) {
 		return propertyFetchInfo{
 			canAnalyze:        true,
 			callerTypeIsMixed: true,
@@ -642,14 +642,18 @@ func cloneRulesForFile(filename string, ruleSet *rules.ScopedSet) *rules.ScopedS
 	return &clone
 }
 
-func isMixedLikeType(typ types.Map) bool {
-	if typ.Is("null") || typ.Is("mixed") ||
-		typ.Is("object") || typ.Is("unknown_from_list") ||
-		typ.Is("undefined") || typ.Is("\\stdClass") {
-		return true
-	}
+func isMixedLikeTypes(typ types.Map) bool {
+	containsNonMixedLike := typ.Find(func(singleType string) bool {
+		return !isMixedLikeType(singleType)
+	})
+
+	return !containsNonMixedLike
+}
 
-	return false
+func isMixedLikeType(typ string) bool {
+	return typ == "mixed" || typ == "object" ||
+		typ == "unknown_from_list" || typ == "undefined" ||
+		typ == "\\stdClass" || typ == "null" || typ == "void"
 }
 
 // List taken from https://wiki.php.net/rfc/context_sensitive_lexer


Test patch:
diff --git a/src/tests/checkers/oop_test.go b/src/tests/checkers/oop_test.go
index 437cded..7e6a498 100644
--- a/src/tests/checkers/oop_test.go
+++ b/src/tests/checkers/oop_test.go
@@ -1080,7 +1080,6 @@ func TestInstanceOf(t *testing.T) {
 		}
 	}`)
 	test.Expect = []string{
-		`Call to undefined method {void}->get2()`,
 		`Call to undefined method {\Element}->callUndefinedMethod()`,
 	}
 	linttest.RunFilterMatch(test, "undefinedMethod")
diff --git a/src/tests/checkers/strict_mixed_test.go b/src/tests/checkers/strict_mixed_test.go
index f34b6c7..7c4f094 100644
--- a/src/tests/checkers/strict_mixed_test.go
+++ b/src/tests/checkers/strict_mixed_test.go
@@ -41,6 +41,22 @@ function f() {
 function f(stdClass $a) {
   $a->f();
 }
+
+function f(mixed|object $a) {
+  $a->f();
+}
+
+function f(mixed|null $a) {
+  $a->f();
+}
+
+function f(?mixed $a) {
+  $a->f();
+}
+
+function f(stdClass|null $a) {
+  $a->f();
+}
 `,
 	)
 	test.Expect = []string{
@@ -52,6 +68,10 @@ function f(stdClass $a) {
 		"Call to undefined method {\\Foo}->f()",
 		"Call to undefined method {null}->f()",
 		"Call to undefined method {\\stdClass}->f()",
+		"Call to undefined method {mixed|object}->f()",
+		"Call to undefined method {mixed|null}->f()",
+		"Call to undefined method {mixed|null}->f()",
+		"Call to undefined method {\\stdClass|null}->f()",
 	}
 	test.RunAndMatch()
 }
@@ -90,6 +110,22 @@ function f() {
 function f(stdClass $a) {
   $a->f();
 }
+
+function f(mixed|object $a) {
+  $a->f();
+}
+
+function f(mixed|null $a) {
+  $a->f();
+}
+
+function f(?mixed $a) {
+  $a->f();
+}
+
+function f(stdClass|null $a) {
+  $a->f();
+}
 `,
 	)
 	test.Expect = []string{