Skip to content

prefer_add_all

v0.8.0 Warning Fix Collection Type

Flags two ways of adding elements one at a time: a for-in loop whose only statement adds the loop variable, and two or more consecutive add calls on the same collection. The quick fix collapses either into addAll.

A copy loop is addAll spelled out across three lines. The reader has to follow the control flow to work out it is a single operation:

List<String> withDefaults(List<String> configured) {
final result = <String>[];
for (final tag in configured) {
result.add(tag);
}
return result;
}
List<String> withDefaults(List<String> configured) {
final result = <String>[];
result.addAll(configured);
return result;
}

addAll also lets a List grow its backing store once instead of on each add.

A run of add calls on the same receiver is one addAll with a literal:

// Don't
final steps = <String>[];
steps.add('validate');
steps.add('persist');
steps.add('notify');
// Do
final steps = <String>[];
steps.addAll(['validate', 'persist', 'notify']);

The run has to be uninterrupted. Any other statement between the calls breaks it, and neither half is reported:

// Accepted — the log call splits the run
steps.add('validate');
log('validating');
steps.add('persist');

For the loop form, only an exact copy is reported. The rule stays silent whenever the loop does anything else:

  • The added value is not the loop variable unchanged. result.add(tag.trim()) is a map, not a copy — reach for addAll(configured.map((t) => t.trim())) yourself.
  • The body has more than one statement, or wraps the add in a condition.
  • The loop is indexed (for (var i = 0; ...)) rather than for-in, since it may skip or reorder elements.
  • The receiver depends on the loop variable. groups.putIfAbsent(key(item), () => []).add(item) distributes the source across a different target per iteration, and has no addAll equivalent.
  • The method is anything other than add — insert, addEntries and the rest are untouched.

For consecutive calls, the receiver must be a plain variable or property chain. items[i].add(x) may denote a different object on each call, so it is left alone — and the receiver must resolve to a collection, since add exists on many unrelated types.

This rule is in the opinionated preset, so it is on with preset: opinionated, or by name.

To turn it off:

many_lints.yaml
rules:
prefer_add_all: false

To keep the rule on but skip certain paths, use per-rule exclude.