diff --git a/bin/linter.dart b/bin/linter.dart index 0ce3c7d2b..a0edddfef 100644 --- a/bin/linter.dart +++ b/bin/linter.dart @@ -12,6 +12,34 @@ import 'package:linter/src/io.dart'; import 'package:linter/src/linter.dart'; void main(List args) { + runLinter(args, new LinterOptions()); +} + +const processFileFailedExitCode = 65; + +const unableToProcessExitCode = 64; + +String getRoot(List paths) => + paths.length == 1 && new Directory(paths[0]).existsSync() ? paths[0] : null; + +isLinterErrorCode(int code) => + code == unableToProcessExitCode || code == processFileFailedExitCode; + +void printUsage(ArgParser parser, IOSink out, [String error]) { + var message = "Lints Dart source files and pubspecs."; + if (error != null) { + message = error; + } + + out.writeln('''$message +Usage: linter +${parser.usage} + +For more information, see https://github.com/dart-lang/linter +'''); +} + +void runLinter(List args, LinterOptions initialLintOptions) { var parser = new ArgParser(allowTrailingOptions: true); parser @@ -49,7 +77,7 @@ void main(List args) { return; } - var lintOptions = new LinterOptions(); + var lintOptions = initialLintOptions; var configFile = options["config"]; if (configFile != null) { @@ -95,27 +123,3 @@ $err $stack'''); } } - -const processFileFailedExitCode = 65; - -const unableToProcessExitCode = 64; - -String getRoot(List paths) => - paths.length == 1 && new Directory(paths[0]).existsSync() ? paths[0] : null; - -isLinterErrorCode(int code) => - code == unableToProcessExitCode || code == processFileFailedExitCode; - -void printUsage(ArgParser parser, IOSink out, [String error]) { - var message = "Lints Dart source files and pubspecs."; - if (error != null) { - message = error; - } - - out.writeln('''$message -Usage: linter -${parser.usage} - -For more information, see https://github.com/dart-lang/linter -'''); -} diff --git a/lib/src/analysis.dart b/lib/src/analysis.dart index 1cd41eeef..d3624089e 100644 --- a/lib/src/analysis.dart +++ b/lib/src/analysis.dart @@ -49,16 +49,13 @@ class AnalysisDriver { List get resolvers { DartSdk sdk = new DirectoryBasedDartSdk(new JavaFile(sdkDir)); - List resolvers = [ - new DartUriResolver(sdk), - new FileUriResolver() - ]; + List resolvers = [new DartUriResolver(sdk)]; if (options.packageRootPath != null) { JavaFile packageDirectory = new JavaFile(options.packageRootPath); resolvers.add(new PackageUriResolver([packageDirectory])); } else { - PubPackageMapProvider pubPackageMapProvider = - new PubPackageMapProvider(PhysicalResourceProvider.INSTANCE, sdk); + PubPackageMapProvider pubPackageMapProvider = new PubPackageMapProvider( + PhysicalResourceProvider.INSTANCE, sdk, options.runPubList); PackageMapInfo packageMapInfo = pubPackageMapProvider.computePackageMap( PhysicalResourceProvider.INSTANCE.getResource('.')); Map> packageMap = packageMapInfo.packageMap; @@ -67,6 +64,9 @@ class AnalysisDriver { PhysicalResourceProvider.INSTANCE, packageMap)); } } + // File URI resolver must come last so that files inside "/lib" are + // are analyzed via "package:" URI's. + resolvers.add(new FileUriResolver()); return resolvers; } @@ -90,6 +90,12 @@ class AnalysisDriver { for (File file in files) { JavaFile sourceFile = new JavaFile(file.path); Source source = new FileBasedSource.con2(sourceFile.toURI(), sourceFile); + Uri uri = context.sourceFactory.restoreUri(source); + if (uri != null) { + // Ensure that we analyze the file using its canonical URI (e.g. if + // it's in "/lib", analyze it using a "package:" URI). + source = new FileBasedSource.con2(uri, sourceFile); + } sources.add(source); changeSet.addedSource(source); } @@ -155,6 +161,10 @@ class DriverOptions { /// Whether to show lints for the transitive closure of imported and exported /// libraries. bool visitTransitiveClosure = false; + + /// If non-null, the function to use to run pub list. This is used to mock + /// out executions of pub list when testing the linter. + RunPubList runPubList = null; } /// Prints logging information comments to the [outSink] and error messages to diff --git a/test/_data/p4/lib/lib1.dart b/test/_data/p4/lib/lib1.dart new file mode 100644 index 000000000..083ff6fd6 --- /dev/null +++ b/test/_data/p4/lib/lib1.dart @@ -0,0 +1,13 @@ +// 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 lib1; + +import 'package:p4/lib2.dart'; + +import 'lib3.dart'; + +void test() { + f(g()); +} diff --git a/test/_data/p4/lib/lib2.dart b/test/_data/p4/lib/lib2.dart new file mode 100644 index 000000000..ceb5b0403 --- /dev/null +++ b/test/_data/p4/lib/lib2.dart @@ -0,0 +1,9 @@ +// 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 lib2; + +import 'lib3.dart'; + +void f(C c) {} diff --git a/test/_data/p4/lib/lib3.dart b/test/_data/p4/lib/lib3.dart new file mode 100644 index 000000000..4c091919f --- /dev/null +++ b/test/_data/p4/lib/lib3.dart @@ -0,0 +1,9 @@ +// 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 lib3; + +C g() => new C(); + +class C {} diff --git a/test/integration_test.dart b/test/integration_test.dart index 8039a0dce..2919f0184 100644 --- a/test/integration_test.dart +++ b/test/integration_test.dart @@ -4,10 +4,14 @@ library linter.test.integration; +import 'dart:convert'; import 'dart:io'; import 'package:linter/src/config.dart'; import 'package:linter/src/io.dart'; +import 'package:linter/src/linter.dart'; +import 'package:mockito/mockito.dart'; +import 'package:path/path.dart'; import 'package:unittest/unittest.dart'; import '../bin/linter.dart' as dartlint; @@ -54,8 +58,32 @@ defineTests() { }); test('bad pubspec', () { dartlint.main(['test/_data/p3', 'test/_data/p3/_pubpspec.yaml']); + expect( + collectingOut.trim(), endsWith('1 file analyzed, 0 issues found.')); + }); + }); + group('p4', () { + IOSink currentOut = outSink; + CollectingSink collectingOut = new CollectingSink(); + setUp(() => outSink = collectingOut); + tearDown(() { + collectingOut.buffer.clear(); + outSink = currentOut; + }); + test('no warnings due to bad canonicalization', () { + var libPath = new Directory('test/_data/p4/lib').absolute.path; + var options = new LinterOptions([]); + options.runPubList = (_) { + var processResult = new MockProcessResult(); + when(processResult.exitCode).thenReturn(0); + when(processResult.stderr).thenReturn(''); + when(processResult.stdout).thenReturn( + JSON.encode({'packages': {'p4': libPath}, 'input_files': []})); + return processResult; + }; + dartlint.runLinter(['test/_data/p4'], options); expect(collectingOut.trim(), - endsWith('1 file analyzed, 0 issues found.')); + endsWith('3 files analyzed, 0 issues found.')); }); }); @@ -75,3 +103,8 @@ defineTests() { }); }); } + +class MockProcessResult extends Mock implements ProcessResult { + @override + noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); +}