mirror of
https://github.com/dart-lang/sdk
synced 2024-09-05 00:13:50 +00:00
analyzer_cli
move to SDK.
Tracking bug: https://github.com/dart-lang/sdk/issues/24731 Note, dartium build changes are in a separate CL: https://codereview.chromium.org/1453413006/ Some tests had to be disabled for want of mockito in the SDK; tracking their reimplementation is here: https://github.com/dart-lang/sdk/issues/24994 BUG=24731 R=whesse@google.com Review URL: https://codereview.chromium.org/1459683003 .
This commit is contained in:
parent
aaf37ad035
commit
9ae1265ff5
3
DEPS
3
DEPS
|
@ -38,7 +38,6 @@ vars = {
|
|||
|
||||
# Revisions of /third_party/* dependencies.
|
||||
"7zip_rev" : "@19997",
|
||||
"analyzer_cli_rev" : "@c93a3d2d1c7715a6c7a067ebdbc9ba716b865226",
|
||||
"args_tag": "@0.13.0",
|
||||
"async_tag": "@1.2.0",
|
||||
"barback_tag" : "@0.15.2+7",
|
||||
|
@ -172,8 +171,6 @@ deps = {
|
|||
(Var("github_mirror") % "dart-services") +
|
||||
Var("dart_services_rev"),
|
||||
|
||||
Var("dart_root") + "/third_party/pkg/analyzer_cli":
|
||||
(Var("github_mirror") % "analyzer_cli") + Var("analyzer_cli_rev"),
|
||||
Var("dart_root") + "/third_party/pkg/args":
|
||||
(Var("github_mirror") % "args") + Var("args_tag"),
|
||||
Var("dart_root") + "/third_party/pkg/async":
|
||||
|
|
11
pkg/analyzer_cli/.gitignore
vendored
Normal file
11
pkg/analyzer_cli/.gitignore
vendored
Normal file
|
@ -0,0 +1,11 @@
|
|||
.buildlog
|
||||
.DS_Store
|
||||
.idea
|
||||
/.packages
|
||||
.project
|
||||
.pub/
|
||||
.settings/
|
||||
analyzer_cli.iml
|
||||
build/
|
||||
packages
|
||||
pubspec.lock
|
16
pkg/analyzer_cli/CHANGELOG.md
Normal file
16
pkg/analyzer_cli/CHANGELOG.md
Normal file
|
@ -0,0 +1,16 @@
|
|||
# Changelog
|
||||
|
||||
## 1.1.1
|
||||
|
||||
- Removed outmoded `--url-mapping` command line option.
|
||||
|
||||
## 1.1.0
|
||||
|
||||
- New `--lints` command line option.
|
||||
|
||||
## 0.0.
|
||||
|
||||
|
||||
## 0.0.1
|
||||
|
||||
- Initial version
|
87
pkg/analyzer_cli/README.md
Normal file
87
pkg/analyzer_cli/README.md
Normal file
|
@ -0,0 +1,87 @@
|
|||
# dartanalyzer
|
||||
|
||||
Use _dartanalyzer_ to statically analyze your code at the command line,
|
||||
checking for errors and warnings that are specified in the
|
||||
[Dart Language Specification](https://www.dartlang.org/docs/spec/).
|
||||
DartPad, code editors, and IDEs such as WebStorm use the same
|
||||
analysis engine that dartanalyzer uses.
|
||||
|
||||
If you want to _contribute_ to the dartanalyzer project, see the
|
||||
[contributor docs](https://github.com/dart-lang/analyzer_cli/blob/master/CONTRIBUTOR.md).
|
||||
This page contains information about _using_ the dartanalyzer command-line tool.
|
||||
|
||||
## Basic usage
|
||||
|
||||
Run the analyzer from the top directory of the package.
|
||||
Here's an example of testing a Dart file.
|
||||
|
||||
```
|
||||
dartanalyzer bin/test.dart
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
The following are the most commonly used options for dartanalyzer:
|
||||
|
||||
* `--packages=`<br>
|
||||
Specify the path to the package resolution configuration file.
|
||||
For more information see
|
||||
[Package Resolution Configuration File](https://github.com/lrhn/dep-pkgspec/blob/master/DEP-pkgspec.md).
|
||||
This option cannot be used with `--package-root`.
|
||||
|
||||
* `--package-warnings`<br>
|
||||
Show warnings not only for code in the specified .dart file and
|
||||
others in its library, but also for libraries imported with `package:`.
|
||||
|
||||
* `--options=`<br>
|
||||
Specify the path to an analysis options file.
|
||||
|
||||
* `--lints`<br>
|
||||
Show the results from the linter.
|
||||
|
||||
* `--no-hints`<br>
|
||||
Don't show hints for improving the code.
|
||||
|
||||
* `--ignore-unrecognized-flags`<br>
|
||||
Rather than printing the help message,
|
||||
ignore any unrecognized command-line flags.
|
||||
|
||||
* `--version`<br>
|
||||
Show the analyzer version.
|
||||
|
||||
* `-h` _or_ `--help`<br>
|
||||
Show all the command-line options.
|
||||
|
||||
The following are advanced options to use with dartanalyzer:
|
||||
|
||||
* `-b` _or_ `--batch`<br>
|
||||
Run in batch mode.
|
||||
|
||||
* `--dart-sdk=`<br>
|
||||
Specify the directory that contains the Dart SDK.
|
||||
|
||||
* `--fatal-warnings`<br>
|
||||
Except for type warnings, treat warnings as fatal.
|
||||
|
||||
* `--format=machine`<br>
|
||||
Produce output in a format suitable for parsing.
|
||||
|
||||
* `--url-mapping=libraryUri,/path/to/library.dart`<br>
|
||||
Tells the analyzer to use the specified library as the source
|
||||
for that particular import.
|
||||
|
||||
The following options are deprecated:
|
||||
|
||||
* `-p` _or_ `--package-root=`<br>
|
||||
**Deprecated.** Specify the directory to search for any libraries that are
|
||||
imported using `package:`. _This option is replaced as of Dart 1.12 with
|
||||
`--packages`._
|
||||
|
||||
* `--machine`<br>
|
||||
**Deprecated.** Replaced by `--format`.
|
||||
|
||||
* `--show-package-warnings`<br>
|
||||
**Deprecated.** Replaced by `--package-warnings`.
|
||||
|
||||
* `--show-sdk-warnings`<br>
|
||||
**Deprecated.** Replaced by `--warnings`.
|
16
pkg/analyzer_cli/bin/analyzer.dart
Normal file
16
pkg/analyzer_cli/bin/analyzer.dart
Normal file
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env dart
|
||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
|
||||
|
||||
|
||||
|
||||
/// The entry point for the analyzer.
|
||||
void main(List<String> args) {
|
||||
var starter = new Driver();
|
||||
starter.start(args);
|
||||
|
||||
}
|
303
pkg/analyzer_cli/lib/src/analyzer_impl.dart
Normal file
303
pkg/analyzer_cli/lib/src/analyzer_impl.dart
Normal file
|
@ -0,0 +1,303 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.src.analyzer_impl;
|
||||
|
||||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/src/generated/element.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/java_engine.dart';
|
||||
import 'package:analyzer/src/generated/java_io.dart';
|
||||
import 'package:analyzer/src/generated/sdk_io.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/source_io.dart';
|
||||
import 'package:analyzer/src/generated/utilities_general.dart';
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:analyzer_cli/src/error_formatter.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
|
||||
DirectoryBasedDartSdk sdk;
|
||||
|
||||
/// The maximum number of sources for which AST structures should be kept in the cache.
|
||||
const int _maxCacheSize = 512;
|
||||
|
||||
int currentTimeMillis() => new DateTime.now().millisecondsSinceEpoch;
|
||||
|
||||
/// Analyzes single library [File].
|
||||
class AnalyzerImpl {
|
||||
final CommandLineOptions options;
|
||||
final int startTime;
|
||||
|
||||
final AnalysisContext context;
|
||||
final Source librarySource;
|
||||
|
||||
/// All [Source]s references by the analyzed library.
|
||||
final Set<Source> sources = new Set<Source>();
|
||||
|
||||
/// All [AnalysisErrorInfo]s in the analyzed library.
|
||||
final List<AnalysisErrorInfo> errorInfos = new List<AnalysisErrorInfo>();
|
||||
|
||||
/// [HashMap] between sources and analysis error infos.
|
||||
final HashMap<Source, AnalysisErrorInfo> sourceErrorsMap =
|
||||
new HashMap<Source, AnalysisErrorInfo>();
|
||||
|
||||
/// If the file specified on the command line is part of a package, the name
|
||||
/// of that package. Otherwise `null`. This allows us to analyze the file
|
||||
/// specified on the command line as though it is reached via a "package:"
|
||||
/// URI, but avoid suppressing its output in the event that the user has not
|
||||
/// specified the "--package-warnings" option.
|
||||
String _selfPackageName;
|
||||
|
||||
AnalyzerImpl(this.context, this.librarySource, this.options, this.startTime);
|
||||
|
||||
/// Returns the maximal [ErrorSeverity] of the recorded errors.
|
||||
ErrorSeverity get maxErrorSeverity {
|
||||
var status = ErrorSeverity.NONE;
|
||||
for (AnalysisErrorInfo errorInfo in errorInfos) {
|
||||
for (AnalysisError error in errorInfo.errors) {
|
||||
if (!_isDesiredError(error)) {
|
||||
continue;
|
||||
}
|
||||
var severity = computeSeverity(error, options);
|
||||
status = status.max(severity);
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void addCompilationUnitSource(CompilationUnitElement unit,
|
||||
Set<LibraryElement> libraries, Set<CompilationUnitElement> units) {
|
||||
if (unit == null || units.contains(unit)) {
|
||||
return;
|
||||
}
|
||||
units.add(unit);
|
||||
sources.add(unit.source);
|
||||
}
|
||||
|
||||
void addLibrarySources(LibraryElement library, Set<LibraryElement> libraries,
|
||||
Set<CompilationUnitElement> units) {
|
||||
if (library == null || !libraries.add(library)) {
|
||||
return;
|
||||
}
|
||||
// Maybe skip library.
|
||||
{
|
||||
UriKind uriKind = library.source.uriKind;
|
||||
// Optionally skip package: libraries.
|
||||
if (!options.showPackageWarnings && _isOtherPackage(library.source.uri)) {
|
||||
return;
|
||||
}
|
||||
// Optionally skip SDK libraries.
|
||||
if (!options.showSdkWarnings && uriKind == UriKind.DART_URI) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Add compilation units.
|
||||
addCompilationUnitSource(library.definingCompilationUnit, libraries, units);
|
||||
for (CompilationUnitElement child in library.parts) {
|
||||
addCompilationUnitSource(child, libraries, units);
|
||||
}
|
||||
// Add referenced libraries.
|
||||
for (LibraryElement child in library.importedLibraries) {
|
||||
addLibrarySources(child, libraries, units);
|
||||
}
|
||||
for (LibraryElement child in library.exportedLibraries) {
|
||||
addLibrarySources(child, libraries, units);
|
||||
}
|
||||
}
|
||||
|
||||
/// Treats the [sourcePath] as the top level library and analyzes it using a
|
||||
/// synchronous algorithm over the analysis engine. If [printMode] is `0`,
|
||||
/// then no error or performance information is printed. If [printMode] is `1`,
|
||||
/// then both will be printed. If [printMode] is `2`, then only performance
|
||||
/// information will be printed, and it will be marked as being for a cold VM.
|
||||
ErrorSeverity analyzeSync({int printMode: 1}) {
|
||||
setupForAnalysis();
|
||||
return _analyzeSync(printMode);
|
||||
}
|
||||
|
||||
/// Fills [errorInfos] using [sources].
|
||||
void prepareErrors() {
|
||||
for (Source source in sources) {
|
||||
context.computeErrors(source);
|
||||
|
||||
errorInfos.add(context.getErrors(source));
|
||||
}
|
||||
}
|
||||
|
||||
/// Fills [sources].
|
||||
void prepareSources(LibraryElement library) {
|
||||
var units = new Set<CompilationUnitElement>();
|
||||
var libraries = new Set<LibraryElement>();
|
||||
addLibrarySources(library, libraries, units);
|
||||
}
|
||||
|
||||
/// Setup local fields such as the analysis context for analysis.
|
||||
void setupForAnalysis() {
|
||||
sources.clear();
|
||||
errorInfos.clear();
|
||||
Uri libraryUri = librarySource.uri;
|
||||
if (libraryUri.scheme == 'package' && libraryUri.pathSegments.length > 0) {
|
||||
_selfPackageName = libraryUri.pathSegments[0];
|
||||
}
|
||||
}
|
||||
|
||||
/// The sync version of analysis.
|
||||
ErrorSeverity _analyzeSync(int printMode) {
|
||||
// Don't try to analyze parts.
|
||||
if (context.computeKindOf(librarySource) == SourceKind.PART) {
|
||||
stderr.writeln("Only libraries can be analyzed.");
|
||||
stderr.writeln(
|
||||
"${librarySource.fullName} is a part and can not be analyzed.");
|
||||
return ErrorSeverity.ERROR;
|
||||
}
|
||||
// Resolve library.
|
||||
var libraryElement = context.computeLibraryElement(librarySource);
|
||||
// Prepare source and errors.
|
||||
prepareSources(libraryElement);
|
||||
prepareErrors();
|
||||
|
||||
// Print errors and performance numbers.
|
||||
if (printMode == 1) {
|
||||
_printErrorsAndPerf();
|
||||
} else if (printMode == 2) {
|
||||
_printColdPerf();
|
||||
}
|
||||
|
||||
// Compute max severity and set exitCode.
|
||||
ErrorSeverity status = maxErrorSeverity;
|
||||
if (status == ErrorSeverity.WARNING && options.warningsAreFatal) {
|
||||
status = ErrorSeverity.ERROR;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
bool _isDesiredError(AnalysisError error) {
|
||||
if (error.errorCode.type == ErrorType.TODO) {
|
||||
return false;
|
||||
}
|
||||
if (computeSeverity(error, options) == ErrorSeverity.INFO &&
|
||||
options.disableHints) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Determine whether the given URI refers to a package other than the package
|
||||
/// being analyzed.
|
||||
bool _isOtherPackage(Uri uri) {
|
||||
if (uri.scheme != 'package') {
|
||||
return false;
|
||||
}
|
||||
if (_selfPackageName != null &&
|
||||
uri.pathSegments.length > 0 &&
|
||||
uri.pathSegments[0] == _selfPackageName) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
_printColdPerf() {
|
||||
// Print cold VM performance numbers.
|
||||
int totalTime = currentTimeMillis() - startTime;
|
||||
int otherTime = totalTime;
|
||||
for (PerformanceTag tag in PerformanceTag.all) {
|
||||
if (tag != PerformanceTag.UNKNOWN) {
|
||||
int tagTime = tag.elapsedMs;
|
||||
outSink.writeln('${tag.label}-cold:$tagTime');
|
||||
otherTime -= tagTime;
|
||||
}
|
||||
}
|
||||
outSink.writeln('other-cold:$otherTime');
|
||||
outSink.writeln("total-cold:$totalTime");
|
||||
}
|
||||
|
||||
_printErrorsAndPerf() {
|
||||
// The following is a hack. We currently print out to stderr to ensure that
|
||||
// when in batch mode we print to stderr, this is because the prints from
|
||||
// batch are made to stderr. The reason that options.shouldBatch isn't used
|
||||
// is because when the argument flags are constructed in BatchRunner and
|
||||
// passed in from batch mode which removes the batch flag to prevent the
|
||||
// "cannot have the batch flag and source file" error message.
|
||||
StringSink sink = options.machineFormat ? errorSink : outSink;
|
||||
|
||||
// Print errors.
|
||||
ErrorFormatter formatter =
|
||||
new ErrorFormatter(sink, options, _isDesiredError);
|
||||
formatter.formatErrors(errorInfos);
|
||||
}
|
||||
|
||||
/// Compute the severity of the error; however:
|
||||
/// * if [options.enableTypeChecks] is false, then de-escalate checked-mode
|
||||
/// compile time errors to a severity of [ErrorSeverity.INFO].
|
||||
/// * if [options.hintsAreFatal] is true, escalate hints to errors.
|
||||
static ErrorSeverity computeSeverity(
|
||||
AnalysisError error, CommandLineOptions options) {
|
||||
if (!options.enableTypeChecks &&
|
||||
error.errorCode.type == ErrorType.CHECKED_MODE_COMPILE_TIME_ERROR) {
|
||||
return ErrorSeverity.INFO;
|
||||
}
|
||||
if (options.hintsAreFatal && error.errorCode is HintCode) {
|
||||
return ErrorSeverity.ERROR;
|
||||
}
|
||||
return error.errorCode.errorSeverity;
|
||||
}
|
||||
|
||||
/// Return the corresponding package directory or `null` if none is found.
|
||||
static JavaFile getPackageDirectoryFor(JavaFile sourceFile) {
|
||||
// We are going to ask parent file, so get absolute path.
|
||||
sourceFile = sourceFile.getAbsoluteFile();
|
||||
// Look in the containing directories.
|
||||
JavaFile dir = sourceFile.getParentFile();
|
||||
while (dir != null) {
|
||||
JavaFile packagesDir = new JavaFile.relative(dir, "packages");
|
||||
if (packagesDir.exists()) {
|
||||
return packagesDir;
|
||||
}
|
||||
dir = dir.getParentFile();
|
||||
}
|
||||
// Not found.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// This [Logger] prints out information comments to [outSink] and error messages
|
||||
/// to [errorSink].
|
||||
class StdLogger extends Logger {
|
||||
StdLogger();
|
||||
|
||||
@override
|
||||
void logError(String message, [CaughtException exception]) {
|
||||
errorSink.writeln(message);
|
||||
if (exception != null) {
|
||||
errorSink.writeln(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void logError2(String message, Object exception) {
|
||||
errorSink.writeln(message);
|
||||
if (exception != null) {
|
||||
errorSink.writeln(exception.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void logInformation(String message, [CaughtException exception]) {
|
||||
outSink.writeln(message);
|
||||
if (exception != null) {
|
||||
outSink.writeln(exception);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void logInformation2(String message, Object exception) {
|
||||
outSink.writeln(message);
|
||||
if (exception != null) {
|
||||
outSink.writeln(exception.toString());
|
||||
}
|
||||
}
|
||||
}
|
196
pkg/analyzer_cli/lib/src/bootloader.dart
Normal file
196
pkg/analyzer_cli/lib/src/bootloader.dart
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.src.bootloader;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:isolate';
|
||||
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/src/context/context.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart' as engine;
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:source_span/source_span.dart';
|
||||
import 'package:yaml/src/yaml_node.dart';
|
||||
|
||||
const _analyzerPackageName = 'analyzer';
|
||||
|
||||
/// Return non-null if there is a validation issue with this plugin.
|
||||
String validate(PluginInfo plugin) {
|
||||
var missing = <String>[];
|
||||
if (plugin.className == null) {
|
||||
missing.add('class name');
|
||||
}
|
||||
if (plugin.libraryUri == null) {
|
||||
missing.add('library uri');
|
||||
}
|
||||
if (missing.isEmpty) {
|
||||
// All good.
|
||||
return null;
|
||||
}
|
||||
return 'Plugin ${plugin.name} skipped, config missing: ${missing.join(", ")}';
|
||||
}
|
||||
|
||||
List<PluginInfo> _validate(Iterable<PluginInfo> plugins) {
|
||||
List<PluginInfo> validated = <PluginInfo>[];
|
||||
plugins.forEach((PluginInfo plugin) {
|
||||
String validation = validate(plugin);
|
||||
if (validation != null) {
|
||||
errorSink.writeln(validation);
|
||||
} else {
|
||||
validated.add(plugin);
|
||||
}
|
||||
});
|
||||
return validated;
|
||||
}
|
||||
|
||||
/// Source code assembler.
|
||||
class Assembler {
|
||||
/// Plugins to configure.
|
||||
final Iterable<PluginInfo> plugins;
|
||||
|
||||
/// Create an assembler for the given plugin [config].
|
||||
Assembler(this.plugins);
|
||||
|
||||
/// A string enumerating required package `import`s.
|
||||
String get enumerateImports =>
|
||||
plugins.map((PluginInfo p) => "import '${p.libraryUri}';").join('\n');
|
||||
|
||||
/// A string listing initialized plugin instances.
|
||||
String get pluginList =>
|
||||
plugins.map((PluginInfo p) => 'new ${p.className}()').join(', ');
|
||||
|
||||
/// Create a file containing a `main()` suitable for loading in spawned
|
||||
/// isolate.
|
||||
String createMain() => _generateMain();
|
||||
|
||||
String _generateMain() => """
|
||||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
// This code was auto-generated, is not intended to be edited, and is subject to
|
||||
// significant change. Please see the README file for more information.
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
|
||||
$enumerateImports
|
||||
|
||||
void main(List<String> args) {
|
||||
var starter = new Driver();
|
||||
starter.userDefinedPlugins = [$pluginList];
|
||||
starter.start(args);
|
||||
}
|
||||
""";
|
||||
}
|
||||
|
||||
/// Given environment information extracted from command-line `args`, creates a
|
||||
/// a loadable analyzer "image".
|
||||
class BootLoader {
|
||||
/// Emits an error message to [errorSink] if plugin config can't be read.
|
||||
static final ErrorHandler _pluginConfigErrorHandler = (Exception e) {
|
||||
String details;
|
||||
if (e is PluginConfigFormatException) {
|
||||
details = e.message;
|
||||
var node = e.yamlNode;
|
||||
if (node is YamlNode) {
|
||||
SourceLocation location = node.span.start;
|
||||
details += ' (line ${location.line}, column ${location.column})';
|
||||
}
|
||||
} else {
|
||||
details = e.toString();
|
||||
}
|
||||
|
||||
errorSink.writeln('Plugin configuration skipped: $details');
|
||||
};
|
||||
|
||||
/// Reads plugin config info from `.analysis_options`.
|
||||
PluginConfigOptionsProcessor _pluginOptionsProcessor =
|
||||
new PluginConfigOptionsProcessor(_pluginConfigErrorHandler);
|
||||
|
||||
/// Create a loadable analyzer image configured with plugins derived from
|
||||
/// the given analyzer command-line `args`.
|
||||
Image createImage(List<String> args) {
|
||||
// Parse commandline options.
|
||||
CommandLineOptions options = CommandLineOptions.parse(args);
|
||||
|
||||
// Process analysis options file (and notify all interested parties).
|
||||
_processAnalysisOptions(options);
|
||||
|
||||
// TODO(pquitslund): Pass in .packages info
|
||||
return new Image(_pluginOptionsProcessor.config,
|
||||
args: args, packageRootPath: options.packageRootPath);
|
||||
}
|
||||
|
||||
void _processAnalysisOptions(CommandLineOptions options) {
|
||||
// Determine options file path.
|
||||
var filePath = options.analysisOptionsFile ??
|
||||
engine.AnalysisEngine.ANALYSIS_OPTIONS_FILE;
|
||||
try {
|
||||
var file = PhysicalResourceProvider.INSTANCE.getFile(filePath);
|
||||
AnalysisOptionsProvider analysisOptionsProvider =
|
||||
new AnalysisOptionsProvider();
|
||||
Map<String, YamlNode> options =
|
||||
analysisOptionsProvider.getOptionsFromFile(file);
|
||||
//TODO(pq): thread in proper context.
|
||||
var temporaryContext = new AnalysisContextImpl();
|
||||
_pluginOptionsProcessor.optionsProcessed(temporaryContext, options);
|
||||
} on Exception catch (e) {
|
||||
_pluginOptionsProcessor.onError(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A loadable "image" of a a configured analyzer instance.
|
||||
class Image {
|
||||
/// (Optional) package root path.
|
||||
final String packageRootPath;
|
||||
|
||||
/// (Optional) package map.
|
||||
final Map<String, Uri> packages;
|
||||
|
||||
/// (Optional) args to be passed on to the loaded main.
|
||||
final List<String> args;
|
||||
|
||||
/// Plugin configuration.
|
||||
final PluginConfig config;
|
||||
|
||||
/// Create an image with the given [config] and optionally [packages],
|
||||
/// [packageRootPath], and command line [args].
|
||||
Image(this.config, {this.packages, this.packageRootPath, this.args});
|
||||
|
||||
/// Load this image.
|
||||
///
|
||||
/// Loading an image consists in assembling an analyzer `main()`, configured
|
||||
/// to include the appropriate analyzer plugins as specified in
|
||||
/// `.analyzer_options` which is then run in a spawned isolate.
|
||||
Future load() {
|
||||
List<PluginInfo> plugins = _validate(config.plugins);
|
||||
String mainSource = new Assembler(plugins).createMain();
|
||||
|
||||
Completer completer = new Completer();
|
||||
ReceivePort exitListener = new ReceivePort();
|
||||
exitListener.listen((data) {
|
||||
completer.complete();
|
||||
exitListener.close();
|
||||
});
|
||||
|
||||
Uri uri =
|
||||
Uri.parse('data:application/dart;charset=utf-8,${Uri.encodeComponent(
|
||||
mainSource)}');
|
||||
|
||||
// TODO(pquitslund): update once .packages are supported.
|
||||
String packageRoot =
|
||||
packageRootPath != null ? packageRootPath : './packages';
|
||||
Uri packageUri = new Uri.file(packageRoot);
|
||||
|
||||
Isolate.spawnUri(uri, args, null /* msg */,
|
||||
packageRoot: packageUri, onExit: exitListener.sendPort);
|
||||
|
||||
return completer.future;
|
||||
}
|
||||
}
|
641
pkg/analyzer_cli/lib/src/driver.dart
Normal file
641
pkg/analyzer_cli/lib/src/driver.dart
Normal file
|
@ -0,0 +1,641 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.src.driver;
|
||||
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/file_system/file_system.dart' as fileSystem;
|
||||
import 'package:analyzer/file_system/physical_file_system.dart';
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/source/package_map_provider.dart';
|
||||
import 'package:analyzer/source/package_map_resolver.dart';
|
||||
import 'package:analyzer/source/pub_package_map_provider.dart';
|
||||
import 'package:analyzer/source/sdk_ext.dart';
|
||||
import 'package:analyzer/src/generated/constant.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/interner.dart';
|
||||
import 'package:analyzer/src/generated/java_engine.dart';
|
||||
import 'package:analyzer/src/generated/java_io.dart';
|
||||
import 'package:analyzer/src/generated/sdk_io.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/generated/source_io.dart';
|
||||
import 'package:analyzer/src/services/lint.dart';
|
||||
import 'package:analyzer/src/task/options.dart';
|
||||
import 'package:analyzer_cli/src/analyzer_impl.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:linter/src/plugin/linter_plugin.dart';
|
||||
import 'package:package_config/discovery.dart' as pkgDiscovery;
|
||||
import 'package:package_config/packages.dart' show Packages;
|
||||
import 'package:package_config/packages_file.dart' as pkgfile show parse;
|
||||
import 'package:package_config/src/packages_impl.dart' show MapPackages;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:plugin/plugin.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
/// The maximum number of sources for which AST structures should be kept in the
|
||||
/// cache.
|
||||
const int _maxCacheSize = 512;
|
||||
|
||||
/// Shared IO sink for standard error reporting.
|
||||
///
|
||||
/// *Visible for testing.*
|
||||
StringSink errorSink = stderr;
|
||||
|
||||
/// Shared IO sink for standard out reporting.
|
||||
///
|
||||
/// *Visible for testing.*
|
||||
StringSink outSink = stdout;
|
||||
|
||||
/// Test this option map to see if it specifies lint rules.
|
||||
bool containsLintRuleEntry(Map<String, YamlNode> options) {
|
||||
var linterNode = options['linter'];
|
||||
return linterNode is YamlMap && linterNode.containsKey('rules');
|
||||
}
|
||||
|
||||
typedef ErrorSeverity _BatchRunnerHandler(List<String> args);
|
||||
|
||||
class Driver {
|
||||
/// The plugins that are defined outside the `analyzer_cli` package.
|
||||
List<Plugin> _userDefinedPlugins = <Plugin>[];
|
||||
|
||||
/// Indicates whether the analyzer is running in batch mode.
|
||||
bool _isBatch;
|
||||
|
||||
/// The context that was most recently created by a call to [_analyzeAll], or
|
||||
/// `null` if [_analyzeAll] hasn't been called yet.
|
||||
AnalysisContext _context;
|
||||
|
||||
/// If [_context] is not `null`, the [CommandLineOptions] that guided its
|
||||
/// creation.
|
||||
CommandLineOptions _previousOptions;
|
||||
|
||||
/// This Driver's current analysis context.
|
||||
///
|
||||
/// *Visible for testing.*
|
||||
AnalysisContext get context => _context;
|
||||
|
||||
/// Set the [plugins] that are defined outside the `analyzer_cli` package.
|
||||
void set userDefinedPlugins(List<Plugin> plugins) {
|
||||
_userDefinedPlugins = plugins == null ? <Plugin>[] : plugins;
|
||||
}
|
||||
|
||||
/// Use the given command-line [args] to start this analysis driver.
|
||||
void start(List<String> args) {
|
||||
StringUtilities.INTERNER = new MappedInterner();
|
||||
|
||||
_processPlugins();
|
||||
|
||||
// Parse commandline options.
|
||||
CommandLineOptions options = CommandLineOptions.parse(args);
|
||||
|
||||
// Cache options of interest to inform analysis.
|
||||
_setupEnv(options);
|
||||
|
||||
// Do analysis.
|
||||
if (_isBatch) {
|
||||
_BatchRunner.runAsBatch(args, (List<String> args) {
|
||||
CommandLineOptions options = CommandLineOptions.parse(args);
|
||||
return _analyzeAll(options);
|
||||
});
|
||||
} else {
|
||||
ErrorSeverity severity = _analyzeAll(options);
|
||||
// In case of error propagate exit code.
|
||||
if (severity == ErrorSeverity.ERROR) {
|
||||
exitCode = severity.ordinal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform analysis according to the given [options].
|
||||
ErrorSeverity _analyzeAll(CommandLineOptions options) {
|
||||
if (!options.machineFormat) {
|
||||
outSink.writeln("Analyzing ${options.sourceFiles}...");
|
||||
}
|
||||
|
||||
// Create a context, or re-use the previous one.
|
||||
try {
|
||||
_createAnalysisContext(options);
|
||||
} on _DriverError catch (error) {
|
||||
outSink.writeln(error.msg);
|
||||
return ErrorSeverity.ERROR;
|
||||
}
|
||||
|
||||
// Add all the files to be analyzed en masse to the context. Skip any
|
||||
// files that were added earlier (whether explicitly or implicitly) to
|
||||
// avoid causing those files to be unnecessarily re-read.
|
||||
Set<Source> knownSources = _context.sources.toSet();
|
||||
List<Source> sourcesToAnalyze = <Source>[];
|
||||
ChangeSet changeSet = new ChangeSet();
|
||||
for (String sourcePath in options.sourceFiles) {
|
||||
sourcePath = sourcePath.trim();
|
||||
// Check that file exists.
|
||||
if (!new File(sourcePath).existsSync()) {
|
||||
errorSink.writeln('File not found: $sourcePath');
|
||||
exitCode = ErrorSeverity.ERROR.ordinal;
|
||||
//Fail fast; don't analyze more files
|
||||
return ErrorSeverity.ERROR;
|
||||
}
|
||||
// Check that file is Dart file.
|
||||
if (!AnalysisEngine.isDartFileName(sourcePath)) {
|
||||
errorSink.writeln('$sourcePath is not a Dart file');
|
||||
exitCode = ErrorSeverity.ERROR.ordinal;
|
||||
// Fail fast; don't analyze more files.
|
||||
return ErrorSeverity.ERROR;
|
||||
}
|
||||
Source source = _computeLibrarySource(sourcePath);
|
||||
if (!knownSources.contains(source)) {
|
||||
changeSet.addedSource(source);
|
||||
}
|
||||
sourcesToAnalyze.add(source);
|
||||
}
|
||||
_context.applyChanges(changeSet);
|
||||
|
||||
// Analyze the libraries.
|
||||
ErrorSeverity allResult = ErrorSeverity.NONE;
|
||||
var libUris = <Uri>[];
|
||||
var parts = <Source>[];
|
||||
for (Source source in sourcesToAnalyze) {
|
||||
if (context.computeKindOf(source) == SourceKind.PART) {
|
||||
parts.add(source);
|
||||
continue;
|
||||
}
|
||||
ErrorSeverity status = _runAnalyzer(source, options);
|
||||
allResult = allResult.max(status);
|
||||
libUris.add(source.uri);
|
||||
}
|
||||
|
||||
// Check that each part has a corresponding source in the input list.
|
||||
for (Source part in parts) {
|
||||
bool found = false;
|
||||
for (var lib in context.getLibrariesContaining(part)) {
|
||||
if (libUris.contains(lib.uri)) {
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
errorSink.writeln("${part.fullName} is a part and cannot be analyzed.");
|
||||
errorSink.writeln("Please pass in a library that contains this part.");
|
||||
exitCode = ErrorSeverity.ERROR.ordinal;
|
||||
allResult = allResult.max(ErrorSeverity.ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
return allResult;
|
||||
}
|
||||
|
||||
/// Determine whether the context created during a previous call to
|
||||
/// [_analyzeAll] can be re-used in order to analyze using [options].
|
||||
bool _canContextBeReused(CommandLineOptions options) {
|
||||
// TODO(paulberry): add a command-line option that disables context re-use.
|
||||
if (_context == null) {
|
||||
return false;
|
||||
}
|
||||
if (options.packageRootPath != _previousOptions.packageRootPath) {
|
||||
return false;
|
||||
}
|
||||
if (options.packageConfigPath != _previousOptions.packageConfigPath) {
|
||||
return false;
|
||||
}
|
||||
if (!_equalMaps(
|
||||
options.definedVariables, _previousOptions.definedVariables)) {
|
||||
return false;
|
||||
}
|
||||
if (options.log != _previousOptions.log) {
|
||||
return false;
|
||||
}
|
||||
if (options.disableHints != _previousOptions.disableHints) {
|
||||
return false;
|
||||
}
|
||||
if (options.enableStrictCallChecks !=
|
||||
_previousOptions.enableStrictCallChecks) {
|
||||
return false;
|
||||
}
|
||||
if (options.showPackageWarnings != _previousOptions.showPackageWarnings) {
|
||||
return false;
|
||||
}
|
||||
if (options.showSdkWarnings != _previousOptions.showSdkWarnings) {
|
||||
return false;
|
||||
}
|
||||
if (options.lints != _previousOptions.lints) {
|
||||
return false;
|
||||
}
|
||||
if (options.strongMode != _previousOptions.strongMode) {
|
||||
return false;
|
||||
}
|
||||
if (options.enableSuperMixins != _previousOptions.enableSuperMixins) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Decide on the appropriate policy for which files need to be fully parsed
|
||||
/// and which files need to be diet parsed, based on [options], and return an
|
||||
/// [AnalyzeFunctionBodiesPredicate] that implements this policy.
|
||||
AnalyzeFunctionBodiesPredicate _chooseDietParsingPolicy(
|
||||
CommandLineOptions options) {
|
||||
if (_isBatch) {
|
||||
// As analyzer is currently implemented, once a file has been diet
|
||||
// parsed, it can't easily be un-diet parsed without creating a brand new
|
||||
// context and losing caching. In batch mode, we can't predict which
|
||||
// files we'll need to generate errors and warnings for in the future, so
|
||||
// we can't safely diet parse anything.
|
||||
return (Source source) => true;
|
||||
}
|
||||
|
||||
// Determine the set of packages requiring a full parse. Use null to
|
||||
// represent the case where all packages require a full parse.
|
||||
Set<String> packagesRequiringFullParse;
|
||||
if (options.showPackageWarnings) {
|
||||
// We are showing warnings from all packages so all packages require a
|
||||
// full parse.
|
||||
packagesRequiringFullParse = null;
|
||||
} else {
|
||||
// We aren't showing warnings for dependent packages, but we may still
|
||||
// need to show warnings for "self" packages, so we need to do a full
|
||||
// parse in any package containing files mentioned on the command line.
|
||||
// TODO(paulberry): implement this. As a temporary workaround, we're
|
||||
// fully parsing all packages.
|
||||
packagesRequiringFullParse = null;
|
||||
}
|
||||
return (Source source) {
|
||||
if (source.uri.scheme == 'dart') {
|
||||
return options.showSdkWarnings;
|
||||
} else if (source.uri.scheme == 'package') {
|
||||
if (packagesRequiringFullParse == null) {
|
||||
return true;
|
||||
} else if (source.uri.pathSegments.length == 0) {
|
||||
// We should never see a URI like this, but fully parse it to be
|
||||
// safe.
|
||||
return true;
|
||||
} else {
|
||||
return packagesRequiringFullParse
|
||||
.contains(source.uri.pathSegments[0]);
|
||||
}
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// Decide on the appropriate method for resolving URIs based on the given
|
||||
/// [options] and [customUrlMappings] settings, and return a
|
||||
/// [SourceFactory] that has been configured accordingly.
|
||||
SourceFactory _chooseUriResolutionPolicy(CommandLineOptions options) {
|
||||
Packages packages;
|
||||
Map<String, List<fileSystem.Folder>> packageMap;
|
||||
UriResolver packageUriResolver;
|
||||
|
||||
// Process options, caching package resolution details.
|
||||
if (options.packageConfigPath != null) {
|
||||
String packageConfigPath = options.packageConfigPath;
|
||||
Uri fileUri = new Uri.file(packageConfigPath);
|
||||
try {
|
||||
File configFile = new File.fromUri(fileUri).absolute;
|
||||
List<int> bytes = configFile.readAsBytesSync();
|
||||
Map<String, Uri> map = pkgfile.parse(bytes, configFile.uri);
|
||||
packages = new MapPackages(map);
|
||||
packageMap = _getPackageMap(packages);
|
||||
} catch (e) {
|
||||
printAndFail(
|
||||
'Unable to read package config data from $packageConfigPath: $e');
|
||||
}
|
||||
} else if (options.packageRootPath != null) {
|
||||
packageMap = _PackageRootPackageMapBuilder
|
||||
.buildPackageMap(options.packageRootPath);
|
||||
|
||||
JavaFile packageDirectory = new JavaFile(options.packageRootPath);
|
||||
packageUriResolver = new PackageUriResolver([packageDirectory]);
|
||||
} else {
|
||||
fileSystem.Resource cwd =
|
||||
PhysicalResourceProvider.INSTANCE.getResource('.');
|
||||
|
||||
// Look for .packages.
|
||||
packages = _discoverPackagespec(new Uri.directory(cwd.path));
|
||||
|
||||
if (packages != null) {
|
||||
packageMap = _getPackageMap(packages);
|
||||
} else {
|
||||
// Fall back to pub list-package-dirs.
|
||||
|
||||
PubPackageMapProvider pubPackageMapProvider =
|
||||
new PubPackageMapProvider(PhysicalResourceProvider.INSTANCE, sdk);
|
||||
PackageMapInfo packageMapInfo =
|
||||
pubPackageMapProvider.computePackageMap(cwd);
|
||||
packageMap = packageMapInfo.packageMap;
|
||||
|
||||
// Only create a packageUriResolver if pub list-package-dirs succeeded.
|
||||
// If it failed, that's not a problem; it simply means we have no way
|
||||
// to resolve packages.
|
||||
if (packageMapInfo.packageMap != null) {
|
||||
packageUriResolver = new PackageMapUriResolver(
|
||||
PhysicalResourceProvider.INSTANCE, packageMap);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now, build our resolver list.
|
||||
|
||||
// 'dart:' URIs come first.
|
||||
List<UriResolver> resolvers = [new DartUriResolver(sdk)];
|
||||
|
||||
// Next SdkExts.
|
||||
if (packageMap != null) {
|
||||
resolvers.add(new SdkExtUriResolver(packageMap));
|
||||
}
|
||||
|
||||
// Then package URIs.
|
||||
if (packageUriResolver != null) {
|
||||
resolvers.add(packageUriResolver);
|
||||
}
|
||||
|
||||
// Finally files.
|
||||
resolvers.add(new FileUriResolver());
|
||||
|
||||
return new SourceFactory(resolvers, packages);
|
||||
}
|
||||
|
||||
/// Convert the given [sourcePath] (which may be relative to the current
|
||||
/// working directory) to a [Source] object that can be fed to the analysis
|
||||
/// context.
|
||||
Source _computeLibrarySource(String sourcePath) {
|
||||
sourcePath = _normalizeSourcePath(sourcePath);
|
||||
JavaFile sourceFile = new JavaFile(sourcePath);
|
||||
Source source = sdk.fromFileUri(sourceFile.toURI());
|
||||
if (source != null) {
|
||||
return source;
|
||||
}
|
||||
source = new FileBasedSource(sourceFile, sourceFile.toURI());
|
||||
Uri uri = _context.sourceFactory.restoreUri(source);
|
||||
if (uri == null) {
|
||||
return source;
|
||||
}
|
||||
return new FileBasedSource(sourceFile, uri);
|
||||
}
|
||||
|
||||
/// Create an analysis context that is prepared to analyze sources according
|
||||
/// to the given [options], and store it in [_context].
|
||||
void _createAnalysisContext(CommandLineOptions options) {
|
||||
if (_canContextBeReused(options)) {
|
||||
return;
|
||||
}
|
||||
_previousOptions = options;
|
||||
// Choose a package resolution policy and a diet parsing policy based on
|
||||
// the command-line options.
|
||||
SourceFactory sourceFactory = _chooseUriResolutionPolicy(options);
|
||||
AnalyzeFunctionBodiesPredicate dietParsingPolicy =
|
||||
_chooseDietParsingPolicy(options);
|
||||
// Create a context using these policies.
|
||||
AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
|
||||
|
||||
context.sourceFactory = sourceFactory;
|
||||
|
||||
Map<String, String> definedVariables = options.definedVariables;
|
||||
if (!definedVariables.isEmpty) {
|
||||
DeclaredVariables declaredVariables = context.declaredVariables;
|
||||
definedVariables.forEach((String variableName, String value) {
|
||||
declaredVariables.define(variableName, value);
|
||||
});
|
||||
}
|
||||
|
||||
if (options.log) {
|
||||
AnalysisEngine.instance.logger = new StdLogger();
|
||||
}
|
||||
|
||||
// Set context options.
|
||||
AnalysisOptionsImpl contextOptions = new AnalysisOptionsImpl();
|
||||
contextOptions.cacheSize = _maxCacheSize;
|
||||
contextOptions.hint = !options.disableHints;
|
||||
contextOptions.enableStrictCallChecks = options.enableStrictCallChecks;
|
||||
contextOptions.enableSuperMixins = options.enableSuperMixins;
|
||||
contextOptions.analyzeFunctionBodiesPredicate = dietParsingPolicy;
|
||||
contextOptions.generateImplicitErrors = options.showPackageWarnings;
|
||||
contextOptions.generateSdkErrors = options.showSdkWarnings;
|
||||
contextOptions.lint = options.lints;
|
||||
contextOptions.strongMode = options.strongMode;
|
||||
context.analysisOptions = contextOptions;
|
||||
_context = context;
|
||||
|
||||
// Process analysis options file (and notify all interested parties).
|
||||
_processAnalysisOptions(options, context);
|
||||
}
|
||||
|
||||
/// Return discovered packagespec, or `null` if none is found.
|
||||
Packages _discoverPackagespec(Uri root) {
|
||||
try {
|
||||
Packages packages = pkgDiscovery.findPackagesFromFile(root);
|
||||
if (packages != Packages.noPackages) {
|
||||
return packages;
|
||||
}
|
||||
} catch (_) {
|
||||
// Ignore and fall through to null.
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
fileSystem.File _getOptionsFile(CommandLineOptions options) {
|
||||
fileSystem.File file;
|
||||
String filePath = options.analysisOptionsFile;
|
||||
if (filePath != null) {
|
||||
file = PhysicalResourceProvider.INSTANCE.getFile(filePath);
|
||||
if (!file.exists) {
|
||||
printAndFail('Options file not found: $filePath',
|
||||
exitCode: ErrorSeverity.ERROR.ordinal);
|
||||
}
|
||||
} else {
|
||||
filePath = AnalysisEngine.ANALYSIS_OPTIONS_FILE;
|
||||
file = PhysicalResourceProvider.INSTANCE.getFile(filePath);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
Map<String, List<fileSystem.Folder>> _getPackageMap(Packages packages) {
|
||||
if (packages == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Map<String, List<fileSystem.Folder>> folderMap =
|
||||
new Map<String, List<fileSystem.Folder>>();
|
||||
packages.asMap().forEach((String packagePath, Uri uri) {
|
||||
folderMap[packagePath] = [
|
||||
PhysicalResourceProvider.INSTANCE.getFolder(path.fromUri(uri))
|
||||
];
|
||||
});
|
||||
return folderMap;
|
||||
}
|
||||
|
||||
void _processAnalysisOptions(
|
||||
CommandLineOptions options, AnalysisContext context) {
|
||||
fileSystem.File file = _getOptionsFile(options);
|
||||
List<OptionsProcessor> optionsProcessors =
|
||||
AnalysisEngine.instance.optionsPlugin.optionsProcessors;
|
||||
try {
|
||||
AnalysisOptionsProvider analysisOptionsProvider =
|
||||
new AnalysisOptionsProvider();
|
||||
Map<String, YamlNode> optionMap =
|
||||
analysisOptionsProvider.getOptionsFromFile(file);
|
||||
optionsProcessors.forEach(
|
||||
(OptionsProcessor p) => p.optionsProcessed(context, optionMap));
|
||||
|
||||
// Fill in lint rule defaults in case lints are enabled and rules are
|
||||
// not specified in an options file.
|
||||
if (options.lints && !containsLintRuleEntry(optionMap)) {
|
||||
setLints(context, linterPlugin.contributedRules);
|
||||
}
|
||||
|
||||
// Ask engine to further process options.
|
||||
if (optionMap != null) {
|
||||
configureContextOptions(context, optionMap);
|
||||
}
|
||||
} on Exception catch (e) {
|
||||
optionsProcessors.forEach((OptionsProcessor p) => p.onError(e));
|
||||
}
|
||||
}
|
||||
|
||||
void _processPlugins() {
|
||||
List<Plugin> plugins = <Plugin>[];
|
||||
plugins.add(linterPlugin);
|
||||
plugins.addAll(_userDefinedPlugins);
|
||||
AnalysisEngine.instance.userDefinedPlugins = plugins;
|
||||
|
||||
// This ensures that AE extension manager processes plugins.
|
||||
AnalysisEngine.instance.taskManager;
|
||||
}
|
||||
|
||||
/// Analyze a single source.
|
||||
ErrorSeverity _runAnalyzer(Source source, CommandLineOptions options) {
|
||||
int startTime = currentTimeMillis();
|
||||
AnalyzerImpl analyzer =
|
||||
new AnalyzerImpl(_context, source, options, startTime);
|
||||
var errorSeverity = analyzer.analyzeSync();
|
||||
if (errorSeverity == ErrorSeverity.ERROR) {
|
||||
exitCode = errorSeverity.ordinal;
|
||||
}
|
||||
if (options.warningsAreFatal && errorSeverity == ErrorSeverity.WARNING) {
|
||||
exitCode = errorSeverity.ordinal;
|
||||
}
|
||||
return errorSeverity;
|
||||
}
|
||||
|
||||
void _setupEnv(CommandLineOptions options) {
|
||||
// In batch mode, SDK is specified on the main command line rather than in
|
||||
// the command lines sent to stdin. So process it before deciding whether
|
||||
// to activate batch mode.
|
||||
if (sdk == null) {
|
||||
sdk = new DirectoryBasedDartSdk(new JavaFile(options.dartSdkPath));
|
||||
}
|
||||
_isBatch = options.shouldBatch;
|
||||
}
|
||||
|
||||
/// Perform a deep comparison of two string maps.
|
||||
static bool _equalMaps(Map<String, String> m1, Map<String, String> m2) {
|
||||
if (m1.length != m2.length) {
|
||||
return false;
|
||||
}
|
||||
for (String key in m1.keys) {
|
||||
if (!m2.containsKey(key) || m1[key] != m2[key]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Convert [sourcePath] into an absolute path.
|
||||
static String _normalizeSourcePath(String sourcePath) =>
|
||||
path.normalize(new File(sourcePath).absolute.path);
|
||||
}
|
||||
|
||||
/// Provides a framework to read command line options from stdin and feed them
|
||||
/// to a callback.
|
||||
class _BatchRunner {
|
||||
/// Run the tool in 'batch' mode, receiving command lines through stdin and
|
||||
/// returning pass/fail status through stdout. This feature is intended for
|
||||
/// use in unit testing.
|
||||
static void runAsBatch(List<String> sharedArgs, _BatchRunnerHandler handler) {
|
||||
outSink.writeln('>>> BATCH START');
|
||||
Stopwatch stopwatch = new Stopwatch();
|
||||
stopwatch.start();
|
||||
int testsFailed = 0;
|
||||
int totalTests = 0;
|
||||
ErrorSeverity batchResult = ErrorSeverity.NONE;
|
||||
// Read line from stdin.
|
||||
Stream cmdLine =
|
||||
stdin.transform(UTF8.decoder).transform(new LineSplitter());
|
||||
cmdLine.listen((String line) {
|
||||
// Maybe finish.
|
||||
if (line.isEmpty) {
|
||||
var time = stopwatch.elapsedMilliseconds;
|
||||
outSink.writeln(
|
||||
'>>> BATCH END (${totalTests - testsFailed}/$totalTests) ${time}ms');
|
||||
exitCode = batchResult.ordinal;
|
||||
}
|
||||
// Prepare aruments.
|
||||
var args;
|
||||
{
|
||||
var lineArgs = line.split(new RegExp('\\s+'));
|
||||
args = new List<String>();
|
||||
args.addAll(sharedArgs);
|
||||
args.addAll(lineArgs);
|
||||
args.remove('-b');
|
||||
args.remove('--batch');
|
||||
}
|
||||
// Analyze single set of arguments.
|
||||
try {
|
||||
totalTests++;
|
||||
ErrorSeverity result = handler(args);
|
||||
bool resultPass = result != ErrorSeverity.ERROR;
|
||||
if (!resultPass) {
|
||||
testsFailed++;
|
||||
}
|
||||
batchResult = batchResult.max(result);
|
||||
// Write stderr end token and flush.
|
||||
errorSink.writeln('>>> EOF STDERR');
|
||||
String resultPassString = resultPass ? 'PASS' : 'FAIL';
|
||||
outSink.writeln(
|
||||
'>>> TEST $resultPassString ${stopwatch.elapsedMilliseconds}ms');
|
||||
} catch (e, stackTrace) {
|
||||
errorSink.writeln(e);
|
||||
errorSink.writeln(stackTrace);
|
||||
errorSink.writeln('>>> EOF STDERR');
|
||||
outSink.writeln('>>> TEST CRASH');
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class _DriverError implements Exception {
|
||||
String msg;
|
||||
_DriverError(this.msg);
|
||||
}
|
||||
|
||||
/// [SdkExtUriResolver] needs a Map from package name to folder. In the case
|
||||
/// that the analyzer is invoked with a --package-root option, we need to
|
||||
/// manually create this mapping. Given [packageRootPath],
|
||||
/// [_PackageRootPackageMapBuilder] creates a simple mapping from package name
|
||||
/// to full path on disk (resolving any symbolic links).
|
||||
class _PackageRootPackageMapBuilder {
|
||||
static Map<String, List<fileSystem.Folder>> buildPackageMap(
|
||||
String packageRootPath) {
|
||||
var packageRoot = new Directory(packageRootPath);
|
||||
if (!packageRoot.existsSync()) {
|
||||
throw new _DriverError(
|
||||
'Package root directory ($packageRootPath) does not exist.');
|
||||
}
|
||||
var packages = packageRoot.listSync(followLinks: false);
|
||||
var result = new Map<String, List<fileSystem.Folder>>();
|
||||
for (var package in packages) {
|
||||
var packageName = path.basename(package.path);
|
||||
var realPath = package.resolveSymbolicLinksSync();
|
||||
result[packageName] = [
|
||||
PhysicalResourceProvider.INSTANCE.getFolder(realPath)
|
||||
];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
196
pkg/analyzer_cli/lib/src/error_formatter.dart
Normal file
196
pkg/analyzer_cli/lib/src/error_formatter.dart
Normal file
|
@ -0,0 +1,196 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.src.error_formatter;
|
||||
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_cli/src/analyzer_impl.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
|
||||
/// Allows any [AnalysisError].
|
||||
bool _anyError(AnalysisError error) => true;
|
||||
|
||||
/// Returns `true` if [AnalysisError] should be printed.
|
||||
typedef bool _ErrorFilter(AnalysisError error);
|
||||
|
||||
/// Helper for formatting [AnalysisError]s.
|
||||
/// The two format options are a user consumable format and a machine consumable format.
|
||||
class ErrorFormatter {
|
||||
final StringSink out;
|
||||
final CommandLineOptions options;
|
||||
final _ErrorFilter errorFilter;
|
||||
|
||||
ErrorFormatter(this.out, this.options, [this.errorFilter = _anyError]);
|
||||
|
||||
void formatError(
|
||||
Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) {
|
||||
Source source = error.source;
|
||||
LineInfo_Location location = errorToLine[error].getLocation(error.offset);
|
||||
int length = error.length;
|
||||
ErrorSeverity severity =
|
||||
AnalyzerImpl.computeSeverity(error, options);
|
||||
if (options.machineFormat) {
|
||||
if (severity == ErrorSeverity.WARNING && options.warningsAreFatal) {
|
||||
severity = ErrorSeverity.ERROR;
|
||||
}
|
||||
out.write(severity);
|
||||
out.write('|');
|
||||
out.write(error.errorCode.type);
|
||||
out.write('|');
|
||||
out.write(error.errorCode.name);
|
||||
out.write('|');
|
||||
out.write(escapePipe(source.fullName));
|
||||
out.write('|');
|
||||
out.write(location.lineNumber);
|
||||
out.write('|');
|
||||
out.write(location.columnNumber);
|
||||
out.write('|');
|
||||
out.write(length);
|
||||
out.write('|');
|
||||
out.write(escapePipe(error.message));
|
||||
} else {
|
||||
String errorType = severity.displayName;
|
||||
if (error.errorCode.type == ErrorType.HINT ||
|
||||
error.errorCode.type == ErrorType.LINT) {
|
||||
errorType = error.errorCode.type.displayName;
|
||||
}
|
||||
// [warning] 'foo' is not a... (/Users/.../tmp/foo.dart, line 1, col 2)
|
||||
out.write('[$errorType] ${error.message} ');
|
||||
out.write('(${source.fullName}');
|
||||
out.write(', line ${location.lineNumber}, col ${location.columnNumber})');
|
||||
}
|
||||
out.writeln();
|
||||
}
|
||||
|
||||
void formatErrors(List<AnalysisErrorInfo> errorInfos) {
|
||||
var errors = new List<AnalysisError>();
|
||||
var errorToLine = new Map<AnalysisError, LineInfo>();
|
||||
for (AnalysisErrorInfo errorInfo in errorInfos) {
|
||||
for (AnalysisError error in errorInfo.errors) {
|
||||
if (errorFilter(error)) {
|
||||
errors.add(error);
|
||||
errorToLine[error] = errorInfo.lineInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Sort errors.
|
||||
errors.sort((AnalysisError error1, AnalysisError error2) {
|
||||
// Severity.
|
||||
ErrorSeverity severity1 =
|
||||
AnalyzerImpl.computeSeverity(error1, options);
|
||||
ErrorSeverity severity2 =
|
||||
AnalyzerImpl.computeSeverity(error2, options);
|
||||
int compare = severity2.compareTo(severity1);
|
||||
if (compare != 0) {
|
||||
return compare;
|
||||
}
|
||||
// Path.
|
||||
compare = Comparable.compare(error1.source.fullName.toLowerCase(),
|
||||
error2.source.fullName.toLowerCase());
|
||||
if (compare != 0) {
|
||||
return compare;
|
||||
}
|
||||
// Offset.
|
||||
return error1.offset - error2.offset;
|
||||
});
|
||||
// Format errors.
|
||||
int errorCount = 0;
|
||||
int warnCount = 0;
|
||||
int hintCount = 0;
|
||||
int lintCount = 0;
|
||||
for (AnalysisError error in errors) {
|
||||
ErrorSeverity severity =
|
||||
AnalyzerImpl.computeSeverity(error, options);
|
||||
if (severity == ErrorSeverity.ERROR) {
|
||||
errorCount++;
|
||||
} else if (severity == ErrorSeverity.WARNING) {
|
||||
if (options.warningsAreFatal) {
|
||||
errorCount++;
|
||||
} else {
|
||||
warnCount++;
|
||||
}
|
||||
} else if (error.errorCode.type == ErrorType.HINT) {
|
||||
hintCount++;
|
||||
} else if (error.errorCode.type == ErrorType.LINT) {
|
||||
lintCount++;
|
||||
}
|
||||
formatError(errorToLine, error);
|
||||
}
|
||||
// Print statistics.
|
||||
if (!options.machineFormat) {
|
||||
var hasErrors = errorCount != 0;
|
||||
var hasWarns = warnCount != 0;
|
||||
var hasHints = hintCount != 0;
|
||||
var hasLints = lintCount != 0;
|
||||
bool hasContent = false;
|
||||
if (hasErrors) {
|
||||
out.write(errorCount);
|
||||
out.write(' ');
|
||||
out.write(pluralize("error", errorCount));
|
||||
hasContent = true;
|
||||
}
|
||||
if (hasWarns) {
|
||||
if (hasContent) {
|
||||
if (!hasHints && !hasLints) {
|
||||
out.write(' and ');
|
||||
} else {
|
||||
out.write(", ");
|
||||
}
|
||||
}
|
||||
out.write(warnCount);
|
||||
out.write(' ');
|
||||
out.write(pluralize("warning", warnCount));
|
||||
hasContent = true;
|
||||
}
|
||||
if (hasHints) {
|
||||
if (hasContent) {
|
||||
if (!hasLints) {
|
||||
out.write(' and ');
|
||||
} else {
|
||||
out.write(", ");
|
||||
}
|
||||
}
|
||||
out.write(hintCount);
|
||||
out.write(' ');
|
||||
out.write(pluralize("hint", hintCount));
|
||||
hasContent = true;
|
||||
}
|
||||
if (hasLints) {
|
||||
if (hasContent) {
|
||||
out.write(" and ");
|
||||
}
|
||||
out.write(lintCount);
|
||||
out.write(' ');
|
||||
out.write(pluralize("lint", lintCount));
|
||||
hasContent = true;
|
||||
}
|
||||
if (hasContent) {
|
||||
out.writeln(" found.");
|
||||
} else {
|
||||
out.writeln("No issues found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String escapePipe(String input) {
|
||||
var result = new StringBuffer();
|
||||
for (var c in input.codeUnits) {
|
||||
if (c == '\\' || c == '|') {
|
||||
result.write('\\');
|
||||
}
|
||||
result.writeCharCode(c);
|
||||
}
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
static String pluralize(String word, int count) {
|
||||
if (count == 1) {
|
||||
return word;
|
||||
} else {
|
||||
return word + "s";
|
||||
}
|
||||
}
|
||||
}
|
485
pkg/analyzer_cli/lib/src/options.dart
Normal file
485
pkg/analyzer_cli/lib/src/options.dart
Normal file
|
@ -0,0 +1,485 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.src.options;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:args/args.dart';
|
||||
import 'package:cli_util/cli_util.dart' show getSdkDir;
|
||||
|
||||
const _binaryName = 'dartanalyzer';
|
||||
|
||||
/// Shared exit handler.
|
||||
///
|
||||
/// *Visible for testing.*
|
||||
ExitHandler exitHandler = exit;
|
||||
|
||||
/// Print the given message to stderr and exit with the given [exitCode]
|
||||
void printAndFail(String message, {int exitCode: 15}) {
|
||||
errorSink.writeln(message);
|
||||
exitHandler(exitCode);
|
||||
}
|
||||
|
||||
/// Exit handler.
|
||||
///
|
||||
/// *Visible for testing.*
|
||||
typedef void ExitHandler(int code);
|
||||
|
||||
/// Analyzer commandline configuration options.
|
||||
class CommandLineOptions {
|
||||
/// The path to an analysis options file
|
||||
final String analysisOptionsFile;
|
||||
|
||||
/// The path to the dart SDK
|
||||
String dartSdkPath;
|
||||
|
||||
/// A table mapping the names of defined variables to their values.
|
||||
final Map<String, String> definedVariables;
|
||||
|
||||
/// Whether to report hints
|
||||
final bool disableHints;
|
||||
|
||||
/// Whether to display version information
|
||||
final bool displayVersion;
|
||||
|
||||
/// Whether to enable null-aware operators (DEP 9).
|
||||
final bool enableNullAwareOperators;
|
||||
|
||||
/// Whether to strictly follow the specification when generating warnings on
|
||||
/// "call" methods (fixes dartbug.com/21938).
|
||||
final bool enableStrictCallChecks;
|
||||
|
||||
/// Whether to relax restrictions on mixins (DEP 34).
|
||||
final bool enableSuperMixins;
|
||||
|
||||
/// Whether to treat type mismatches found during constant evaluation as
|
||||
/// errors.
|
||||
final bool enableTypeChecks;
|
||||
|
||||
/// Whether to treat hints as fatal
|
||||
final bool hintsAreFatal;
|
||||
|
||||
/// Whether to ignore unrecognized flags
|
||||
final bool ignoreUnrecognizedFlags;
|
||||
|
||||
/// Whether to report lints
|
||||
final bool lints;
|
||||
|
||||
/// Whether to log additional analysis messages and exceptions
|
||||
final bool log;
|
||||
|
||||
/// Whether to use machine format for error display
|
||||
final bool machineFormat;
|
||||
|
||||
/// The path to the package root
|
||||
final String packageRootPath;
|
||||
|
||||
/// The path to a `.packages` configuration file
|
||||
final String packageConfigPath;
|
||||
|
||||
/// Batch mode (for unit testing)
|
||||
final bool shouldBatch;
|
||||
|
||||
/// Whether to show package: warnings
|
||||
final bool showPackageWarnings;
|
||||
|
||||
/// Whether to show SDK warnings
|
||||
final bool showSdkWarnings;
|
||||
|
||||
/// The source files to analyze
|
||||
final List<String> sourceFiles;
|
||||
|
||||
/// Whether to treat warnings as fatal
|
||||
final bool warningsAreFatal;
|
||||
|
||||
/// Whether to use strong static checking.
|
||||
final bool strongMode;
|
||||
|
||||
/// Initialize options from the given parsed [args].
|
||||
CommandLineOptions._fromArgs(
|
||||
ArgResults args, Map<String, String> definedVariables)
|
||||
: dartSdkPath = args['dart-sdk'],
|
||||
this.definedVariables = definedVariables,
|
||||
analysisOptionsFile = args['options'],
|
||||
disableHints = args['no-hints'],
|
||||
displayVersion = args['version'],
|
||||
enableNullAwareOperators = args['enable-null-aware-operators'],
|
||||
enableStrictCallChecks = args['enable-strict-call-checks'],
|
||||
enableSuperMixins = args['supermixin'],
|
||||
enableTypeChecks = args['enable_type_checks'],
|
||||
hintsAreFatal = args['fatal-hints'],
|
||||
ignoreUnrecognizedFlags = args['ignore-unrecognized-flags'],
|
||||
lints = args['lints'],
|
||||
log = args['log'],
|
||||
machineFormat = args['machine'] || args['format'] == 'machine',
|
||||
packageConfigPath = args['packages'],
|
||||
packageRootPath = args['package-root'],
|
||||
shouldBatch = args['batch'],
|
||||
showPackageWarnings =
|
||||
args['show-package-warnings'] || args['package-warnings'],
|
||||
showSdkWarnings = args['show-sdk-warnings'] || args['warnings'],
|
||||
sourceFiles = args.rest,
|
||||
warningsAreFatal = args['fatal-warnings'],
|
||||
strongMode = args['strong'];
|
||||
|
||||
/// Parse [args] into [CommandLineOptions] describing the specified
|
||||
/// analyzer options. In case of a format error, calls [printAndFail], which
|
||||
/// by default prints an error message to stderr and exits.
|
||||
static CommandLineOptions parse(List<String> args,
|
||||
[printAndFail = printAndFail]) {
|
||||
CommandLineOptions options = _parse(args);
|
||||
// Check SDK.
|
||||
{
|
||||
// Infer if unspecified.
|
||||
if (options.dartSdkPath == null) {
|
||||
Directory sdkDir = getSdkDir(args);
|
||||
if (sdkDir != null) {
|
||||
options.dartSdkPath = sdkDir.path;
|
||||
}
|
||||
}
|
||||
|
||||
var sdkPath = options.dartSdkPath;
|
||||
|
||||
// Check that SDK is specified.
|
||||
if (sdkPath == null) {
|
||||
printAndFail('No Dart SDK found.');
|
||||
}
|
||||
// Check that SDK is existing directory.
|
||||
if (!(new Directory(sdkPath)).existsSync()) {
|
||||
printAndFail('Invalid Dart SDK path: $sdkPath');
|
||||
}
|
||||
}
|
||||
|
||||
// Check package config.
|
||||
{
|
||||
if (options.packageRootPath != null &&
|
||||
options.packageConfigPath != null) {
|
||||
printAndFail("Cannot specify both '--package-root' and '--packages.");
|
||||
}
|
||||
}
|
||||
|
||||
// OK. Report deprecated options.
|
||||
if (options.enableNullAwareOperators) {
|
||||
stderr.writeln(
|
||||
"Info: Option '--enable-null-aware-operators' is no longer needed. Null aware operators are supported by default.");
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
static String _getVersion() {
|
||||
try {
|
||||
// This is relative to bin/snapshot, so ../..
|
||||
String versionPath =
|
||||
Platform.script.resolve('../../version').toFilePath();
|
||||
File versionFile = new File(versionPath);
|
||||
return versionFile.readAsStringSync().trim();
|
||||
} catch (_) {
|
||||
// This happens when the script is not running in the context of an SDK.
|
||||
return "<unknown>";
|
||||
}
|
||||
}
|
||||
|
||||
static CommandLineOptions _parse(List<String> args) {
|
||||
args = args.expand((String arg) => arg.split('=')).toList();
|
||||
var parser = new CommandLineParser()
|
||||
..addFlag('batch',
|
||||
abbr: 'b',
|
||||
help: 'Read commands from standard input (for testing).',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addOption('dart-sdk', help: 'The path to the Dart SDK.')
|
||||
..addOption('packages',
|
||||
help:
|
||||
'Path to the package resolution configuration file, which supplies a mapping of package names to paths. This option cannot be used with --package-root.')
|
||||
..addOption('package-root',
|
||||
abbr: 'p',
|
||||
help:
|
||||
'Path to a package root directory (deprecated). This option cannot be used with --packages.')
|
||||
..addOption('options', help: 'Path to an analysis options file.')
|
||||
..addOption('format',
|
||||
help: 'Specifies the format in which errors are displayed.')
|
||||
..addFlag('machine',
|
||||
help: 'Print errors in a format suitable for parsing (deprecated).',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('version',
|
||||
help: 'Print the analyzer version.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('lints',
|
||||
help: 'Show lint results.', defaultsTo: false, negatable: false)
|
||||
..addFlag('no-hints',
|
||||
help: 'Do not show hint results.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('ignore-unrecognized-flags',
|
||||
help: 'Ignore unrecognized command line flags.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('fatal-hints',
|
||||
help: 'Treat hints as fatal.', defaultsTo: false, negatable: false)
|
||||
..addFlag('fatal-warnings',
|
||||
help: 'Treat non-type warnings as fatal.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('package-warnings',
|
||||
help: 'Show warnings from package: imports.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('show-package-warnings',
|
||||
help: 'Show warnings from package: imports (deprecated).',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('warnings',
|
||||
help: 'Show warnings from SDK imports.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('show-sdk-warnings',
|
||||
help: 'Show warnings from SDK imports (deprecated).',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addFlag('help',
|
||||
abbr: 'h',
|
||||
help: 'Display this help message.',
|
||||
defaultsTo: false,
|
||||
negatable: false)
|
||||
..addOption('url-mapping',
|
||||
help: '--url-mapping=libraryUri,/path/to/library.dart directs the '
|
||||
'analyzer to use "library.dart" as the source for an import '
|
||||
'of "libraryUri".',
|
||||
allowMultiple: true,
|
||||
splitCommas: false)
|
||||
//
|
||||
// Hidden flags.
|
||||
//
|
||||
..addFlag('enable-async',
|
||||
help: 'Enable support for the proposed async feature.',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('enable-enum',
|
||||
help: 'Enable support for the proposed enum feature.',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('enable-null-aware-operators',
|
||||
help: 'Enable support for null-aware operators (DEP 9).',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('enable-strict-call-checks',
|
||||
help: 'Fix issue 21938.',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('enable-new-task-model',
|
||||
help: 'Ennable new task model.',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('supermixin',
|
||||
help: 'Relax restrictions on mixins (DEP 34).',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('log',
|
||||
help: 'Log additional messages and exceptions.',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('enable_type_checks',
|
||||
help: 'Check types in constant evaluation.',
|
||||
defaultsTo: false,
|
||||
negatable: false,
|
||||
hide: true)
|
||||
..addFlag('strong',
|
||||
help: 'Enable strong static checks (https://goo.gl/DqcBsw)');
|
||||
|
||||
try {
|
||||
// TODO(scheglov) https://code.google.com/p/dart/issues/detail?id=11061
|
||||
args =
|
||||
args.map((String arg) => arg == '-batch' ? '--batch' : arg).toList();
|
||||
Map<String, String> definedVariables = <String, String>{};
|
||||
var results = parser.parse(args, definedVariables);
|
||||
// Help requests.
|
||||
if (results['help']) {
|
||||
_showUsage(parser);
|
||||
exit(0);
|
||||
}
|
||||
// Batch mode and input files.
|
||||
if (results['batch']) {
|
||||
if (results.rest.isNotEmpty) {
|
||||
stderr.writeln('No source files expected in the batch mode.');
|
||||
_showUsage(parser);
|
||||
exit(15);
|
||||
}
|
||||
} else if (results['version']) {
|
||||
print('$_binaryName version ${_getVersion()}');
|
||||
exit(0);
|
||||
} else {
|
||||
if (results.rest.isEmpty) {
|
||||
_showUsage(parser);
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
return new CommandLineOptions._fromArgs(results, definedVariables);
|
||||
} on FormatException catch (e) {
|
||||
stderr.writeln(e.message);
|
||||
_showUsage(parser);
|
||||
exit(15);
|
||||
}
|
||||
}
|
||||
|
||||
static _showUsage(parser) {
|
||||
stderr
|
||||
.writeln('Usage: $_binaryName [options...] <libraries to analyze...>');
|
||||
stderr.writeln(parser.getUsage());
|
||||
stderr.writeln('');
|
||||
stderr.writeln(
|
||||
'For more information, see http://www.dartlang.org/tools/analyzer.');
|
||||
}
|
||||
}
|
||||
|
||||
/// Commandline argument parser.
|
||||
///
|
||||
/// TODO(pquitslund): when the args package supports ignoring unrecognized
|
||||
/// options/flags, this class can be replaced with a simple [ArgParser]
|
||||
/// instance.
|
||||
class CommandLineParser {
|
||||
final List<String> _knownFlags;
|
||||
final bool _alwaysIgnoreUnrecognized;
|
||||
final ArgParser _parser;
|
||||
|
||||
/// Creates a new command line parser.
|
||||
CommandLineParser({bool alwaysIgnoreUnrecognized: false})
|
||||
: _knownFlags = <String>[],
|
||||
_alwaysIgnoreUnrecognized = alwaysIgnoreUnrecognized,
|
||||
_parser = new ArgParser(allowTrailingOptions: true);
|
||||
|
||||
ArgParser get parser => _parser;
|
||||
|
||||
/// Defines a flag.
|
||||
/// See [ArgParser.addFlag()].
|
||||
void addFlag(String name,
|
||||
{String abbr,
|
||||
String help,
|
||||
bool defaultsTo: false,
|
||||
bool negatable: true,
|
||||
void callback(bool value),
|
||||
bool hide: false}) {
|
||||
_knownFlags.add(name);
|
||||
_parser.addFlag(name,
|
||||
abbr: abbr,
|
||||
help: help,
|
||||
defaultsTo: defaultsTo,
|
||||
negatable: negatable,
|
||||
callback: callback,
|
||||
hide: hide);
|
||||
}
|
||||
|
||||
/// Defines a value-taking option.
|
||||
/// See [ArgParser.addOption()].
|
||||
void addOption(String name,
|
||||
{String abbr,
|
||||
String help,
|
||||
List<String> allowed,
|
||||
Map<String, String> allowedHelp,
|
||||
String defaultsTo,
|
||||
void callback(value),
|
||||
bool allowMultiple: false,
|
||||
bool splitCommas}) {
|
||||
_knownFlags.add(name);
|
||||
_parser.addOption(name,
|
||||
abbr: abbr,
|
||||
help: help,
|
||||
allowed: allowed,
|
||||
allowedHelp: allowedHelp,
|
||||
defaultsTo: defaultsTo,
|
||||
callback: callback,
|
||||
allowMultiple: allowMultiple,
|
||||
splitCommas: splitCommas);
|
||||
}
|
||||
|
||||
/// Generates a string displaying usage information for the defined options.
|
||||
/// See [ArgParser.usage].
|
||||
String getUsage() => _parser.usage;
|
||||
|
||||
/// Parses [args], a list of command-line arguments, matches them against the
|
||||
/// flags and options defined by this parser, and returns the result. The
|
||||
/// values of any defined variables are captured in the given map.
|
||||
/// See [ArgParser].
|
||||
ArgResults parse(List<String> args, Map<String, String> definedVariables) =>
|
||||
_parser.parse(
|
||||
_filterUnknowns(parseDefinedVariables(args, definedVariables)));
|
||||
|
||||
List<String> parseDefinedVariables(
|
||||
List<String> args, Map<String, String> definedVariables) {
|
||||
int count = args.length;
|
||||
List<String> remainingArgs = <String>[];
|
||||
for (int i = 0; i < count; i++) {
|
||||
String arg = args[i];
|
||||
if (arg == '--') {
|
||||
while (i < count) {
|
||||
remainingArgs.add(args[i++]);
|
||||
}
|
||||
} else if (arg.startsWith("-D")) {
|
||||
definedVariables[arg.substring(2)] = args[++i];
|
||||
} else {
|
||||
remainingArgs.add(arg);
|
||||
}
|
||||
}
|
||||
return remainingArgs;
|
||||
}
|
||||
|
||||
List<String> _filterUnknowns(List<String> args) {
|
||||
// Only filter args if the ignore flag is specified, or if
|
||||
// _alwaysIgnoreUnrecognized was set to true.
|
||||
if (_alwaysIgnoreUnrecognized ||
|
||||
args.contains('--ignore-unrecognized-flags')) {
|
||||
//TODO(pquitslund): replace w/ the following once library skew issues are
|
||||
// sorted out
|
||||
//return args.where((arg) => !arg.startsWith('--') ||
|
||||
// _knownFlags.contains(arg.substring(2)));
|
||||
|
||||
// Filter all unrecognized flags and options.
|
||||
List<String> filtered = <String>[];
|
||||
for (int i = 0; i < args.length; ++i) {
|
||||
String arg = args[i];
|
||||
if (arg.startsWith('--') && arg.length > 2) {
|
||||
String option = arg.substring(2);
|
||||
// strip the last '=value'
|
||||
int equalsOffset = option.lastIndexOf('=');
|
||||
if (equalsOffset != -1) {
|
||||
option = option.substring(0, equalsOffset);
|
||||
}
|
||||
// Check the option
|
||||
if (!_knownFlags.contains(option)) {
|
||||
//"eat" params by advancing to the next flag/option
|
||||
i = _getNextFlagIndex(args, i);
|
||||
} else {
|
||||
filtered.add(arg);
|
||||
}
|
||||
} else {
|
||||
filtered.add(arg);
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
} else {
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
int _getNextFlagIndex(args, i) {
|
||||
for (; i < args.length; ++i) {
|
||||
if (args[i].startsWith('--')) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
110
pkg/analyzer_cli/lib/src/plugin/plugin_manager.dart
Normal file
110
pkg/analyzer_cli/lib/src/plugin/plugin_manager.dart
Normal file
|
@ -0,0 +1,110 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.src.plugin.plugin_manager;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
|
||||
const _manifestFileName = 'plugins.yaml';
|
||||
|
||||
/// Given a local configuration (as defined in `.analysis_options`)
|
||||
/// and information from a plugin manifest, return plugin info
|
||||
/// appropriate for configuring this plugin.
|
||||
PluginInfo combine(PluginInfo localConfig, PluginInfo manifestInfo) {
|
||||
return new PluginInfo(
|
||||
name: localConfig.name,
|
||||
version: manifestInfo.version,
|
||||
className: manifestInfo.className,
|
||||
libraryUri: manifestInfo.libraryUri);
|
||||
}
|
||||
|
||||
/// Call-back to allow for the injection of manifest readers that do not need
|
||||
/// to go to disk (for testing purposes).
|
||||
typedef String ManifestReader(Uri uri);
|
||||
|
||||
/// Wraps a [plugin] info object elaborated with any configuration information
|
||||
/// extracted from an associated manifest and [status].
|
||||
class PluginDetails {
|
||||
/// Plugin status.
|
||||
final PluginStatus status;
|
||||
|
||||
/// Plugin info.
|
||||
final PluginInfo plugin;
|
||||
|
||||
/// Wrap a [plugin] with [status] info.
|
||||
PluginDetails(this.plugin) : status = PluginStatus.Applicable;
|
||||
PluginDetails.notApplicable(this.plugin)
|
||||
: status = PluginStatus.NotApplicable;
|
||||
PluginDetails.notFound(this.plugin) : status = PluginStatus.NotFound;
|
||||
}
|
||||
|
||||
/// Manages plugin information derived from plugin manifests.
|
||||
class PluginManager {
|
||||
/// Mapping from package name to package location.
|
||||
final Map<String, Uri> _packageMap;
|
||||
|
||||
/// The package naming the app to host plugins.
|
||||
final String hostPackage;
|
||||
|
||||
/// Function to perform the reading of manifest URIs. (For testing.)
|
||||
ManifestReader _manifestReader;
|
||||
|
||||
/// Create a plugin manager with backing package map information.
|
||||
PluginManager(this._packageMap, this.hostPackage,
|
||||
[ManifestReader manifestReader]) {
|
||||
_manifestReader =
|
||||
manifestReader != null ? manifestReader : _findAndReadManifestAtUri;
|
||||
}
|
||||
|
||||
/// Find a plugin manifest describing the given [pluginPackage].
|
||||
PluginManifest findManifest(String pluginPackage) {
|
||||
Uri uri = _packageMap[pluginPackage];
|
||||
String contents = _manifestReader(uri);
|
||||
if (contents == null) {
|
||||
return null;
|
||||
}
|
||||
return parsePluginManifestString(contents);
|
||||
}
|
||||
|
||||
/// Return [PluginDetails] derived from associated plugin manifests
|
||||
/// corresponding to plugins specified in the given [config].
|
||||
Iterable<PluginDetails> getPluginDetails(PluginConfig config) =>
|
||||
config.plugins.map((PluginInfo localConfig) {
|
||||
PluginManifest manifest = findManifest(localConfig.name);
|
||||
return _getDetails(localConfig, manifest);
|
||||
});
|
||||
|
||||
String _findAndReadManifestAtUri(Uri uri) {
|
||||
File manifestFile = _findManifest(uri);
|
||||
return manifestFile?.readAsStringSync();
|
||||
}
|
||||
|
||||
File _findManifest(Uri uri) {
|
||||
if (uri == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
Directory directory = new Directory.fromUri(uri);
|
||||
File file = new File(path.join(directory.path, _manifestFileName));
|
||||
|
||||
return file.existsSync() ? file : null;
|
||||
}
|
||||
|
||||
PluginDetails _getDetails(PluginInfo localConfig, PluginManifest manifest) {
|
||||
if (manifest == null) {
|
||||
return new PluginDetails.notFound(localConfig);
|
||||
}
|
||||
if (!manifest.contributesTo.contains(hostPackage)) {
|
||||
return new PluginDetails.notApplicable(localConfig);
|
||||
}
|
||||
|
||||
return new PluginDetails(combine(localConfig, manifest.plugin));
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes plugin status.
|
||||
enum PluginStatus { Applicable, NotApplicable, NotFound }
|
18
pkg/analyzer_cli/pubspec.yaml
Normal file
18
pkg/analyzer_cli/pubspec.yaml
Normal file
|
@ -0,0 +1,18 @@
|
|||
name: analyzer_cli
|
||||
version: 1.1.2
|
||||
author: Dart Team <misc@dartlang.org>
|
||||
description: Command line interface for the Dart Analyzer.
|
||||
homepage: https://github.com/dart-lang/analyzer_cli
|
||||
environment:
|
||||
sdk: '>=1.12.0 <2.0.0'
|
||||
dependencies:
|
||||
analyzer: ^0.26.1+17
|
||||
args: ^0.13.0
|
||||
cli_util: ^0.0.1
|
||||
linter: ^0.1.3+4
|
||||
package_config: ^0.1.1
|
||||
plugin: ^0.1.0
|
||||
yaml: ^2.1.2
|
||||
dev_dependencies:
|
||||
typed_mock: '>=0.0.4 <1.0.0'
|
||||
test: ^0.12.0
|
23
pkg/analyzer_cli/test/all.dart
Normal file
23
pkg/analyzer_cli/test/all.dart
Normal file
|
@ -0,0 +1,23 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'driver_test.dart' as driver;
|
||||
import 'error_test.dart' as error;
|
||||
import 'options_test.dart' as options;
|
||||
import 'plugin_manager_test.dart' as plugin_manager;
|
||||
import 'reporter_test.dart' as reporter;
|
||||
import 'sdk_ext_test.dart' as sdk_ext;
|
||||
import 'strong_mode_test.dart' as strong_mode;
|
||||
import 'super_mixin_test.dart' as super_mixin;
|
||||
|
||||
main() {
|
||||
driver.main();
|
||||
error.main();
|
||||
options.main();
|
||||
plugin_manager.main();
|
||||
reporter.main();
|
||||
sdk_ext.main();
|
||||
strong_mode.main();
|
||||
super_mixin.main();
|
||||
}
|
7
pkg/analyzer_cli/test/data/angular_options.yaml
Normal file
7
pkg/analyzer_cli/test/data/angular_options.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
analyzer:
|
||||
plugins:
|
||||
angular2_analyzer_plugin:
|
||||
path: /Users/pquitslund/src/git/clones/angular2-dart-analyzer/analyzer_plugin/
|
||||
class_name: AngularAnalyzerPlugin
|
||||
library_uri: package:angular2_analyzer_plugin/plugin.dart
|
||||
|
6
pkg/analyzer_cli/test/data/bad_plugin_options.yaml
Normal file
6
pkg/analyzer_cli/test/data/bad_plugin_options.yaml
Normal file
|
@ -0,0 +1,6 @@
|
|||
analyzer:
|
||||
plugins:
|
||||
- lists
|
||||
- are
|
||||
- not
|
||||
- supported
|
0
pkg/analyzer_cli/test/data/empty_options.yaml
Normal file
0
pkg/analyzer_cli/test/data/empty_options.yaml
Normal file
3
pkg/analyzer_cli/test/data/file_with_error.dart
Normal file
3
pkg/analyzer_cli/test/data/file_with_error.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
|
||||
error
|
||||
|
3
pkg/analyzer_cli/test/data/file_with_hint.dart
Normal file
3
pkg/analyzer_cli/test/data/file_with_hint.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
main() {
|
||||
int unused;
|
||||
}
|
3
pkg/analyzer_cli/test/data/file_with_warning.dart
Normal file
3
pkg/analyzer_cli/test/data/file_with_warning.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
main() {
|
||||
undefined();
|
||||
}
|
3
pkg/analyzer_cli/test/data/library_and_parts/lib.dart
Normal file
3
pkg/analyzer_cli/test/data/library_and_parts/lib.dart
Normal file
|
@ -0,0 +1,3 @@
|
|||
library example;
|
||||
|
||||
part 'part1.dart';
|
1
pkg/analyzer_cli/test/data/library_and_parts/part1.dart
Normal file
1
pkg/analyzer_cli/test/data/library_and_parts/part1.dart
Normal file
|
@ -0,0 +1 @@
|
|||
part of example;
|
1
pkg/analyzer_cli/test/data/library_and_parts/part2.dart
Normal file
1
pkg/analyzer_cli/test/data/library_and_parts/part2.dart
Normal file
|
@ -0,0 +1 @@
|
|||
part of nothing;
|
|
@ -0,0 +1,3 @@
|
|||
linter:
|
||||
rules:
|
||||
- camel_case_types
|
7
pkg/analyzer_cli/test/data/linter_project/test_file.dart
Normal file
7
pkg/analyzer_cli/test/data/linter_project/test_file.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.test.data.linter_project.test_file;
|
||||
|
||||
class a {}
|
|
@ -0,0 +1 @@
|
|||
# empty
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.test.data.no_lints_project.test_file;
|
||||
|
||||
class a {}
|
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:foo';
|
||||
|
||||
main() {
|
||||
// import of 'dart:foo' should resolve to a file which defines bar().
|
||||
bar();
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
analyzer:
|
||||
errors:
|
||||
unused_local_variable: ignore
|
||||
language:
|
||||
enableSuperMixins: true
|
|
@ -0,0 +1,7 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.test.data.options_test_project.test_file;
|
||||
|
||||
class a {}
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"dart:foo": "foo.dart"
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
void bar() {}
|
10
pkg/analyzer_cli/test/data/packages_file/sdk_ext_user.dart
Normal file
10
pkg/analyzer_cli/test/data/packages_file/sdk_ext_user.dart
Normal file
|
@ -0,0 +1,10 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
import 'dart:foo';
|
||||
|
||||
main() {
|
||||
// import of 'dart:foo' should resolve to a file which defines bar().
|
||||
bar();
|
||||
}
|
7
pkg/analyzer_cli/test/data/plugin_options.yaml
Normal file
7
pkg/analyzer_cli/test/data/plugin_options.yaml
Normal file
|
@ -0,0 +1,7 @@
|
|||
analyzer:
|
||||
plugins:
|
||||
my_plugin1:
|
||||
version: any
|
||||
library_uri: 'package:my_plugin/my_plugin.dart'
|
||||
class_name: MyPlugin
|
||||
|
18
pkg/analyzer_cli/test/data/strong_example.dart
Normal file
18
pkg/analyzer_cli/test/data/strong_example.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
/// This produces an error with --strong enabled, but not otherwise.
|
||||
class MyIterable extends Iterable<String> {
|
||||
// Error: invalid override
|
||||
Iterator<Object> get iterator => [1, 2, 3].iterator;
|
||||
}
|
||||
|
||||
main() {
|
||||
var i = new MyIterable().iterator..moveNext();
|
||||
print(i.current);
|
||||
|
||||
// Error: type check failed
|
||||
List<String> list = <dynamic>[1, 2, 3];
|
||||
print(list);
|
||||
}
|
18
pkg/analyzer_cli/test/data/super_mixin_example.dart
Normal file
18
pkg/analyzer_cli/test/data/super_mixin_example.dart
Normal file
|
@ -0,0 +1,18 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
/// This produces errors normally, but --supermixin disables them.
|
||||
class Test extends Object with C {
|
||||
void foo() {}
|
||||
}
|
||||
|
||||
abstract class B {
|
||||
void foo();
|
||||
}
|
||||
|
||||
abstract class C extends B {
|
||||
void bar() {
|
||||
super.foo();
|
||||
}
|
||||
}
|
5
pkg/analyzer_cli/test/data/test_file.dart
Normal file
5
pkg/analyzer_cli/test/data/test_file.dart
Normal file
|
@ -0,0 +1,5 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.test.data.test_file;
|
2
pkg/analyzer_cli/test/data/test_options.yaml
Normal file
2
pkg/analyzer_cli/test/data/test_options.yaml
Normal file
|
@ -0,0 +1,2 @@
|
|||
test_plugin:
|
||||
foo: bar
|
484
pkg/analyzer_cli/test/driver_test.dart
Normal file
484
pkg/analyzer_cli/test/driver_test.dart
Normal file
|
@ -0,0 +1,484 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
library analyzer_cli.test.driver;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer/plugin/options.dart';
|
||||
import 'package:analyzer/source/analysis_options_provider.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/error.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:analyzer/src/services/lint.dart';
|
||||
import 'package:analyzer_cli/src/bootloader.dart';
|
||||
import 'package:analyzer_cli/src/driver.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:plugin/plugin.dart';
|
||||
import 'package:test/test.dart';
|
||||
import 'package:yaml/src/yaml_node.dart';
|
||||
|
||||
main() {
|
||||
group('Driver', () {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitCode = exitCode;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
});
|
||||
|
||||
group('options', () {
|
||||
test('custom processor', () {
|
||||
Driver driver = new Driver();
|
||||
TestProcessor processor = new TestProcessor();
|
||||
driver.userDefinedPlugins = [new TestPlugin(processor)];
|
||||
driver.start([
|
||||
'--options',
|
||||
'test/data/test_options.yaml',
|
||||
'test/data/test_file.dart'
|
||||
]);
|
||||
expect(processor.options['test_plugin'], isNotNull);
|
||||
expect(processor.exception, isNull);
|
||||
});
|
||||
});
|
||||
|
||||
group('exit codes', () {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
ExitHandler savedExitHandler;
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitCode = exitCode;
|
||||
savedExitHandler = exitHandler;
|
||||
exitHandler = (code) => exitCode = code;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
exitHandler = savedExitHandler;
|
||||
});
|
||||
|
||||
test('fatal hints', () {
|
||||
drive('test/data/file_with_hint.dart', args: ['--fatal-hints']);
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
|
||||
test('not fatal hints', () {
|
||||
drive('test/data/file_with_hint.dart');
|
||||
expect(exitCode, 0);
|
||||
});
|
||||
|
||||
test('fatal errors', () {
|
||||
drive('test/data/file_with_error.dart');
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
|
||||
test('not fatal warnings', () {
|
||||
drive('test/data/file_with_warning.dart');
|
||||
expect(exitCode, 0);
|
||||
});
|
||||
|
||||
test('fatal warnings', () {
|
||||
drive('test/data/file_with_warning.dart', args: ['--fatal-warnings']);
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
|
||||
test('missing options file', () {
|
||||
drive('test/data/test_file.dart', options: 'test/data/NO_OPTIONS_HERE');
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
|
||||
test('missing dart file', () {
|
||||
drive('test/data/NO_DART_FILE_HERE.dart');
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
|
||||
test('part file', () {
|
||||
drive('test/data/library_and_parts/part2.dart');
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
|
||||
test('non-dangling part file', () {
|
||||
Driver driver = new Driver();
|
||||
driver.start([
|
||||
'test/data/library_and_parts/lib.dart',
|
||||
'test/data/library_and_parts/part1.dart',
|
||||
]);
|
||||
expect(exitCode, 0);
|
||||
});
|
||||
|
||||
test('extra part file', () {
|
||||
Driver driver = new Driver();
|
||||
driver.start([
|
||||
'test/data/library_and_parts/lib.dart',
|
||||
'test/data/library_and_parts/part1.dart',
|
||||
'test/data/library_and_parts/part2.dart',
|
||||
]);
|
||||
expect(exitCode, 3);
|
||||
});
|
||||
});
|
||||
|
||||
group('linter', () {
|
||||
group('lints in options', () {
|
||||
StringSink savedOutSink;
|
||||
Driver driver;
|
||||
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
outSink = new StringBuffer();
|
||||
|
||||
driver = new Driver();
|
||||
driver.start([
|
||||
'--options',
|
||||
'test/data/linter_project/.analysis_options',
|
||||
'--lints',
|
||||
'test/data/linter_project/test_file.dart'
|
||||
]);
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
});
|
||||
|
||||
test('gets analysis options', () {
|
||||
/// Lints should be enabled.
|
||||
expect(driver.context.analysisOptions.lint, isTrue);
|
||||
|
||||
/// The .analysis_options file only specifies 'camel_case_types'.
|
||||
var lintNames = getLints(driver.context).map((r) => r.name);
|
||||
expect(lintNames, orderedEquals(['camel_case_types']));
|
||||
});
|
||||
|
||||
test('generates lints', () {
|
||||
expect(outSink.toString(),
|
||||
contains('[lint] Name types using UpperCamelCase.'));
|
||||
});
|
||||
});
|
||||
|
||||
group('default lints', () {
|
||||
StringSink savedOutSink;
|
||||
Driver driver;
|
||||
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
outSink = new StringBuffer();
|
||||
|
||||
driver = new Driver();
|
||||
driver.start([
|
||||
'--lints',
|
||||
'test/data/linter_project/test_file.dart',
|
||||
'--options',
|
||||
'test/data/linter_project/.analysis_options'
|
||||
]);
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
});
|
||||
|
||||
test('gets default lints', () {
|
||||
/// Lints should be enabled.
|
||||
expect(driver.context.analysisOptions.lint, isTrue);
|
||||
|
||||
/// Default list should include camel_case_types.
|
||||
var lintNames = getLints(driver.context).map((r) => r.name);
|
||||
expect(lintNames, contains('camel_case_types'));
|
||||
});
|
||||
|
||||
test('generates lints', () {
|
||||
expect(outSink.toString(),
|
||||
contains('[lint] Name types using UpperCamelCase.'));
|
||||
});
|
||||
});
|
||||
|
||||
group('no `--lints` flag (none in options)', () {
|
||||
StringSink savedOutSink;
|
||||
Driver driver;
|
||||
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
outSink = new StringBuffer();
|
||||
|
||||
driver = new Driver();
|
||||
driver.start([
|
||||
'test/data/no_lints_project/test_file.dart',
|
||||
'--options',
|
||||
'test/data/no_lints_project/.analysis_options'
|
||||
]);
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
});
|
||||
|
||||
test('lints disabled', () {
|
||||
expect(driver.context.analysisOptions.lint, isFalse);
|
||||
});
|
||||
|
||||
test('no registered lints', () {
|
||||
expect(getLints(driver.context), isEmpty);
|
||||
});
|
||||
|
||||
test('no generated warnings', () {
|
||||
expect(outSink.toString(), contains('No issues found'));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
test('containsLintRuleEntry', () {
|
||||
Map<String, YamlNode> options;
|
||||
options = parseOptions('''
|
||||
linter:
|
||||
rules:
|
||||
- foo
|
||||
''');
|
||||
expect(containsLintRuleEntry(options), true);
|
||||
options = parseOptions('''
|
||||
''');
|
||||
expect(containsLintRuleEntry(options), false);
|
||||
options = parseOptions('''
|
||||
linter:
|
||||
rules:
|
||||
# - foo
|
||||
''');
|
||||
expect(containsLintRuleEntry(options), true);
|
||||
options = parseOptions('''
|
||||
linter:
|
||||
# rules:
|
||||
# - foo
|
||||
''');
|
||||
expect(containsLintRuleEntry(options), false);
|
||||
});
|
||||
|
||||
group('options processing', () {
|
||||
group('error filters', () {
|
||||
StringSink savedOutSink;
|
||||
Driver driver;
|
||||
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
outSink = new StringBuffer();
|
||||
|
||||
driver = new Driver();
|
||||
driver.start([
|
||||
'test/data/options_tests_project/test_file.dart',
|
||||
'--options',
|
||||
'test/data/options_tests_project/.analysis_options'
|
||||
]);
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
});
|
||||
|
||||
test('filters', () {
|
||||
var filters =
|
||||
driver.context.getConfigurationData(CONFIGURED_ERROR_FILTERS);
|
||||
expect(filters, hasLength(1));
|
||||
|
||||
var unused_error = new AnalysisError(
|
||||
new TestSource(), 0, 1, HintCode.UNUSED_LOCAL_VARIABLE, [
|
||||
['x']
|
||||
]);
|
||||
expect(filters.any((filter) => filter(unused_error)), isTrue);
|
||||
});
|
||||
|
||||
test('language config', () {
|
||||
expect(driver.context.analysisOptions.enableSuperMixins, isTrue);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('in temp directory', () {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
Directory savedCurrentDirectory;
|
||||
Directory tempDir;
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitCode = exitCode;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
savedCurrentDirectory = Directory.current;
|
||||
tempDir = Directory.systemTemp.createTempSync('analyzer_');
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
Directory.current = savedCurrentDirectory;
|
||||
tempDir.deleteSync(recursive: true);
|
||||
});
|
||||
|
||||
test('packages folder', () {
|
||||
Directory.current = tempDir;
|
||||
new File(path.join(tempDir.path, 'test.dart')).writeAsStringSync('''
|
||||
import 'package:foo/bar.dart';
|
||||
main() {
|
||||
baz();
|
||||
}
|
||||
''');
|
||||
Directory packagesDir =
|
||||
new Directory(path.join(tempDir.path, 'packages'));
|
||||
packagesDir.createSync();
|
||||
Directory fooDir = new Directory(path.join(packagesDir.path, 'foo'));
|
||||
fooDir.createSync();
|
||||
new File(path.join(fooDir.path, 'bar.dart')).writeAsStringSync('''
|
||||
void baz() {}
|
||||
''');
|
||||
new Driver().start(['test.dart']);
|
||||
expect(exitCode, 0);
|
||||
});
|
||||
|
||||
test('no package resolution', () {
|
||||
Directory.current = tempDir;
|
||||
new File(path.join(tempDir.path, 'test.dart')).writeAsStringSync('''
|
||||
import 'package:path/path.dart';
|
||||
main() {}
|
||||
''');
|
||||
new Driver().start(['test.dart']);
|
||||
expect(exitCode, 3);
|
||||
String stdout = outSink.toString();
|
||||
expect(stdout, contains('[error] Target of URI does not exist'));
|
||||
expect(stdout, contains('1 error found.'));
|
||||
expect(errorSink.toString(), '');
|
||||
});
|
||||
|
||||
test('bad package root', () {
|
||||
new Driver().start(['--package-root', 'does/not/exist', 'test.dart']);
|
||||
String stdout = outSink.toString();
|
||||
expect(exitCode, 3);
|
||||
expect(
|
||||
stdout,
|
||||
contains(
|
||||
'Package root directory (does/not/exist) does not exist.'));
|
||||
});
|
||||
});
|
||||
});
|
||||
group('Bootloader', () {
|
||||
group('plugin processing', () {
|
||||
StringSink savedErrorSink;
|
||||
setUp(() {
|
||||
savedErrorSink = errorSink;
|
||||
errorSink = new StringBuffer();
|
||||
});
|
||||
tearDown(() {
|
||||
errorSink = savedErrorSink;
|
||||
});
|
||||
test('bad format', () {
|
||||
BootLoader loader = new BootLoader();
|
||||
loader.createImage([
|
||||
'--options',
|
||||
'test/data/bad_plugin_options.yaml',
|
||||
'test/data/test_file.dart'
|
||||
]);
|
||||
expect(
|
||||
errorSink.toString(),
|
||||
equals('Plugin configuration skipped: Unrecognized plugin config '
|
||||
'format, expected `YamlMap`, got `YamlList` '
|
||||
'(line 2, column 4)\n'));
|
||||
});
|
||||
test('plugin config', () {
|
||||
BootLoader loader = new BootLoader();
|
||||
Image image = loader.createImage([
|
||||
'--options',
|
||||
'test/data/plugin_options.yaml',
|
||||
'test/data/test_file.dart'
|
||||
]);
|
||||
var plugins = image.config.plugins;
|
||||
expect(plugins, hasLength(1));
|
||||
expect(plugins.first.name, equals('my_plugin1'));
|
||||
});
|
||||
group('plugin validation', () {
|
||||
test('requires class name', () {
|
||||
expect(
|
||||
validate(new PluginInfo(
|
||||
name: 'test_plugin', libraryUri: 'my_package/foo.dart')),
|
||||
isNotNull);
|
||||
});
|
||||
test('requires library URI', () {
|
||||
expect(
|
||||
validate(
|
||||
new PluginInfo(name: 'test_plugin', className: 'MyPlugin')),
|
||||
isNotNull);
|
||||
});
|
||||
test('check', () {
|
||||
expect(
|
||||
validate(new PluginInfo(
|
||||
name: 'test_plugin',
|
||||
className: 'MyPlugin',
|
||||
libraryUri: 'my_package/foo.dart')),
|
||||
isNull);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const emptyOptionsFile = 'test/data/empty_options.yaml';
|
||||
|
||||
/// Start a driver for the given [source], optionally providing additional
|
||||
/// [args] and an [options] file path. The value of [options] defaults to
|
||||
/// an empty options file to avoid unwanted configuration from an otherwise
|
||||
/// discovered options file.
|
||||
void drive(String source,
|
||||
{String options: emptyOptionsFile,
|
||||
List<String> args: const <String>[]}) =>
|
||||
new Driver().start(['--options', options, source]..addAll(args));
|
||||
|
||||
Map<String, YamlNode> parseOptions(String src) =>
|
||||
new AnalysisOptionsProvider().getOptionsFromString(src);
|
||||
|
||||
class TestPlugin extends Plugin {
|
||||
TestProcessor processor;
|
||||
TestPlugin(this.processor);
|
||||
|
||||
@override
|
||||
String get uniqueIdentifier => 'test_plugin.core';
|
||||
|
||||
@override
|
||||
void registerExtensionPoints(RegisterExtensionPoint register) {
|
||||
// None
|
||||
}
|
||||
|
||||
@override
|
||||
void registerExtensions(RegisterExtension register) {
|
||||
register(OPTIONS_PROCESSOR_EXTENSION_POINT_ID, processor);
|
||||
}
|
||||
}
|
||||
|
||||
class TestProcessor extends OptionsProcessor {
|
||||
Map<String, YamlNode> options;
|
||||
Exception exception;
|
||||
|
||||
@override
|
||||
void onError(Exception exception) {
|
||||
this.exception = exception;
|
||||
}
|
||||
|
||||
@override
|
||||
void optionsProcessed(
|
||||
AnalysisContext context, Map<String, YamlNode> options) {
|
||||
this.options = options;
|
||||
}
|
||||
}
|
||||
|
||||
class TestSource implements Source {
|
||||
TestSource();
|
||||
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
55
pkg/analyzer_cli/test/error_test.dart
Normal file
55
pkg/analyzer_cli/test/error_test.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
|
||||
library analyzer_cli.test.error;
|
||||
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
void main() {
|
||||
group('error', () {
|
||||
test("a valid Dart file doesn't throw any errors", () {
|
||||
expect(errorsForFile('void main() => print("Hello, world!");'), isNull);
|
||||
});
|
||||
|
||||
test("an empty Dart file doesn't throw any errors", () {
|
||||
expect(errorsForFile(''), isNull);
|
||||
});
|
||||
|
||||
test("an error on the first line", () {
|
||||
expect(errorsForFile('void foo;\n'), equals(
|
||||
"Error in test.dart: Variables cannot have a type of 'void'\n"));
|
||||
});
|
||||
|
||||
test("an error on the last line", () {
|
||||
expect(errorsForFile('\nvoid foo;'), equals(
|
||||
"Error in test.dart: Variables cannot have a type of 'void'\n"));
|
||||
});
|
||||
|
||||
test("an error in the middle", () {
|
||||
expect(errorsForFile('\nvoid foo;\n'), equals(
|
||||
"Error in test.dart: Variables cannot have a type of 'void'\n"));
|
||||
});
|
||||
|
||||
var veryLongString = new List.filled(107, ' ').join('');
|
||||
|
||||
test("an error at the end of a very long line", () {
|
||||
expect(errorsForFile('$veryLongString void foo;'), equals(
|
||||
"Error in test.dart: Variables cannot have a type of 'void'\n"));
|
||||
});
|
||||
|
||||
test("an error at the beginning of a very long line", () {
|
||||
expect(errorsForFile('void foo; $veryLongString'), equals(
|
||||
"Error in test.dart: Variables cannot have a type of 'void'\n"));
|
||||
});
|
||||
|
||||
test("an error in the middle of a very long line", () {
|
||||
expect(errorsForFile('$veryLongString void foo;$veryLongString'), equals(
|
||||
"Error in test.dart: Variables cannot have a type of 'void'\n"));
|
||||
});
|
||||
});
|
||||
}
|
51
pkg/analyzer_cli/test/mocks.dart
Normal file
51
pkg/analyzer_cli/test/mocks.dart
Normal file
|
@ -0,0 +1,51 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.test.mocks;
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:analyzer/src/generated/engine.dart';
|
||||
import 'package:analyzer/src/generated/source.dart';
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:typed_mock/typed_mock.dart';
|
||||
|
||||
class MockAnalysisError extends TypedMock implements AnalysisError {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockAnalysisErrorInfo extends TypedMock implements AnalysisErrorInfo {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockCommandLineOptions extends TypedMock implements CommandLineOptions {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockErrorCode extends TypedMock implements ErrorCode {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockErrorType extends TypedMock implements ErrorType {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockLineInfo extends TypedMock implements LineInfo {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockLineInfo_Location extends TypedMock implements LineInfo_Location {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
||||
|
||||
class MockSource extends TypedMock implements Source {
|
||||
@override
|
||||
noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||
}
|
191
pkg/analyzer_cli/test/options_test.dart
Normal file
191
pkg/analyzer_cli/test/options_test.dart
Normal file
|
@ -0,0 +1,191 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
library analyzer_cli.test.options;
|
||||
|
||||
import 'package:analyzer_cli/src/options.dart';
|
||||
import 'package:args/args.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
group('CommandLineOptions', () {
|
||||
group('parse', () {
|
||||
test('defaults', () {
|
||||
CommandLineOptions options =
|
||||
CommandLineOptions.parse(['--dart-sdk', '.', 'foo.dart']);
|
||||
expect(options, isNotNull);
|
||||
expect(options.dartSdkPath, isNotNull);
|
||||
expect(options.disableHints, isFalse);
|
||||
expect(options.lints, isFalse);
|
||||
expect(options.displayVersion, isFalse);
|
||||
expect(options.enableStrictCallChecks, isFalse);
|
||||
expect(options.enableSuperMixins, isFalse);
|
||||
expect(options.enableTypeChecks, isFalse);
|
||||
expect(options.hintsAreFatal, isFalse);
|
||||
expect(options.ignoreUnrecognizedFlags, isFalse);
|
||||
expect(options.log, isFalse);
|
||||
expect(options.machineFormat, isFalse);
|
||||
expect(options.packageRootPath, isNull);
|
||||
expect(options.shouldBatch, isFalse);
|
||||
expect(options.showPackageWarnings, isFalse);
|
||||
expect(options.showSdkWarnings, isFalse);
|
||||
expect(options.sourceFiles, equals(['foo.dart']));
|
||||
expect(options.warningsAreFatal, isFalse);
|
||||
expect(options.strongMode, isFalse);
|
||||
});
|
||||
|
||||
test('batch', () {
|
||||
CommandLineOptions options =
|
||||
CommandLineOptions.parse(['--dart-sdk', '.', '--batch']);
|
||||
expect(options.shouldBatch, isTrue);
|
||||
});
|
||||
|
||||
test('defined variables', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '-Dfoo=bar', 'foo.dart']);
|
||||
expect(options.definedVariables['foo'], equals('bar'));
|
||||
expect(options.definedVariables['bar'], isNull);
|
||||
});
|
||||
|
||||
test('enable strict call checks', () {
|
||||
CommandLineOptions options = CommandLineOptions.parse(
|
||||
['--dart-sdk', '.', '--enable-strict-call-checks', 'foo.dart']);
|
||||
expect(options.enableStrictCallChecks, isTrue);
|
||||
});
|
||||
|
||||
test('enable super mixins', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--supermixin', 'foo.dart']);
|
||||
expect(options.enableSuperMixins, isTrue);
|
||||
});
|
||||
|
||||
test('enable type checks', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--enable_type_checks', 'foo.dart']);
|
||||
expect(options.enableTypeChecks, isTrue);
|
||||
});
|
||||
|
||||
test('hintsAreFatal', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--fatal-hints', 'foo.dart']);
|
||||
expect(options.hintsAreFatal, isTrue);
|
||||
});
|
||||
|
||||
test('log', () {
|
||||
CommandLineOptions options =
|
||||
CommandLineOptions.parse(['--dart-sdk', '.', '--log', 'foo.dart']);
|
||||
expect(options.log, isTrue);
|
||||
});
|
||||
|
||||
test('machine format', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--format=machine', 'foo.dart']);
|
||||
expect(options.machineFormat, isTrue);
|
||||
});
|
||||
|
||||
test('no-hints', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--no-hints', 'foo.dart']);
|
||||
expect(options.disableHints, isTrue);
|
||||
});
|
||||
|
||||
test('options', () {
|
||||
CommandLineOptions options = CommandLineOptions.parse(
|
||||
['--dart-sdk', '.', '--options', 'options.yaml', 'foo.dart']);
|
||||
expect(options.analysisOptionsFile, equals('options.yaml'));
|
||||
});
|
||||
|
||||
test('lints', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--lints', 'foo.dart']);
|
||||
expect(options.lints, isTrue);
|
||||
});
|
||||
|
||||
test('package root', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '-p', 'bar', 'foo.dart']);
|
||||
expect(options.packageRootPath, equals('bar'));
|
||||
});
|
||||
|
||||
test('package warnings', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--package-warnings', 'foo.dart']);
|
||||
expect(options.showPackageWarnings, isTrue);
|
||||
});
|
||||
|
||||
test('sdk warnings', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--warnings', 'foo.dart']);
|
||||
expect(options.showSdkWarnings, isTrue);
|
||||
});
|
||||
|
||||
test('sourceFiles', () {
|
||||
CommandLineOptions options = CommandLineOptions.parse(
|
||||
['--dart-sdk', '.', '--log', 'foo.dart', 'foo2.dart', 'foo3.dart']);
|
||||
expect(options.sourceFiles,
|
||||
equals(['foo.dart', 'foo2.dart', 'foo3.dart']));
|
||||
});
|
||||
|
||||
test('warningsAreFatal', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--dart-sdk', '.', '--fatal-warnings', 'foo.dart']);
|
||||
expect(options.warningsAreFatal, isTrue);
|
||||
});
|
||||
|
||||
test('notice unrecognized flags', () {
|
||||
expect(
|
||||
() => new CommandLineParser()
|
||||
.parse(['--bar', '--baz', 'foo.dart'], {}),
|
||||
throwsA(new isInstanceOf<FormatException>()));
|
||||
});
|
||||
|
||||
test('ignore unrecognized flags', () {
|
||||
CommandLineOptions options = CommandLineOptions.parse([
|
||||
'--ignore-unrecognized-flags',
|
||||
'--bar',
|
||||
'--baz',
|
||||
'--dart-sdk',
|
||||
'.',
|
||||
'foo.dart'
|
||||
]);
|
||||
expect(options, isNotNull);
|
||||
expect(options.sourceFiles, equals(['foo.dart']));
|
||||
});
|
||||
|
||||
test('ignore unrecognized options', () {
|
||||
CommandLineParser parser =
|
||||
new CommandLineParser(alwaysIgnoreUnrecognized: true);
|
||||
parser.addOption('optionA');
|
||||
parser.addFlag('flagA');
|
||||
ArgResults argResults =
|
||||
parser.parse(['--optionA=1', '--optionB=2', '--flagA'], {});
|
||||
expect(argResults['optionA'], '1');
|
||||
expect(argResults['flagA'], isTrue);
|
||||
});
|
||||
|
||||
test('strong mode', () {
|
||||
CommandLineOptions options = CommandLineOptions
|
||||
.parse(['--strong', 'foo.dart']);
|
||||
expect(options.strongMode, isTrue);
|
||||
});
|
||||
|
||||
test("can't specify package and package-root", () {
|
||||
var failureMessage;
|
||||
CommandLineOptions.parse(
|
||||
['--package-root', '.', '--packages', '.', 'foo.dart'],
|
||||
(msg) => failureMessage = msg);
|
||||
expect(failureMessage,
|
||||
equals("Cannot specify both '--package-root' and '--packages."));
|
||||
});
|
||||
|
||||
test("bad SDK dir", () {
|
||||
var failureMessage;
|
||||
CommandLineOptions.parse(
|
||||
['--dart-sdk', '&&&&&', 'foo.dart'], (msg) => failureMessage = msg);
|
||||
expect(failureMessage, equals('Invalid Dart SDK path: &&&&&'));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
94
pkg/analyzer_cli/test/plugin_manager_test.dart
Normal file
94
pkg/analyzer_cli/test/plugin_manager_test.dart
Normal file
|
@ -0,0 +1,94 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
library analyzer_cli.test.plugin_manager_test;
|
||||
|
||||
import 'package:analyzer/src/plugin/plugin_configuration.dart';
|
||||
import 'package:analyzer_cli/src/plugin/plugin_manager.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
main() {
|
||||
group('plugin manager tests', () {
|
||||
test('combine plugin info', () {
|
||||
PluginInfo localInfo = new PluginInfo(name: 'my_plugin');
|
||||
PluginInfo manifestInfo = new PluginInfo(
|
||||
className: 'MyPlugin', libraryUri: 'my_plugin/my_plugin.dart');
|
||||
|
||||
PluginInfo merged = combine(localInfo, manifestInfo);
|
||||
expect(merged.name, equals('my_plugin'));
|
||||
expect(merged.className, equals('MyPlugin'));
|
||||
expect(merged.libraryUri, equals('my_plugin/my_plugin.dart'));
|
||||
});
|
||||
|
||||
test('find manifest', () {
|
||||
const manifestSrc = '''
|
||||
library_uri: 'my_plugin/my_plugin.dart'
|
||||
''';
|
||||
var packageMap = {'my_plugin': new Uri.file('my_plugin')};
|
||||
|
||||
PluginManager pm =
|
||||
new PluginManager(packageMap, 'analyzer', (Uri uri) => manifestSrc);
|
||||
|
||||
PluginManifest manifest = pm.findManifest('my_plugin');
|
||||
expect(manifest, isNotNull);
|
||||
expect(manifest.plugin.libraryUri, equals('my_plugin/my_plugin.dart'));
|
||||
});
|
||||
|
||||
final plugin1Uri = new Uri.file('my_plugin1');
|
||||
final plugin2Uri = new Uri.file('my_plugin2');
|
||||
final plugin3Uri = new Uri.file('my_plugin3');
|
||||
|
||||
const serverPluginManifest = '''
|
||||
library_uri: 'my_plugin2/my_plugin2.dart'
|
||||
contributes_to: analysis_server
|
||||
''';
|
||||
const analyzerPluginManifest = '''
|
||||
library_uri: 'my_plugin3/my_plugin3.dart'
|
||||
contributes_to: analyzer
|
||||
''';
|
||||
|
||||
var packageMap = {
|
||||
'my_plugin': plugin1Uri,
|
||||
'my_plugin2': plugin2Uri,
|
||||
'my_plugin3': plugin3Uri
|
||||
};
|
||||
|
||||
var manifestReader = (Uri uri) {
|
||||
if (uri == plugin2Uri) return serverPluginManifest;
|
||||
if (uri == plugin3Uri) return analyzerPluginManifest;
|
||||
return null;
|
||||
};
|
||||
|
||||
test('get plugin details', () {
|
||||
PluginManager pm =
|
||||
new PluginManager(packageMap, 'analysis_server', manifestReader);
|
||||
|
||||
PluginInfo notFound = new PluginInfo(name: 'my_plugin1');
|
||||
PluginInfo applicable = new PluginInfo(name: 'my_plugin2');
|
||||
PluginInfo notApplicable = new PluginInfo(name: 'my_plugin3');
|
||||
|
||||
PluginConfig localConfig =
|
||||
new PluginConfig([notFound, applicable, notApplicable]);
|
||||
|
||||
Iterable<PluginDetails> details = pm.getPluginDetails(localConfig);
|
||||
expect(details, hasLength(3));
|
||||
|
||||
List<PluginDetails> plugins = sortByName(details);
|
||||
|
||||
expect(plugins[0].plugin.name, equals('my_plugin1'));
|
||||
expect(plugins[0].status, equals(PluginStatus.NotFound));
|
||||
expect(plugins[1].plugin.name, equals('my_plugin2'));
|
||||
expect(
|
||||
plugins[1].plugin.libraryUri, equals('my_plugin2/my_plugin2.dart'));
|
||||
expect(plugins[1].status, equals(PluginStatus.Applicable));
|
||||
expect(plugins[2].plugin.name, equals('my_plugin3'));
|
||||
expect(plugins[2].status, equals(PluginStatus.NotApplicable));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
List<PluginDetails> sortByName(Iterable<PluginDetails> details) =>
|
||||
details.toList()
|
||||
..sort((p1, p2) => p1.plugin.name.compareTo(p2.plugin.name));
|
77
pkg/analyzer_cli/test/reporter_test.dart
Normal file
77
pkg/analyzer_cli/test/reporter_test.dart
Normal file
|
@ -0,0 +1,77 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
//@TestOn("vm")
|
||||
library analyzer_cli.test.formatter;
|
||||
|
||||
//import 'package:analyzer/analyzer.dart';
|
||||
//import 'package:analyzer_cli/src/error_formatter.dart';
|
||||
//import 'package:test/test.dart' hide ErrorFormatter;
|
||||
|
||||
//import 'mocks.dart';
|
||||
|
||||
|
||||
//TODO(pq): fix mocks and reimplement
|
||||
main() {
|
||||
// group('reporter', () {
|
||||
// var out = new StringBuffer();
|
||||
//
|
||||
// tearDown(() => out.clear());
|
||||
//
|
||||
// // Options
|
||||
// var options = new MockCommandLineOptions();
|
||||
// when(options.disableHints).thenReturn(true);
|
||||
// when(options.enableTypeChecks).thenReturn(true);
|
||||
// when(options.machineFormat).thenReturn(false);
|
||||
// when(options.hintsAreFatal).thenReturn(false);
|
||||
//
|
||||
// var reporter = new ErrorFormatter(out, options);
|
||||
//
|
||||
// test('error', () {
|
||||
// var error = mockError(ErrorType.SYNTACTIC_ERROR, ErrorSeverity.ERROR);
|
||||
// reporter.formatErrors([error]);
|
||||
//
|
||||
// expect(out.toString(),
|
||||
// equals('''[error] MSG (/foo/bar/baz.dart, line 3, col 3)
|
||||
//1 error found.
|
||||
//'''));
|
||||
// });
|
||||
//
|
||||
// test('hint', () {
|
||||
// var error = mockError(ErrorType.HINT, ErrorSeverity.INFO);
|
||||
// reporter.formatErrors([error]);
|
||||
//
|
||||
// expect(out.toString(),
|
||||
// equals('''[hint] MSG (/foo/bar/baz.dart, line 3, col 3)
|
||||
//1 hint found.
|
||||
//'''));
|
||||
// });
|
||||
// });
|
||||
}
|
||||
|
||||
//MockAnalysisErrorInfo mockError(ErrorType type, ErrorSeverity severity) {
|
||||
// // ErrorInfo
|
||||
// var info = new MockAnalysisErrorInfo();
|
||||
// var error = new MockAnalysisError();
|
||||
// var lineInfo = new MockLineInfo();
|
||||
// var location = new MockLineInfo_Location();
|
||||
// when(location.columnNumber).thenReturn(3);
|
||||
// when(location.lineNumber).thenReturn(3);
|
||||
// when(lineInfo.getLocation(any)).thenReturn(location);
|
||||
// when(info.lineInfo).thenReturn(lineInfo);
|
||||
//
|
||||
// // Details
|
||||
// var code = new MockErrorCode();
|
||||
// when(code.type).thenReturn(type);
|
||||
// when(code.errorSeverity).thenReturn(severity);
|
||||
// when(code.name).thenReturn('mock_code');
|
||||
// when(error.errorCode).thenReturn(code);
|
||||
// when(error.message).thenReturn('MSG');
|
||||
// var source = new MockSource();
|
||||
// when(source.fullName).thenReturn('/foo/bar/baz.dart');
|
||||
// when(error.source).thenReturn(source);
|
||||
// when(info.errors).thenReturn([error]);
|
||||
//
|
||||
// return info;
|
||||
//}
|
55
pkg/analyzer_cli/test/sdk_ext_test.dart
Normal file
55
pkg/analyzer_cli/test/sdk_ext_test.dart
Normal file
|
@ -0,0 +1,55 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
|
||||
/// Test that sdk extensions are properly detected in various scenarios.
|
||||
library analyzer_cli.test.sdk_ext;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart' show Driver, errorSink, outSink;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
main() {
|
||||
group('Sdk extensions', () {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
Directory savedCurrentDirectory;
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitCode = exitCode;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
savedCurrentDirectory = Directory.current;
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
Directory.current = savedCurrentDirectory;
|
||||
});
|
||||
|
||||
test('--packages option supplied', () async {
|
||||
var testDir = path.join(testDirectory, 'data', 'no_packages_file');
|
||||
Directory.current = new Directory(testDir);
|
||||
var packagesPath = path.join('..', 'packages_file', '.packages');
|
||||
new Driver().start(['--packages', packagesPath, 'sdk_ext_user.dart']);
|
||||
|
||||
expect(exitCode, 0);
|
||||
});
|
||||
|
||||
test('.packages file present', () async {
|
||||
var testDir = path.join(testDirectory, 'data', 'packages_file');
|
||||
Directory.current = new Directory(testDir);
|
||||
new Driver().start(['sdk_ext_user.dart']);
|
||||
|
||||
expect(exitCode, 0);
|
||||
});
|
||||
});
|
||||
}
|
53
pkg/analyzer_cli/test/strong_mode_test.dart
Normal file
53
pkg/analyzer_cli/test/strong_mode_test.dart
Normal file
|
@ -0,0 +1,53 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
library analyzer_cli.test.strong_mode;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart' show Driver, errorSink, outSink;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'driver_test.dart';
|
||||
import 'utils.dart';
|
||||
|
||||
/// End-to-end test for --strong checking.
|
||||
///
|
||||
/// Most strong mode tests are in Analyzer, but this verifies the option is
|
||||
/// working and producing extra errors as expected.
|
||||
///
|
||||
/// Generally we don't want a lot of cases here as it requires spinning up a
|
||||
/// full analysis context.
|
||||
void main() {
|
||||
group('--strong', () {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitCode = exitCode;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
});
|
||||
|
||||
test('produces stricter errors', () async {
|
||||
var testPath = path.join(testDirectory, 'data/strong_example.dart');
|
||||
new Driver().start(['--options', emptyOptionsFile, '--strong', testPath]);
|
||||
|
||||
expect(exitCode, 3);
|
||||
var stdout = outSink.toString();
|
||||
expect(stdout, contains('[error] Invalid override'));
|
||||
expect(stdout, contains('[error] Type check failed'));
|
||||
expect(stdout, contains('2 errors found.'));
|
||||
expect(errorSink.toString(), '');
|
||||
});
|
||||
});
|
||||
}
|
67
pkg/analyzer_cli/test/super_mixin_test.dart
Normal file
67
pkg/analyzer_cli/test/super_mixin_test.dart
Normal file
|
@ -0,0 +1,67 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
@TestOn("vm")
|
||||
library analyzer_cli.test.super_mixin;
|
||||
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:analyzer_cli/src/driver.dart' show Driver, errorSink, outSink;
|
||||
import 'package:path/path.dart' as path;
|
||||
import 'package:test/test.dart';
|
||||
|
||||
import 'utils.dart';
|
||||
|
||||
/// End-to-end test for --supermixins.
|
||||
///
|
||||
/// Most super mixin tests are in Analyzer, but this verifies the option is
|
||||
/// working and producing extra errors as expected.
|
||||
///
|
||||
/// Generally we don't want a lot of cases here as it requires spinning up a
|
||||
/// full analysis context.
|
||||
void main() {
|
||||
group('--supermixins', () {
|
||||
StringSink savedOutSink, savedErrorSink;
|
||||
int savedExitCode;
|
||||
setUp(() {
|
||||
savedOutSink = outSink;
|
||||
savedErrorSink = errorSink;
|
||||
savedExitCode = exitCode;
|
||||
outSink = new StringBuffer();
|
||||
errorSink = new StringBuffer();
|
||||
});
|
||||
tearDown(() {
|
||||
outSink = savedOutSink;
|
||||
errorSink = savedErrorSink;
|
||||
exitCode = savedExitCode;
|
||||
});
|
||||
|
||||
test('produces errors when option absent', () async {
|
||||
var testPath = path.join(testDirectory, 'data/super_mixin_example.dart');
|
||||
new Driver().start([testPath]);
|
||||
|
||||
expect(exitCode, 3);
|
||||
var stdout = outSink.toString();
|
||||
expect(
|
||||
stdout,
|
||||
contains(
|
||||
"[error] The class 'C' cannot be used as a mixin because it extends a class other than Object"));
|
||||
expect(
|
||||
stdout,
|
||||
contains(
|
||||
"[error] The class 'C' cannot be used as a mixin because it references 'super'"));
|
||||
expect(stdout, contains('2 errors found.'));
|
||||
expect(errorSink.toString(), '');
|
||||
});
|
||||
|
||||
test('produces no errors when option present', () async {
|
||||
var testPath = path.join(testDirectory, 'data/super_mixin_example.dart');
|
||||
new Driver().start(['--supermixin', testPath]);
|
||||
|
||||
expect(exitCode, 0);
|
||||
var stdout = outSink.toString();
|
||||
expect(stdout, contains('No issues found'));
|
||||
});
|
||||
});
|
||||
}
|
56
pkg/analyzer_cli/test/utils.dart
Normal file
56
pkg/analyzer_cli/test/utils.dart
Normal file
|
@ -0,0 +1,56 @@
|
|||
// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
|
||||
// for details. All rights reserved. Use of this source code is governed by a
|
||||
// BSD-style license that can be found in the LICENSE file.
|
||||
|
||||
library analyzer_cli.test.utils;
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:mirrors';
|
||||
|
||||
import 'package:analyzer/analyzer.dart';
|
||||
import 'package:path/path.dart' as pathos;
|
||||
|
||||
/// Returns the string representation of the [AnalyzerErrorGroup] thrown when
|
||||
/// parsing [contents] as a Dart file. If [contents] doesn't throw any errors,
|
||||
/// this will return null.
|
||||
///
|
||||
/// This replaces the filename in the error string with its basename, since the
|
||||
/// full path will vary from machine to machine. It also replaces the exception
|
||||
/// message with "..." to decouple these tests from the specific exception
|
||||
/// messages.
|
||||
String errorsForFile(String contents) {
|
||||
return withTempDir((temp) {
|
||||
var path = pathos.join(temp, 'test.dart');
|
||||
new File(path).writeAsStringSync(contents);
|
||||
try {
|
||||
parseDartFile(path);
|
||||
} on AnalyzerErrorGroup catch (e) {
|
||||
return e.toString().replaceAllMapped(
|
||||
new RegExp(r"^(Error on line \d+ of )((?:[A-Z]+:)?[^:]+): .*$",
|
||||
multiLine: true),
|
||||
(match) => match[1] + pathos.basename(match[2]) + ': ...');
|
||||
}
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
/// Creates a temporary directory and passes its path to [fn]. Once [fn]
|
||||
/// completes, the temporary directory and all its contents will be deleted.
|
||||
///
|
||||
/// Returns the return value of [fn].
|
||||
dynamic withTempDir(fn(String path)) {
|
||||
var tempDir = Directory.systemTemp.createTempSync('analyzer_').path;
|
||||
try {
|
||||
return fn(tempDir);
|
||||
} finally {
|
||||
new Directory(tempDir).deleteSync(recursive: true);
|
||||
}
|
||||
}
|
||||
|
||||
/// Gets the test directory in a way that works with
|
||||
/// package:test and package:unittest.
|
||||
/// See <https://github.com/dart-lang/test/issues/110> for more info.
|
||||
final String testDirectory = pathos.dirname(
|
||||
pathos.fromUri((reflectClass(_TestUtils).owner as LibraryMirror).uri));
|
||||
|
||||
class _TestUtils {}
|
|
@ -43,7 +43,7 @@ fi
|
|||
|
||||
DART_ROOT="$(cd "${SDK_DIR}/.." ; pwd -P)"
|
||||
|
||||
ANALYZER="$DART_ROOT/third_party/pkg/analyzer_cli/bin/analyzer.dart"
|
||||
ANALYZER="$DART_ROOT/pkg/analyzer_cli/bin/analyzer.dart"
|
||||
|
||||
if [[ `uname` == 'Darwin' ]];
|
||||
then
|
||||
|
|
|
@ -38,7 +38,7 @@ for %%i in ("%SDK_DIR%\..\") do set DART_ROOT=%%~fi
|
|||
rem Remove trailing backslash if there is one
|
||||
if %DART_ROOT:~-1%==\ set DART_ROOT=%DART_ROOT:~0,-1%
|
||||
|
||||
set ANALYZER=%DART_ROOT%\third_party\pkg\analyzer_cli\bin\analyzer.dart
|
||||
set ANALYZER=%DART_ROOT%\pkg\analyzer_cli\bin\analyzer.dart
|
||||
|
||||
rem DART_CONFIGURATION defaults to ReleaseIA32
|
||||
if "%DART_CONFIGURATION%"=="" set DART_CONFIGURATION=ReleaseIA32
|
||||
|
|
|
@ -29,8 +29,9 @@ import sys
|
|||
import utils
|
||||
|
||||
# Useful messages when we find orphaned checkouts.
|
||||
old_directories = {'package_config':
|
||||
'Please remove third_party/pkg/package_config.'}
|
||||
old_directories = {
|
||||
'package_config': 'Please remove third_party/pkg/package_config.',
|
||||
'analyzer_cli': 'Please remove third_party/pkg/analyzer_cli.'}
|
||||
|
||||
def get_options():
|
||||
result = optparse.OptionParser()
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)dart<(EXECUTABLE_SUFFIX)',
|
||||
'../../sdk/lib/_internal/sdk_library_metadata/lib/libraries.dart',
|
||||
'<(SHARED_INTERMEDIATE_DIR)/packages.stamp',
|
||||
'<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../third_party/pkg/analyzer_cli"])',
|
||||
'<!@(["python", "../../tools/list_files.py", "\\.dart$", "../../pkg/analyzer_cli"])',
|
||||
],
|
||||
'outputs': [
|
||||
'<(SHARED_INTERMEDIATE_DIR)/dartanalyzer.dart.snapshot',
|
||||
|
@ -27,7 +27,7 @@
|
|||
'<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)dart<(EXECUTABLE_SUFFIX)',
|
||||
'--snapshot=<(SHARED_INTERMEDIATE_DIR)/dartanalyzer.dart.snapshot',
|
||||
'--package-root=<(PRODUCT_DIR)/packages/',
|
||||
'../../third_party/pkg/analyzer_cli/bin/analyzer.dart',
|
||||
'../../pkg/analyzer_cli/bin/analyzer.dart',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
|
Loading…
Reference in a new issue