Repository: VKCOM/noverify
Pull request #815 (2020-11-11 07:47:18)

linter: interpret `any` type as `mixed`
This is needed so the KPHP code can work correctly.

To avoid confusion when NoVerify is used with PHP,
this behavior is implemented under the new -kphp flag.

Signed-off-by: Iskander Sharipov <quasilyte@gmail.com>


Patch:
diff --git a/src/cmd/flags.go b/src/cmd/flags.go
index b65ea41..737e998 100644
--- a/src/cmd/flags.go
+++ b/src/cmd/flags.go
@@ -51,6 +51,7 @@ type cmdlineArguments struct {
 	phpExtensionsArg string
 
 	gitignore bool
+	kphp      bool
 
 	gitPushArg                 string
 	gitAuthorsWhitelist        string
@@ -132,6 +133,8 @@ func bindFlags(ruleSets []*rules.Set, args *cmdlineArguments) {
 
 	flag.BoolVar(&args.gitignore, "gitignore", false,
 		"If enabled, noverify tries to use .gitignore files to exclude matched ignored files from the analysis")
+	flag.BoolVar(&args.kphp, "kphp", false,
+		"If enabled, treat the code as KPHP")
 
 	flag.StringVar(&args.gitRepo, "git", "", "Path to git repository to analyze")
 	flag.StringVar(&args.mutable.gitCommitFrom, "git-commit-from", "", "Analyze changes between commits <git-commit-from> and <git-commit-to>")
diff --git a/src/cmd/linter_runner.go b/src/cmd/linter_runner.go
index 6592090..609c6c8 100644
--- a/src/cmd/linter_runner.go
+++ b/src/cmd/linter_runner.go
@@ -98,6 +98,7 @@ func (l *linterRunner) Init(ruleSets []*rules.Set, args *cmdlineArguments) error
 	}
 
 	linter.ApplyQuickFixes = l.args.fix
+	linter.KPHP = l.args.kphp
 
 	if err := l.compileRegexes(); err != nil {
 		return err
diff --git a/src/linter/conf.go b/src/linter/conf.go
index c278753..151b1ba 100644
--- a/src/linter/conf.go
+++ b/src/linter/conf.go
@@ -22,6 +22,9 @@ var (
 
 	ApplyQuickFixes bool
 
+	// KPHP tells whether we're working in KPHP-compatible mode.
+	KPHP bool
+
 	CacheDir string
 
 	// TypoFixer is a rule set for English typos correction.
diff --git a/src/linter/types.go b/src/linter/types.go
index e77b7ec..d8c03ab 100644
--- a/src/linter/types.go
+++ b/src/linter/types.go
@@ -240,6 +240,15 @@ func (n typeNormalizer) normalizeType(typ *meta.Type) {
 		return
 	}
 
+	if typ.Elem == "any" && KPHP {
+		// `any` is a special KPHP type that is more-or-less
+		// identical to `mixed|object`. In PHP, `mixed` already covers
+		// objects, so there is no need to add `object`.
+		// See https://php.watch/versions/8.0/mixed-type
+		typ.Elem = "mixed"
+		return
+	}
+
 	switch typ.Elem {
 	case "array":
 		// Rewrite `array` to `mixed[]`.


Test patch:
diff --git a/src/tests/checkers/kphp_test.go b/src/tests/checkers/kphp_test.go
new file mode 100644
index 0000000..165b273
--- /dev/null
+++ b/src/tests/checkers/kphp_test.go
@@ -0,0 +1,20 @@
+package checkers_test
+
+import (
+	"testing"
+
+	"github.com/VKCOM/noverify/src/linter"
+	"github.com/VKCOM/noverify/src/linttest"
+)
+
+func TestArrayAccessForAny(t *testing.T) {
+	linter.KPHP = true
+	linttest.SimpleNegativeTest(t, `<?php
+	/** @return any */
+	function get_any() {
+		return [];
+	}
+	$any = get_any();
+	$_ = $any[0];`)
+	linter.KPHP = false
+}
diff --git a/src/tests/exprtype/kphp_test.go b/src/tests/exprtype/kphp_test.go
new file mode 100644
index 0000000..c4c173a
--- /dev/null
+++ b/src/tests/exprtype/kphp_test.go
@@ -0,0 +1,31 @@
+package exprtype_test
+
+import (
+	"testing"
+
+	"github.com/VKCOM/noverify/src/linter"
+)
+
+func TestExprTypeAny(t *testing.T) {
+	code := `<?php
+/** @return any */
+function get_any() {
+  return 10;
+}
+
+/** @return any[][] */
+function get_any_arr() {
+  return [[1]];
+}
+
+exprtype(get_any(), 'int|mixed');
+exprtype(get_any_arr(), 'int[][]|mixed[][]');
+`
+	runKPHPExprTypeTest(t, &exprTypeTestParams{code: code})
+}
+
+func runKPHPExprTypeTest(t *testing.T, params *exprTypeTestParams) {
+	linter.KPHP = true
+	runExprTypeTest(t, params)
+	linter.KPHP = false
+}