OLD | NEW |
1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2016, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 library kernel.checks; | 4 library kernel.checks; |
5 | 5 |
6 import 'ast.dart'; | 6 import 'ast.dart'; |
7 import 'transformations/flags.dart'; | 7 import 'transformations/flags.dart'; |
8 | 8 |
9 void verifyProgram(Program program) { | 9 void verifyProgram(Program program) { |
10 VerifyingVisitor.check(program); | 10 VerifyingVisitor.check(program); |
(...skipping 20 matching lines...) Expand all Loading... |
31 return "$file:${location.line}:${location.column}: Verification error:" | 31 return "$file:${location.line}:${location.column}: Verification error:" |
32 " $details"; | 32 " $details"; |
33 } else { | 33 } else { |
34 return "Verification error: $details\n" | 34 return "Verification error: $details\n" |
35 "Context: '$context'.\n" | 35 "Context: '$context'.\n" |
36 "Node: '$node'."; | 36 "Node: '$node'."; |
37 } | 37 } |
38 } | 38 } |
39 } | 39 } |
40 | 40 |
| 41 enum TypedefState { Done, BeingChecked } |
| 42 |
41 /// Checks that a kernel program is well-formed. | 43 /// Checks that a kernel program is well-formed. |
42 /// | 44 /// |
43 /// This does not include any kind of type checking. | 45 /// This does not include any kind of type checking. |
44 class VerifyingVisitor extends RecursiveVisitor { | 46 class VerifyingVisitor extends RecursiveVisitor { |
45 final Set<Class> classes = new Set<Class>(); | 47 final Set<Class> classes = new Set<Class>(); |
46 final Set<TypeParameter> typeParameters = new Set<TypeParameter>(); | 48 final Set<Typedef> typedefs = new Set<Typedef>(); |
| 49 Set<TypeParameter> typeParametersInScope = new Set<TypeParameter>(); |
47 final List<VariableDeclaration> variableStack = <VariableDeclaration>[]; | 50 final List<VariableDeclaration> variableStack = <VariableDeclaration>[]; |
| 51 final Map<Typedef, TypedefState> typedefState = <Typedef, TypedefState>{}; |
48 bool classTypeParametersAreInScope = false; | 52 bool classTypeParametersAreInScope = false; |
49 | 53 |
50 /// If true, relax certain checks for *outline* mode. For example, don't | 54 /// If true, relax certain checks for *outline* mode. For example, don't |
51 /// attempt to validate constructor initializers. | 55 /// attempt to validate constructor initializers. |
52 bool isOutline = false; | 56 bool isOutline = false; |
53 | 57 |
54 bool inCatchBlock = false; | 58 bool inCatchBlock = false; |
55 | 59 |
56 Member currentMember; | 60 Member currentMember; |
57 Class currentClass; | 61 Class currentClass; |
58 TreeNode currentParent; | 62 TreeNode currentParent; |
59 | 63 |
60 TreeNode get context => currentMember ?? currentClass; | 64 TreeNode get context => currentMember ?? currentClass; |
61 | 65 |
62 static void check(Program program) { | 66 static void check(Program program) { |
63 program.accept(new VerifyingVisitor()); | 67 program.accept(new VerifyingVisitor()); |
64 } | 68 } |
65 | 69 |
66 defaultTreeNode(TreeNode node) { | 70 defaultTreeNode(TreeNode node) { |
67 visitChildren(node); | 71 visitChildren(node); |
68 } | 72 } |
69 | 73 |
70 problem(TreeNode node, String details) { | 74 problem(TreeNode node, String details, {TreeNode context}) { |
| 75 context ??= this.context; |
71 throw new VerificationError(context, node, details); | 76 throw new VerificationError(context, node, details); |
72 } | 77 } |
73 | 78 |
74 TreeNode enterParent(TreeNode node) { | 79 TreeNode enterParent(TreeNode node) { |
75 if (!identical(node.parent, currentParent)) { | 80 if (!identical(node.parent, currentParent)) { |
76 problem( | 81 problem( |
77 node, | 82 node, |
78 "Incorrect parent pointer: expected '${node.parent.runtimeType}'," | 83 "Incorrect parent pointer on ${node.runtimeType}:" |
| 84 " expected '${node.parent.runtimeType}'," |
79 " but found: '${currentParent.runtimeType}'."); | 85 " but found: '${currentParent.runtimeType}'."); |
80 } | 86 } |
81 var oldParent = currentParent; | 87 var oldParent = currentParent; |
82 currentParent = node; | 88 currentParent = node; |
83 return oldParent; | 89 return oldParent; |
84 } | 90 } |
85 | 91 |
86 void exitParent(TreeNode oldParent) { | 92 void exitParent(TreeNode oldParent) { |
87 currentParent = oldParent; | 93 currentParent = oldParent; |
88 } | 94 } |
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 variableStack.add(variable); | 134 variableStack.add(variable); |
129 } | 135 } |
130 | 136 |
131 void undeclareVariable(VariableDeclaration variable) { | 137 void undeclareVariable(VariableDeclaration variable) { |
132 variable.flags &= ~VariableDeclaration.FlagInScope; | 138 variable.flags &= ~VariableDeclaration.FlagInScope; |
133 } | 139 } |
134 | 140 |
135 void declareTypeParameters(List<TypeParameter> parameters) { | 141 void declareTypeParameters(List<TypeParameter> parameters) { |
136 for (int i = 0; i < parameters.length; ++i) { | 142 for (int i = 0; i < parameters.length; ++i) { |
137 var parameter = parameters[i]; | 143 var parameter = parameters[i]; |
138 if (!typeParameters.add(parameter)) { | 144 if (!typeParametersInScope.add(parameter)) { |
139 problem(parameter, "Type parameter '$parameter' redeclared."); | 145 problem(parameter, "Type parameter '$parameter' redeclared."); |
140 } | 146 } |
141 } | 147 } |
142 } | 148 } |
143 | 149 |
144 void undeclareTypeParameters(List<TypeParameter> parameters) { | 150 void undeclareTypeParameters(List<TypeParameter> parameters) { |
145 typeParameters.removeAll(parameters); | 151 typeParametersInScope.removeAll(parameters); |
146 } | 152 } |
147 | 153 |
148 void checkVariableInScope(VariableDeclaration variable, TreeNode where) { | 154 void checkVariableInScope(VariableDeclaration variable, TreeNode where) { |
149 if (variable.flags & VariableDeclaration.FlagInScope == 0) { | 155 if (variable.flags & VariableDeclaration.FlagInScope == 0) { |
150 problem(where, "Variable '$variable' used out of scope."); | 156 problem(where, "Variable '$variable' used out of scope."); |
151 } | 157 } |
152 } | 158 } |
153 | 159 |
154 visitProgram(Program program) { | 160 visitProgram(Program program) { |
155 try { | 161 try { |
156 for (var library in program.libraries) { | 162 for (var library in program.libraries) { |
157 for (var class_ in library.classes) { | 163 for (var class_ in library.classes) { |
158 if (!classes.add(class_)) { | 164 if (!classes.add(class_)) { |
159 problem(class_, "Class '$class_' declared more than once."); | 165 problem(class_, "Class '$class_' declared more than once."); |
160 } | 166 } |
161 } | 167 } |
| 168 for (var typedef_ in library.typedefs) { |
| 169 if (!typedefs.add(typedef_)) { |
| 170 problem(typedef_, "Typedef '$typedef_' declared more than once."); |
| 171 } |
| 172 } |
162 library.members.forEach(declareMember); | 173 library.members.forEach(declareMember); |
163 for (var class_ in library.classes) { | 174 for (var class_ in library.classes) { |
164 class_.members.forEach(declareMember); | 175 class_.members.forEach(declareMember); |
165 } | 176 } |
166 } | 177 } |
167 visitChildren(program); | 178 visitChildren(program); |
168 } finally { | 179 } finally { |
169 for (var library in program.libraries) { | 180 for (var library in program.libraries) { |
170 library.members.forEach(undeclareMember); | 181 library.members.forEach(undeclareMember); |
171 for (var class_ in library.classes) { | 182 for (var class_ in library.classes) { |
172 class_.members.forEach(undeclareMember); | 183 class_.members.forEach(undeclareMember); |
173 } | 184 } |
174 } | 185 } |
175 variableStack.forEach(undeclareVariable); | 186 variableStack.forEach(undeclareVariable); |
176 } | 187 } |
177 } | 188 } |
178 | 189 |
| 190 void checkTypedef(Typedef node) { |
| 191 var state = typedefState[node]; |
| 192 if (state == TypedefState.Done) return; |
| 193 if (state == TypedefState.BeingChecked) { |
| 194 problem(node, "The typedef '$node' refers to itself", context: node); |
| 195 } |
| 196 assert(state == null); |
| 197 typedefState[node] = TypedefState.BeingChecked; |
| 198 var savedTypeParameters = typeParametersInScope; |
| 199 typeParametersInScope = node.typeParameters.toSet(); |
| 200 var savedParent = currentParent; |
| 201 currentParent = node; |
| 202 // Visit children without checking the parent pointer on the typedef itself |
| 203 // since this can be called from a context other than its true parent. |
| 204 node.visitChildren(this); |
| 205 currentParent = savedParent; |
| 206 typeParametersInScope = savedTypeParameters; |
| 207 typedefState[node] = TypedefState.Done; |
| 208 } |
| 209 |
| 210 visitTypedef(Typedef node) { |
| 211 checkTypedef(node); |
| 212 // Enter and exit the node to check the parent pointer on the typedef node. |
| 213 exitParent(enterParent(node)); |
| 214 } |
| 215 |
179 visitField(Field node) { | 216 visitField(Field node) { |
180 currentMember = node; | 217 currentMember = node; |
181 var oldParent = enterParent(node); | 218 var oldParent = enterParent(node); |
182 classTypeParametersAreInScope = !node.isStatic; | 219 classTypeParametersAreInScope = !node.isStatic; |
183 node.initializer?.accept(this); | 220 node.initializer?.accept(this); |
184 classTypeParametersAreInScope = false; | 221 classTypeParametersAreInScope = false; |
185 visitList(node.annotations, this); | 222 visitList(node.annotations, this); |
186 exitParent(oldParent); | 223 exitParent(oldParent); |
| 224 node.type.accept(this); |
187 currentMember = null; | 225 currentMember = null; |
188 } | 226 } |
189 | 227 |
190 visitProcedure(Procedure node) { | 228 visitProcedure(Procedure node) { |
191 currentMember = node; | 229 currentMember = node; |
192 var oldParent = enterParent(node); | 230 var oldParent = enterParent(node); |
193 classTypeParametersAreInScope = !node.isStatic; | 231 classTypeParametersAreInScope = !node.isStatic; |
194 node.function.accept(this); | 232 node.function.accept(this); |
195 classTypeParametersAreInScope = false; | 233 classTypeParametersAreInScope = false; |
196 visitList(node.annotations, this); | 234 visitList(node.annotations, this); |
(...skipping 288 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
485 | 523 |
486 @override | 524 @override |
487 visitClassReference(Class node) { | 525 visitClassReference(Class node) { |
488 if (!classes.contains(node)) { | 526 if (!classes.contains(node)) { |
489 problem( | 527 problem( |
490 node, "Dangling reference to '$node', parent is: '${node.parent}'."); | 528 node, "Dangling reference to '$node', parent is: '${node.parent}'."); |
491 } | 529 } |
492 } | 530 } |
493 | 531 |
494 @override | 532 @override |
| 533 visitTypedefReference(Typedef node) { |
| 534 if (!typedefs.contains(node)) { |
| 535 problem( |
| 536 node, "Dangling reference to '$node', parent is: '${node.parent}'"); |
| 537 } |
| 538 } |
| 539 |
| 540 @override |
495 visitTypeParameterType(TypeParameterType node) { | 541 visitTypeParameterType(TypeParameterType node) { |
496 var parameter = node.parameter; | 542 var parameter = node.parameter; |
497 if (!typeParameters.contains(parameter)) { | 543 if (!typeParametersInScope.contains(parameter)) { |
498 problem( | 544 problem( |
499 currentParent, | 545 currentParent, |
500 "Type parameter '$parameter' referenced out of" | 546 "Type parameter '$parameter' referenced out of" |
501 " scope, parent is: '${parameter.parent}'."); | 547 " scope, parent is: '${parameter.parent}'."); |
502 } | 548 } |
503 if (parameter.parent is Class && !classTypeParametersAreInScope) { | 549 if (parameter.parent is Class && !classTypeParametersAreInScope) { |
504 problem( | 550 problem( |
505 currentParent, | 551 currentParent, |
506 "Type parameter '$parameter' referenced from" | 552 "Type parameter '$parameter' referenced from" |
507 " static context, parent is '${parameter.parent}'."); | 553 " static context, parent is '${parameter.parent}'."); |
508 } | 554 } |
509 } | 555 } |
510 | 556 |
511 @override | 557 @override |
512 visitInterfaceType(InterfaceType node) { | 558 visitInterfaceType(InterfaceType node) { |
513 node.visitChildren(this); | 559 node.visitChildren(this); |
514 if (node.typeArguments.length != node.classNode.typeParameters.length) { | 560 if (node.typeArguments.length != node.classNode.typeParameters.length) { |
515 problem( | 561 problem( |
516 currentParent, | 562 currentParent, |
517 "Type $node provides ${node.typeArguments.length}" | 563 "Type $node provides ${node.typeArguments.length}" |
518 " type arguments but the class declares" | 564 " type arguments but the class declares" |
519 " ${node.classNode.typeParameters.length} parameters."); | 565 " ${node.classNode.typeParameters.length} parameters."); |
520 } | 566 } |
521 } | 567 } |
| 568 |
| 569 @override |
| 570 visitTypedefType(TypedefType node) { |
| 571 checkTypedef(node.typedefNode); |
| 572 node.visitChildren(this); |
| 573 if (node.typeArguments.length != node.typedefNode.typeParameters.length) { |
| 574 problem( |
| 575 currentParent, |
| 576 "The typedef type $node provides ${node.typeArguments.length}" |
| 577 " type arguments but the typedef declares" |
| 578 " ${node.typedefNode.typeParameters.length} parameters."); |
| 579 } |
| 580 } |
522 } | 581 } |
523 | 582 |
524 class CheckParentPointers extends Visitor { | 583 class CheckParentPointers extends Visitor { |
525 static void check(TreeNode node) { | 584 static void check(TreeNode node) { |
526 node.accept(new CheckParentPointers(node.parent)); | 585 node.accept(new CheckParentPointers(node.parent)); |
527 } | 586 } |
528 | 587 |
529 TreeNode parent; | 588 TreeNode parent; |
530 | 589 |
531 CheckParentPointers([this.parent]); | 590 CheckParentPointers([this.parent]); |
(...skipping 10 matching lines...) Expand all Loading... |
542 var oldParent = parent; | 601 var oldParent = parent; |
543 parent = node; | 602 parent = node; |
544 node.visitChildren(this); | 603 node.visitChildren(this); |
545 parent = oldParent; | 604 parent = oldParent; |
546 } | 605 } |
547 } | 606 } |
548 | 607 |
549 void checkInitializers(Constructor constructor) { | 608 void checkInitializers(Constructor constructor) { |
550 // TODO(ahe): I'll add more here in other CLs. | 609 // TODO(ahe): I'll add more here in other CLs. |
551 } | 610 } |
OLD | NEW |