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 |