-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Description
Combining dart cascade notation with normal method invocations results in expressions that don't evaluate how users expect. dartfmt helps a bit by enforcing indentation that clarifies the precedence a bit but results can still be confusing.
Examples of confused users:
https://stackoverflow.com/questions/17025769/how-do-method-cascades-work-exactly-in-dart
myList..clear().addAll(otherList)gives an error that it cannot call addAll on null (if myList is dynamic).
A().b..c.d..runtimeTypeA().b.c..foo()..bar(); // For discussion. Including all . before all .. might be ok or we could force parens in this case as well to fully disambiguate.does not return runtimeType.
This is because cascade has an extremely low operator precedence which is surprising to users as . and .. seems similar but have very different precedence.
https://dart.dev/guides/language/language-tour#operators
The only operator that .. plays with consistently is = as it is given lower precedence than .
A lint and quickfix could help ease the way towards future Dart support for more intuitive precedence rules for cascades. The most aggressive version of the lint could force adding parenthesis for all cases where the expression order for cascades is required to get the current behavior.
Possible lint messages:
"If you end a cascade section with a getter you're going to have a bad time"
"Expression contains cascades without parenthesis to disambiguate the order of operations"
These lints would be used by any Dart user that uses cascades. We are seeing more Dart users use cascades as some Dart test frameworks now strongly benefit from using cascades but also result in more abuse of cascades.
An existing nice lint for cascades is
https://dart-lang.github.io/linter/lints/avoid_single_cascade_in_expression_statements.html
A quickfix could add parens so that the code no longer depends on the surprising order of operations for cascades.
Examples
A().b..c.d..runtimeType // ErrorA().b..c.d..someMethod() // Error((A().b..c).d)..someMethod() // No error as parens disambiguateA()..b()..c()..d() // no error as calls do not mix . and ..