Skip to content

New rule: prefer_iterable_as_function_parameter #57727

Open
@srawlins

Description

@srawlins

Skip to TL;DR to skip the premise.

Here's some Dart 2 fun:

void parseArgs(List<String> args) {
  // ...
}

void main() {
  var args = ['command', '--a', '--b', '--c', '--d'];
  parseArgs(args.skip(1));
}

The fun, of course, is that there is no static analysis warning for this code, but it might fail at runtime, because an Iterable was passed where a List was expected. Because we do fun implicit casts (#31410), its allowed statically. If, however, we add something like --no-implicit-cast-in-function-arguments, this code above would not be allowed.

I think, in general, authors of Dart code write too many methods which expect a List as a parameter where an Iterable would work fine. Cleaning up method signatures to expect Iterable arguments will help understand and prevent implicit casts.

TL;DR

I want a lint rule that examines functions with List (or Queue or Set...) parameters, and reports when such a parameter should be an Iterable instead.

Examples

void fn1(List<String> list) => list.forEach((s) => print(s));

void fn2(List<String> list) {
  if (list.isEmpty) return;
  print(list.length);
  for (var s in list) print(s);
}

Neither fn1 nor fn2 use any List-specific APIs. Nor is list passed to other functions, which might expect List arguments. Their signatures should be more lenient, and accept Iterable<String>.

void fn3(List<String> list) {
  fn4(list);
  fn5(list);
}

void fn4(Iterable<String> list) => print(list);

void fn5(dynamic list) => print(list);

Here, fn3 does pass list to other functions, but their signatures expect Iterable or dynamic, so we're all good there.

To be conservative against false positives, we can report on more restrictive situations than this... like if its expensive/tricky to see signatures of all methods where list is passed.

Risk of False Positives

I'm not sure... maybe people write APIs that spell out "I expect a List!" because of performance stuff, like... "I call .length all the time on this thing, so don't pass an Iterable which has an expensive .length call" (I'm not sure if that's true, just an example).

CC @alorenzen @matanlurey

Metadata

Metadata

Assignees

No one assigned

    Labels

    P4area-devexpFor issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages.devexp-linterIssues with the analyzer's support for the linter packagelinter-lint-requesttype-enhancementA request for a change that isn't a bug

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions