Compare commits

...

1 Commits

3 changed files with 132 additions and 0 deletions

View File

@ -0,0 +1,24 @@
{
"title": "For elements should be preferred to Map.fromIterable",
"type": "CODE_SMELL",
"status": "ready",
"remediation": {
"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [
"performance"
],
"defaultSeverity": "Major",
"ruleSpecification": "RSPEC-7083",
"sqKey": "S7083",
"scope": "All",
"defaultQualityProfiles": ["Sonar way"],
"quickfix": "unknown",
"code": {
"impacts": {
"MAINTAINABILITY": "HIGH"
},
"attribute": "EFFICIENT"
}
}

106
rules/S7083/dart/rule.adoc Normal file
View File

@ -0,0 +1,106 @@
https://dart.dev/language/collections#control-flow-operators[`for` elements] should be preferred to https://api.dart.dev/stable/dart-core/Map/Map.fromIterable.html[`fromIterable`] when building a https://api.dart.dev/stable/dart-core/Map/Map.html[`Map`] from an https://api.dart.dev/stable/dart-core/Iterable-class.html[`Iterable`].
== Why is this an issue?
While it is possible to build a `Map` from an `Iterable` using `Map.fromIterable`, writing an explicit `for` loop with `for` elements should be used instead.
The main reason to prefer `for` elements is that it can be optimized by the Dart compiler for performance, depending on the iterable being visited.
Moreover, `for` elements are more idiomatic and generally easier to understand than `Map.fromIterable`, which requires explicit map type upfront, as well as the input iterable and two different lambda functions as parameters.
[source,dart]
----
Map<String, int>.fromIterable( // Explicit Map<String, int> type required
inputIterable,
key: (item) => ..., // Key generator
value: (item) => ..., // Value generator
);
----
On the other hand, `for` elements are more flexible, look like normal `for` loops, and support better inference of the resulting `Map` type.
[source,dart]
----
{
for (final item in inputIterable)
'The value is $v': v // Implicit Map<String, int> type inference
}
----
=== Exceptions
The rule only applies to the `fromIterable` factory method of `Map`. It does not apply to other `fromIterable` methods, such as the one from `LinkedHashMap<T, U>` in `dart:collection`.
Moreover, it only applies when all the arguments of the `Map.fromIterable` call are provided. If either the `key` or the `value` parameter is omitted, the rule does not apply.
[source,dart]
----
Map<int, int>.fromIterable(l1, key: (item) => ...); // OK
Map<int, int>.fromIterable(l1, value: (item) => ...); // OK
----
The rule also does not apply when the `key` and `value` parameters are not inline function expressions, but variables defined elesewhere in the code.
[source,dart]
----
final key = (item) => ...;
final value = (item) => ...;
Map<int, int>.fromIterable(l1, key: key, value: value);
----
== How to fix it
In order to replace `Map.fromIterable` with `for` elements:
* replace the `Map<String, int>.fromIterable` call with a `for` loop over the input iterable, that is the first argument of the call
* define the body of the `for` elements as a `key: value` pair, where `key` is the body of the key generator lambda, and `value` is body of the value generator lambda
=== Code examples
==== Noncompliant code example
[source,dart,diff-id=1,diff-type=noncompliant]
----
Map<String, int>.fromIterable(
[1, 2, 3],
key: (v) => 'The value is $v',
value: (v) => v,
);
----
==== Compliant solution
[source,dart,diff-id=1,diff-type=compliant]
----
{
for (final v in [1, 2, 3])
'The value is $v': v
}
----
== Resources
=== Documentation
* Dart Docs - https://dart.dev/tools/linter-rules/prefer_for_elements_to_map_fromIterable[Linter rule - prefer_for_elements_to_map_fromIterable]
* Dart Docs - https://dart.dev/language/collections#control-flow-operators[Language - Control-flow operators]
* Dart API Reference - https://api.dart.dev/stable/dart-core/Map/Map.html[Map<K, V> class]
* Dart API Reference - https://api.dart.dev/stable/dart-core/Iterable-class.html[Iterable<E> class]
* Dart API Reference - https://api.dart.dev/stable/dart-core/Map/Map.fromIterable.html[Map<K, V>.fromIterable constructor]
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
=== Message
Use 'for' elements when building maps from iterables.
=== Highlighting
The entire `Map.fromIterable` call, including parentheses and arguments: e.g. `Map<String, int>.fromIterable([1, 2, 3], key: (v) => 'The value is $v', value: (v) => v);`.
endif::env-github,rspecator-view[]

View File

@ -0,0 +1,2 @@
{
}