Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[lint] cascade_invocations #46

Open
evanweible-wf opened this issue Nov 8, 2019 · 5 comments
Open

[lint] cascade_invocations #46

evanweible-wf opened this issue Nov 8, 2019 · 5 comments
Labels

Comments

@evanweible-wf
Copy link
Contributor

evanweible-wf commented Nov 8, 2019

http://dart-lang.github.io/linter/lints/cascade_invocations.html

@greglittlefield-wf
Copy link
Contributor

I've found that this can negatively impact readability in certain cases, and be annoying to deal with in other cases, and question whether making this recommended provides significant benefit.

I propose removing this as a recommended lint.

Some examples from wdesk_sdk, with the diff showing what it would take to satisfy the lint.

  1. Performing an action on the result of the call. If you're forced to cascade it, it shows up on the same line, which in some cases IMO isn't as readable.

       _handleRemovePanelItem(PanelModelContext context) {
    -    var panel = _getPanel(context);
    -    panel._removePanelItem(context);
    +    var panel = _getPanel(context).._removePanelItem(context);
    
         if (panel.models.isEmpty) {
           _resizePanels(shouldTransition: false);
         }
       }
  2. When performing a series of calls, interleaved with other calls, you end up sometimes having to cascade things and sometimes not, making moving around these calls during development a pain. Also, for cascades of two items, you end up with code that's a line longer.

     action = AddEditorAction('cssNamespace', parentId: rootEditor);
     childEditorA = action.editorId;
     store.dispatch(action);

     action = AddEditorAction('cssNamespace', parentId: rootEditor);
     childEditorB = action.editorId;
-    store.dispatch(action);
-    store.dispatch(SetActiveChildEditorAction(rootEditor, childEditorA));
+    store
+      ..dispatch(action);
+      ..dispatch(SetActiveChildEditorAction(rootEditor, childEditorA));

@dustyholmes-wf
Copy link

I recognize that this lint may be arbitrary and a little heavy for recommended. I opted to enforce it in DPC because of these reasons.

  1. When you change a variable name, it has fewer instances that must be updated.
-    stoore     
+    store
      ..dispatch(action);
      ..dispatch(SetActiveChildEditorAction(rootEditor, childEditorA));
  1. It is a language feature that helps us completely avoid having instances floating around longer than necessary. When implementing this lint in the past I discovered many instances of this that people just couldn't see without the lint.
-   final store = Store();
-   store.dispatch(action);
+   Store()..dispatch(action);

Despite the fact that it truly is a syntax preference, I would prefer keeping it as a recommended lint.

@greglittlefield-wf
Copy link
Contributor

Thanks for the discussion, @dustyholmes-wf!

  1. When you change a variable name, it has fewer instances that must be updated.

That's a fair point, but also Dart IDEs can automate variable renaming, so developers don't have to manually update each instance. They're also free to take that opportunity to refactor the calls into a cascade.

  1. It is a language feature that helps us completely avoid having instances floating around longer than necessary. When implementing this lint in the past I discovered many instances of this that people just couldn't see without the lint.

I would expect it to be relatively rare to have cases of instantiating an object only to call an instance method on it and discard both the instance and the result of the method call. Do you have examples of this?

It doesn't seem clear-cut that the benefits of this lint outweigh the drawbacks, so to me it doesn't seem appropriate to put in recommended. That doesn't stop specific repos from adding it to their sets of lints, though.

@dustyholmes-wf
Copy link

dustyholmes-wf commented Sep 30, 2022

@greglittlefield-wf my concern for 1 was more on the review side than on the edit side. It's a small thing, but helping people make smaller changes has been top of my mind.

As for examples:

When we need to make multiple calls into APIs.

void _updateToolbar([_]) {
-    final edit = _toolbarModule.api.edit;

-    edit.setStyle(_formatTags.getString(TableFormatTypes.styleKey));
-    edit.setFontFamily(_formatTags.getString(TableFormatTypes.fontFamily, 'Arial'));
-    edit.setIsBold(_formatTags.getBool(TableFormatTypes.bold));
-    edit.setIsItalic(_formatTags.getBool(TableFormatTypes.italic));
-    edit.setIsUnderline(_formatTags.getBool(TableFormatTypes.underline));
-    edit.setIsStrikethrough(_formatTags.getBool(TableFormatTypes.strike));
-    edit.setDisplayValue(_formatTags.displayValue);
-    edit.setSelectionTextColor(_formatTags.getString(TableFormatTypes.textColor));
-    edit.setSelectionBackgroundColor(_formatTags.getString(TableFormatTypes.backgroundColor));
-    edit.setCharacterSpacing(_formatTags.getInt(TableFormatTypes.characterSpacing, 0));

+    _toolbarModule.api.edit
+       ..setStyle(_formatTags.getString(TableFormatTypes.styleKey))
+       ..setFontFamily(_formatTags.getString(TableFormatTypes.fontFamily, 'Arial'))
+       ..setIsBold(_formatTags.getBool(TableFormatTypes.bold))
+       ..edit.setIsItalic(_formatTags.getBool(TableFormatTypes.italic))
+       ..edit.setIsUnderline(_formatTags.getBool(TableFormatTypes.underline))
+       ..edit.setIsStrikethrough(_formatTags.getBool(TableFormatTypes.strike))
+       ..edit.setDisplayValue(_formatTags.displayValue)
+       ..edit.setSelectionTextColor(_formatTags.getString(TableFormatTypes.textColor))
+       ..edit.setSelectionBackgroundColor(_formatTags.getString(TableFormatTypes.backgroundColor))
+       ..edit.setCharacterSpacing(_formatTags.getInt(TableFormatTypes.characterSpacing, 0))
  }

When we're constructing a child object or parameter.
```diff
- final so = util.waterfallWithTotalColumnSeriesOptions(mgr, 's5', 2, 4, null);
- so.dataLabels = highcharts.DataLabels()..enabled = true;
- final dil = util.di(so);

+ final dil = util.di(
+   util.waterfallWithTotalColumnSeriesOptions(mgr, 's5', 2, 4, null)
+     ..dataLabels = (highcharts.DataLabels()..enabled = true),
+ )

I turned this on in a few packages and found that about 1/3 of the instances could avoid a variable entirely.

@evanweible-wf
Copy link
Contributor Author

We talked more about this one internally and it was pretty 50/50. We're going to remove it from the v2 recommended ruleset in #182 and teams can choose to independently enable it if desired.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants