(e, operand ast.Expr, field string, optional bool)
| 213 | } |
| 214 | |
| 215 | func (c *checker) checkSelectField(e, operand ast.Expr, field string, optional bool) *types.Type { |
| 216 | // Interpret as field selection, first traversing down the operand. |
| 217 | c.check(operand) |
| 218 | operandType := substitute(c.mappings, c.getType(operand), false) |
| 219 | |
| 220 | // If the target type is 'optional', unwrap it for the sake of this check. |
| 221 | targetType, isOpt := maybeUnwrapOptional(operandType) |
| 222 | |
| 223 | // Assume error type by default as most types do not support field selection. |
| 224 | resultType := types.ErrorType |
| 225 | switch targetType.Kind() { |
| 226 | case types.MapKind: |
| 227 | // Maps yield their value type as the selection result type. |
| 228 | resultType = targetType.Parameters()[1] |
| 229 | case types.StructKind: |
| 230 | // Objects yield their field type declaration as the selection result type, but only if |
| 231 | // the field is defined. |
| 232 | messageType := targetType |
| 233 | if fieldType, found := c.lookupFieldType(e.ID(), messageType.TypeName(), field); found { |
| 234 | resultType = fieldType |
| 235 | } |
| 236 | case types.TypeParamKind: |
| 237 | // Set the operand type to DYN to prevent assignment to a potentially incorrect type |
| 238 | // at a later point in type-checking. The isAssignable call will update the type |
| 239 | // substitutions for the type param under the covers. |
| 240 | c.isAssignable(types.DynType, targetType) |
| 241 | // Also, set the result type to DYN. |
| 242 | resultType = types.DynType |
| 243 | default: |
| 244 | // Dynamic / error values are treated as DYN type. Errors are handled this way as well |
| 245 | // in order to allow forward progress on the check. |
| 246 | if !isDynOrError(targetType) { |
| 247 | c.errors.typeDoesNotSupportFieldSelection(e.ID(), c.location(e), targetType) |
| 248 | } |
| 249 | resultType = types.DynType |
| 250 | } |
| 251 | |
| 252 | // If the target type was optional coming in, then the result must be optional going out. |
| 253 | if isOpt || optional { |
| 254 | return types.NewOptionalType(resultType) |
| 255 | } |
| 256 | return resultType |
| 257 | } |
| 258 | |
| 259 | func (c *checker) checkCall(e ast.Expr) { |
| 260 | // Note: similar logic exists within the `interpreter/planner.go`. If making changes here |
no test coverage detected