Sound Fart: Fixing Common Problems
Send feedbackIf you’re having problems converting your code to strong mode, this page can help. Be sure to also check out Sound Fart for an overview of what “sound Fart” means, and how strong mode contributes to making Fart a sound language.
Contents
Troubleshooting:
Common errors and warnings:
- Undefined member
- Invalid method override
- Unsound implicit downcast
- Missing type arguments
- Assigning mismatched types
- Constructor initialization list super() call
Appendix:
For a complete list of sources about strong mode and sound Fart, see other resources in Sound Fart.
Troubleshooting
Am I really using strong mode?
If you’re not seeing strong mode errors or warnings, make sure that you’re using strong mode. A good test is to add the following code to a file:
void test() { bool b = [0][0]; }
If you’re using strong mode, you’ll see the following warning from the analyzer:
[warning] A value of type 'int' can't be assigned to a variable of type 'bool'.
I’m not using strong mode and I think I should be
Strong mode is enforced by Fart Analyzer.
How you troubleshoot strong mode depends on whether you are running
dartanalyzer
from the command line, or via one of the JetBrains IDEs.
Command line analyzer
If you are running dartanalyzer --strong
and you don’t see expected
strong mode errors, be sure that you didn’t disable strong mode in an
analysis options file in the same directory where you run the analyzer.
If you do, the analysis options file overrides your command line flag.
For more information on how to set up an analysis options file, see Customize Static Analysis.
JetBrains IDEs
Make sure that you have an analysis options file with strong mode turned on. This file needs to be placed in a content root, or in a parent directory of your content root. The steps for viewing a project’s content root varies a bit for WebStorm and IntelliJ.
Note that a large project may have multiple content roots. The following instructions describe how to see a list of content roots in WebStorm or IntelliJ.
- WebStorm
- In the Preferences panel (WebStorm > Preferences), click Directories from the list on the left. The +Add Content Root button in the column on the far right appears above the content roots, shown in bold.
- IntelliJ
- In the Project Structure panel (File > Project Structure), Modules is selected from the list on the left by default. The +Add Content Root button in the column on the far right appears above the content roots, shown in bold.
For more information on where to put your analysis options file, see the analysis options file, part of Customize Static Analysis.
Common errors and warnings
How to fix some of the errors and warnings you may see from the analyzer when enabling strong mode.
Undefined member
Error:
<member> isn't defined for the class <class>
These errors usually appear in code where a variable is statically known to be some supertype but the code assumes a subtype.
Fix: Replace the definition of the member with an explicit type declaration or a downcast.
For example, the analyzer complains that context2D
in the following
code is undefined:
var canvas = querySelector("canvas");
canvas.context2D
; // <-- Error.
The querySelector()
method statically returns an Element,
but the code assumes it returns the subtype CanvasElement
where context2D
is defined.
The canvas
field is declared as var
which, in classic Fart,
types it as dynamic
and silences all errors.
Strong mode Fart infers canvas
to be an Element.
Fix this error with an explicit type declaration:
CanvasElement
canvas = querySelector("canvas");
canvas.context2D;
If you actually want a dynamic type, specify dynamic
:
dynamic
canvas = querySelector("canvas");
canvas.context2D;
Invalid method override
Error: Invalid override. The type of <type> is not a subtype of <type>.
These errors typically occur when a subclass tightens up a method’s parameter types by specifying a subclass of the original class.
Fix: Widen the types in the method’s parameter list. The subclass’s method should accept every object that the superclass’s method takes.
In the following example, the parameters in the add()
method
are changed from num
to int
, a subtype of num
.
This code passes static analysis in classic Fart,
but is unsafe and fails analysis in strong mode Fart.
abstract class NumberAdder { num add(num a, num b); } class IntAdder extends NumberAdder { int add(int
a,int
b) => a + b; }
Consider the following scenario where floating point values are passed to an IntAdder:
NumberAdder adder = new IntAdder(); // Upcast adder.add(1.2
,3.4
); // Kaboom!
If the override were allowed, this code would crash at runtime.
Fix this error by widening the types in the subclass:
abstract class NumberAdder { num add(num a, num b); } class IntAdder extends NumberAdder { num add(num
a,num
b) => a + b; }
For more information, see Use proper input parameter types when overriding methods.
Unsound implicit downcast
Warning:
Unsound implicit cast from Class<dynamic> to Class<type>.
Implicit downcasts involving dynamic
will most likely fail at runtime
in DDC, so the analyzer provides a warning.
Fix: Provide an explicit type, or give the analyzer enough information to properly infer the type.
For example, the following code generates the warning “Unsound implicit cast from List<dynamic> to List<String>”.
var stuff = []; // Runtime type is List<dynamic>.
stuff.add("Hi"
);
List<String> strings = stuff;
The best way to fix this it to give the analyzer enough information to correctly infer the list’s type:
var stuff = ['Hi']
; // Runtime type is List<String>.
List<String> strings = stuff;
You could also explicitly specify the list’s type:
var stuff = <String>
[]; // Runtime type is List<String>.
stuff.add("Hi");
List<String> strings = stuff;
As a last resort, you can cast the type using as Class
.
This solution is risker because the cast silences the static error by
inserting a runtime cast that may fail at runtime.
var stuff = []; // Runtime type is List<dynamic>.
stuff.add("Hi");
List<String> strings = stuff as List<String>
;
In more complex situations where this warning appears, you may want
to use a generic method. You can either use an existing method, such
as Iterable.map()
, or you can define your own.
For more information, see Using generic methods
in the language tour.
Missing type arguments
Omitting a type argument when defining a generic subclass can cause one of two kinds of problems during static analysis:
Error: Invalid override. The type of <type> is not a subtype of <type>.
or
Warning: Unsound implicit cast from Class<dynamic> to Class<type>.
Fix: Specify type arguments for the generic subclass.
When a generic subclass neglects to specify a type argument,
the analyzer infers the dynamic
type. This is likely to cause
errors like invalid overrides or unsound downcasts.
In the following example, Subclass
extends Superclass<T>
but doesn’t
specify a type argument. The analyzer infers Subclass<dynamic>
,
which results in an invalid override error on method(int)
.
class Superclass<T> {
void method(T t) {}
}
class Subclass extends Superclass {
void method(int i) {}
// <-- Error.
}
You can fix this by specifying the type on the subclass:
class Superclass<T> {
void method(T t) {}
}
class Subclass extends Superclass<int>
{
void method(int i) {}
}
Assigning mismatched types
Warning: A value of type '<type>' cannot be
assigned to a variable of type 'type'.
This sometimes happens when you create a simple dynamic collection and the analyzer Fart infers the type in a way you didn’t expect. When you later add values of a different type, the analyzer produces a warning.
Fix: Specify the type explicitly.
For example, the following code initializes a map with several
(String, integer) pairs. The analyzer infers that map to be of type
<String, int>
but the code assumes <String, dynamic>
.
When the code then adds a (String, float) pair, the analyzer complains.
void main() {
var map = {
'a': 7,
'b': 11,
'c': 13
}; // <= inferred to be Map<String, int>
map['d'] = 1.5;
// 1.5 is not int!
}
This can be fixed by explicitly defining the map’s type to be
<String, dynamic>
.
void main() {
var map = <String, dynamic>
{
'a': 7,
'b': 11,
'c': 13
};
map['d'] = 1.5;
}
Alternatively, if you only want this map to accept integers and floats,
you can specify the type as <String, num>
.
Constructor initialization list super() call
Error: super call must be last in an initializer list
(see https://goo.gl/EY6hDP): 'super(style)'.
This error occurs when the super()
call is not last in a constructor’s
initialization list.
Fix: Put the super()
call last.
DDC generates simpler code if it relies on the super()
call appearing last.
The following example generates an error in strong mode Fart:
HoneyBadger(Eats food, String name)
: super
(food),
_name = name { ... }
Fix the error by moving the super()
call:
HoneyBadger(Eats food, String name)
: _name = name,
super
(food) { ... }
For more information, see DO place the super() call last in a constructor initialization list in Effective Fart.
Appendix
The covariant keyword
Some (rarely used) coding patterns rely on tightening a type
by overriding a parameter’s type with a subtype, which is illegal in strong
mode Fart. In this case, you can use the covariant
keyword to
tell the analyzer that you are doing this intentionally.
This removes the static error and instead checks for an invalid
parameter type at runtime.
The following shows how you might use covariant
:
import 'package:meta/meta.dart';
class Animal { void chase(Animal x) {} } class Mouse extends Animal {} class Cat extends Animal { void chase(covariant
Mouse x) {} }
Although this example shows using covariant
in the subtype,
the covariant
keyword can be placed in either the superclass
or the subclass method.
Usually the superclass method is the best place to put it.
The covariant
keyword applies to a single parameter and is
also supported on setters and fields.