Skip to content

Commit

Permalink
Make parts hermetic, fixes #531
Browse files Browse the repository at this point in the history
R=paulberry@google.com, sigmund@google.com

Review URL: https://codereview.chromium.org/1930133002 .
  • Loading branch information
John Messerly committed Apr 28, 2016
1 parent 9d49469 commit 9f764bf
Show file tree
Hide file tree
Showing 3 changed files with 116 additions and 2 deletions.
33 changes: 31 additions & 2 deletions pkg/dev_compiler/lib/src/compiler/compiler.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,15 @@
// 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:collection' show HashSet;
import 'package:args/args.dart' show ArgParser, ArgResults;
import 'package:analyzer/analyzer.dart'
show AnalysisError, CompilationUnit, ErrorSeverity;
show
AnalysisError,
CompilationUnit,
CompileTimeErrorCode,
ErrorSeverity,
StaticWarningCode;
import 'package:analyzer/src/generated/engine.dart' show AnalysisContext;
import 'package:analyzer/src/generated/java_engine.dart' show AnalysisException;
import 'package:analyzer/src/generated/source_io.dart' show Source, SourceKind;
Expand Down Expand Up @@ -52,6 +58,10 @@ class ModuleCompiler {
var trees = <CompilationUnit>[];
var errors = <AnalysisError>[];

// Validate that all parts were explicitly passed in.
// If not, it's an error.
var explicitParts = new HashSet<Source>();
var usedParts = new HashSet<Source>();
for (var sourcePath in unit.sources) {
var sourceUri = Uri.parse(sourcePath);
if (sourceUri.scheme == '') {
Expand All @@ -64,7 +74,8 @@ class ModuleCompiler {
}

// Ignore parts. They need to be handled in the context of their library.
if (context.getKindOf(source) == SourceKind.PART) {
if (context.computeKindOf(source) == SourceKind.PART) {
explicitParts.add(source);
continue;
}

Expand All @@ -74,11 +85,21 @@ class ModuleCompiler {

var library = resolvedTree.element.library;
for (var part in library.parts) {
if (!library.isInSdk) usedParts.add(part.source);
trees.add(context.resolveCompilationUnit(part.source, library));
errors.addAll(context.computeErrors(part.source));
}
}

// Check if all parts were explicitly passed in.
// Also verify all explicitly parts were used.
var missingParts = usedParts.difference(explicitParts);
var unusedParts = explicitParts.difference(usedParts);
errors.addAll(missingParts
.map((s) => new AnalysisError(s, 0, 0, missingPartErrorCode)));
errors.addAll(unusedParts
.map((s) => new AnalysisError(s, 0, 0, unusedPartWarningCode)));

sortErrors(context, errors);
var messages = <String>[];
for (var e in errors) {
Expand Down Expand Up @@ -269,3 +290,11 @@ class JSModuleFile {
return map;
}
}

/// (Public for tests) the error code used when a part is missing.
final missingPartErrorCode = const CompileTimeErrorCode(
'MISSING_PART', 'The part was not supplied as an input to the compiler.');

/// (Public for tests) the error code used when a part is unused.
final unusedPartWarningCode = const StaticWarningCode(
'UNUSED_PART', 'The part was not used by any libraries being compiled.');
79 changes: 79 additions & 0 deletions pkg/dev_compiler/test/worker/worker_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ import 'package:bazel_worker/src/async_message_grouper.dart';
import 'package:bazel_worker/testing.dart';
import 'package:test/test.dart';

import 'package:dev_compiler/src/compiler/compiler.dart'
show missingPartErrorCode, unusedPartWarningCode;

main() {
group('Hello World', () {
final argsFile = new File('test/worker/hello_world.args').absolute;
Expand Down Expand Up @@ -178,6 +181,82 @@ main() {
expect(result.stdout, contains("[error] Expected to find ';'"));
});
});

group('Parts', () {
final partFile = new File('test/worker/greeting.dart').absolute;
final libraryFile = new File('test/worker/hello.dart').absolute;

final outJS = new File('test/worker/output.js').absolute;

setUp(() {
partFile.writeAsStringSync('part of hello;\n'
'String greeting = "hello";');
libraryFile.writeAsStringSync('library hello;\n'
'part "greeting.dart";\n'
'main() => print(greeting);\n');
});

tearDown(() {
if (partFile.existsSync()) partFile.deleteSync();
if (libraryFile.existsSync()) libraryFile.deleteSync();
if (outJS.existsSync()) outJS.deleteSync();
});

test('works if part and library supplied', () {
var result = Process.runSync('dart', [
'bin/dartdevc.dart',
'compile',
'--no-summarize',
'--no-source-map',
'-o',
outJS.path,
partFile.path,
libraryFile.path,
]);
expect(result.stdout, isEmpty);
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
expect(outJS.existsSync(), isTrue);
});

test('error if part is not supplied', () {
var result = Process.runSync('dart', [
'bin/dartdevc.dart',
'compile',
'--no-summarize',
'--no-source-map',
'-o',
outJS.path,
libraryFile.path,
]);
expect(
result.stdout,
startsWith('[error] ${missingPartErrorCode.message} '
'(test/worker/greeting.dart, line 1, col 1)'));
expect(result.stderr, isEmpty);
expect(result.exitCode, 1);
expect(outJS.existsSync(), isFalse);
});

test('warning if part without library is supplied', () {
var result = Process.runSync('dart', [
'bin/dartdevc.dart',
'compile',
'--no-summarize',
'--no-source-map',
'-o',
outJS.path,
partFile.path,
]);
expect(
result.stdout,
startsWith('[warning] ${unusedPartWarningCode.message} '
'(test/worker/greeting.dart, line 1, col 1)'));
expect(result.stderr, isEmpty);
expect(result.exitCode, 0);
expect(outJS.existsSync(), isTrue);
});
});
}

Future<WorkResponse> _readResponse(MessageGrouper messageGrouper) async {
Expand Down
6 changes: 6 additions & 0 deletions pkg/dev_compiler/tool/sdk_expected_errors.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
[error] Missing concrete implementation of 'String.==' (dart:_interceptors/js_string.dart, line 14, col 7)
[error] Type check failed: (_BroadcastSubscription<T> subscription) {subscription._close();} ((_BroadcastSubscription<T>) → void) is not of type (_BufferingStreamSubscription<T>) → void (dart:async/broadcast_stream_controller.dart, line 371, col 24)
[error] Annotation can be only constant variable or constant constructor invocation (dart:core/stacktrace.dart, line 27, col 3)
[error] The part was not supplied as an input to the compiler. (dart:html_common/conversions.dart, line 1, col 1)
[error] The part was not supplied as an input to the compiler. (dart:html_common/conversions_dart2js.dart, line 1, col 1)
[error] The part was not supplied as an input to the compiler. (dart:html_common/css_class_set.dart, line 1, col 1)
[error] The part was not supplied as an input to the compiler. (dart:html_common/device.dart, line 1, col 1)
[error] The part was not supplied as an input to the compiler. (dart:html_common/filtered_element_list.dart, line 1, col 1)
[error] The part was not supplied as an input to the compiler. (dart:html_common/lists.dart, line 1, col 1)
[warning] Unsound implicit cast from dynamic to List<String> (dart:_debugger, line 25, col 5)
[warning] Unsound implicit cast from dynamic to List<String> (dart:_isolate_helper, line 839, col 37)
[warning] Unsound implicit cast from dynamic to List<String> (dart:_isolate_helper, line 886, col 11)
Expand Down

0 comments on commit 9f764bf

Please sign in to comment.