OLD | NEW |
(Empty) | |
| 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 |
| 3 // BSD-style license that can be found in the LICENSE file. |
| 4 |
| 5 import 'dart:collection'; |
| 6 import 'dart:io' as io; |
| 7 |
| 8 import 'package:analyzer/dart/element/element.dart'; |
| 9 import 'package:analyzer/file_system/file_system.dart' |
| 10 show File, Folder, ResourceProvider, ResourceUriResolver; |
| 11 import 'package:analyzer/file_system/physical_file_system.dart'; |
| 12 import 'package:analyzer/source/package_map_resolver.dart'; |
| 13 import 'package:analyzer/src/context/builder.dart'; |
| 14 import 'package:analyzer/src/dart/sdk/sdk.dart'; |
| 15 import 'package:analyzer/src/generated/engine.dart'; |
| 16 import 'package:analyzer/src/generated/sdk.dart'; |
| 17 import 'package:analyzer/src/generated/source.dart'; |
| 18 import 'package:analyzer/src/generated/source_io.dart'; |
| 19 import 'package:analyzer/src/lint/io.dart'; |
| 20 import 'package:analyzer/src/lint/linter.dart'; |
| 21 import 'package:analyzer/src/lint/project.dart'; |
| 22 import 'package:analyzer/src/lint/registry.dart'; |
| 23 import 'package:analyzer/src/services/lint.dart'; |
| 24 import 'package:cli_util/cli_util.dart' as cli_util; |
| 25 import 'package:package_config/packages.dart' show Packages; |
| 26 import 'package:package_config/packages_file.dart' as pkgfile show parse; |
| 27 import 'package:package_config/src/packages_impl.dart' show MapPackages; |
| 28 import 'package:path/path.dart' as p; |
| 29 import 'package:plugin/manager.dart'; |
| 30 import 'package:plugin/plugin.dart'; |
| 31 |
| 32 Source createSource(Uri sourceUri) { |
| 33 return PhysicalResourceProvider.INSTANCE |
| 34 .getFile(sourceUri.toFilePath()) |
| 35 .createSource(sourceUri); |
| 36 } |
| 37 |
| 38 /// Print the given message and exit with the given [exitCode] |
| 39 void printAndFail(String message, {int exitCode: 15}) { |
| 40 print(message); |
| 41 io.exit(exitCode); |
| 42 } |
| 43 |
| 44 AnalysisOptions _buildAnalyzerOptions(DriverOptions options) { |
| 45 AnalysisOptionsImpl analysisOptions = new AnalysisOptionsImpl(); |
| 46 analysisOptions.strongMode = options.strongMode; |
| 47 analysisOptions.hint = false; |
| 48 analysisOptions.lint = options.enableLints; |
| 49 analysisOptions.generateSdkErrors = options.showSdkWarnings; |
| 50 analysisOptions.enableTiming = options.enableTiming; |
| 51 return analysisOptions; |
| 52 } |
| 53 |
| 54 class AnalysisDriver { |
| 55 /// The sources which have been analyzed so far. This is used to avoid |
| 56 /// analyzing a source more than once, and to compute the total number of |
| 57 /// sources analyzed for statistics. |
| 58 Set<Source> _sourcesAnalyzed = new HashSet<Source>(); |
| 59 |
| 60 final LinterOptions options; |
| 61 |
| 62 AnalysisDriver(this.options) { |
| 63 _processPlugins(); |
| 64 } |
| 65 |
| 66 /// Return the number of sources that have been analyzed so far. |
| 67 int get numSourcesAnalyzed => _sourcesAnalyzed.length; |
| 68 |
| 69 List<UriResolver> get resolvers { |
| 70 // TODO(brianwilkerson) Use the context builder to compute all of the resolv
ers. |
| 71 ResourceProvider resourceProvider = PhysicalResourceProvider.INSTANCE; |
| 72 ContextBuilder builder = new ContextBuilder(resourceProvider, null, null); |
| 73 |
| 74 DartSdk sdk = options.mockSdk ?? |
| 75 new FolderBasedDartSdk( |
| 76 resourceProvider, resourceProvider.getFolder(sdkDir)); |
| 77 |
| 78 List<UriResolver> resolvers = [new DartUriResolver(sdk)]; |
| 79 |
| 80 if (options.packageRootPath != null) { |
| 81 // TODO(brianwilkerson) After 0.30.0 is published, clean up the following. |
| 82 try { |
| 83 // Try to use the post 0.30.0 API. |
| 84 (builder as dynamic).builderOptions.defaultPackagesDirectoryPath = |
| 85 options.packageRootPath; |
| 86 } catch (_) { |
| 87 // If that fails, fall back to the pre 0.30.0 API. |
| 88 (builder as dynamic).defaultPackagesDirectoryPath = |
| 89 options.packageRootPath; |
| 90 } |
| 91 Map<String, List<Folder>> packageMap = |
| 92 builder.convertPackagesToMap(builder.createPackageMap(null)); |
| 93 resolvers.add(new PackageMapUriResolver(resourceProvider, packageMap)); |
| 94 } |
| 95 |
| 96 // File URI resolver must come last so that files inside "/lib" are |
| 97 // are analyzed via "package:" URI's. |
| 98 resolvers.add(new ResourceUriResolver(resourceProvider)); |
| 99 return resolvers; |
| 100 } |
| 101 |
| 102 String get sdkDir { |
| 103 if (options.dartSdkPath != null) { |
| 104 return options.dartSdkPath; |
| 105 } |
| 106 // In case no SDK has been specified, fall back to inferring it |
| 107 // TODO: pass args to cli_util |
| 108 return cli_util.getSdkDir().path; |
| 109 } |
| 110 |
| 111 List<AnalysisErrorInfo> analyze(Iterable<io.File> files) { |
| 112 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext(); |
| 113 context.analysisOptions = _buildAnalyzerOptions(options); |
| 114 registerLinters(context); |
| 115 |
| 116 Packages packages = _getPackageConfig(); |
| 117 |
| 118 context.sourceFactory = new SourceFactory(resolvers, packages); |
| 119 AnalysisEngine.instance.logger = new StdLogger(); |
| 120 |
| 121 List<Source> sources = []; |
| 122 ChangeSet changeSet = new ChangeSet(); |
| 123 for (io.File file in files) { |
| 124 File sourceFile = PhysicalResourceProvider.INSTANCE |
| 125 .getFile(p.normalize(file.absolute.path)); |
| 126 Source source = sourceFile.createSource(); |
| 127 Uri uri = context.sourceFactory.restoreUri(source); |
| 128 if (uri != null) { |
| 129 // Ensure that we analyze the file using its canonical URI (e.g. if |
| 130 // it's in "/lib", analyze it using a "package:" URI). |
| 131 source = sourceFile.createSource(uri); |
| 132 } |
| 133 sources.add(source); |
| 134 changeSet.addedSource(source); |
| 135 } |
| 136 context.applyChanges(changeSet); |
| 137 |
| 138 // Temporary location |
| 139 var project = new DartProject(context, sources); |
| 140 // This will get pushed into the generator (or somewhere comparable) when |
| 141 // we have a proper plugin. |
| 142 Registry.ruleRegistry.forEach((lint) { |
| 143 if (lint is ProjectVisitor) { |
| 144 (lint as ProjectVisitor).visit(project); |
| 145 } |
| 146 }); |
| 147 |
| 148 List<AnalysisErrorInfo> errors = []; |
| 149 |
| 150 for (Source source in sources) { |
| 151 context.computeErrors(source); |
| 152 errors.add(context.getErrors(source)); |
| 153 _sourcesAnalyzed.add(source); |
| 154 } |
| 155 |
| 156 if (options.visitTransitiveClosure) { |
| 157 // In the process of computing errors for all the sources in [sources], |
| 158 // the analyzer has visited the transitive closure of all libraries |
| 159 // referenced by those sources. So now we simply need to visit all |
| 160 // library sources known to the analysis context, and all parts they |
| 161 // refer to. |
| 162 for (Source librarySource in context.librarySources) { |
| 163 for (Source source in _getAllUnitSources(context, librarySource)) { |
| 164 if (!_sourcesAnalyzed.contains(source)) { |
| 165 context.computeErrors(source); |
| 166 errors.add(context.getErrors(source)); |
| 167 _sourcesAnalyzed.add(source); |
| 168 } |
| 169 } |
| 170 } |
| 171 } |
| 172 |
| 173 return errors; |
| 174 } |
| 175 |
| 176 void registerLinters(AnalysisContext context) { |
| 177 if (options.enableLints) { |
| 178 setLints(context, options.enabledLints?.toList(growable: false)); |
| 179 } |
| 180 } |
| 181 |
| 182 /// Yield the sources for all the compilation units constituting |
| 183 /// [librarySource] (including the defining compilation unit). |
| 184 Iterable<Source> _getAllUnitSources( |
| 185 AnalysisContext context, Source librarySource) { |
| 186 List<Source> result = <Source>[librarySource]; |
| 187 result.addAll(context |
| 188 .getLibraryElement(librarySource) |
| 189 .parts |
| 190 .map((CompilationUnitElement e) => e.source)); |
| 191 return result; |
| 192 } |
| 193 |
| 194 Packages _getPackageConfig() { |
| 195 if (options.packageConfigPath != null) { |
| 196 String packageConfigPath = options.packageConfigPath; |
| 197 Uri fileUri = new Uri.file(packageConfigPath); |
| 198 try { |
| 199 io.File configFile = new io.File.fromUri(fileUri).absolute; |
| 200 List<int> bytes = configFile.readAsBytesSync(); |
| 201 Map<String, Uri> map = pkgfile.parse(bytes, configFile.uri); |
| 202 return new MapPackages(map); |
| 203 } catch (e) { |
| 204 printAndFail( |
| 205 'Unable to read package config data from $packageConfigPath: $e'); |
| 206 } |
| 207 } |
| 208 return null; |
| 209 } |
| 210 |
| 211 void _processPlugins() { |
| 212 List<Plugin> plugins = <Plugin>[]; |
| 213 plugins.addAll(AnalysisEngine.instance.requiredPlugins); |
| 214 plugins.add(AnalysisEngine.instance.commandLinePlugin); |
| 215 plugins.add(AnalysisEngine.instance.optionsPlugin); |
| 216 ExtensionManager manager = new ExtensionManager(); |
| 217 manager.processPlugins(plugins); |
| 218 } |
| 219 } |
| 220 |
| 221 class DriverOptions { |
| 222 /// The maximum number of sources for which AST structures should be kept |
| 223 /// in the cache. The default is 512. |
| 224 int cacheSize = 512; |
| 225 |
| 226 /// The path to the dart SDK. |
| 227 String dartSdkPath; |
| 228 |
| 229 /// Whether to show lint warnings. |
| 230 bool enableLints = true; |
| 231 |
| 232 /// Whether to gather timing data during analysis. |
| 233 bool enableTiming = false; |
| 234 |
| 235 /// The path to a `.packages` configuration file |
| 236 String packageConfigPath; |
| 237 |
| 238 /// The path to the package root. |
| 239 String packageRootPath; |
| 240 |
| 241 /// Whether to show SDK warnings. |
| 242 bool showSdkWarnings = false; |
| 243 |
| 244 /// Whether to use Dart's Strong Mode analyzer. |
| 245 bool strongMode = true; |
| 246 |
| 247 /// The mock SDK (to speed up testing) or `null` to use the actual SDK. |
| 248 DartSdk mockSdk; |
| 249 |
| 250 /// Whether to show lints for the transitive closure of imported and exported |
| 251 /// libraries. |
| 252 bool visitTransitiveClosure = false; |
| 253 } |
| 254 |
| 255 /// Prints logging information comments to the [outSink] and error messages to |
| 256 /// [errorSink]. |
| 257 class StdLogger extends Logger { |
| 258 @override |
| 259 void logError(String message, [exception]) => errorSink.writeln(message); |
| 260 @override |
| 261 void logInformation(String message, [exception]) => outSink.writeln(message); |
| 262 } |
OLD | NEW |