diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart index 0b6d2cadbd6c..bf04895a70cb 100644 --- a/pkg/analyzer/lib/src/generated/error_verifier.dart +++ b/pkg/analyzer/lib/src/generated/error_verifier.dart @@ -16,6 +16,7 @@ import 'package:analyzer/error/error.dart'; import 'package:analyzer/error/listener.dart'; import 'package:analyzer/src/dart/ast/ast.dart'; import 'package:analyzer/src/dart/element/element.dart'; +import 'package:analyzer/src/dart/element/handle.dart'; import 'package:analyzer/src/dart/element/member.dart'; import 'package:analyzer/src/dart/element/type.dart'; import 'package:analyzer/src/dart/resolver/inheritance_manager.dart'; @@ -4273,9 +4274,12 @@ class ErrorVerifier extends RecursiveAstVisitor { /// [mixinIndex] in the list of mixins of [_enclosingClass] has concrete /// implementations of all the super-invoked members of the [mixinElement]. bool _checkForMixinSuperInvokedMembers( - int mixinIndex, TypeName mixinName, ClassElementImpl mixinElement) { + int mixinIndex, TypeName mixinName, ClassElement mixinElement) { + ClassElementImpl mixinElementImpl = mixinElement is ClassElementHandle + ? mixinElement.actualElement + : mixinElement; InterfaceTypeImpl enclosingType = _enclosingClass.type; - for (var name in mixinElement.superInvokedNames) { + for (var name in mixinElementImpl.superInvokedNames) { var superMember = enclosingType.lookUpInheritedMember( name, _currentLibrary, concrete: true, @@ -5801,7 +5805,7 @@ class ErrorVerifier extends RecursiveAstVisitor { void _checkMixinInference( NamedCompilationUnitMember node, WithClause withClause) { - if (withClause == null || !_options.enableSuperMixins) { + if (withClause == null) { return; } ClassElement classElement = node.declaredElement; @@ -5811,7 +5815,8 @@ class ErrorVerifier extends RecursiveAstVisitor { ClassElementImpl.collectAllSupertypes( supertypesForMixinInference, supertype, type); for (var typeName in withClause.mixinTypes) { - var mixinElement = typeName.name.staticElement; + var mixinType = typeName.type; + var mixinElement = mixinType.element; if (mixinElement is ClassElement) { if (typeName.typeArguments == null) { var mixinSupertypeConstraints = @@ -5840,7 +5845,7 @@ class ErrorVerifier extends RecursiveAstVisitor { } } ClassElementImpl.collectAllSupertypes( - supertypesForMixinInference, mixinElement.type, type); + supertypesForMixinInference, mixinType, type); } } } diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart index 4f5e506ee77f..b5f0cb07441d 100644 --- a/pkg/analyzer/lib/src/generated/resolver.dart +++ b/pkg/analyzer/lib/src/generated/resolver.dart @@ -7546,10 +7546,18 @@ class TypeNameResolver { final Source source; final AnalysisErrorListener errorListener; + /// Indicates whether bare typenames in "with" clauses should have their type + /// inferred type arguments loaded from the element model. + /// + /// This is needed for mixin type inference, but is incompatible with the old + /// task model. + final bool shouldUseWithClauseInferredTypes; + Scope nameScope; TypeNameResolver(this.typeSystem, TypeProvider typeProvider, - this.definingLibrary, this.source, this.errorListener) + this.definingLibrary, this.source, this.errorListener, + {this.shouldUseWithClauseInferredTypes: true}) : dynamicType = typeProvider.dynamicType, undefinedType = typeProvider.undefinedType; @@ -7831,8 +7839,35 @@ class TypeNameResolver { } } } - typeName.staticType = type; - node.type = type; + DartType refinedType; + if (shouldUseWithClauseInferredTypes) { + var parent = node.parent; + if (parent is WithClause && + type is InterfaceType && + type.element.typeParameters.isNotEmpty) { + // Get the (possibly inferred) mixin type from the element model. + var grandParent = parent.parent; + if (grandParent is ClassDeclaration) { + refinedType = + _getInferredMixinType(grandParent.declaredElement, type.element); + } else if (grandParent is ClassTypeAlias) { + refinedType = + _getInferredMixinType(grandParent.declaredElement, type.element); + } else { + assert(false, 'Unexpected context for "with" clause'); + } + } + } + refinedType ??= type; + typeName.staticType = refinedType; + node.type = refinedType; + } + + DartType _getInferredMixinType( + ClassElement classElement, ClassElement mixinElement) { + for (var candidateMixin in classElement.mixins) { + if (candidateMixin.element == mixinElement) return candidateMixin; + } } /// The number of type arguments in the given [typeName] does not match the @@ -8863,14 +8898,17 @@ class TypeResolverVisitor extends ScopedVisitor { /// created based on [definingLibrary] and [typeProvider]. TypeResolverVisitor(LibraryElement definingLibrary, Source source, TypeProvider typeProvider, AnalysisErrorListener errorListener, - {Scope nameScope, this.mode: TypeResolverMode.everything}) + {Scope nameScope, + this.mode: TypeResolverMode.everything, + bool shouldUseWithClauseInferredTypes: true}) : super(definingLibrary, source, typeProvider, errorListener, nameScope: nameScope) { _dynamicType = typeProvider.dynamicType; _undefinedType = typeProvider.undefinedType; _typeSystem = TypeSystem.create(definingLibrary.context); _typeNameResolver = new TypeNameResolver( - _typeSystem, typeProvider, definingLibrary, source, errorListener); + _typeSystem, typeProvider, definingLibrary, source, errorListener, + shouldUseWithClauseInferredTypes: shouldUseWithClauseInferredTypes); } @override diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart index ec06d190aac4..1cedd7a0466c 100644 --- a/pkg/analyzer/lib/src/task/dart.dart +++ b/pkg/analyzer/lib/src/task/dart.dart @@ -5104,7 +5104,8 @@ class ResolveUnitTypeNamesTask extends SourceBasedAnalysisTask { // RecordingErrorListener errorListener = new RecordingErrorListener(); TypeResolverVisitor visitor = new TypeResolverVisitor( - library, unitElement.source, typeProvider, errorListener); + library, unitElement.source, typeProvider, errorListener, + shouldUseWithClauseInferredTypes: false); unit.accept(visitor); // // Re-write the AST to handle the optional new and const feature. diff --git a/pkg/analyzer/test/generated/non_error_resolver_test.dart b/pkg/analyzer/test/generated/non_error_resolver_test.dart index 3f637e7655ea..4f36053a6153 100644 --- a/pkg/analyzer/test/generated/non_error_resolver_test.dart +++ b/pkg/analyzer/test/generated/non_error_resolver_test.dart @@ -74,6 +74,12 @@ class NonErrorResolverTest extends NonErrorResolverTestBase { return super.test_mixinInference_with_actual_mixins(); } + @override + @failingTest // Does not work with old task model + test_mixinInference_with_actual_mixins_supermixins_enabled() { + return super.test_mixinInference_with_actual_mixins_supermixins_enabled(); + } + @override @failingTest test_null_callMethod() { @@ -3887,6 +3893,35 @@ mixin M1 on I { class A = I with M0, M1; +void main () { + var x = new A().foo(); +} +'''); + var result = await computeAnalysisResult(source); + assertNoErrors(source); + verify([source]); + var main = result.unit.declarations.last as FunctionDeclaration; + var mainBody = main.functionExpression.body as BlockFunctionBody; + var xDecl = mainBody.block.statements[0] as VariableDeclarationStatement; + var xElem = xDecl.variables.variables[0].declaredElement; + expect(xElem.type.toString(), 'int'); + } + + test_mixinInference_with_actual_mixins_supermixins_enabled() async { + AnalysisOptionsImpl options = new AnalysisOptionsImpl(); + options.enableSuperMixins = true; + resetWith(options: options); + Source source = addSource(''' +class I {} + +mixin M0 on I {} + +mixin M1 on I { + T foo() => null; +} + +class A = I with M0, M1; + void main () { var x = new A().foo(); } diff --git a/tests/language_2/issue34404_flutter_modified_test.dart b/tests/language_2/issue34404_flutter_modified_test.dart new file mode 100644 index 000000000000..b78202aa07bd --- /dev/null +++ b/tests/language_2/issue34404_flutter_modified_test.dart @@ -0,0 +1,33 @@ +// 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. + +// This test case is a reduction of some Flutter code, modified to use the new +// mixin syntax. We wish to verify that the class _DismissibleState doesn't +// have any type inference errors. + +class _DismissibleState extends State + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {} + +abstract class State extends Diagnosticable {} + +abstract class StatefulWidget extends Widget {} + +abstract class Widget extends DiagnosticableTree {} + +abstract class DiagnosticableTree extends Diagnosticable {} + +abstract class Diagnosticable {} + +class Dismissible extends StatefulWidget {} + +mixin TickerProviderStateMixin on State + implements TickerProvider {} + +abstract class TickerProvider {} + +mixin AutomaticKeepAliveClientMixin on State {} + +main() { + new _DismissibleState(); +} diff --git a/tests/language_2/issue34404_flutter_test.dart b/tests/language_2/issue34404_flutter_test.dart new file mode 100644 index 000000000000..4e1c7f7ebf38 --- /dev/null +++ b/tests/language_2/issue34404_flutter_test.dart @@ -0,0 +1,34 @@ +// 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. +// SharedOptions=--supermixin + +// This test case is a reduction of some Flutter code. We wish to verify that +// the class _DismissibleState doesn't have any type inference errors. + +class _DismissibleState extends State + with TickerProviderStateMixin, AutomaticKeepAliveClientMixin {} + +abstract class State extends Diagnosticable {} + +abstract class StatefulWidget extends Widget {} + +abstract class Widget extends DiagnosticableTree {} + +abstract class DiagnosticableTree extends Diagnosticable {} + +abstract class Diagnosticable {} + +class Dismissible extends StatefulWidget {} + +abstract class TickerProviderStateMixin + extends State implements TickerProvider {} + +abstract class TickerProvider {} + +abstract class AutomaticKeepAliveClientMixin + extends State {} + +main() { + new _DismissibleState(); +} diff --git a/tests/language_2/language_2_analyzer.status b/tests/language_2/language_2_analyzer.status index 0389ef8189f8..e5c15f0ee956 100644 --- a/tests/language_2/language_2_analyzer.status +++ b/tests/language_2/language_2_analyzer.status @@ -75,6 +75,8 @@ malformed2_test: Pass, MissingCompileTimeError # Flaky: issue 31056. mixin_declaration/mixin_declaration_inference_invalid_07_test: MissingCompileTimeError mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError # Issue 30552 mixin_method_override_test/01: MissingCompileTimeError +mixin_mixin6_test: CompileTimeError # TODO(paulberry): triage +mixin_mixin7_test: CompileTimeError # TODO(paulberry): triage mixin_super_2_test/01: MissingCompileTimeError mixin_super_2_test/03: MissingCompileTimeError mixin_supertype_subclass2_test/02: MissingStaticWarning # Issue 25614 diff --git a/tests/language_2/language_2_dart2js.status b/tests/language_2/language_2_dart2js.status index 03943daa0b1d..708b7608c003 100644 --- a/tests/language_2/language_2_dart2js.status +++ b/tests/language_2/language_2_dart2js.status @@ -45,6 +45,7 @@ int64_literal_test/40: CompileTimeError, OK # Error if web int literal cannot be int64_literal_test/none: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351 issue23244_test: RuntimeError # Isolates - enum canonicalization - Issue 23244 issue32353_test: CompileTimeError +issue34404_flutter_test: CompileTimeError # --supermixin not supported library_env_test/has_mirror_support: RuntimeError, OK library_env_test/has_no_html_support: RuntimeError, OK list_test: CompileTimeError, OK # Error if web int literal cannot be represented exactly, see http://dartbug.com/33351 diff --git a/tests/language_2/language_2_dartdevc.status b/tests/language_2/language_2_dartdevc.status index 2f65cbb43532..77949d7123f2 100644 --- a/tests/language_2/language_2_dartdevc.status +++ b/tests/language_2/language_2_dartdevc.status @@ -101,6 +101,8 @@ issue31596_super_test/none: CompileTimeError issue31596_tearoff_test: CompileTimeError issue31596_test: CompileTimeError issue32353_test: RuntimeError +issue34404_flutter_modified_test: CompileTimeError # DDC doesn't support mixin inference +issue34404_flutter_test: CompileTimeError # DDC doesn't support mixin inference issue34498_test: MissingCompileTimeError # Issue 34500 label_test: RuntimeError left_shift_test: RuntimeError # Ints and doubles are unified. @@ -143,6 +145,8 @@ mixin_declaration/mixin_declaration_inference_valid_C12_test: CompileTimeError # mixin_declaration/mixin_declaration_inference_valid_C13_test: CompileTimeError # Issue #34164 mixin_declaration/mixin_declaration_invalid_superinvocation_test/10: CompileTimeError # Analyzer chooses wrong(?) super method. mixin_method_override_test/01: MissingCompileTimeError # Issue 34235 +mixin_mixin6_test: CompileTimeError # TODO(paulberry): triage +mixin_mixin7_test: CompileTimeError # TODO(paulberry): triage mixin_super_2_test/01: MissingCompileTimeError mixin_super_2_test/03: MissingCompileTimeError mixin_super_test: RuntimeError