Index: pkg/analyzer/lib/src/generated/constant.dart |
diff --git a/pkg/analyzer/lib/src/generated/constant.dart b/pkg/analyzer/lib/src/generated/constant.dart |
index 7c866c1014e63dafc44514fa484a7dbc7e95d3ea..6aafe29f04a2986e1380ede8cf5b77cc0326f051 100644 |
--- a/pkg/analyzer/lib/src/generated/constant.dart |
+++ b/pkg/analyzer/lib/src/generated/constant.dart |
@@ -303,6 +303,11 @@ class ConstantFinder extends RecursiveAstVisitor<Object> { |
*/ |
final List<Annotation> annotations = <Annotation>[]; |
+ /** |
+ * True if instance variables marked as "final" should be treated as "const". |
+ */ |
+ bool treatFinalInstanceVarAsConst = false; |
+ |
@override |
Object visitAnnotation(Annotation node) { |
super.visitAnnotation(node); |
@@ -311,6 +316,22 @@ class ConstantFinder extends RecursiveAstVisitor<Object> { |
} |
@override |
+ Object visitClassDeclaration(ClassDeclaration node) { |
+ bool prevTreatFinalInstanceVarAsConst = treatFinalInstanceVarAsConst; |
+ if (node.element.constructors.any((ConstructorElement e) => e.isConst)) { |
+ // Instance vars marked "final" need to be included in the dependency |
+ // graph, since constant constructors implicitly use the values in their |
+ // initializers. |
+ treatFinalInstanceVarAsConst = true; |
+ } |
+ try { |
+ return super.visitClassDeclaration(node); |
+ } finally { |
+ treatFinalInstanceVarAsConst = prevTreatFinalInstanceVarAsConst; |
+ } |
+ } |
+ |
+ @override |
Object visitConstructorDeclaration(ConstructorDeclaration node) { |
super.visitConstructorDeclaration(node); |
if (node.constKeyword != null) { |
@@ -335,7 +356,13 @@ class ConstantFinder extends RecursiveAstVisitor<Object> { |
Object visitVariableDeclaration(VariableDeclaration node) { |
super.visitVariableDeclaration(node); |
Expression initializer = node.initializer; |
- if (initializer != null && node.isConst) { |
+ VariableElement element = node.element; |
+ if (initializer != null && |
+ (node.isConst || |
+ treatFinalInstanceVarAsConst && |
+ element is FieldElement && |
+ node.isFinal && |
+ !element.isStatic)) { |
if (node.element != null) { |
variableMap[node.element as PotentiallyConstVariableElement] = node; |
} |
@@ -454,6 +481,13 @@ class ConstantValueComputer { |
void beforeComputeValue(AstNode constNode) {} |
/** |
+ * This method is called just before getting the constant value of a field |
+ * with an initializer. Unit tests will override this method to introduce |
+ * additional error checking. |
+ */ |
+ void beforeGetFieldEvaluationResult(FieldElementImpl field) {} |
+ |
+ /** |
* This method is called just before getting the constant initializers |
* associated with the [constructor]. Unit tests will override this method to |
* introduce additional error checking. |
@@ -520,6 +554,16 @@ class ConstantValueComputer { |
} |
} |
} |
+ for (FieldElement field in element.enclosingElement.fields) { |
+ // Note: non-static const isn't allowed but we handle it anyway so that |
+ // we won't be confused by incorrect code. |
+ if ((field.isFinal || field.isConst) && !field.isStatic) { |
+ VariableDeclaration fieldDeclaration = _variableDeclarationMap[field]; |
+ if (fieldDeclaration != null) { |
+ referenceGraph.addEdge(declaration, fieldDeclaration); |
+ } |
+ } |
+ } |
for (FormalParameter parameter in declaration.parameters.parameters) { |
referenceGraph.addNode(parameter); |
referenceGraph.addEdge(declaration, parameter); |
@@ -581,6 +625,10 @@ class ConstantValueComputer { |
ConstructorElement constructor) => |
constructorDeclarationMap[_getConstructorBase(constructor)]; |
+ VariableDeclaration findVariableDeclaration( |
+ PotentiallyConstVariableElement variable) => |
+ _variableDeclarationMap[variable]; |
+ |
/** |
* Check that the arguments to a call to fromEnvironment() are correct. The |
* [arguments] are the AST nodes of the arguments. The [argumentValues] are |
@@ -898,6 +946,25 @@ class ConstantValueComputer { |
} |
HashMap<String, DartObjectImpl> fieldMap = |
new HashMap<String, DartObjectImpl>(); |
+ // Start with final fields that are initialized at their declaration site. |
+ for (FieldElement field in constructor.enclosingElement.fields) { |
+ if ((field.isFinal || field.isConst) && |
+ !field.isStatic && |
+ field is ConstFieldElementImpl) { |
+ beforeGetFieldEvaluationResult(field); |
+ EvaluationResultImpl evaluationResult = field.evaluationResult; |
+ DartType fieldType = |
+ FieldMember.from(field, constructor.returnType).type; |
+ DartObjectImpl fieldValue = evaluationResult.value; |
+ if (fieldValue != null && !_runtimeTypeMatch(fieldValue, fieldType)) { |
+ errorReporter.reportErrorForNode( |
+ CheckedModeCompileTimeErrorCode.CONST_CONSTRUCTOR_FIELD_TYPE_MISMATCH, |
+ node, [fieldValue.type, field.name, fieldType]); |
+ } |
+ fieldMap[field.name] = evaluationResult.value; |
+ } |
+ } |
+ // Now evaluate the constructor declaration. |
HashMap<String, DartObjectImpl> parameterMap = |
new HashMap<String, DartObjectImpl>(); |
List<ParameterElement> parameters = constructor.parameters; |