Description
Describe the issue
"Unrelated type checking" is used by a few rules:
unrelated_type_equality_checks
collection_methods_unrelated_type
iterable_contains_unrelated_type
(soft-deprecated)list_remove_unrelated_type
It currently decides that any a record type is "related" to any other type Tuple2
map keys to record map keys. The analyzer did not complain when I asked a Map<(int, int), String>
if it containsKey
with a Tuple2
To Reproduce
Use any of the above linter rules with:
- a record type and a non-Null, non-Object, non-record type (considered related, but definitely unrelated)
- two record types with different shape (considered related, but definitely unrelated)
- two record types which are mutually non-assignable (considered related, but definitely unrelated)
Expected behavior
Each of the above should be considered unrelated. The only types that a given record type, R1
should be considered "related to", modulo nullability, are:
- Null (maybe only if the record type is nullable? Match existing behavior for other types)
- Object
- another record type,
R2
such thatR2
is assignable toR1
, orR1
is assignable toR2
. Assignability is the only consideration, and that is provided byTypeSystem.isAssignableTo
.
(Maybe also FutureOr<T>
where T
is "related" to the record type.)
Additional context
The notion of "related types" in these lint rules has only grown organically, and has not been super principled. But reviewing the implementation, it is very close to the Dart language's idea of "assignability". I think there are only a few main differences:
- "relatedness" is a symmetric notion.
T
is related toU
iffU
is related toT
. - In deciding whether two types are unrelated, each is considered modulo nullability (and I think the same for each component type). This is partially to support the symmetric notion.
Future<int>?
is related toFuture<num?>
even though neither is assignable to the other. - In deciding whether two types are related, each of their component types (for example
T
inFuture<T>
, or inFuture<U extends T>
) are considered "related" independently. SoMap<num, int>
andMap<int, num>
are related, even though neither is assignable to the other. - Function-typed analysis is not implemented.
I think "relatedness" could be overhauled, maybe to use something much simpler like a few TypeSystem.isAssignable
calls. But that would be a separate task, as it could be statically breaking for both google3 and Flutter.