-
Notifications
You must be signed in to change notification settings - Fork 1.7k
[lint] [omit_obvious_local_variable_types] false positive #59957
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
Comments
The reason why the type of The iterable is a local variable, (If you want that then The underlying rationale is that the developer who wrote that local variable declaration (of In short, when it comes to local variables that depend on each other, the rule of thumb is as follows: "If some of these variables need a type annotation then go to the root cause rather than fixing it downstream". This would make the code more evidently typed overall with a small number of type annotations (most dependency trees have more nodes near the leaves than near the root). We could adopt other approaches. E.g., the type of a local variable could be considered to be non-obvious if its declaration has an initializing expression whose type isn't obvious (which could then rely on recursive queries in cases like |
We can see with paths that it is not always the case. If paths type was obvious I would agree with your lint but because here var is used it is not obvious. Your assumption about local variable obviousness is not always right. I think you should revisit this assumption and see if var is used on local variables before deciding it's obvious |
@stephane-archer, it sounds like you'd prefer the version in void early() {
Iterable<String> paths = dropDoneDetails.files.map((e) { // <---
return e.path;
});
for (var path in paths) { // <---
if (FileSystemEntity.isDirectorySync(path)) {
await lutFoldersNotifier.addLutDir(path);
}
}
}
void late() {
var paths = dropDoneDetails.files.map((e) { // <---
return e.path;
});
for (String path in paths) { // <---
if (FileSystemEntity.isDirectorySync(path)) {
await lutFoldersNotifier.addLutDir(path);
}
}
} The lint will flag the I wasn't suggesting that It would of course also be possible to consider local variables to have a non-obvious type, but my experience with this approach (which was the approach I used at first, and for a while) is that it gets quite noisy (that is, there will be type annotations all over the place). We could also say that a local variable has an obvious type if its declaration has an initializing expression whose type is obvious, or if it has a type annotation (and in both cases: if it isn't promoted). Thinking about it. ;-) |
@eernstg void early() {
Iterable<String> paths = dropDoneDetails.files.map((e) {
return e.path;
});
for (String path in paths) { // <- omit_obvious_local_variable_types warning
if (FileSystemEntity.isDirectorySync(path)) {
await lutFoldersNotifier.addLutDir(path);
}
}
} I would agree with the lint because From my understanding, you explain that this warning forces you to write your code like the
in the original code, we can see this assumption is not true. var str = "hello word" // ok var paths = dropDoneDetails.files.map((e) { // not ok, we need the presence of type annotation to assume that
return e.path;
}); I hope you can consider my point and I'm happy to discuss about it. |
This lint tells you to not type variables whose types are obvious. I think that's a reasonable base assumption. If you want to catch cases where a variable isn't obvious, you need the other lint. |
I used var on paths not because his type was obvious but because I didn't care about the type as long as I had a string in the for loop. Here the lint mentions that string is an obvious type and it's not if you look at the original function. I see this has a false positive for the lint because it asks you to remove a type that is not obvious to see without the type annotation |
@stephane-archer wrote:
I can certainly follow your line of reasoning: The type annotation on However, this line of reasoning doesn't quite match the notion of "having an obvious type" that governs the behavior of the lints This works fine for However, we don't get to use declarations without a type annotation often enough just based on these fully unambiguous cases. In practice, there are many other expressions that deserve to be treated as "obviously typed". This is a policy rather than an objective fact, so we can't expect to settle a debate about where to draw the line based on hard facts alone, this will to some extent be an opinionated choice. So we include constructor invocations like This means that "having an obvious type" relies on very foundational properties, like "this is a constructor" vs. "this is a static method", and a reader of the code doesn't need to look up the declaration of that constructor in order to know more about the type of the result. For example, type arguments must be provided explicitly in order to make the type obvious (and the reader doesn't need to look up the constructor declaration in order to reason about how those actual type arguments could be inferred from the types of the actual arguments). The treatment of local variables is similar: It should be enough to know that a particular name denotes a local variable, and then it's classified in a certain way by the During my early experiments with the lints I hadn't done anything special about local variables. This gives rise to a very heavily type annotated style (because So we have a conflict: In isolation, your argument makes sense (that is, |
@eernstg I think we both agree that this lint assumes a local variable has an obvious type and my example shows that is not always the case. If we consider that local variable types have obvious types when they are annotated, then the lint will work as usual for people who use I think the main issue I have with this lint only concerns loops. |
Yes, I do agree. I also understand that the case you mentioned is a strong counter example because the type of However, I prefer the remedy which is to use a style where every local variable is sufficiently clearly typed to justify the rule that the type of a local variable is always "obvious", rather than the remedy which is to classify the type of some local variables as non-obvious, and then propagating that status to any number of other local variables (e.g., from You mention that you don't care about the type of The point is that the following is a simple policy which will help keeping the total number of type annotations low while preserving a clear typing of all expressions: "Local variables have an obvious type, and if you don't think it is sufficiently obvious then please add a type annotation to the earliest possible point in the dependency graph". Otherwise (if this policy is not adopted), I'm worried about the amount of type annotations which will be held up as appropriate if you enable both Some local variables have a type which could never be determined from the type of an initializing expression. For example, For all other local variables with an initializing expression, there will be a unique recommendation if both This is an automatic judgment, and the point is that (1) we don't have to spend a lot of time discussing whether or not a given local variable should have a type annotation, because the lints will say so, and (2) we can use This means that if we change In the end, the overall "goal" of having these lints is that the code should be as readable as possible, with as few local variable type annotations as possible, and I'm afraid the code will have a lot more type annotations if we start considering some local variables to have a non-obvious type. |
You explained my point perfectly. I'm still in favor of omitting the lint warning only in "for loop" if the type is not annotated. var a;
for (int i in a) { // no warning
} List<int> a;
for (int i in a) { // warning
} But I have no more evidence to show and you have well understood my point, I trust in your judgment. Should we close this issue? 🙂 |
Thanks for a very constructive and elucidating discussion! Still thinking about it. I think it might be helpful to communicate to developers how the notion of "obviously typed" is defined, and in particular to mention that all local variables are classified as obviously typed, and the recommended remedy in the case where this isn't true is to add an explicit type annotation to the variable whose type isn't obvious. @bwilkerson, what do you think about adding a link to the lint message, pointing to this kind of information? |
@eernstg I enjoyed the conversation and learn something 😊 |
Not surprisingly, I think improved documentation is a good idea. :-) We've been discussing the best way to do this (in general, not for this specific lint). The direction we've been discussing is to have two pieces of documentation. The first would explain what a lint does and why/when you'd want to have it enabled. The second would be an explanation of why the lint produced a diagnostic in a specific location (see https://dart.dev/tools/diagnostic-messages for examples). For lint rules, the second set of docs would point back to the first, making it easier for users to find the longer descriptions/definitions. I think the kind of documentation you're describing (such as what the lint means by "obvious") would fit nicely into the first set of docs. |
Very good! However, I guess this page would fit the description
and that page is already among the longest lint description pages. This is also the reason why it doesn't list the precise rules about which expressions have an "obvious" type. Similarly for the other lints whose name contains 'obvious' (they all rely on the same implementation of |
@eernstg I'm not sure the lint suffers from a lack of documentation. |
We could. Or we could (probably) add another page that would explain what "obvious" means and link to it from the other four pages. @MaryaBelanger @parlough For opinions. |
We used to have the diagnostics glossary for things like this, which is now just the site-wide glossary. I think an "Obvious (type)" entry could easily go there. I could also see adding an "obvious type" section to the Type system page, too. What are the "30-odd lines of text" we're referring to? Is the definition generally just these few lines from the page Erik shared?
If there's more to be said about "obvious", let's add it to the site. But if the 3 sentences above (or some form of them) captures the definition enough, I think adding it to each lint is fine. We could even shorten it to 1 (long) line:
From reading the thread though I don't think documentation closes this issue? But do agree we should add it! |
This sounds like a very good placement! I think it's better than the Type system page because it's a concept which is used in several lints, it is not a part of the language as such. A proper definition of expressions whose type is obvious is going to be quite similar to the section 'Obvious expression types' in the original posting of this issue. Moreover, we need to document the policy of considering local variables as obviously typed, no matter what, and recommending that a local variable is equipped with a type annotation when its type actually isn't obvious. |
Omit the type annotation on a local variable when the type is obvious. Try removing the type annotation.
for me, there is nothing obvious here that makes
path
aString
except the name.So I think this is a false positive for
omit_obvious_local_variable_types
The text was updated successfully, but these errors were encountered: