blob: 58fc71e18f87ed2ce9f9a2d8e741be3d2f3095a0 [file] [log] [blame]
// Copyright (c) 2014, 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.
import 'dart:collection';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/visitor.dart';
import 'package:analyzer/dart/element/element.dart';
import 'package:analyzer/dart/element/type.dart';
import 'package:analyzer/src/dart/element/element.dart';
import 'package:analyzer/src/error/codes.dart';
import 'package:analyzer/src/generated/engine.dart';
import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
import 'package:analyzer/src/generated/resolver.dart';
import 'package:analyzer/src/generated/testing/ast_test_factory.dart';
import 'package:analyzer/src/generated/testing/element_factory.dart';
import 'package:test/test.dart';
import 'package:test_reflective_loader/test_reflective_loader.dart';
import '../src/dart/resolution/driver_resolution.dart';
import 'resolver_test_case.dart';
import 'test_analysis_context.dart';
main() {
defineReflectiveSuite(() {
defineReflectiveTests(EnclosedScopeTest);
defineReflectiveTests(ErrorResolverTest);
defineReflectiveTests(LibraryImportScopeTest);
defineReflectiveTests(LibraryScopeTest);
defineReflectiveTests(PrefixedNamespaceTest);
defineReflectiveTests(ScopeTest);
defineReflectiveTests(StrictModeTest);
defineReflectiveTests(TypePropagationTest);
});
}
@reflectiveTest
class EnclosedScopeTest extends DriverResolutionTest {
test_define_duplicate() async {
Scope rootScope = new _RootScope();
EnclosedScope scope = new EnclosedScope(rootScope);
SimpleIdentifier identifier = AstTestFactory.identifier3('v');
VariableElement element1 = ElementFactory.localVariableElement(identifier);
VariableElement element2 = ElementFactory.localVariableElement(identifier);
scope.define(element1);
scope.define(element2);
expect(scope.lookup(identifier, null), same(element1));
}
}
@reflectiveTest
class ErrorResolverTest extends DriverResolutionTest {
test_breakLabelOnSwitchMember() async {
await assertErrorsInCode(r'''
class A {
void m(int i) {
switch (i) {
l: case 0:
break;
case 1:
break l;
}
}
}''', [
error(ResolverErrorCode.BREAK_LABEL_ON_SWITCH_MEMBER, 105, 1),
]);
}
test_continueLabelOnSwitch() async {
await assertErrorsInCode(r'''
class A {
void m(int i) {
l: switch (i) {
case 0:
continue l;
}
}
}''', [
error(ResolverErrorCode.CONTINUE_LABEL_ON_SWITCH, 79, 1),
]);
}
test_enclosingElement_invalidLocalFunction() async {
await resolveTestCode(r'''
class C {
C() {
int get x => 0;
}
}''');
assertTestErrorsWithCodes([
ParserErrorCode.MISSING_FUNCTION_PARAMETERS,
ParserErrorCode.EXPECTED_TOKEN
]);
var constructor = findElement.unnamedConstructor('C');
var x = findElement.localFunction('x');
expect(x.enclosingElement, constructor);
}
}
/**
* Tests for generic method and function resolution that do not use strong mode.
*/
@reflectiveTest
class GenericMethodResolverTest extends StaticTypeAnalyzer2TestShared {
test_genericMethod_propagatedType_promotion() async {
// Regression test for:
// https://github.com/dart-lang/sdk/issues/25340
//
// Note, after https://github.com/dart-lang/sdk/issues/25486 the original
// strong mode example won't work, as we now compute a static type and
// therefore discard the propagated type.
//
// So this test does not use strong mode.
await assertNoErrorsInCode(r'''
abstract class Iter {
List<S> map<S>(S f(x));
}
class C {}
C toSpan(dynamic element) {
if (element is Iter) {
var y = element.map(toSpan);
}
return null;
}''');
expectIdentifierType('y = ', 'dynamic');
}
}
@reflectiveTest
class LibraryImportScopeTest extends ResolverTestCase {
void test_creation_empty() {
new LibraryImportScope(createDefaultTestLibrary());
}
void test_creation_nonEmpty() {
AnalysisContext context = TestAnalysisContext();
String importedTypeName = "A";
ClassElement importedType = new ClassElementImpl.forNode(
AstTestFactory.identifier3(importedTypeName));
LibraryElement importedLibrary = createTestLibrary(context, "imported");
(importedLibrary.definingCompilationUnit as CompilationUnitElementImpl)
.types = <ClassElement>[importedType];
LibraryElementImpl definingLibrary =
createTestLibrary(context, "importing");
ImportElementImpl importElement = new ImportElementImpl(0);
importElement.importedLibrary = importedLibrary;
definingLibrary.imports = <ImportElement>[importElement];
Scope scope = new LibraryImportScope(definingLibrary);
expect(
scope.lookup(
AstTestFactory.identifier3(importedTypeName), definingLibrary),
importedType);
}
void test_extensions_imported() {
var context = TestAnalysisContext();
var extension = ElementFactory.extensionElement('test_extension');
var importedUnit1 = ElementFactory.compilationUnit('/imported1.dart');
importedUnit1.extensions = <ExtensionElement>[extension];
var importedLibraryName = 'imported_lib';
var importedLibrary = LibraryElementImpl(context, null, importedLibraryName,
0, importedLibraryName.length, false);
importedLibrary.definingCompilationUnit = importedUnit1;
var importingLibraryName = 'importing_lib';
var importingLibrary = LibraryElementImpl(context, null,
importingLibraryName, 0, importingLibraryName.length, false);
importingLibrary.definingCompilationUnit =
ElementFactory.compilationUnit('/importing.dart');
var importElement = ImportElementImpl(0);
importElement.importedLibrary = importedLibrary;
importingLibrary.imports = <ImportElement>[importElement];
expect(
LibraryImportScope(importingLibrary).extensions, contains(extension));
}
void test_prefixedAndNonPrefixed() {
AnalysisContext context = TestAnalysisContext();
String typeName = "C";
String prefixName = "p";
ClassElement prefixedType = ElementFactory.classElement2(typeName);
ClassElement nonPrefixedType = ElementFactory.classElement2(typeName);
LibraryElement prefixedLibrary =
createTestLibrary(context, "import.prefixed");
(prefixedLibrary.definingCompilationUnit as CompilationUnitElementImpl)
.types = <ClassElement>[prefixedType];
ImportElementImpl prefixedImport = ElementFactory.importFor(
prefixedLibrary, ElementFactory.prefix(prefixName));
LibraryElement nonPrefixedLibrary =
createTestLibrary(context, "import.nonPrefixed");
(nonPrefixedLibrary.definingCompilationUnit as CompilationUnitElementImpl)
.types = <ClassElement>[nonPrefixedType];
ImportElementImpl nonPrefixedImport =
ElementFactory.importFor(nonPrefixedLibrary, null);
LibraryElementImpl importingLibrary =
createTestLibrary(context, "importing");
importingLibrary.imports = <ImportElement>[
prefixedImport,
nonPrefixedImport
];
Scope scope = new LibraryImportScope(importingLibrary);
Element prefixedElement = scope.lookup(
AstTestFactory.identifier5(prefixName, typeName), importingLibrary);
expect(prefixedElement, same(prefixedType));
Element nonPrefixedElement =
scope.lookup(AstTestFactory.identifier3(typeName), importingLibrary);
expect(nonPrefixedElement, same(nonPrefixedType));
}
}
@reflectiveTest
class LibraryScopeTest extends ResolverTestCase {
void test_creation_empty() {
new LibraryScope(createDefaultTestLibrary());
}
void test_creation_nonEmpty() {
AnalysisContext context = TestAnalysisContext();
String importedTypeName = "A";
ClassElement importedType = new ClassElementImpl.forNode(
AstTestFactory.identifier3(importedTypeName));
LibraryElement importedLibrary = createTestLibrary(context, "imported");
(importedLibrary.definingCompilationUnit as CompilationUnitElementImpl)
.types = <ClassElement>[importedType];
LibraryElementImpl definingLibrary =
createTestLibrary(context, "importing");
ImportElementImpl importElement = new ImportElementImpl(0);
importElement.importedLibrary = importedLibrary;
definingLibrary.imports = <ImportElement>[importElement];
Scope scope = new LibraryScope(definingLibrary);
expect(
scope.lookup(
AstTestFactory.identifier3(importedTypeName), definingLibrary),
importedType);
}
void test_extensions() {
ExtensionElement extension =
ElementFactory.extensionElement('test_extension');
CompilationUnitElementImpl compilationUnit =
ElementFactory.compilationUnit('/test.dart');
compilationUnit.extensions = <ExtensionElement>[extension];
String libraryName = 'lib';
LibraryElementImpl library = new LibraryElementImpl(
null, null, libraryName, 0, libraryName.length, false);
library.definingCompilationUnit = compilationUnit;
expect(LibraryScope(library).extensions, contains(extension));
}
void test_extensions_imported() {
var context = TestAnalysisContext();
var importedUnit1 = ElementFactory.compilationUnit('/imported1.dart');
var importedExtension = ElementFactory.extensionElement('test_extension');
var unnamedImportedExtension = ElementFactory.extensionElement();
importedUnit1.extensions = [importedExtension, unnamedImportedExtension];
var importedLibraryName = 'imported_lib';
var importedLibrary = LibraryElementImpl(context, null, importedLibraryName,
0, importedLibraryName.length, false);
importedLibrary.definingCompilationUnit = importedUnit1;
var importingLibraryName = 'importing_lib';
var importingLibrary = LibraryElementImpl(context, null,
importingLibraryName, 0, importingLibraryName.length, false);
var localExtension = ElementFactory.extensionElement('test_extension');
var importingUnit = ElementFactory.compilationUnit('/importing.dart');
importingUnit.extensions = [localExtension];
importingLibrary.definingCompilationUnit = importingUnit;
var importElement = ImportElementImpl(0);
importElement.importedLibrary = importedLibrary;
importingLibrary.imports = [importElement];
var libraryExtensions = LibraryScope(importingLibrary).extensions;
expect(libraryExtensions, contains(localExtension));
expect(libraryExtensions, contains(importedExtension));
expect(libraryExtensions, isNot(contains(unnamedImportedExtension)));
}
/// Ensure that if a library L1 defines an extension E, L2 exports L1, and L3
/// imports L2, then E is included in the list.
void test_extensions_imported_chain() {
var context = TestAnalysisContext();
var unit1 = ElementFactory.compilationUnit('/unit1.dart');
var ext1 = ElementFactory.extensionElement('ext1');
unit1.extensions = [ext1];
var lib1Name = 'lib1';
var lib1 =
LibraryElementImpl(context, null, lib1Name, 0, lib1Name.length, false);
lib1.definingCompilationUnit = unit1;
var unit2 = ElementFactory.compilationUnit('/unit2.dart');
var lib2Name = 'lib2';
var lib2 =
LibraryElementImpl(context, null, lib2Name, 0, lib2Name.length, false);
lib2.definingCompilationUnit = unit2;
var lib1Export = ExportElementImpl(0);
lib1Export.exportedLibrary = lib1;
lib2.exports = [lib1Export];
var importingLibraryName = 'importing_lib';
var importingLibrary = LibraryElementImpl(context, null,
importingLibraryName, 0, importingLibraryName.length, false);
var importingUnit = ElementFactory.compilationUnit('/importing.dart');
importingLibrary.definingCompilationUnit = importingUnit;
var lib2Import = ImportElementImpl(0);
lib2Import.importedLibrary = lib2;
importingLibrary.imports = [lib2Import];
var libraryExtensions = LibraryScope(importingLibrary).extensions;
expect(libraryExtensions, orderedEquals([ext1]));
}
/// Ensure that if there are two extensions with the same name that are
/// imported from different libraries that they are both in the list of
/// extensions.
void test_extensions_imported_same_name() {
var context = TestAnalysisContext();
var sharedExtensionName = 'test_ext';
var unit1 = ElementFactory.compilationUnit('/unit1.dart');
var ext1 = ElementFactory.extensionElement(sharedExtensionName);
unit1.extensions = [ext1];
var lib1Name = 'lib1';
var lib1 =
LibraryElementImpl(context, null, lib1Name, 0, lib1Name.length, false);
lib1.definingCompilationUnit = unit1;
var unit2 = ElementFactory.compilationUnit('/unit2.dart');
var ext2 = ElementFactory.extensionElement(sharedExtensionName);
unit2.extensions = [ext2];
var lib2Name = 'lib2';
var lib2 =
LibraryElementImpl(context, null, lib2Name, 0, lib2Name.length, false);
lib2.definingCompilationUnit = unit2;
var importingLibraryName = 'importing_lib';
var importingLibrary = LibraryElementImpl(context, null,
importingLibraryName, 0, importingLibraryName.length, false);
var importingUnit = ElementFactory.compilationUnit('/importing.dart');
importingLibrary.definingCompilationUnit = importingUnit;
var importElement1 = ImportElementImpl(0);
importElement1.importedLibrary = lib1;
var importElement2 = ImportElementImpl(0);
importElement2.importedLibrary = lib2;
importingLibrary.imports = [importElement1, importElement2];
var libraryExtensions = LibraryScope(importingLibrary).extensions;
expect(libraryExtensions, contains(ext1));
expect(libraryExtensions, contains(ext2));
}
/// Ensure that if there are two imports for the same library that the
/// imported extension is only in the list one time.
void test_extensions_imported_twice() {
var context = TestAnalysisContext();
var sharedExtensionName = 'test_ext';
var unit1 = ElementFactory.compilationUnit('/unit1.dart');
var ext1 = ElementFactory.extensionElement(sharedExtensionName);
unit1.extensions = [ext1];
var lib1Name = 'lib1';
var lib1 =
LibraryElementImpl(context, null, lib1Name, 0, lib1Name.length, false);
lib1.definingCompilationUnit = unit1;
var importingLibraryName = 'importing_lib';
var importingLibrary = LibraryElementImpl(context, null,
importingLibraryName, 0, importingLibraryName.length, false);
var importingUnit = ElementFactory.compilationUnit('/importing.dart');
importingLibrary.definingCompilationUnit = importingUnit;
var importElement1 = ImportElementImpl(0);
importElement1.importedLibrary = lib1;
var importElement2 = ImportElementImpl(0);
importElement2.importedLibrary = lib1;
importingLibrary.imports = [importElement1, importElement2];
var libraryExtensions = LibraryScope(importingLibrary).extensions;
expect(libraryExtensions, orderedEquals([ext1]));
}
}
@reflectiveTest
class PrefixedNamespaceTest extends DriverResolutionTest {
void test_lookup_missing() {
ClassElement element = ElementFactory.classElement2('A');
PrefixedNamespace namespace = new PrefixedNamespace('p', _toMap([element]));
expect(namespace.get('p.B'), isNull);
}
void test_lookup_missing_matchesPrefix() {
ClassElement element = ElementFactory.classElement2('A');
PrefixedNamespace namespace = new PrefixedNamespace('p', _toMap([element]));
expect(namespace.get('p'), isNull);
}
void test_lookup_valid() {
ClassElement element = ElementFactory.classElement2('A');
PrefixedNamespace namespace = new PrefixedNamespace('p', _toMap([element]));
expect(namespace.get('p.A'), same(element));
}
Map<String, Element> _toMap(List<Element> elements) {
Map<String, Element> map = new HashMap<String, Element>();
for (Element element in elements) {
map[element.name] = element;
}
return map;
}
}
@reflectiveTest
class ScopeTest extends DriverResolutionTest {
void test_define_duplicate() {
Scope scope = new _RootScope();
SimpleIdentifier identifier = AstTestFactory.identifier3('v');
VariableElement element1 = ElementFactory.localVariableElement(identifier);
VariableElement element2 = ElementFactory.localVariableElement(identifier);
scope.define(element1);
scope.define(element2);
expect(scope.localLookup('v', null), same(element1));
}
void test_isPrivateName_nonPrivate() {
expect(Scope.isPrivateName("Public"), isFalse);
}
void test_isPrivateName_private() {
expect(Scope.isPrivateName("_Private"), isTrue);
}
}
/**
* Instances of the class `StaticTypeVerifier` verify that all of the nodes in an AST
* structure that should have a static type associated with them do have a static type.
*/
class StaticTypeVerifier extends GeneralizingAstVisitor<void> {
/**
* A list containing all of the AST Expression nodes that were not resolved.
*/
List<Expression> _unresolvedExpressions = new List<Expression>();
/**
* The TypeAnnotation nodes that were not resolved.
*/
List<TypeAnnotation> _unresolvedTypes = new List<TypeAnnotation>();
/**
* Counter for the number of Expression nodes visited that are resolved.
*/
int _resolvedExpressionCount = 0;
/**
* Counter for the number of TypeName nodes visited that are resolved.
*/
int _resolvedTypeCount = 0;
/**
* Assert that all of the visited nodes have a static type associated with them.
*/
void assertResolved() {
if (_unresolvedExpressions.isNotEmpty || _unresolvedTypes.isNotEmpty) {
StringBuffer buffer = new StringBuffer();
int unresolvedTypeCount = _unresolvedTypes.length;
if (unresolvedTypeCount > 0) {
buffer.write("Failed to resolve ");
buffer.write(unresolvedTypeCount);
buffer.write(" of ");
buffer.write(_resolvedTypeCount + unresolvedTypeCount);
buffer.writeln(" type names:");
for (TypeAnnotation identifier in _unresolvedTypes) {
buffer.write(" ");
buffer.write(identifier.toString());
buffer.write(" (");
buffer.write(_getFileName(identifier));
buffer.write(" : ");
buffer.write(identifier.offset);
buffer.writeln(")");
}
}
int unresolvedExpressionCount = _unresolvedExpressions.length;
if (unresolvedExpressionCount > 0) {
buffer.writeln("Failed to resolve ");
buffer.write(unresolvedExpressionCount);
buffer.write(" of ");
buffer.write(_resolvedExpressionCount + unresolvedExpressionCount);
buffer.writeln(" expressions:");
for (Expression expression in _unresolvedExpressions) {
buffer.write(" ");
buffer.write(expression.toString());
buffer.write(" (");
buffer.write(_getFileName(expression));
buffer.write(" : ");
buffer.write(expression.offset);
buffer.writeln(")");
}
}
fail(buffer.toString());
}
}
@override
void visitBreakStatement(BreakStatement node) {}
@override
void visitCommentReference(CommentReference node) {}
@override
void visitContinueStatement(ContinueStatement node) {}
@override
void visitExportDirective(ExportDirective node) {}
@override
void visitExpression(Expression node) {
node.visitChildren(this);
DartType staticType = node.staticType;
if (staticType == null) {
_unresolvedExpressions.add(node);
} else {
_resolvedExpressionCount++;
}
}
@override
void visitImportDirective(ImportDirective node) {}
@override
void visitLabel(Label node) {}
@override
void visitLibraryIdentifier(LibraryIdentifier node) {}
@override
void visitPrefixedIdentifier(PrefixedIdentifier node) {
// In cases where we have a prefixed identifier where the prefix is dynamic,
// we don't want to assert that the node will have a type.
if (node.staticType == null && node.prefix.staticType.isDynamic) {
return;
}
super.visitPrefixedIdentifier(node);
}
@override
void visitSimpleIdentifier(SimpleIdentifier node) {
// In cases where identifiers are being used for something other than an
// expressions, then they can be ignored.
AstNode parent = node.parent;
if (parent is MethodInvocation && identical(node, parent.methodName)) {
return;
} else if (parent is RedirectingConstructorInvocation &&
identical(node, parent.constructorName)) {
return;
} else if (parent is SuperConstructorInvocation &&
identical(node, parent.constructorName)) {
return;
} else if (parent is ConstructorName && identical(node, parent.name)) {
return;
} else if (parent is ConstructorFieldInitializer &&
identical(node, parent.fieldName)) {
return;
} else if (node.staticElement is PrefixElement) {
// Prefixes don't have a type.
return;
}
super.visitSimpleIdentifier(node);
}
@override
void visitTypeAnnotation(TypeAnnotation node) {
if (node.type == null) {
_unresolvedTypes.add(node);
} else {
_resolvedTypeCount++;
}
super.visitTypeAnnotation(node);
}
@override
void visitTypeName(TypeName node) {
// Note: do not visit children from this node, the child SimpleIdentifier in
// TypeName (i.e. "String") does not have a static type defined.
// TODO(brianwilkerson) Not visiting the children means that we won't catch
// type arguments that were not resolved.
if (node.type == null) {
_unresolvedTypes.add(node);
} else {
_resolvedTypeCount++;
}
}
String _getFileName(AstNode node) {
// TODO (jwren) there are two copies of this method, one here and one in
// ResolutionVerifier, they should be resolved into a single method
if (node != null) {
AstNode root = node.root;
if (root is CompilationUnit) {
CompilationUnit rootCU = root;
if (rootCU.declaredElement != null) {
return rootCU.declaredElement.source.fullName;
} else {
return "<unknown file- CompilationUnit.getElement() returned null>";
}
} else {
return "<unknown file- CompilationUnit.getRoot() is not a CompilationUnit>";
}
}
return "<unknown file- ASTNode is null>";
}
}
/**
* The class `StrictModeTest` contains tests to ensure that the correct errors
* and warnings are reported when the analysis engine is run in strict mode.
*/
@reflectiveTest
class StrictModeTest extends DriverResolutionTest {
test_assert_is() async {
await assertErrorsInCode(r'''
int f(num n) {
assert (n is int);
return n & 0x0F;
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 47, 1),
]);
}
test_conditional_and_is() async {
await assertNoErrorsInCode(r'''
int f(num n) {
return (n is int && n > 0) ? n & 0x0F : 0;
}''');
}
test_conditional_is() async {
await assertNoErrorsInCode(r'''
int f(num n) {
return (n is int) ? n & 0x0F : 0;
}''');
}
test_conditional_isNot() async {
await assertErrorsInCode(r'''
int f(num n) {
return (n is! int) ? 0 : n & 0x0F;
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 44, 1),
]);
}
test_conditional_or_is() async {
await assertErrorsInCode(r'''
int f(num n) {
return (n is! int || n < 0) ? 0 : n & 0x0F;
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 53, 1),
]);
}
// @failingTest
test_for() async {
await assertErrorsInCode(r'''
int f(List<int> list) {
num sum = 0;
for (num i = 0; i < list.length; i++) {
sum += list[i];
}
}''', [
error(HintCode.MISSING_RETURN, 4, 1),
error(HintCode.UNUSED_LOCAL_VARIABLE, 30, 3),
]);
}
test_forEach() async {
await assertErrorsInCode(r'''
int f(List<int> list) {
num sum = 0;
for (num n in list) {
sum += n & 0x0F;
}
}''', [
error(HintCode.MISSING_RETURN, 4, 1),
error(HintCode.UNUSED_LOCAL_VARIABLE, 30, 3),
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 76, 1),
]);
}
test_if_and_is() async {
await assertNoErrorsInCode(r'''
int f(num n) {
if (n is int && n > 0) {
return n & 0x0F;
}
return 0;
}''');
}
test_if_is() async {
await assertNoErrorsInCode(r'''
int f(num n) {
if (n is int) {
return n & 0x0F;
}
return 0;
}''');
}
test_if_isNot() async {
await assertErrorsInCode(r'''
int f(num n) {
if (n is! int) {
return 0;
} else {
return n & 0x0F;
}
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 72, 1),
]);
}
test_if_isNot_abrupt() async {
await assertErrorsInCode(r'''
int f(num n) {
if (n is! int) {
return 0;
}
return n & 0x0F;
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 63, 1),
]);
}
test_if_or_is() async {
await assertErrorsInCode(r'''
int f(num n) {
if (n is! int || n < 0) {
return 0;
} else {
return n & 0x0F;
}
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 81, 1),
]);
}
test_localVar() async {
await assertErrorsInCode(r'''
int f() {
num n = 1234;
return n & 0x0F;
}''', [
error(StaticTypeWarningCode.UNDEFINED_OPERATOR, 37, 1),
]);
}
}
@reflectiveTest
class TypePropagationTest extends DriverResolutionTest {
test_assignment_null() async {
String code = r'''
main() {
int v; // declare
v = null;
return v; // return
}''';
await resolveTestCode(code);
assertElementTypeString(findElement.localVar('v').type, 'int');
assertTypeNull(findNode.simple('v; // declare'));
assertType(findNode.simple('v = null;'), 'int');
assertType(findNode.simple('v; // return'), 'int');
}
test_functionExpression_asInvocationArgument_notSubtypeOfStaticType() async {
await assertErrorsInCode(r'''
class A {
m(void f(int i)) {}
}
x() {
A a = new A();
a.m(() => 0);
}''', [
error(StaticWarningCode.ARGUMENT_TYPE_NOT_ASSIGNABLE, 63, 7),
]);
assertType(findNode.functionExpression('() => 0'), 'int Function()');
}
test_initializer_hasStaticType() async {
await resolveTestCode(r'''
f() {
int v = 0;
return v;
}''');
assertElementTypeString(findElement.localVar('v').type, 'int');
assertTypeNull(findNode.simple('v = 0;'));
assertType(findNode.simple('v;'), 'int');
}
test_initializer_hasStaticType_parameterized() async {
await resolveTestCode(r'''
f() {
List<int> v = <int>[];
return v;
}''');
assertElementTypeString(findElement.localVar('v').type, 'List<int>');
assertTypeNull(findNode.simple('v ='));
assertType(findNode.simple('v;'), 'List<int>');
}
test_initializer_null() async {
await resolveTestCode(r'''
main() {
int v = null;
return v;
}''');
assertElementTypeString(findElement.localVar('v').type, 'int');
assertTypeNull(findNode.simple('v ='));
assertType(findNode.simple('v;'), 'int');
}
test_invocation_target_prefixed() async {
newFile('/test/lib/a.dart', content: r'''
int max(int x, int y) => 0;
''');
await resolveTestCode('''
import 'a.dart' as helper;
main() {
helper.max(10, 10); // marker
}''');
assertElement(
findNode.simple('max(10, 10)'),
findElement.importFind('package:test/a.dart').topFunction('max'),
);
}
test_is_subclass() async {
await resolveTestCode(r'''
class A {}
class B extends A {
B m() => this;
}
A f(A p) {
if (p is B) {
return p.m();
}
return p;
}''');
assertElement(
findNode.methodInvocation('p.m()'),
findElement.method('m', of: 'B'),
);
}
test_mutatedOutsideScope() async {
// https://code.google.com/p/dart/issues/detail?id=22732
await assertNoErrorsInCode(r'''
class Base {
}
class Derived extends Base {
get y => null;
}
class C {
void f() {
Base x = null;
if (x is Derived) {
print(x.y); // BAD
}
x = null;
}
}
void g() {
Base x = null;
if (x is Derived) {
print(x.y); // GOOD
}
x = null;
}''');
}
test_objectAccessInference_disabled_for_library_prefix() async {
newFile('/test/lib/a.dart', content: '''
dynamic get hashCode => 42;
''');
await assertNoErrorsInCode('''
import 'a.dart' as helper;
main() {
helper.hashCode;
}''');
assertTypeDynamic(findNode.prefixed('helper.hashCode'));
}
test_objectAccessInference_disabled_for_local_getter() async {
await assertNoErrorsInCode('''
dynamic get hashCode => null;
main() {
hashCode; // marker
}''');
assertTypeDynamic(findNode.simple('hashCode; // marker'));
}
test_objectMethodInference_disabled_for_library_prefix() async {
newFile('/test/lib/a.dart', content: '''
dynamic toString = (int x) => x + 42;
''');
await assertNoErrorsInCode('''
import 'a.dart' as helper;
main() {
helper.toString();
}''');
assertTypeDynamic(
findNode.functionExpressionInvocation('helper.toString()'),
);
}
test_objectMethodInference_disabled_for_local_function() async {
await resolveTestCode('''
main() {
dynamic toString = () => null;
toString(); // marker
}''');
assertElementTypeDynamic(findElement.localVar('toString').type);
assertTypeNull(findNode.simple('toString ='));
assertTypeDynamic(findNode.simple('toString(); // marker'));
}
@failingTest
test_propagatedReturnType_functionExpression() async {
// TODO(scheglov) disabled because we don't resolve function expression
await resolveTestCode(r'''
main() {
var v = (() {return 42;})();
}''');
assertTypeDynamic(findNode.simple('v = '));
}
}
class _RootScope extends Scope {
@override
Element internalLookup(Identifier identifier, String name,
LibraryElement referencingLibrary) =>
null;
}