Skip to content

Analyzer: strict_raw_types difficult to comply with atomically #36445

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

Open
srawlins opened this issue Apr 2, 2019 · 12 comments
Open

Analyzer: strict_raw_types difficult to comply with atomically #36445

srawlins opened this issue Apr 2, 2019 · 12 comments
Labels
area-devexp For issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages. devexp-warning Issues with the analyzer's Warning codes P4 strict-analysis-needed Mark issues where strict-raw-types, strict-inference and similar analysis options were helpful type-enhancement A request for a change that isn't a bug

Comments

@srawlins
Copy link
Member

srawlins commented Apr 2, 2019

Basically I'd like to split up the STRICT_RAW_TYPE code into a few more, to make it an easier pill to swallow. Teams can either make their code compliant incrementally, or opt out of individual checks they feel they are too onerous. Something like:

  • STRICT_RAW_TYPE_LIST_LITERAL for var foo = [];
  • STRICT_RAW_TYPE_MAP_LITERAL for var foo = {};
  • STRICT_RAW_TYPE_SET_LITERAL
  • STRICT_RAW_TYPE_NAME for foo is List, foo as List, List foo, Future main(), main(List args), perhaps
  • STRICT_RAW_TYPE_CONSTRUCTOR for Future.value(), Set()
@srawlins
Copy link
Member Author

srawlins commented Apr 2, 2019

@matanlurey
Copy link
Contributor

Sounds good to me as long as there is a single flag we can enable in the future

@leafpetersen
Copy link
Member

The first three aren't raw type issues, they're implicit dynamic issues, right?

I'm fine with splitting things up, but I do wonder whether, if this is turning out to be such a bitter pill, we should reconsider and perhaps do something at least incrementally more sophisticated?

@srawlins
Copy link
Member Author

srawlins commented Apr 3, 2019

Yeah I was wondering that as well, Leaf. @jmesserly committed a lot of tests with the mode, so you can see some examples:

var rawConstructorCall = /*info:STRICT_RAW_TYPE*/C();
var rawList = /*info:STRICT_RAW_TYPE*/[];
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/[42 as dynamic];
var rawSetOfSets = </*info:STRICT_RAW_TYPE*/Set>{};
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/{42 as dynamic};
var rawMap = /*info:STRICT_RAW_TYPE*/{};
dynamic d;
var upwardsInfersDynamic1 = /*info:STRICT_RAW_TYPE*/{d: 2};

Not sure why I didn't comment on this during code review; I think all the "strict" modes have just been swimming around in my head. I agree these should be strict-inference: true or implicit-dynamic: false reports. If that's the case, then I can just keep that 4th example (foo is List, foo as List, List foo, Future main(), main(List args), class MyList extends List {}) as STRICT_RAW_TYPE. WDYT, @jmesserly ?

@srawlins
Copy link
Member Author

srawlins commented Apr 3, 2019

Oh, and my plan, @leafpetersen, is to bring up strict codes one by one for review by a few user teams. I personally enjoy complying with, e.g., strict-raw-types, but for very complex type hierarchies, I'd like team input. And I think @optionalTypeArgs will really help here. I think this will make for an incremental transition.

@kevmoo kevmoo added the legacy-area-analyzer Use area-devexp instead. label Apr 3, 2019
@jmesserly
Copy link

jmesserly commented Apr 4, 2019

Yeah, everything except STRICT_RAW_TYPE_NAME in @srawlins' list above could be argued to fall under strict-inference instead. I'd be fine with that split.

It may still be worth separate error codes within strict-inference, so constructors/literals are separate from generic functions/methods? I've noticed that users seem to conceptualize constructor/literal inference as distinct from generic method inference (even though they're essentially the same inference algorithm). So we may wish to reflect that in the error UX.


I'm fine with splitting things up, but I do wonder whether, if this is turning out to be such a bitter pill, we should reconsider and perhaps do something at least incrementally more sophisticated?

This has been my worry. Conceptually there is nothing wrong with no-implicit-dynamic but it proved too much. strict-raw-types along with strict-inference will be an improvement, because they're smarter: they focus on semantics (we had to guess a type) rather than syntax (a type wasn't written).

If we find those are still too hard to roll out, we could focus more narrowly. Implicit dynamic is fine until we'd do something that Object wouldn't allow, such as dynamic dispatch. So we could try to implement a rule like that, if needed. (The easiest way to implement it would be to change generic-inference/instantiate-to-bounds to use Object rather than dynamic as their default top type. IDK if that would worry the language team? EDIT: of course, this would only apply when the appropriate strictness flag is enabled.)

Overall, checking for implicit downcasts and unintended dynamic dispatch should also cover all of the potential runtime problems related to dynamic. (Of course, "unintended dynamic dispatch" is very tricky to define.)

@srawlins
Copy link
Member Author

I have confidence that we can land the (now) strict inference literals internally; not a hard sell. And I think a further split of STRICT_RAW_TYPE_NAME, as @jmesserly suggests, would help to pinpoint any spots where it might be less beneficial; but I think there could be a dozen :P e.g

  1. STRICT_RAW_TYPE_IN_DECLARATION for List foo (locals, fields, top-level)
  2. STRICT_RAW_TYPE_IN_PARAMETER for main(List args)
  3. STRICT_RAW_TYPE_IN_RETURN for Future main() (or combine with above as something like STRICT_RAW_TYPE_IN_FUNCTION_SIGNATURE)
  4. STRICT_RAW_TYPE_IN_IS for foo is List
  5. STRICT_RAW_TYPE_IN_AS for foo as List (or perhaps combine with above; STRICT_RAW_TYPE_IN_TYPE_PROMOTION or STRICT_RAW_TYPE_IN_EXPRESSION)?
  6. STRICT_RAW_TYPE_CONSTRUCTOR for Future.value(), Set()
  7. STRICT_RAW_TYPE_IN_CLASS_DECLARATION for class MyList extends List {}
  8. STRICT_RAW_TYPE_IN_TYPEDEF_DECLARATION for typedef AsyncCallback = Future Function()
  9. STRICT_RAW_TYPE_IN_TYPE_ANNOTATION for List<List> foo, class MyList extends List<Map> {} (or just walk a bit further up the AST from the raw type to find its in, e.g. a variable declaration or parameter, etc.)

Personally, I'm up for this; I can ask the "strict" teams about each individual check for their opinion or experience; I can mail out targeted changes which eliminate violations of each individual code.

Regarding dynamic invocation; this would be a pretty sweet check, but I think orthogonal; strict-raw-types and strict-inference are primarily designed to catch unintentional casts.

@leafpetersen
Copy link
Member

This seems right to me. For the examples above, I would think of them as follows:

var rawConstructorCall = /*info:STRICT_RAW_TYPE*/C();  // raw type
var rawList = /*info:STRICT_RAW_TYPE*/[]; // strict inference
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/[42 as dynamic]; // probably no implicit dynamic
var rawSetOfSets = </*info:STRICT_RAW_TYPE*/Set>{}; // raw type
var upwardsInfersDynamic = /*info:STRICT_RAW_TYPE*/{42 as dynamic}; // no implicit dynamic
var rawMap = /*info:STRICT_RAW_TYPE*/{}; // strict inference
dynamic d;
var upwardsInfersDynamic1 = /*info:STRICT_RAW_TYPE*/{d: 2}; // no implicit dynamic

Splitting out the additional raw types could be useful, not sure.

@jmesserly
Copy link

@leafpetersen -- just for curiosity's sake, what's the mental model for treating list/set/map literals as distinct from constructor invocations?

We don't have rest parameters, but if we did, these two seem equivalent:

class C<T> {
  C(List<T> ...args); // made up syntax for rest parameters.
}
f() {
  // with some variables: x, y, and z in scope
  var list = [x, y, z];
  var c = C(x, y, z);
}

It seems like they're all conceptually constructor invocations, and use the same inference algorithm. But maybe that's my bias towards semantics :). Syntactically "C" is an identifier referring to a class (similar to "List"), whereas "[]" does not refer to a class directly (i.e. it implies the List class, but does not name it explicitly).

@jmesserly
Copy link

jmesserly commented Apr 11, 2019

Thinking about this more... in new C(), C refers to a constructor rather than a type. Whereas in the type annotation like C c;, C refers to a type. So maybe the constructor invocations belong under "strict-inference"?

@srawlins
Copy link
Member Author

Oops, yes, 100% agree, @jmesserly ; I would mark those as strict-inference; I'll make this change.

@leafpetersen
Copy link
Member

leafpetersen commented Apr 11, 2019

@leafpetersen -- just for curiosity's sake, what's the mental model for treating list/set/map literals as distinct from constructor invocations?

I'm not sure I am treating them differently, but maybe I am. My thinking is this:

class C<T> {
  C([T x]);
  C.named();
}

void test() {
  new C();
  new C.named();
}

I think I would definitely argue that the first constructor call is definitely a strict inference failure: inference has to guess. It is also arguably a strict raw type failure, since the way that inference guess is by treating it as a raw type.

The second constructor call feels a little different to me, but probably isn't. Inference still has to guess, but in this case it always has to guess (unless there's a downward context). So probably should still be a strict inference failure? And also could arguably a strict raw type failure.

So maybe the constructor invocations belong under "strict-inference"?

I think they should definitely fall under strict inference. If we allow people to specify strict raw types independently of strict inference, we could flag them there as well, but I think this is probably not worthwhile. I'd say make strict raw types strictly about types.

dart-bot pushed a commit that referenced this issue Apr 11, 2019
Bug: #36445 #33749
Change-Id: I127d671840bfcfe3857225932164fa4098963715
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99085
Commit-Queue: Samuel Rawlins <[email protected]>
Reviewed-by: Brian Wilkerson <[email protected]>
dart-bot pushed a commit that referenced this issue Apr 12, 2019
…_CREATION

Bug: #33749 #36445
Change-Id: Ic34f0e07b1cc5cbbc265ab2c32cabc61d7901c0f
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/99264
Reviewed-by: Brian Wilkerson <[email protected]>
Commit-Queue: Samuel Rawlins <[email protected]>
dart-bot pushed a commit that referenced this issue May 6, 2019
Bug: #36445
Change-Id: Ic716d89b1a6d00945a74c7484f8301403b8ea371
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101420
Reviewed-by: Brian Wilkerson <[email protected]>
Commit-Queue: Samuel Rawlins <[email protected]>
@srawlins srawlins added the P4 label Jan 11, 2021
@eernstg eernstg added the strict-analysis-needed Mark issues where strict-raw-types, strict-inference and similar analysis options were helpful label Apr 18, 2023
@srawlins srawlins added the type-enhancement A request for a change that isn't a bug label Mar 13, 2024
@srawlins srawlins added the devexp-warning Issues with the analyzer's Warning codes label Aug 2, 2024
@bwilkerson bwilkerson added area-devexp For issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages. and removed legacy-area-analyzer Use area-devexp instead. labels Feb 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area-devexp For issues related to the analysis server, IDE support, linter, `dart fix`, and diagnostic messages. devexp-warning Issues with the analyzer's Warning codes P4 strict-analysis-needed Mark issues where strict-raw-types, strict-inference and similar analysis options were helpful type-enhancement A request for a change that isn't a bug
Projects
None yet
Development

No branches or pull requests

7 participants