Skip to content

"unrelated type" checking does not account for record types #59108

Closed
dart-archive/linter
#4266
@srawlins

Description

@srawlins

Describe the issue

"Unrelated type checking" is used by a few rules:

It currently decides that any a record type is "related" to any other type ☹️ . I discovered this when converting use of 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 that R2 is assignable to R1, or R1 is assignable to R2. Assignability is the only consideration, and that is provided by TypeSystem.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 to U iff U is related to T.
  • 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 to Future<num?> even though neither is assignable to the other.
  • In deciding whether two types are related, each of their component types (for example T in Future<T>, or in Future<U extends T>) are considered "related" independently. So Map<num, int> and Map<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.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions