-
Notifications
You must be signed in to change notification settings - Fork 28.7k
Enable obvious lints #169971
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
base: master
Are you sure you want to change the base?
Enable obvious lints #169971
Conversation
@@ -175,7 +175,7 @@ List<OpacityPeepholeCase> allOpacityPeepholeCases = <OpacityPeepholeCase>[ | ||
name: 'Grid of Opacity', | ||
builder: (double v) { | ||
double rowV = v; | ||
double colV = rowV; | ||
var colV = rowV; |
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.
Why is only the 2nd line changed, not the 1st? It's made less pretty.
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.
I agree this threw me off. Also see from below:
- for (int r = 0; r < numRows; r++, v = 1 - v) {
+ for (var r = 0; r < numRows; r++, v = 1 - v) {
But, as @loic-sharma pointed out, I think this change enables this kind of thing:
- const int magic = 0;
+ const magic = 0;
Is that right @eernstg ?
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.
That's a good point!
The lints omit_obvious_local_variable_types
, specify_nonobvious_local_variable_types
and the corresponding ...property_types
all share the same notion of what it takes for an expression to have an 'obvious' type, so any change that moves this borderline will have consequences for all of those. This means that (1) any discussion about where to draw the line should be considered slightly more global in nature than this particular lint, and (2) deciding on something being 'obvious' implies that it is not 'nonobvious', and vice versa.
That said, I think this is a very good example showing something that should be modified.
The reason why line 178 is updated and 177 isn't updated is that rowV
is a local variable, and local variables are considered to have an obvious type, but v
is a formal parameter, and they aren't considered obvious.
The reason why local variables are considered to have an obvious type is that we only have to scan a limited amount of source code in order to find the declaration of a local variable, and it's considered reasonable to assume that the type of the local variable is obvious when we're looking at its declaration. (That is, if it doesn't have an initializing expression whose type is actually obvious then it should have an explicit type annotation.) I think this rule makes sense, and it certainly helps a lot of expressions to be considered obviously typed.
However, the same reasoning is completely valid for a formal parameter (which is, by the way, considered to be just another kind of local variable in specification texts, which might well be the reason why I missed that one. ;-)
So my response would be: Yes, the lints should be adjusted to consider formal parameters as obviously typed.
About constant variables:
- const int magic = 0;
+ const magic = 0;
Yes, a local constant variable is subject to the same treatment as other local variables. Given that it's equally easy to discern the type of the initializing expression when it is constant and when it is not, I don't think this should be a problem.
Do you think otherwise?
Overall I think this is an excellent brevity win. However, I dislike how this removes types when using a named constructor. From a call site, you cannot distinguish a named constructor from a static method. In my opinion, the type from a named constructor invocation is not obvious at the call site. For example: class Foo {
Foo.fromEntries(Map<String, dynamic> entries);
static int fromEntry(dynamic entry) => 5;
}
// Before - types are obvious
final Foo one = Foo.fromEntries({'name': 'example', 'value': 42});
final int two = Foo.fromEntry('example');
// After - what's the type of `one`?
// It's not obvious `Foo.fromEntries` is a named constructor
final one = Foo.fromEntries({'name': 'example', 'value': 42});
final int two = Foo.fromEntry('example'); |
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.
Is there any way to do this change in pieces? This PR is so big that my browser can barely handle navigating the Files tab here.
This actually didn't change nearly as much code as I thought it would (despite 2692 files changed). If you look at a file like material/text_field.dart, that is an 1800 line file, but only 3 lines were affected by the lints. I'm not sure if that is an argument in favor or against this change, but it's very interesting.
Overall, skimming through the changes, the code seems to come out looking reasonable.
@@ -175,7 +175,7 @@ List<OpacityPeepholeCase> allOpacityPeepholeCases = <OpacityPeepholeCase>[ | ||
name: 'Grid of Opacity', | ||
builder: (double v) { | ||
double rowV = v; | ||
double colV = rowV; | ||
var colV = rowV; |
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.
I agree this threw me off. Also see from below:
- for (int r = 0; r < numRows; r++, v = 1 - v) {
+ for (var r = 0; r < numRows; r++, v = 1 - v) {
But, as @loic-sharma pointed out, I think this change enables this kind of thing:
- const int magic = 0;
+ const magic = 0;
Is that right @eernstg ?
I would argue that most of the time as a reader/reviewer I don't really care what the exact type of a variable is since the typechcker / analyzer would catch any potential typing error, and the variable name is (or at least should be) much more helpful for understanding what it is for. Sometimes I do find it convenient that when the expression returns a collection, the type annotation tells me right away it's a List or a Map (which is usually not reflected in variable names), but I would also have to look at how the variable is used to see if the choice of data structure makes sense so that part of review is not local anyway. |
@loic-sharma wrote:
That's a good point, and I have had discussions about this previously. However, here's a reason why it could be justified, especially in the case where both If we encounter a declaration of the form (Of course, if it's OK to have some local variable declarations with a type annotation whose initializing expression actually calls a constructor then it's enough to have |
@justinmc wrote:
We could temporarily relax the lints: Disable |
@LongCatIsLooong wrote:
The underlying goal with the lint pair If you want to go further and eliminate some type annotations, even in a situation where the type isn't obvious, you could choose to enable If you want to go even further and discourage local variable type annotations entirely then you could forget all about the (In any case, declarations like I think the starting point for this particular effort is that both lints will be enabled. |
@eernstg This makes perfect sense for framework developers. However, in my mind the primary target audience for the framework's source code is not framework developers, but rather all Flutter app developers. That includes the next 1 million Flutter app developers, most of whom have never used Dart before and don't know which lints the framework has enabled. I'd prefer if we strove to make the framework's source code obvious to this target demographic, even though it makes the experience a little worse for framework developers. |
@loic-sharma wrote, about considering instance creations of the form
I always had the impression that the framework (and indeed any core element of Flutter, including the engine) would use a style which implies more work and more verbosity in return for added correctness support, but application developers would not be expected to use the same approach. In short, code which is widely (re)used would need more scrutiny than code which is only used by other libraries in the same application. With that, I'd expect Flutter app developers to already have a culture where code can be less heavily type annotated, compared to Flutter itself. With that, I'd think it's OK to primarily think about the Flutter framework and engine, and then trust the community to make choices that are optimized for their purposes. |
No description provided.