From 9fc3d725fa70fc1057e02b5866f76c4a5dab159f Mon Sep 17 00:00:00 2001
From: Richardas Kuchinskas <36065987+Hidanio@users.noreply.github.com>
Date: Tue, 4 Mar 2025 15:35:20 +0300
Subject: [PATCH] linter: null safety for functions, methods and properties for
classes (#1245)
---
docs/checkers_doc.md | 22 +-
src/linter/block.go | 203 ++++++++++++++-
src/linter/cache.go | 73 +++---
src/linter/report.go | 10 +
src/linter/root.go | 13 +
src/meta/metainfo.go | 1 +
src/tests/checkers/null_safety_test.go | 243 ++++++++++++++++++
src/tests/checkers/regexpsimplify_test.go | 203 +++++++++++++++
src/tests/checkers/regexpvet_test.go | 33 +++
src/tests/checkers/trigger_error_test.go | 1 +
src/tests/golden/golden_test.go | 14 +-
.../golden/testdata/embeddedrules/golden.txt | 12 +
src/tests/golden/testdata/idn/golden.txt | 24 ++
src/tests/golden/testdata/math/golden.txt | 24 ++
src/tests/golden/testdata/mustache/golden.txt | 144 +++++++++++
src/tests/golden/testdata/qrcode/golden.txt | 60 +++++
src/types/predicates.go | 17 ++
17 files changed, 1052 insertions(+), 45 deletions(-)
create mode 100644 src/tests/checkers/null_safety_test.go
diff --git a/docs/checkers_doc.md b/docs/checkers_doc.md
index 1b67701b9..587630d20 100644
--- a/docs/checkers_doc.md
+++ b/docs/checkers_doc.md
@@ -4,7 +4,7 @@
| Total checks | Checks enabled by default | Disabled checks by default | Autofixable checks |
| ------------ | ------------------------- | -------------------------- | ------------------ |
-| 106 | 88 | 18 | 15 |
+| 107 | 89 | 18 | 15 |
## Table of contents
- Enabled by default
@@ -60,6 +60,7 @@
- [`newAbstract` checker](#newabstract-checker)
- [`nonPublicInterfaceMember` checker](#nonpublicinterfacemember-checker)
- [`notExplicitNullableParam` checker (autofixable)](#notexplicitnullableparam-checker)
+ - [`notNullSafety` checker](#notnullsafety-checker)
- [`offBy1` checker (autofixable)](#offby1-checker)
- [`oldStyleConstructor` checker](#oldstyleconstructor-checker)
- [`paramClobber` checker](#paramclobber-checker)
@@ -1199,6 +1200,25 @@ function f(?string $str = null);
+### `notNullSafety` checker
+
+#### Description
+
+Report not nullsafety call
+
+#### Non-compliant code:
+```php
+function f(A $klass);
+ f(null);
+```
+
+#### Compliant code:
+```php
+reported not safety call
+```
+
+
+
### `offBy1` checker
> Auto fix available
diff --git a/src/linter/block.go b/src/linter/block.go
index fa3a95f93..a8dc54d5c 100644
--- a/src/linter/block.go
+++ b/src/linter/block.go
@@ -1027,7 +1027,202 @@ func (b *blockWalker) handleIssetDimFetch(e *ir.ArrayDimFetchExpr) {
}
}
+func nullSafetyRealParamForCheck(fn meta.FuncInfo, paramIndex int, haveVariadic bool) meta.FuncParam {
+ if haveVariadic && paramIndex >= len(fn.Params)-1 {
+ return fn.Params[len(fn.Params)-1]
+ }
+ return fn.Params[paramIndex]
+}
+
+func formatSlashesFuncName(fn meta.FuncInfo) string {
+ return strings.TrimPrefix(fn.Name, "\\")
+}
+
+func (b *blockWalker) checkNullSafetyCallArgsF(args []ir.Node, fn meta.FuncInfo) {
+ if fn.Params == nil || fn.Name == "" {
+ return
+ }
+ haveVariadic := fn.Flags&meta.FuncVariadic != 0
+
+ for i, arg := range args {
+ if arg == nil {
+ continue
+ }
+
+ // If there are more arguments than declared and function is not variadic, ignore extra arguments.
+ if !haveVariadic && i > len(fn.Params)-1 {
+ return
+ }
+
+ switch a := arg.(*ir.Argument).Expr.(type) {
+ case *ir.SimpleVar:
+ b.checkSimpleVarNullSafety(arg, fn, i, a, haveVariadic)
+ case *ir.ConstFetchExpr:
+ b.checkConstFetchNullSafety(arg, fn, i, a, haveVariadic)
+ case *ir.ArrayDimFetchExpr:
+ b.checkArrayDimFetchNullSafety(arg, fn, i, a, haveVariadic)
+ case *ir.ListExpr:
+ b.checkListExprNullSafety(arg, fn, i, a, haveVariadic)
+ case *ir.PropertyFetchExpr:
+ b.checkPropertyFetchNullSafety(a, fn, i, haveVariadic)
+ }
+ }
+}
+
+func (b *blockWalker) checkSimpleVarNullSafety(arg ir.Node, fn meta.FuncInfo, paramIndex int, variable *ir.SimpleVar, haveVariadic bool) {
+ varInfo, ok := b.ctx.sc.GetVar(variable)
+ if !ok {
+ return
+ }
+
+ param := nullSafetyRealParamForCheck(fn, paramIndex, haveVariadic)
+ if haveVariadic && paramIndex >= len(fn.Params)-1 {
+ // For variadic parameter check, if type is mixed then skip.
+ if types.IsTypeMixed(param.Typ) {
+ return
+ }
+ }
+ paramAllowsNull := types.IsTypeNullable(param.Typ)
+ varIsNullable := types.IsTypeNullable(varInfo.Type)
+ if varIsNullable && !paramAllowsNull {
+ b.report(arg, LevelWarning, "notNullSafety",
+ "not null safety call in function %s signature of param %s",
+ formatSlashesFuncName(fn), param.Name)
+ }
+}
+
+func (b *blockWalker) checkConstFetchNullSafety(arg ir.Node, fn meta.FuncInfo, paramIndex int, constExpr *ir.ConstFetchExpr, haveVariadic bool) {
+ constVal := constExpr.Constant.Value
+ isNull := constVal == "null"
+
+ param := nullSafetyRealParamForCheck(fn, paramIndex, haveVariadic)
+ if haveVariadic && paramIndex >= len(fn.Params)-1 {
+ if types.IsTypeMixed(param.Typ) {
+ return
+ }
+ }
+ paramAllowsNull := types.IsTypeNullable(param.Typ)
+ if isNull && !paramAllowsNull {
+ b.report(arg, LevelWarning, "notNullSafety",
+ "null passed to non-nullable parameter %s in function %s",
+ param.Name, formatSlashesFuncName(fn))
+ }
+}
+
+func (b *blockWalker) checkArrayDimFetchNullSafety(arg ir.Node, fn meta.FuncInfo, paramIndex int, arrayExpr *ir.ArrayDimFetchExpr, haveVariadic bool) {
+ baseVar, ok := arrayExpr.Variable.(*ir.SimpleVar)
+ if !ok {
+ return
+ }
+
+ varInfo, found := b.ctx.sc.GetVar(baseVar)
+ if !found {
+ return
+ }
+
+ param := nullSafetyRealParamForCheck(fn, paramIndex, haveVariadic)
+ if haveVariadic && paramIndex >= len(fn.Params)-1 {
+ if types.IsTypeMixed(param.Typ) {
+ return
+ }
+ }
+ paramAllowsNull := types.IsTypeNullable(param.Typ)
+ if types.IsTypeNullable(varInfo.Type) && !paramAllowsNull {
+ b.report(arg, LevelWarning, "notNullSafety",
+ "potential null array access in parameter %s of function %s",
+ param.Name, formatSlashesFuncName(fn))
+ }
+}
+
+func (b *blockWalker) checkListExprNullSafety(arg ir.Node, fn meta.FuncInfo, paramIndex int, listExpr *ir.ListExpr, haveVariadic bool) {
+ for _, item := range listExpr.Items {
+ if item == nil {
+ continue
+ }
+
+ if item.Key != nil {
+ b.checkNullSafetyCallArgsF([]ir.Node{item.Key}, fn)
+ }
+
+ if item.Val != nil {
+ param := nullSafetyRealParamForCheck(fn, paramIndex, haveVariadic)
+ if simpleVar, ok := item.Val.(*ir.SimpleVar); ok {
+ varInfo, found := b.ctx.sc.GetVar(simpleVar)
+ if found && types.IsTypeNullable(varInfo.Type) && !types.IsTypeNullable(param.Typ) {
+ b.report(arg, LevelWarning, "notNullSafety",
+ "potential null value in list assignment for param %s in function %s",
+ param.Name, formatSlashesFuncName(fn))
+ }
+ }
+ b.checkNullSafetyCallArgsF([]ir.Node{item.Val}, fn)
+ }
+ }
+}
+
+func (b *blockWalker) getPropertyComputedType(expr *ir.PropertyFetchExpr) (meta.ClassInfo, types.Map) {
+ baseCall, ok := expr.Variable.(*ir.SimpleVar)
+ if !ok {
+ return meta.ClassInfo{}, types.Map{}
+ }
+
+ varInfo, ok := b.ctx.sc.GetVar(baseCall)
+ if !ok {
+ return meta.ClassInfo{}, types.Map{}
+ }
+
+ classInfo, ok := b.r.ctx.st.Info.GetClass(varInfo.Type.String())
+ if !ok {
+ return meta.ClassInfo{}, types.Map{}
+ }
+
+ property, ok := expr.Property.(*ir.Identifier)
+ if !ok {
+ return meta.ClassInfo{}, types.Map{}
+ }
+
+ propertyInfoFromClass := classInfo.Properties[property.Value]
+ return classInfo, propertyInfoFromClass.Typ
+}
+
+func (b *blockWalker) checkPropertyFetchNullSafety(expr *ir.PropertyFetchExpr, fn meta.FuncInfo, paramIndex int, haveVariadic bool) {
+ // Recursively check the left part of the chain if it is also a property fetch.
+ if nested, ok := expr.Variable.(*ir.PropertyFetchExpr); ok {
+ b.checkPropertyFetchNullSafety(nested, fn, paramIndex, haveVariadic)
+ }
+
+ classInfo, propType := b.getPropertyComputedType(expr)
+ if classInfo.Name == "" || propType.Empty() {
+ return
+ }
+
+ prp, ok := expr.Property.(*ir.Identifier)
+ if !ok {
+ return
+ }
+
+ isPrpNullable := types.IsTypeNullable(propType)
+ param := nullSafetyRealParamForCheck(fn, paramIndex, haveVariadic)
+ if haveVariadic && paramIndex >= len(fn.Params)-1 {
+ if types.IsTypeMixed(param.Typ) {
+ return
+ }
+ paramAllowsNull := types.IsTypeNullable(param.Typ)
+ if isPrpNullable && !paramAllowsNull {
+ b.report(expr, LevelWarning, "notNullSafety",
+ "potential null dereference when accessing property '%s'", prp.Value)
+ }
+ return
+ }
+
+ paramAllowsNull := types.IsTypeNullable(param.Typ)
+ if isPrpNullable && !paramAllowsNull {
+ b.report(expr, LevelWarning, "notNullSafety",
+ "potential null dereference when accessing property '%s'", prp.Value)
+ }
+}
func (b *blockWalker) handleCallArgs(args []ir.Node, fn meta.FuncInfo) {
+ b.checkNullSafetyCallArgsF(args, fn)
+
for i, arg := range args {
if i >= len(fn.Params) {
arg.Walk(b)
@@ -1388,13 +1583,19 @@ func (b *blockWalker) enterClosure(fun *ir.ClosureExpr, haveThis bool, thisType
b.r.meta.Functions = meta.NewFunctionsMap()
}
+ var funcFlags meta.FuncFlags
+
+ if params.isVariadic {
+ funcFlags |= meta.FuncVariadic
+ }
+
b.r.meta.Functions.Set(name, meta.FuncInfo{
Params: params.params,
Name: name,
Pos: b.r.getElementPos(fun),
Typ: returnTypes.Immutable(),
MinParamsCnt: params.minParamsCount,
- Flags: 0,
+ Flags: funcFlags,
ExitFlags: exitFlags,
DeprecationInfo: doc.Deprecation,
})
diff --git a/src/linter/cache.go b/src/linter/cache.go
index b18c7a5ab..bf66e43b8 100644
--- a/src/linter/cache.go
+++ b/src/linter/cache.go
@@ -17,42 +17,43 @@ import (
//
// Version log:
//
-// 27 - added Static field to meta.FuncInfo
-// 28 - array type parsed as mixed[]
-// 29 - updated type inference for ClassConstFetch
-// 30 - resolve ClassConstFetch to a wrapped type string
-// 31 - fixed plus operator type inference for arrays
-// 32 - replaced Static:bool with Flags:uint8 in meta.FuncInfo
-// 33 - support parsing of array and list
-// 34 - support parsing of ?ClassName as "ClassName|null"
-// 35 - added Flags:uint8 to meta.ClassInfo
-// 36 - added FuncAbstract bit to FuncFlags
-// added FuncFinal bit to FuncFlags
-// added ClassFinal bit to ClassFlags
-// FuncInfo now stores original function name
-// ClassInfo now stores original class name
-// 37 - added ClassShape bit to ClassFlags
-// changed meta.scopeVar bool fields representation
-// 38 - replaced TypesMap.immutable:bool with flags:uint8.
-// added mapPrecise flag to mark precise type maps.
-// 39 - added new field Value in ConstantInfo
-// 40 - changed string const value storage (no quotes)
-// 41 - const-folding affected const definition values
-// 42 - bool-typed consts are now stored in meta info
-// 43 - define'd const values stored in cache
-// 44 - rename ConstantInfo => ConstInfo
-// 45 - added Mixins field to meta.ClassInfo
-// 46 - changed the way of inferring the return type of functions and methods
-// 47 - forced cache version invalidation due to the #921
-// 48 - renamed meta.TypesMap to types.Map; this affects gob encoding
-// 49 - for shape, names are now generated using the keys that make up this shape
-// 50 - added Flags field for meta.PropertyInfo
-// 51 - added anonymous classes
-// 52 - renamed all PhpDoc and Phpdoc with PHPDoc
-// 53 - added DeprecationInfo for functions and methods and support for some attributes
-// 54 - forced cache version invalidation due to the #1165
-// 55 - updated go version 1.16 -> 1.21
-const cacheVersion = 55
+// 27 - added Static field to meta.FuncInfo
+// 28 - array type parsed as mixed[]
+// 29 - updated type inference for ClassConstFetch
+// 30 - resolve ClassConstFetch to a wrapped type string
+// 31 - fixed plus operator type inference for arrays
+// 32 - replaced Static:bool with Flags:uint8 in meta.FuncInfo
+// 33 - support parsing of array and list
+// 34 - support parsing of ?ClassName as "ClassName|null"
+// 35 - added Flags:uint8 to meta.ClassInfo
+// 36 - added FuncAbstract bit to FuncFlags
+// added FuncFinal bit to FuncFlags
+// added ClassFinal bit to ClassFlags
+// FuncInfo now stores original function name
+// ClassInfo now stores original class name
+// 37 - added ClassShape bit to ClassFlags
+// changed meta.scopeVar bool fields representation
+// 38 - replaced TypesMap.immutable:bool with flags:uint8.
+// added mapPrecise flag to mark precise type maps.
+// 39 - added new field Value in ConstantInfo
+// 40 - changed string const value storage (no quotes)
+// 41 - const-folding affected const definition values
+// 42 - bool-typed consts are now stored in meta info
+// 43 - define'd const values stored in cache
+// 44 - rename ConstantInfo => ConstInfo
+// 45 - added Mixins field to meta.ClassInfo
+// 46 - changed the way of inferring the return type of functions and methods
+// 47 - forced cache version invalidation due to the #921
+// 48 - renamed meta.TypesMap to types.Map; this affects gob encoding
+// 49 - for shape, names are now generated using the keys that make up this shape
+// 50 - added Flags field for meta.PropertyInfo
+// 51 - added anonymous classes
+// 52 - renamed all PhpDoc and Phpdoc with PHPDoc
+// 53 - added DeprecationInfo for functions and methods and support for some attributes
+// 54 - forced cache version invalidation due to the #1165
+// 55 - updated go version 1.16 -> 1.21
+// 56 - added isVariadic to meta.FuncInfo
+const cacheVersion = 56
var (
errWrongVersion = errors.New("Wrong cache version")
diff --git a/src/linter/report.go b/src/linter/report.go
index 9ef7e1ff5..26c247f7c 100644
--- a/src/linter/report.go
+++ b/src/linter/report.go
@@ -25,6 +25,16 @@ func addBuiltinCheckers(reg *CheckersRegistry) {
After: `$s = strip_tags($s, '
')`,
},
+ {
+ Name: "notNullSafety",
+ Default: true,
+ Quickfix: false,
+ Comment: "Report not nullsafety call",
+ Before: `function f(A $klass);
+ f(null);`,
+ After: `reported not safety call`,
+ },
+
{
Name: "notExplicitNullableParam",
Default: true,
diff --git a/src/linter/root.go b/src/linter/root.go
index b7fe6fa86..03cdf2a7a 100644
--- a/src/linter/root.go
+++ b/src/linter/root.go
@@ -486,6 +486,7 @@ type parseFuncParamsResult struct {
params []meta.FuncParam
paramsTypeHint map[string]types.Map
minParamsCount int
+ isVariadic bool
}
func (d *rootWalker) parseFuncParams(params []ir.Node, docblockParams phpdoctypes.ParamsMap, sc *meta.Scope, closureSolver *solver.ClosureCallerInfo) (res parseFuncParamsResult) {
@@ -496,6 +497,7 @@ func (d *rootWalker) parseFuncParams(params []ir.Node, docblockParams phpdoctype
minArgs := 0
parsedParams := make([]meta.FuncParam, 0, len(params))
paramHints := make(map[string]types.Map, len(params))
+ isVariadic := false
if closureSolver != nil && solver.IsClosureUseFunction(closureSolver.Name) {
return d.parseFuncArgsForCallback(params, sc, closureSolver)
@@ -535,6 +537,8 @@ func (d *rootWalker) parseFuncParams(params []ir.Node, docblockParams phpdoctype
// Handle variadic.
if param.Variadic {
paramType = paramType.Map(types.WrapArrayOf)
+
+ isVariadic = true
}
// Append @param type.
@@ -565,6 +569,7 @@ func (d *rootWalker) parseFuncParams(params []ir.Node, docblockParams phpdoctype
params: parsedParams,
paramsTypeHint: paramHints,
minParamsCount: minArgs,
+ isVariadic: isVariadic,
}
}
@@ -664,6 +669,10 @@ func (d *rootWalker) enterFunction(fun *ir.FunctionStmt) bool {
if solver.SideEffectFreeFunc(d.scope(), d.ctx.st, nil, fun.Stmts) {
funcFlags |= meta.FuncPure
}
+ if funcParams.isVariadic {
+ funcFlags |= meta.FuncVariadic
+ }
+
d.meta.Functions.Set(nm, meta.FuncInfo{
Params: funcParams.params,
Name: nm,
@@ -1135,6 +1144,10 @@ func (d *rootWalker) enterClassMethod(meth *ir.ClassMethodStmt) bool {
if !insideInterface && !modif.abstract && solver.SideEffectFreeFunc(d.scope(), d.ctx.st, nil, stmts) {
funcFlags |= meta.FuncPure
}
+ if funcParams.isVariadic {
+ funcFlags |= meta.FuncVariadic
+ }
+
class.Methods.Set(nm, meta.FuncInfo{
Params: funcParams.params,
Name: nm,
diff --git a/src/meta/metainfo.go b/src/meta/metainfo.go
index 160d7a504..38f88c004 100644
--- a/src/meta/metainfo.go
+++ b/src/meta/metainfo.go
@@ -15,6 +15,7 @@ const (
FuncFinal
// FuncFromAnnotation is set if the function is described in the class annotation.
FuncFromAnnotation
+ FuncVariadic
)
type PropertyFlags uint8
diff --git a/src/tests/checkers/null_safety_test.go b/src/tests/checkers/null_safety_test.go
new file mode 100644
index 000000000..bb8ac88a7
--- /dev/null
+++ b/src/tests/checkers/null_safety_test.go
@@ -0,0 +1,243 @@
+package checkers
+
+import (
+ "testing"
+
+ "github.com/VKCOM/noverify/src/linttest"
+)
+
+func TestFunctionPassingNull(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+test(null);
+`)
+ test.Expect = []string{
+ "null passed to non-nullable parameter a in function test",
+ }
+ test.RunAndMatch()
+}
+
+func TestPropertyFetchNullSafety(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+$a = new A();
+test($a->b);
+`)
+ test.Expect = []string{
+ "potential null dereference when accessing property 'b'",
+ }
+ test.RunAndMatch()
+}
+
+func TestChainedPropertyAccessNullSafety(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`c = new C();
+ }
+}
+
+class C {
+ public string $value = 'Hello';
+}
+
+/**
+ * Function expecting an object of class C.
+ */
+function test(C $c): void {
+ echo $c->value;
+}
+
+$a = new A();
+// $a->b is null, so accessing $a->b->c leads to a potential null dereference.
+test($a->b->c);
+`)
+ test.Expect = []string{
+ "potential null dereference when accessing property",
+ }
+ test.RunAndMatch()
+}
+
+// After fix issue with not correct checking existing variable after unpacking - should fail
+func TestListExprNullSafety(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+list($m, list($n, $o)) = [new A(), [new A(), null]];
+
+test($m);
+test($n);
+test($o);
+`)
+ test.Expect = []string{
+ "not null safety call in function test signature of param",
+ "Cannot find referenced variable $o",
+ "Cannot find referenced variable $n",
+ }
+ test.RunAndMatch()
+}
+
+func TestArrayDimFetchNullSafety(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+$arr = [new A(), null];
+test($arr[1]);
+`)
+ test.Expect = []string{
+ "potential null array access in parameter a of function test",
+ }
+ test.RunAndMatch()
+}
+
+func TestVariadicParameterNullSafety(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value, "\n";
+ }
+}
+
+testVariadic(new A(), null);
+`)
+ test.Expect = []string{
+ "null passed to non-nullable parameter a in function testVariadic",
+ }
+ test.RunAndMatch()
+}
+
+// TODO: After realisation Control Flow Graph (CFG) и Data Flow Graph (DFG) this test must fail
+func TestIfNullCheckSafe(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+$v = null;
+if ($v == null) {
+ // Correctly assign a new instance when $v is null.
+ $v = new A();
+}
+test($v); // Should be safe.
+`)
+
+ test.Expect = []string{
+ `not null safety call in function test signature of param`,
+ }
+ test.RunAndMatch()
+}
+
+func TestIfNullCheckUnsafe(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+$v = null;
+if ($v == null) {
+ // No assignment is done here – $v remains null.
+}
+test($v); // Should trigger a null safety error.
+`)
+ test.Expect = []string{
+ "not null safety call in function test signature of param",
+ }
+ test.RunAndMatch()
+}
+
+func TestIfNotNullCheck(t *testing.T) {
+ test := linttest.NewSuite(t)
+ test.AddFile(`value;
+}
+
+$v = new A();
+if ($v !== null) {
+ // $v is known to be non-null.
+ test($v); // Should be safe.
+}
+`)
+
+ test.Expect = []string{}
+ test.RunAndMatch()
+}
diff --git a/src/tests/checkers/regexpsimplify_test.go b/src/tests/checkers/regexpsimplify_test.go
index c501a0d4e..d7a264597 100644
--- a/src/tests/checkers/regexpsimplify_test.go
+++ b/src/tests/checkers/regexpsimplify_test.go
@@ -19,6 +19,9 @@ function f($s) {
test.Expect = []string{
`May re-write '~(?P[0-9])~' as '~(?P\d)~'`,
`May re-write '~(?[0-9])~' as '~(?\d)~'`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
}
test.RunAndMatch()
}
@@ -39,6 +42,9 @@ function f($s) {
`May re-write '/^write([-]?\d\d*)?$/i' as '/^write(-?\d+)?$/i'`,
`May re-write '/[a-a]/' as '/a/'`,
`May re-write '/[a-a]*?/' as '/a*?/'`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match_all signature of param subject`,
+ `not null safety call in function preg_split signature of param subject`,
}
test.RunAndMatch()
}
@@ -113,6 +119,7 @@ function unrepeat($s) {
preg_match('/x{0,}/', $s);
}
`)
+
test.Expect = []string{
`May re-write '/(?:x)/' as '/x/'`,
`May re-write '/(?:[abc])/' as '/[abc]/'`,
@@ -138,6 +145,30 @@ function unrepeat($s) {
`May re-write '/[0-9]{1,}/' as '/\d+/'`,
`May re-write '/[0-9]{0,1}/' as '/\d?/'`,
`May re-write '/x{0,}/' as '/x*/'`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
}
test.RunAndMatch()
}
@@ -158,6 +189,10 @@ function f($s) {
`May re-write '/^http:\/\/~/' as '@^http://~@'`,
`May re-write '/^http:\/\/~@/' as '#^http://~@#'`,
`May re-write '@mail\@gmail\.ru@' as '/mail@gmail\.ru/'`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
}
test.RunAndMatch()
}
@@ -354,5 +389,173 @@ function _6($s) {
preg_match('/(?i)\([^)]*mix[^)]*\)$/', $s);
}
`)
+ test.Expect = []string{
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ }
test.RunAndMatch()
}
diff --git a/src/tests/checkers/regexpvet_test.go b/src/tests/checkers/regexpvet_test.go
index c97770d64..66b26220d 100644
--- a/src/tests/checkers/regexpvet_test.go
+++ b/src/tests/checkers/regexpvet_test.go
@@ -26,6 +26,10 @@ function parseErrors($s) {
`parse error: 'a' is not a valid delimiter`,
`parse error: can't find '>' ending delimiter`,
`parse error: unterminated '['`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
}
test.RunAndMatch()
}
@@ -204,6 +208,9 @@ function f($s) {
`'\s' intersects with '\t' in [\s\t]`,
`'e' is duplicated in [com|org|edu|net]`,
`'\w' intersects with '\d' in [\w\d]`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
}
test.RunAndMatch()
}
@@ -273,5 +280,31 @@ function goodAnchors($s) {
preg_match('~(?i)(?:)(^| )\S+~', $s);
}
`)
+ test.Expect = []string{
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ `not null safety call in function preg_match signature of param subject`,
+ }
test.RunAndMatch()
}
diff --git a/src/tests/checkers/trigger_error_test.go b/src/tests/checkers/trigger_error_test.go
index a5d909af2..82432fab2 100644
--- a/src/tests/checkers/trigger_error_test.go
+++ b/src/tests/checkers/trigger_error_test.go
@@ -50,6 +50,7 @@ echo 'unreachable';
`)
test.Expect = []string{
`Unreachable code`,
+ `not null safety call in function trigger_error signature of param message`,
}
test.RunAndMatch()
}
diff --git a/src/tests/golden/golden_test.go b/src/tests/golden/golden_test.go
index 88657c9ec..19dddd6e1 100644
--- a/src/tests/golden/golden_test.go
+++ b/src/tests/golden/golden_test.go
@@ -96,7 +96,7 @@ func TestGolden(t *testing.T) {
{
Name: "parsedown",
- Disable: []string{"missingPhpdoc", "arraySyntax", "phpAliases"},
+ Disable: []string{"missingPhpdoc", "arraySyntax", "phpAliases", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/pcre/pcre.php`,
`stubs/phpstorm-stubs/mbstring/mbstring.php`,
@@ -105,7 +105,7 @@ func TestGolden(t *testing.T) {
{
Name: "underscore",
- Disable: []string{"missingPhpdoc", "phpAliases"},
+ Disable: []string{"missingPhpdoc", "phpAliases", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/pcre/pcre.php`,
},
@@ -113,7 +113,7 @@ func TestGolden(t *testing.T) {
{
Name: "phprocksyd",
- Disable: []string{"missingPhpdoc"},
+ Disable: []string{"missingPhpdoc", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/standard/basic.php`,
`stubs/phpstorm-stubs/pcntl/pcntl.php`,
@@ -124,7 +124,7 @@ func TestGolden(t *testing.T) {
{
Name: "flysystem",
- Disable: []string{"redundantCast"},
+ Disable: []string{"redundantCast", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/pcre/pcre.php`,
`stubs/phpstorm-stubs/SPL/SPL.php`,
@@ -141,7 +141,7 @@ func TestGolden(t *testing.T) {
{
Name: "inflector",
- Disable: []string{"missingPhpdoc"},
+ Disable: []string{"missingPhpdoc", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/pcre/pcre.php`,
`stubs/phpstorm-stubs/SPL/SPL.php`,
@@ -151,7 +151,7 @@ func TestGolden(t *testing.T) {
{
Name: "options-resolver",
- Disable: []string{"missingPhpdoc"},
+ Disable: []string{"missingPhpdoc", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/SPL/SPL.php`,
`stubs/phpstorm-stubs/Reflection/Reflection.php`,
@@ -164,7 +164,7 @@ func TestGolden(t *testing.T) {
{
Name: "twitter-api-php",
- Disable: []string{"missingPhpdoc", "arraySyntax"},
+ Disable: []string{"missingPhpdoc", "arraySyntax", "notNullSafety"},
Deps: []string{
`stubs/phpstorm-stubs/pcre/pcre.php`,
`stubs/phpstorm-stubs/SPL/SPL.php`,
diff --git a/src/tests/golden/testdata/embeddedrules/golden.txt b/src/tests/golden/testdata/embeddedrules/golden.txt
index e0418748c..50f964aa8 100644
--- a/src/tests/golden/testdata/embeddedrules/golden.txt
+++ b/src/tests/golden/testdata/embeddedrules/golden.txt
@@ -13,9 +13,21 @@ WARNING argsOrder: Potentially incorrect haystack and needle arguments order at
WARNING argsOrder: Potentially incorrect replacement and subject arguments order at testdata/embeddedrules/argsOrder.php:32
$_ = preg_replace($pat, $subj, 'replacement');
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function explode signature of param separator at testdata/embeddedrules/argsOrder.php:41
+ $_ = explode($s, '/');
+ ^^
WARNING argsOrder: Potentially incorrect delimiter and string arguments order at testdata/embeddedrules/argsOrder.php:41
$_ = explode($s, '/');
^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function explode signature of param string at testdata/embeddedrules/argsOrder.php:45
+ $_ = explode('/', $s);
+ ^^
+WARNING notNullSafety: not null safety call in function explode signature of param separator at testdata/embeddedrules/argsOrder.php:46
+ $_ = explode($delim, $s);
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function explode signature of param string at testdata/embeddedrules/argsOrder.php:46
+ $_ = explode($delim, $s);
+ ^^
WARNING argsOrder: Potentially incorrect replace and string arguments order at testdata/embeddedrules/argsOrder.php:50
$_ = str_replace($search, $subj, ' ');
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tests/golden/testdata/idn/golden.txt b/src/tests/golden/testdata/idn/golden.txt
index 17aace3ae..564ebefaf 100644
--- a/src/tests/golden/testdata/idn/golden.txt
+++ b/src/tests/golden/testdata/idn/golden.txt
@@ -7,6 +7,9 @@ MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at tes
WARNING errorSilence: Don't use @, silencing errors is bad practice at testdata/idn/idn.php:67
@trigger_error('idn_to_ascii(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function mb_strtolower signature of param string at testdata/idn/idn.php:71
+ $domain = mb_strtolower($domain, 'utf-8');
+ ^^^^^^^
MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at testdata/idn/idn.php:87
$idna_info = array(
@@ -19,9 +22,15 @@ MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at tes
WARNING errorSilence: Don't use @, silencing errors is bad practice at testdata/idn/idn.php:99
@trigger_error('idn_to_utf8(): INTL_IDNA_VARIANT_2003 is deprecated', E_USER_DEPRECATED);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function explode signature of param string at testdata/idn/idn.php:102
+ $parts = explode('.', $domain);
+ ^^^^^^^
MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at testdata/idn/idn.php:119
$idna_info = array(
+WARNING notNullSafety: not null safety call in function mb_strlen signature of param string at testdata/idn/idn.php:152
+ $length = mb_strlen($input, 'utf-8');
+ ^^^^^^
MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at testdata/idn/idn.php:195
'all' => array(),
^^^^^^^
@@ -34,6 +43,12 @@ MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at tes
MAYBE arraySyntax: Use the short form '[]' instead of the old 'array()' at testdata/idn/idn.php:194
$codePoints = array(
+WARNING notNullSafety: not null safety call in function mb_strlen signature of param string at testdata/idn/idn.php:200
+ $length = mb_strlen($input, 'utf-8');
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function mb_substr signature of param string at testdata/idn/idn.php:202
+ $char = mb_substr($input, $i, 1, 'utf-8');
+ ^^^^^^
MAYBE redundantCast: Expression already has int type at testdata/idn/idn.php:233
$delta = (int) ($delta / 35);
^^^^^^^^^^^^^
@@ -43,6 +58,15 @@ MAYBE assignOp: Could rewrite as `$k += 36` at testdata/idn/idn.php:234
MAYBE redundantCast: Expression already has int type at testdata/idn/idn.php:237
return $k + (int) (36 * $delta / ($delta + 38));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function strrpos signature of param haystack at testdata/idn/idn.php:247
+ $pos = strrpos($input, '-');
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/idn/idn.php:249
+ $output = substr($input, 0, $pos++);
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function strlen signature of param string at testdata/idn/idn.php:255
+ $inputLength = \strlen($input);
+ ^^^^^^
MAYBE assignOp: Could rewrite as `$n += (int) ($i / $outputLength)` at testdata/idn/idn.php:274
$n = $n + (int) ($i / $outputLength);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tests/golden/testdata/math/golden.txt b/src/tests/golden/testdata/math/golden.txt
index ae3f8b9b4..80fa121a5 100644
--- a/src/tests/golden/testdata/math/golden.txt
+++ b/src/tests/golden/testdata/math/golden.txt
@@ -1,18 +1,42 @@
WARNING unused: Variable $a is unused (use $_ to ignore this inspection or specify --unused-var-regex flag) at testdata/math/src/BigDecimal.php:292
[$a, $b] = $this->scaleValues($this, $that);
^^
+WARNING notNullSafety: not null safety call in function of signature of param value at testdata/math/src/BigDecimal.php:588
+ $that = BigNumber::of($that);
+ ^^^^^
MAYBE trailingComma: Last element in a multi-line array should have a trailing comma at testdata/math/src/BigInteger.php:435
new BigInteger($remainder)
^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function of signature of param value at testdata/math/src/BigInteger.php:742
+ $that = BigNumber::of($that);
+ ^^^^^
MAYBE ternarySimplify: Could rewrite as `$matches['fractional'] ?? ''` at testdata/math/src/BigNumber.php:90
$fractional = isset($matches['fractional']) ? $matches['fractional'] : '';
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function isLessThan signature of param that at testdata/math/src/BigNumber.php:173
+ if ($min === null || $value->isLessThan($min)) {
+ ^^^^
+WARNING notNullSafety: not null safety call in function isGreaterThan signature of param that at testdata/math/src/BigNumber.php:205
+ if ($max === null || $value->isGreaterThan($max)) {
+ ^^^^
+WARNING notNullSafety: not null safety call in function add signature of param a at testdata/math/src/BigNumber.php:241
+ $sum = self::add($sum, $value);
+ ^^^^
+WARNING notNullSafety: not null safety call in function minus signature of param that at testdata/math/src/BigRational.php:365
+ return $this->minus($that)->getSign();
+ ^^^^^
MAYBE implicitModifiers: Specify the access modifier for \Brick\Math\Internal\Calculator::powmod method explicitly at testdata/math/src/Internal/Calculator.php:260
abstract function powmod(string $base, string $exp, string $mod) : string;
^^^^^^
MAYBE assignOp: Could rewrite as `$number ^= $xor` at testdata/math/src/Internal/Calculator.php:622
$number = $number ^ $xor;
^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function gmp_strval signature of param num at testdata/math/src/Internal/Calculator/GmpCalculator.php:66
+ \gmp_strval($q),
+ ^^
+WARNING notNullSafety: not null safety call in function gmp_strval signature of param num at testdata/math/src/Internal/Calculator/GmpCalculator.php:67
+ \gmp_strval($r)
+ ^^
MAYBE trailingComma: Last element in a multi-line array should have a trailing comma at testdata/math/src/Internal/Calculator/GmpCalculator.php:67
\gmp_strval($r)
^^^^^^^^^^^^^^^
diff --git a/src/tests/golden/testdata/mustache/golden.txt b/src/tests/golden/testdata/mustache/golden.txt
index c5667a990..9987a66b7 100644
--- a/src/tests/golden/testdata/mustache/golden.txt
+++ b/src/tests/golden/testdata/mustache/golden.txt
@@ -1,3 +1,6 @@
+WARNING notNullSafety: not null safety call in function realpath signature of param path at testdata/mustache/src/Mustache/Autoloader.php:39
+ $realDir = realpath($baseDir);
+ ^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$baseDir ?: 0` at testdata/mustache/src/Mustache/Autoloader.php:56
$key = $baseDir ? $baseDir : 0;
^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,24 +28,87 @@ WARNING errorSilence: Don't use @, silencing errors is bad practice at testdata/
WARNING useEval: Don't use the 'eval' function at testdata/mustache/src/Mustache/Cache/NoopCache.php:45
eval('?>' . $value);
^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function section at testdata/mustache/src/Mustache/Compiler.php:98
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter start of function section at testdata/mustache/src/Mustache/Compiler.php:100
+ $node[Mustache_Tokenizer::INDEX],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter end of function section at testdata/mustache/src/Mustache/Compiler.php:101
+ $node[Mustache_Tokenizer::END],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter otag of function section at testdata/mustache/src/Mustache/Compiler.php:102
+ $node[Mustache_Tokenizer::OTAG],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter ctag of function section at testdata/mustache/src/Mustache/Compiler.php:103
+ $node[Mustache_Tokenizer::CTAG],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$node[Mustache_Tokenizer::FILTERS] ?? array()` at testdata/mustache/src/Mustache/Compiler.php:99
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function invertedSection at testdata/mustache/src/Mustache/Compiler.php:111
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$node[Mustache_Tokenizer::FILTERS] ?? array()` at testdata/mustache/src/Mustache/Compiler.php:112
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function partial at testdata/mustache/src/Mustache/Compiler.php:119
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$node[Mustache_Tokenizer::INDENT] ?? ''` at testdata/mustache/src/Mustache/Compiler.php:120
isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function parent at testdata/mustache/src/Mustache/Compiler.php:127
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$node[Mustache_Tokenizer::INDENT] ?? ''` at testdata/mustache/src/Mustache/Compiler.php:128
isset($node[Mustache_Tokenizer::INDENT]) ? $node[Mustache_Tokenizer::INDENT] : '',
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function blockArg at testdata/mustache/src/Mustache/Compiler.php:137
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter start of function blockArg at testdata/mustache/src/Mustache/Compiler.php:138
+ $node[Mustache_Tokenizer::INDEX],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter end of function blockArg at testdata/mustache/src/Mustache/Compiler.php:139
+ $node[Mustache_Tokenizer::END],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter otag of function blockArg at testdata/mustache/src/Mustache/Compiler.php:140
+ $node[Mustache_Tokenizer::OTAG],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter ctag of function blockArg at testdata/mustache/src/Mustache/Compiler.php:141
+ $node[Mustache_Tokenizer::CTAG],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function blockVar at testdata/mustache/src/Mustache/Compiler.php:149
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter start of function blockVar at testdata/mustache/src/Mustache/Compiler.php:150
+ $node[Mustache_Tokenizer::INDEX],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter end of function blockVar at testdata/mustache/src/Mustache/Compiler.php:151
+ $node[Mustache_Tokenizer::END],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter otag of function blockVar at testdata/mustache/src/Mustache/Compiler.php:152
+ $node[Mustache_Tokenizer::OTAG],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter ctag of function blockVar at testdata/mustache/src/Mustache/Compiler.php:153
+ $node[Mustache_Tokenizer::CTAG],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter id of function variable at testdata/mustache/src/Mustache/Compiler.php:165
+ $node[Mustache_Tokenizer::NAME],
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$node[Mustache_Tokenizer::FILTERS] ?? array()` at testdata/mustache/src/Mustache/Compiler.php:166
isset($node[Mustache_Tokenizer::FILTERS]) ? $node[Mustache_Tokenizer::FILTERS] : array(),
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter text of function text at testdata/mustache/src/Mustache/Compiler.php:173
+ $code .= $this->text($node[Mustache_Tokenizer::VALUE], $level);
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
WARNING unused: Variable $keystr is unused (use $_ to ignore this inspection or specify --unused-var-regex flag) at testdata/mustache/src/Mustache/Compiler.php:289
$keystr = var_export($key, true);
^^^^^^^
+WARNING notNullSafety: not null safety call in function getFindMethod signature of param id at testdata/mustache/src/Mustache/Compiler.php:556
+ $method = $this->getFindMethod($name);
+ ^^^^^
MAYBE callSimplify: Could simplify to $id[0] at testdata/mustache/src/Mustache/Compiler.php:646
if (substr($id, 0, 1) === '.') {
^^^^^^^^^^^^^^^^^
@@ -121,12 +187,33 @@ MAYBE callSimplify: Could simplify to $this->stack[] = $value at testdata/must
MAYBE callSimplify: Could simplify to $this->blockStack[] = $value at testdata/mustache/src/Mustache/Context.php:49
array_push($this->blockStack, $value);
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function findVariableInStack signature of param id at testdata/mustache/src/Mustache/Context.php:131
+ $value = $this->findVariableInStack($first, $this->stack);
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function findVariableInStack signature of param id at testdata/mustache/src/Mustache/Context.php:138
+ $value = $this->findVariableInStack($chunk, array($value));
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function findVariableInStack signature of param id at testdata/mustache/src/Mustache/Context.php:174
+ $value = $this->findVariableInStack($chunk, array($value));
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function method_exists signature of param object_or_class at testdata/mustache/src/Mustache/Context.php:218
+ if (method_exists($frame, $id)) {
+ ^^^^^^
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/mustache/src/Mustache/Context.php:213
switch (gettype($frame)) {
^
MAYBE ternarySimplify: Could rewrite as `$options['cache_file_mode'] ?? null` at testdata/mustache/src/Mustache/Engine.php:156
$mode = isset($options['cache_file_mode']) ? $options['cache_file_mode'] : null;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function setCache signature of param cache at testdata/mustache/src/Mustache/Engine.php:160
+ $this->setCache($cache);
+ ^^^^^^
+WARNING notNullSafety: potential null array access in parameter loader of function setLoader at testdata/mustache/src/Mustache/Engine.php:168
+ $this->setLoader($options['loader']);
+ ^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter partialsLoader of function setPartialsLoader at testdata/mustache/src/Mustache/Engine.php:172
+ $this->setPartialsLoader($options['partials_loader']);
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE misspellComment: "entitity" is a misspelling of "entity" at testdata/mustache/src/Mustache/Engine.php:254
public function getEntityFlags()
^^^^^^^^^^^^^^
@@ -151,6 +238,12 @@ ERROR undefinedMethod: Call to undefined method {\Mustache_Cache}->setLogger()
MAYBE ternarySimplify: Could rewrite as `$this->delimiters ?: '{{ }}'` at testdata/mustache/src/Mustache/Engine.php:628
'delimiters' => $this->delimiters ? $this->delimiters : '{{ }}',
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null dereference when accessing property 'charset' at testdata/mustache/src/Mustache/Engine.php:813
+ return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
+ ^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null dereference when accessing property 'strictCallables' at testdata/mustache/src/Mustache/Engine.php:813
+ return $compiler->compile($source, $tree, $name, isset($this->escape), $this->charset, $this->strictCallables, $this->entityFlags);
+ ^^^^^^^^^^^^^^^^^^^^^^
WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/mustache/src/Mustache/Engine.php:727
private function loadSource($source, Mustache_Cache $cache = null)
^^^^^^^^^^^^^^
@@ -175,9 +268,48 @@ MAYBE missingPhpdoc: Missing PHPDoc for \Mustache_Exception_UnknownTemplateExc
WARNING notExplicitNullableParam: parameter with null default value should be explicitly nullable at testdata/mustache/src/Mustache/Exception/UnknownTemplateException.php:23
public function __construct($templateName, Exception $previous = null)
^^^^^^^^^
+WARNING notNullSafety: not null safety call in function addLoader signature of param loader at testdata/mustache/src/Mustache/Loader/CascadingLoader.php:35
+ $this->addLoader($loader);
+ ^^^^^^^
+WARNING notNullSafety: potential null array access in parameter string of function ltrim at testdata/mustache/src/Mustache/Loader/FilesystemLoader.php:64
+ $this->extension = '.' . ltrim($options['extension'], '.');
+ ^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: null passed to non-nullable parameter context in function file_get_contents at testdata/mustache/src/Mustache/Loader/InlineLoader.php:114
+ $data = file_get_contents($this->fileName, false, null, $this->offset);
+ ^^^^
WARNING regexpVet: '\w' intersects with '\d' in [\w\d\.] at testdata/mustache/src/Mustache/Loader/InlineLoader.php:115
foreach (preg_split("/^@@(?= [\w\d\.]+$)/m", $data, -1) as $chunk) {
^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null dereference when accessing property 'stream' at testdata/mustache/src/Mustache/Logger/StreamLogger.php:61
+ fclose($this->stream);
+ ^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function array_key_exists signature of param key at testdata/mustache/src/Mustache/Logger/StreamLogger.php:102
+ if (!array_key_exists($level, self::$levels)) {
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function writeLog signature of param level at testdata/mustache/src/Mustache/Logger/StreamLogger.php:107
+ $this->writeLog($level, $message, $context);
+ ^^^^^^
+WARNING notNullSafety: potential null dereference when accessing property 'url' at testdata/mustache/src/Mustache/Logger/StreamLogger.php:128
+ $this->stream = fopen($this->url, 'a');
+ ^^^^^^^^^^
+WARNING notNullSafety: potential null dereference when accessing property 'stream' at testdata/mustache/src/Mustache/Logger/StreamLogger.php:136
+ fwrite($this->stream, self::formatLine($level, $message, $context));
+ ^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function enablePragma signature of param name at testdata/mustache/src/Mustache/Parser.php:58
+ $this->enablePragma($pragma);
+ ^^^^^^^
+WARNING notNullSafety: potential null array access in parameter name of function getNameAndFilters at testdata/mustache/src/Mustache/Parser.php:88
+ list($name, $filters) = $this->getNameAndFilters($token[Mustache_Tokenizer::NAME]);
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter name of function enablePragma at testdata/mustache/src/Mustache/Parser.php:167
+ $this->enablePragma($token[Mustache_Tokenizer::NAME]);
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter string of function substr at testdata/mustache/src/Mustache/Parser.php:235
+ if (substr($next[Mustache_Tokenizer::VALUE], -1) !== "\n") {
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: potential null array access in parameter subject of function preg_match at testdata/mustache/src/Mustache/Parser.php:262
+ return preg_match('/^\s*$/', $token[Mustache_Tokenizer::VALUE]);
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/mustache/src/Mustache/Parser.php:307
switch ($name) {
^
@@ -190,3 +322,15 @@ WARNING errorSilence: Don't use @, silencing errors is bad practice at testdata/
WARNING unused: Variable $v is unused (use $_ to ignore this inspection or specify --unused-var-regex flag) at testdata/mustache/src/Mustache/Template.php:122
foreach ($value as $k => $v) {
^^
+WARNING notNullSafety: not null safety call in function call_user_func signature of param callback at testdata/mustache/src/Mustache/Template.php:174
+ ->loadLambda((string) call_user_func($value))
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function trim signature of param string at testdata/mustache/src/Mustache/Tokenizer.php:110
+ if ($delimiters = trim($delimiters)) {
+ ^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/mustache/src/Mustache/Tokenizer.php:188
+ if (substr($lastName, -1) === '}') {
+ ^^^^^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/mustache/src/Mustache/Tokenizer.php:189
+ $token[self::NAME] = trim(substr($lastName, 0, -1));
+ ^^^^^^^^^
diff --git a/src/tests/golden/testdata/qrcode/golden.txt b/src/tests/golden/testdata/qrcode/golden.txt
index e6b2f8e57..e201c4cc7 100644
--- a/src/tests/golden/testdata/qrcode/golden.txt
+++ b/src/tests/golden/testdata/qrcode/golden.txt
@@ -4,12 +4,33 @@ MAYBE missingPhpdoc: Missing PHPDoc for \QRCode::output_image public method at
MAYBE missingPhpdoc: Missing PHPDoc for \QRCode::render_image public method at testdata/qrcode/qrcode.php:53
public function render_image() {
^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function imagecreatetruecolor signature of param width at testdata/qrcode/qrcode.php:56
+ $image = imagecreatetruecolor($width, $height);
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function imagecreatetruecolor signature of param height at testdata/qrcode/qrcode.php:56
+ $image = imagecreatetruecolor($width, $height);
+ ^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$this->options['bc'] ?? 'FFFFFF'` at testdata/qrcode/qrcode.php:59
$bgcolor = (isset($this->options['bc']) ? $this->options['bc'] : 'FFFFFF');
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
MAYBE ternarySimplify: Could rewrite as `$this->options['fc'] ?? '000000'` at testdata/qrcode/qrcode.php:63
$fgcolor = (isset($this->options['fc']) ? $this->options['fc'] : '000000');
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function floor signature of param num at testdata/qrcode/qrcode.php:72
+ $scale = (($scale > 1) ? floor($scale) : 1);
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:134
+ $r = hexdec(substr($color, 0, 2));
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:135
+ $g = hexdec(substr($color, 2, 2));
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:136
+ $b = hexdec(substr($color, 4, 2));
+ ^^^^^^
+WARNING notNullSafety: not null safety call in function imagecolorallocate signature of param image at testdata/qrcode/qrcode.php:137
+ return imagecolorallocate($image, $r, $g, $b);
+ ^^^^^^
WARNING deadCode: Unreachable code at testdata/qrcode/qrcode.php:155
return null;
^^^^
@@ -19,12 +40,30 @@ MAYBE trailingComma: Last element in a multi-line array should have a trailing
WARNING unused: Variable $mode is unused (use $_ to ignore this inspection or specify --unused-var-regex flag) at testdata/qrcode/qrcode.php:177
list($mode, $vers, $ec, $data) = $this->qr_encode_data($data, $ecl);
^^^^^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:200
+ $data = substr($data, 0, $max_chars);
+ ^^^^^
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/qrcode/qrcode.php:203
switch ($mode) {
^
WARNING maybeUndefined: Possibly undefined variable $code at testdata/qrcode/qrcode.php:221
while (count($code) % 8) {
^^^^^
+WARNING notNullSafety: not null safety call in function preg_match signature of param subject at testdata/qrcode/qrcode.php:268
+ if (preg_match($numeric, $data)) {
+ ^^^^^
+WARNING notNullSafety: not null safety call in function preg_match signature of param subject at testdata/qrcode/qrcode.php:271
+ if (preg_match($alphanumeric, $data)) {
+ ^^^^^
+WARNING notNullSafety: not null safety call in function preg_match signature of param subject at testdata/qrcode/qrcode.php:274
+ if (preg_match($kanji, $data)) {
+ ^^^^^
+WARNING notNullSafety: not null safety call in function strlen signature of param string at testdata/qrcode/qrcode.php:281
+ $length = strlen($data);
+ ^^^^^
+WARNING notNullSafety: not null safety call in function strlen signature of param string at testdata/qrcode/qrcode.php:295
+ $length = strlen($data);
+ ^^^^^
WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testdata/qrcode/qrcode.php:297
case 2: /* 27 - 40 */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,6 +73,9 @@ WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testd
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/qrcode/qrcode.php:296
switch ($version_group) {
^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:316
+ $group = substr($data, $i, 3);
+ ^^^^^
WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testdata/qrcode/qrcode.php:318
case 3:
^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -43,6 +85,9 @@ WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testd
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/qrcode/qrcode.php:317
switch (strlen($group)) {
^
+WARNING notNullSafety: not null safety call in function strlen signature of param string at testdata/qrcode/qrcode.php:339
+ $length = strlen($data);
+ ^^^^^
WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testdata/qrcode/qrcode.php:341
case 2: /* 27 - 40 */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -52,21 +97,33 @@ WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testd
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/qrcode/qrcode.php:340
switch ($version_group) {
^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:359
+ $group = substr($data, $i, 2);
+ ^^^^^
MAYBE callSimplify: Could simplify to $group[0] at testdata/qrcode/qrcode.php:361
$c1 = strpos($alphabet, substr($group, 0, 1));
^^^^^^^^^^^^^^^^^^^^
MAYBE callSimplify: Could simplify to $group[1] at testdata/qrcode/qrcode.php:362
$c2 = strpos($alphabet, substr($group, 1, 1));
^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function strlen signature of param string at testdata/qrcode/qrcode.php:390
+ $length = strlen($data);
+ ^^^^^
WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testdata/qrcode/qrcode.php:393
case 1: /* 10 - 26 */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/qrcode/qrcode.php:391
switch ($version_group) {
^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:413
+ $ch = ord(substr($data, $i, 1));
+ ^^^^^
MAYBE callSimplify: Could simplify to $data[$i] at testdata/qrcode/qrcode.php:413
$ch = ord(substr($data, $i, 1));
^^^^^^^^^^^^^^^^^^^^
+WARNING notNullSafety: not null safety call in function strlen signature of param string at testdata/qrcode/qrcode.php:428
+ $length = strlen($data);
+ ^^^^^
WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testdata/qrcode/qrcode.php:430
case 2: /* 27 - 40 */
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -76,6 +133,9 @@ WARNING caseBreak: Add break or '// fallthrough' to the end of the case at testd
WARNING switchDefault: Add 'default' branch to avoid unexpected unhandled condition values at testdata/qrcode/qrcode.php:429
switch ($version_group) {
^
+WARNING notNullSafety: not null safety call in function substr signature of param string at testdata/qrcode/qrcode.php:447
+ $group = substr($data, $i, 2);
+ ^^^^^
MAYBE callSimplify: Could simplify to $group[0] at testdata/qrcode/qrcode.php:448
$c1 = ord(substr($group, 0, 1));
^^^^^^^^^^^^^^^^^^^^
diff --git a/src/types/predicates.go b/src/types/predicates.go
index d0b4034f2..3d963e3fc 100644
--- a/src/types/predicates.go
+++ b/src/types/predicates.go
@@ -37,6 +37,23 @@ func IsAlias(s string) bool {
return has
}
+func IsTypeNullable(typ Map) bool {
+ isNullable := false
+ if typ.m == nil {
+ return true // We consider that if the type is not inferred, then it is mixed
+ }
+ typ.Iterate(func(t string) {
+ if strings.Contains(t, "null") || strings.Contains(t, "mixed") {
+ isNullable = true
+ }
+ })
+ return isNullable
+}
+
+func IsTypeMixed(typ Map) bool {
+ return typ.Contains("mixed")
+}
+
func Alias(s string) (string, bool) {
alias, has := aliases[s]
return alias, has