Chromium Code Reviews| Index: pkg/analyzer/lib/src/dart/constant/evaluation.dart |
| diff --git a/pkg/analyzer/lib/src/dart/constant/evaluation.dart b/pkg/analyzer/lib/src/dart/constant/evaluation.dart |
| index 57eee8973823954d54ef94305c1c0136612c826c..45c3bda38126a82d0a935f0e197f9269f2ec77ea 100644 |
| --- a/pkg/analyzer/lib/src/dart/constant/evaluation.dart |
| +++ b/pkg/analyzer/lib/src/dart/constant/evaluation.dart |
| @@ -75,6 +75,9 @@ class ConstantEvaluationEngine { |
| */ |
| final ConstantEvaluationValidator validator; |
| + /** Whether we are running in strong mode. */ |
| + final bool strongMode; |
| + |
| /** |
| * Initialize a newly created [ConstantEvaluationEngine]. The [typeProvider] |
| * is used to access known types. [_declaredVariables] is the set of |
| @@ -82,9 +85,12 @@ class ConstantEvaluationEngine { |
| * given, is used to verify correct dependency analysis when running unit |
| * tests. |
| */ |
| - ConstantEvaluationEngine(this.typeProvider, this._declaredVariables, |
| + ConstantEvaluationEngine(TypeProvider typeProvider, this._declaredVariables, |
| {ConstantEvaluationValidator validator, TypeSystem typeSystem}) |
| - : validator = |
| + : typeProvider = typeProvider, |
| + strongMode = |
| + typeProvider.objectType.element.context.analysisOptions.strongMode, |
| + validator = |
| validator ?? new ConstantEvaluationValidator_ForProduction(), |
| typeSystem = typeSystem ?? new TypeSystemImpl(); |
| @@ -409,7 +415,7 @@ class ConstantEvaluationEngine { |
| DartObjectImpl evaluateConstructorCall( |
| AstNode node, |
| - NodeList<Expression> arguments, |
| + List<Expression> arguments, |
| ConstructorElement constructor, |
| ConstantVisitor constantVisitor, |
| ErrorReporter errorReporter) { |
| @@ -515,25 +521,49 @@ class ConstantEvaluationEngine { |
| // so consider it an unknown value to suppress further errors. |
| return new DartObjectImpl.validWithUnknownValue(definingClass); |
| } |
| - HashMap<String, DartObjectImpl> fieldMap = |
| - new HashMap<String, DartObjectImpl>(); |
| + |
| + // In strong mode, we allow constants to have type arguments. |
| + // |
| + // They will be added to the lexical environment when evaluating |
| + // subexpressions. |
| + HashMap<String, DartObjectImpl> typeArgumentMap; |
| + if (strongMode) { |
| + // Instantiate the constructor with the in-scope type arguments. |
| + definingClass = constantVisitor.evaluateType(definingClass); |
| + constructor = ConstructorMember.from(constructorBase, definingClass); |
| + |
| + typeArgumentMap = new HashMap<String, DartObjectImpl>.fromIterables( |
| + definingClass.typeParameters.map((t) => t.name), |
| + definingClass.typeArguments.map(constantVisitor.typeConstant)); |
| + } |
| + |
| + var fieldMap = new HashMap<String, DartObjectImpl>(); |
| + var fieldInitVisitor = new ConstantVisitor(this, errorReporter, |
| + lexicalEnvironment: typeArgumentMap); |
| // Start with final fields that are initialized at their declaration site. |
| - for (FieldElement field in constructor.enclosingElement.fields) { |
| + List<FieldElement> fields = constructor.enclosingElement.fields; |
| + for (int i = 0; i < fields.length; i++) { |
| + FieldElement field = fields[i]; |
| if ((field.isFinal || field.isConst) && |
| !field.isStatic && |
| field is ConstFieldElementImpl) { |
| validator.beforeGetFieldEvaluationResult(field); |
| - EvaluationResultImpl evaluationResult = field.evaluationResult; |
| + |
| + DartObjectImpl fieldValue; |
| + if (strongMode) { |
| + fieldValue = field.constantInitializer.accept(fieldInitVisitor); |
| + } else { |
| + fieldValue = field.evaluationResult?.value; |
| + } |
| // It is possible that the evaluation result is null. |
| // This happens for example when we have duplicate fields. |
| // class Test {final x = 1; final x = 2; const Test();} |
| - if (evaluationResult == null) { |
| + if (fieldValue == null) { |
| continue; |
| } |
| // Match the value and the type. |
| DartType fieldType = |
| FieldMember.from(field, constructor.returnType).type; |
| - DartObjectImpl fieldValue = evaluationResult.value; |
| if (fieldValue != null && !runtimeTypeMatch(fieldValue, fieldType)) { |
| errorReporter.reportErrorForNode( |
| CheckedModeCompileTimeErrorCode |
| @@ -549,6 +579,7 @@ class ConstantEvaluationEngine { |
| new HashMap<String, DartObjectImpl>(); |
| List<ParameterElement> parameters = constructor.parameters; |
| int parameterCount = parameters.length; |
| + |
| for (int i = 0; i < parameterCount; i++) { |
| ParameterElement parameter = parameters[i]; |
| ParameterElement baseParameter = parameter; |
| @@ -574,12 +605,23 @@ class ConstantEvaluationEngine { |
| // The parameter is an optional positional parameter for which no value |
| // was provided, so use the default value. |
| validator.beforeGetParameterDefault(baseParameter); |
| - EvaluationResultImpl evaluationResult = baseParameter.evaluationResult; |
| - if (evaluationResult == null) { |
| - // No default was provided, so the default value is null. |
| - argumentValue = typeProvider.nullObject; |
| - } else if (evaluationResult.value != null) { |
| - argumentValue = evaluationResult.value; |
| + if (strongMode && baseParameter is ConstVariableElement) { |
| + var defaultValue = |
| + (baseParameter as ConstVariableElement).constantInitializer; |
| + if (defaultValue == null) { |
| + argumentValue = typeProvider.nullObject; |
| + } else { |
| + argumentValue = defaultValue.accept(fieldInitVisitor); |
| + } |
| + } else { |
| + EvaluationResultImpl evaluationResult = |
| + baseParameter.evaluationResult; |
| + if (evaluationResult == null) { |
| + // No default was provided, so the default value is null. |
| + argumentValue = typeProvider.nullObject; |
| + } else if (evaluationResult.value != null) { |
| + argumentValue = evaluationResult.value; |
| + } |
| } |
| } |
| if (argumentValue != null) { |
| @@ -677,6 +719,7 @@ class ConstantEvaluationEngine { |
| if (superArguments == null) { |
| superArguments = new NodeList<Expression>(null); |
| } |
| + |
| evaluateSuperConstructorCall(node, fieldMap, superConstructor, |
| superArguments, initializerVisitor, errorReporter); |
| } |
| @@ -688,7 +731,7 @@ class ConstantEvaluationEngine { |
| AstNode node, |
| HashMap<String, DartObjectImpl> fieldMap, |
| ConstructorElement superConstructor, |
| - NodeList<Expression> superArguments, |
| + List<Expression> superArguments, |
| ConstantVisitor initializerVisitor, |
| ErrorReporter errorReporter) { |
| if (superConstructor != null && superConstructor.isConst) { |
| @@ -1231,6 +1274,7 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> { |
| // problem - the error has already been reported. |
| return null; |
| } |
| + |
| return evaluationEngine.evaluateConstructorCall( |
| node, node.argumentList.arguments, constructor, this, _errorReporter); |
| } |
| @@ -1274,9 +1318,9 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> { |
| return null; |
| } |
| DartType elementType = _typeProvider.dynamicType; |
| - if (node.typeArguments != null && |
| - node.typeArguments.arguments.length == 1) { |
| - DartType type = node.typeArguments.arguments[0].type; |
| + NodeList<TypeName> typeArgs = node.typeArguments?.arguments; |
| + if (typeArgs?.length == 1) { |
| + DartType type = visitTypeName(typeArgs[0])?.toTypeValue(); |
| if (type != null) { |
| elementType = type; |
| } |
| @@ -1309,13 +1353,13 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> { |
| } |
| DartType keyType = _typeProvider.dynamicType; |
| DartType valueType = _typeProvider.dynamicType; |
| - if (node.typeArguments != null && |
| - node.typeArguments.arguments.length == 2) { |
| - DartType keyTypeCandidate = node.typeArguments.arguments[0].type; |
| + NodeList<TypeName> typeArgs = node.typeArguments?.arguments; |
| + if (typeArgs?.length == 2) { |
| + DartType keyTypeCandidate = visitTypeName(typeArgs[0])?.toTypeValue(); |
| if (keyTypeCandidate != null) { |
| keyType = keyTypeCandidate; |
| } |
| - DartType valueTypeCandidate = node.typeArguments.arguments[1].type; |
| + DartType valueTypeCandidate = visitTypeName(typeArgs[1])?.toTypeValue(); |
| if (valueTypeCandidate != null) { |
| valueType = valueTypeCandidate; |
| } |
| @@ -1465,6 +1509,56 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> { |
| _typeProvider.symbolType, new SymbolState(buffer.toString())); |
| } |
| + @override |
| + DartObjectImpl visitTypeName(TypeName node) { |
| + DartType type = evaluateType(node.type); |
| + if (type == null) { |
| + return super.visitTypeName(node); |
| + } |
| + return typeConstant(type); |
| + } |
| + |
| + /** |
| + * Given a [type], returns the constant value that contains that type value. |
| + */ |
| + DartObjectImpl typeConstant(DartType type) { |
| + return new DartObjectImpl(_typeProvider.typeType, new TypeState(type)); |
| + } |
| + |
| + /** |
| + * Given a [type] that may contain free type variables, evaluate them against |
| + * the current lexical environment and return the substituted type. |
| + */ |
| + DartType evaluateType(DartType type) { |
| + if (type is TypeParameterType) { |
| + if (!evaluationEngine.strongMode) { |
| + return null; |
|
Paul Berry
2016/07/29 12:22:46
Nit: it would be nice to have a comment explaining
Jennifer Messerly
2016/07/29 16:04:50
Done.
|
| + } |
| + |
| + String name = type.name; |
| + if (_lexicalEnvironment != null) { |
| + return _lexicalEnvironment[name]?.toTypeValue() ?? type; |
| + } |
| + return type; |
| + } |
| + if (type is ParameterizedType) { |
| + List<DartType> typeArguments; |
| + for (int i = 0; i < type.typeArguments.length; i++) { |
| + DartType ta = type.typeArguments[i]; |
| + DartType t = evaluateType(ta); |
| + if (!identical(t, ta)) { |
| + if (typeArguments == null) { |
| + typeArguments = type.typeArguments.toList(growable: false); |
| + } |
| + typeArguments[i] = t; |
| + } |
| + } |
| + if (typeArguments == null) return type; |
| + return type.substitute2(typeArguments, type.typeArguments); |
| + } |
| + return type; |
| + } |
| + |
| /** |
| * Create an error associated with the given [node]. The error will have the |
| * given error [code]. |
| @@ -1497,10 +1591,12 @@ class ConstantVisitor extends UnifyingAstVisitor<DartObjectImpl> { |
| } |
| return new DartObjectImpl(functionType, new FunctionState(function)); |
| } |
| - } else if (variableElement is ClassElement || |
| - variableElement is FunctionTypeAliasElement || |
| - variableElement is DynamicElementImpl) { |
| - return new DartObjectImpl(_typeProvider.typeType, new TypeState(element)); |
| + } else if (variableElement is TypeDefiningElement) { |
| + if (evaluationEngine.strongMode || |
| + variableElement is! TypeParameterElement) { |
| + return new DartObjectImpl( |
| + _typeProvider.typeType, new TypeState(variableElement.type)); |
| + } |
| } |
| // TODO(brianwilkerson) Figure out which error to report. |
| _error(node, null); |