| // 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; |
| } |