Skip to content

Commit 9803d5c

Browse files
Kevin Millikincommit-bot@chromium.org
Kevin Millikin
authored andcommitted
Signal an error when invoking a non-function
When invoking an expression whose static type is not a function type, signal an error. However, allow invocations of expressions with static type `dynamic` or `Function`. Fixes #32975. Change-Id: Ia54d8df650076ad5c9c9c3a2c6f79ea31acbbbfe Reviewed-on: https://dart-review.googlesource.com/72082 Commit-Queue: Kevin Millikin <[email protected]> Reviewed-by: Daniel Hillerström <[email protected]>
1 parent 02b2dee commit 9803d5c

18 files changed

+95
-37
lines changed

pkg/front_end/lib/src/fasta/fasta_codes_generated.dart

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4666,6 +4666,26 @@ const MessageCode messageInvalidVoid = const MessageCode("InvalidVoid",
46664666
tip:
46674667
r"""Try removing 'void' keyword or replace it with 'var', 'final', or a type.""");
46684668

4669+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
4670+
const Template<Message Function(String name)> templateInvokeNonFunction =
4671+
const Template<Message Function(String name)>(
4672+
messageTemplate:
4673+
r"""'#name' isn't a function or method and can't be invoked.""",
4674+
withArguments: _withArgumentsInvokeNonFunction);
4675+
4676+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
4677+
const Code<Message Function(String name)> codeInvokeNonFunction =
4678+
const Code<Message Function(String name)>(
4679+
"InvokeNonFunction", templateInvokeNonFunction,
4680+
analyzerCode: "INVOCATION_OF_NON_FUNCTION", severity: Severity.error);
4681+
4682+
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
4683+
Message _withArgumentsInvokeNonFunction(String name) {
4684+
return new Message(codeInvokeNonFunction,
4685+
message: """'${name}' isn't a function or method and can't be invoked.""",
4686+
arguments: {'name': name});
4687+
}
4688+
46694689
// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
46704690
const Template<
46714691
Message Function(String name)> templateLabelNotFound = const Template<

pkg/front_end/lib/src/fasta/kernel/kernel_shadow_ast.dart

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -608,8 +608,8 @@ abstract class ComplexAssignmentJudgment extends SyntheticExpressionJudgment {
608608
.isOverloadedArithmeticOperatorAndType(combinerMember, readType);
609609
}
610610
DartType rhsType;
611-
var combinerType =
612-
inferrer.getCalleeFunctionType(combinerMember, readType, false);
611+
var combinerType = inferrer.getCalleeFunctionType(
612+
inferrer.getCalleeType(combinerMember, readType), false);
613613
if (isPreIncDec || isPostIncDec) {
614614
rhsType = inferrer.coreTypes.intClass.rawType;
615615
} else {
@@ -1554,8 +1554,8 @@ class IndexAssignmentJudgment extends ComplexAssignmentJudgmentWithReceiver {
15541554
// To replicate analyzer behavior, we base type inference on the write
15551555
// member. TODO(paulberry): would it be better to use the read member
15561556
// when doing compound assignment?
1557-
var calleeType =
1558-
inferrer.getCalleeFunctionType(writeMember, receiverType, false);
1557+
var calleeType = inferrer.getCalleeFunctionType(
1558+
inferrer.getCalleeType(writeMember, receiverType), false);
15591559
DartType expectedIndexTypeForWrite;
15601560
DartType indexContext = const UnknownType();
15611561
DartType writeContext = const UnknownType();
@@ -1581,8 +1581,8 @@ class IndexAssignmentJudgment extends ComplexAssignmentJudgmentWithReceiver {
15811581
if (read != null) {
15821582
var readMember =
15831583
inferrer.findMethodInvocationMember(receiverType, read, silent: true);
1584-
var calleeFunctionType =
1585-
inferrer.getCalleeFunctionType(readMember, receiverType, false);
1584+
var calleeFunctionType = inferrer.getCalleeFunctionType(
1585+
inferrer.getCalleeType(readMember, receiverType), false);
15861586
inferrer.ensureAssignable(
15871587
getPositionalParameterType(calleeFunctionType, 0),
15881588
indexType,

pkg/front_end/lib/src/fasta/type_inference/type_inferrer.dart

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ import '../fasta_codes.dart'
8787
templateInvalidCastNewExpr,
8888
templateInvalidCastStaticMethod,
8989
templateInvalidCastTopLevelFunction,
90+
templateInvokeNonFunction,
9091
templateMixinInferenceNoMatchingClass,
9192
templateUndefinedGetter,
9293
templateUndefinedMethod,
@@ -969,14 +970,12 @@ abstract class TypeInferrerImpl extends TypeInferrer {
969970
}
970971
}
971972

972-
FunctionType getCalleeFunctionType(
973-
Object interfaceMember, DartType receiverType, bool followCall) {
974-
var type = getCalleeType(interfaceMember, receiverType);
975-
if (type is FunctionType) {
976-
return type;
977-
} else if (followCall && type is InterfaceType) {
978-
var member = _getInterfaceMember(type.classNode, callName, false);
979-
var callType = getCalleeType(member, type);
973+
FunctionType getCalleeFunctionType(DartType calleeType, bool followCall) {
974+
if (calleeType is FunctionType) {
975+
return calleeType;
976+
} else if (followCall && calleeType is InterfaceType) {
977+
var member = _getInterfaceMember(calleeType.classNode, callName, false);
978+
var callType = getCalleeType(member, calleeType);
980979
if (callType is FunctionType) {
981980
return callType;
982981
}
@@ -1634,20 +1633,30 @@ abstract class TypeInferrerImpl extends TypeInferrer {
16341633
isOverloadedArithmeticOperator = typeSchemaEnvironment
16351634
.isOverloadedArithmeticOperatorAndType(interfaceMember, receiverType);
16361635
}
1637-
var calleeType =
1638-
getCalleeFunctionType(interfaceMember, receiverType, !isImplicitCall);
1636+
var calleeType = getCalleeType(interfaceMember, receiverType);
1637+
var functionType = getCalleeFunctionType(calleeType, !isImplicitCall);
1638+
if (interfaceMember != null &&
1639+
calleeType is! DynamicType &&
1640+
calleeType != coreTypes.functionClass.rawType &&
1641+
identical(functionType, unknownFunction)) {
1642+
var parent = expression.parent;
1643+
kernel.Expression error = helper.wrapInProblem(expression,
1644+
templateInvokeNonFunction.withArguments(methodName.name), noLength);
1645+
parent?.replaceChild(expression, error);
1646+
return new ExpressionInferenceResult(null, const DynamicType());
1647+
}
16391648
var checkKind = preCheckInvocationContravariance(receiver, receiverType,
16401649
interfaceMember, desugaredInvocation, arguments, expression);
1641-
var inferenceResult = inferInvocation(
1642-
typeContext, fileOffset, calleeType, calleeType.returnType, arguments,
1650+
var inferenceResult = inferInvocation(typeContext, fileOffset, functionType,
1651+
functionType.returnType, arguments,
16431652
isOverloadedArithmeticOperator: isOverloadedArithmeticOperator,
16441653
receiverType: receiverType);
16451654
var inferredType = inferenceResult.type;
16461655
if (methodName.name == '==') {
16471656
inferredType = coreTypes.boolClass.rawType;
16481657
}
16491658
handleInvocationContravariance(checkKind, desugaredInvocation, arguments,
1650-
expression, inferredType, calleeType, fileOffset);
1659+
expression, inferredType, functionType, fileOffset);
16511660
int resultOffset = arguments.fileOffset != -1
16521661
? arguments.fileOffset
16531662
: expression.fileOffset;

pkg/front_end/messages.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2675,3 +2675,16 @@ ReturnFromVoidFunction:
26752675
RethrowNotCatch:
26762676
template: "'rethrow' can only be used in catch clauses."
26772677
analyzerCode: RETHROW_OUTSIDE_CATCH
2678+
2679+
InvokeNonFunction:
2680+
template: "'#name' isn't a function or method and can't be invoked."
2681+
severity: ERROR
2682+
analyzerCode: INVOCATION_OF_NON_FUNCTION
2683+
script: |
2684+
class Foo {
2685+
int f;
2686+
}
2687+
main() {
2688+
Foo foo = new Foo();
2689+
foo.f();
2690+
}

pkg/front_end/testcases/inference/call_corner_cases.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,12 @@ class D {
2020
B get getB => new B();
2121
}
2222

23-
main() {
23+
test() {
2424
var /*@type=int*/ callA = new A() /*@target=A::call*/ ();
2525
var /*@type=int*/ callFieldA = new D(). /*@target=D::fieldA*/ fieldA();
2626
var /*@type=int*/ callGetA = new D(). /*@target=D::getA*/ getA();
2727
var /*@type=dynamic*/ callFieldB = new D(). /*@target=D::fieldB*/ fieldB();
2828
var /*@type=dynamic*/ callGetB = new D(). /*@target=D::getB*/ getB();
2929
}
30+
31+
main() {}

pkg/front_end/testcases/inference/call_corner_cases.dart.direct.expect

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ class D extends core::Object {
2727
get getB() → self::B
2828
return new self::B::•();
2929
}
30-
static method main() → dynamic {
30+
static method test() → dynamic {
3131
dynamic callA = new self::A::•().call();
3232
dynamic callFieldA = new self::D::•().fieldA();
3333
dynamic callGetA = new self::D::•().getA();
3434
dynamic callFieldB = new self::D::•().fieldB();
3535
dynamic callGetB = new self::D::•().getB();
3636
}
37+
static method main() → dynamic {}

pkg/front_end/testcases/inference/call_corner_cases.dart.direct.transformed.expect

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,11 @@ class D extends core::Object {
2727
get getB() → self::B
2828
return new self::B::•();
2929
}
30-
static method main() → dynamic {
30+
static method test() → dynamic {
3131
dynamic callA = new self::A::•().call();
3232
dynamic callFieldA = new self::D::•().fieldA();
3333
dynamic callGetA = new self::D::•().getA();
3434
dynamic callFieldB = new self::D::•().fieldB();
3535
dynamic callGetB = new self::D::•().getB();
3636
}
37+
static method main() → dynamic {}

pkg/front_end/testcases/inference/call_corner_cases.dart.outline.expect

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,7 @@ class D extends core::Object {
2424
get getB() → self::B
2525
;
2626
}
27+
static method test() → dynamic
28+
;
2729
static method main() → dynamic
2830
;

pkg/front_end/testcases/inference/call_corner_cases.dart.strong.expect

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@ class D extends core::Object {
2727
get getB() → self::B
2828
return new self::B::•();
2929
}
30-
static method main() → dynamic {
30+
static method test() → dynamic {
3131
core::int callA = new self::A::•().{self::A::call}();
3232
core::int callFieldA = new self::D::•().{self::D::fieldA}();
3333
core::int callGetA = new self::D::•().{self::D::getA}();
34-
dynamic callFieldB = new self::D::•().{self::D::fieldB}();
35-
dynamic callGetB = new self::D::•().{self::D::getB}();
34+
dynamic callFieldB = let dynamic _ = null in let final dynamic #t1 = let dynamic _ = null in invalid-expression "pkg/front_end/testcases/inference/call_corner_cases.dart:27:69: Error: 'fieldB' isn't a function or method and can't be invoked.
35+
var /*@type=dynamic*/ callFieldB = new D(). /*@target=D::fieldB*/ fieldB();
36+
^" in let final dynamic #t2 = new self::D::•().{self::D::fieldB}() in null;
37+
dynamic callGetB = let dynamic _ = null in let final dynamic #t3 = let dynamic _ = null in invalid-expression "pkg/front_end/testcases/inference/call_corner_cases.dart:28:65: Error: 'getB' isn't a function or method and can't be invoked.
38+
var /*@type=dynamic*/ callGetB = new D(). /*@target=D::getB*/ getB();
39+
^" in let final dynamic #t4 = new self::D::•().{self::D::getB}() in null;
3640
}
41+
static method main() → dynamic {}

pkg/front_end/testcases/inference/call_corner_cases.dart.strong.transformed.expect

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,10 +27,15 @@ class D extends core::Object {
2727
get getB() → self::B
2828
return new self::B::•();
2929
}
30-
static method main() → dynamic {
30+
static method test() → dynamic {
3131
core::int callA = new self::A::•().{self::A::call}();
3232
core::int callFieldA = new self::D::•().{self::D::fieldA}();
3333
core::int callGetA = new self::D::•().{self::D::getA}();
34-
dynamic callFieldB = new self::D::•().{self::D::fieldB}();
35-
dynamic callGetB = new self::D::•().{self::D::getB}();
34+
dynamic callFieldB = let<BottomType> _ = null in let final dynamic #t1 = let<BottomType> _ = null in invalid-expression "pkg/front_end/testcases/inference/call_corner_cases.dart:27:69: Error: 'fieldB' isn't a function or method and can't be invoked.
35+
var /*@type=dynamic*/ callFieldB = new D(). /*@target=D::fieldB*/ fieldB();
36+
^" in let final dynamic #t2 = new self::D::•().{self::D::fieldB}() in null;
37+
dynamic callGetB = let<BottomType> _ = null in let final dynamic #t3 = let<BottomType> _ = null in invalid-expression "pkg/front_end/testcases/inference/call_corner_cases.dart:28:65: Error: 'getB' isn't a function or method and can't be invoked.
38+
var /*@type=dynamic*/ callGetB = new D(). /*@target=D::getB*/ getB();
39+
^" in let final dynamic #t4 = new self::D::•().{self::D::getB}() in null;
3640
}
41+
static method main() → dynamic {}

pkg/front_end/testcases/inference/dynamic_methods.dart.strong.expect

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class Foo extends core::Object {
1212
static method test() → dynamic {
1313
dynamic d = new self::Foo::•();
1414
core::int get_hashCode = d.{core::Object::hashCode};
15-
dynamic call_hashCode = d.{core::Object::hashCode}();
15+
dynamic call_hashCode = let dynamic _ = null in let final dynamic #t1 = let dynamic _ = null in invalid-expression "pkg/front_end/testcases/inference/dynamic_methods.dart:16:39: Error: 'hashCode' isn't a function or method and can't be invoked.
16+
d. /*@target=Object::hashCode*/ hashCode();
17+
^" in let final dynamic #t2 = d.{core::Object::hashCode}() in null;
1618
core::String call_toString = d.{core::Object::toString}();
1719
dynamic call_toStringArg = d.toString(color: "pink");
1820
dynamic call_foo0 = d.foo();

pkg/front_end/testcases/inference/dynamic_methods.dart.strong.transformed.expect

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ class Foo extends core::Object {
1212
static method test() → dynamic {
1313
dynamic d = new self::Foo::•();
1414
core::int get_hashCode = d.{core::Object::hashCode};
15-
dynamic call_hashCode = d.{core::Object::hashCode}();
15+
dynamic call_hashCode = let<BottomType> _ = null in let final dynamic #t1 = let<BottomType> _ = null in invalid-expression "pkg/front_end/testcases/inference/dynamic_methods.dart:16:39: Error: 'hashCode' isn't a function or method and can't be invoked.
16+
d. /*@target=Object::hashCode*/ hashCode();
17+
^" in let final dynamic #t2 = d.{core::Object::hashCode}() in null;
1618
core::String call_toString = d.{core::Object::toString}();
1719
dynamic call_toStringArg = d.toString(color: "pink");
1820
dynamic call_foo0 = d.foo();

runtime/lib/errors_patch.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class _AssertionError extends Error implements AssertionError {
4545
return condition;
4646
}
4747
if (condition is _Closure) {
48-
return condition.call();
48+
return (condition as dynamic)();
4949
}
5050
if (condition is Function) {
5151
condition = condition();

tests/co19/co19-kernel.status

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44

55
[ $compiler == fasta ]
66
Language/Classes/Constructors/Generative_Constructors/final_variables_t01: CompileTimeError # Expects a warning, but this is an error in Dart 2
7+
Language/Expressions/Method_Invocation/Ordinary_Invocation/function_type_t01: CompileTimeError # Expects a warning, but this is an error in Dart 2
8+
Language/Expressions/Method_Invocation/Ordinary_Invocation/static_type_t01: CompileTimeError # Expects a warning, but this is an error in Dart 2
79
Language/Functions/Formal_Parameters/Optional_Formals/default_value_t01: MissingCompileTimeError
810
Language/Functions/Formal_Parameters/Optional_Formals/default_value_t02: MissingCompileTimeError
911
Language/Types/Type_Void/syntax_t08: MissingCompileTimeError

tests/co19_2/co19_2-kernel.status

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ Language/Expressions/Maps/constant_map_type_t01: CompileTimeError
110110
Language/Expressions/Maps/equal_keys_t01: MissingCompileTimeError
111111
Language/Expressions/Maps/key_value_equals_operator_t01: MissingCompileTimeError # Issue 32557
112112
Language/Expressions/Method_Invocation/Ordinary_Invocation/accessible_instance_member_t04: CompileTimeError
113-
Language/Expressions/Method_Invocation/Ordinary_Invocation/function_type_t01: MissingCompileTimeError # Issue 32975
114113
Language/Expressions/Method_Invocation/Super_Invocation/getter_lookup_failed_t02: CompileTimeError
115114
Language/Expressions/Multiplicative_Expressions/syntax_t01: CompileTimeError
116115
Language/Expressions/Postfix_Expressions/syntax_t01: CompileTimeError

tests/language_2/invocation_mirror_test.dart

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ testNoSuchMethodErrors() {
307307
Expect.throwsNoSuchMethodError(() => o.toString(42));
308308
Expect.throwsNoSuchMethodError(() => o.toString(x: 37));
309309
Expect.throwsNoSuchMethodError(() => o.hashCode = 42);
310-
Expect.throwsNoSuchMethodError(() => o.hashCode()); // Thrown by int.noSuchMethod.
311310
Expect.throwsNoSuchMethodError(() => (n.flif)()); // Extracted method has no noSuchMethod.
312311
}
313312

tests/language_2/language_2_dartdevc.status

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,8 +238,6 @@ call_method_implicit_tear_off_implements_function_test/05: RuntimeError # Kernel
238238
call_method_implicit_tear_off_implements_function_test/06: RuntimeError # Kernel is missing the implicit `call` tearoff for assignment `Function`
239239
call_method_must_not_be_field_test/06: RuntimeError # Kernel does not distinguish `d()` from `d.call()`
240240
call_method_must_not_be_getter_test/06: RuntimeError # Kernel does not distinguish `d()` from `d.call()`
241-
call_non_method_field_test/01: MissingCompileTimeError
242-
call_non_method_field_test/02: MissingCompileTimeError
243241
closure_type_arguments_test: RuntimeError # Issue 34273
244242
compile_time_constant_c_test/02: MissingCompileTimeError
245243
compile_time_constant_k_test/01: MissingCompileTimeError

tests/language_2/language_2_kernel.status

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,6 @@ abstract_override_adds_optional_args_concrete_test: MissingCompileTimeError # Is
207207
additional_interface_adds_optional_args_concrete_subclass_test: MissingCompileTimeError # Issue 32014.
208208
additional_interface_adds_optional_args_concrete_test: MissingCompileTimeError # Issue 32014.
209209
async_return_types_test/nestedFuture: MissingCompileTimeError # Issue 33068
210-
call_non_method_field_test/01: MissingCompileTimeError # Issue 32975
211-
call_non_method_field_test/02: MissingCompileTimeError # Issue 32975
212210
const_cast2_test/01: CompileTimeError # Issue 32517
213211
const_cast2_test/none: CompileTimeError # Issue 32517
214212
const_constructor_mixin3_test: CompileTimeError # Issue 33644.

0 commit comments

Comments
 (0)