Add --no-declaration-casts option to analyzer.

Adds a flag to disable and enable declaration casts independently
from other casts.

Fixes #29546

BUG=
R=brianwilkerson@google.com, rnystrom@google.com

Review-Url: https://codereview.chromium.org/2976963002 .
This commit is contained in:
Leaf Petersen 2017-07-20 15:55:00 -07:00
parent 203955bc48
commit 1eb480d266
15 changed files with 316 additions and 38 deletions

View file

@ -17,6 +17,7 @@ import 'package:path/path.dart';
const String analysisOptionsFileOption = 'options';
const String bazelAnalysisOptionsPath =
'package:dart.analysis_options/default.yaml';
const String declarationCastsFlag = 'declaration-casts';
const String defineVariableOption = 'D';
const String enableInitializingFormalAccessFlag = 'initializing-formal-access';
const String enableStrictCallChecksFlag = 'enable-strict-call-checks';
@ -24,8 +25,8 @@ const String enableSuperMixinFlag = 'supermixin';
const String flutterAnalysisOptionsPath =
'package:flutter/analysis_options_user.yaml';
const String ignoreUnrecognizedFlagsFlag = 'ignore-unrecognized-flags';
const String implicitCastsFlag = 'implicit-casts';
const String lintsFlag = 'lints';
const String noImplicitCastsFlag = 'no-implicit-casts';
const String noImplicitDynamicFlag = 'no-implicit-dynamic';
const String packageDefaultAnalysisOptions = 'package-default-analysis-options';
const String packageRootOption = 'package-root';
@ -54,9 +55,16 @@ void applyAnalysisOptionFlags(AnalysisOptionsImpl options, ArgResults args,
options.enableSuperMixins = args[enableSuperMixinFlag];
verbose('$enableSuperMixinFlag = ${options.enableSuperMixins}');
}
if (args.wasParsed(noImplicitCastsFlag)) {
options.implicitCasts = !args[noImplicitCastsFlag];
verbose('$noImplicitCastsFlag = ${options.implicitCasts}');
if (args.wasParsed(implicitCastsFlag)) {
options.implicitCasts = args[implicitCastsFlag];
verbose('$implicitCastsFlag = ${options.implicitCasts}');
}
if (args.wasParsed(declarationCastsFlag)) {
options.declarationCasts = args[declarationCastsFlag];
verbose('$declarationCastsFlag = ${options.declarationCasts}');
} else if (args.wasParsed(implicitCastsFlag)) {
options.declarationCasts = args[implicitCastsFlag];
verbose('$declarationCastsFlag = ${options.declarationCasts}');
}
if (args.wasParsed(noImplicitDynamicFlag)) {
options.implicitDynamic = !args[noImplicitDynamicFlag];
@ -174,8 +182,12 @@ void defineAnalysisArguments(ArgParser parser, {bool hide: true, ddc: false}) {
parser.addFlag(strongModeFlag,
help: 'Enable strong static checks (https://goo.gl/DqcBsw).',
defaultsTo: ddc);
parser.addFlag(noImplicitCastsFlag,
negatable: false,
parser.addFlag(declarationCastsFlag,
negatable: true,
help:
'Disable declaration casts in strong mode (https://goo.gl/cTLz40).');
parser.addFlag(implicitCastsFlag,
negatable: true,
help: 'Disable implicit casts in strong mode (https://goo.gl/cTLz40).');
parser.addFlag(noImplicitDynamicFlag,
negatable: false,

View file

@ -284,6 +284,9 @@ class AnalysisContextImpl implements InternalAnalysisContext {
((options is AnalysisOptionsImpl)
? this._options.strongModeHints != options.strongModeHints
: false) ||
((options is AnalysisOptionsImpl)
? this._options.declarationCasts != options.declarationCasts
: false) ||
((options is AnalysisOptionsImpl)
? this._options.implicitCasts != options.implicitCasts
: false) ||
@ -323,6 +326,7 @@ class AnalysisContextImpl implements InternalAnalysisContext {
this._options.patchPaths = options.patchPaths;
if (options is AnalysisOptionsImpl) {
this._options.strongModeHints = options.strongModeHints;
this._options.declarationCasts = options.declarationCasts;
this._options.implicitCasts = options.implicitCasts;
this._options.nonnullableTypes = options.nonnullableTypes;
this._options.implicitDynamic = options.implicitDynamic;

View file

@ -272,6 +272,7 @@ class LibraryAnalyzer {
_typeProvider,
new StrongTypeSystemImpl(_typeProvider,
implicitCasts: options.implicitCasts,
declarationCasts: options.declarationCasts,
nonnullableTypes: options.nonnullableTypes),
errorListener,
options);

View file

@ -1361,6 +1361,14 @@ class AnalysisOptionsImpl implements AnalysisOptions {
*/
Uint32List _signature;
/**
* A flag indicating whether declaration casts are allowed in [strongMode]
* (they are always allowed in Dart 1.0 mode).
*
* This option is experimental and subject to change.
*/
bool declarationCasts = true;
@override
@deprecated
int cacheSize = 64;
@ -1491,6 +1499,7 @@ class AnalysisOptionsImpl implements AnalysisOptions {
preserveComments = options.preserveComments;
strongMode = options.strongMode;
if (options is AnalysisOptionsImpl) {
declarationCasts = options.declarationCasts;
strongModeHints = options.strongModeHints;
implicitCasts = options.implicitCasts;
nonnullableTypes = options.nonnullableTypes;
@ -1607,6 +1616,7 @@ class AnalysisOptionsImpl implements AnalysisOptions {
ApiSignature buffer = new ApiSignature();
// Append boolean flags.
buffer.addBool(declarationCasts);
buffer.addBool(enableAssertInitializer);
buffer.addBool(enableLazyAssignmentOperators);
buffer.addBool(enableStrictCallChecks);
@ -1638,6 +1648,7 @@ class AnalysisOptionsImpl implements AnalysisOptions {
@override
void resetToDefaults() {
declarationCasts = true;
dart2jsHint = false;
disableCacheFlushing = false;
enableAssertInitializer = false;

View file

@ -1205,7 +1205,8 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
SimpleIdentifier nameNode = node.name;
Expression initializerNode = node.initializer;
// do checks
_checkForInvalidAssignment(nameNode, initializerNode);
_checkForInvalidAssignment(nameNode, initializerNode,
isDeclarationCast: true);
_checkForImplicitDynamicIdentifier(node, nameNode);
// visit name
nameNode.accept(this);
@ -2504,20 +2505,24 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
}
bool _checkForAssignableExpression(
Expression expression, DartType expectedStaticType, ErrorCode errorCode) {
Expression expression, DartType expectedStaticType, ErrorCode errorCode,
{bool isDeclarationCast = false}) {
DartType actualStaticType = getStaticType(expression);
return actualStaticType != null &&
_checkForAssignableExpressionAtType(
expression, actualStaticType, expectedStaticType, errorCode);
expression, actualStaticType, expectedStaticType, errorCode,
isDeclarationCast: isDeclarationCast);
}
bool _checkForAssignableExpressionAtType(
Expression expression,
DartType actualStaticType,
DartType expectedStaticType,
ErrorCode errorCode) {
ErrorCode errorCode,
{bool isDeclarationCast = false}) {
if (!_expressionIsAssignableAtType(
expression, actualStaticType, expectedStaticType)) {
expression, actualStaticType, expectedStaticType,
isDeclarationCast: isDeclarationCast)) {
_errorReporter.reportTypeErrorForNode(
errorCode, expression, [actualStaticType, expectedStaticType]);
return false;
@ -4272,7 +4277,8 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
StaticTypeWarningCode.FOR_IN_OF_INVALID_TYPE,
node.iterable,
[iterableType, loopTypeName]);
} else if (!_typeSystem.isAssignableTo(bestIterableType, variableType)) {
} else if (!_typeSystem.isAssignableTo(bestIterableType, variableType,
isDeclarationCast: true)) {
_errorReporter.reportTypeErrorForNode(
StaticTypeWarningCode.FOR_IN_OF_INVALID_ELEMENT_TYPE,
node.iterable,
@ -4426,7 +4432,8 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
*
* See [StaticTypeWarningCode.INVALID_ASSIGNMENT].
*/
void _checkForInvalidAssignment(Expression lhs, Expression rhs) {
void _checkForInvalidAssignment(Expression lhs, Expression rhs,
{bool isDeclarationCast = false}) {
if (lhs == null || rhs == null) {
return;
}
@ -4435,7 +4442,8 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
? getStaticType(lhs)
: leftVariableElement.type;
_checkForAssignableExpression(
rhs, leftType, StaticTypeWarningCode.INVALID_ASSIGNMENT);
rhs, leftType, StaticTypeWarningCode.INVALID_ASSIGNMENT,
isDeclarationCast: isDeclarationCast);
}
/**
@ -6318,13 +6326,15 @@ class ErrorVerifier extends RecursiveAstVisitor<Object> {
}
bool _expressionIsAssignableAtType(Expression expression,
DartType actualStaticType, DartType expectedStaticType) {
DartType actualStaticType, DartType expectedStaticType,
{isDeclarationCast: false}) {
bool concrete = _options.strongMode && checker.hasStrictArrow(expression);
if (concrete && actualStaticType is FunctionType) {
actualStaticType =
_typeSystem.functionTypeToConcreteType(actualStaticType);
}
return _typeSystem.isAssignableTo(actualStaticType, expectedStaticType);
return _typeSystem.isAssignableTo(actualStaticType, expectedStaticType,
isDeclarationCast: isDeclarationCast);
}
MethodElement _findOverriddenMemberThatMustCallSuper(MethodDeclaration node) {

View file

@ -847,14 +847,16 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
? ErrorVerifier.getStaticType(lhs)
: leftVariableElement.type;
DartType staticRightType = ErrorVerifier.getStaticType(rhs);
if (!_typeSystem.isAssignableTo(staticRightType, leftType)) {
if (!_typeSystem.isAssignableTo(staticRightType, leftType,
isDeclarationCast: true)) {
// The warning was generated on this rhs
return false;
}
// Test for, and then generate the hint
DartType bestRightType = rhs.bestType;
if (leftType != null && bestRightType != null) {
if (!_typeSystem.isAssignableTo(bestRightType, leftType)) {
if (!_typeSystem.isAssignableTo(bestRightType, leftType,
isDeclarationCast: true)) {
_errorReporter.reportTypeErrorForNode(
HintCode.INVALID_ASSIGNMENT, rhs, [bestRightType, leftType]);
return true;

View file

@ -47,6 +47,13 @@ typedef bool _GuardedSubtypeChecker<T>(T t1, T t2, Set<TypeImpl> visitedTypes);
class StrongTypeSystemImpl extends TypeSystem {
static bool _comparingTypeParameterBounds = false;
/**
* True if declaration casts should be allowed, otherwise false.
*
* This affects the behavior of [isAssignableTo].
*/
final bool declarationCasts;
/**
* True if implicit casts should be allowed, otherwise false.
*
@ -62,7 +69,8 @@ class StrongTypeSystemImpl extends TypeSystem {
final TypeProvider typeProvider;
StrongTypeSystemImpl(this.typeProvider,
{this.implicitCasts: true,
{this.declarationCasts: true,
this.implicitCasts: true,
this.nonnullableTypes: AnalysisOptionsImpl.NONNULLABLE_TYPES});
@override
@ -409,7 +417,8 @@ class StrongTypeSystemImpl extends TypeSystem {
}
@override
bool isAssignableTo(DartType fromType, DartType toType) {
bool isAssignableTo(DartType fromType, DartType toType,
{bool isDeclarationCast = false}) {
// TODO(leafp): Document the rules in play here
// An actual subtype
@ -417,7 +426,11 @@ class StrongTypeSystemImpl extends TypeSystem {
return true;
}
if (!implicitCasts) {
if (isDeclarationCast) {
if (!declarationCasts) {
return false;
}
} else if (!implicitCasts) {
return false;
}
@ -1222,7 +1235,8 @@ abstract class TypeSystem {
* Return `true` if the [leftType] is assignable to the [rightType] (that is,
* if leftType <==> rightType).
*/
bool isAssignableTo(DartType leftType, DartType rightType);
bool isAssignableTo(DartType leftType, DartType rightType,
{bool isDeclarationCast = false});
/**
* Return `true` if the [leftType] is more specific than the [rightType]
@ -1476,6 +1490,7 @@ abstract class TypeSystem {
var options = context.analysisOptions as AnalysisOptionsImpl;
return options.strongMode
? new StrongTypeSystemImpl(context.typeProvider,
declarationCasts: options.declarationCasts,
implicitCasts: options.implicitCasts,
nonnullableTypes: options.nonnullableTypes)
: new TypeSystemImpl(context.typeProvider);
@ -1511,7 +1526,8 @@ class TypeSystemImpl extends TypeSystem {
}
@override
bool isAssignableTo(DartType leftType, DartType rightType) {
bool isAssignableTo(DartType leftType, DartType rightType,
{bool isDeclarationCast = false}) {
return leftType.isAssignableTo(rightType);
}

View file

@ -5490,6 +5490,7 @@ class StrongModeVerifyUnitTask extends SourceBasedAnalysisTask {
typeProvider,
new StrongTypeSystemImpl(typeProvider,
implicitCasts: options.implicitCasts,
declarationCasts: options.declarationCasts,
nonnullableTypes: options.nonnullableTypes),
errorListener,
options);

View file

@ -58,6 +58,7 @@ class AnalyzerOptions {
static const String strong_mode = 'strong-mode';
// Strong mode options, see AnalysisOptionsImpl for documentation.
static const String declarationCasts = 'declaration-casts';
static const String implicitCasts = 'implicit-casts';
static const String implicitDynamic = 'implicit-dynamic';
@ -573,6 +574,9 @@ class _OptionsProcessor {
AnalysisOptionsImpl options, Object feature, Object value) {
bool boolValue = toBool(value);
if (boolValue != null) {
if (feature == AnalyzerOptions.declarationCasts) {
options.declarationCasts = boolValue;
}
if (feature == AnalyzerOptions.implicitCasts) {
options.implicitCasts = boolValue;
}

View file

@ -197,10 +197,19 @@ class CodeChecker extends RecursiveAstVisitor {
}
void checkAssignment(Expression expr, DartType type) {
checkForCast(expr, type);
}
void checkDeclarationCast(Expression expr, DartType type) {
checkForCast(expr, type, isDeclarationCast: true);
}
void checkForCast(Expression expr, DartType type,
{bool isDeclarationCast = false}) {
if (expr is ParenthesizedExpression) {
checkAssignment(expr.expression, type);
checkForCast(expr.expression, type);
} else {
_checkImplicitCast(expr, type);
_checkImplicitCast(expr, type, isDeclarationCast: isDeclarationCast);
}
}
@ -432,7 +441,7 @@ class CodeChecker extends RecursiveAstVisitor {
// Insert a cast from the sequence's element type to the loop variable's
// if needed.
_checkImplicitCast(loopVariable, _getDefiniteType(loopVariable),
from: elementType);
from: elementType, isDeclarationCast: true);
}
}
@ -684,7 +693,7 @@ class CodeChecker extends RecursiveAstVisitor {
var initializer = variable.initializer;
if (initializer != null) {
if (type != null) {
checkAssignment(initializer, type.type);
checkDeclarationCast(initializer, type.type);
}
}
}
@ -760,10 +769,12 @@ class CodeChecker extends RecursiveAstVisitor {
/// If [expr] does not require an implicit cast because it is not related to
/// [to] or is already a subtype of it, does nothing.
void _checkImplicitCast(Expression expr, DartType to,
{DartType from, bool opAssign: false}) {
{DartType from, bool opAssign: false, bool isDeclarationCast: false}) {
from ??= _getDefiniteType(expr);
if (_needsImplicitCast(expr, to, from: from) == true) {
if (_needsImplicitCast(expr, to,
from: from, isDeclarationCast: isDeclarationCast) ==
true) {
_recordImplicitCast(expr, to, from: from, opAssign: opAssign);
}
}
@ -1062,7 +1073,8 @@ class CodeChecker extends RecursiveAstVisitor {
/// types are statically incompatible.
///
/// If [from] is omitted, uses the static type of [expr]
bool _needsImplicitCast(Expression expr, DartType to, {DartType from}) {
bool _needsImplicitCast(Expression expr, DartType to,
{DartType from, bool isDeclarationCast: false}) {
from ??= _getDefiniteType(expr);
if (!_checkNonNullAssignment(expr, to, from)) return false;
@ -1074,7 +1086,8 @@ class CodeChecker extends RecursiveAstVisitor {
if (rules.isSubtypeOf(from, to)) return false;
// Down cast or legal sideways cast, coercion needed.
if (rules.isAssignableTo(from, to)) return true;
if (rules.isAssignableTo(from, to, isDeclarationCast: isDeclarationCast))
return true;
// Special case for FutureOr to handle returned values from async functions.
// In this case, we're more permissive than assignability.

View file

@ -35,6 +35,7 @@ class ArgumentsTest {
'-Dfoo=1',
'-Dbar=2',
'--enable-strict-call-checks',
'--no-declaration-casts',
'--no-implicit-casts',
'--no-implicit-dynamic',
'--options=$defaultAnalysisOptionsFilePath',
@ -59,6 +60,7 @@ class ArgumentsTest {
expect(defaultOptions, isNotNull);
expect(defaultOptions.enableStrictCallChecks, true);
expect(defaultOptions.strongMode, true);
expect(defaultOptions.declarationCasts, false);
expect(defaultOptions.implicitCasts, false);
expect(defaultOptions.implicitDynamic, false);
}
@ -80,6 +82,7 @@ class ArgumentsTest {
expect(defaultOptions, isNotNull);
expect(defaultOptions.enableStrictCallChecks, false);
expect(defaultOptions.strongMode, false);
expect(defaultOptions.declarationCasts, true);
expect(defaultOptions.implicitCasts, true);
expect(defaultOptions.implicitDynamic, true);
}
@ -134,10 +137,27 @@ class ArgumentsTest {
expect(manager.canUseSummaries, true);
}
void test_declarationCast_noImplicitCast() {
MemoryResourceProvider provider = new MemoryResourceProvider();
ArgParser parser = new ArgParser();
defineAnalysisArguments(parser);
List<String> args = [
'--declaration-casts',
'--no-implicit-casts',
];
ArgResults result = parse(provider, parser, args);
ContextBuilderOptions options = createContextBuilderOptions(result);
expect(options, isNotNull);
AnalysisOptionsImpl defaultOptions = options.defaultOptions;
expect(defaultOptions, isNotNull);
expect(defaultOptions.declarationCasts, true);
expect(defaultOptions.implicitCasts, false);
}
void test_defineAnalysisArguments() {
ArgParser parser = new ArgParser();
defineAnalysisArguments(parser);
expect(parser.options, hasLength(14));
expect(parser.options, hasLength(15));
}
void test_extractDefinedVariables() {
@ -166,6 +186,55 @@ class ArgumentsTest {
expect(result, orderedEquals(['--a', '--c=0', '-e=2', '-f', 'bar']));
}
void test_noAssignmentCast() {
MemoryResourceProvider provider = new MemoryResourceProvider();
ArgParser parser = new ArgParser();
defineAnalysisArguments(parser);
List<String> args = [
'--no-declaration-casts',
];
ArgResults result = parse(provider, parser, args);
ContextBuilderOptions options = createContextBuilderOptions(result);
expect(options, isNotNull);
AnalysisOptionsImpl defaultOptions = options.defaultOptions;
expect(defaultOptions, isNotNull);
expect(defaultOptions.declarationCasts, false);
expect(defaultOptions.implicitCasts, true);
}
void test_noAssignmentCast_implicitCast() {
MemoryResourceProvider provider = new MemoryResourceProvider();
ArgParser parser = new ArgParser();
defineAnalysisArguments(parser);
List<String> args = [
'--no-declaration-casts',
'--implicit-casts',
];
ArgResults result = parse(provider, parser, args);
ContextBuilderOptions options = createContextBuilderOptions(result);
expect(options, isNotNull);
AnalysisOptionsImpl defaultOptions = options.defaultOptions;
expect(defaultOptions, isNotNull);
expect(defaultOptions.declarationCasts, false);
expect(defaultOptions.implicitCasts, true);
}
void test_noImplicitCast() {
MemoryResourceProvider provider = new MemoryResourceProvider();
ArgParser parser = new ArgParser();
defineAnalysisArguments(parser);
List<String> args = [
'--no-implicit-casts',
];
ArgResults result = parse(provider, parser, args);
ContextBuilderOptions options = createContextBuilderOptions(result);
expect(options, isNotNull);
AnalysisOptionsImpl defaultOptions = options.defaultOptions;
expect(defaultOptions, isNotNull);
expect(defaultOptions.declarationCasts, false);
expect(defaultOptions.implicitCasts, false);
}
void test_parse_noReplacement_noIgnored() {
MemoryResourceProvider provider = new MemoryResourceProvider();
ArgParser parser = new ArgParser();

View file

@ -1014,6 +1014,7 @@ linter:
expect(actual.preserveComments, expected.preserveComments);
expect(actual.strongMode, expected.strongMode);
expect(actual.strongModeHints, expected.strongModeHints);
expect(actual.declarationCasts, expected.declarationCasts);
expect(actual.implicitCasts, expected.implicitCasts);
expect(actual.implicitDynamic, expected.implicitDynamic);
expect(actual.trackCacheDependencies, expected.trackCacheDependencies);

View file

@ -2196,11 +2196,96 @@ main() {
''');
}
test_implicitCasts() async {
addFile('num n; int i = /*info:ASSIGNMENT_CAST*/n;');
test_implicitCasts_assignment() async {
addFile(
'num n; int i; void main() { i = /*info:DOWN_CAST_IMPLICIT*/n;}//yy');
await check();
addFile('num n; int i = /*error:INVALID_ASSIGNMENT*/n;');
await check(implicitCasts: false);
addFile(
'num n; int i; void main() { i = /*error:INVALID_ASSIGNMENT*/n;}//ny');
await check(implicitCasts: false, declarationCasts: true);
addFile(
'num n; int i; void main() { i = /*info:DOWN_CAST_IMPLICIT*/n;}//yn');
await check(implicitCasts: true, declarationCasts: false);
addFile(
'num n; int i; void main() { i = /*error:INVALID_ASSIGNMENT*/n;}//nn');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_compoundAssignment() async {
addFile('''f(num n, int i) {
/*info:DOWN_CAST_IMPLICIT_ASSIGN*/i += n;}//yy''');
await check();
addFile('''f(num n, int i) {
i += /*error:INVALID_ASSIGNMENT*/n;}//ny''');
await check(implicitCasts: false, declarationCasts: true);
addFile('''f(num n, int i) {
/*info:DOWN_CAST_IMPLICIT_ASSIGN*/i += n;}//yn''');
await check(implicitCasts: true, declarationCasts: false);
addFile('''f(num n, int i) {
i += /*error:INVALID_ASSIGNMENT*/n;}//nn''');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_constructorInitializer() async {
addFile(
'class A { int i; A(num n) : i = /*info:DOWN_CAST_IMPLICIT*/n;}//yy');
await check();
addFile(
'class A { int i; A(num n) : i = /*error:FIELD_INITIALIZER_NOT_ASSIGNABLE*/n;}//ny');
await check(implicitCasts: false, declarationCasts: true);
addFile(
'class A { int i; A(num n) : i = /*info:DOWN_CAST_IMPLICIT*/n;}//yn');
await check(implicitCasts: true, declarationCasts: false);
addFile(
'class A { int i; A(num n) : i = /*error:FIELD_INITIALIZER_NOT_ASSIGNABLE*/n;}//nn');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_defaultValue() async {
addFile('''const num n = 0;
f({int i = /*info:DOWN_CAST_IMPLICIT*/n}) => i;//yy''');
await check();
addFile('''const num n = 0;
f({int i = /*error:INVALID_ASSIGNMENT*/n}) => i;//ny''');
await check(implicitCasts: false, declarationCasts: true);
addFile('''const num n = 0;
f({int i = /*info:DOWN_CAST_IMPLICIT*/n}) => i;//yn''');
await check(implicitCasts: true, declarationCasts: false);
addFile('''const num n = 0;
f({int i = /*error:INVALID_ASSIGNMENT*/n}) => i;//nn''');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_fieldInitializer() async {
addFile('class A { static num n; int i = /*info:ASSIGNMENT_CAST*/n;}//yy');
await check();
addFile('class A { static num n; int i = /*info:ASSIGNMENT_CAST*/n;}//ny');
await check(implicitCasts: false, declarationCasts: true);
addFile(
'class A { static num n; int i = /*error:INVALID_ASSIGNMENT*/n;}//yn');
await check(implicitCasts: true, declarationCasts: false);
addFile(
'class A { static num n; int i = /*error:INVALID_ASSIGNMENT*/n;}//nn');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_functionCall() async {
addFile('''num n;
f(int i) => i;
var i = f(/*info:DOWN_CAST_IMPLICIT*/n);//yy''');
await check();
addFile('''num n;
f(int i) => i;
var i = f(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/n);//ny''');
await check(implicitCasts: false, declarationCasts: true);
addFile('''num n;
f(int i) => i;
var i = f(/*info:DOWN_CAST_IMPLICIT*/n);//yn''');
await check(implicitCasts: true, declarationCasts: false);
addFile('''num n;
f(int i) => i;
var i = f(/*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/n);//nn''');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_genericMethods() async {
@ -2210,6 +2295,17 @@ var x = <String>[].map<String>((x) => "");
await check(implicitCasts: false);
}
test_implicitCasts_initializer() async {
addFile('num n; int i = /*info:ASSIGNMENT_CAST*/n;//yy');
await check();
addFile('num n; int i = /*info:ASSIGNMENT_CAST*/n;//ny');
await check(implicitCasts: false, declarationCasts: true);
addFile('num n; int i = /*error:INVALID_ASSIGNMENT*/n;//yn');
await check(implicitCasts: true, declarationCasts: false);
addFile('num n; int i = /*error:INVALID_ASSIGNMENT*/n;//nn');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_numericOps() async {
// Regression test for https://github.com/dart-lang/sdk/issues/26912
addFile(r'''
@ -2222,7 +2318,37 @@ void f() {
await check(implicitCasts: false);
}
test_implicitCasts_operator() async {
addFile('''num n;
int i;
var r = i & /*info:DOWN_CAST_IMPLICIT*/n;//yy''');
await check();
addFile('''num n;
int i;
var r = i & /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/n;//ny''');
await check(implicitCasts: false, declarationCasts: true);
addFile('''num n;
int i;
var r = i & /*info:DOWN_CAST_IMPLICIT*/n;//yn''');
await check(implicitCasts: true, declarationCasts: false);
addFile('''num n;
int i;
var r = i & /*error:ARGUMENT_TYPE_NOT_ASSIGNABLE*/n;//nn''');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_return() async {
addFile('int f(num n) => /*info:DOWN_CAST_IMPLICIT*/n;//yy');
await check();
addFile('int f(num n) => /*error:RETURN_OF_INVALID_TYPE*/n;//ny');
await check(implicitCasts: false, declarationCasts: true);
addFile('int f(num n) => /*info:DOWN_CAST_IMPLICIT*/n;//yn');
await check(implicitCasts: true, declarationCasts: false);
addFile('int f(num n) => /*error:RETURN_OF_INVALID_TYPE*/n;//nn');
await check(implicitCasts: false, declarationCasts: false);
}
test_implicitCasts_return_async() async {
addFile(r'''
import 'dart:async';

View file

@ -270,7 +270,8 @@ class AbstractStrongTest {
///
/// Returns the main resolved library. This can be used for further checks.
Future<CompilationUnit> check(
{bool implicitCasts: true,
{bool declarationCasts: true,
bool implicitCasts: true,
bool implicitDynamic: true,
List<String> nonnullableTypes:
AnalysisOptionsImpl.NONNULLABLE_TYPES}) async {
@ -283,6 +284,7 @@ class AbstractStrongTest {
AnalysisOptionsImpl analysisOptions = new AnalysisOptionsImpl();
analysisOptions.strongMode = true;
analysisOptions.strongModeHints = true;
analysisOptions.declarationCasts = declarationCasts;
analysisOptions.implicitCasts = implicitCasts;
analysisOptions.implicitDynamic = implicitDynamic;
analysisOptions.nonnullableTypes = nonnullableTypes;

View file

@ -39,6 +39,9 @@ class CommandLineOptions {
/// a constructor.
final bool enableAssertInitializer;
/// Whether declaration casts are enabled (in strong mode)
final bool declarationCasts;
/// The path to output analysis results when in build mode.
final String buildAnalysisOutput;
@ -170,6 +173,9 @@ class CommandLineOptions {
contextBuilderOptions = createContextBuilderOptions(args),
dartSdkPath = args['dart-sdk'],
dartSdkSummaryPath = args['dart-sdk-summary'],
declarationCasts = args.wasParsed(declarationCastsFlag)
? args[declarationCastsFlag]
: args[implicitCastsFlag],
disableCacheFlushing = args['disable-cache-flushing'],
disableHints = args['no-hints'],
displayVersion = args['version'],
@ -191,7 +197,7 @@ class CommandLineOptions {
warningsAreFatal = args['fatal-warnings'],
lintsAreFatal = args['fatal-lints'],
strongMode = args['strong'],
implicitCasts = !args['no-implicit-casts'],
implicitCasts = args[implicitCastsFlag],
implicitDynamic = !args['no-implicit-dynamic'],
useAnalysisDriverMemoryByteStore =
args['use-analysis-driver-memory-byte-store'],