Chromium Code Reviews| OLD | NEW |
|---|---|
| (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 } | |
| OLD | NEW |