Merge pull request #709 from sass/analysis

Enable stricter analysis options, especially around typing
This commit is contained in:
Natalie Weizenbaum 2019-05-31 17:01:13 +01:00 committed by GitHub
commit 11e3749721
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
44 changed files with 115 additions and 87 deletions

View File

@ -92,7 +92,7 @@ jobs:
# Miscellaneous checks.
- name: static analysis
language: dart
dart_task: {dartanalyzer: --fatal-warnings lib tool test}
dart_task: {dartanalyzer: --fatal-warnings --fatal-infos lib tool test}
- name: code formatting
language: dart
dart_task: dartfmt

View File

@ -1,5 +1,17 @@
analyzer:
strong-mode:
implicit-casts: false
language:
strict-inference: true
strict-raw-types: true
errors:
missing_js_lib_annotation: ignore
deprecated_member_use_from_same_package: ignore
# These are necessary for matching the JS API.
avoid_types_as_parameter_names: ignore
# This has tons of false positives for StreamSubscription.close().
unawaited_futures: ignore
include: package:pedantic/analysis_options.yaml

View File

@ -13,7 +13,7 @@ class Interpolation implements SassNode {
///
/// This contains [String]s and [Expression]s. It never contains two adjacent
/// [String]s.
final List contents;
final List<Object /* String | Expression */ > contents;
final FileSpan span;
@ -33,7 +33,7 @@ class Interpolation implements SassNode {
return first is String ? first : '';
}
Interpolation(Iterable /*(String|Expression)*/ contents, this.span)
Interpolation(Iterable<Object /* String | Expression */ > contents, this.span)
: contents = List.unmodifiable(contents) {
for (var i = 0; i < this.contents.length; i++) {
if (this.contents[i] is! String && this.contents[i] is! Expression) {

View File

@ -505,8 +505,8 @@ class AsyncEnvironment {
}
/// Sets [content] as [this.content] for the duration of [callback].
Future withContent(
UserDefinedCallable<AsyncEnvironment> content, Future callback()) async {
Future<void> withContent(UserDefinedCallable<AsyncEnvironment> content,
Future<void> callback()) async {
var oldContent = _content;
_content = content;
await callback();
@ -514,7 +514,7 @@ class AsyncEnvironment {
}
/// Sets [inMixin] to `true` for the duration of [callback].
Future asMixin(Future callback()) async {
Future<void> asMixin(Future<void> callback()) async {
var oldInMixin = _inMixin;
_inMixin = true;
await callback();

View File

@ -35,6 +35,6 @@ abstract class AsyncCallable {
AsyncBuiltInCallable(name, arguments, (arguments) {
var result = callback(arguments);
if (result is ext.Value) return result as Value;
return (result as Future).then((value) => value as Value);
return (result as Future<ext.Value>).then((value) => value as Value);
});
}

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_environment.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: 3210a5c0528eac456ae8ca7827b65f3976f6b29d
// Checksum: 23c920bd76d38c4ccf2024a0740aeae9672143d0
//
// ignore_for_file: unused_import

View File

@ -30,7 +30,7 @@ import 'options.dart';
/// If [ifModified] is `true`, only recompiles if [source]'s modification time
/// or that of a file it imports is more recent than [destination]'s
/// modification time. Note that these modification times are cached by [graph].
Future compileStylesheet(ExecutableOptions options, StylesheetGraph graph,
Future<void> compileStylesheet(ExecutableOptions options, StylesheetGraph graph,
String source, String destination,
{bool ifModified = false}) async {
var importer = FilesystemImporter('.');

View File

@ -253,7 +253,7 @@ class ExecutableOptions {
(!_isWindowsPath(argument, 0) ||
// Look for colons after index 1, since that's where the drive
// letter is on Windows paths.
argument.indexOf(":", 2) != -1)) {
argument.contains(":", 2))) {
colonArgs = true;
} else if (dirExists(argument)) {
directories.add(argument);

View File

@ -16,7 +16,7 @@ import '../value.dart' as internal;
import '../visitor/evaluate.dart';
/// Runs an interactive SassScript shell according to [options].
Future repl(ExecutableOptions options) async {
Future<void> repl(ExecutableOptions options) async {
var repl = Repl(prompt: '>> ');
var variables = <String, internal.Value>{};
await for (String line in repl.runAsync()) {

View File

@ -19,7 +19,7 @@ import 'compile_stylesheet.dart';
import 'options.dart';
/// Watches all the files in [graph] for changes and updates them as necessary.
Future watch(ExecutableOptions options, StylesheetGraph graph) async {
Future<void> watch(ExecutableOptions options, StylesheetGraph graph) async {
var directoriesToWatch = [
...options.sourceDirectoriesToDestinations.keys,
...options.sourcesToDestinations.keys.map(p.dirname),
@ -30,7 +30,9 @@ Future watch(ExecutableOptions options, StylesheetGraph graph) async {
await Future.wait(directoriesToWatch.map((dir) {
// If a directory doesn't exist, watch its parent directory so that we're
// notified once it starts existing.
while (!dirExists(dir)) dir = p.dirname(dir);
while (!dirExists(dir)) {
dir = p.dirname(dir);
}
return dirWatcher.watch(dir);
}));
@ -117,7 +119,7 @@ class _Watcher {
/// Listens to `watcher.events` and updates the filesystem accordingly.
///
/// Returns a future that will only complete if an unexpected error occurs.
Future watch(MultiDirWatcher watcher) async {
Future<void> watch(MultiDirWatcher watcher) async {
await for (var event in _debounceEvents(watcher.events)) {
var extension = p.extension(event.path);
if (extension != '.sass' && extension != '.scss') continue;
@ -226,7 +228,7 @@ class _Watcher {
var toRecompile = Queue.of(nodes);
var allSucceeded = true;
while (!toRecompile.isEmpty) {
while (toRecompile.isNotEmpty) {
var node = toRecompile.removeFirst();
if (!seen.add(node)) continue;

View File

@ -76,8 +76,9 @@ class Extension {
/// query context for this extender.
void assertCompatibleMediaContext(List<CssMediaQuery> mediaContext) {
if (this.mediaContext == null) return;
if (mediaContext != null && listEquals(this.mediaContext, mediaContext))
if (mediaContext != null && listEquals(this.mediaContext, mediaContext)) {
return;
}
throw SassException(
"You may not @extend selectors across media queries.", span);

View File

@ -48,9 +48,10 @@ class NodeImporter {
/// The importer functions passed in by the user.
final List<JSFunction> _importers;
NodeImporter(this._context, Iterable<String> includePaths, Iterable importers)
NodeImporter(
this._context, Iterable<String> includePaths, Iterable<Object> importers)
: _includePaths = List.unmodifiable(_addSassPath(includePaths)),
_importers = List.unmodifiable(importers);
_importers = List.unmodifiable(importers.cast());
/// Returns [includePaths] followed by any paths loaded from the `SASS_PATH`
/// environment variable.
@ -184,7 +185,7 @@ class NodeImporter {
/// Calls an importer that may or may not be asynchronous.
Future<Object> _callImporterAsync(
JSFunction importer, String url, String previousString) async {
var completer = Completer();
var completer = Completer<Object>();
var result = call3(importer, _context, url, previousString,
allowInterop(completer.complete));

View File

@ -7,8 +7,8 @@ import 'dart:async';
import 'package:tuple/tuple.dart';
class NodeImporter {
NodeImporter(
Object context, Iterable<String> includePaths, Iterable importers);
NodeImporter(Object context, Iterable<String> includePaths,
Iterable<Object> importers);
Tuple2<String, String> load(String url, Uri previous) => null;

View File

@ -48,7 +48,7 @@ class InterpolationBuffer implements StringSink {
void addInterpolation(Interpolation interpolation) {
if (interpolation.contents.isEmpty) return;
Iterable toAdd = interpolation.contents;
Iterable<Object> toAdd = interpolation.contents;
var first = interpolation.contents.first;
if (first is String) {
_text.write(first);

View File

@ -22,7 +22,7 @@ class _FS {
external void mkdirSync(String path);
external _Stat statSync(String path);
external void unlinkSync(String path);
external List readdirSync(String path);
external List<Object> readdirSync(String path);
}
@JS()

View File

@ -40,7 +40,7 @@ import 'visitor/serialize.dart';
/// to run the executable when installed from npm.
void main() {
exports.run_ =
allowInterop((args) => executable.main(List.from(args as List)));
allowInterop((args) => executable.main(List.from(args as List<Object>)));
exports.render = allowInterop(_render);
exports.renderSync = allowInterop(_renderSync);
exports.info =
@ -225,7 +225,7 @@ List<AsyncCallable> _parseFunctions(RenderOptions options,
} else {
result.add(AsyncBuiltInCallable.parsed(tuple.item1, tuple.item2,
(arguments) async {
var completer = Completer();
var completer = Completer<Object>();
var jsArguments = [
...arguments.map(wrapValue),
allowInterop(([result]) => completer.complete(result))
@ -245,8 +245,8 @@ NodeImporter _parseImporter(RenderOptions options, DateTime start) {
List<JSFunction> importers;
if (options.importer == null) {
importers = [];
} else if (options.importer is List) {
importers = (options.importer as List).cast();
} else if (options.importer is List<Object>) {
importers = (options.importer as List<Object>).cast();
} else {
importers = [options.importer as JSFunction];
}

View File

@ -12,5 +12,5 @@ class JSFunction implements Function {
// than calling `Function.prototype.call()`. See sdk#31271.
external call([arg1, arg2, arg3]);
external apply(thisArg, [List args]);
external apply(thisArg, [List<Object> args]);
}

View File

@ -13,7 +13,7 @@ class RenderOptions {
external String get data;
external dynamic get importer;
external dynamic get functions;
external List get includePaths; // contains Strings
external List<Object /* String */ > get includePaths;
external bool get indentedSyntax;
external bool get omitSourceMapUrl;
external String get outFile;

View File

@ -24,7 +24,7 @@ class RenderResultStats {
external int get start;
external int get end;
external int get duration;
external List get includedFiles; // contains Strings
external List<Object /* String */ > get includedFiles;
external factory RenderResultStats(
{String entry,

View File

@ -1459,7 +1459,7 @@ relase. For details, see http://bit.ly/moz-document.
resolveOperations() {
if (operators == null) return;
while (!operators.isEmpty) {
while (operators.isNotEmpty) {
resolveOneOperation();
}
}

View File

@ -37,7 +37,7 @@ class MultiDirWatcher {
///
/// Returns a [Future] that completes when [events] is ready to emit events
/// from [directory].
Future watch(String directory) {
Future<void> watch(String directory) {
var isParentOfExistingDir = false;
for (var existingDir in _watchers.keys.toList()) {
if (!isParentOfExistingDir &&

View File

@ -19,7 +19,7 @@ class NoSourceMapBuffer implements SourceMapBuffer {
T forSpan<T>(SourceSpan span, T callback()) => callback();
void write(Object object) => _buffer.write(object);
void writeAll(Iterable objects, [String separator = ""]) =>
void writeAll(Iterable<Object> objects, [String separator = ""]) =>
_buffer.writeAll(objects, separator);
void writeCharCode(int charCode) => _buffer.writeCharCode(charCode);
void writeln([Object object = ""]) => _buffer.writeln(object);

View File

@ -102,7 +102,7 @@ class SourceMapBuffer implements StringBuffer {
}
}
void writeAll(Iterable objects, [String separator = ""]) =>
void writeAll(Iterable<Object> objects, [String separator = ""]) =>
write(objects.join(separator));
void writeCharCode(int charCode) {

View File

@ -18,7 +18,7 @@ import 'util/character.dart';
final _noSourceUrl = Uri.parse("-");
/// Converts [iter] into a sentence, separating each word with [conjunction].
String toSentence(Iterable iter, [String conjunction]) {
String toSentence(Iterable<Object> iter, [String conjunction]) {
conjunction ??= "and";
if (iter.length == 1) return iter.first.toString();
return iter.take(iter.length - 1).join(", ") + " $conjunction ${iter.last}";
@ -165,11 +165,19 @@ int codeUnitIndexToCodepointIndex(String string, int codeUnitIndex) {
}
/// Returns whether [list1] and [list2] have the same contents.
bool listEquals<T>(List<T> list1, List<T> list2) =>
const ListEquality().equals(list1, list2);
bool listEquals(List<Object> list1, List<Object> list2) =>
const ListEquality<Object>().equals(list1, list2);
/// Returns a hash code for [list] that matches [listEquals].
int listHash(List list) => const ListEquality().hash(list);
int listHash(List<Object> list) => const ListEquality<Object>().hash(list);
/// Returns whether [map1] and [map2] have the same contents.
bool mapEquals(Map<Object, Object> map1, Map<Object, Object> map2) =>
const MapEquality<Object, Object>().equals(map1, map2);
/// Returns a hash code for [map] that matches [mapEquals].
int mapHash(Map<Object, Object> map) =>
const MapEquality<Object, Object>().hash(map);
/// Returns a stack frame for the given [span] with the given [member] name.
///
@ -395,7 +403,7 @@ void mapAddAll2<K1, K2, V>(
/// Rotates the element in list from [start] (inclusive) to [end] (exclusive)
/// one index higher, looping the final element back to [start].
void rotateSlice(List list, int start, int end) {
void rotateSlice(List<Object> list, int start, int end) {
var element = list[end - 1];
for (var i = start; i < end; i++) {
var next = list[i];

View File

@ -2,10 +2,9 @@
// MIT-style license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT.
import 'package:collection/collection.dart';
import '../visitor/interface/value.dart';
import '../value.dart';
import '../utils.dart';
import 'external/value.dart' as ext;
class SassMap extends Value implements ext.SassMap {
@ -33,11 +32,9 @@ class SassMap extends Value implements ext.SassMap {
SassMap assertMap([String name]) => this;
bool operator ==(other) =>
(other is SassMap &&
const MapEquality().equals(other.contents, contents)) ||
(other is SassMap && mapEquals(other.contents, contents)) ||
(contents.isEmpty && other is SassList && other.asList.isEmpty);
int get hashCode => contents.isEmpty
? const SassList.empty().hashCode
: const MapEquality().hash(contents);
int get hashCode =>
contents.isEmpty ? const SassList.empty().hashCode : mapHash(contents);
}

View File

@ -42,7 +42,8 @@ import 'interface/modifiable_css.dart';
import 'interface/statement.dart';
/// A function that takes a callback with no arguments.
typedef _ScopeCallback = Future Function(Future Function() callback);
typedef _ScopeCallback = Future<void> Function(
Future<void> Function() callback);
/// Converts [stylesheet] to a plain CSS tree.
///
@ -675,7 +676,7 @@ class _EvaluateVisitor
ModifiableCssParentNode newParent,
AtRootQuery query,
List<ModifiableCssParentNode> included) {
var scope = (Future callback()) async {
var scope = (Future<void> callback()) async {
// We can't use [_withParent] here because it'll add the node to the tree
// in the wrong place.
var oldParent = _parent;
@ -985,7 +986,7 @@ class _EvaluateVisitor
}
/// Adds the stylesheet imported by [import] to the current document.
Future _visitDynamicImport(DynamicImport import) async {
Future<void> _visitDynamicImport(DynamicImport import) async {
var result = await _loadStylesheet(import.url, import.span);
var importer = result.item1;
var stylesheet = result.item2;
@ -1104,7 +1105,7 @@ class _EvaluateVisitor
}
/// Adds a CSS import for [import].
Future _visitStaticImport(StaticImport import) async {
Future<void> _visitStaticImport(StaticImport import) async {
// NOTE: this logic is largely duplicated in [visitCssImport]. Most changes
// here should be mirrored there.
@ -1580,6 +1581,7 @@ class _EvaluateVisitor
_verifyArguments(positional.length, named, IfExpression.declaration, node);
// ignore: prefer_is_empty
var condition = positional.length > 0 ? positional[0] : named["condition"];
var ifTrue = positional.length > 1 ? positional[1] : named["if-true"];
var ifFalse = positional.length > 2 ? positional[2] : named["if-false"];

View File

@ -5,7 +5,7 @@
// DO NOT EDIT. This file was generated from async_evaluate.dart.
// See tool/grind/synchronize.dart for details.
//
// Checksum: 71e3dfeef2683bd246e21ddfd25b21df408cf18e
// Checksum: f316e802a42334d416c62f1d60d281a8262016f2
//
// ignore_for_file: unused_import
@ -1574,6 +1574,7 @@ class _EvaluateVisitor
_verifyArguments(positional.length, named, IfExpression.declaration, node);
// ignore: prefer_is_empty
var condition = positional.length > 0 ? positional[0] : named["condition"];
var ifTrue = positional.length > 1 ? positional[1] : named["if-true"];
var ifFalse = positional.length > 2 ? positional[2] : named["if-false"];

View File

@ -11,7 +11,7 @@ List<DynamicImport> findImports(Stylesheet stylesheet) =>
/// A visitor that traverses a stylesheet and records all the [DynamicImport]s
/// it contains.
class _FindImportsVisitor extends RecursiveStatementVisitor {
class _FindImportsVisitor extends RecursiveStatementVisitor<void> {
final _imports = <DynamicImport>[];
List<DynamicImport> run(Stylesheet stylesheet) {

View File

@ -106,7 +106,8 @@ String serializeSelector(Selector selector, {bool inspect = false}) {
}
/// A visitor that converts CSS syntax trees to plain strings.
class _SerializeVisitor implements CssVisitor, ValueVisitor, SelectorVisitor {
class _SerializeVisitor
implements CssVisitor<void>, ValueVisitor<void>, SelectorVisitor<void> {
/// A buffer that contains the CSS produced so far.
final SourceMapBuffer _buffer;

View File

@ -17,7 +17,7 @@ void sharedTests(
{Map<String, String> environment})) {
/// Runs the executable on [arguments] plus an output file, then verifies that
/// the contents of the output file match [expected].
Future expectCompiles(List<String> arguments, expected,
Future<void> expectCompiles(List<String> arguments, expected,
{Map<String, String> environment}) async {
var sass = await runSass([...arguments, "out.css", "--no-source-map"],
environment: environment);

View File

@ -31,7 +31,7 @@ void sharedTests(Future<TestProcess> runSass(Iterable<String> arguments)) {
/// Modifying a file very quickly after it was processed can go
/// unrecognized, especially on Windows where filesystem operations can have
/// very high delays.
Future tickIfPoll() => poll ? tick : Future.value();
Future<void> tickIfPoll() => poll ? tick : Future.value();
group("${poll ? 'with' : 'without'} --poll", () {
group("when started", () {

View File

@ -11,7 +11,7 @@ import 'package:sass/src/io.dart';
import 'io.dart';
hybridMain(StreamChannel channel) async {
void hybridMain(StreamChannel<Object> channel) async {
ensureUpToDate("build/npm/sass.dart.js", "pub run grinder npm-package");
channel.sink.close();
}
@ -19,7 +19,7 @@ hybridMain(StreamChannel channel) async {
/// Ensures that the NPM package is compiled and up-to-date.
///
/// This is safe to call even outside the Dart VM.
Future ensureNpmPackage() async {
Future<void> ensureNpmPackage() async {
// spawnHybridUri() doesn't currently work on Windows and Node due to busted
// path handling in the SDK.
if (isNode && isWindows) return;

View File

@ -12,21 +12,21 @@ Future<String> createTempDir() async => (await runHybridExpression(
'(await Directory.systemTemp.createTemp("dart_sass_")).path')) as String;
/// Writes [text] to [path].
Future writeTextFile(String path, String text) => runHybridExpression(
Future<void> writeTextFile(String path, String text) => runHybridExpression(
'new File(message[0]).writeAsString(message[1])', [path, text]);
/// Creates a directoy at [path].
Future createDirectory(String path) =>
Future<void> createDirectory(String path) =>
runHybridExpression('new Directory(message).create()', path);
/// Recursively deletes the directoy at [path].
Future deleteDirectory(String path) =>
Future<void> deleteDirectory(String path) =>
runHybridExpression('new Directory(message).delete(recursive: true)', path);
/// Runs [expression], which may be asynchronous, in a hybrid isolate.
///
/// Returns the result of [expression] if it's JSON-serializable.
Future runHybridExpression(String expression, [message]) async {
Future<Object> runHybridExpression(String expression, [message]) async {
var channel = spawnHybridCode('''
import 'dart:async';
import 'dart:convert';

View File

@ -180,7 +180,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(callConstructor(sass.types.Number, [1]));
});
})
@ -210,7 +210,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(JSError("aw beans"));
});
})
@ -223,7 +223,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(callConstructor(sass.types.Error, ["aw beans"]));
});
})
@ -236,7 +236,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(null);
});
})
@ -249,7 +249,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done();
});
})
@ -291,7 +291,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(callConstructor(sass.types.Number, [1]));
});
})
@ -313,7 +313,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(JSError("aw beans"));
});
})
@ -327,7 +327,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(null);
});
})
@ -341,7 +341,7 @@ void main() {
data: "a {b: foo()}",
functions: jsify({
"foo": allowInterop((done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done();
});
})

View File

@ -589,7 +589,7 @@ void main() {
render(RenderOptions(
data: "@import 'foo'",
importer: allowInterop((_, __, done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(NodeImporterResult(contents: 'a {b: c}'));
});
}))),
@ -601,7 +601,7 @@ void main() {
renderError(RenderOptions(
data: "@import 'foo'",
importer: allowInterop((_, __, done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(JSError('oh no'));
});
}))),
@ -651,7 +651,7 @@ void main() {
render(RenderOptions(
data: "@import 'foo'",
importer: allowInterop((_, __, done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(NodeImporterResult(contents: 'a {b: c}'));
});
}),
@ -686,7 +686,7 @@ void main() {
renderError(RenderOptions(
data: "@import 'foo'",
importer: allowInterop((_, __, done) {
Future.delayed(Duration.zero).then((_) {
Timer(Duration.zero, () {
done(JSError('oh no'));
});
}),

View File

@ -12,7 +12,7 @@ import 'package:path/path.dart' as p;
import 'package:source_maps/source_maps.dart';
import 'package:test/test.dart';
import 'package:sass/sass.dart' as dartSass;
import 'package:sass/sass.dart' as dart_sass;
import '../ensure_npm_package.dart';
import '../hybrid.dart';
@ -39,7 +39,8 @@ void main() {
test("includes correct mappings", () {
SingleMapping expectedMap;
dartSass.compileString("a {b: c}", sourceMap: (map) => expectedMap = map);
dart_sass.compileString("a {b: c}",
sourceMap: (map) => expectedMap = map);
expectedMap.targetUrl = "out.css";
expect(map, containsPair("mappings", expectedMap.toJson()["mappings"]));

View File

@ -108,8 +108,7 @@ void setEnvironmentVariable(String name, String value) {
// Runs [callback] with the `SASS_PATH` environment variable set to [paths].
T withSassPath<T>(List<String> paths, T callback()) {
var oldSassPath =
setEnvironmentVariable("SASS_PATH", paths.join(isWindows ? ';' : ':'));
setEnvironmentVariable("SASS_PATH", paths.join(isWindows ? ';' : ':'));
try {
return callback();

View File

@ -14,7 +14,7 @@ import 'utils.dart';
void main() {
group("from a parameter", () {
var value;
Object value;
setUp(() {
value = parseValue("null");
});

View File

@ -17,7 +17,7 @@ final _sourceMapCommentRegExp = RegExp(r"/\*# sourceMappingURL=(.*) \*/\s*$");
///
/// Windows (or at least Appveyor) seems to require a more coarse-grained time
/// than Unixes.
Future get tick =>
Future<void> get tick =>
Future.delayed(Duration(milliseconds: isWindows ? 1000 : 50));
/// Loads and decodes the source map embedded as a `data:` URI in [css].

View File

@ -68,7 +68,7 @@ benchmarkGenerate() async {
/// If [header] is passed, it's written before [text]. If [footer] is passed,
/// it's written after [text]. If the file already exists and is the expected
/// length, it's not written.
Future _writeNTimes(String path, String text, num times,
Future<void> _writeNTimes(String path, String text, num times,
{String header, String footer}) async {
var file = File(path);
var expectedLength = (header == null ? 0 : header.length + 1) +

View File

@ -69,7 +69,7 @@ npmReleasePackage() => _npm(release: true);
void _npm({@required bool release}) {
var json = {
...(jsonDecode(File('package/package.json').readAsStringSync())
as Map<String, dynamic>),
as Map<String, Object>),
"version": version
};

View File

@ -4,10 +4,11 @@
import 'dart:io';
import 'package:collection/collection.dart';
import 'package:grinder/grinder.dart';
import 'package:pub_semver/pub_semver.dart';
import 'package:sass/src/utils.dart';
import 'utils.dart';
@Task('Verify that the package is in a good state to release.')
@ -17,7 +18,7 @@ sanityCheckBeforeRelease() {
fail("TRAVIS_TAG $travisTag is different than pubspec version $version.");
}
if (const ListEquality().equals(Version.parse(version).preRelease, ["dev"])) {
if (listEquals(Version.parse(version).preRelease, ["dev"])) {
fail("$version is a dev release.");
}

View File

@ -7,7 +7,6 @@ import 'dart:io';
import 'package:archive/archive.dart';
import 'package:grinder/grinder.dart';
import 'package:meta/meta.dart';
import 'package:path/path.dart' as p;
import 'package:http/http.dart' as http;
@ -61,7 +60,7 @@ packageMacOs() => _buildPackage("macos");
packageWindows() => _buildPackage("windows");
/// Builds standalone 32- and 64-bit Sass packages for the given [os].
Future _buildPackage(String os) async {
Future<void> _buildPackage(String os) async {
var client = http.Client();
await Future.wait(["ia32", "x64"].map((architecture) async {
// TODO: Compile a single executable that embeds the Dart VM and the

View File

@ -5,6 +5,9 @@
import 'dart:convert';
import 'dart:io';
// See https://groups.google.com/a/dartlang.org/d/msg/misc/bZ0AGuEo41c/u05-1M7yAgAJ.
//
// ignore: deprecated_member_use
import 'package:analyzer/analyzer.dart';
import 'package:analyzer/dart/ast/ast.dart';
import 'package:analyzer/dart/ast/token.dart';
@ -56,7 +59,7 @@ synchronize() {
/// original source where possible. It tracks the [_position] at the end of the
/// text that's been written, and writes from that position to the new position
/// whenever text needs to be emitted.
class _Visitor extends RecursiveAstVisitor {
class _Visitor extends RecursiveAstVisitor<void> {
/// The source of the original asynchronous file.
final String _source;