Skip to content

Commit

Permalink
added deep chain catching for checkPropertyFetchNullSafety
Browse files Browse the repository at this point in the history
  • Loading branch information
Hidanio committed Feb 13, 2025
1 parent e84f894 commit 483e5a4
Showing 1 changed file with 51 additions and 34 deletions.
85 changes: 51 additions & 34 deletions src/linter/block.go
Original file line number Diff line number Diff line change
Expand Up @@ -1138,51 +1138,68 @@ func (b *blockWalker) checkListExprNullSafety(arg ir.Node, fn meta.FuncInfo, par
}
}

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) {
objVar, ok := expr.Variable.(*ir.SimpleVar)
// If the left part of the chain is also a property call, we check it recursively
if nested, ok := expr.Variable.(*ir.PropertyFetchExpr); ok {
b.checkPropertyFetchNullSafety(nested, fn, paramIndex, haveVariadic)
}

if ok {
varInfo, _ := b.ctx.sc.GetVar(objVar)
classInfo, _ := b.r.ctx.st.Info.GetClass(varInfo.Type.String())

prp, okPrp := expr.Property.(*ir.Identifier)

if okPrp {
property := classInfo.Properties[prp.Value]

isPrpNullable := types.IsTypeNullable(property.Typ)
if haveVariadic {
// If the parameter is outside the declared parameters, we check the latter as a variable
if paramIndex >= len(fn.Params)-1 {
lastParam := fn.Params[len(fn.Params)-1] // last param (variadic ...args)
if types.IsTypeMixed(lastParam.Typ) {
return
}

paramAllowsNull := types.IsTypeNullable(lastParam.Typ)
if isPrpNullable && !paramAllowsNull {
b.report(expr, LevelError, "notNullSafety",
"potential null dereference when accessing property '%s'", prp.Value)
}
return
classInfo, propType := b.getPropertyComputedType(expr)
if classInfo.Name == "" || propType.Empty() {
return
}

}
}
prp, ok := expr.Property.(*ir.Identifier)
if !ok {
return
}

paramAllowsNull := types.IsTypeNullable(fn.Params[paramIndex].Typ)
isPrpNullable := types.IsTypeNullable(propType)

if haveVariadic {
if paramIndex >= len(fn.Params)-1 {
lastParam := fn.Params[len(fn.Params)-1]
if types.IsTypeMixed(lastParam.Typ) {
return
}
paramAllowsNull := types.IsTypeNullable(lastParam.Typ)
if isPrpNullable && !paramAllowsNull {
b.report(expr, LevelError, "notNullSafety",
"potential null dereference when accessing property '%s'", prp.Value)
}

println(classInfo.Name)
return
}
}

// TODO: check difficult chains like $a->b->c->d
/* if nestedProp, ok := expr.Variable.(*ir.PropertyFetchExpr); ok {
b.checkPropertyFetchNullSafety(nestedProp)
}*/
paramAllowsNull := types.IsTypeNullable(fn.Params[paramIndex].Typ)
if isPrpNullable && !paramAllowsNull {
b.report(expr, LevelError, "notNullSafety",
"potential null dereference when accessing property '%s'", prp.Value)
}
}

Expand Down

0 comments on commit 483e5a4

Please sign in to comment.