OLD | NEW |
1 // Copyright (c) 2017, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2017, 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 | 4 |
5 import 'package:compiler/src/common.dart'; | 5 import 'package:compiler/src/common.dart'; |
6 import 'package:compiler/src/common_elements.dart'; | |
7 import 'package:compiler/src/compiler.dart'; | 6 import 'package:compiler/src/compiler.dart'; |
8 import 'package:compiler/src/elements/elements.dart'; | 7 import 'package:compiler/src/elements/elements.dart'; |
9 import 'package:compiler/src/elements/entities.dart'; | |
10 import 'package:compiler/src/resolution/tree_elements.dart'; | |
11 import 'package:compiler/src/tree/nodes.dart'; | 8 import 'package:compiler/src/tree/nodes.dart'; |
12 import 'package:compiler/src/types/types.dart'; | 9 import 'package:compiler/src/types/types.dart'; |
13 import 'package:expect/expect.dart'; | |
14 | 10 |
15 import '../annotated_code_helper.dart'; | 11 import '../equivalence/id_equivalence.dart'; |
16 import '../memory_compiler.dart'; | 12 import '../equivalence/id_equivalence_helper.dart'; |
17 import 'enumerator.dart'; | |
18 | |
19 typedef void CheckMemberFunction( | |
20 Compiler compiler, Map<Id, String> expectedMap, MemberElement member); | |
21 | |
22 /// Compiles the [annotatedCode] with the provided [options] and calls | |
23 /// [checkMember] for each member in the code providing the map from [Id] to | |
24 /// annotation. Any [Id] left in the map will be reported as missing. | |
25 checkCode(String annotatedCode, CheckMemberFunction checkMember, | |
26 {List<String> options: const <String>[]}) async { | |
27 AnnotatedCode code = | |
28 new AnnotatedCode.fromText(annotatedCode, commentStart, commentEnd); | |
29 Map<Id, String> expectedMap = computeExpectedMap(code); | |
30 Compiler compiler = compilerFor( | |
31 memorySourceFiles: {'main.dart': code.sourceCode}, options: options); | |
32 compiler.stopAfterTypeInference = true; | |
33 Uri mainUri = Uri.parse('memory:main.dart'); | |
34 await compiler.run(mainUri); | |
35 LibraryElement mainApp = | |
36 compiler.frontendStrategy.elementEnvironment.mainLibrary; | |
37 mainApp.forEachLocalMember((dynamic member) { | |
38 if (member.isClass) { | |
39 member.forEachLocalMember((member) { | |
40 checkMember(compiler, expectedMap, member); | |
41 }); | |
42 } else if (member.isTypedef) { | |
43 // Skip. | |
44 } else { | |
45 checkMember(compiler, expectedMap, member); | |
46 } | |
47 }); | |
48 expectedMap.forEach((Id id, String expected) { | |
49 reportHere( | |
50 compiler.reporter, | |
51 computeSpannable(compiler.resolution.elementEnvironment, mainUri, id), | |
52 'expected:${expected},actual:null'); | |
53 }); | |
54 Expect.isTrue(expectedMap.isEmpty, "Ids not found: $expectedMap."); | |
55 } | |
56 | 13 |
57 void checkMemberAstTypeMasks( | 14 void checkMemberAstTypeMasks( |
58 Compiler compiler, Map<Id, String> expectedMap, MemberElement member) { | 15 Compiler compiler, Map<Id, String> expectedMap, MemberElement member) { |
59 ResolvedAst resolvedAst = member.resolvedAst; | 16 ResolvedAst resolvedAst = member.resolvedAst; |
60 if (resolvedAst.kind != ResolvedAstKind.PARSED) return; | 17 if (resolvedAst.kind != ResolvedAstKind.PARSED) return; |
61 compiler.reporter.withCurrentElement(member.implementation, () { | 18 compiler.reporter.withCurrentElement(member.implementation, () { |
62 new TypeMaskChecker(compiler.reporter, expectedMap, resolvedAst, | 19 new TypeMaskChecker(compiler.reporter, expectedMap, resolvedAst, |
63 compiler.globalInference.results) | 20 compiler.globalInference.results) |
64 .check(); | 21 .check(); |
65 }); | 22 }); |
66 } | 23 } |
67 | 24 |
68 Spannable computeSpannable( | 25 class TypeMaskChecker extends AbstractResolvedAstChecker { |
69 ElementEnvironment elementEnvironment, Uri mainUri, Id id) { | |
70 if (id is NodeId) { | |
71 return new SourceSpan(mainUri, id.value, id.value + 1); | |
72 } else if (id is ElementId) { | |
73 LibraryEntity library = elementEnvironment.lookupLibrary(mainUri); | |
74 if (id.className != null) { | |
75 ClassEntity cls = | |
76 elementEnvironment.lookupClass(library, id.className, required: true); | |
77 return elementEnvironment.lookupClassMember(cls, id.memberName); | |
78 } else { | |
79 return elementEnvironment.lookupLibraryMember(library, id.memberName); | |
80 } | |
81 } | |
82 throw new UnsupportedError('Unsupported id $id.'); | |
83 } | |
84 | |
85 Map<Id, String> computeExpectedMap(AnnotatedCode code) { | |
86 Map<Id, String> map = <Id, String>{}; | |
87 for (Annotation annotation in code.annotations) { | |
88 String text = annotation.text; | |
89 int colonPos = text.indexOf(':'); | |
90 Id id; | |
91 String expected; | |
92 if (colonPos == -1) { | |
93 id = new NodeId(annotation.offset); | |
94 expected = text; | |
95 } else { | |
96 id = new ElementId(text.substring(0, colonPos)); | |
97 expected = text.substring(colonPos + 1); | |
98 } | |
99 map[id] = expected; | |
100 } | |
101 return map; | |
102 } | |
103 | |
104 class TypeMaskChecker extends Visitor with AstEnumeratorMixin { | |
105 final DiagnosticReporter reporter; | |
106 final Map<Id, String> expectedMap; | |
107 final ResolvedAst resolvedAst; | |
108 final GlobalTypeInferenceResults results; | 26 final GlobalTypeInferenceResults results; |
109 final GlobalTypeInferenceElementResult result; | 27 final GlobalTypeInferenceElementResult result; |
110 | 28 |
111 TypeMaskChecker( | 29 TypeMaskChecker(DiagnosticReporter reporter, Map<Id, String> expectedMap, |
112 this.reporter, this.expectedMap, this.resolvedAst, this.results) | 30 ResolvedAst resolvedAst, this.results) |
113 : result = results.resultOfMember(resolvedAst.element as MemberElement); | 31 : result = results.resultOfMember(resolvedAst.element as MemberElement), |
| 32 super(reporter, expectedMap, resolvedAst); |
114 | 33 |
115 TreeElements get elements => resolvedAst.elements; | 34 @override |
116 | 35 String computeElementValue(AstElement element) { |
117 void check() { | |
118 resolvedAst.node.accept(this); | |
119 } | |
120 | |
121 visitNode(Node node) { | |
122 node.visitChildren(this); | |
123 } | |
124 | |
125 void checkElement(AstElement element) { | |
126 ElementId id = computeElementId(element); | |
127 GlobalTypeInferenceElementResult elementResult = | 36 GlobalTypeInferenceElementResult elementResult = |
128 results.resultOfElement(element); | 37 results.resultOfElement(element); |
129 TypeMask value = | 38 TypeMask value = |
130 element.isFunction ? elementResult.returnType : elementResult.type; | 39 element.isFunction ? elementResult.returnType : elementResult.type; |
131 String expected = annotationForId(id); | 40 return value != null ? '$value' : null; |
132 checkValue(element, expected, value); | |
133 } | 41 } |
134 | 42 |
135 String annotationForId(Id id) { | 43 @override |
136 if (id == null) return null; | 44 String computeNodeValue(Node node, [AstElement element]) { |
137 return expectedMap.remove(id); | 45 if (node is Send) { |
138 } | 46 TypeMask value = result.typeOfSend(node); |
139 | 47 return value != null ? '$value' : null; |
140 void checkValue(Spannable spannable, String expected, TypeMask value) { | 48 } else if (element != null && element.isLocal) { |
141 if (value != null || expected != null) { | 49 return computeElementValue(element); |
142 String valueText = '$value'; | |
143 if (valueText != expected) { | |
144 reportHere(reporter, spannable, 'expected:${expected},actual:${value}'); | |
145 } | |
146 Expect.equals(expected, valueText); | |
147 } | 50 } |
148 } | 51 return null; |
149 | |
150 void checkSend(Send node) { | |
151 NodeId id = computeNodeId(node); | |
152 TypeMask value = result.typeOfSend(node); | |
153 String expected = annotationForId(id); | |
154 checkValue(node, expected, value); | |
155 } | |
156 | |
157 visitVariableDefinitions(VariableDefinitions node) { | |
158 for (Node child in node.definitions) { | |
159 AstElement element = elements[child]; | |
160 if (element == null) { | |
161 reportHere(reporter, child, 'No element for variable.'); | |
162 } else if (!element.isLocal) { | |
163 checkElement(element); | |
164 } | |
165 } | |
166 visitNode(node); | |
167 } | |
168 | |
169 visitFunctionExpression(FunctionExpression node) { | |
170 AstElement element = elements.getFunctionDefinition(node); | |
171 checkElement(element); | |
172 visitNode(node); | |
173 } | |
174 | |
175 visitSend(Send node) { | |
176 checkSend(node); | |
177 visitNode(node); | |
178 } | |
179 | |
180 visitSendSet(SendSet node) { | |
181 checkSend(node); | |
182 visitNode(node); | |
183 } | 52 } |
184 } | 53 } |
OLD | NEW |