Issue a warning if top-level inference depends on an implicitly typed instance getter.

If an implicitly typed top level variable or field depends on an
implicitly typed instance getter or instance field, the analyzer
implementation of type inference isn't guaranteed to infer the
depended-upon field first, therefore the type might be inferred
incorrectly.

The new front end doesn't have this problem, so the user's code will
execute correctly at runtime, but they might get confusing results
from the analyzer.  To alert users of this problem, we issue a
compile-time warning whenever an implicitly typed top level variable
or field depends on an implicitly typed instance getter or instance
field.

Change-Id: I100bcbe1a76472bcb7d493eb12e4a3e2d0605e79
Reviewed-on: https://dart-review.googlesource.com/35385
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Paul Berry <paulberry@google.com>
This commit is contained in:
Paul Berry 2018-01-17 21:58:28 +00:00 committed by commit-bot@chromium.org
parent 2e5264ddea
commit 23cba1d438
10 changed files with 285 additions and 8 deletions

View file

@ -5052,10 +5052,11 @@ class StrongModeCode extends ErrorCode {
"Try adding an explicit type to either the variable '{0}' or the variable '{1}'.");
static const StrongModeCode TOP_LEVEL_INSTANCE_GETTER = const StrongModeCode(
ErrorType.HINT,
ErrorType.STATIC_WARNING,
'TOP_LEVEL_INSTANCE_GETTER',
"The type of '{0}' can't be inferred because of the use of the instance getter '{1}'.",
"Try removing the use of the instance getter {1}, or add an explicit type for '{0}'.");
"The type of '{0}' can't be inferred because it refers to an instance "
"getter, '{1}', which has an implicit type.",
"Add an explicit type for either '{0}' or '{1}'.");
static const StrongModeCode TOP_LEVEL_TYPE_ARGUMENTS = const StrongModeCode(
ErrorType.HINT,

View file

@ -1312,8 +1312,20 @@ class CodeChecker extends RecursiveAstVisitor {
return;
}
if (e is PropertyAccessorElement) {
validateHasType(e);
Element enclosing = e.enclosingElement;
if (enclosing is CompilationUnitElement) {
if (e is PropertyAccessorElement) {
validateHasType(e);
}
} else if (enclosing is ClassElement) {
if (e is PropertyAccessorElement) {
if (e.isStatic) {
validateHasType(e);
} else if (e.hasImplicitReturnType) {
_recordMessage(
n, StrongModeCode.TOP_LEVEL_INSTANCE_GETTER, [name, e.name]);
}
}
}
}
@ -1391,6 +1403,7 @@ class CodeChecker extends RecursiveAstVisitor {
} else if (n is FunctionExpressionInvocation) {
_validateTopLevelInitializer(name, n.function);
} else if (n is MethodInvocation) {
_validateTopLevelInitializer(name, n.methodName);
_validateTopLevelInitializer(name, n.target);
} else if (n is CascadeExpression) {
_validateTopLevelInitializer(name, n.target);

View file

@ -2859,8 +2859,15 @@ class A {
final y = x;
}''');
await computeAnalysisResult(source);
assertErrors(
source, [CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER]);
if (enableKernelDriver) {
assertErrors(source,
[CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER]);
} else {
assertErrors(source, [
CompileTimeErrorCode.IMPLICIT_THIS_REFERENCE_IN_INITIALIZER,
StrongModeCode.TOP_LEVEL_INSTANCE_GETTER
]);
}
verify([source]);
}

View file

@ -4,6 +4,7 @@
library analyzer.test.generated.hint_code_test;
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/error/error.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
@ -2898,6 +2899,206 @@ main() {
verify([source]);
}
test_strongMode_topLevelInstanceGetter() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
int get g => 0;
}
var b = new A().g;
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
TopLevelVariableDeclaration b = analysisResult.unit.declarations[1];
expect(b.variables.variables[0].element.type.toString(), 'int');
verify([source]);
}
test_strongMode_topLevelInstanceGetter_call() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
int Function() get g => () => 0;
}
var a = new A();
var b = a.g();
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
TopLevelVariableDeclaration b = analysisResult.unit.declarations[2];
expect(b.variables.variables[0].element.type.toString(), 'int');
verify([source]);
}
test_strongMode_topLevelInstanceGetter_field() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
int g;
}
var b = new A().g;
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
TopLevelVariableDeclaration b = analysisResult.unit.declarations[1];
expect(b.variables.variables[0].element.type.toString(), 'int');
verify([source]);
}
test_strongMode_topLevelInstanceGetter_field_call() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
int Function() g;
}
var a = new A();
var b = a.g();
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
TopLevelVariableDeclaration b = analysisResult.unit.declarations[2];
expect(b.variables.variables[0].element.type.toString(), 'int');
verify([source]);
}
test_strongMode_topLevelInstanceGetter_field_prefixedIdentifier() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
int g;
}
var a = new A();
var b = a.g;
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
TopLevelVariableDeclaration b = analysisResult.unit.declarations[2];
expect(b.variables.variables[0].element.type.toString(), 'int');
verify([source]);
}
test_strongMode_topLevelInstanceGetter_implicitlyTyped() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
get g => 0;
}
var b = new A().g;
''');
await computeAnalysisResult(source);
if (enableKernelDriver) {
assertNoErrors(source);
} else {
assertErrors(source, [StrongModeCode.TOP_LEVEL_INSTANCE_GETTER]);
}
verify([source]);
}
test_strongMode_topLevelInstanceGetter_implicitlyTyped_call() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
get g => () => 0;
}
var a = new A();
var b = a.g();
''');
await computeAnalysisResult(source);
if (enableKernelDriver) {
assertNoErrors(source);
} else {
assertErrors(source, [StrongModeCode.TOP_LEVEL_INSTANCE_GETTER]);
}
verify([source]);
}
test_strongMode_topLevelInstanceGetter_implicitlyTyped_field() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
var g = 0;
}
var b = new A().g;
''');
await computeAnalysisResult(source);
if (enableKernelDriver) {
assertNoErrors(source);
} else {
assertErrors(source, [StrongModeCode.TOP_LEVEL_INSTANCE_GETTER]);
}
verify([source]);
}
test_strongMode_topLevelInstanceGetter_implicitlyTyped_field_call() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
var g = () => 0;
}
var a = new A();
var b = a.g();
''');
await computeAnalysisResult(source);
if (enableKernelDriver) {
assertNoErrors(source);
} else {
assertErrors(source, [StrongModeCode.TOP_LEVEL_INSTANCE_GETTER]);
}
verify([source]);
}
test_strongMode_topLevelInstanceGetter_implicitlyTyped_field_prefixedIdentifier() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
var g = 0;
}
var a = new A();
var b = a.g;
''');
await computeAnalysisResult(source);
if (enableKernelDriver) {
assertNoErrors(source);
} else {
assertErrors(source, [StrongModeCode.TOP_LEVEL_INSTANCE_GETTER]);
}
verify([source]);
}
test_strongMode_topLevelInstanceGetter_implicitlyTyped_prefixedIdentifier() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
get g => 0;
}
var a = new A();
var b = a.g;
''');
await computeAnalysisResult(source);
if (enableKernelDriver) {
assertNoErrors(source);
} else {
assertErrors(source, [StrongModeCode.TOP_LEVEL_INSTANCE_GETTER]);
}
verify([source]);
}
test_strongMode_topLevelInstanceGetter_prefixedIdentifier() async {
resetWith(options: new AnalysisOptionsImpl()..strongMode = true);
Source source = addSource('''
class A {
int get g => 0;
}
var a = new A();
var b = a.g;
''');
var analysisResult = await computeAnalysisResult(source);
assertNoErrors(source);
TopLevelVariableDeclaration b = analysisResult.unit.declarations[2];
expect(b.variables.variables[0].element.type.toString(), 'int');
verify([source]);
}
test_typeCheck_type_is_Null() async {
Source source = addSource(r'''
m(i) {

View file

@ -1152,7 +1152,9 @@ abstract class AbstractResynthesizeTest extends AbstractSingleUnitTest {
void compareVariableElements(
VariableElement resynthesized, VariableElement original, String desc) {
compareElements(resynthesized, original, desc);
compareTypes(resynthesized.type, original.type, '$desc.type');
if ((resynthesized as VariableElementImpl).typeInferenceError == null) {
compareTypes(resynthesized.type, original.type, '$desc.type');
}
VariableElementImpl resynthesizedActual =
getActualElement(resynthesized, desc);
VariableElementImpl originalActual = getActualElement(original, desc);

View file

@ -1120,6 +1120,7 @@ string_unicode4_negative_test: CompileTimeError
super_bound_closure_test/none: CompileTimeError
super_setter_test: StaticWarning # Issue 28823
switch_case_test/none: CompileTimeError
type_inference_accessor_ref_test/06: MissingCompileTimeError
type_inference_circularity_test: MissingCompileTimeError
type_promotion_functions_test/01: Pass
type_promotion_functions_test/05: Pass
@ -1570,6 +1571,8 @@ transitive_private_library_access_test: MissingCompileTimeError
try_catch_on_syntax_test/07: MissingCompileTimeError
try_catch_syntax_test/08: MissingCompileTimeError
type_checks_in_factory_method_test/01: MissingCompileTimeError
type_inference_accessor_ref_test/03: MissingCompileTimeError
type_inference_accessor_ref_test/06: MissingCompileTimeError
type_inference_circularity_test: MissingCompileTimeError
type_promotion_functions_test/01: MissingCompileTimeError
type_promotion_functions_test/05: MissingCompileTimeError

View file

@ -1627,6 +1627,8 @@ library_env_test/has_no_mirror_support: Pass
object_has_no_call_method_test/02: MissingCompileTimeError
object_has_no_call_method_test/05: MissingCompileTimeError
object_has_no_call_method_test/08: MissingCompileTimeError
type_inference_accessor_ref_test/03: MissingCompileTimeError
type_inference_accessor_ref_test/06: MissingCompileTimeError
type_inference_circularity_test: MissingCompileTimeError
type_inference_inconsistent_inheritance_test: MissingCompileTimeError
@ -3732,6 +3734,8 @@ stacktrace_test: Pass, RuntimeError # # Issue 12698
truncdiv_test: RuntimeError # Issue 15246
try_catch_on_syntax_test/10: Fail # Issue 19823
try_catch_on_syntax_test/11: Fail # Issue 19823
type_inference_accessor_ref_test/03: MissingCompileTimeError
type_inference_accessor_ref_test/06: MissingCompileTimeError
type_inference_circularity_test: MissingCompileTimeError
type_inference_inconsistent_inheritance_test: MissingCompileTimeError
type_variable_conflict_test/01: Fail # Issue 13702

View file

@ -884,6 +884,8 @@ try_catch_on_syntax_test/10: MissingCompileTimeError
try_catch_on_syntax_test/11: MissingCompileTimeError
try_catch_syntax_test/08: MissingCompileTimeError
type_checks_in_factory_method_test/01: MissingCompileTimeError
type_inference_accessor_ref_test/03: MissingCompileTimeError
type_inference_accessor_ref_test/06: MissingCompileTimeError
type_inference_circularity_test: MissingCompileTimeError
type_inference_inconsistent_inheritance_test: MissingCompileTimeError
type_parameter_test/05: MissingCompileTimeError

View file

@ -842,6 +842,8 @@ try_catch_on_syntax_test/10: MissingCompileTimeError
try_catch_on_syntax_test/11: MissingCompileTimeError
try_catch_syntax_test/08: MissingCompileTimeError
type_checks_in_factory_method_test/01: MissingCompileTimeError
type_inference_accessor_ref_test/03: MissingCompileTimeError
type_inference_accessor_ref_test/06: MissingCompileTimeError
type_inference_circularity_test: MissingCompileTimeError
type_inference_inconsistent_inheritance_test: MissingCompileTimeError
type_promotion_functions_test/01: MissingCompileTimeError

View file

@ -0,0 +1,42 @@
// Copyright (c) 2018, 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.
/*@testedFeatures=inference*/
library test;
class A {
B b;
}
class B {
C get c => null;
void set c(C value) {}
}
class C {}
class D extends C {}
class E extends C {}
// Inferred type: A
var a = new A();
// Inferred type: C
var x = a.b.c;
// Inferred type: C
var y = a.b.c ??= new D();
test() {
// Verify the types of x and y by trying to assign to them.
x = new C(); //# 01: ok
x = new E(); //# 02: ok
x = new B(); //# 03: compile-time error
y = new C(); //# 04: ok
y = new E(); //# 05: ok
y = new B(); //# 06: compile-time error
}
main() {}