Sdk: Implementing a class with call()

Created on 7 Jul 2017  Â·  7Comments  Â·  Source: dart-lang/sdk

This code does not compile with an error "foo is not a function":

class Foo { int call() => 1; }
class MockFoo extends Mock implements Foo {} // Mockito's Mock

var foo = new MockFoo();
foo(); // static error: "foo is not a function"

Replacing "var foo" with "Foo foo" would work.

P2 area-analyzer type-bug

All 7 comments

What Dart version? Compile how? Running on the console or Dartium or compiling with pub build with or without ddc?

Looks like the analyzer in strong mode is giving this static error.

If:

MockFoo foo = new MockFoo();

then the analyzer is weak mode gives a warning. In general, MockFoo is not recognized as a callable class.

Assigning to the language team to determine if this is a language issue or analyzer one.

Short summary: It's an Analyzer issue.

The spec says (in the section "Function Types"):

If a type I includes an instance method named call, and the type of call is the function type F, then I is considered to be more specific than F. It is a static warning if a concrete class implements Function and does not have a concrete method named call unless that class has an implementation of noSuchMethod() distinct from the one declared in class Object.

In this case, the MockFoo class is concrete and has a noSuchMethod distinct from the one of Object, so no warning is caused at the class level, and the types Foo and MockFoo are more specific than the function type int Function().

The variable foo has type MockFoo (by inference in strong mode, or if declared as such directly). It is a local variable (I assume), so this is treated as a "function expression invocation". That section says:

It is a static warning if the static type F of ef may not be assigned to a function type.

The type MockFoo is more specific than int Function(), and the "Interface types" section states that:

An interface type T may be assigned to a type S, written T ⟺ S, iff either T <: S or S <: T.
Together that means that MockFoo is assignable to a function type, so this should also not cause any warnings.

The invocation is then equivalent to foo.call(), so we then look to the method invocation section, but there should be no need to go into details since this is a method invocation of a method explicitly declared by the interface. That must work, or method invocations wouldn't work at all.

All in all, the specification doesn't require any warnings.
It's not clear which warning the foo is not a function message refers to, but it's most likely the warning from the "function expression invocation" section, which does not apply – MockFoo is assignable to a function type.

The Dart version is 1.25.0-dev.3.0+google3-v1.25.0.dev.3.0.

I use blaze to do the compilation. The error seems to come from dartanalyzer.

The behavior has changed in Dart 2. There should now be an actual call member on MockFoo which forwards to noSuchMethod. The class should still be callable.

This is still a problem in the latest SDK. CFE does not emit an error and the VM runs this code just fine, analyzer emits an invocation_of_non_function.

Here is a repro with no external dependencies:

class Foo {
  int call() => 1;
}

class Bar implements Foo {
  dynamic noSuchMethod(Invocation i) {
    return 2;
  }
}

void main() {
  var b = new Bar();
  print(b()); // unexpected error here.
}
Was this page helpful?
0 / 5 - 0 ratings