OLD | NEW |
(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:async'; |
| 6 import 'dart:convert'; |
| 7 import 'dart:io'; |
| 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/element/element.dart'; |
| 11 import 'package:analyzer/dart/element/type.dart'; |
| 12 import 'package:analyzer/src/dart/analysis/driver.dart'; |
| 13 import 'package:analyzer/src/generated/engine.dart' show AnalysisOptionsImpl; |
| 14 import 'package:analyzer/src/generated/parser.dart'; |
| 15 import 'package:analyzer/src/generated/resolver.dart'; |
| 16 import 'package:analyzer/src/generated/scanner.dart'; |
| 17 import 'package:analyzer/src/generated/source.dart'; |
| 18 import 'package:analyzer/src/generated/utilities_dart.dart'; |
| 19 import 'package:front_end/src/base/instrumentation.dart' as fasta; |
| 20 import 'package:front_end/src/fasta/compiler_context.dart' as fasta; |
| 21 import 'package:front_end/src/fasta/testing/validating_instrumentation.dart' |
| 22 as fasta; |
| 23 import 'package:front_end/src/fasta/util/relativize.dart' show relativizeUri; |
| 24 import 'package:kernel/kernel.dart' as fasta; |
| 25 import 'package:path/path.dart' as pathos; |
| 26 import 'package:test/test.dart'; |
| 27 |
| 28 import '../../dart/analysis/base.dart'; |
| 29 |
| 30 /// Set this to `true` to cause expectation comments to be updated. |
| 31 const bool fixProblems = false; |
| 32 |
| 33 class ElementNamer { |
| 34 final ConstructorElement currentFactoryConstructor; |
| 35 |
| 36 ElementNamer(this.currentFactoryConstructor); |
| 37 |
| 38 void appendElementName(StringBuffer buffer, Element element) { |
| 39 // Synthetic FunctionElement(s) don't have a name or enclosing library. |
| 40 if (element.isSynthetic && element is FunctionElement) { |
| 41 return; |
| 42 } |
| 43 |
| 44 var enclosing = element.enclosingElement; |
| 45 if (enclosing is CompilationUnitElement) { |
| 46 enclosing = enclosing.enclosingElement; |
| 47 } else if (enclosing is ClassElement && |
| 48 currentFactoryConstructor != null && |
| 49 identical(enclosing, currentFactoryConstructor.enclosingElement) && |
| 50 element is TypeParameterElement) { |
| 51 enclosing = currentFactoryConstructor; |
| 52 } |
| 53 if (enclosing != null) { |
| 54 if (enclosing is LibraryElement && |
| 55 (enclosing.name == 'dart.core' || |
| 56 enclosing.name == 'dart.async' || |
| 57 enclosing.name == 'test')) { |
| 58 // For brevity, omit library name |
| 59 } else { |
| 60 appendElementName(buffer, enclosing); |
| 61 buffer.write('::'); |
| 62 } |
| 63 } |
| 64 |
| 65 String name = element.name ?? ''; |
| 66 if (element is ConstructorElement && name == '') { |
| 67 name = '•'; |
| 68 } else if (name.endsWith('=') && |
| 69 element is PropertyAccessorElement && |
| 70 element.isSetter) { |
| 71 name = name.substring(0, name.length - 1); |
| 72 } |
| 73 buffer.write(name); |
| 74 } |
| 75 } |
| 76 |
| 77 /// Instance of [InstrumentationValue] describing an [ExecutableElement]. |
| 78 class InstrumentationValueForExecutableElement |
| 79 extends fasta.InstrumentationValue { |
| 80 final ExecutableElement element; |
| 81 final ElementNamer elementNamer; |
| 82 |
| 83 InstrumentationValueForExecutableElement(this.element, this.elementNamer); |
| 84 |
| 85 @override |
| 86 String toString() { |
| 87 StringBuffer buffer = new StringBuffer(); |
| 88 elementNamer.appendElementName(buffer, element); |
| 89 return buffer.toString(); |
| 90 } |
| 91 } |
| 92 |
| 93 /** |
| 94 * Instance of [InstrumentationValue] describing a [DartType]. |
| 95 */ |
| 96 class InstrumentationValueForType extends fasta.InstrumentationValue { |
| 97 final DartType type; |
| 98 final ElementNamer elementNamer; |
| 99 |
| 100 InstrumentationValueForType(this.type, this.elementNamer); |
| 101 |
| 102 @override |
| 103 String toString() { |
| 104 StringBuffer buffer = new StringBuffer(); |
| 105 _appendType(buffer, type); |
| 106 return buffer.toString(); |
| 107 } |
| 108 |
| 109 void _appendList<T>(StringBuffer buffer, String open, String close, |
| 110 List<T> items, String separator, writeItem(T item), |
| 111 {bool includeEmpty: false}) { |
| 112 if (!includeEmpty && items.isEmpty) { |
| 113 return; |
| 114 } |
| 115 buffer.write(open); |
| 116 bool first = true; |
| 117 for (T item in items) { |
| 118 if (!first) { |
| 119 buffer.write(separator); |
| 120 } |
| 121 writeItem(item); |
| 122 first = false; |
| 123 } |
| 124 buffer.write(close); |
| 125 } |
| 126 |
| 127 void _appendParameters( |
| 128 StringBuffer buffer, List<ParameterElement> parameters) { |
| 129 buffer.write('('); |
| 130 bool first = true; |
| 131 ParameterKind lastKind = ParameterKind.REQUIRED; |
| 132 for (var parameter in parameters) { |
| 133 if (!first) { |
| 134 buffer.write(', '); |
| 135 } |
| 136 if (lastKind != parameter.parameterKind) { |
| 137 if (parameter.parameterKind == ParameterKind.POSITIONAL) { |
| 138 buffer.write('['); |
| 139 } else if (parameter.parameterKind == ParameterKind.NAMED) { |
| 140 buffer.write('{'); |
| 141 } |
| 142 } |
| 143 if (parameter.parameterKind == ParameterKind.NAMED) { |
| 144 buffer.write(parameter.name); |
| 145 buffer.write(': '); |
| 146 } |
| 147 _appendType(buffer, parameter.type); |
| 148 lastKind = parameter.parameterKind; |
| 149 first = false; |
| 150 } |
| 151 if (lastKind == ParameterKind.POSITIONAL) { |
| 152 buffer.write(']'); |
| 153 } else if (lastKind == ParameterKind.NAMED) { |
| 154 buffer.write('}'); |
| 155 } |
| 156 buffer.write(')'); |
| 157 } |
| 158 |
| 159 void _appendType(StringBuffer buffer, DartType type) { |
| 160 if (type is FunctionType) { |
| 161 if (type.typeFormals.isNotEmpty) { |
| 162 _appendTypeFormals(buffer, type.typeFormals); |
| 163 } |
| 164 _appendParameters(buffer, type.parameters); |
| 165 buffer.write(' -> '); |
| 166 _appendType(buffer, type.returnType); |
| 167 } else if (type is InterfaceType) { |
| 168 ClassElement element = type.element; |
| 169 elementNamer.appendElementName(buffer, element); |
| 170 _appendTypeArguments(buffer, type.typeArguments); |
| 171 } else if (type.isBottom) { |
| 172 buffer.write('<BottomType>'); |
| 173 } else if (type is TypeParameterType) { |
| 174 elementNamer.appendElementName(buffer, type.element); |
| 175 } else { |
| 176 buffer.write(type.toString()); |
| 177 } |
| 178 } |
| 179 |
| 180 void _appendTypeArguments(StringBuffer buffer, List<DartType> typeArguments) { |
| 181 _appendList<DartType>(buffer, '<', '>', typeArguments, ', ', |
| 182 (type) => _appendType(buffer, type)); |
| 183 } |
| 184 |
| 185 void _appendTypeFormals( |
| 186 StringBuffer buffer, List<TypeParameterElement> typeFormals) { |
| 187 _appendList<TypeParameterElement>(buffer, '<', '>', typeFormals, ', ', |
| 188 (formal) { |
| 189 buffer.write(formal.name); |
| 190 buffer.write(' extends '); |
| 191 if (formal.bound == null) { |
| 192 buffer.write('Object'); |
| 193 } else { |
| 194 _appendType(buffer, formal.bound); |
| 195 } |
| 196 }); |
| 197 } |
| 198 } |
| 199 |
| 200 /** |
| 201 * Instance of [InstrumentationValue] describing a list of [DartType]s. |
| 202 */ |
| 203 class InstrumentationValueForTypeArgs extends fasta.InstrumentationValue { |
| 204 final List<DartType> types; |
| 205 final ElementNamer elementNamer; |
| 206 |
| 207 const InstrumentationValueForTypeArgs(this.types, this.elementNamer); |
| 208 |
| 209 @override |
| 210 String toString() => types |
| 211 .map((type) => |
| 212 new InstrumentationValueForType(type, elementNamer).toString()) |
| 213 .join(', '); |
| 214 } |
| 215 |
| 216 class _FrontEndInferenceTest extends BaseAnalysisDriverTest { |
| 217 final RunFrontEndTest _frontEndTestRunner; |
| 218 |
| 219 _FrontEndInferenceTest(this._frontEndTestRunner); |
| 220 |
| 221 @override |
| 222 AnalysisOptionsImpl createAnalysisOptions() => |
| 223 super.createAnalysisOptions()..enableAssertInitializer = true; |
| 224 |
| 225 Future<String> runTest(String path, String code) { |
| 226 return fasta.CompilerContext.runWithDefaultOptions((_) async { |
| 227 Uri uri = provider.pathContext.toUri(path); |
| 228 |
| 229 List<int> lineStarts = new LineInfo.fromContent(code).lineStarts; |
| 230 fasta.CompilerContext.current.uriToSource[relativizeUri(uri).toString()] = |
| 231 new fasta.Source(lineStarts, UTF8.encode(code)); |
| 232 |
| 233 var validation = new fasta.ValidatingInstrumentation(); |
| 234 await validation.loadExpectations(uri); |
| 235 |
| 236 _addFileAndImports(path, code); |
| 237 |
| 238 AnalysisResult result = await driver.getResult(path); |
| 239 _frontEndTestRunner.visitUnit( |
| 240 result.typeProvider, result.unit, validation, uri); |
| 241 |
| 242 validation.finish(); |
| 243 |
| 244 if (validation.hasProblems) { |
| 245 if (fixProblems) { |
| 246 validation.fixSource(uri, true); |
| 247 return null; |
| 248 } else { |
| 249 return validation.problemsAsString; |
| 250 } |
| 251 } else { |
| 252 return null; |
| 253 } |
| 254 }); |
| 255 } |
| 256 |
| 257 void _addFileAndImports(String path, String code) { |
| 258 provider.newFile(path, code); |
| 259 var source = null; |
| 260 var analysisErrorListener = null; |
| 261 var scanner = new Scanner( |
| 262 source, new CharSequenceReader(code), analysisErrorListener); |
| 263 var token = scanner.tokenize(); |
| 264 var compilationUnit = |
| 265 new Parser(source, analysisErrorListener).parseDirectives(token); |
| 266 for (var directive in compilationUnit.directives) { |
| 267 if (directive is UriBasedDirective) { |
| 268 Uri uri = Uri.parse(directive.uri.stringValue); |
| 269 if (uri.scheme == 'dart') { |
| 270 // Ignore these--they should be in the mock SDK. |
| 271 } else if (uri.scheme == '') { |
| 272 var pathSegments = uri.pathSegments; |
| 273 // For these tests we don't support any directory traversal; we just |
| 274 // assume the URI is the name of a file in the same directory as all |
| 275 // the other tests. |
| 276 if (pathSegments.length != 1) fail('URI too complex: $uri'); |
| 277 var referencedPath = |
| 278 pathos.join(pathos.dirname(path), pathSegments[0]); |
| 279 if (!provider.getFile(referencedPath).exists) { |
| 280 var referencedCode = new File(referencedPath).readAsStringSync(); |
| 281 _addFileAndImports(referencedPath, referencedCode); |
| 282 } |
| 283 } |
| 284 } |
| 285 } |
| 286 } |
| 287 } |
| 288 |
| 289 abstract class RunFrontEndTest { |
| 290 String get testSubdir; |
| 291 |
| 292 test_run() async { |
| 293 String pkgPath = _findPkgRoot(); |
| 294 String fePath = pathos.join(pkgPath, 'front_end', 'testcases', testSubdir); |
| 295 List<File> dartFiles = new Directory(fePath) |
| 296 .listSync() |
| 297 .where((entry) => entry is File && entry.path.endsWith('.dart')) |
| 298 .map((entry) => entry as File) |
| 299 .toList(); |
| 300 |
| 301 var allProblems = new StringBuffer(); |
| 302 for (File file in dartFiles) { |
| 303 var test = new _FrontEndInferenceTest(this); |
| 304 await test.setUp(); |
| 305 try { |
| 306 String code = file.readAsStringSync(); |
| 307 String problems = await test.runTest(file.path, code); |
| 308 if (problems != null) { |
| 309 allProblems.writeln(problems); |
| 310 } |
| 311 } finally { |
| 312 await test.tearDown(); |
| 313 } |
| 314 } |
| 315 if (allProblems.isNotEmpty) { |
| 316 fail(allProblems.toString()); |
| 317 } |
| 318 } |
| 319 |
| 320 /** |
| 321 * Expects that the [Platform.script] is a test inside of `pkg/analyzer/test` |
| 322 * folder, and return the absolute path of the `pkg` folder. |
| 323 */ |
| 324 String _findPkgRoot() { |
| 325 String scriptPath = pathos.fromUri(Platform.script); |
| 326 List<String> parts = pathos.split(scriptPath); |
| 327 for (int i = 0; i < parts.length - 2; i++) { |
| 328 if (parts[i] == 'pkg' && |
| 329 parts[i + 1] == 'analyzer' && |
| 330 parts[i + 2] == 'test') { |
| 331 return pathos.joinAll(parts.sublist(0, i + 1)); |
| 332 } |
| 333 } |
| 334 throw new StateError('Unable to find sdk/pkg/ in $scriptPath'); |
| 335 } |
| 336 |
| 337 void visitUnit(TypeProvider typeProvider, CompilationUnit unit, |
| 338 fasta.ValidatingInstrumentation validation, Uri uri); |
| 339 } |
OLD | NEW |