| // Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| // There are a few circumstances where implicit tear-off of `call` methods does |
| // not occur; this test exercises the user-visible static analysis behaviors |
| // arising from those circumstances. |
| |
| // NOTICE: This test checks the currently implemented behavior, even though the |
| // implemented behavior does not match the language specification. Until an |
| // official decision has been made about whether to change the implementation to |
| // match the specification, or vice versa, this regression test is intended to |
| // protect against inadvertent implementation changes. |
| |
| // A note on how the tests work: in several places we use the pattern |
| // `context<C>(b ? d : (E..expectStaticType<Exactly<T>>()))` (where `b` has type |
| // `bool` and `d` has type `dynamic`). This pattern ensures that `E` will be |
| // type analyzed with a context of `C`, and tests that the resulting expression |
| // has a type of `T`. However, the presence of `b ? d :` at the beginning |
| // ensures that the overall expression has type `dynamic`, so no assignability |
| // error will occur if types `C` and `T` are different. |
| |
| import "package:expect/expect.dart"; |
| |
| import '../static_type_helper.dart'; |
| |
| class A {} |
| |
| class C extends A { |
| void call() {} |
| void m() {} |
| } |
| |
| // These are top level getters rather than local variables to avoid triggering |
| // flow analysis. |
| bool get bTrue => true; |
| bool get bFalse => false; |
| |
| void testCascadeTarget() { |
| C c = C(); |
| // Even though the subexpression `c` has type `C` and context `void |
| // Function()`, we don't tear off `.call` for subexpressions that are the |
| // target of a cascade; instead, we tear-off `.call` on the full cascade |
| // expression. So `c..m()` is equivalent to `(c..m()).call` (which is valid) |
| // rather than `(c.call)..m()` (which is not). |
| context<void Function()>(c..m()); |
| |
| // Same as above, but confirm that extra parens around `c` don't change the |
| // behavior. |
| context<void Function()>((c)..m()); |
| context<void Function()>(((c))..m()); |
| } |
| |
| void testConditional() { |
| A a = A(); |
| C c = C(); |
| dynamic d = null; |
| // Even though the subexpression `c` has type `C` and context `void |
| // Function()`, we don't tear off `.call` for the `then` or `else` |
| // subexpressions of a conditional expression; instead, we tear off `.call` |
| // for the conditional expression as a whole (if appropriate). So, in |
| // `(bTrue ? c : a)..expectStaticType<...>()`, no implicit tearoff of `c` |
| // occurs, and the subexpression `bTrue ? c : a` gets assigned a static type |
| // of `A`. |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((bTrue ? c : a)..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((bFalse ? a : c)..expectStaticType<Exactly<A>>()))); |
| |
| // Same as above, but confirm that extra parens around `c` don't change the |
| // behavior. |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((bTrue ? (c) : a)..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((bFalse ? a : (c))..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((bTrue ? ((c)) : a)..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((bFalse ? a : ((c)))..expectStaticType<Exactly<A>>()))); |
| } |
| |
| void testIfNull() { |
| A a = A(); |
| A? aq = null; |
| C c = C(); |
| dynamic d = null; |
| // Even though the subexpression `c` has type `C` and context `void |
| // Function()?`, we don't tear off `.call` for the LHS of a `??` expression; |
| // instead, we tear off `.call` for the `??` expression as a whole (if |
| // appropriate). So, in |
| // `(c ?? a)..expectStaticType<...>()`, no implicit tearoff of `c` occurs, and |
| // the subexpression `c ?? a` gets assigned a static type of `A`. |
| Expect.throws(() => context<void Function()>(bFalse |
| ? d |
| : ((c ?? a) // ignore: dead_null_aware_expression |
| ..expectStaticType<Exactly<A>>()))); |
| |
| // In `(aq ?? c)..expectStaticType<...>()`, the situation is similar, but the |
| // context for `c` is (non-nullable) `void Function()`. |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((aq ?? c)..expectStaticType<Exactly<A>>()))); |
| |
| // Same as above, but confirm that extra parens around `c` don't change the |
| // behavior. |
| Expect.throws(() => context<void Function()>(bFalse |
| ? d |
| : (((c) ?? a) // ignore: dead_null_aware_expression |
| ..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((aq ?? (c))..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>(bFalse |
| ? d |
| : ((((c)) ?? a) // ignore: dead_null_aware_expression |
| ..expectStaticType<Exactly<A>>()))); |
| Expect.throws(() => context<void Function()>( |
| bFalse ? d : ((aq ?? ((c)))..expectStaticType<Exactly<A>>()))); |
| } |
| |
| main() { |
| testCascadeTarget(); |
| testConditional(); |
| testIfNull(); |
| } |