| OLD | NEW |
| 1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 // TODO(jmesserly): this file needs to be refactored, it's a port from | 5 // TODO(jmesserly): this file needs to be refactored, it's a port from |
| 6 // package:dev_compiler's tests | 6 // package:dev_compiler's tests |
| 7 library analyzer.test.src.task.strong.strong_test_helper; | 7 library analyzer.test.src.task.strong.strong_test_helper; |
| 8 | 8 |
| 9 import 'package:analyzer/dart/ast/ast.dart'; | 9 import 'package:analyzer/dart/ast/ast.dart'; |
| 10 import 'package:analyzer/dart/ast/token.dart'; | 10 import 'package:analyzer/dart/ast/token.dart'; |
| (...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 new SourceFactory([new DartUriResolver(new MockSdk()), uriResolver]); | 67 new SourceFactory([new DartUriResolver(new MockSdk()), uriResolver]); |
| 68 | 68 |
| 69 // Run the checker on /main.dart. | 69 // Run the checker on /main.dart. |
| 70 Source mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart')); | 70 Source mainSource = uriResolver.resolveAbsolute(new Uri.file('/main.dart')); |
| 71 var initialLibrary = context.resolveCompilationUnit2(mainSource, mainSource); | 71 var initialLibrary = context.resolveCompilationUnit2(mainSource, mainSource); |
| 72 | 72 |
| 73 var collector = new _ErrorCollector(); | 73 var collector = new _ErrorCollector(); |
| 74 | 74 |
| 75 // Extract expectations from the comments in the test files, and | 75 // Extract expectations from the comments in the test files, and |
| 76 // check that all errors we emit are included in the expected map. | 76 // check that all errors we emit are included in the expected map. |
| 77 var allLibraries = reachableLibraries(initialLibrary.element.library); | 77 var allLibraries = _reachableLibraries(initialLibrary.element.library); |
| 78 for (var lib in allLibraries) { | 78 for (var lib in allLibraries) { |
| 79 for (var unit in lib.units) { | 79 for (var unit in lib.units) { |
| 80 var errors = <AnalysisError>[]; | 80 var errors = <AnalysisError>[]; |
| 81 collector.errors = errors; | 81 collector.errors = errors; |
| 82 | 82 |
| 83 var source = unit.source; | 83 var source = unit.source; |
| 84 if (source.uri.scheme == 'dart') continue; | 84 if (source.uri.scheme == 'dart') continue; |
| 85 | 85 |
| 86 var librarySource = context.getLibrariesContaining(source).single; | 86 var librarySource = context.getLibrariesContaining(source).single; |
| 87 var resolved = context.resolveCompilationUnit2(source, librarySource); | 87 var resolved = context.resolveCompilationUnit2(source, librarySource); |
| 88 errors.addAll(context.getErrors(source).errors.where((e) => | 88 errors.addAll(context.getErrors(source).errors.where((e) => |
| 89 e.errorCode != HintCode.UNUSED_LOCAL_VARIABLE && | 89 e.errorCode != HintCode.UNUSED_LOCAL_VARIABLE && |
| 90 // TODO(jmesserly): these are usually intentional dynamic calls. | 90 // TODO(jmesserly): these are usually intentional dynamic calls. |
| 91 e.errorCode.name != 'UNDEFINED_METHOD')); | 91 e.errorCode.name != 'UNDEFINED_METHOD')); |
| 92 | 92 |
| 93 _expectErrors(resolved, errors); | 93 _expectErrors(resolved, errors); |
| 94 } | 94 } |
| 95 } | 95 } |
| 96 } | 96 } |
| 97 | 97 |
| 98 /// Adds a file using [addFile] and calls [check]. | 98 /// Adds a file using [addFile] and calls [check]. |
| 99 void checkFile(String content) { | 99 void checkFile(String content) { |
| 100 addFile(content); | 100 addFile(content); |
| 101 check(); | 101 check(); |
| 102 } | 102 } |
| 103 | 103 |
| 104 SourceSpanWithContext createSpanHelper( | 104 SourceSpanWithContext _createSpanHelper( |
| 105 LineInfo lineInfo, int start, Source source, String content, | 105 LineInfo lineInfo, int start, Source source, String content, |
| 106 {int end}) { | 106 {int end}) { |
| 107 var startLoc = locationForOffset(lineInfo, source.uri, start); | 107 var startLoc = _locationForOffset(lineInfo, source.uri, start); |
| 108 var endLoc = locationForOffset(lineInfo, source.uri, end ?? start); | 108 var endLoc = _locationForOffset(lineInfo, source.uri, end ?? start); |
| 109 | 109 |
| 110 var lineStart = startLoc.offset - startLoc.column; | 110 var lineStart = startLoc.offset - startLoc.column; |
| 111 // Find the end of the line. This is not exposed directly on LineInfo, but | 111 // Find the end of the line. This is not exposed directly on LineInfo, but |
| 112 // we can find it pretty easily. | 112 // we can find it pretty easily. |
| 113 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get | 113 // TODO(jmesserly): for now we do the simple linear scan. Ideally we can get |
| 114 // some help from the LineInfo API. | 114 // some help from the LineInfo API. |
| 115 int lineEnd = endLoc.offset; | 115 int lineEnd = endLoc.offset; |
| 116 int lineNum = lineInfo.getLocation(lineEnd).lineNumber; | 116 int lineNum = lineInfo.getLocation(lineEnd).lineNumber; |
| 117 while (lineEnd < content.length && | 117 while (lineEnd < content.length && |
| 118 lineInfo.getLocation(++lineEnd).lineNumber == lineNum); | 118 lineInfo.getLocation(++lineEnd).lineNumber == lineNum); |
| 119 | 119 |
| 120 if (end == null) { | 120 if (end == null) { |
| 121 end = lineEnd; | 121 end = lineEnd; |
| 122 endLoc = locationForOffset(lineInfo, source.uri, lineEnd); | 122 endLoc = _locationForOffset(lineInfo, source.uri, lineEnd); |
| 123 } | 123 } |
| 124 | 124 |
| 125 var text = content.substring(start, end); | 125 var text = content.substring(start, end); |
| 126 var lineText = content.substring(lineStart, lineEnd); | 126 var lineText = content.substring(lineStart, lineEnd); |
| 127 return new SourceSpanWithContext(startLoc, endLoc, text, lineText); | 127 return new SourceSpanWithContext(startLoc, endLoc, text, lineText); |
| 128 } | 128 } |
| 129 | 129 |
| 130 String _errorCodeName(ErrorCode errorCode) { | 130 String _errorCodeName(ErrorCode errorCode) { |
| 131 var name = errorCode.name; | 131 var name = errorCode.name; |
| 132 final prefix = 'STRONG_MODE_'; | 132 final prefix = 'STRONG_MODE_'; |
| 133 if (name.startsWith(prefix)) { | 133 if (name.startsWith(prefix)) { |
| 134 return name.substring(prefix.length); | 134 return name.substring(prefix.length); |
| 135 } else { | 135 } else { |
| 136 return name; | 136 return name; |
| 137 } | 137 } |
| 138 } | 138 } |
| 139 | 139 |
| 140 initStrongModeTests() { | 140 void initStrongModeTests() { |
| 141 setUp(() { | 141 setUp(() { |
| 142 AnalysisEngine.instance.processRequiredPlugins(); | 142 AnalysisEngine.instance.processRequiredPlugins(); |
| 143 files = new MemoryResourceProvider(); | 143 files = new MemoryResourceProvider(); |
| 144 _checkCalled = false; | 144 _checkCalled = false; |
| 145 }); | 145 }); |
| 146 | 146 |
| 147 tearDown(() { | 147 tearDown(() { |
| 148 // This is a sanity check, in case only addFile is called. | 148 // This is a sanity check, in case only addFile is called. |
| 149 expect(_checkCalled, true, reason: 'must call check() method in test case'); | 149 expect(_checkCalled, true, reason: 'must call check() method in test case'); |
| 150 files = null; | 150 files = null; |
| 151 }); | 151 }); |
| 152 } | 152 } |
| 153 | 153 |
| 154 // TODO(jmesserly): can we reuse the same mock SDK as Analyzer tests? | 154 SourceLocation _locationForOffset(LineInfo lineInfo, Uri uri, int offset) { |
| 155 SourceLocation locationForOffset(LineInfo lineInfo, Uri uri, int offset) { | |
| 156 var loc = lineInfo.getLocation(offset); | 155 var loc = lineInfo.getLocation(offset); |
| 157 return new SourceLocation(offset, | 156 return new SourceLocation(offset, |
| 158 sourceUrl: uri, line: loc.lineNumber - 1, column: loc.columnNumber - 1); | 157 sourceUrl: uri, line: loc.lineNumber - 1, column: loc.columnNumber - 1); |
| 159 } | 158 } |
| 160 | 159 |
| 161 /// Returns all libraries transitively imported or exported from [start]. | 160 /// Returns all libraries transitively imported or exported from [start]. |
| 162 List<LibraryElement> reachableLibraries(LibraryElement start) { | 161 List<LibraryElement> _reachableLibraries(LibraryElement start) { |
| 163 var results = <LibraryElement>[]; | 162 var results = <LibraryElement>[]; |
| 164 var seen = new Set(); | 163 var seen = new Set(); |
| 165 void find(LibraryElement lib) { | 164 void find(LibraryElement lib) { |
| 166 if (seen.contains(lib)) return; | 165 if (seen.contains(lib)) return; |
| 167 seen.add(lib); | 166 seen.add(lib); |
| 168 results.add(lib); | 167 results.add(lib); |
| 169 lib.importedLibraries.forEach(find); | 168 lib.importedLibraries.forEach(find); |
| 170 lib.exportedLibraries.forEach(find); | 169 lib.exportedLibraries.forEach(find); |
| 171 } | 170 } |
| 172 find(start); | 171 find(start); |
| (...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 230 | 229 |
| 231 List<_ErrorExpectation> _findExpectedErrors(Token beginToken) { | 230 List<_ErrorExpectation> _findExpectedErrors(Token beginToken) { |
| 232 var expectedErrors = <_ErrorExpectation>[]; | 231 var expectedErrors = <_ErrorExpectation>[]; |
| 233 | 232 |
| 234 // Collect expectations like "severe:STATIC_TYPE_ERROR" from comment tokens. | 233 // Collect expectations like "severe:STATIC_TYPE_ERROR" from comment tokens. |
| 235 for (Token t = beginToken; t.type != TokenType.EOF; t = t.next) { | 234 for (Token t = beginToken; t.type != TokenType.EOF; t = t.next) { |
| 236 for (CommentToken c = t.precedingComments; c != null; c = c.next) { | 235 for (CommentToken c = t.precedingComments; c != null; c = c.next) { |
| 237 if (c.type == TokenType.MULTI_LINE_COMMENT) { | 236 if (c.type == TokenType.MULTI_LINE_COMMENT) { |
| 238 String value = c.lexeme.substring(2, c.lexeme.length - 2); | 237 String value = c.lexeme.substring(2, c.lexeme.length - 2); |
| 239 if (value.contains(':')) { | 238 if (value.contains(':')) { |
| 240 var offset = c.end; | 239 int offset = t.offset; |
| 241 if (c.next?.type == TokenType.GENERIC_METHOD_TYPE_LIST) { | 240 Token previous = t.previous; |
| 242 offset += 2; | 241 while (previous != null && previous.offset > c.offset) { |
| 242 offset = previous.offset; |
| 243 previous = previous.previous; |
| 243 } | 244 } |
| 244 for (var expectCode in value.split(',')) { | 245 for (var expectCode in value.split(',')) { |
| 245 var expected = _ErrorExpectation.parse(offset, expectCode); | 246 var expected = _ErrorExpectation.parse(offset, expectCode); |
| 246 if (expected != null) { | 247 if (expected != null) { |
| 247 expectedErrors.add(expected); | 248 expectedErrors.add(expected); |
| 248 } | 249 } |
| 249 } | 250 } |
| 250 } | 251 } |
| 251 } | 252 } |
| 252 } | 253 } |
| 253 } | 254 } |
| 254 return expectedErrors; | 255 return expectedErrors; |
| 255 } | 256 } |
| 256 | 257 |
| 257 void _reportFailure( | 258 void _reportFailure( |
| 258 CompilationUnit unit, | 259 CompilationUnit unit, |
| 259 List<_ErrorExpectation> unreported, | 260 List<_ErrorExpectation> unreported, |
| 260 List<AnalysisError> unexpected, | 261 List<AnalysisError> unexpected, |
| 261 Map<_ErrorExpectation, AnalysisError> different) { | 262 Map<_ErrorExpectation, AnalysisError> different) { |
| 262 // Get the source code. This reads the data again, but it's safe because | 263 // Get the source code. This reads the data again, but it's safe because |
| 263 // all tests use memory file system. | 264 // all tests use memory file system. |
| 264 var sourceCode = unit.element.source.contents.data; | 265 var sourceCode = unit.element.source.contents.data; |
| 265 | 266 |
| 266 String formatActualError(AnalysisError error) { | 267 String formatActualError(AnalysisError error) { |
| 267 int offset = error.offset; | 268 int offset = error.offset; |
| 268 int length = error.length; | 269 int length = error.length; |
| 269 var span = createSpanHelper( | 270 var span = _createSpanHelper( |
| 270 unit.lineInfo, offset, unit.element.source, sourceCode, | 271 unit.lineInfo, offset, unit.element.source, sourceCode, |
| 271 end: offset + length); | 272 end: offset + length); |
| 272 var levelName = _actualErrorLevel(error).name.toLowerCase(); | 273 var levelName = _actualErrorLevel(error).name.toLowerCase(); |
| 273 return '@$offset $levelName:${_errorCodeName(error.errorCode)}\n' + | 274 return '@$offset $levelName:${_errorCodeName(error.errorCode)}\n' + |
| 274 span.message(error.message); | 275 span.message(error.message); |
| 275 } | 276 } |
| 276 | 277 |
| 277 String formatExpectedError(_ErrorExpectation error) { | 278 String formatExpectedError(_ErrorExpectation error) { |
| 278 int offset = error.offset; | 279 int offset = error.offset; |
| 279 var span = createSpanHelper( | 280 var span = _createSpanHelper( |
| 280 unit.lineInfo, offset, unit.element.source, sourceCode); | 281 unit.lineInfo, offset, unit.element.source, sourceCode); |
| 281 var levelName = error.level.toString().toLowerCase(); | 282 var levelName = error.level.toString().toLowerCase(); |
| 282 return '@$offset $levelName:${error.typeName}\n' + span.message(''); | 283 return '@$offset $levelName:${error.typeName}\n' + span.message(''); |
| 283 } | 284 } |
| 284 | 285 |
| 285 var message = new StringBuffer(); | 286 var message = new StringBuffer(); |
| 286 if (unreported.isNotEmpty) { | 287 if (unreported.isNotEmpty) { |
| 287 message.writeln('Expected errors that were not reported:'); | 288 message.writeln('Expected errors that were not reported:'); |
| 288 unreported.map(formatExpectedError).forEach(message.writeln); | 289 unreported.map(formatExpectedError).forEach(message.writeln); |
| 289 message.writeln(); | 290 message.writeln(); |
| (...skipping 87 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 377 | 378 |
| 378 @override | 379 @override |
| 379 Source resolveAbsolute(Uri uri, [Uri actualUri]) { | 380 Source resolveAbsolute(Uri uri, [Uri actualUri]) { |
| 380 if (uri.scheme == 'package') { | 381 if (uri.scheme == 'package') { |
| 381 return (provider.getResource('/packages/' + uri.path) as File) | 382 return (provider.getResource('/packages/' + uri.path) as File) |
| 382 .createSource(uri); | 383 .createSource(uri); |
| 383 } | 384 } |
| 384 return super.resolveAbsolute(uri, actualUri); | 385 return super.resolveAbsolute(uri, actualUri); |
| 385 } | 386 } |
| 386 } | 387 } |
| OLD | NEW |