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
Add require_trailing_comma lint rule. #2557
Conversation
assert(true); | ||
|
||
assert('a very very very very very very very very very long string' | ||
.isNotEmpty); // LINT |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Heads up: I suspect this part will be controversial. I don't have a strong opinion yet, but I suspect for many people, requiring a trailing comma for a single parameter/argument (on a long line) may be a bridge too far.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall the implementation looks good.
I didn't address any questions about whether this implements the semantics we want, only whether the semantics this implements were implemented correctly.
I do have a larger concern. This implementation depends on having first run the formatter over the code. This has some negative consequences.
- Sometimes users won't be told about missing commas until after they format the code. This could lead to a workflow in which users need to format their code, add commas, and then format again, which is less than ideal.
- Sometimes users will be told that a comma is missing even if the formatter could put the whole invocation on a single line, which could lead to unnecessary commas.
- Users will never be told when they could remove a trailing comma (such as when one or more of the arguments are extracted into local variables).
Removing that requirement would be very difficult, so I'm not sure the best way to proceed from here.
- Replaced 'definitions' with 'declarations' - Add testcase for self-executing closure - Add testcase for operator declaration - Add testcase for named param with default value - Clarify exception includes both parameter and arguments - Add AssertInitializer visitor - Change `charOffset` to `offset`
Thanks for the review, @bwilkerson and @srawlins. I've addressed most comments.
You're right, this assumes
True, this lint does not enforce the reverse when everything fits on a single line. @pq, any thoughts? |
I agree that this is awkward (and complicates quick fixes) but I'm not sure it's a show-stopper. It would definitely be good to capture this in the rule documentation somehow. |
This is an interesting idea. Maybe |
My perspective is that this is a rule designed to enforce a particular style concerning when (and when not) to use a trailing comma in parameter and argument lists. I think it would be much better for it to enforce the whole style than for it to require a companion rule to enforce some portion of the style. And from that perspective, perhaps we should rename the lint to indicate that it is (or could be) more encompassing (though I don't have a good suggestion off the top of my head). Also, I'm concerned that
It would be prohibitively expensive, but We might also be able to do some close-enough computation of the length of various code snippets to guess at whether multiple lines are required. For example, given an invocation like
we could add the lengths of the individual argument, plus commas and spaces, and determine that this would all fit on one line if the trailing comma were removed. I'm not sure what kind of nasty corner cases we might run into, but it might be worth exploring at some point. |
Removing trailing commas by simply counting the length of each individual argument could be tricky due to corner cases like this: // Comment prevents everything from fitting on 1 line.
method(
a, // comment
b,
); For this case, I should be able to easily detect that the token spans multiple lines and skip: // Function literal that has few characters but prevents everything from being on 1 line.
method(
a,
() {
// doSomething
},
); If we do count chars, we also have to remember to count in the named params: method(
aVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryExtremelyLongNamedParameter: a,
b,
); My thought: if we can get this to a good state that's agreeable by most, shall we move forward with what we have currently and propose further improvements later? |
Yes, I'm fine with that. If the changes we're contemplating would be breaking, then we might want to mark it as experimental in the meantime, but other than that I'm always in favor of making incremental progress on a feature. |
literal set or a literal array. This exception only applies if the final | ||
parameter does not fit entirely on one line. | ||
|
||
**Note:** This lint rule assumes `dartfmt` has been run over the code. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider adding "and might produce false positives until that's happened" to make the implications more clear.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Updated, although I feel that this could be a given.
The assumption that dartfmt
has been run is not unique to this lint rule.
curly_braces_in_flow_control_structures
also has similar assumptions but is undocumented.
Sounds good! What are the implications of a rule being marked experimental? Does that mean it's unenforced by default but can be manually turned on? |
It's basically just a signal to the community that we reserve the right to change its behavior. With lints that aren't marked as being experimental we're very careful to consider the possible impact on clients (such as new lints being generated) before making changes to them.
All lints are disabled by default and need to be explicitly enabled in the analysis options file. So yes, but not because it's marked experimental. |
@bwilkerson Marked the lint as experimental, PTAL |
Only 7211 lints found on flutter package 😆 🥵 Some random samples that might be discutable / need an exception:
print('''
--help Print a usage message.
--temp-dir A location where temporary files may be written. Defaults to a
directory in the system temp folder. If a temp_dir is not
specified, then the default temp_dir will be created, used, and
removed automatically.
'''); await tester.pumpWidget(Wrap(
alignment: WrapAlignment.spaceAround,
spacing: 5.0,
textDirection: TextDirection.rtl,
children: const <Widget>[
SizedBox(width: 100.0, height: 10.0),
SizedBox(width: 200.0, height: 20.0),
SizedBox(width: 310.0, height: 30.0),
],
)); assert(() {
_debugIsRenderViewInitialized = true;
return true;
}());
testWidgets('debugIsLocalCreationLocation test', (WidgetTester tester) async {
// several statements
}, skip: !WidgetInspectorService.instance.isWidgetCreationTracked()); // Test requires --track-widget-creation flag. Timeline.instantSync('ImageCache.evict', arguments: <String, dynamic>{
'type': 'keepAlive',
'sizeInBytes': image.sizeBytes,
}); |
I've written a tool to automate most of the fixes and will be happy to help with the effort to apply this to the flutter package.
I'm of the thought that single argument calls shouldn't be given this exception if it spans more than one line. Of course, open to discussion and would like to hear from the community.
The lint rule already excepts the trailing comma if the last arg is a function literal or literal set/map/array. Exemption does not apply if the last param is named rather than positional. Again, open to discussion, but I think keeping the rule simple while covering most cases should be the goal. |
Ideally we should add a fix in the analysis server for this lint and make it possible to apply the fix through |
Regarding the lints reported on flutter I would recommand that the lint triggers:
|
Agree. Shall we start by opening an issue on dart-lang/sdk for this request?
@jefflim-google any thoughts on the single-arg exception proposed by @a14n? |
If you'd like to create an issue, then that's the best place for it. Not sure whether we need an issue, though. |
I've just took another look at some flutter diagnostics for this lint. I can see 2 common patterns that should be excluded with a single argument:
assert(() {
// code
return true;
} ());
// example with method call
rect: animation.drive(slideFromLeadingEdge(
fromKey: bottomComponents.middleKey,
fromNavBarBox: bottomNavBarBox,
toKey: topComponents.backLabelKey,
toNavBarBox: topNavBarBox,
)),
// example with constructor
style: animation.drive(TextStyleTween(
begin: bottomTitleTextStyle,
end: topBackButtonTextStyle,
)), |
Add
require_trailing_comma
lint rule.DO use trailing commas for all function calls and definitions unless the function call or definition, from the start of the function name up to the closing parenthesis, fits in a single line.
GOOD:
BAD:
Exception: If the final parameter is positional (vs named) and either a function literal implemented using curly braces, a literal map, a literal set or a literal array. This exception only applies if the final parameter does not fit entirely on one line.
Fixes #1338