Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(557)

Side by Side Diff: pkg/analyzer/test/src/summary/element_text.dart

Issue 2728883004: Verify resynthesized elements by comparing text presentations. (Closed)
Patch Set: Created 3 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 import 'dart:io';
Brian Wilkerson 2017/03/03 16:09:33 Missing copyright notice.
scheglov 2017/03/03 16:24:29 Done.
2
3 import 'package:analyzer/dart/element/element.dart';
4 import 'package:analyzer/dart/element/type.dart';
5 import 'package:analyzer/src/dart/element/element.dart';
6 import 'package:analyzer/src/dart/element/type.dart';
7 import 'package:analyzer/src/generated/source.dart';
8 import 'package:analyzer/src/generated/utilities_dart.dart';
9 import 'package:test/test.dart';
10
11 const String _testPath =
12 '/Users/scheglov/Source/Dart/sdk.git/sdk/pkg/analyzer/test/src/summary/resyn thesize_common.dart';
Brian Wilkerson 2017/03/03 16:09:33 That won't work on other machines. :-)
scheglov 2017/03/03 16:24:29 No, it won't. I will null it before commit. Then t
Paul Berry 2017/03/03 17:22:52 You should be able to dynamically figure out the c
13
14 final List<_Replacement> _replacements = [];
15
16 String _testCode;
17 LineInfo _testCodeLines;
18 void applyReplacements() {
19 if (_testPath != null && _replacements.isNotEmpty) {
20 _replacements.sort((a, b) => b.offset - a.offset);
21 String newCode = _testCode;
22 _replacements.forEach((r) {
23 newCode =
24 newCode.substring(0, r.offset) + r.text + newCode.substring(r.end);
25 });
26 new File(_testPath).writeAsStringSync(newCode);
27 }
28 }
29
30 void checkElementText(LibraryElement library, String expected,
31 {bool updateExpectations: false, bool withOffsets: false}) {
32 var writer = new _ElementWriter(withOffsets: withOffsets);
33 writer.writeLibraryElement(library);
34 String actualText = writer.buffer.toString();
35 if (/*updateExpectations &&*/ actualText != expected) {
Brian Wilkerson 2017/03/03 16:09:33 I assume you'll remove the comments before committ
scheglov 2017/03/03 16:24:29 Acknowledged.
36 if (_testCode == null) {
37 _testCode = new File(_testPath).readAsStringSync();
38 _testCodeLines = new LineInfo.fromContent(_testCode);
39 }
40
41 try {
42 throw 42;
43 } catch (e, trace) {
44 String traceString = trace.toString();
45
46 int testFilePathOffset = traceString.indexOf(_testPath);
Paul Berry 2017/03/03 17:22:51 Reading this code, it's really non-obvious what th
scheglov 2017/03/03 18:46:31 Done.
47 expect(testFilePathOffset, isNonNegative);
48
49 int lineOffset = testFilePathOffset + _testPath.length + ':'.length;
Paul Berry 2017/03/03 17:22:52 Nit: for sanity, verify that traceString[testFileP
scheglov 2017/03/03 18:46:31 Done.
50 int invocationLine = int.parse(traceString.substring(
51 lineOffset, traceString.indexOf(':', lineOffset)));
52 int invocationOffset = _testCodeLines.getOffsetOfLine(invocationLine - 1);
53
54 int expectationOffset = _testCode.indexOf("r'''", invocationOffset);
Paul Berry 2017/03/03 17:22:51 It would be nice to have a sanity check here to ma
scheglov 2017/03/03 18:46:30 Done.
55 expectationOffset += 5;
Paul Berry 2017/03/03 17:22:51 Why 5? I would have expected `"r'''".length`, whi
scheglov 2017/03/03 18:46:31 Done.
56 int expectationEnd = _testCode.indexOf("'''", expectationOffset);
57
58 _replacements
59 .add(new _Replacement(expectationOffset, expectationEnd, actualText));
60 }
61 }
62
63 expect(actualText, expected);
64 }
65
66 class _ElementWriter {
67 final bool withOffsets;
68 final StringBuffer buffer = new StringBuffer();
69
70 _ElementWriter({this.withOffsets: false});
71
72 bool isDynamicType(DartType type) => type is DynamicTypeImpl;
73
74 void newLineIfNotEmpty() {
75 if (buffer.isNotEmpty) {
76 buffer.writeln('');
Brian Wilkerson 2017/03/03 16:09:33 You don't need an argument to writeln.
scheglov 2017/03/03 16:24:29 Done.
77 }
78 }
79
80 void writeClassElement(ClassElement e) {
81 if (e.isAbstract) {
82 buffer.write('abstract ');
83 }
84 buffer.write('class ');
85
86 writeName(e);
87 writeTypeParameterElements(e.typeParameters);
88
89 if (e.supertype != null && e.supertype.displayName != 'Object' ||
90 e.mixins.isNotEmpty) {
91 buffer.write(' extends ');
92 writeType(e.supertype);
93 }
94
95 writeList(' with ', '', e.mixins, ', ', writeType);
96 writeList(' implements ', '', e.interfaces, ', ', writeType);
97
98 buffer.writeln(' {');
99
100 e.fields.forEach(writeFieldElement);
101 e.accessors.forEach(writePropertyAccessorElement);
102
103 expect(e.constructors, isNotEmpty);
104 if (e.constructors.length == 1 &&
105 e.constructors[0].isSynthetic &&
106 e.mixins.isEmpty) {
107 expect(e.constructors[0].parameters, isEmpty);
108 } else {
109 e.constructors.forEach(writeConstructorElement);
110 }
111
112 e.methods.forEach(writeMethodElement);
113 buffer.writeln('}');
114 }
115
116 void writeConstructorElement(ConstructorElement e) {
117 buffer.write(' ');
118
119 if (e.isSynthetic) {
120 buffer.write('synthetic ');
121 }
122
123 buffer.write(e.enclosingElement.name);
124 if (e.name != '') {
Brian Wilkerson 2017/03/03 16:09:33 isNotEmpty?
scheglov 2017/03/03 16:24:29 Done.
125 buffer.write('.');
126 writeName(e);
127 }
128
129 writeParameterElements(e.parameters);
130 buffer.writeln(';');
131 }
132
133 void writeEnumElement(ClassElement e) {
Paul Berry 2017/03/03 17:22:52 I'm concerned that this method makes a lot of assu
scheglov 2017/03/03 18:46:31 Done. Alternatively we could add more checks for
134 // var e = _e as EnumElementImpl;
Paul Berry 2017/03/03 17:22:52 Delete this line.
135 expect(e.isEnum, isTrue);
Brian Wilkerson 2017/03/03 16:09:33 Should we expect the opposite is writeClassElement
scheglov 2017/03/03 16:24:29 Done.
136
137 buffer.write('enum ');
138 writeName(e);
139 buffer.write(' { ');
140
141 bool firstEnumValue = true;
142 for (FieldElement field in e.fields) {
143 if (field.name == 'index') {
144 expect(field.isSynthetic, isTrue);
145 expect(field.isStatic, isFalse);
146 continue;
147 }
148 if (field.name == 'values') {
149 expect(field.isSynthetic, isTrue);
150 expect(field.isStatic, isTrue);
151 continue;
152 }
153 if (firstEnumValue) {
154 firstEnumValue = false;
155 } else {
156 buffer.write(', ');
157 }
158 expect(field.isStatic, isTrue);
159 writeName(field);
160 }
161
162 buffer.writeln(' }');
163 }
164
165 void writeExportElement(ExportElement e) {
166 buffer.write('export ');
167 writeUri(e, e.exportedLibrary.source);
168
169 e.combinators.forEach(writeNamespaceCombinator);
170
Paul Berry 2017/03/03 17:22:52 Also write out conditional URIs.
scheglov 2017/03/03 18:46:30 I'm adding TODO for now.
171 buffer.writeln(';');
172 }
173
174 void writeFieldElement(FieldElement e) {
175 if (!e.isSynthetic) {
176 buffer.write(' ');
177
178 if (e.isStatic) {
179 buffer.write('static ');
180 }
181
182 if (e is FieldElementImpl && e.isCovariant) {
183 buffer.write('convariant ');
Paul Berry 2017/03/03 17:22:52 s/convariant/covariant/
scheglov 2017/03/03 18:46:30 Done.
184 }
185
186 writePropertyInducingElement(e);
187 }
188 }
189
190 void writeFunctionElement(FunctionElement e) {
191 if (e.isExternal) {
192 buffer.write('external ');
193 }
194
195 writeType2(e.returnType);
196
197 writeName(e);
198
199 writeTypeParameterElements(e.typeParameters);
200 writeParameterElements(e.parameters);
201
202 // TODO(scheglov) async*
203
204 buffer.writeln(' {}');
205 }
206
207 void writeFunctionTypeAliasElement(FunctionTypeAliasElement e) {
208 buffer.write('typedef ');
209 writeType2(e.returnType);
210
211 writeName(e);
212
213 writeTypeParameterElements(e.typeParameters);
214 writeParameterElements(e.parameters);
215
216 buffer.writeln(';');
217 }
218
219 void writeImportElement(ImportElement e) {
220 if (!e.isSynthetic) {
221 buffer.write('import ');
222 writeUri(e, e.importedLibrary.source);
223
224 if (e.isDeferred) {
225 buffer.write(' deferred');
226 }
227
228 if (e.prefix != null) {
229 buffer.write(' as ');
230 writeName(e.prefix);
231 if (withOffsets) {
232 buffer.write('(${e.prefixOffset})');
233 }
234 }
235
236 e.combinators.forEach(writeNamespaceCombinator);
237
Paul Berry 2017/03/03 17:22:52 Also write out conditional URIs.
scheglov 2017/03/03 18:46:31 I'm adding TODO for now.
scheglov 2017/03/03 19:03:06 Actually, ImportElement does not expose all condit
238 buffer.writeln(';');
239 }
240 }
241
242 void writeLibraryElement(LibraryElement e) {
243 if (e.displayName != '') {
244 buffer.write('library ');
245 writeName(e);
246 buffer.writeln(';');
247 }
248
249 e.imports.forEach(writeImportElement);
250 e.exports.forEach(writeExportElement);
251 e.parts.forEach(writePartElement);
252
253 e.units.forEach(writeUnitElement);
254 }
255
256 void writeList<T>(String open, String close, List<T> items, String separator,
257 writeItem(T item),
258 {bool includeEmpty: false}) {
259 if (!includeEmpty && items.isEmpty) {
260 return;
261 }
262 buffer.write(open);
263 bool first = true;
264 for (T item in items) {
265 if (!first) {
266 buffer.write(separator);
267 }
268 writeItem(item);
269 first = false;
270 }
271 buffer.write(close);
272 }
273
274 void writeMethodElement(MethodElement e) {
275 buffer.write(' ');
276
277 if (e.isStatic) {
278 buffer.write('static ');
279 }
280
281 if (e.isExternal) {
282 buffer.write('external ');
283 }
284
285 writeType2(e.returnType);
286
287 writeName(e);
288
289 writeTypeParameterElements(e.typeParameters);
290 writeParameterElements(e.parameters);
291
292 // TODO(scheglov) async*
293
294 if (e.isAbstract) {
295 buffer.writeln(';');
296 } else {
297 buffer.writeln(' {}');
298 }
299 }
300
301 void writeName(Element e) {
302 buffer.write(e.displayName);
303 if (withOffsets) {
304 buffer.write('(');
Paul Berry 2017/03/03 17:22:52 This makes a name decorated with an offset look li
scheglov 2017/03/03 18:46:31 Done.
Paul Berry 2017/03/03 23:24:35 It looks like you haven't done this yet. Did you
scheglov 2017/03/04 02:21:58 I'm sorry for the oversight. I made the change, bu
305 buffer.write(e.nameOffset);
306 buffer.write(')');
307 }
308 }
309
310 void writeNamespaceCombinator(NamespaceCombinator e) {
311 if (e is ShowElementCombinator) {
312 buffer.write(' show ');
313 buffer.write(e.shownNames.join(', '));
314 } else if (e is HideElementCombinator) {
315 buffer.write(' hide ');
316 buffer.write(e.hiddenNames.join(', '));
317 }
318 }
319
320 void writeParameterElement(ParameterElement e) {
321 String closeString;
322 ParameterKind kind = e.parameterKind;
323 if (kind == ParameterKind.REQUIRED) {
324 closeString = '';
325 } else if (kind == ParameterKind.POSITIONAL) {
326 buffer.write('[');
327 closeString = ']';
328 } else if (kind == ParameterKind.NAMED) {
329 buffer.write('{');
330 closeString = '}';
331 } else {
332 fail('Unknown parameter kind: $kind');
333 }
334
335 if (e.isCovariant) {
336 buffer.write('convariant ');
Paul Berry 2017/03/03 17:22:52 s/convariant/covariant/
scheglov 2017/03/03 18:46:30 Done.
337 }
338
339 if (e.isFinal) {
340 buffer.write('final ');
341 }
342
343 writeType2(e.type);
344
345 if (e is FieldFormalParameterElement) {
346 buffer.write('this.');
347 }
348
349 writeName(e);
350
351 buffer.write(closeString);
352 }
353
354 void writeParameterElements(List<ParameterElement> elements) {
355 writeList('(', ')', elements, ', ', writeParameterElement,
356 includeEmpty: true);
357 }
358
359 void writePartElement(CompilationUnitElement e) {
360 buffer.write('part ');
361 writeUri(e, e.source);
362 buffer.writeln(';');
363 }
364
365 void writePropertyAccessorElement(PropertyAccessorElement e) {
366 if (e.isSynthetic) {
367 return;
368 }
369
370 if (e.enclosingElement is ClassElement) {
371 buffer.write(' ');
372
373 if (e.isStatic) {
374 buffer.write('static ');
375 }
376 }
377
378 if (e.isExternal) {
379 buffer.write('external ');
380 }
381
382 writeType2(e.returnType);
383
384 if (e.isGetter) {
385 buffer.write('get ');
386 } else {
387 buffer.write('set ');
388 }
389
390 writeName(e);
391
392 if (e.isSetter || e.parameters.isNotEmpty) {
393 writeParameterElements(e.parameters);
394 }
395
396 expect(e.typeParameters, isEmpty);
397
398 expect(e.isSynchronous, isTrue);
399 expect(e.isAsynchronous, isFalse);
400 expect(e.isGenerator, isFalse);
401
402 if (e.isAbstract) {
403 buffer.writeln(';');
404 } else {
405 buffer.writeln(' {}');
406 }
407 }
408
409 void writePropertyInducingElement(PropertyInducingElement e) {
410 if (!e.isSynthetic) {
411 DartType type = e.type;
412 expect(type, isNotNull);
413 bool hasDynamicType = isDynamicType(type);
414
415 if (e.isFinal) {
416 buffer.write('final ');
417 } else if (e.isConst) {
418 buffer.write('const ');
419 } else if (hasDynamicType) {
420 buffer.write('var ');
421 }
422
423 if (!hasDynamicType) {
424 writeType(type);
425 buffer.write(' ');
426 }
Brian Wilkerson 2017/03/03 16:09:33 Replace with writeType2?
scheglov 2017/03/03 16:24:29 Done.
427
428 writeName(e);
429
Paul Berry 2017/03/03 17:22:52 One of the things that was hardest to get right wh
scheglov 2017/03/03 18:46:31 I'm adding TODO for now.
430 buffer.writeln(';');
431 }
432 }
433
434 void writeTopLevelVariableElement(TopLevelVariableElement e) {
435 writePropertyInducingElement(e);
436 }
437
438 void writeType(DartType type) {
439 buffer.write(type.displayName);
Paul Berry 2017/03/03 17:22:52 The "→" character that we use when writing out fun
scheglov 2017/03/03 18:46:31 It looks better with this character :-( If possibl
440 }
441
442 void writeType2(DartType type) {
Paul Berry 2017/03/03 17:22:52 IMHO this is more sophisticated than we ought to b
scheglov 2017/03/03 18:46:31 Done.
443 if (type != DynamicTypeImpl.instance) {
444 writeType(type);
445 buffer.write(' ');
446 }
447 }
448
449 void writeTypeParameterElement(TypeParameterElement e) {
450 writeName(e);
451 if (e.bound != null) {
452 buffer.write(' extends ');
453 writeType(e.bound);
454 }
455 }
456
457 void writeTypeParameterElements(List<TypeParameterElement> elements) {
458 writeList('<', '>', elements, ', ', writeTypeParameterElement);
Brian Wilkerson 2017/03/03 16:09:33 Probably want to include if empty.
scheglov 2017/03/03 16:24:29 I don't think so. This would transform "class C {}
459 }
460
461 void writeUnitElement(CompilationUnitElement e) {
462 if (e.library.definingCompilationUnit != e) {
463 buffer.writeln('-' * 20);
464 buffer.writeln('unit: ${e.source.shortName}');
465 buffer.writeln('');
466 }
467 e.functionTypeAliases.forEach(writeFunctionTypeAliasElement);
468 e.enums.forEach(writeEnumElement);
469 e.types.forEach(writeClassElement);
470 e.topLevelVariables.forEach(writeTopLevelVariableElement);
471 e.accessors.forEach(writePropertyAccessorElement);
472 e.functions.forEach(writeFunctionElement);
473 }
474
475 void writeUri(UriReferencedElement e, Source source) {
476 String uri = e.uri ?? source.uri.toString();
477 buffer.write('\'$uri\'');
478 if (withOffsets) {
479 buffer.write('(');
480 buffer.write('${e.uriOffset}, ');
481 buffer.write('${e.uriEnd})');
482 buffer.write(')');
483 }
484 }
485 }
486
487 class _Replacement {
488 final int offset;
489 final int end;
490 final String text;
491 _Replacement(this.offset, this.end, this.text);
492 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698