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

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

Issue 2728883004: Verify resynthesized elements by comparing text presentations. (Closed)
Patch Set: Ready for review. 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 // 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
3 // BSD-style license that can be found in the LICENSE file.
4
5 import 'dart:io';
6
7 import 'package:analyzer/dart/ast/ast.dart';
8 import 'package:analyzer/dart/ast/token.dart';
9 import 'package:analyzer/dart/element/element.dart';
10 import 'package:analyzer/dart/element/type.dart';
11 import 'package:analyzer/src/dart/element/element.dart';
12 import 'package:analyzer/src/dart/element/type.dart';
13 import 'package:analyzer/src/generated/source.dart';
14 import 'package:analyzer/src/generated/utilities_dart.dart';
15 import 'package:test/test.dart';
16
17 const String _testPath =
18 '/Users/scheglov/Source/Dart/sdk.git/sdk/pkg/analyzer/test/src/summary/resyn thesize_common.dart';
Paul Berry 2017/03/03 23:24:35 Change to null before committing. As I suggested
scheglov 2017/03/04 02:21:58 I will change it to null for now. Yes, initially I
19
20 final List<_Replacement> _replacements = [];
21
22 String _testCode;
23 LineInfo _testCodeLines;
24
25 void applyReplacements() {
26 if (_testPath != null && _replacements.isNotEmpty) {
27 _replacements.sort((a, b) => b.offset - a.offset);
28 String newCode = _testCode;
29 _replacements.forEach((r) {
30 newCode =
31 newCode.substring(0, r.offset) + r.text + newCode.substring(r.end);
32 });
33 new File(_testPath).writeAsStringSync(newCode);
34 }
35 }
36
37 void checkElementText(LibraryElement library, String expected,
38 {bool updateExpectations: false, bool withOffsets: false}) {
39 var writer = new _ElementWriter(withOffsets: withOffsets);
40 writer.writeLibraryElement(library);
41 String actualText = writer.buffer.toString();
42 if (/*updateExpectations &&*/ actualText != expected) {
Paul Berry 2017/03/03 23:24:35 Remove commented out text before committing.
scheglov 2017/03/04 02:21:58 Done.
43 if (_testCode == null) {
44 _testCode = new File(_testPath).readAsStringSync();
45 _testCodeLines = new LineInfo.fromContent(_testCode);
46 }
47
48 try {
49 throw 42;
50 } catch (e, trace) {
51 String traceString = trace.toString();
52
53 // Assuming traceString contains "$_testPath:$invocationLine:$column",
54 // figure out the value of invocationLine.
55
56 print(traceString);
57 int testFilePathOffset = traceString.indexOf(_testPath);
58 expect(testFilePathOffset, isNonNegative);
59
60 // Sanity check: there must be ':' after the path.
61 expect(traceString[testFilePathOffset + _testPath.length], ':');
62
63 int lineOffset = testFilePathOffset + _testPath.length + ':'.length;
64 int invocationLine = int.parse(traceString.substring(
65 lineOffset, traceString.indexOf(':', lineOffset)));
66 int invocationOffset = _testCodeLines.getOffsetOfLine(invocationLine - 1);
67
68 const String rawStringPrefix = "r'''";
69 int expectationOffset =
70 _testCode.indexOf(rawStringPrefix, invocationOffset);
71
72 // Sanity check: there must be no other strings or blocks.
73 expect(_testCode.substring(invocationOffset, expectationOffset),
74 isNot(anyOf(contains("'"), contains('"'), contains('}'))));
75
76 expectationOffset += rawStringPrefix.length;
77 int expectationEnd = _testCode.indexOf("'''", expectationOffset);
78
79 _replacements.add(new _Replacement(
80 expectationOffset, expectationEnd, '\n' + actualText));
81 }
82 }
83
84 expect(actualText, expected);
85 }
86
87 class _ElementWriter {
88 final bool withOffsets;
89 final StringBuffer buffer = new StringBuffer();
90
91 _ElementWriter({this.withOffsets: false});
92
93 bool isDynamicType(DartType type) => type is DynamicTypeImpl;
94
95 bool isEnumElement(Element e) {
96 return e is ClassElement && e.isEnum;
97 }
98
99 void newLineIfNotEmpty() {
100 if (buffer.isNotEmpty) {
101 buffer.writeln();
102 }
103 }
104
105 void writeBodyModifiers(ExecutableElement e) {
106 if (e.isAsynchronous) {
107 expect(e.isSynchronous, isFalse);
108 buffer.write(' async');
109 }
110
111 if (e.isSynchronous && e.isGenerator) {
112 expect(e.isAsynchronous, isFalse);
113 buffer.write(' sync');
114 }
115
116 writeIf(e.isGenerator, '*');
117 }
118
119 void writeClassElement(ClassElement e) {
120 writeDocumentation(e);
121 writeMetadata(e, '', '\n');
122
123 writeIf(e.isAbstract, 'abstract ');
124
125 if (e.isEnum) {
126 buffer.write('enum ');
127 } else {
128 buffer.write('class ');
129 }
130
131 writeIf(e.isMixinApplication, 'alias ');
132
133 writeName(e);
134 writeTypeParameterElements(e.typeParameters);
135
136 if (e.supertype != null && e.supertype.displayName != 'Object' ||
137 e.mixins.isNotEmpty) {
138 buffer.write(' extends ');
139 writeType(e.supertype);
140 }
141
142 writeList(' with ', '', e.mixins, ', ', writeType);
143 writeList(' implements ', '', e.interfaces, ', ', writeType);
144
145 buffer.writeln(' {');
146
147 e.fields.forEach(writeFieldElement);
148 e.accessors.forEach(writePropertyAccessorElement);
149
150 if (e.isEnum) {
151 expect(e.constructors, isEmpty);
152 } else {
153 expect(e.constructors, isNotEmpty);
154 }
155
156 if (e.constructors.length == 1 &&
157 e.constructors[0].isSynthetic &&
158 e.mixins.isEmpty) {
159 expect(e.constructors[0].parameters, isEmpty);
160 } else {
161 e.constructors.forEach(writeConstructorElement);
162 }
163
164 e.methods.forEach(writeMethodElement);
165 buffer.writeln('}');
166 }
167
168 void writeConstructorElement(ConstructorElement e) {
169 writeDocumentation(e, ' ');
170 writeMetadata(e, ' ', '\n');
171
172 buffer.write(' ');
173
174 writeIf(e.isSynthetic, 'synthetic ');
175 writeIf(e.isExternal, 'external ');
176 writeIf(e.isConst, 'const ');
177 writeIf(e.isFactory, 'factory ');
178
179 buffer.write(e.enclosingElement.name);
180 if (e.name.isNotEmpty) {
181 buffer.write('.');
182 writeName(e);
183 }
184
185 writeParameterElements(e.parameters);
186
187 {
188 ConstructorElement redirected = e.redirectedConstructor;
189 if (redirected != null) {
190 buffer.write(' = ');
191 buffer.write(redirected.returnType);
192 if (redirected.name.isNotEmpty) {
193 buffer.write('.');
194 buffer.write(redirected.name);
195 }
196 }
197 }
198
199 if (e is ConstructorElementImpl) {
200 if (e.constantInitializers != null) {
201 writeList(' : ', '', e.constantInitializers, ', ', writeExpression);
202 }
203 }
204
205 expect(e.isAsynchronous, isFalse);
206 expect(e.isGenerator, isFalse);
207
208 buffer.writeln(';');
209 }
210
211 void writeDocumentation(Element e, [String prefix = '']) {
212 if (e.documentationComment != null) {
213 buffer.write(prefix);
214 buffer.writeln(e.documentationComment);
215 }
216 }
217
218 void writeExportElement(ExportElement e) {
219 writeMetadata(e, '', '\n');
220 buffer.write('export ');
221 writeUri(e, e.exportedLibrary.source);
222
223 e.combinators.forEach(writeNamespaceCombinator);
224
225 buffer.writeln(';');
226 }
227
228 void writeExpression(AstNode e) {
229 if (e is Annotation) {
230 buffer.write('@');
231 writeExpression(e.name);
232 if (e.constructorName != null) {
233 buffer.write('.');
234 writeExpression(e.constructorName);
235 }
236 if (e.arguments != null) {
237 writeList('(', ')', e.arguments.arguments, ', ', writeExpression,
238 includeEmpty: true);
239 }
240 } else if (e is AssertInitializer) {
241 buffer.write('assert(');
242 writeExpression(e.condition);
243 if (e.message != null) {
244 buffer.write(', ');
245 writeExpression(e.message);
246 }
247 buffer.write(')');
248 } else if (e is BinaryExpression) {
249 writeExpression(e.leftOperand);
250 buffer.write(' ');
251 buffer.write(e.operator.lexeme);
252 buffer.write(' ');
253 writeExpression(e.rightOperand);
254 } else if (e is BooleanLiteral) {
255 buffer.write(e.value);
256 } else if (e is ConditionalExpression) {
257 writeExpression(e.condition);
258 buffer.write(' ? ');
259 writeExpression(e.thenExpression);
260 buffer.write(' : ');
261 writeExpression(e.elseExpression);
262 } else if (e is ConstructorFieldInitializer) {
263 writeExpression(e.fieldName);
264 buffer.write(' = ');
265 writeExpression(e.expression);
266 } else if (e is ConstructorName) {
267 writeExpression(e.type);
268 if (e.name != null) {
269 buffer.write('.');
270 writeExpression(e.name);
271 }
272 } else if (e is DoubleLiteral) {
273 buffer.write(e.value);
274 } else if (e is InstanceCreationExpression) {
275 buffer.write(e.keyword.lexeme);
276 buffer.write(' ');
277 writeExpression(e.constructorName);
278 writeList('(', ')', e.argumentList.arguments, ', ', writeExpression,
279 includeEmpty: true);
280 } else if (e is IntegerLiteral) {
281 buffer.write(e.value);
282 } else if (e is InterpolationExpression) {
283 buffer.write(r'${');
284 writeExpression(e.expression);
285 buffer.write(r'}');
286 } else if (e is InterpolationString) {
287 buffer.write(e.value.replaceAll("'", r"\'"));
288 } else if (e is ListLiteral) {
289 if (e.constKeyword != null) {
290 buffer.write('const ');
291 }
292 if (e.typeArguments != null) {
293 writeList('<', '>', e.typeArguments.arguments, ', ', writeExpression);
294 }
295 writeList('[', ']', e.elements, ', ', writeExpression,
296 includeEmpty: true);
297 } else if (e is Label) {
298 writeExpression(e.label);
299 buffer.write(': ');
300 } else if (e is MapLiteral) {
301 if (e.constKeyword != null) {
302 buffer.write('const ');
303 }
304 if (e.typeArguments != null) {
305 writeList('<', '>', e.typeArguments.arguments, ', ', writeExpression);
306 }
307 writeList('{', '}', e.entries, ', ', writeExpression, includeEmpty: true);
308 } else if (e is MapLiteralEntry) {
309 writeExpression(e.key);
310 buffer.write(': ');
311 writeExpression(e.value);
312 } else if (e is NamedExpression) {
313 writeExpression(e.name);
314 buffer.write(e.expression);
315 } else if (e is NullLiteral) {
316 buffer.write('null');
317 } else if (e is PrefixExpression) {
318 buffer.write(e.operator.lexeme);
319 writeExpression(e.operand);
320 } else if (e is PrefixedIdentifier) {
321 writeExpression(e.prefix);
322 buffer.write('.');
323 writeExpression(e.identifier);
324 } else if (e is PropertyAccess) {
325 writeExpression(e.target);
326 buffer.write('.');
327 writeExpression(e.propertyName);
328 } else if (e is RedirectingConstructorInvocation) {
329 buffer.write('this');
330 if (e.constructorName != null) {
331 buffer.write('.');
332 writeExpression(e.constructorName);
333 }
334 writeList('(', ')', e.argumentList.arguments, ', ', writeExpression,
335 includeEmpty: true);
336 } else if (e is SimpleIdentifier) {
337 buffer.write(e.name);
338 } else if (e is SimpleStringLiteral) {
339 buffer.write("'");
340 buffer.write(e.value.replaceAll("'", r"\'"));
341 buffer.write("'");
342 } else if (e is StringInterpolation) {
343 buffer.write("'");
344 e.elements.forEach(writeExpression);
345 buffer.write("'");
346 } else if (e is SuperConstructorInvocation) {
347 buffer.write('super');
348 if (e.constructorName != null) {
349 buffer.write('.');
350 writeExpression(e.constructorName);
351 }
352 writeList('(', ')', e.argumentList.arguments, ', ', writeExpression,
353 includeEmpty: true);
354 } else if (e is SuperExpression) {
355 buffer.write('super');
356 } else if (e is SymbolLiteral) {
357 buffer.write('#');
358 writeList('', '', e.components, '.',
359 (Token token) => buffer.write(token.lexeme));
360 } else if (e is ThisExpression) {
361 buffer.write('this');
362 } else if (e is TypeName) {
363 writeExpression(e.name);
364 if (e.typeArguments != null) {
365 writeList('<', '>', e.typeArguments.arguments, ', ', writeExpression);
366 }
367 } else {
368 fail('Unsupported expression type: ${e.runtimeType}');
369 }
370 }
371
372 void writeFieldElement(FieldElement e) {
373 if (e.isSynthetic && !isEnumElement(e.enclosingElement)) {
374 return;
375 }
376
377 writeDocumentation(e, ' ');
378 writeMetadata(e, ' ', '\n');
379
380 buffer.write(' ');
381
382 writeIf(e.isStatic, 'static ');
383 writeIf(e is FieldElementImpl && e.isCovariant, 'covariant ');
384
385 writePropertyInducingElement(e);
386 }
387
388 void writeFunctionElement(FunctionElement e) {
389 writeIf(e.isExternal, 'external ');
390
391 writeType2(e.returnType);
392
393 writeName(e);
394
395 writeTypeParameterElements(e.typeParameters);
396 writeParameterElements(e.parameters);
397
398 writeBodyModifiers(e);
399
400 buffer.writeln(' {}');
401 }
402
403 void writeFunctionTypeAliasElement(FunctionTypeAliasElement e) {
404 writeDocumentation(e);
405 writeMetadata(e, '', '\n');
406
407 buffer.write('typedef ');
408 writeType2(e.returnType);
409
410 writeName(e);
411
412 writeTypeParameterElements(e.typeParameters);
413 writeParameterElements(e.parameters);
414
415 buffer.writeln(';');
416 }
417
418 void writeIf(bool flag, String str) {
419 if (flag) {
420 buffer.write(str);
421 }
422 }
423
424 void writeImportElement(ImportElement e) {
425 if (!e.isSynthetic) {
426 writeMetadata(e, '', '\n');
427 buffer.write('import ');
428 writeUri(e, e.importedLibrary.source);
429
430 writeIf(e.isDeferred, ' deferred');
431
432 if (e.prefix != null) {
433 buffer.write(' as ');
434 writeName(e.prefix);
435 if (withOffsets) {
436 buffer.write('(${e.prefixOffset})');
437 }
438 }
439
440 e.combinators.forEach(writeNamespaceCombinator);
441
442 buffer.writeln(';');
443 }
444 }
445
446 void writeLibraryElement(LibraryElement e) {
447 if (e.displayName != '') {
448 writeMetadata(e, '', '\n');
449 buffer.write('library ');
450 writeName(e);
451 buffer.writeln(';');
452 }
453
454 e.imports.forEach(writeImportElement);
455 e.exports.forEach(writeExportElement);
456 e.parts.forEach(writePartElement);
457
458 e.units.forEach(writeUnitElement);
459 }
460
461 void writeList<T>(String open, String close, List<T> items, String separator,
462 writeItem(T item),
463 {bool includeEmpty: false}) {
464 if (!includeEmpty && items.isEmpty) {
465 return;
466 }
467 buffer.write(open);
468 bool first = true;
469 for (T item in items) {
470 if (!first) {
471 buffer.write(separator);
472 }
473 writeItem(item);
474 first = false;
475 }
476 buffer.write(close);
477 }
478
479 void writeMetadata(Element e, String prefix, String separator) {
480 if (e.metadata.isNotEmpty) {
481 writeList(prefix, '', e.metadata, '$separator$prefix', (a) {
482 writeExpression((a as ElementAnnotationImpl).annotationAst);
483 });
484 buffer.write(separator);
485 }
486 }
487
488 void writeMethodElement(MethodElement e) {
489 writeDocumentation(e, ' ');
490 writeMetadata(e, ' ', '\n');
491
492 buffer.write(' ');
493
494 writeIf(e.isExternal, 'external ');
495 writeIf(e.isStatic, 'static ');
496
497 writeType2(e.returnType);
498
499 writeName(e);
500
501 writeTypeParameterElements(e.typeParameters);
502 writeParameterElements(e.parameters);
503
504 writeBodyModifiers(e);
505
506 if (e.isAbstract) {
507 buffer.writeln(';');
508 } else {
509 buffer.writeln(' {}');
510 }
511 }
512
513 void writeName(Element e) {
514 buffer.write(e.displayName);
515 if (withOffsets) {
516 buffer.write('(');
517 buffer.write(e.nameOffset);
518 buffer.write(')');
519 }
520 }
521
522 void writeNamespaceCombinator(NamespaceCombinator e) {
523 if (e is ShowElementCombinator) {
524 buffer.write(' show ');
525 buffer.write(e.shownNames.join(', '));
526 } else if (e is HideElementCombinator) {
527 buffer.write(' hide ');
528 buffer.write(e.hiddenNames.join(', '));
529 }
530 }
531
532 void writeParameterElement(ParameterElement e) {
533 String defaultValueSeparator;
534 Expression defaultValue =
535 e is DefaultParameterElementImpl ? e.constantInitializer : null;
536 String closeString;
537 ParameterKind kind = e.parameterKind;
538 if (kind == ParameterKind.REQUIRED) {
539 closeString = '';
540 } else if (kind == ParameterKind.POSITIONAL) {
541 buffer.write('[');
542 defaultValueSeparator = ' = ';
543 closeString = ']';
544 } else if (kind == ParameterKind.NAMED) {
545 buffer.write('{');
546 defaultValueSeparator = ': ';
547 closeString = '}';
548 } else {
549 fail('Unknown parameter kind: $kind');
550 }
551
552 writeMetadata(e, '', ' ');
553
554 writeIf(e.isCovariant, 'covariant ');
555 writeIf(e.isFinal, 'final ');
556
557 writeType2(e.type);
558
559 if (e is FieldFormalParameterElement) {
560 buffer.write('this.');
561 }
562
563 writeName(e);
564
565 if (defaultValue != null) {
566 buffer.write(defaultValueSeparator);
567 writeExpression(defaultValue);
568 }
569
570 buffer.write(closeString);
571 }
572
573 void writeParameterElements(List<ParameterElement> elements) {
574 writeList('(', ')', elements, ', ', writeParameterElement,
575 includeEmpty: true);
576 }
577
578 void writePartElement(CompilationUnitElement e) {
579 writeMetadata(e, '', '\n');
580 buffer.write('part ');
581 writeUri(e, e.source);
582 buffer.writeln(';');
583 }
584
585 void writePropertyAccessorElement(PropertyAccessorElement e) {
586 if (e.isSynthetic) {
587 return;
588 }
589
590 if (e.enclosingElement is ClassElement) {
591 writeDocumentation(e, ' ');
592 writeMetadata(e, ' ', '\n');
593
594 buffer.write(' ');
595
596 writeIf(e.isStatic, 'static ');
597 } else {
598 writeDocumentation(e);
599 writeMetadata(e, '', '\n');
600 }
601
602 writeIf(e.isExternal, 'external ');
603
604 writeType2(e.returnType);
605
606 if (e.isGetter) {
607 buffer.write('get ');
608 } else {
609 buffer.write('set ');
610 }
611
612 writeName(e);
613
614 if (e.isSetter || e.parameters.isNotEmpty) {
615 writeParameterElements(e.parameters);
616 }
617
618 expect(e.typeParameters, isEmpty);
619
620 expect(e.isSynchronous, isTrue);
621 expect(e.isAsynchronous, isFalse);
622 expect(e.isGenerator, isFalse);
623
624 if (e.isAbstract) {
625 buffer.writeln(';');
626 } else {
627 buffer.writeln(' {}');
628 }
629 }
630
631 void writePropertyInducingElement(PropertyInducingElement e) {
632 DartType type = e.type;
633 expect(type, isNotNull);
634
635 writeIf(e.isFinal, 'final ');
636 writeIf(e.isConst, 'const ');
637 writeType2(type);
638
639 writeName(e);
640
641 if (e is ConstVariableElement) {
642 Expression initializer = (e as ConstVariableElement).constantInitializer;
643 if (initializer != null) {
644 buffer.write(' = ');
645 writeExpression(initializer);
646 }
647 }
648
649 // TODO(scheglov) Paul: One of the things that was hardest to get right
650 // when resynthesizing the element model was the synthetic function for the
651 // initializer. Can we write that out (along with its return type)?
652
653 buffer.writeln(';');
654 }
655
656 void writeTopLevelVariableElement(TopLevelVariableElement e) {
657 if (e.isSynthetic) {
658 return;
659 }
660 writeDocumentation(e);
661 writeMetadata(e, '', '\n');
662 writePropertyInducingElement(e);
663 }
664
665 void writeType(DartType type) {
666 if (type is InterfaceType) {
667 buffer.write(type.element.name);
668 if (type.element.typeParameters.isNotEmpty) {
669 writeList('<', '>', type.typeArguments, ', ', writeType);
670 }
671 } else {
672 buffer.write(type.displayName);
673 }
674 }
675
676 void writeType2(DartType type) {
677 writeType(type);
678 buffer.write(' ');
679 }
680
681 void writeTypeParameterElement(TypeParameterElement e) {
682 writeName(e);
683 if (e.bound != null) {
684 buffer.write(' extends ');
685 writeType(e.bound);
686 }
687 }
688
689 void writeTypeParameterElements(List<TypeParameterElement> elements) {
690 writeList('<', '>', elements, ', ', writeTypeParameterElement);
691 }
692
693 void writeUnitElement(CompilationUnitElement e) {
694 if (e.library.definingCompilationUnit != e) {
695 buffer.writeln('-' * 20);
696 buffer.writeln('unit: ${e.source.shortName}');
697 buffer.writeln();
698 }
699 e.functionTypeAliases.forEach(writeFunctionTypeAliasElement);
700 e.enums.forEach(writeClassElement);
701 e.types.forEach(writeClassElement);
702 e.topLevelVariables.forEach(writeTopLevelVariableElement);
703 e.accessors.forEach(writePropertyAccessorElement);
704 e.functions.forEach(writeFunctionElement);
705 }
706
707 void writeUri(UriReferencedElement e, Source source) {
708 String uri = e.uri ?? source.uri.toString();
709 buffer.write('\'$uri\'');
710 if (withOffsets) {
711 buffer.write('(');
712 buffer.write('${e.uriOffset}, ');
713 buffer.write('${e.uriEnd})');
714 buffer.write(')');
715 }
716 }
717 }
718
719 class _Replacement {
720 final int offset;
721 final int end;
722 final String text;
723 _Replacement(this.offset, this.end, this.text);
724 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698