Chromium Code Reviews| Index: pkg/analyzer/lib/src/task/dart.dart |
| diff --git a/pkg/analyzer/lib/src/task/dart.dart b/pkg/analyzer/lib/src/task/dart.dart |
| index 9e5bbf63f7525c73ee27f11a298e21ea480ff2b5..dd907f6bcd225261ad14272551bf1a36b3f5160d 100644 |
| --- a/pkg/analyzer/lib/src/task/dart.dart |
| +++ b/pkg/analyzer/lib/src/task/dart.dart |
| @@ -5,6 +5,7 @@ |
| library analyzer.src.task.dart; |
| import 'dart:collection'; |
| +import 'dart:math' as math; |
| import 'package:analyzer/src/generated/ast.dart'; |
| import 'package:analyzer/src/generated/element.dart'; |
| @@ -67,6 +68,23 @@ final ResultDescriptor<List<ClassElement>> CLASS_ELEMENTS = |
| new ResultDescriptor<List<ClassElement>>('CLASS_ELEMENTS', null); |
| /** |
| + * The [ConstructorElement]s of a [ClassElement]. |
| + */ |
| +final ResultDescriptor<List<ConstructorElement>> CONSTRUCTORS = |
| + new ResultDescriptor<List<ConstructorElement>>('CONSTRUCTORS', null); |
| + |
| +/** |
| + * The errors produced while building a [ClassElement] constructors. |
| + * |
| + * The list will be empty if there were no errors, but will not be `null`. |
| + * |
| + * The result is only available for targets representing a [ClassElement]. |
| + */ |
| +final ResultDescriptor<List<AnalysisError>> CONSTRUCTORS_ERRORS = |
| + new ResultDescriptor<List<AnalysisError>>( |
| + 'CONSTRUCTORS_ERRORS', AnalysisError.NO_ERRORS); |
| + |
| +/** |
| * The sources representing the export closure of a library. |
| * |
| * The result is only available for targets representing a Dart library. |
| @@ -189,6 +207,223 @@ final ResultDescriptor<TypeProvider> TYPE_PROVIDER = |
| new ResultDescriptor<TypeProvider>('TYPE_PROVIDER', null); |
| /** |
| + * A task that builds implicit constructors for a [ClassElement], or keeps |
| + * the existing explicit constructors if the class has them. |
| + */ |
| +class BuildClassConstructorsTask extends SourceBasedAnalysisTask { |
| + /** |
| + * The name of the [CONSTRUCTORS] input for the superclass. |
| + */ |
| + static const String SUPER_CONSTRUCTORS = 'SUPER_CONSTRUCTORS'; |
| + |
| + /** |
| + * The task descriptor describing this kind of task. |
| + */ |
| + static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| + 'BuildConstructorsForClassTask', createTask, buildInputs, |
| + <ResultDescriptor>[CONSTRUCTORS, CONSTRUCTORS_ERRORS]); |
| + |
| + BuildClassConstructorsTask( |
| + InternalAnalysisContext context, AnalysisTarget target) |
| + : super(context, target); |
| + |
| + @override |
| + TaskDescriptor get descriptor => DESCRIPTOR; |
| + |
| + @override |
| + void internalPerform() { |
| + List<AnalysisError> errors = <AnalysisError>[]; |
| + // |
| + // Prepare inputs. |
| + // |
| + ClassElementImpl classElement = this.target; |
| + List<ConstructorElement> superConstructors = inputs[SUPER_CONSTRUCTORS]; |
| + DartType superType = classElement.supertype; |
| + ClassElement superElement = superType.element; |
| + // |
| + // Shortcut for ClassElement(s) without implicit constructors. |
| + // |
| + if (superConstructors == null) { |
| + outputs[CONSTRUCTORS] = classElement.constructors; |
| + outputs[CONSTRUCTORS_ERRORS] = AnalysisError.NO_ERRORS; |
| + return; |
| + } |
| + // |
| + // ClassTypeAlias |
| + // |
| + if (classElement.isTypedef) { |
| + List<ConstructorElement> implicitConstructors = |
| + new List<ConstructorElement>(); |
| + void callback(ConstructorElement explicitConstructor, |
| + List<DartType> parameterTypes, List<DartType> argumentTypes) { |
| + implicitConstructors.add(_createImplicitContructor(classElement.type, |
| + explicitConstructor, parameterTypes, argumentTypes)); |
| + } |
| + if (_findForwardedConstructors(classElement, superType, callback)) { |
| + if (implicitConstructors.isEmpty) { |
| + errors.add(new AnalysisError.con2(classElement.source, |
| + classElement.nameOffset, classElement.name.length, |
| + CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS, |
| + [superElement.name])); |
| + } else { |
| + classElement.constructors = implicitConstructors; |
| + } |
| + } |
| + outputs[CONSTRUCTORS] = classElement.constructors; |
| + outputs[CONSTRUCTORS_ERRORS] = errors; |
| + } |
| + // |
| + // ClassDeclaration |
| + // |
| + if (!classElement.isTypedef) { |
| + bool constructorFound = false; |
| + void callback(ConstructorElement explicitConstructor, |
| + List<DartType> parameterTypes, List<DartType> argumentTypes) { |
| + constructorFound = true; |
| + } |
| + if (_findForwardedConstructors(classElement, superType, callback) && |
| + !constructorFound) { |
| + SourceRange withRange = classElement.withClauseRange; |
| + errors.add(new AnalysisError.con2(classElement.source, withRange.offset, |
| + withRange.length, CompileTimeErrorCode.MIXIN_HAS_NO_CONSTRUCTORS, |
| + [superElement.name])); |
| + classElement.mixinErrorsReported = true; |
| + } |
| + outputs[CONSTRUCTORS] = classElement.constructors; |
| + outputs[CONSTRUCTORS_ERRORS] = errors; |
| + } |
| + } |
| + |
| + /** |
| + * Return a map from the names of the inputs of this kind of task to the task |
| + * input descriptors describing those inputs for a task with the |
| + * given [classElement]. |
| + */ |
| + static Map<String, TaskInput> buildInputs(ClassElement classElement) { |
|
Paul Berry
2015/03/24 20:10:17
I think this is going to cause a problem in the lo
scheglov
2015/03/24 21:17:15
I will add TODO for now.
We need to discuss it whe
|
| + DartType superType = classElement.supertype; |
| + if (superType is InterfaceType) { |
| + if (classElement.isTypedef || classElement.mixins.isNotEmpty) { |
| + ClassElement superElement = superType.element; |
| + return <String, TaskInput>{ |
| + SUPER_CONSTRUCTORS: CONSTRUCTORS.inputFor(superElement) |
| + }; |
| + } |
| + } |
| + // No implicit constructors, no inputs required. |
| + return <String, TaskInput>{}; |
| + } |
| + |
| + /** |
| + * Create a [BuildClassConstructorsTask] based on the given |
| + * [target] in the given [context]. |
| + */ |
| + static BuildClassConstructorsTask createTask( |
| + AnalysisContext context, AnalysisTarget target) { |
| + return new BuildClassConstructorsTask(context, target); |
| + } |
| + |
| + /** |
| + * Create an implicit constructor that is copied from the given |
| + * [explicitConstructor], but that is in the given class. |
| + * |
| + * [classType] - the class in which the implicit constructor is defined. |
| + * [explicitConstructor] - the constructor on which the implicit constructor |
| + * is modeled. |
| + * [parameterTypes] - the types to be replaced when creating parameters. |
| + * [argumentTypes] - the types with which the parameters are to be replaced. |
| + */ |
| + static ConstructorElement _createImplicitContructor(InterfaceType classType, |
| + ConstructorElement explicitConstructor, List<DartType> parameterTypes, |
| + List<DartType> argumentTypes) { |
| + ConstructorElementImpl implicitConstructor = |
| + new ConstructorElementImpl(explicitConstructor.name, -1); |
| + implicitConstructor.synthetic = true; |
| + implicitConstructor.redirectedConstructor = explicitConstructor; |
| + implicitConstructor.const2 = explicitConstructor.isConst; |
| + implicitConstructor.returnType = classType; |
| + List<ParameterElement> explicitParameters = explicitConstructor.parameters; |
| + int count = explicitParameters.length; |
| + if (count > 0) { |
| + List<ParameterElement> implicitParameters = |
| + new List<ParameterElement>(count); |
| + for (int i = 0; i < count; i++) { |
| + ParameterElement explicitParameter = explicitParameters[i]; |
| + ParameterElementImpl implicitParameter = |
| + new ParameterElementImpl(explicitParameter.name, -1); |
| + implicitParameter.const3 = explicitParameter.isConst; |
| + implicitParameter.final2 = explicitParameter.isFinal; |
| + implicitParameter.parameterKind = explicitParameter.parameterKind; |
| + implicitParameter.synthetic = true; |
| + implicitParameter.type = |
| + explicitParameter.type.substitute2(argumentTypes, parameterTypes); |
| + implicitParameters[i] = implicitParameter; |
| + } |
| + implicitConstructor.parameters = implicitParameters; |
| + } |
| + FunctionTypeImpl type = new FunctionTypeImpl.con1(implicitConstructor); |
| + type.typeArguments = classType.typeArguments; |
| + implicitConstructor.type = type; |
| + return implicitConstructor; |
| + } |
| + |
| + /** |
| + * Find all the constructors that should be forwarded from the given |
| + * [superType], to the class or mixin application [classElement], |
| + * and pass information about them to [callback]. |
| + * |
| + * Return true if some constructors were considered. (A false return value |
| + * can only happen if the supeclass is a built-in type, in which case it |
| + * can't be used as a mixin anyway). |
| + */ |
| + static bool _findForwardedConstructors(ClassElementImpl classElement, |
| + InterfaceType superType, void callback( |
| + ConstructorElement explicitConstructor, List<DartType> parameterTypes, |
| + List<DartType> argumentTypes)) { |
| + ClassElement superclassElement = superType.element; |
| + List<ConstructorElement> constructors = superclassElement.constructors; |
| + int count = constructors.length; |
| + if (count == 0) { |
| + return false; |
| + } |
| + List<DartType> parameterTypes = |
| + TypeParameterTypeImpl.getTypes(superType.typeParameters); |
| + List<DartType> argumentTypes = _getArgumentTypes(superType, parameterTypes); |
| + for (int i = 0; i < count; i++) { |
| + ConstructorElement explicitConstructor = constructors[i]; |
| + if (!explicitConstructor.isFactory && |
| + classElement.isSuperConstructorAccessible(explicitConstructor)) { |
| + callback(explicitConstructor, parameterTypes, argumentTypes); |
| + } |
| + } |
| + return true; |
| + } |
| + |
| + /** |
| + * Return a list of argument types that corresponds to the [parameterTypes] |
| + * and that are derived from the type arguments of the given [superType]. |
| + */ |
| + static List<DartType> _getArgumentTypes( |
| + InterfaceType superType, List<DartType> parameterTypes) { |
| + DynamicTypeImpl dynamic = DynamicTypeImpl.instance; |
| + int parameterCount = parameterTypes.length; |
| + List<DartType> types = new List<DartType>(parameterCount); |
| + if (superType == null) { |
| + types = new List<DartType>.filled(parameterCount, dynamic); |
| + } else { |
| + List<DartType> typeArguments = superType.typeArguments; |
| + int argumentCount = math.min(typeArguments.length, parameterCount); |
| + for (int i = 0; i < argumentCount; i++) { |
| + types[i] = typeArguments[i]; |
| + } |
| + for (int i = argumentCount; i < parameterCount; i++) { |
| + types[i] = dynamic; |
| + } |
| + } |
| + return types; |
| + } |
| +} |
| + |
| +/** |
| * A task that builds a compilation unit element for a single compilation unit. |
| */ |
| class BuildCompilationUnitElementTask extends SourceBasedAnalysisTask { |