-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
I wish we had --no-dynamic-calls
as a lint
#57703
Comments
The map entry is indeed an explicit dynamic. You have no static information about the In dynamic code, you are in charge of types, so either do: var userIds = linkData['userIds'].map<int>((id) => int.parse(id)); or List userIdStrings = linkData['userIds'];
var userIds = userIdStrings.map((id) => int.parse(id)); or var userIds = linkData['userIds'].cast<String>().map(int.parse); (which allows you to do as the style guide recommends). I'm not sure what there is to fix here, short of removing either |
@matanlurey What's the right lint here? Some possibilities:
cc @munificent |
Thanks @lrhn @leafpetersen. Some replies below:
Part of the issue is the user didn't know they were making a dynamic call. It's quite hard to tell.
A creative option might be you can only assign a dynamic call to something typed void doSomethingA(dynamic list) {
// Lint
Iterable<String> x = list.map((e) => '$e');
}
void doSomethingB(dynamic list) {
// OK
dynamic x = list.map((e) => '$e');
}
Sounds right. I think if you say "it's OK to dcall when the assignee is also dynamic" it might remove the boilerplate of |
Yes, this is the answer I've gotten for almost 5 years now. I actually have it enabled myself, but:
Of course, it also doesn't catch refactors. I've hit cases internally quite a bit where I used to get back a say, |
@matanlurey Totally agree. |
This is pretty frustrating, but it's hard to fix this cleanly while still actually having Instead, here's some other ideas:
|
Right. Something like
I don't think we need to require it, but I think being able to opt-in to those semantics is ideal. For example I'd think that framework authors would want to make sure they are never using dcalls on accident (as is this case), and certainly some teams would as well.
Wasn't my code. Was helping a team debug, and as you mentioned it's hard to change the type down-stream several hops (might be code gen'd etc). It really seems like it needs to be something that the invocation level, or |
+1 on being able to opt-in to |
Doesn't that opt-in already exist, though? Use |
No, because:
... believe me, my team cares a lot about this, and even we've added d-calls on accident. |
The problem is when it's dynamic due to inference rules and we're trained to use var expectingListOfStrings = someList.fold(<String>[], (list, value) {
list.add(getStringForValue(value));
return list;
}); Note that because the callback takes Another can come up when we don't want to introduce an intermediate variable for the sake of avoiding var result = someMapOfDynamic['key'] + 1; // I want a warning here. vs Object value = someMapOfDynamic['key'];
var result = value + 1; // I get a warning here, but I'd never write code like this |
Good examples @natebosch! |
--no-dynamic-calls
as a lint
You happened to catch some errors, but not all. Consider: linkData['userIds'].forEach(invokeSomeFunctionInAnotherLibrary); ... here there is a d-call |
FWIW, I was planning to try a DDC flag for |
The tracking bug for the DDC flag is #30481. Originally I'd thought of the flag for my own benefit: it's easy to accidentally introduce |
We definitely want a lint for this, but the DDC support will probably happen first. |
/cc @srawlins @leafpetersen @vsmenon We (AngularDart) and Sam (google3) would be happy to help develop/test/rollout this flag. It definitely would be better to be a static analysis warning/error than a runtime (DDC-only) behavior, though. |
It would be a compile-time (static analysis) flag in DDC. Agree that linter flag is more useful though. If we can enable that lint from DDC, it would be ideal, saves me some work 👍 |
Oh ok, I see. Yeah, even just in DDC would be a good step (better than nothing). I think for our internal users though, folks would be annoyed if the analyzer didn't flag code that the compiler would, if that makes sense. Just thinking out loud, I'm wondering what a good pattern for "intentional" dynamic calls is:
What if we added in, as an example, /// Returns an object that may be dynamically invoked without flagging `--strict-dynamic-calls`.
dynamic allowDynamicCall(Object any) => ... It might read better, for example: void example(dynamic x) {
// LINT: strict_dynamic_calls
x.doUntyped();
// OK
allowDynamicCall(x).doUntyped();
} |
About |
That's true! I don't like everything about those flags though:
If both of those were changed, I'd consider using the comment notation, but until then, similar to my request for |
Relevant: TypeScript's new I'm hesitant to get excited about yet another magical type, but it's a good reference for research. |
How is |
I think mechanically it's the same, but it signals a different intent. "Object" says "I work with any object of any type". "unknown" would say "I don't have a way to express the set of types I work with.". |
"unknown" would also presumably not allow implicit casts like "Object" does today. |
Hi @denniskaselow! I don't think this is directly related to Instead, you might want to file your support/interest of: |
Another use case might be linting |
Late to the thread here, but given that the only difference between Object and dynamic is that dynamic allows dynamic calls, should this not be a lint like "no dynamic" ? Rather than linting:
why not lint the
I suppose the obvious counterargument is if you import a lib that gives you dynamic, you still want to be warned when you use it. But it seems like with no_dynamic_calls, that should imply no_dynamic, so maybe it should just be one lint, no_dynamic, which catches both dynamic calls, and, dynamic declarations (implicit or explicit). |
Using
There are many many many reasons that this can't be fixed without linting the actual call. |
yeah, and we have previous experience with |
Amazing @matanlurey! I'll work on rolling a new release with this in it early next week. 👍 |
@matanlurey final root = jsonDecode(jsonStr);
if (root['foo'] != null) {
final foo = root['foo'] as String;
...
} then the linter is throwing a bunch of |
You can (and I'd recommend to) write that as: Map<String, dynamic> root = jsonDecode(jsonStr);
String? foo = root['foo'];
if (foo != null) {
...
} If you want to avoid implicit downcasts too, that would be: var root = jsonDecode(jsonStr) as Map<String, dynamic>;
var foo = root['foo'] as String?;
if (foo != null) {
...
} but it means exactly the same thing. |
Filing under "please lets fix this in Dart 2.1+".
Migrating internal code to Dart2, I hit the following fun error case:
... throws ...
type 'List<dynamic>' is not a subtype of type 'List<int>'
Huh? Shouldn't this work:
It turns out, no!
.map
is a dcall, becauselinkData['userIds']
isdynamic
. DOH!Would
--no-implicit-dynamic
fix this? It seems like its an "explicit dynamic" (the map is typed of having values of typedynamic
explicitly). It sounds like we'd need something strong_er_, i.e .--no-dynamic-calls
./cc @leafpetersen @lrhn
The text was updated successfully, but these errors were encountered: