OLD | NEW |
| (Empty) |
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 | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 library kernel.checks; | |
5 | |
6 import 'ast.dart'; | |
7 import 'transformations/flags.dart'; | |
8 | |
9 void runSanityChecks(Program program) { | |
10 SanityCheck.check(program); | |
11 } | |
12 | |
13 /// Checks that references refer to something in scope. | |
14 /// | |
15 /// Currently only checks member, class, and type parameter references. | |
16 class SanityCheck extends RecursiveVisitor { | |
17 final Set<Class> classes = new Set<Class>(); | |
18 final Set<TypeParameter> typeParameters = new Set<TypeParameter>(); | |
19 | |
20 Member currentMember; | |
21 Class currentClass; | |
22 TreeNode currentParent; | |
23 | |
24 TreeNode get context => currentMember ?? currentClass; | |
25 | |
26 static void check(Program program) { | |
27 program.accept(new SanityCheck()); | |
28 } | |
29 | |
30 defaultTreeNode(TreeNode node) { | |
31 visitChildren(node); | |
32 } | |
33 | |
34 void visitChildren(TreeNode node) { | |
35 if (!identical(node.parent, currentParent)) { | |
36 throw 'Parent pointer on ${node.runtimeType} ' | |
37 'is ${node.parent.runtimeType} ' | |
38 'but should be ${currentParent.runtimeType}'; | |
39 } | |
40 var oldParent = currentParent; | |
41 currentParent = node; | |
42 node.visitChildren(this); | |
43 currentParent = oldParent; | |
44 } | |
45 | |
46 void declareMember(Member member) { | |
47 if (member.transformerFlags & TransformerFlag.seenBySanityCheck != 0) { | |
48 throw '$member has been declared more than once'; | |
49 } | |
50 member.transformerFlags |= TransformerFlag.seenBySanityCheck; | |
51 } | |
52 | |
53 void undeclareMember(Member member) { | |
54 member.transformerFlags &= ~TransformerFlag.seenBySanityCheck; | |
55 } | |
56 | |
57 visitProgram(Program program) { | |
58 for (var library in program.libraries) { | |
59 classes.addAll(library.classes); | |
60 library.members.forEach(declareMember); | |
61 for (var class_ in library.classes) { | |
62 class_.members.forEach(declareMember); | |
63 } | |
64 } | |
65 visitChildren(program); | |
66 for (var library in program.libraries) { | |
67 library.members.forEach(undeclareMember); | |
68 for (var class_ in library.classes) { | |
69 class_.members.forEach(undeclareMember); | |
70 } | |
71 } | |
72 } | |
73 | |
74 defaultMember(Member node) { | |
75 currentMember = node; | |
76 visitChildren(node); | |
77 currentMember = null; | |
78 } | |
79 | |
80 visitClass(Class node) { | |
81 currentClass = node; | |
82 typeParameters.addAll(node.typeParameters); | |
83 visitChildren(node); | |
84 typeParameters.removeAll(node.typeParameters); | |
85 currentClass = null; | |
86 } | |
87 | |
88 visitFunctionNode(FunctionNode node) { | |
89 typeParameters.addAll(node.typeParameters); | |
90 visitChildren(node); | |
91 typeParameters.removeAll(node.typeParameters); | |
92 } | |
93 | |
94 visitFunctionType(FunctionType node) { | |
95 for (int i = 1; i < node.namedParameters.length; ++i) { | |
96 if (node.namedParameters[i - 1].compareTo(node.namedParameters[i]) >= 0) { | |
97 throw 'Named parameters are not sorted on function type found in ' | |
98 '$context'; | |
99 } | |
100 } | |
101 typeParameters.addAll(node.typeParameters); | |
102 for (var typeParameter in node.typeParameters) { | |
103 typeParameter.bound?.accept(this); | |
104 } | |
105 visitList(node.positionalParameters, this); | |
106 visitList(node.namedParameters, this); | |
107 node.returnType.accept(this); | |
108 typeParameters.removeAll(node.typeParameters); | |
109 } | |
110 | |
111 @override | |
112 defaultMemberReference(Member node) { | |
113 if (node.transformerFlags & TransformerFlag.seenBySanityCheck == 0) { | |
114 throw 'Dangling reference to $node found in $context.\n' | |
115 'Parent pointer is set to ${node.parent}'; | |
116 } | |
117 } | |
118 | |
119 @override | |
120 visitClassReference(Class node) { | |
121 if (!classes.contains(node)) { | |
122 throw 'Dangling reference to $node found in $context.\n' | |
123 'Parent pointer is set to ${node.parent}'; | |
124 } | |
125 } | |
126 | |
127 @override | |
128 visitTypeParameterType(TypeParameterType node) { | |
129 if (!typeParameters.contains(node.parameter)) { | |
130 throw 'Type parameter ${node.parameter} referenced out of scope ' | |
131 'in $context.\n' | |
132 'Parent pointer is set to ${node.parameter.parent}'; | |
133 } | |
134 } | |
135 | |
136 @override | |
137 visitInterfaceType(InterfaceType node) { | |
138 node.visitChildren(this); | |
139 if (node.typeArguments.length != node.classNode.typeParameters.length) { | |
140 throw 'Type $node provides ${node.typeArguments.length} type arguments ' | |
141 'but the class declares ${node.classNode.typeParameters.length} ' | |
142 'parameters. Found in $context.'; | |
143 } | |
144 } | |
145 } | |
146 | |
147 class CheckParentPointers extends Visitor { | |
148 static void check(TreeNode node) { | |
149 node.accept(new CheckParentPointers(node.parent)); | |
150 } | |
151 | |
152 TreeNode parent; | |
153 | |
154 CheckParentPointers([this.parent]); | |
155 | |
156 defaultTreeNode(TreeNode node) { | |
157 if (node.parent != parent) { | |
158 throw 'Parent pointer on ${node.runtimeType} ' | |
159 'is ${node.parent.runtimeType} ' | |
160 'but should be ${parent.runtimeType}'; | |
161 } | |
162 var oldParent = parent; | |
163 parent = node; | |
164 node.visitChildren(this); | |
165 parent = oldParent; | |
166 } | |
167 } | |
OLD | NEW |