@@ -1138,51 +1138,68 @@ func (b *blockWalker) checkListExprNullSafety(arg ir.Node, fn meta.FuncInfo, par
1138
1138
}
1139
1139
}
1140
1140
1141
+ func (b * blockWalker ) getPropertyComputedType (expr * ir.PropertyFetchExpr ) (meta.ClassInfo , types.Map ) {
1142
+ baseCall , ok := expr .Variable .(* ir.SimpleVar )
1143
+ if ! ok {
1144
+ return meta.ClassInfo {}, types.Map {}
1145
+ }
1146
+
1147
+ varInfo , ok := b .ctx .sc .GetVar (baseCall )
1148
+ if ! ok {
1149
+ return meta.ClassInfo {}, types.Map {}
1150
+ }
1151
+
1152
+ classInfo , ok := b .r .ctx .st .Info .GetClass (varInfo .Type .String ())
1153
+ if ! ok {
1154
+ return meta.ClassInfo {}, types.Map {}
1155
+ }
1156
+
1157
+ property , ok := expr .Property .(* ir.Identifier )
1158
+ if ! ok {
1159
+ return meta.ClassInfo {}, types.Map {}
1160
+ }
1161
+
1162
+ propertyInfoFromClass := classInfo .Properties [property .Value ]
1163
+ return classInfo , propertyInfoFromClass .Typ
1164
+ }
1165
+
1141
1166
func (b * blockWalker ) checkPropertyFetchNullSafety (expr * ir.PropertyFetchExpr , fn meta.FuncInfo , paramIndex int , haveVariadic bool ) {
1142
- objVar , ok := expr .Variable .(* ir.SimpleVar )
1167
+ // If the left part of the chain is also a property call, we check it recursively
1168
+ if nested , ok := expr .Variable .(* ir.PropertyFetchExpr ); ok {
1169
+ b .checkPropertyFetchNullSafety (nested , fn , paramIndex , haveVariadic )
1170
+ }
1143
1171
1144
- if ok {
1145
- varInfo , _ := b .ctx .sc .GetVar (objVar )
1146
- classInfo , _ := b .r .ctx .st .Info .GetClass (varInfo .Type .String ())
1147
-
1148
- prp , okPrp := expr .Property .(* ir.Identifier )
1149
-
1150
- if okPrp {
1151
- property := classInfo .Properties [prp .Value ]
1152
-
1153
- isPrpNullable := types .IsTypeNullable (property .Typ )
1154
- if haveVariadic {
1155
- // If the parameter is outside the declared parameters, we check the latter as a variable
1156
- if paramIndex >= len (fn .Params )- 1 {
1157
- lastParam := fn .Params [len (fn .Params )- 1 ] // last param (variadic ...args)
1158
- if types .IsTypeMixed (lastParam .Typ ) {
1159
- return
1160
- }
1161
-
1162
- paramAllowsNull := types .IsTypeNullable (lastParam .Typ )
1163
- if isPrpNullable && ! paramAllowsNull {
1164
- b .report (expr , LevelError , "notNullSafety" ,
1165
- "potential null dereference when accessing property '%s'" , prp .Value )
1166
- }
1167
- return
1172
+ classInfo , propType := b .getPropertyComputedType (expr )
1173
+ if classInfo .Name == "" || propType .Empty () {
1174
+ return
1175
+ }
1168
1176
1169
- }
1170
- }
1177
+ prp , ok := expr .Property .(* ir.Identifier )
1178
+ if ! ok {
1179
+ return
1180
+ }
1171
1181
1172
- paramAllowsNull := types .IsTypeNullable (fn . Params [ paramIndex ]. Typ )
1182
+ isPrpNullable := types .IsTypeNullable (propType )
1173
1183
1184
+ if haveVariadic {
1185
+ if paramIndex >= len (fn .Params )- 1 {
1186
+ lastParam := fn .Params [len (fn .Params )- 1 ]
1187
+ if types .IsTypeMixed (lastParam .Typ ) {
1188
+ return
1189
+ }
1190
+ paramAllowsNull := types .IsTypeNullable (lastParam .Typ )
1174
1191
if isPrpNullable && ! paramAllowsNull {
1175
1192
b .report (expr , LevelError , "notNullSafety" ,
1176
1193
"potential null dereference when accessing property '%s'" , prp .Value )
1177
1194
}
1178
-
1179
- println (classInfo .Name )
1195
+ return
1180
1196
}
1197
+ }
1181
1198
1182
- // TODO: check difficult chains like $a->b->c->d
1183
- /* if nestedProp, ok := expr.Variable.(*ir.PropertyFetchExpr); ok {
1184
- b.checkPropertyFetchNullSafety(nestedProp)
1185
- }*/
1199
+ paramAllowsNull := types . IsTypeNullable ( fn . Params [ paramIndex ]. Typ )
1200
+ if isPrpNullable && ! paramAllowsNull {
1201
+ b . report ( expr , LevelError , "notNullSafety" ,
1202
+ "potential null dereference when accessing property '%s'" , prp . Value )
1186
1203
}
1187
1204
}
1188
1205
0 commit comments