| 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 |