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 library analyzer_cli.src.driver; | 5 library analyzer_cli.src.driver; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:convert'; | 8 import 'dart:convert'; |
9 import 'dart:io'; | 9 import 'dart:io'; |
10 | 10 |
(...skipping 61 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
72 /// The plugins that are defined outside the `analyzer_cli` package. | 72 /// The plugins that are defined outside the `analyzer_cli` package. |
73 List<Plugin> _userDefinedPlugins = <Plugin>[]; | 73 List<Plugin> _userDefinedPlugins = <Plugin>[]; |
74 | 74 |
75 /// Indicates whether the analyzer is running in batch mode. | 75 /// Indicates whether the analyzer is running in batch mode. |
76 bool _isBatch; | 76 bool _isBatch; |
77 | 77 |
78 /// The context that was most recently created by a call to [_analyzeAll], or | 78 /// The context that was most recently created by a call to [_analyzeAll], or |
79 /// `null` if [_analyzeAll] hasn't been called yet. | 79 /// `null` if [_analyzeAll] hasn't been called yet. |
80 AnalysisContext _context; | 80 AnalysisContext _context; |
81 | 81 |
82 /// If [_context] is not `null`, the [CommandLineOptions] that guided its | |
83 /// creation. | |
84 CommandLineOptions _previousOptions; | |
85 | |
86 @override | 82 @override |
87 EmbeddedResolverProvider embeddedUriResolverProvider; | 83 EmbeddedResolverProvider embeddedUriResolverProvider; |
88 | 84 |
89 @override | 85 @override |
90 ResolverProvider packageResolverProvider; | 86 ResolverProvider packageResolverProvider; |
91 | 87 |
92 /// This Driver's current analysis context. | 88 /// This Driver's current analysis context. |
93 /// | 89 /// |
94 /// *Visible for testing.* | 90 /// *Visible for testing.* |
95 AnalysisContext get context => _context; | 91 AnalysisContext get context => _context; |
96 | 92 |
97 @override | 93 @override |
98 void set userDefinedPlugins(List<Plugin> plugins) { | 94 void set userDefinedPlugins(List<Plugin> plugins) { |
99 _userDefinedPlugins = plugins == null ? <Plugin>[] : plugins; | 95 _userDefinedPlugins = plugins == null ? <Plugin>[] : plugins; |
100 } | 96 } |
101 | 97 |
102 @override | 98 @override |
103 void start(List<String> args) { | 99 void start(List<String> args) { |
100 if (_context != null) { | |
101 throw new StateError("start() can only be called once"); | |
Brian Wilkerson
2016/03/18 14:13:17
Start *is* only called once, and it seems unlikely
skybrian
2016/03/18 22:32:44
It's to make it easier for the next reader to tell
| |
102 } | |
104 int startTime = new DateTime.now().millisecondsSinceEpoch; | 103 int startTime = new DateTime.now().millisecondsSinceEpoch; |
105 | 104 |
106 StringUtilities.INTERNER = new MappedInterner(); | 105 StringUtilities.INTERNER = new MappedInterner(); |
107 | 106 |
108 _processPlugins(); | 107 _processPlugins(); |
109 | 108 |
110 // Parse commandline options. | 109 // Parse commandline options. |
111 CommandLineOptions options = CommandLineOptions.parse(args); | 110 CommandLineOptions options = CommandLineOptions.parse(args); |
112 | 111 |
113 // Cache options of interest to inform analysis. | 112 // Cache options of interest to inform analysis. |
(...skipping 13 matching lines...) Expand all Loading... | |
127 }); | 126 }); |
128 } else { | 127 } else { |
129 ErrorSeverity severity = _analyzeAll(options); | 128 ErrorSeverity severity = _analyzeAll(options); |
130 // In case of error propagate exit code. | 129 // In case of error propagate exit code. |
131 if (severity == ErrorSeverity.ERROR) { | 130 if (severity == ErrorSeverity.ERROR) { |
132 exitCode = severity.ordinal; | 131 exitCode = severity.ordinal; |
133 } | 132 } |
134 } | 133 } |
135 | 134 |
136 if (options.perfReport != null) { | 135 if (options.perfReport != null) { |
137 String json = makePerfReport(startTime, currentTimeMillis(), options); | 136 String json = |
137 makePerfReport(startTime, currentTimeMillis(), options, _context); | |
138 new File(options.perfReport).writeAsStringSync(json); | 138 new File(options.perfReport).writeAsStringSync(json); |
139 } | 139 } |
140 } | 140 } |
141 | 141 |
142 ErrorSeverity _analyzeAll(CommandLineOptions options) { | 142 ErrorSeverity _analyzeAll(CommandLineOptions options) { |
143 return _analyzeAllTag.makeCurrentWhile(() { | 143 return _analyzeAllTag.makeCurrentWhile(() { |
144 return _analyzeAllImpl(options); | 144 return _analyzeAllImpl(options); |
145 }); | 145 }); |
146 } | 146 } |
147 | 147 |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
222 return allResult; | 222 return allResult; |
223 } | 223 } |
224 | 224 |
225 /// Perform package analysis according to the given [options]. | 225 /// Perform package analysis according to the given [options]. |
226 ErrorSeverity _analyzePackage(CommandLineOptions options) { | 226 ErrorSeverity _analyzePackage(CommandLineOptions options) { |
227 return _analyzeAllTag.makeCurrentWhile(() { | 227 return _analyzeAllTag.makeCurrentWhile(() { |
228 return new PackageAnalyzer(options).analyze(); | 228 return new PackageAnalyzer(options).analyze(); |
229 }); | 229 }); |
230 } | 230 } |
231 | 231 |
232 /// Determine whether the context created during a previous call to | |
233 /// [_analyzeAll] can be re-used in order to analyze using [options]. | |
234 bool _canContextBeReused(CommandLineOptions options) { | |
235 // TODO(paulberry): add a command-line option that disables context re-use. | |
236 if (_context == null) { | |
237 return false; | |
238 } | |
239 if (options.packageRootPath != _previousOptions.packageRootPath) { | |
240 return false; | |
241 } | |
242 if (options.packageConfigPath != _previousOptions.packageConfigPath) { | |
243 return false; | |
244 } | |
245 if (!_equalMaps( | |
246 options.definedVariables, _previousOptions.definedVariables)) { | |
247 return false; | |
248 } | |
249 if (options.log != _previousOptions.log) { | |
250 return false; | |
251 } | |
252 if (options.disableHints != _previousOptions.disableHints) { | |
253 return false; | |
254 } | |
255 if (options.enableStrictCallChecks != | |
256 _previousOptions.enableStrictCallChecks) { | |
257 return false; | |
258 } | |
259 if (options.showPackageWarnings != _previousOptions.showPackageWarnings) { | |
260 return false; | |
261 } | |
262 if (options.showSdkWarnings != _previousOptions.showSdkWarnings) { | |
263 return false; | |
264 } | |
265 if (options.lints != _previousOptions.lints) { | |
266 return false; | |
267 } | |
268 if (options.strongMode != _previousOptions.strongMode) { | |
269 return false; | |
270 } | |
271 if (options.enableSuperMixins != _previousOptions.enableSuperMixins) { | |
272 return false; | |
273 } | |
274 if (options.enableConditionalDirectives != | |
275 _previousOptions.enableConditionalDirectives) { | |
276 return false; | |
277 } | |
278 return true; | |
279 } | |
280 | |
281 /// Decide on the appropriate policy for which files need to be fully parsed | 232 /// Decide on the appropriate policy for which files need to be fully parsed |
282 /// and which files need to be diet parsed, based on [options], and return an | 233 /// and which files need to be diet parsed, based on [options], and return an |
283 /// [AnalyzeFunctionBodiesPredicate] that implements this policy. | 234 /// [AnalyzeFunctionBodiesPredicate] that implements this policy. |
284 AnalyzeFunctionBodiesPredicate _chooseDietParsingPolicy( | 235 AnalyzeFunctionBodiesPredicate _chooseDietParsingPolicy( |
285 CommandLineOptions options) { | 236 CommandLineOptions options) { |
286 if (_isBatch) { | 237 if (_isBatch) { |
287 // As analyzer is currently implemented, once a file has been diet | 238 // As analyzer is currently implemented, once a file has been diet |
288 // parsed, it can't easily be un-diet parsed without creating a brand new | 239 // parsed, it can't easily be un-diet parsed without creating a brand new |
289 // context and losing caching. In batch mode, we can't predict which | 240 // context and losing caching. In batch mode, we can't predict which |
290 // files we'll need to generate errors and warnings for in the future, so | 241 // files we'll need to generate errors and warnings for in the future, so |
291 // we can't safely diet parse anything. | 242 // we can't safely diet parse anything. |
292 return (Source source) => true; | 243 return (Source source) => true; |
293 } | 244 } |
294 | 245 |
295 // Determine the set of packages requiring a full parse. Use null to | |
296 // represent the case where all packages require a full parse. | |
297 Set<String> packagesRequiringFullParse; | |
298 if (options.showPackageWarnings) { | |
299 // We are showing warnings from all packages so all packages require a | |
300 // full parse. | |
301 packagesRequiringFullParse = null; | |
302 } else { | |
303 // We aren't showing warnings for dependent packages, but we may still | |
304 // need to show warnings for "self" packages, so we need to do a full | |
305 // parse in any package containing files mentioned on the command line. | |
306 // TODO(paulberry): implement this. As a temporary workaround, we're | |
307 // fully parsing all packages. | |
308 packagesRequiringFullParse = null; | |
309 } | |
310 return (Source source) { | 246 return (Source source) { |
311 if (options.sourceFiles.contains(source.fullName)) { | 247 if (options.sourceFiles.contains(source.fullName)) { |
312 return true; | 248 return true; |
313 } else if (source.uri.scheme == 'dart') { | 249 } else if (source.uri.scheme == 'dart') { |
314 return options.showSdkWarnings; | 250 return options.showSdkWarnings; |
315 } else if (source.uri.scheme == 'package') { | |
316 if (packagesRequiringFullParse == null) { | |
317 return true; | |
318 } else if (source.uri.pathSegments.length == 0) { | |
319 // We should never see a URI like this, but fully parse it to be | |
320 // safe. | |
321 return true; | |
322 } else { | |
323 return packagesRequiringFullParse | |
324 .contains(source.uri.pathSegments[0]); | |
325 } | |
326 } else { | 251 } else { |
252 // TODO(paulberry): diet parse package: imports where we don't want | |
253 // diagnostics. (Full parse is still needed for "self" packages.) | |
327 return true; | 254 return true; |
328 } | 255 } |
329 }; | 256 }; |
330 } | 257 } |
331 | 258 |
332 /// Decide on the appropriate method for resolving URIs based on the given | 259 /// Decide on the appropriate method for resolving URIs based on the given |
333 /// [options] and [customUrlMappings] settings, and return a | 260 /// [options] and [customUrlMappings] settings, and return a |
334 /// [SourceFactory] that has been configured accordingly. | 261 /// [SourceFactory] that has been configured accordingly. |
335 SourceFactory _chooseUriResolutionPolicy( | 262 SourceFactory _chooseUriResolutionPolicy( |
336 CommandLineOptions options, EmbedderYamlLocator yamlLocator) { | 263 CommandLineOptions options, EmbedderYamlLocator yamlLocator) { |
(...skipping 128 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
465 Uri uri = _context.sourceFactory.restoreUri(source); | 392 Uri uri = _context.sourceFactory.restoreUri(source); |
466 if (uri == null) { | 393 if (uri == null) { |
467 return source; | 394 return source; |
468 } | 395 } |
469 return new FileBasedSource(sourceFile, uri); | 396 return new FileBasedSource(sourceFile, uri); |
470 } | 397 } |
471 | 398 |
472 /// Create an analysis context that is prepared to analyze sources according | 399 /// Create an analysis context that is prepared to analyze sources according |
473 /// to the given [options], and store it in [_context]. | 400 /// to the given [options], and store it in [_context]. |
474 void _createAnalysisContext(CommandLineOptions options) { | 401 void _createAnalysisContext(CommandLineOptions options) { |
475 if (_canContextBeReused(options)) { | 402 assert(_context == null); |
476 return; | 403 _context = AnalysisEngine.instance.createAnalysisContext(); |
477 } | |
478 _previousOptions = options; | |
479 | |
480 // Create a context. | |
481 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext(); | |
482 _context = context; | |
483 | 404 |
484 // Choose a package resolution policy and a diet parsing policy based on | 405 // Choose a package resolution policy and a diet parsing policy based on |
485 // the command-line options. | 406 // the command-line options. |
486 SourceFactory sourceFactory = _chooseUriResolutionPolicy( | 407 SourceFactory sourceFactory = _chooseUriResolutionPolicy( |
487 options, (context as InternalAnalysisContext).embedderYamlLocator); | 408 options, (_context as InternalAnalysisContext).embedderYamlLocator); |
488 AnalyzeFunctionBodiesPredicate dietParsingPolicy = | 409 AnalyzeFunctionBodiesPredicate dietParsingPolicy = |
489 _chooseDietParsingPolicy(options); | 410 _chooseDietParsingPolicy(options); |
490 | 411 |
491 context.sourceFactory = sourceFactory; | 412 _context.sourceFactory = sourceFactory; |
492 | 413 |
493 setAnalysisContextOptions(_context, options, | 414 setAnalysisContextOptions(_context, options, |
494 (AnalysisOptionsImpl contextOptions) { | 415 (AnalysisOptionsImpl contextOptions) { |
495 contextOptions.analyzeFunctionBodiesPredicate = dietParsingPolicy; | 416 contextOptions.analyzeFunctionBodiesPredicate = dietParsingPolicy; |
496 }); | 417 }); |
497 } | 418 } |
498 | 419 |
499 /// Return discovered packagespec, or `null` if none is found. | 420 /// Return discovered packagespec, or `null` if none is found. |
500 Packages _discoverPackagespec(Uri root) { | 421 Packages _discoverPackagespec(Uri root) { |
501 try { | 422 try { |
(...skipping 90 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
592 configureContextOptions(contextOptions); | 513 configureContextOptions(contextOptions); |
593 | 514 |
594 // Set context options. | 515 // Set context options. |
595 context.analysisOptions = contextOptions; | 516 context.analysisOptions = contextOptions; |
596 context.sourceFactory.dartSdk.context.analysisOptions = contextOptions; | 517 context.sourceFactory.dartSdk.context.analysisOptions = contextOptions; |
597 | 518 |
598 // Process analysis options file (and notify all interested parties). | 519 // Process analysis options file (and notify all interested parties). |
599 _processAnalysisOptions(context, options); | 520 _processAnalysisOptions(context, options); |
600 } | 521 } |
601 | 522 |
602 /// Perform a deep comparison of two string maps. | |
603 static bool _equalMaps(Map<String, String> m1, Map<String, String> m2) { | |
604 if (m1.length != m2.length) { | |
605 return false; | |
606 } | |
607 for (String key in m1.keys) { | |
608 if (!m2.containsKey(key) || m1[key] != m2[key]) { | |
609 return false; | |
610 } | |
611 } | |
612 return true; | |
613 } | |
614 | |
615 static fileSystem.File _getOptionsFile(CommandLineOptions options) { | 523 static fileSystem.File _getOptionsFile(CommandLineOptions options) { |
616 fileSystem.File file; | 524 fileSystem.File file; |
617 String filePath = options.analysisOptionsFile; | 525 String filePath = options.analysisOptionsFile; |
618 if (filePath != null) { | 526 if (filePath != null) { |
619 file = PhysicalResourceProvider.INSTANCE.getFile(filePath); | 527 file = PhysicalResourceProvider.INSTANCE.getFile(filePath); |
620 if (!file.exists) { | 528 if (!file.exists) { |
621 printAndFail('Options file not found: $filePath', | 529 printAndFail('Options file not found: $filePath', |
622 exitCode: ErrorSeverity.ERROR.ordinal); | 530 exitCode: ErrorSeverity.ERROR.ordinal); |
623 } | 531 } |
624 } else { | 532 } else { |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
742 for (var package in packages) { | 650 for (var package in packages) { |
743 var packageName = path.basename(package.path); | 651 var packageName = path.basename(package.path); |
744 var realPath = package.resolveSymbolicLinksSync(); | 652 var realPath = package.resolveSymbolicLinksSync(); |
745 result[packageName] = [ | 653 result[packageName] = [ |
746 PhysicalResourceProvider.INSTANCE.getFolder(realPath) | 654 PhysicalResourceProvider.INSTANCE.getFolder(realPath) |
747 ]; | 655 ]; |
748 } | 656 } |
749 return result; | 657 return result; |
750 } | 658 } |
751 } | 659 } |
OLD | NEW |