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 |