Fix newly enforce package:pedantic lints (#880)

- always_declare_return_types
- annotate_overrides
- prefer_collection_literals
- prefer_conditional_assignment
- prefer_final_fields
- prefer_if_null_operators
- prefer_single_quotes
- prefer_spread_collections
- use_function_type_syntax_for_parameters

Fix existing hint missing_js_lib_annotation
diff --git a/benchmark/benchmark.dart b/benchmark/benchmark.dart
index 117558c..6d02384 100644
--- a/benchmark/benchmark.dart
+++ b/benchmark/benchmark.dart
@@ -16,8 +16,8 @@
 /// Note, these files use ".txt" because while they can be *parsed* correctly,
 /// they don't resolve without error. That's OK because the formatter doesn't
 /// care about that.
-final source = loadFile("before.dart.txt");
-final expected = loadFile("after.dart.txt");
+final source = loadFile('before.dart.txt');
+final expected = loadFile('after.dart.txt');
 
 void main(List<String> args) {
   var best = 99999999.0;
@@ -43,7 +43,7 @@
     // Sanity check to make sure the output is what we expect and to make sure
     // the VM doesn't optimize "dead" code away.
     if (result != expected) {
-      print("Incorrect output:\n$result");
+      print('Incorrect output:\n$result');
       exit(1);
     }
 
@@ -53,7 +53,7 @@
     printResult("Run ${padLeft('#$i', 3)}", elapsed);
   }
 
-  printResult("Best   ", best);
+  printResult('Best   ', best);
 }
 
 String loadFile(String name) {
@@ -62,14 +62,14 @@
 }
 
 void printResult(String label, double time) {
-  print("$label: ${padLeft(time.toStringAsFixed(2), 4)}ms "
+  print('$label: ${padLeft(time.toStringAsFixed(2), 4)}ms '
       "${'=' * ((time * 5).toInt())}");
 }
 
 String padLeft(input, int length) {
   var result = input.toString();
   if (result.length < length) {
-    result = " " * (length - result.length) + result;
+    result = ' ' * (length - result.length) + result;
   }
 
   return result;
diff --git a/bin/format.dart b/bin/format.dart
index 373ec61..c7a8dd1 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -15,58 +15,58 @@
 import 'package:dart_style/src/style_fix.dart';
 
 // Note: The following line of code is modified by tool/grind.dart.
-const version = "1.3.3";
+const version = '1.3.3';
 
 void main(List<String> args) {
   var parser = ArgParser(allowTrailingOptions: true);
 
-  parser.addSeparator("Common options:");
-  parser.addFlag("help",
-      abbr: "h", negatable: false, help: "Shows usage information.");
-  parser.addFlag("version",
-      negatable: false, help: "Shows version information.");
-  parser.addOption("line-length",
-      abbr: "l", help: "Wrap lines longer than this.", defaultsTo: "80");
-  parser.addFlag("overwrite",
-      abbr: "w",
+  parser.addSeparator('Common options:');
+  parser.addFlag('help',
+      abbr: 'h', negatable: false, help: 'Shows usage information.');
+  parser.addFlag('version',
+      negatable: false, help: 'Shows version information.');
+  parser.addOption('line-length',
+      abbr: 'l', help: 'Wrap lines longer than this.', defaultsTo: '80');
+  parser.addFlag('overwrite',
+      abbr: 'w',
       negatable: false,
-      help: "Overwrite input files with formatted output.");
-  parser.addFlag("dry-run",
-      abbr: "n",
+      help: 'Overwrite input files with formatted output.');
+  parser.addFlag('dry-run',
+      abbr: 'n',
       negatable: false,
-      help: "Show which files would be modified but make no changes.");
+      help: 'Show which files would be modified but make no changes.');
 
-  parser.addSeparator("Non-whitespace fixes (off by default):");
-  parser.addFlag("fix", negatable: false, help: "Apply all style fixes.");
+  parser.addSeparator('Non-whitespace fixes (off by default):');
+  parser.addFlag('fix', negatable: false, help: 'Apply all style fixes.');
 
   for (var fix in StyleFix.all) {
     // TODO(rnystrom): Allow negating this if used in concert with "--fix"?
-    parser.addFlag("fix-${fix.name}", negatable: false, help: fix.description);
+    parser.addFlag('fix-${fix.name}', negatable: false, help: fix.description);
   }
 
-  parser.addSeparator("Other options:");
-  parser.addOption("indent",
-      abbr: "i", help: "Spaces of leading indentation.", defaultsTo: "0");
-  parser.addFlag("machine",
-      abbr: "m",
+  parser.addSeparator('Other options:');
+  parser.addOption('indent',
+      abbr: 'i', help: 'Spaces of leading indentation.', defaultsTo: '0');
+  parser.addFlag('machine',
+      abbr: 'm',
       negatable: false,
-      help: "Produce machine-readable JSON output.");
-  parser.addFlag("set-exit-if-changed",
+      help: 'Produce machine-readable JSON output.');
+  parser.addFlag('set-exit-if-changed',
       negatable: false,
-      help: "Return exit code 1 if there are any formatting changes.");
-  parser.addFlag("follow-links",
+      help: 'Return exit code 1 if there are any formatting changes.');
+  parser.addFlag('follow-links',
       negatable: false,
-      help: "Follow links to files and directories.\n"
-          "If unset, links will be ignored.");
-  parser.addOption("preserve",
+      help: 'Follow links to files and directories.\n'
+          'If unset, links will be ignored.');
+  parser.addOption('preserve',
       help: 'Selection to preserve, formatted as "start:length".');
-  parser.addOption("stdin-name",
-      help: "The path name to show when an error occurs in source read from "
-          "stdin.",
-      defaultsTo: "<stdin>");
+  parser.addOption('stdin-name',
+      help: 'The path name to show when an error occurs in source read from '
+          'stdin.',
+      defaultsTo: '<stdin>');
 
-  parser.addFlag("profile", negatable: false, hide: true);
-  parser.addFlag("transform", abbr: "t", negatable: false, hide: true);
+  parser.addFlag('profile', negatable: false, hide: true);
+  parser.addFlag('transform', abbr: 't', negatable: false, hide: true);
 
   ArgResults argResults;
   try {
@@ -75,24 +75,24 @@
     usageError(parser, err.message);
   }
 
-  if (argResults["help"]) {
+  if (argResults['help']) {
     printUsage(parser);
     return;
   }
 
-  if (argResults["version"]) {
+  if (argResults['version']) {
     print(version);
     return;
   }
 
   // Can only preserve a selection when parsing from stdin.
   List<int> selection;
-  if (argResults["preserve"] != null && argResults.rest.isNotEmpty) {
-    usageError(parser, "Can only use --preserve when reading from stdin.");
+  if (argResults['preserve'] != null && argResults.rest.isNotEmpty) {
+    usageError(parser, 'Can only use --preserve when reading from stdin.');
   }
 
   try {
-    selection = parseSelection(argResults["preserve"]);
+    selection = parseSelection(argResults['preserve']);
   } on FormatException catch (_) {
     usageError(
         parser,
@@ -100,47 +100,47 @@
         '"${argResults['preserve']}".');
   }
 
-  if (argResults["dry-run"] && argResults["overwrite"]) {
+  if (argResults['dry-run'] && argResults['overwrite']) {
     usageError(
-        parser, "Cannot use --dry-run and --overwrite at the same time.");
+        parser, 'Cannot use --dry-run and --overwrite at the same time.');
   }
 
-  checkForReporterCollision(String chosen, String other) {
+  void checkForReporterCollision(String chosen, String other) {
     if (!argResults[other]) return;
 
-    usageError(parser, "Cannot use --$chosen and --$other at the same time.");
+    usageError(parser, 'Cannot use --$chosen and --$other at the same time.');
   }
 
   var reporter = OutputReporter.print;
-  if (argResults["dry-run"]) {
-    checkForReporterCollision("dry-run", "overwrite");
-    checkForReporterCollision("dry-run", "machine");
+  if (argResults['dry-run']) {
+    checkForReporterCollision('dry-run', 'overwrite');
+    checkForReporterCollision('dry-run', 'machine');
 
     reporter = OutputReporter.dryRun;
-  } else if (argResults["overwrite"]) {
-    checkForReporterCollision("overwrite", "machine");
+  } else if (argResults['overwrite']) {
+    checkForReporterCollision('overwrite', 'machine');
 
     if (argResults.rest.isEmpty) {
       usageError(parser,
-          "Cannot use --overwrite without providing any paths to format.");
+          'Cannot use --overwrite without providing any paths to format.');
     }
 
     reporter = OutputReporter.overwrite;
-  } else if (argResults["machine"]) {
+  } else if (argResults['machine']) {
     reporter = OutputReporter.printJson;
   }
 
-  if (argResults["profile"]) {
+  if (argResults['profile']) {
     reporter = ProfileReporter(reporter);
   }
 
-  if (argResults["set-exit-if-changed"]) {
+  if (argResults['set-exit-if-changed']) {
     reporter = SetExitReporter(reporter);
   }
 
   int pageWidth;
   try {
-    pageWidth = int.parse(argResults["line-length"]);
+    pageWidth = int.parse(argResults['line-length']);
   } on FormatException catch (_) {
     usageError(
         parser,
@@ -150,7 +150,7 @@
 
   int indent;
   try {
-    indent = int.parse(argResults["indent"]);
+    indent = int.parse(argResults['indent']);
     if (indent < 0 || indent.toInt() != indent) throw FormatException();
   } on FormatException catch (_) {
     usageError(
@@ -159,22 +159,22 @@
         '"${argResults['indent']}".');
   }
 
-  var followLinks = argResults["follow-links"];
+  var followLinks = argResults['follow-links'];
 
   var fixes = <StyleFix>[];
-  if (argResults["fix"]) fixes.addAll(StyleFix.all);
+  if (argResults['fix']) fixes.addAll(StyleFix.all);
   for (var fix in StyleFix.all) {
-    if (argResults["fix-${fix.name}"]) {
-      if (argResults["fix"]) {
-        usageError(parser, "--fix-${fix.name} is redundant with --fix.");
+    if (argResults['fix-${fix.name}']) {
+      if (argResults['fix']) {
+        usageError(parser, '--fix-${fix.name} is redundant with --fix.');
       }
 
       fixes.add(fix);
     }
   }
 
-  if (argResults.wasParsed("stdin-name") && argResults.rest.isNotEmpty) {
-    usageError(parser, "Cannot pass --stdin-name when not reading from stdin.");
+  if (argResults.wasParsed('stdin-name') && argResults.rest.isNotEmpty) {
+    usageError(parser, 'Cannot pass --stdin-name when not reading from stdin.');
   }
 
   var options = FormatterOptions(reporter,
@@ -184,12 +184,12 @@
       fixes: fixes);
 
   if (argResults.rest.isEmpty) {
-    formatStdin(options, selection, argResults["stdin-name"] as String);
+    formatStdin(options, selection, argResults['stdin-name'] as String);
   } else {
     formatPaths(options, argResults.rest);
   }
 
-  if (argResults["profile"]) {
+  if (argResults['profile']) {
     (reporter as ProfileReporter).showProfile();
   }
 }
@@ -197,7 +197,7 @@
 List<int> parseSelection(String selection) {
   if (selection == null) return null;
 
-  var coordinates = selection.split(":");
+  var coordinates = selection.split(':');
   if (coordinates.length != 2) {
     throw FormatException(
         'Selection should be a colon-separated pair of integers, "123:45".');
@@ -276,13 +276,13 @@
 void printUsage(ArgParser parser, [String error]) {
   var output = stdout;
 
-  var message = "Idiomatically formats Dart source code.";
+  var message = 'Idiomatically formats Dart source code.';
   if (error != null) {
     message = error;
     output = stdout;
   }
 
-  output.write("""$message
+  output.write('''$message
 
 Usage:   dartfmt [options...] [files or directories...]
 
@@ -290,5 +290,5 @@
          Reformats every Dart file in the current directory tree.
 
 ${parser.usage}
-""");
+''');
 }
diff --git a/example/format.dart b/example/format.dart
index 60c0bed..ff1a7cd 100644
--- a/example/format.dart
+++ b/example/format.dart
@@ -20,7 +20,7 @@
   debug.traceSplitter = true;
   debug.useAnsiColors = true;
 
-  formatStmt("a is int????;");
+  formatStmt('a is int????;');
 }
 
 void formatStmt(String source, [int pageWidth = 80]) {
@@ -42,9 +42,9 @@
       result = formatter.formatStatement(source);
     }
 
-    drawRuler("before", pageWidth);
+    drawRuler('before', pageWidth);
     print(source);
-    drawRuler("after", pageWidth);
+    drawRuler('after', pageWidth);
     print(result);
   } on FormatterException catch (error) {
     print(error.message());
@@ -52,14 +52,14 @@
 }
 
 void drawRuler(String label, int width) {
-  var padding = " " * (width - label.length - 1);
-  print("$label:$padding|");
+  var padding = ' ' * (width - label.length - 1);
+  print('$label:$padding|');
 }
 
 /// Runs the formatter test starting on [line] at [path] inside the "test"
 /// directory.
 void runTest(String path, int line) {
-  var indentPattern = RegExp(r"^\(indent (\d+)\)\s*");
+  var indentPattern = RegExp(r'^\(indent (\d+)\)\s*');
 
   // Locate the "test" directory. Use mirrors so that this works with the test
   // package, which loads this suite into an isolate.
@@ -68,20 +68,20 @@
           .findLibrary(#dart_style.example.format)
           .uri
           .path),
-      "../test");
+      '../test');
 
   var lines = File(p.join(testDir, path)).readAsLinesSync();
 
   // The first line may have a "|" to indicate the page width.
   var pageWidth = 80;
-  if (lines[0].endsWith("|")) {
-    pageWidth = lines[0].indexOf("|");
+  if (lines[0].endsWith('|')) {
+    pageWidth = lines[0].indexOf('|');
     lines = lines.skip(1).toList();
   }
 
   var i = 0;
   while (i < lines.length) {
-    var description = lines[i++].replaceAll(">>>", "").trim();
+    var description = lines[i++].replaceAll('>>>', '').trim();
 
     // Let the test specify a leading indentation. This is handy for
     // regression tests which often come from a chunk of nested code.
@@ -92,26 +92,26 @@
       description = description.substring(indentMatch.end);
     }
 
-    if (description == "") {
-      description = "line ${i + 1}";
+    if (description == '') {
+      description = 'line ${i + 1}';
     } else {
-      description = "line ${i + 1}: $description";
+      description = 'line ${i + 1}: $description';
     }
     var startLine = i + 1;
 
-    var input = "";
-    while (!lines[i].startsWith("<<<")) {
-      input += lines[i++] + "\n";
+    var input = '';
+    while (!lines[i].startsWith('<<<')) {
+      input += lines[i++] + '\n';
     }
 
-    var expectedOutput = "";
-    while (++i < lines.length && !lines[i].startsWith(">>>")) {
-      expectedOutput += lines[i] + "\n";
+    var expectedOutput = '';
+    while (++i < lines.length && !lines[i].startsWith('>>>')) {
+      expectedOutput += lines[i] + '\n';
     }
 
     if (line != startLine) continue;
 
-    var isCompilationUnit = p.extension(path) == ".unit";
+    var isCompilationUnit = p.extension(path) == '.unit';
 
     var inputCode =
         _extractSelection(input, isCompilationUnit: isCompilationUnit);
@@ -127,19 +127,19 @@
     // Statements from the formatter (correctly) don't have that, so add
     // one to line up with the expected result.
     var actualText = actual.text;
-    if (!isCompilationUnit) actualText += "\n";
+    if (!isCompilationUnit) actualText += '\n';
 
-    print("$path $description");
-    drawRuler("before", pageWidth);
+    print('$path $description');
+    drawRuler('before', pageWidth);
     print(input);
     if (actualText == expected.text) {
-      drawRuler("result", pageWidth);
+      drawRuler('result', pageWidth);
       print(actualText);
     } else {
-      print("FAIL");
-      drawRuler("expected", pageWidth);
+      print('FAIL');
+      drawRuler('expected', pageWidth);
       print(expected.text);
-      drawRuler("actual", pageWidth);
+      drawRuler('actual', pageWidth);
       print(actualText);
     }
   }
@@ -149,11 +149,11 @@
 /// a [SourceCode] with the text (with the selection markers removed) and the
 /// correct selection range.
 SourceCode _extractSelection(String source, {bool isCompilationUnit = false}) {
-  var start = source.indexOf("‹");
-  source = source.replaceAll("‹", "");
+  var start = source.indexOf('‹');
+  source = source.replaceAll('‹', '');
 
-  var end = source.indexOf("›");
-  source = source.replaceAll("›", "");
+  var end = source.indexOf('›');
+  source = source.replaceAll('›', '');
 
   return SourceCode(source,
       isCompilationUnit: isCompilationUnit,
diff --git a/lib/src/argument_list_visitor.dart b/lib/src/argument_list_visitor.dart
index 92fa7c3..45f8cd9 100644
--- a/lib/src/argument_list_visitor.dart
+++ b/lib/src/argument_list_visitor.dart
@@ -73,7 +73,7 @@
     for (var i = 0; i < arguments.length; i++) {
       var argument = arguments[i];
       if (_isBlockFunction(argument)) {
-        if (functionsStart == null) functionsStart = i;
+        functionsStart ??= i;
 
         // The functions must be one contiguous section.
         if (functionsEnd != null && functionsEnd != i) {
diff --git a/lib/src/call_chain_visitor.dart b/lib/src/call_chain_visitor.dart
index 30b72e8..80b1b51 100644
--- a/lib/src/call_chain_visitor.dart
+++ b/lib/src/call_chain_visitor.dart
@@ -182,7 +182,7 @@
   /// to force a cascade after a method chain to be more deeply nested than
   /// the methods.
   void visit({bool unnest}) {
-    if (unnest == null) unnest = true;
+    unnest ??= true;
 
     _visitor.builder.nestExpression();
 
@@ -469,11 +469,14 @@
 
   _MethodSelector(this._node);
 
+  @override
   bool get isProperty => false;
 
+  @override
   bool isBlockCall(SourceVisitor visitor) =>
       ArgumentListVisitor(visitor, _node.argumentList).hasBlockArguments;
 
+  @override
   void writeSelector(CallChainVisitor visitor) {
     visitor._visitor.token(_node.operator);
     visitor._visitor.token(_node.methodName.token);
@@ -493,6 +496,7 @@
 
   _PrefixedSelector(this._node);
 
+  @override
   void writeSelector(CallChainVisitor visitor) {
     visitor._visitor.token(_node.period);
     visitor._visitor.visit(_node.identifier);
@@ -504,6 +508,7 @@
 
   _PropertySelector(this._node);
 
+  @override
   void writeSelector(CallChainVisitor visitor) {
     visitor._visitor.token(_node.operator);
     visitor._visitor.visit(_node.propertyName);
diff --git a/lib/src/chunk.dart b/lib/src/chunk.dart
index 5dfe201..30a6d92 100644
--- a/lib/src/chunk.dart
+++ b/lib/src/chunk.dart
@@ -68,6 +68,7 @@
 /// block-based [indent] and expression-wrapping-based [nesting].
 class Chunk extends Selection {
   /// The literal text output for the chunk.
+  @override
   String get text => _text;
   String _text;
 
@@ -123,7 +124,7 @@
   ///
   /// However, this getter does not expose that. It will return `false` if the
   /// chunk is still indeterminate.
-  bool get isDouble => _isDouble != null ? _isDouble : false;
+  bool get isDouble => _isDouble ?? false;
   bool _isDouble;
 
   /// If `true`, then the line after this chunk should always be at column
@@ -201,14 +202,13 @@
   /// combine that information into a single split.
   void applySplit(Rule rule, int indent, NestingLevel nesting,
       {bool flushLeft, bool isDouble, bool space}) {
-    if (flushLeft == null) flushLeft = false;
-    if (space == null) space = false;
+    flushLeft ??= false;
+    space ??= false;
     if (rule.isHardened) {
       // A hard split always wins.
       _rule = rule;
-    } else if (_rule == null) {
-      // If the chunk hasn't been initialized yet, just inherit the rule.
-      _rule = rule;
+    } else {
+      _rule ??= rule;
     }
 
     // Last split settings win.
@@ -219,7 +219,7 @@
     _spaceWhenUnsplit = space;
 
     // Pin down the double state, if given and we haven't already.
-    if (_isDouble == null) _isDouble = isDouble;
+    _isDouble ??= isDouble;
   }
 
   /// Turns this chunk into one that can contain a block of child chunks.
@@ -230,7 +230,7 @@
 
   /// Returns `true` if the block body owned by this chunk should be expression
   /// indented given a set of rule values provided by [getValue].
-  bool indentBlock(int getValue(Rule rule)) {
+  bool indentBlock(int Function(Rule) getValue) {
     if (_block == null) return false;
     if (_block.argument == null) return false;
 
@@ -246,28 +246,29 @@
     _canDivide = canDivide;
   }
 
+  @override
   String toString() {
     var parts = [];
 
     if (text.isNotEmpty) parts.add(text);
 
-    if (_indent != null) parts.add("indent:$_indent");
-    if (spaceWhenUnsplit == true) parts.add("space");
-    if (_isDouble == true) parts.add("double");
-    if (_flushLeft == true) parts.add("flush");
+    if (_indent != null) parts.add('indent:$_indent');
+    if (spaceWhenUnsplit == true) parts.add('space');
+    if (_isDouble == true) parts.add('double');
+    if (_flushLeft == true) parts.add('flush');
 
     if (_rule == null) {
-      parts.add("(no split)");
+      parts.add('(no split)');
     } else {
       parts.add(rule.toString());
-      if (rule.isHardened) parts.add("(hard)");
+      if (rule.isHardened) parts.add('(hard)');
 
       if (_rule.constrainedRules.isNotEmpty) {
         parts.add("-> ${_rule.constrainedRules.join(' ')}");
       }
     }
 
-    return parts.join(" ");
+    return parts.join(' ');
   }
 }
 
@@ -343,16 +344,16 @@
 /// been completed.
 class OpenSpan {
   /// Index of the first chunk contained in this span.
-  int get start => _start;
-  int _start;
+  final int start;
 
   /// The cost applied when the span is split across multiple lines or `null`
   /// if the span is for a multisplit.
   final int cost;
 
-  OpenSpan(this._start, this.cost);
+  OpenSpan(this.start, this.cost);
 
-  String toString() => "OpenSpan($start, \$$cost)";
+  @override
+  String toString() => 'OpenSpan($start, \$$cost)';
 }
 
 /// Delimits a range of chunks that must end up on the same line to avoid an
@@ -371,13 +372,15 @@
 
   Span(this.cost);
 
-  String toString() => "$id\$$cost";
+  @override
+  String toString() => '$id\$$cost';
 }
 
 /// A comment in the source, with a bit of information about the surrounding
 /// whitespace.
 class SourceComment extends Selection {
   /// The text of the comment, including `//`, `/*`, and `*/`.
+  @override
   final String text;
 
   /// The number of newlines between the comment or token preceding this comment
diff --git a/lib/src/chunk_builder.dart b/lib/src/chunk_builder.dart
index 151543e..bab9151 100644
--- a/lib/src/chunk_builder.dart
+++ b/lib/src/chunk_builder.dart
@@ -15,17 +15,17 @@
 import 'whitespace.dart';
 
 /// Matches if the last character of a string is an identifier character.
-final _trailingIdentifierChar = RegExp(r"[a-zA-Z0-9_]$");
+final _trailingIdentifierChar = RegExp(r'[a-zA-Z0-9_]$');
 
 /// Matches a JavaDoc-style doc comment that starts with "/**" and ends with
 /// "*/" or "**/".
-final _javaDocComment = RegExp(r"^/\*\*([^*/][\s\S]*?)\*?\*/$");
+final _javaDocComment = RegExp(r'^/\*\*([^*/][\s\S]*?)\*?\*/$');
 
 /// Matches the leading "*" in a line in the middle of a JavaDoc-style comment.
-final _javaDocLine = RegExp(r"^\s*\*(.*)");
+final _javaDocLine = RegExp(r'^\s*\*(.*)');
 
 /// Matches spaces at the beginning of as string.
-final _leadingIndentation = RegExp(r"^(\s*)");
+final _leadingIndentation = RegExp(r'^(\s*)');
 
 /// Takes the incremental serialized output of [SourceVisitor]--the source text
 /// along with any comments and preserved whitespace--and produces a coherent
@@ -68,7 +68,7 @@
   /// the hard split appears. For example, a hard split in a positional
   /// argument list needs to force the named arguments to split too, but we
   /// don't create that rule until after the positional arguments are done.
-  final _hardSplitRules = Set<Rule>();
+  final _hardSplitRules = <Rule>{};
 
   /// The list of rules that are waiting until the next whitespace has been
   /// written before they start.
@@ -293,7 +293,7 @@
 
         // The comment follows other text, so we need to decide if it gets a
         // space before it or not.
-        if (_needsSpaceBeforeComment(comment)) _writeText(" ");
+        if (_needsSpaceBeforeComment(comment)) _writeText(' ');
       } else {
         // The comment starts a line, so make sure it stays on its own line.
         _writeHardSplit(
@@ -326,7 +326,7 @@
         //     /**
         //      * Some doc comment.
         //      */ someFunction() { ... }
-        if (linesAfter == 0 && comments.last.text.contains("\n")) {
+        if (linesAfter == 0 && comments.last.text.contains('\n')) {
           linesAfter = 1;
         }
       }
@@ -359,7 +359,7 @@
       return;
     }
 
-    var lines = match.group(1).split("\n").toList();
+    var lines = match.group(1).split('\n').toList();
     var leastIndentation = comment.text.length;
 
     for (var i = 0; i < lines.length; i++) {
@@ -388,14 +388,14 @@
     if (lines.isNotEmpty && lines.last.isEmpty) lines.removeLast();
 
     // Don't completely eliminate an empty block comment.
-    if (lines.isEmpty) lines.add("");
+    if (lines.isEmpty) lines.add('');
 
     for (var line in lines) {
-      _writeText("///");
+      _writeText('///');
       if (line.isNotEmpty) {
         // Discard any indentation shared by all lines.
         line = line.substring(leastIndentation);
-        _writeText(" $line");
+        _writeText(' $line');
       }
       _pendingWhitespace = Whitespace.newline;
       _emitPendingWhitespace();
@@ -476,7 +476,7 @@
   ///
   /// If omitted, defaults to a new [Rule].
   void startRule([Rule rule]) {
-    if (rule == null) rule = Rule();
+    rule ??= Rule();
 
     // If there are any pending lazy rules, start them now so that the proper
     // stack ordering of rules is maintained.
@@ -504,7 +504,7 @@
   ///
   /// If [rule] is omitted, defaults to a new [Rule].
   void startLazyRule([Rule rule]) {
-    if (rule == null) rule = Rule();
+    rule ??= Rule();
 
     _lazyRules.add(rule);
   }
@@ -537,7 +537,7 @@
   /// `true`, commits the nesting change immediately instead of waiting until
   /// after the next chunk of text is written.
   void nestExpression({int indent, bool now}) {
-    if (now == null) now = false;
+    now ??= false;
 
     _nesting.nest(indent);
     if (now) _nesting.commitNesting();
@@ -551,7 +551,7 @@
   /// If [now] is `false`, does not commit the nesting change until after the
   /// next chunk of text is written.
   void unnest({bool now}) {
-    if (now == null) now = true;
+    now ??= true;
 
     _nesting.unnest();
     if (now) _nesting.commitNesting();
@@ -662,7 +662,7 @@
     _divideChunks();
 
     if (debug.traceChunkBuilder) {
-      debug.log(debug.green("\nBuilt:"));
+      debug.log(debug.green('\nBuilt:'));
       debug.dumpChunks(0, _chunks);
       debug.log();
     }
@@ -679,8 +679,8 @@
 
       // If we haven't hit the beginning and/or end of the selection yet, they
       // must be at the very end of the code.
-      if (selectionStart == null) selectionStart = writer.length;
-      if (selectionEnd == null) selectionEnd = writer.length;
+      selectionStart ??= writer.length;
+      selectionEnd ??= writer.length;
 
       selectionLength = selectionEnd - selectionStart;
     }
@@ -698,7 +698,7 @@
 
   void endPreventSplit() {
     _preventSplitNesting--;
-    assert(_preventSplitNesting >= 0, "Mismatched calls.");
+    assert(_preventSplitNesting >= 0, 'Mismatched calls.');
   }
 
   /// Writes the current pending [Whitespace] to the output, if any.
@@ -710,7 +710,7 @@
     // trailing.
     switch (_pendingWhitespace) {
       case Whitespace.space:
-        _writeText(" ");
+        _writeText(' ');
         break;
 
       case Whitespace.newline:
@@ -747,16 +747,16 @@
     if (_chunks.isEmpty) return false;
 
     // Multi-line comments are always pushed to the next line.
-    if (comment.contains("\n")) return false;
+    if (comment.contains('\n')) return false;
 
     var text = _chunks.last.text;
 
     // A block comment following a comma probably refers to the following item.
-    if (text.endsWith(",") && comment.startsWith("/*")) return false;
+    if (text.endsWith(',') && comment.startsWith('/*')) return false;
 
     // If the text before the split is an open grouping character, it looks
     // better to keep it with the elements than with the bracket itself.
-    return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{");
+    return !text.endsWith('(') && !text.endsWith('[') && !text.endsWith('{');
   }
 
   /// Returns `true` if [comment] appears to be a magic generic method comment.
@@ -765,7 +765,7 @@
   ///
   ///     int f/*<S, T>*/(int x) => 3;
   bool _isGenericMethodComment(SourceComment comment) {
-    return comment.text.startsWith("/*<") || comment.text.startsWith("/*=");
+    return comment.text.startsWith('/*<') || comment.text.startsWith('/*=');
   }
 
   /// Returns `true` if a space should be output between the end of the current
@@ -788,7 +788,7 @@
     if (!_chunks.last.canAddText) return false;
 
     var text = _chunks.last.text;
-    if (text.endsWith("\n")) return false;
+    if (text.endsWith('\n')) return false;
 
     // Always put a space before line comments.
     if (comment.isLineComment) return true;
@@ -800,7 +800,7 @@
     }
 
     // Block comments do not get a space if following a grouping character.
-    return !text.endsWith("(") && !text.endsWith("[") && !text.endsWith("{");
+    return !text.endsWith('(') && !text.endsWith('[') && !text.endsWith('{');
   }
 
   /// Returns `true` if a space should be output after the last comment which
@@ -813,18 +813,18 @@
     if (!_chunks.last.canAddText) return false;
 
     // Magic generic method comments like "Foo/*<T>*/" don't get spaces.
-    if (_isGenericMethodComment(comments.last) && token == "(") {
+    if (_isGenericMethodComment(comments.last) && token == '(') {
       return false;
     }
 
     // Otherwise, it gets a space if the following token is not a delimiter or
     // the empty string, for EOF.
-    return token != ")" &&
-        token != "]" &&
-        token != "}" &&
-        token != "," &&
-        token != ";" &&
-        token != "";
+    return token != ')' &&
+        token != ']' &&
+        token != '}' &&
+        token != ',' &&
+        token != ';' &&
+        token != '';
   }
 
   /// Appends a hard split with the current indentation and nesting (the latter
@@ -926,7 +926,7 @@
   void _hardenRules() {
     if (_hardSplitRules.isEmpty) return;
 
-    walkConstraints(rule) {
+    void walkConstraints(rule) {
       rule.harden();
 
       // Follow this rule's constraints, recursively.
diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart
index fe0c1ca..c602874 100644
--- a/lib/src/dart_formatter.dart
+++ b/lib/src/dart_formatter.dart
@@ -38,7 +38,7 @@
   /// The number of characters of indentation to prefix the output lines with.
   final int indent;
 
-  final Set<StyleFix> fixes = Set();
+  final Set<StyleFix> fixes = {};
 
   /// Creates a new formatter for Dart code.
   ///
@@ -70,7 +70,7 @@
     } else if (uri is String) {
       // Do nothing.
     } else {
-      throw ArgumentError("uri must be `null`, a Uri, or a String.");
+      throw ArgumentError('uri must be `null`, a Uri, or a String.');
     }
 
     return formatSource(SourceCode(source, uri: uri, isCompilationUnit: true))
@@ -94,8 +94,8 @@
     // TODO(paulberry): consider plumbing in experiment enable flags from the
     // command line.
     var featureSet = FeatureSet.fromEnableFlags([
-      "extension-methods",
-      "non-nullable",
+      'extension-methods',
+      'non-nullable',
     ]);
 
     // Tokenize the source.
@@ -113,9 +113,9 @@
       if (scanner.lineStarts.length > 1 &&
           scanner.lineStarts[1] >= 2 &&
           source.text[scanner.lineStarts[1] - 2] == '\r') {
-        lineEnding = "\r\n";
+        lineEnding = '\r\n';
       } else {
-        lineEnding = "\n";
+        lineEnding = '\n';
       }
     }
 
diff --git a/lib/src/debug.dart b/lib/src/debug.dart
index ea04a41..9068af9 100644
--- a/lib/src/debug.dart
+++ b/lib/src/debug.dart
@@ -21,14 +21,14 @@
 
 bool useAnsiColors = false;
 
-const unicodeSection = "\u00a7";
-const unicodeMidDot = "\u00b7";
+const unicodeSection = '\u00a7';
+const unicodeMidDot = '\u00b7';
 
 /// The whitespace prefixing each line of output.
-String _indent = "";
+String _indent = '';
 
 void indent() {
-  _indent = "  $_indent";
+  _indent = '  $_indent';
 }
 
 void unindent() {
@@ -36,29 +36,29 @@
 }
 
 /// Constants for ANSI color escape codes.
-final _gray = _color("\u001b[1;30m");
-final _green = _color("\u001b[32m");
-final _none = _color("\u001b[0m");
-final _bold = _color("\u001b[1m");
+final _gray = _color('\u001b[1;30m');
+final _green = _color('\u001b[32m');
+final _none = _color('\u001b[0m');
+final _bold = _color('\u001b[1m');
 
 /// Prints [message] to stdout with each line correctly indented.
 void log([message]) {
   if (message == null) {
-    print("");
+    print('');
     return;
   }
 
-  print(_indent + message.toString().replaceAll("\n", "\n$_indent"));
+  print(_indent + message.toString().replaceAll('\n', '\n$_indent'));
 }
 
 /// Wraps [message] in gray ANSI escape codes if enabled.
-String gray(message) => "$_gray$message$_none";
+String gray(message) => '$_gray$message$_none';
 
 /// Wraps [message] in green ANSI escape codes if enabled.
-String green(message) => "$_green$message$_none";
+String green(message) => '$_green$message$_none';
 
 /// Wraps [message] in bold ANSI escape codes if enabled.
-String bold(message) => "$_bold$message$_none";
+String bold(message) => '$_bold$message$_none';
 
 /// Prints [chunks] to stdout, one chunk per line, with detailed information
 /// about each chunk.
@@ -67,8 +67,8 @@
 
   // Show the spans as vertical bands over their range (unless there are too
   // many).
-  var spanSet = Set<Span>();
-  addSpans(List<Chunk> chunks) {
+  var spanSet = <Span>{};
+  void addSpans(List<Chunk> chunks) {
     for (var chunk in chunks) {
       spanSet.addAll(chunk.spans);
 
@@ -84,9 +84,9 @@
 
   var rows = <List<String>>[];
 
-  addChunk(List<Chunk> chunks, String prefix, int index) {
+  void addChunk(List<Chunk> chunks, String prefix, int index) {
     var row = <String>[];
-    row.add("$prefix$index:");
+    row.add('$prefix$index:');
 
     var chunk = chunks[index];
     if (chunk.text.length > 70) {
@@ -96,46 +96,46 @@
     }
 
     if (spans.length <= 20) {
-      var spanBars = "";
+      var spanBars = '';
       for (var span in spans) {
         if (chunk.spans.contains(span)) {
           if (index == 0 || !chunks[index - 1].spans.contains(span)) {
             if (span.cost == 1) {
-              spanBars += "â•–";
+              spanBars += 'â•–';
             } else {
               spanBars += span.cost.toString();
             }
           } else {
-            spanBars += "â•‘";
+            spanBars += 'â•‘';
           }
         } else {
           if (index > 0 && chunks[index - 1].spans.contains(span)) {
-            spanBars += "╜";
+            spanBars += '╜';
           } else {
-            spanBars += " ";
+            spanBars += ' ';
           }
         }
       }
       row.add(spanBars);
     }
 
-    writeIf(predicate, String callback()) {
+    void writeIf(predicate, String Function() callback) {
       if (predicate) {
         row.add(callback());
       } else {
-        row.add("");
+        row.add('');
       }
     }
 
     if (chunk.rule == null) {
-      row.add("");
-      row.add("(no rule)");
-      row.add("");
+      row.add('');
+      row.add('(no rule)');
+      row.add('');
     } else {
-      writeIf(chunk.rule.cost != 0, () => "\$${chunk.rule.cost}");
+      writeIf(chunk.rule.cost != 0, () => '\$${chunk.rule.cost}');
 
       var ruleString = chunk.rule.toString();
-      if (chunk.rule.isHardened) ruleString += "!";
+      if (chunk.rule.isHardened) ruleString += '!';
       row.add(ruleString);
 
       var constrainedRules =
@@ -145,26 +145,26 @@
     }
 
     writeIf(chunk.indent != null && chunk.indent != 0,
-        () => "indent ${chunk.indent}");
+        () => 'indent ${chunk.indent}');
 
     writeIf(chunk.nesting != null && chunk.nesting.indent != 0,
-        () => "nest ${chunk.nesting}");
+        () => 'nest ${chunk.nesting}');
 
-    writeIf(chunk.flushLeft != null && chunk.flushLeft, () => "flush");
+    writeIf(chunk.flushLeft != null && chunk.flushLeft, () => 'flush');
 
-    writeIf(chunk.canDivide, () => "divide");
+    writeIf(chunk.canDivide, () => 'divide');
 
     rows.add(row);
 
     if (chunk.isBlock) {
       for (var j = 0; j < chunk.block.chunks.length; j++) {
-        addChunk(chunk.block.chunks, "$prefix$index.", j);
+        addChunk(chunk.block.chunks, '$prefix$index.', j);
       }
     }
   }
 
   for (var i = start; i < chunks.length; i++) {
-    addChunk(chunks, "", i);
+    addChunk(chunks, '', i);
   }
 
   var rowWidths = List.filled(rows.first.length, 0);
@@ -182,7 +182,7 @@
       if (i != 1) cell = gray(cell);
 
       buffer.write(cell);
-      buffer.write("  ");
+      buffer.write('  ');
     }
 
     buffer.writeln();
@@ -205,7 +205,7 @@
 
         var constraint = rule.constrain(value, other);
         if (constraint != null) {
-          constraints.add("$other->$constraint");
+          constraints.add('$other->$constraint');
         }
       }
 
@@ -225,12 +225,12 @@
 void dumpLines(List<Chunk> chunks, int firstLineIndent, SplitSet splits) {
   var buffer = StringBuffer();
 
-  writeIndent(indent) => buffer.write(gray("| " * (indent ~/ 2)));
+  void writeIndent(indent) => buffer.write(gray('| ' * (indent ~/ 2)));
 
-  writeChunksUnsplit(List<Chunk> chunks) {
+  void writeChunksUnsplit(List<Chunk> chunks) {
     for (var chunk in chunks) {
       buffer.write(chunk.text);
-      if (chunk.spaceWhenUnsplit) buffer.write(" ");
+      if (chunk.spaceWhenUnsplit) buffer.write(' ');
 
       // Recurse into the block.
       if (chunk.isBlock) writeChunksUnsplit(chunk.block.chunks);
@@ -251,7 +251,7 @@
     } else {
       if (chunk.isBlock) writeChunksUnsplit(chunk.block.chunks);
 
-      if (chunk.spaceWhenUnsplit) buffer.write(" ");
+      if (chunk.spaceWhenUnsplit) buffer.write(' ');
     }
   }
 
@@ -259,4 +259,4 @@
   log(buffer);
 }
 
-String _color(String ansiEscape) => useAnsiColors ? ansiEscape : "";
+String _color(String ansiEscape) => useAnsiColors ? ansiEscape : '';
diff --git a/lib/src/error_listener.dart b/lib/src/error_listener.dart
index bceb91e..a1df699 100644
--- a/lib/src/error_listener.dart
+++ b/lib/src/error_listener.dart
@@ -13,6 +13,7 @@
 class ErrorListener implements AnalysisErrorListener {
   final _errors = <AnalysisError>[];
 
+  @override
   void onError(AnalysisError error) {
     // Fasta produces some semantic errors, which we want to ignore so that
     // users can format code containing static errors.
diff --git a/lib/src/exceptions.dart b/lib/src/exceptions.dart
index c517f40..8e8dcfe 100644
--- a/lib/src/exceptions.dart
+++ b/lib/src/exceptions.dart
@@ -19,7 +19,7 @@
   /// Creates a human-friendly representation of the analysis errors.
   String message({bool color}) {
     var buffer = StringBuffer();
-    buffer.writeln("Could not format because the source could not be parsed:");
+    buffer.writeln('Could not format because the source could not be parsed:');
 
     // In case we get a huge series of cascaded errors, just show the first few.
     var shownErrors = errors;
@@ -32,7 +32,7 @@
       // the error position will go past the end of the source. In that case,
       // just pad the source with spaces so we can report it nicely.
       if (error.offset + error.length > source.length) {
-        source += " " * (error.offset + error.length - source.length);
+        source += ' ' * (error.offset + error.length - source.length);
       }
 
       var file = SourceFile.fromString(source, url: error.source.fullName);
@@ -43,12 +43,13 @@
 
     if (shownErrors.length != errors.length) {
       buffer.writeln();
-      buffer.write("(${errors.length - shownErrors.length} more errors...)");
+      buffer.write('(${errors.length - shownErrors.length} more errors...)');
     }
 
     return buffer.toString();
   }
 
+  @override
   String toString() => message();
 }
 
@@ -63,10 +64,11 @@
 
   UnexpectedOutputException(this._input, this._output);
 
+  @override
   String toString() {
-    return """The formatter produced unexpected output. Input was:
+    return '''The formatter produced unexpected output. Input was:
 $_input
 Which formatted to:
-$_output""";
+$_output''';
   }
 }
diff --git a/lib/src/fast_hash.dart b/lib/src/fast_hash.dart
index f0b6de4..f1b2fe8 100644
--- a/lib/src/fast_hash.dart
+++ b/lib/src/fast_hash.dart
@@ -17,5 +17,6 @@
   /// innocuous and prevents ids from growing without bound.
   final int id = _nextId = (_nextId + 1) & 0x0fffffff;
 
+  @override
   int get hashCode => id;
 }
diff --git a/lib/src/formatter_options.dart b/lib/src/formatter_options.dart
index 2940b79..b4073a6 100644
--- a/lib/src/formatter_options.dart
+++ b/lib/src/formatter_options.dart
@@ -73,6 +73,7 @@
 /// Prints only the names of files whose contents are different from their
 /// formatted version.
 class _DryRunReporter extends OutputReporter {
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     // Only show the changed files.
     if (changed) print(label);
@@ -81,18 +82,22 @@
 
 /// Prints the formatted results of each file to stdout.
 class _PrintReporter extends OutputReporter {
+  @override
   void showDirectory(String path) {
-    print("Formatting directory $path:");
+    print('Formatting directory $path:');
   }
 
+  @override
   void showSkippedLink(String path) {
-    print("Skipping link $path");
+    print('Skipping link $path');
   }
 
+  @override
   void showHiddenPath(String path) {
-    print("Skipping hidden path $path");
+    print('Skipping hidden path $path');
   }
 
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     // Don't add an extra newline.
     stdout.write(output.text);
@@ -102,6 +107,7 @@
 /// Prints the formatted result and selection info of each file to stdout as a
 /// JSON map.
 class _PrintJsonReporter extends OutputReporter {
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     // TODO(rnystrom): Put an empty selection in here to remain compatible with
     // the old formatter. Since there's no way to pass a selection on the
@@ -109,11 +115,11 @@
     // -1, -1. If we add support for passing in a selection, put the real
     // result here.
     print(jsonEncode({
-      "path": label,
-      "source": output.text,
-      "selection": {
-        "offset": output.selectionStart != null ? output.selectionStart : -1,
-        "length": output.selectionLength != null ? output.selectionLength : -1
+      'path': label,
+      'source': output.text,
+      'selection': {
+        'offset': output.selectionStart ?? -1,
+        'length': output.selectionLength ?? -1
       }
     }));
   }
@@ -121,17 +127,18 @@
 
 /// Overwrites each file with its formatted result.
 class _OverwriteReporter extends _PrintReporter {
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     if (changed) {
       try {
         file.writeAsStringSync(output.text);
-        print("Formatted $label");
+        print('Formatted $label');
       } on FileSystemException catch (err) {
-        stderr.writeln("Could not overwrite $label: "
-            "${err.osError.message} (error code ${err.osError.errorCode})");
+        stderr.writeln('Could not overwrite $label: '
+            '${err.osError.message} (error code ${err.osError.errorCode})');
       }
     } else {
-      print("Unchanged $label");
+      print('Unchanged $label');
     }
   }
 }
@@ -142,22 +149,27 @@
 
   _ReporterDecorator(this._inner);
 
+  @override
   void showDirectory(String path) {
     _inner.showDirectory(path);
   }
 
+  @override
   void showSkippedLink(String path) {
     _inner.showSkippedLink(path);
   }
 
+  @override
   void showHiddenPath(String path) {
     _inner.showHiddenPath(path);
   }
 
+  @override
   void beforeFile(File file, String label) {
     _inner.beforeFile(file, label);
   }
 
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     _inner.afterFile(file, label, output, changed: changed);
   }
@@ -188,16 +200,17 @@
     files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a]));
 
     for (var file in files) {
-      print("${_elapsed[file]}: $file");
+      print('${_elapsed[file]}: $file');
     }
 
     if (_elided >= 1) {
       var s = _elided > 1 ? 's' : '';
-      print("...$_elided more file$s each took less than 10ms.");
+      print('...$_elided more file$s each took less than 10ms.');
     }
   }
 
   /// Called when [file] is about to be formatted.
+  @override
   void beforeFile(File file, String label) {
     super.beforeFile(file, label);
     _ongoing[label] = DateTime.now();
@@ -207,6 +220,7 @@
   ///
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     var elapsed = DateTime.now().difference(_ongoing.remove(label));
     if (elapsed.inMilliseconds >= 10) {
@@ -227,6 +241,7 @@
   ///
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
+  @override
   void afterFile(File file, String label, SourceCode output, {bool changed}) {
     if (changed) exitCode = 1;
 
diff --git a/lib/src/io.dart b/lib/src/io.dart
index 40fc1f7..a69e500 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -22,7 +22,7 @@
   options.reporter.showDirectory(directory.path);
 
   var success = true;
-  var shownHiddenPaths = Set<String>();
+  var shownHiddenPaths = <String>{};
 
   for (var entry in directory.listSync(
       recursive: true, followLinks: options.followLinks)) {
@@ -33,13 +33,13 @@
       continue;
     }
 
-    if (entry is! File || !entry.path.endsWith(".dart")) continue;
+    if (entry is! File || !entry.path.endsWith('.dart')) continue;
 
     // If the path is in a subdirectory starting with ".", ignore it.
     var parts = p.split(relative);
     var hiddenIndex;
     for (var i = 0; i < parts.length; i++) {
-      if (parts[i].startsWith(".")) {
+      if (parts[i].startsWith('.')) {
         hiddenIndex = i;
         break;
       }
@@ -65,7 +65,7 @@
 ///
 /// Returns `true` if successful or `false` if an error occurred.
 bool processFile(FormatterOptions options, File file, {String label}) {
-  if (label == null) label = file.path;
+  label ??= file.path;
 
   var formatter = DartFormatter(
       indent: options.indent,
@@ -79,7 +79,7 @@
         .afterFile(file, label, output, changed: source.text != output.text);
     return true;
   } on FormatterException catch (err) {
-    var color = Platform.operatingSystem != "windows" &&
+    var color = Platform.operatingSystem != 'windows' &&
         stdioType(stderr) == StdioType.terminal;
 
     stderr.writeln(err.message(color: color));
diff --git a/lib/src/line_splitting/line_splitter.dart b/lib/src/line_splitting/line_splitter.dart
index 95d545a..137c21e 100644
--- a/lib/src/line_splitting/line_splitter.dart
+++ b/lib/src/line_splitting/line_splitter.dart
@@ -178,8 +178,8 @@
       }
 
       if (debug.traceSplitter) {
-        var best = state == _bestSolution ? " (best)" : "";
-        debug.log("$state$best");
+        var best = state == _bestSolution ? ' (best)' : '';
+        debug.log('$state$best');
         debug.dumpLines(chunks, firstLineIndent, state.splits);
         debug.log();
       }
@@ -191,7 +191,7 @@
     }
 
     if (debug.traceSplitter) {
-      debug.log("$_bestSolution (winner)");
+      debug.log('$_bestSolution (winner)');
       debug.dumpLines(chunks, firstLineIndent, _bestSolution.splits);
       debug.log();
     }
diff --git a/lib/src/line_splitting/rule_set.dart b/lib/src/line_splitting/rule_set.dart
index 7011393..20eb5cc 100644
--- a/lib/src/line_splitting/rule_set.dart
+++ b/lib/src/line_splitting/rule_set.dart
@@ -43,7 +43,7 @@
 
   /// Invokes [callback] for each rule in [rules] with the rule's value, which
   /// will be `null` if it is not bound.
-  void forEach(List<Rule> rules, callback(Rule rule, int value)) {
+  void forEach(List<Rule> rules, void Function(Rule, int) callback) {
     var i = 0;
     for (var rule in rules) {
       var value = _values[i];
@@ -63,7 +63,8 @@
   ///
   /// If an unbound rule gets constrained to `-1` (meaning it must split, but
   /// can split any way it wants), invokes [onSplitRule] with it.
-  bool tryBind(List<Rule> rules, Rule rule, int value, onSplitRule(Rule rule)) {
+  bool tryBind(
+      List<Rule> rules, Rule rule, int value, void Function(Rule) onSplitRule) {
     assert(!rule.isHardened);
 
     _values[rule.index] = value;
@@ -118,8 +119,8 @@
     return true;
   }
 
-  String toString() =>
-      _values.map((value) => value == null ? "?" : value).join(" ");
+  @override
+  String toString() => _values.map((value) => value ?? '?').join(' ');
 }
 
 /// For each chunk, this tracks if it has been split and, if so, what the
@@ -130,7 +131,7 @@
 /// not split. This had about a 10% perf improvement over using a [Set] of
 /// splits.
 class SplitSet {
-  List<int> _columns;
+  final List<int> _columns;
 
   /// The cost of the solution that led to these splits.
   int get cost => _cost;
@@ -159,14 +160,15 @@
     _cost = cost;
   }
 
+  @override
   String toString() {
     var result = [];
     for (var i = 0; i < _columns.length; i++) {
       if (_columns[i] != null) {
-        result.add("$i:${_columns[i]}");
+        result.add('$i:${_columns[i]}');
       }
     }
 
-    return result.join(" ");
+    return result.join(' ');
   }
 }
diff --git a/lib/src/line_splitting/solve_state.dart b/lib/src/line_splitting/solve_state.dart
index 734b1e0..8f202a2 100644
--- a/lib/src/line_splitting/solve_state.dart
+++ b/lib/src/line_splitting/solve_state.dart
@@ -4,6 +4,7 @@
 
 library dart_style.src.line_splitting.solve_state;
 
+import '../chunk.dart';
 import '../debug.dart' as debug;
 import '../nesting_level.dart';
 import '../rule/rule.dart';
@@ -63,7 +64,7 @@
   /// There is one other set of rules that go in here. Sometimes a bound rule
   /// in the solve state constrains some other unbound rule to split. In that
   /// case, we also consider that active so we know to not leave it at zero.
-  final _liveRules = Set<Rule>();
+  final _liveRules = <Rule>{};
 
   /// The set of splits chosen for this state.
   SplitSet get splits => _splits;
@@ -176,7 +177,7 @@
 
     // The way SolveStates are expanded should guarantee that we never generate
     // the exact same state twice. Getting here implies that that failed.
-    throw "unreachable";
+    throw 'unreachable';
   }
 
   /// Enqueues more solve states to consider based on this one.
@@ -198,7 +199,7 @@
 
           List<Rule> mustSplitRules;
           var valid = boundRules.tryBind(_splitter.rules, rule, value, (rule) {
-            if (mustSplitRules == null) mustSplitRules = [];
+            mustSplitRules ??= [];
             mustSplitRules.add(rule);
           });
 
@@ -281,7 +282,7 @@
   void _calculateSplits() {
     // Figure out which expression nesting levels got split and need to be
     // assigned columns.
-    var usedNestingLevels = Set<NestingLevel>();
+    var usedNestingLevels = <NestingLevel>{};
     for (var i = 0; i < _splitter.chunks.length - 1; i++) {
       var chunk = _splitter.chunks[i];
       if (chunk.rule.isSplit(getValue(chunk.rule), chunk)) {
@@ -331,7 +332,7 @@
     var foundOverflowRules = false;
     var start = 0;
 
-    endLine(int end) {
+    void endLine(int end) {
       // Track lines that went over the length. It is only rules contained in
       // long lines that we may want to split.
       if (length > _splitter.writer.pageWidth) {
@@ -354,7 +355,7 @@
     // The set of spans that contain chunks that ended up splitting. We store
     // these in a set so a span's cost doesn't get double-counted if more than
     // one split occurs in it.
-    var splitSpans = Set();
+    var splitSpans = <Span>{};
 
     // The nesting level of the chunk that ended the previous line.
     var previousNesting;
@@ -456,9 +457,9 @@
   void _ensureBoundRulesInUnboundLines() {
     if (_boundRulesInUnboundLines != null) return;
 
-    _boundRulesInUnboundLines = Set<Rule>();
+    _boundRulesInUnboundLines = <Rule>{};
 
-    var boundInLine = Set<Rule>();
+    var boundInLine = <Rule>{};
     var hasUnbound = false;
 
     for (var i = 0; i < _splitter.chunks.length - 1; i++) {
@@ -490,8 +491,8 @@
   void _ensureConstraints() {
     if (_constraints != null) return;
 
-    _unboundRules = Set();
-    _boundRules = Set();
+    _unboundRules = <Rule>{};
+    _boundRules = <Rule>{};
 
     for (var rule in _splitter.rules) {
       if (_ruleValues.contains(rule)) {
@@ -555,7 +556,7 @@
           }
 
           if (disallowedValues == null) {
-            disallowedValues = Set<int>();
+            disallowedValues = <int>{};
             _unboundConstraints[unbound] = disallowedValues;
           }
 
@@ -565,15 +566,16 @@
     }
   }
 
+  @override
   String toString() {
     var buffer = StringBuffer();
 
     buffer.writeAll(_splitter.rules.map((rule) {
-      var valueLength = "${rule.fullySplitValue}".length;
+      var valueLength = '${rule.fullySplitValue}'.length;
 
-      var value = "?";
+      var value = '?';
       if (_ruleValues.contains(rule)) {
-        value = "${_ruleValues.getValue(rule)}";
+        value = '${_ruleValues.getValue(rule)}';
       }
 
       value = value.padLeft(valueLength);
@@ -584,13 +586,13 @@
       }
 
       return value;
-    }), " ");
+    }), ' ');
 
-    buffer.write("   \$${splits.cost}");
+    buffer.write('   \$${splits.cost}');
 
-    if (overflowChars > 0) buffer.write(" (${overflowChars} over)");
-    if (!_isComplete) buffer.write(" (incomplete)");
-    if (splits == null) buffer.write(" invalid");
+    if (overflowChars > 0) buffer.write(' (${overflowChars} over)');
+    if (!_isComplete) buffer.write(' (incomplete)');
+    if (splits == null) buffer.write(' invalid');
 
     return buffer.toString();
   }
diff --git a/lib/src/line_splitting/solve_state_queue.dart b/lib/src/line_splitting/solve_state_queue.dart
index a22731a..7fdbaa6 100644
--- a/lib/src/line_splitting/solve_state_queue.dart
+++ b/lib/src/line_splitting/solve_state_queue.dart
@@ -114,7 +114,7 @@
 
     // The way SolveStates are expanded should guarantee that we never generate
     // the exact same state twice. Getting here implies that that failed.
-    throw "unreachable";
+    throw 'unreachable';
   }
 
   /// Determines if any already enqueued state overlaps [state].
diff --git a/lib/src/line_writer.dart b/lib/src/line_writer.dart
index 2c6d103..748bb9b 100644
--- a/lib/src/line_writer.dart
+++ b/lib/src/line_writer.dart
@@ -143,7 +143,7 @@
     var chunks = _chunks.sublist(start, end);
 
     if (debug.traceLineWriter) {
-      debug.log(debug.green("\nWriting:"));
+      debug.log(debug.green('\nWriting:'));
       debug.dumpChunks(0, chunks);
       debug.log();
     }
@@ -155,7 +155,7 @@
 
     // Write the indentation of the first line.
     if (!flushLeft) {
-      _buffer.write(" " * (indent + _blockIndentation));
+      _buffer.write(' ' * (indent + _blockIndentation));
     }
 
     // Write each chunk with the appropriate splits between them.
@@ -192,9 +192,9 @@
         _buffer.write(_lineEnding);
         if (chunk.isDouble) _buffer.write(_lineEnding);
 
-        _buffer.write(" " * (splits.getColumn(i)));
+        _buffer.write(' ' * (splits.getColumn(i)));
       } else {
-        if (chunk.spaceWhenUnsplit) _buffer.write(" ");
+        if (chunk.spaceWhenUnsplit) _buffer.write(' ');
       }
     }
 
@@ -209,7 +209,7 @@
     for (var blockChunk in chunk.block.chunks) {
       _writeChunk(blockChunk);
 
-      if (blockChunk.spaceWhenUnsplit) _buffer.write(" ");
+      if (blockChunk.spaceWhenUnsplit) _buffer.write(' ');
 
       // Recurse into the block.
       _writeChunksUnsplit(blockChunk);
@@ -246,11 +246,13 @@
 
   _BlockKey(this.chunk, this.column);
 
+  @override
   bool operator ==(other) {
     if (other is! _BlockKey) return false;
     return chunk == other.chunk && column == other.column;
   }
 
+  @override
   int get hashCode => chunk.hashCode ^ column.hashCode;
 }
 
diff --git a/lib/src/nesting_builder.dart b/lib/src/nesting_builder.dart
index bbae652..8ed62f1 100644
--- a/lib/src/nesting_builder.dart
+++ b/lib/src/nesting_builder.dart
@@ -61,8 +61,7 @@
   NestingLevel _nesting = NestingLevel();
 
   /// The current nesting, including any pending nesting.
-  NestingLevel get currentNesting =>
-      _pendingNesting != null ? _pendingNesting : _nesting;
+  NestingLevel get currentNesting => _pendingNesting ?? _nesting;
 
   /// Creates a new indentation level [spaces] deeper than the current one.
   ///
@@ -97,7 +96,7 @@
   ///
   /// If [indent] is omitted, defaults to [Indent.expression].
   void nest([int indent]) {
-    if (indent == null) indent = Indent.expression;
+    indent ??= Indent.expression;
 
     if (_pendingNesting != null) {
       _pendingNesting = _pendingNesting.nest(indent);
diff --git a/lib/src/nesting_level.dart b/lib/src/nesting_level.dart
index 3be1a83..c1088c6 100644
--- a/lib/src/nesting_level.dart
+++ b/lib/src/nesting_level.dart
@@ -71,8 +71,9 @@
     if (usedNesting.contains(this)) _totalUsedIndent += indent;
   }
 
+  @override
   String toString() {
     if (_parent == null) return indent.toString();
-    return "$parent:$indent";
+    return '$parent:$indent';
   }
 }
diff --git a/lib/src/rule/argument.dart b/lib/src/rule/argument.dart
index 1a8a707..a483e4e 100644
--- a/lib/src/rule/argument.dart
+++ b/lib/src/rule/argument.dart
@@ -34,16 +34,19 @@
   bool _trackInnerRules = true;
 
   /// Don't split when an inner collection rule splits.
+  @override
   bool get splitsOnInnerRules => _trackInnerRules;
 
   ArgumentRule(this._collectionRule, this._leadingCollections,
       this._trailingCollections);
 
+  @override
   void addConstrainedRules(Set<Rule> rules) {
     super.addConstrainedRules(rules);
     if (_collectionRule != null) rules.add(_collectionRule);
   }
 
+  @override
   void forgetUnusedRules() {
     super.forgetUnusedRules();
     if (_collectionRule != null && _collectionRule.index == null) {
@@ -100,6 +103,7 @@
       Rule collectionRule, int leadingCollections, int trailingCollections)
       : super(collectionRule, leadingCollections, trailingCollections);
 
+  @override
   int get numValues {
     // Can split before any one argument or none.
     var result = _arguments.length + 1;
@@ -119,11 +123,13 @@
     return result;
   }
 
+  @override
   void addConstrainedRules(Set<Rule> rules) {
     super.addConstrainedRules(rules);
     if (_namedArgsRule != null) rules.add(_namedArgsRule);
   }
 
+  @override
   void forgetUnusedRules() {
     super.forgetUnusedRules();
     if (_namedArgsRule != null && _namedArgsRule.index == null) {
@@ -131,6 +137,7 @@
     }
   }
 
+  @override
   bool isSplitAtValue(int value, Chunk chunk) {
     // Split only before the first argument. Keep the entire argument list
     // together on the next line.
@@ -178,6 +185,7 @@
   ///      function(
   ///          argument,
   ///          argument, named: argument);
+  @override
   int constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
@@ -235,7 +243,8 @@
     return null;
   }
 
-  String toString() => "Pos${super.toString()}";
+  @override
+  String toString() => 'Pos${super.toString()}';
 }
 
 /// Splitting rule for a list of named arguments or parameters. Its values mean:
@@ -244,12 +253,14 @@
 /// * Split only before first argument.
 /// * Split before all arguments.
 class NamedRule extends ArgumentRule {
+  @override
   int get numValues => 3;
 
   NamedRule(
       Rule collectionRule, int leadingCollections, int trailingCollections)
       : super(collectionRule, leadingCollections, trailingCollections);
 
+  @override
   bool isSplitAtValue(int value, Chunk chunk) {
     // Move all arguments to the second line as a unit.
     if (value == 1) return chunk == _arguments.first;
@@ -258,6 +269,7 @@
     return true;
   }
 
+  @override
   int constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
@@ -281,5 +293,6 @@
     return null;
   }
 
-  String toString() => "Named${super.toString()}";
+  @override
+  String toString() => 'Named${super.toString()}';
 }
diff --git a/lib/src/rule/combinator.dart b/lib/src/rule/combinator.dart
index 29b2550..aa83527 100644
--- a/lib/src/rule/combinator.dart
+++ b/lib/src/rule/combinator.dart
@@ -46,7 +46,7 @@
 /// the beginning of the line.
 class CombinatorRule extends Rule {
   /// The set of chunks before the combinators.
-  final Set<Chunk> _combinators = Set();
+  final _combinators = <Chunk>{};
 
   /// A list of sets of chunks prior to each name in a combinator.
   ///
@@ -54,6 +54,7 @@
   /// inner set is the set of names for that combinator.
   final List<Set<Chunk>> _names = [];
 
+  @override
   int get numValues {
     var count = 2; // No wrapping, or wrap just before each combinator.
 
@@ -72,7 +73,7 @@
   /// This must be called before adding any names.
   void addCombinator(Chunk chunk) {
     _combinators.add(chunk);
-    _names.add(Set());
+    _names.add({});
   }
 
   /// Adds a chunk prior to a name to the current combinator.
@@ -80,6 +81,7 @@
     _names.last.add(chunk);
   }
 
+  @override
   bool isSplitAtValue(int value, Chunk chunk) {
     switch (value) {
       case 1:
@@ -113,5 +115,6 @@
     return _combinators.contains(chunk) || _names[combinator].contains(chunk);
   }
 
-  String toString() => "Comb${super.toString()}";
+  @override
+  String toString() => 'Comb${super.toString()}';
 }
diff --git a/lib/src/rule/metadata.dart b/lib/src/rule/metadata.dart
index 9abbe8f..c552101 100644
--- a/lib/src/rule/metadata.dart
+++ b/lib/src/rule/metadata.dart
@@ -34,6 +34,7 @@
 
   /// Constrains the surrounding argument list rules to fully split if the
   /// metadata does.
+  @override
   int constrain(int value, Rule other) {
     var constrained = super.constrain(value, other);
     if (constrained != null) return constrained;
@@ -48,11 +49,13 @@
     return null;
   }
 
+  @override
   void addConstrainedRules(Set<Rule> rules) {
     if (_positionalRule != null) rules.add(_positionalRule);
     if (_namedRule != null) rules.add(_namedRule);
   }
 
+  @override
   void forgetUnusedRules() {
     super.forgetUnusedRules();
     if (_positionalRule != null && _positionalRule.index == null) {
diff --git a/lib/src/rule/rule.dart b/lib/src/rule/rule.dart
index acca87f..7813bf1 100644
--- a/lib/src/rule/rule.dart
+++ b/lib/src/rule/rule.dart
@@ -59,7 +59,7 @@
   ///
   /// This contains all direct as well as transitive relationships. If A
   /// contains B which contains C, C's outerRules contains both B and A.
-  final Set<Rule> _implied = Set<Rule>();
+  final Set<Rule> _implied = <Rule>{};
 
   /// Marks [other] as implied by this one.
   ///
@@ -165,14 +165,14 @@
   /// on, directly or indirectly, including itself.
   Set<Rule> get allConstrainedRules {
     if (_allConstrainedRules == null) {
-      visit(Rule rule) {
+      void visit(Rule rule) {
         if (_allConstrainedRules.contains(rule)) return;
 
         _allConstrainedRules.add(rule);
         rule.constrainedRules.forEach(visit);
       }
 
-      _allConstrainedRules = Set();
+      _allConstrainedRules = {};
       visit(this);
     }
 
@@ -181,5 +181,6 @@
 
   Set<Rule> _allConstrainedRules;
 
-  String toString() => "$id";
+  @override
+  String toString() => '$id';
 }
diff --git a/lib/src/rule/type_argument.dart b/lib/src/rule/type_argument.dart
index 55d99be..8a5081b 100644
--- a/lib/src/rule/type_argument.dart
+++ b/lib/src/rule/type_argument.dart
@@ -24,8 +24,10 @@
   /// The chunks prior to each positional type argument.
   final List<Chunk> _arguments = [];
 
+  @override
   int get cost => Cost.typeArgument;
 
+  @override
   int get numValues => _arguments.length == 1 ? 2 : _arguments.length + 2;
 
   /// Remembers [chunk] as containing the split that occurs right before a type
@@ -34,6 +36,7 @@
     _arguments.add(chunk);
   }
 
+  @override
   bool isSplit(int value, Chunk chunk) {
     // Don't split at all.
     if (value == Rule.unsplit) return false;
@@ -46,5 +49,6 @@
     return chunk == _arguments[_arguments.length - value];
   }
 
-  String toString() => "TypeArg${super.toString()}";
+  @override
+  String toString() => 'TypeArg${super.toString()}';
 }
diff --git a/lib/src/source_code.dart b/lib/src/source_code.dart
index 061a0f3..949daff 100644
--- a/lib/src/source_code.dart
+++ b/lib/src/source_code.dart
@@ -37,7 +37,7 @@
   ///
   /// If there is no selection, returns an empty string.
   String get selectedText {
-    if (selectionStart == null) return "";
+    if (selectionStart == null) return '';
     return text.substring(selectionStart, selectionStart + selectionLength);
   }
 
@@ -45,7 +45,7 @@
   ///
   /// If there is no selection, returns an empty string.
   String get textAfterSelection {
-    if (selectionStart == null) return "";
+    if (selectionStart == null) return '';
     return text.substring(selectionStart + selectionLength);
   }
 
@@ -57,26 +57,26 @@
     // Must either provide both selection bounds or neither.
     if ((selectionStart == null) != (selectionLength == null)) {
       throw ArgumentError(
-          "Is selectionStart is provided, selectionLength must be too.");
+          'Is selectionStart is provided, selectionLength must be too.');
     }
 
     if (selectionStart != null) {
       if (selectionStart < 0) {
-        throw ArgumentError("selectionStart must be non-negative.");
+        throw ArgumentError('selectionStart must be non-negative.');
       }
 
       if (selectionStart > text.length) {
-        throw ArgumentError("selectionStart must be within text.");
+        throw ArgumentError('selectionStart must be within text.');
       }
     }
 
     if (selectionLength != null) {
       if (selectionLength < 0) {
-        throw ArgumentError("selectionLength must be non-negative.");
+        throw ArgumentError('selectionLength must be non-negative.');
       }
 
       if (selectionStart + selectionLength > text.length) {
-        throw ArgumentError("selectionLength must end within text.");
+        throw ArgumentError('selectionLength must end within text.');
       }
     }
   }
diff --git a/lib/src/source_visitor.dart b/lib/src/source_visitor.dart
index 417bf2f..ffd7a0f 100644
--- a/lib/src/source_visitor.dart
+++ b/lib/src/source_visitor.dart
@@ -81,10 +81,10 @@
   /// letter (so that we can distinguish them from SCREAMING_CAPS constants).
   static bool _looksLikeClassName(String name) {
     // Handle the weird lowercase corelib names.
-    if (name == "bool") return true;
-    if (name == "double") return true;
-    if (name == "int") return true;
-    if (name == "num") return true;
+    if (name == 'bool') return true;
+    if (name == 'double') return true;
+    if (name == 'int') return true;
+    if (name == 'num') return true;
 
     // TODO(rnystrom): A simpler implementation is to test against the regex
     // "_?[A-Z].*?[a-z]". However, that currently has much worse performance on
@@ -123,7 +123,7 @@
   final DartFormatter _formatter;
 
   /// Cached line info for calculating blank lines.
-  LineInfo _lineInfo;
+  final LineInfo _lineInfo;
 
   /// The source being formatted.
   final SourceCode _source;
@@ -193,7 +193,7 @@
 
   /// Comments and new lines attached to tokens added here are suppressed
   /// from the output.
-  Set<Token> _suppressPrecedingCommentsAndNewLines = {};
+  final Set<Token> _suppressPrecedingCommentsAndNewLines = {};
 
   /// Initialize a newly created visitor to write source code representing
   /// the visited nodes to the given [writer].
@@ -214,13 +214,14 @@
     // Output trailing comments.
     writePrecedingCommentsAndNewlines(node.endToken.next);
 
-    assert(_constNesting == 0, "Should have exited all const contexts.");
+    assert(_constNesting == 0, 'Should have exited all const contexts.');
 
     // Finish writing and return the complete result.
     return builder.end();
   }
 
-  visitAdjacentStrings(AdjacentStrings node) {
+  @override
+  void visitAdjacentStrings(AdjacentStrings node) {
     // We generally want to indent adjacent strings because it can be confusing
     // otherwise when they appear in a list of expressions, like:
     //
@@ -289,7 +290,8 @@
     builder.endSpan();
   }
 
-  visitAnnotation(Annotation node) {
+  @override
+  void visitAnnotation(Annotation node) {
     token(node.atSign);
     visit(node.name);
     token(node.period);
@@ -312,7 +314,8 @@
   /// 4. Split between one or more positional arguments, trying to keep as many
   ///    on earlier lines as possible.
   /// 5. Split the named arguments each onto their own line.
-  visitArgumentList(ArgumentList node, {bool nestExpression = true}) {
+  @override
+  void visitArgumentList(ArgumentList node, {bool nestExpression = true}) {
     // Corner case: handle empty argument lists.
     if (node.arguments.isEmpty) {
       token(node.leftParenthesis);
@@ -338,7 +341,8 @@
     if (nestExpression) builder.unnest();
   }
 
-  visitAsExpression(AsExpression node) {
+  @override
+  void visitAsExpression(AsExpression node) {
     builder.startSpan();
     builder.nestExpression();
     visit(node.expression);
@@ -350,7 +354,8 @@
     builder.endSpan();
   }
 
-  visitAssertInitializer(AssertInitializer node) {
+  @override
+  void visitAssertInitializer(AssertInitializer node) {
     token(node.assertKeyword);
 
     var arguments = <Expression>[node.condition];
@@ -372,7 +377,8 @@
     builder.unnest();
   }
 
-  visitAssertStatement(AssertStatement node) {
+  @override
+  void visitAssertStatement(AssertStatement node) {
     _simpleStatement(node, () {
       token(node.assertKeyword);
 
@@ -394,7 +400,8 @@
     });
   }
 
-  visitAssignmentExpression(AssignmentExpression node) {
+  @override
+  void visitAssignmentExpression(AssignmentExpression node) {
     builder.nestExpression();
 
     visit(node.leftHandSide);
@@ -403,13 +410,15 @@
     builder.unnest();
   }
 
-  visitAwaitExpression(AwaitExpression node) {
+  @override
+  void visitAwaitExpression(AwaitExpression node) {
     token(node.awaitKeyword);
     space();
     visit(node.expression);
   }
 
-  visitBinaryExpression(BinaryExpression node) {
+  @override
+  void visitBinaryExpression(BinaryExpression node) {
     builder.startSpan();
 
     // If a binary operator sequence appears immediately after a `=>`, don't
@@ -431,7 +440,8 @@
     // precedence level, we will break all of them.
     var precedence = node.operator.type.precedence;
 
-    traverse(Expression e) {
+    @override
+    void traverse(Expression e) {
       if (e is BinaryExpression && e.operator.type.precedence == precedence) {
         traverse(e.leftOperand);
 
@@ -458,7 +468,8 @@
     builder.endRule();
   }
 
-  visitBlock(Block node) {
+  @override
+  void visitBlock(Block node) {
     // Treat empty blocks specially. In most cases, they are not allowed to
     // split. However, an empty block as the then statement of an if with an
     // else is always split.
@@ -520,7 +531,8 @@
     }
   }
 
-  visitBlockFunctionBody(BlockFunctionBody node) {
+  @override
+  void visitBlockFunctionBody(BlockFunctionBody node) {
     // Space after the parameter list.
     space();
 
@@ -534,18 +546,21 @@
     visit(node.block);
   }
 
-  visitBooleanLiteral(BooleanLiteral node) {
+  @override
+  void visitBooleanLiteral(BooleanLiteral node) {
     token(node.literal);
   }
 
-  visitBreakStatement(BreakStatement node) {
+  @override
+  void visitBreakStatement(BreakStatement node) {
     _simpleStatement(node, () {
       token(node.breakKeyword);
       visit(node.label, before: space);
     });
   }
 
-  visitCascadeExpression(CascadeExpression node) {
+  @override
+  void visitCascadeExpression(CascadeExpression node) {
     var splitIfOperandsSplit =
         node.cascadeSections.length > 1 || _isCollectionLike(node.target);
 
@@ -676,7 +691,8 @@
     return true;
   }
 
-  visitCatchClause(CatchClause node) {
+  @override
+  void visitCatchClause(CatchClause node) {
     token(node.onKeyword, after: space);
     visit(node.exceptionType);
 
@@ -698,7 +714,8 @@
     visit(node.body);
   }
 
-  visitClassDeclaration(ClassDeclaration node) {
+  @override
+  void visitClassDeclaration(ClassDeclaration node) {
     visitMetadata(node.metadata);
 
     builder.nestExpression();
@@ -723,7 +740,8 @@
     _endBody(node.rightBracket);
   }
 
-  visitClassTypeAlias(ClassTypeAlias node) {
+  @override
+  void visitClassTypeAlias(ClassTypeAlias node) {
     visitMetadata(node.metadata);
 
     _simpleStatement(node, () {
@@ -745,11 +763,14 @@
     });
   }
 
-  visitComment(Comment node) => null;
+  @override
+  void visitComment(Comment node) => null;
 
-  visitCommentReference(CommentReference node) => null;
+  @override
+  void visitCommentReference(CommentReference node) => null;
 
-  visitCompilationUnit(CompilationUnit node) {
+  @override
+  void visitCompilationUnit(CompilationUnit node) {
     visit(node.scriptTag);
 
     // Put a blank line between the library tag and the other directives.
@@ -795,7 +816,8 @@
     }
   }
 
-  visitConditionalExpression(ConditionalExpression node) {
+  @override
+  void visitConditionalExpression(ConditionalExpression node) {
     builder.nestExpression();
 
     // Start lazily so we don't force the operator to split if a line comment
@@ -829,7 +851,8 @@
     builder.unnest();
   }
 
-  visitConfiguration(Configuration node) {
+  @override
+  void visitConfiguration(Configuration node) {
     token(node.ifKeyword);
     space();
     token(node.leftParenthesis);
@@ -849,7 +872,8 @@
     visit(node.uri);
   }
 
-  visitConstructorDeclaration(ConstructorDeclaration node) {
+  @override
+  void visitConstructorDeclaration(ConstructorDeclaration node) {
     visitMetadata(node.metadata);
 
     modifier(node.externalKeyword);
@@ -902,7 +926,7 @@
       //           super();
       space();
       if (node.initializers.length > 1) {
-        _writeText(node.parameters.parameters.last.isOptional ? " " : "  ",
+        _writeText(node.parameters.parameters.last.isOptional ? ' ' : '  ',
             node.separator.offset);
       }
 
@@ -960,7 +984,8 @@
     builder.endRule();
   }
 
-  visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
+  @override
+  void visitConstructorFieldInitializer(ConstructorFieldInitializer node) {
     builder.nestExpression();
 
     token(node.thisKeyword);
@@ -972,26 +997,30 @@
     builder.unnest();
   }
 
-  visitConstructorName(ConstructorName node) {
+  @override
+  void visitConstructorName(ConstructorName node) {
     visit(node.type);
     token(node.period);
     visit(node.name);
   }
 
-  visitContinueStatement(ContinueStatement node) {
+  @override
+  void visitContinueStatement(ContinueStatement node) {
     _simpleStatement(node, () {
       token(node.continueKeyword);
       visit(node.label, before: space);
     });
   }
 
-  visitDeclaredIdentifier(DeclaredIdentifier node) {
+  @override
+  void visitDeclaredIdentifier(DeclaredIdentifier node) {
     modifier(node.keyword);
     visit(node.type, after: space);
     visit(node.identifier);
   }
 
-  visitDefaultFormalParameter(DefaultFormalParameter node) {
+  @override
+  void visitDefaultFormalParameter(DefaultFormalParameter node) {
     visit(node.parameter);
     if (node.separator != null) {
       builder.startSpan();
@@ -1001,7 +1030,7 @@
         // Change the separator to "=".
         space();
         writePrecedingCommentsAndNewlines(node.separator);
-        _writeText("=", node.separator.offset);
+        _writeText('=', node.separator.offset);
       } else {
         // The '=' separator is preceded by a space, ":" is not.
         if (node.separator.type == TokenType.EQ) space();
@@ -1016,7 +1045,8 @@
     }
   }
 
-  visitDoStatement(DoStatement node) {
+  @override
+  void visitDoStatement(DoStatement node) {
     builder.nestExpression();
     token(node.doKeyword);
     space();
@@ -1035,7 +1065,8 @@
     builder.unnest();
   }
 
-  visitDottedName(DottedName node) {
+  @override
+  void visitDottedName(DottedName node) {
     for (var component in node.components) {
       // Write the preceding ".".
       if (component != node.components.first) {
@@ -1046,24 +1077,29 @@
     }
   }
 
-  visitDoubleLiteral(DoubleLiteral node) {
+  @override
+  void visitDoubleLiteral(DoubleLiteral node) {
     token(node.literal);
   }
 
-  visitEmptyFunctionBody(EmptyFunctionBody node) {
+  @override
+  void visitEmptyFunctionBody(EmptyFunctionBody node) {
     token(node.semicolon);
   }
 
-  visitEmptyStatement(EmptyStatement node) {
+  @override
+  void visitEmptyStatement(EmptyStatement node) {
     token(node.semicolon);
   }
 
-  visitEnumConstantDeclaration(EnumConstantDeclaration node) {
+  @override
+  void visitEnumConstantDeclaration(EnumConstantDeclaration node) {
     visitMetadata(node.metadata);
     visit(node.name);
   }
 
-  visitEnumDeclaration(EnumDeclaration node) {
+  @override
+  void visitEnumDeclaration(EnumDeclaration node) {
     visitMetadata(node.metadata);
 
     token(node.enumKeyword);
@@ -1082,7 +1118,8 @@
     _endBody(node.rightBracket, space: true);
   }
 
-  visitExportDirective(ExportDirective node) {
+  @override
+  void visitExportDirective(ExportDirective node) {
     _visitDirectiveMetadata(node);
     _simpleStatement(node, () {
       token(node.keyword);
@@ -1097,7 +1134,8 @@
     });
   }
 
-  visitExpressionFunctionBody(ExpressionFunctionBody node) {
+  @override
+  void visitExpressionFunctionBody(ExpressionFunctionBody node) {
     // Space after the parameter list.
     space();
 
@@ -1315,7 +1353,8 @@
     }
   }
 
-  visitExpressionStatement(ExpressionStatement node) {
+  @override
+  void visitExpressionStatement(ExpressionStatement node) {
     if (_formatter.fixes.contains(StyleFix.singleCascadeStatements) &&
         node.expression is CascadeExpression &&
         _fixSingleCascadeStatement(node)) {
@@ -1327,13 +1366,15 @@
     });
   }
 
-  visitExtendsClause(ExtendsClause node) {
+  @override
+  void visitExtendsClause(ExtendsClause node) {
     soloSplit();
     token(node.extendsKeyword);
     space();
     visit(node.superclass);
   }
 
+  @override
   void visitExtensionDeclaration(ExtensionDeclaration node) {
     visitMetadata(node.metadata);
 
@@ -1360,7 +1401,8 @@
     _endBody(node.rightBracket);
   }
 
-  visitFieldDeclaration(FieldDeclaration node) {
+  @override
+  void visitFieldDeclaration(FieldDeclaration node) {
     visitMetadata(node.metadata);
 
     _simpleStatement(node, () {
@@ -1370,7 +1412,8 @@
     });
   }
 
-  visitFieldFormalParameter(FieldFormalParameter node) {
+  @override
+  void visitFieldFormalParameter(FieldFormalParameter node) {
     visitParameterMetadata(node.metadata, () {
       _beginFormalParameter(node);
       token(node.keyword, after: space);
@@ -1383,7 +1426,8 @@
     });
   }
 
-  visitFormalParameterList(FormalParameterList node,
+  @override
+  void visitFormalParameterList(FormalParameterList node,
       {bool nestExpression = true}) {
     // Corner case: empty parameter lists.
     if (node.parameters.isEmpty) {
@@ -1480,6 +1524,7 @@
     if (nestExpression) builder.unnest();
   }
 
+  @override
   void visitForElement(ForElement node) {
     // Treat a spread of a collection literal like a block in a for statement
     // and don't split after the for parts.
@@ -1519,7 +1564,8 @@
     builder.endRule();
   }
 
-  visitForStatement(ForStatement node) {
+  @override
+  void visitForStatement(ForStatement node) {
     builder.nestExpression();
     token(node.awaitKeyword, after: space);
     token(node.forKeyword);
@@ -1537,7 +1583,8 @@
     _visitLoopBody(node.body);
   }
 
-  visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
+  @override
+  void visitForEachPartsWithDeclaration(ForEachPartsWithDeclaration node) {
     // TODO(rnystrom): The formatting logic here is slightly different from
     // how parameter metadata is handled and from how variable metadata is
     // handled. I think what it does works better in the context of a for-in
@@ -1582,12 +1629,14 @@
     visit(node.iterable);
   }
 
-  visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
+  @override
+  void visitForEachPartsWithIdentifier(ForEachPartsWithIdentifier node) {
     visit(node.identifier);
     _visitForEachPartsFromIn(node);
   }
 
-  visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
+  @override
+  void visitForPartsWithDeclarations(ForPartsWithDeclarations node) {
     // Nest split variables more so they aren't at the same level
     // as the rest of the loop clauses.
     builder.nestExpression();
@@ -1610,7 +1659,8 @@
     _visitForPartsFromLeftSeparator(node);
   }
 
-  visitForPartsWithExpression(ForPartsWithExpression node) {
+  @override
+  void visitForPartsWithExpression(ForPartsWithExpression node) {
     visit(node.initialization);
     _visitForPartsFromLeftSeparator(node);
   }
@@ -1636,15 +1686,18 @@
     }
   }
 
-  visitFunctionDeclaration(FunctionDeclaration node) {
+  @override
+  void visitFunctionDeclaration(FunctionDeclaration node) {
     _visitMemberDeclaration(node, node.functionExpression);
   }
 
-  visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
+  @override
+  void visitFunctionDeclarationStatement(FunctionDeclarationStatement node) {
     visit(node.functionDeclaration);
   }
 
-  visitFunctionExpression(FunctionExpression node) {
+  @override
+  void visitFunctionExpression(FunctionExpression node) {
     // Inside a function body is no longer in the surrounding const context.
     var oldConstNesting = _constNesting;
     _constNesting = 0;
@@ -1667,7 +1720,8 @@
     _constNesting = oldConstNesting;
   }
 
-  visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
+  @override
+  void visitFunctionExpressionInvocation(FunctionExpressionInvocation node) {
     // Try to keep the entire invocation one line.
     builder.startSpan();
     builder.nestExpression();
@@ -1680,7 +1734,8 @@
     builder.endSpan();
   }
 
-  visitFunctionTypeAlias(FunctionTypeAlias node) {
+  @override
+  void visitFunctionTypeAlias(FunctionTypeAlias node) {
     visitMetadata(node.metadata);
 
     if (_formatter.fixes.contains(StyleFix.functionTypedefs)) {
@@ -1710,7 +1765,8 @@
     });
   }
 
-  visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
+  @override
+  void visitFunctionTypedFormalParameter(FunctionTypedFormalParameter node) {
     visitParameterMetadata(node.metadata, () {
       if (!_insideNewTypedefFix) {
         modifier(node.requiredKeyword);
@@ -1734,13 +1790,15 @@
     });
   }
 
-  visitGenericFunctionType(GenericFunctionType node) {
+  @override
+  void visitGenericFunctionType(GenericFunctionType node) {
     _visitGenericFunctionType(node.returnType, node.functionKeyword, null,
         node.typeParameters, node.parameters);
     token(node.question);
   }
 
-  visitGenericTypeAlias(GenericTypeAlias node) {
+  @override
+  void visitGenericTypeAlias(GenericTypeAlias node) {
     visitNodes(node.metadata, between: newline, after: newline);
     _simpleStatement(node, () {
       _visitGenericTypeAliasHeader(node.typedefKeyword, node.name,
@@ -1752,10 +1810,12 @@
     });
   }
 
-  visitHideCombinator(HideCombinator node) {
+  @override
+  void visitHideCombinator(HideCombinator node) {
     _visitCombinator(node.keyword, node.hiddenNames);
   }
 
+  @override
   void visitIfElement(IfElement node) {
     // Treat a chain of if-else elements as a single unit so that we don't
     // unnecessarily indent each subsequent section of the chain.
@@ -1807,7 +1867,8 @@
       beforeBlock(elseSpreadBracket, spreadRule, null);
     }
 
-    visitChild(CollectionElement element, CollectionElement child) {
+    @override
+    void visitChild(CollectionElement element, CollectionElement child) {
       builder.nestExpression(indent: 2, now: true);
 
       // Treat a spread of a collection literal like a block in an if statement
@@ -1868,7 +1929,8 @@
     builder.endRule();
   }
 
-  visitIfStatement(IfStatement node) {
+  @override
+  void visitIfStatement(IfStatement node) {
     builder.nestExpression();
     token(node.ifKeyword);
     space();
@@ -1877,7 +1939,8 @@
     token(node.rightParenthesis);
     builder.unnest();
 
-    visitClause(Statement clause) {
+    @override
+    void visitClause(Statement clause) {
       if (clause is Block || clause is IfStatement) {
         space();
         visit(clause);
@@ -1920,11 +1983,13 @@
     }
   }
 
-  visitImplementsClause(ImplementsClause node) {
+  @override
+  void visitImplementsClause(ImplementsClause node) {
     _visitCombinator(node.implementsKeyword, node.interfaces);
   }
 
-  visitImportDirective(ImportDirective node) {
+  @override
+  void visitImportDirective(ImportDirective node) {
     _visitDirectiveMetadata(node);
     _simpleStatement(node, () {
       token(node.keyword);
@@ -1947,7 +2012,8 @@
     });
   }
 
-  visitIndexExpression(IndexExpression node) {
+  @override
+  void visitIndexExpression(IndexExpression node) {
     builder.nestExpression();
 
     if (node.isCascaded) {
@@ -1982,7 +2048,8 @@
     builder.endSpan();
   }
 
-  visitInstanceCreationExpression(InstanceCreationExpression node) {
+  @override
+  void visitInstanceCreationExpression(InstanceCreationExpression node) {
     builder.startSpan();
 
     var includeKeyword = true;
@@ -2024,11 +2091,13 @@
     builder.unnest();
   }
 
-  visitIntegerLiteral(IntegerLiteral node) {
+  @override
+  void visitIntegerLiteral(IntegerLiteral node) {
     token(node.literal);
   }
 
-  visitInterpolationExpression(InterpolationExpression node) {
+  @override
+  void visitInterpolationExpression(InterpolationExpression node) {
     builder.preventSplit();
     token(node.leftBracket);
     builder.startSpan();
@@ -2038,11 +2107,13 @@
     builder.endPreventSplit();
   }
 
-  visitInterpolationString(InterpolationString node) {
+  @override
+  void visitInterpolationString(InterpolationString node) {
     _writeStringLiteral(node.contents);
   }
 
-  visitIsExpression(IsExpression node) {
+  @override
+  void visitIsExpression(IsExpression node) {
     builder.startSpan();
     builder.nestExpression();
     visit(node.expression);
@@ -2055,17 +2126,20 @@
     builder.endSpan();
   }
 
-  visitLabel(Label node) {
+  @override
+  void visitLabel(Label node) {
     visit(node.label);
     token(node.colon);
   }
 
-  visitLabeledStatement(LabeledStatement node) {
+  @override
+  void visitLabeledStatement(LabeledStatement node) {
     _visitLabels(node.labels);
     visit(node.statement);
   }
 
-  visitLibraryDirective(LibraryDirective node) {
+  @override
+  void visitLibraryDirective(LibraryDirective node) {
     _visitDirectiveMetadata(node);
     _simpleStatement(node, () {
       token(node.keyword);
@@ -2074,7 +2148,8 @@
     });
   }
 
-  visitLibraryIdentifier(LibraryIdentifier node) {
+  @override
+  void visitLibraryIdentifier(LibraryIdentifier node) {
     visit(node.components.first);
     for (var component in node.components.skip(1)) {
       token(component.beginToken.previous); // "."
@@ -2082,7 +2157,8 @@
     }
   }
 
-  visitListLiteral(ListLiteral node) {
+  @override
+  void visitListLiteral(ListLiteral node) {
     // Corner case: Splitting inside a list looks bad if there's only one
     // element, so make those more costly.
     var cost = node.elements.length <= 1 ? Cost.singleElementList : Cost.normal;
@@ -2090,7 +2166,8 @@
         node, node.leftBracket, node.elements, node.rightBracket, cost);
   }
 
-  visitMapLiteralEntry(MapLiteralEntry node) {
+  @override
+  void visitMapLiteralEntry(MapLiteralEntry node) {
     builder.nestExpression();
     visit(node.key);
     token(node.separator);
@@ -2099,11 +2176,13 @@
     builder.unnest();
   }
 
-  visitMethodDeclaration(MethodDeclaration node) {
+  @override
+  void visitMethodDeclaration(MethodDeclaration node) {
     _visitMemberDeclaration(node, node);
   }
 
-  visitMethodInvocation(MethodInvocation node) {
+  @override
+  void visitMethodInvocation(MethodInvocation node) {
     // If there's no target, this is a "bare" function call like "foo(1, 2)",
     // or a section in a cascade.
     //
@@ -2157,7 +2236,8 @@
     CallChainVisitor(this, node).visit();
   }
 
-  visitMixinDeclaration(MixinDeclaration node) {
+  @override
+  void visitMixinDeclaration(MixinDeclaration node) {
     visitMetadata(node.metadata);
 
     builder.nestExpression();
@@ -2196,16 +2276,19 @@
     _endBody(node.rightBracket);
   }
 
-  visitNamedExpression(NamedExpression node) {
+  @override
+  void visitNamedExpression(NamedExpression node) {
     visitNamedArgument(node);
   }
 
-  visitNativeClause(NativeClause node) {
+  @override
+  void visitNativeClause(NativeClause node) {
     token(node.nativeKeyword);
     visit(node.name, before: space);
   }
 
-  visitNativeFunctionBody(NativeFunctionBody node) {
+  @override
+  void visitNativeFunctionBody(NativeFunctionBody node) {
     _simpleStatement(node, () {
       builder.nestExpression(now: true);
       soloSplit();
@@ -2215,15 +2298,18 @@
     });
   }
 
-  visitNullLiteral(NullLiteral node) {
+  @override
+  void visitNullLiteral(NullLiteral node) {
     token(node.literal);
   }
 
-  visitOnClause(OnClause node) {
+  @override
+  void visitOnClause(OnClause node) {
     _visitCombinator(node.onKeyword, node.superclassConstraints);
   }
 
-  visitParenthesizedExpression(ParenthesizedExpression node) {
+  @override
+  void visitParenthesizedExpression(ParenthesizedExpression node) {
     builder.nestExpression();
     token(node.leftParenthesis);
     visit(node.expression);
@@ -2231,7 +2317,8 @@
     token(node.rightParenthesis);
   }
 
-  visitPartDirective(PartDirective node) {
+  @override
+  void visitPartDirective(PartDirective node) {
     _visitDirectiveMetadata(node);
     _simpleStatement(node, () {
       token(node.keyword);
@@ -2240,7 +2327,8 @@
     });
   }
 
-  visitPartOfDirective(PartOfDirective node) {
+  @override
+  void visitPartOfDirective(PartOfDirective node) {
     _visitDirectiveMetadata(node);
     _simpleStatement(node, () {
       token(node.keyword);
@@ -2255,30 +2343,34 @@
     });
   }
 
-  visitPostfixExpression(PostfixExpression node) {
+  @override
+  void visitPostfixExpression(PostfixExpression node) {
     visit(node.operand);
     token(node.operator);
   }
 
-  visitPrefixedIdentifier(PrefixedIdentifier node) {
+  @override
+  void visitPrefixedIdentifier(PrefixedIdentifier node) {
     CallChainVisitor(this, node).visit();
   }
 
-  visitPrefixExpression(PrefixExpression node) {
+  @override
+  void visitPrefixExpression(PrefixExpression node) {
     token(node.operator);
 
     // Edge case: put a space after "-" if the operand is "-" or "--" so we
     // don't merge the operators.
     var operand = node.operand;
     if (operand is PrefixExpression &&
-        (operand.operator.lexeme == "-" || operand.operator.lexeme == "--")) {
+        (operand.operator.lexeme == '-' || operand.operator.lexeme == '--')) {
       space();
     }
 
     visit(node.operand);
   }
 
-  visitPropertyAccess(PropertyAccess node) {
+  @override
+  void visitPropertyAccess(PropertyAccess node) {
     if (node.isCascaded) {
       token(node.operator);
       visit(node.propertyName);
@@ -2288,7 +2380,9 @@
     CallChainVisitor(this, node).visit();
   }
 
-  visitRedirectingConstructorInvocation(RedirectingConstructorInvocation node) {
+  @override
+  void visitRedirectingConstructorInvocation(
+      RedirectingConstructorInvocation node) {
     builder.startSpan();
 
     token(node.thisKeyword);
@@ -2299,18 +2393,21 @@
     builder.endSpan();
   }
 
-  visitRethrowExpression(RethrowExpression node) {
+  @override
+  void visitRethrowExpression(RethrowExpression node) {
     token(node.rethrowKeyword);
   }
 
-  visitReturnStatement(ReturnStatement node) {
+  @override
+  void visitReturnStatement(ReturnStatement node) {
     _simpleStatement(node, () {
       token(node.returnKeyword);
       visit(node.expression, before: space);
     });
   }
 
-  visitScriptTag(ScriptTag node) {
+  @override
+  void visitScriptTag(ScriptTag node) {
     // The lexeme includes the trailing newline. Strip it off since the
     // formatter ensures it gets a newline after it. Since the script tag must
     // come at the top of the file, we don't have to worry about preceding
@@ -2319,16 +2416,19 @@
     newline();
   }
 
-  visitSetOrMapLiteral(SetOrMapLiteral node) {
+  @override
+  void visitSetOrMapLiteral(SetOrMapLiteral node) {
     _visitCollectionLiteral(
         node, node.leftBracket, node.elements, node.rightBracket);
   }
 
-  visitShowCombinator(ShowCombinator node) {
+  @override
+  void visitShowCombinator(ShowCombinator node) {
     _visitCombinator(node.keyword, node.shownNames);
   }
 
-  visitSimpleFormalParameter(SimpleFormalParameter node) {
+  @override
+  void visitSimpleFormalParameter(SimpleFormalParameter node) {
     visitParameterMetadata(node.metadata, () {
       _beginFormalParameter(node);
 
@@ -2351,7 +2451,7 @@
 
         // Ensure comments on the identifier comes before the inserted type.
         token(node.identifier.token, before: () {
-          _writeText("dynamic", node.identifier.offset);
+          _writeText('dynamic', node.identifier.offset);
           split();
         });
       } else {
@@ -2367,26 +2467,31 @@
     });
   }
 
-  visitSimpleIdentifier(SimpleIdentifier node) {
+  @override
+  void visitSimpleIdentifier(SimpleIdentifier node) {
     token(node.token);
   }
 
-  visitSimpleStringLiteral(SimpleStringLiteral node) {
+  @override
+  void visitSimpleStringLiteral(SimpleStringLiteral node) {
     _writeStringLiteral(node.literal);
   }
 
-  visitSpreadElement(SpreadElement node) {
+  @override
+  void visitSpreadElement(SpreadElement node) {
     token(node.spreadOperator);
     visit(node.expression);
   }
 
-  visitStringInterpolation(StringInterpolation node) {
+  @override
+  void visitStringInterpolation(StringInterpolation node) {
     for (var element in node.elements) {
       visit(element);
     }
   }
 
-  visitSuperConstructorInvocation(SuperConstructorInvocation node) {
+  @override
+  void visitSuperConstructorInvocation(SuperConstructorInvocation node) {
     builder.startSpan();
 
     token(node.superKeyword);
@@ -2397,11 +2502,13 @@
     builder.endSpan();
   }
 
-  visitSuperExpression(SuperExpression node) {
+  @override
+  void visitSuperExpression(SuperExpression node) {
     token(node.superKeyword);
   }
 
-  visitSwitchCase(SwitchCase node) {
+  @override
+  void visitSwitchCase(SwitchCase node) {
     _visitLabels(node.labels);
     token(node.keyword);
     space();
@@ -2416,7 +2523,8 @@
     builder.unindent();
   }
 
-  visitSwitchDefault(SwitchDefault node) {
+  @override
+  void visitSwitchDefault(SwitchDefault node) {
     _visitLabels(node.labels);
     token(node.keyword);
     token(node.colon);
@@ -2429,7 +2537,8 @@
     builder.unindent();
   }
 
-  visitSwitchStatement(SwitchStatement node) {
+  @override
+  void visitSwitchStatement(SwitchStatement node) {
     builder.nestExpression();
     token(node.switchKeyword);
     space();
@@ -2450,7 +2559,8 @@
     });
   }
 
-  visitSymbolLiteral(SymbolLiteral node) {
+  @override
+  void visitSymbolLiteral(SymbolLiteral node) {
     token(node.poundSign);
     var components = node.components;
     for (var component in components) {
@@ -2462,17 +2572,20 @@
     }
   }
 
-  visitThisExpression(ThisExpression node) {
+  @override
+  void visitThisExpression(ThisExpression node) {
     token(node.thisKeyword);
   }
 
-  visitThrowExpression(ThrowExpression node) {
+  @override
+  void visitThrowExpression(ThrowExpression node) {
     token(node.throwKeyword);
     space();
     visit(node.expression);
   }
 
-  visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
+  @override
+  void visitTopLevelVariableDeclaration(TopLevelVariableDeclaration node) {
     visitMetadata(node.metadata);
 
     _simpleStatement(node, () {
@@ -2480,7 +2593,8 @@
     });
   }
 
-  visitTryStatement(TryStatement node) {
+  @override
+  void visitTryStatement(TryStatement node) {
     token(node.tryKeyword);
     space();
     visit(node.body);
@@ -2489,17 +2603,20 @@
     visit(node.finallyBlock);
   }
 
-  visitTypeArgumentList(TypeArgumentList node) {
+  @override
+  void visitTypeArgumentList(TypeArgumentList node) {
     _visitGenericList(node.leftBracket, node.rightBracket, node.arguments);
   }
 
-  visitTypeName(TypeName node) {
+  @override
+  void visitTypeName(TypeName node) {
     visit(node.name);
     visit(node.typeArguments);
     token(node.question);
   }
 
-  visitTypeParameter(TypeParameter node) {
+  @override
+  void visitTypeParameter(TypeParameter node) {
     visitParameterMetadata(node.metadata, () {
       visit(node.name);
       token(node.extendsKeyword, before: space, after: space);
@@ -2507,7 +2624,8 @@
     });
   }
 
-  visitTypeParameterList(TypeParameterList node) {
+  @override
+  void visitTypeParameterList(TypeParameterList node) {
     _metadataRules.add(MetadataRule());
 
     _visitGenericList(node.leftBracket, node.rightBracket, node.typeParameters);
@@ -2515,7 +2633,8 @@
     _metadataRules.removeLast();
   }
 
-  visitVariableDeclaration(VariableDeclaration node) {
+  @override
+  void visitVariableDeclaration(VariableDeclaration node) {
     visit(node.name);
     if (node.initializer == null) return;
 
@@ -2539,7 +2658,8 @@
     _visitAssignment(node.equals, node.initializer, nest: hasMultipleVariables);
   }
 
-  visitVariableDeclarationList(VariableDeclarationList node) {
+  @override
+  void visitVariableDeclarationList(VariableDeclarationList node) {
     visitMetadata(node.metadata);
 
     // Allow but try to avoid splitting between the type and name.
@@ -2564,13 +2684,15 @@
     _endPossibleConstContext(node.keyword);
   }
 
-  visitVariableDeclarationStatement(VariableDeclarationStatement node) {
+  @override
+  void visitVariableDeclarationStatement(VariableDeclarationStatement node) {
     _simpleStatement(node, () {
       visit(node.variables);
     });
   }
 
-  visitWhileStatement(WhileStatement node) {
+  @override
+  void visitWhileStatement(WhileStatement node) {
     builder.nestExpression();
     token(node.whileKeyword);
     space();
@@ -2583,11 +2705,13 @@
     _visitLoopBody(node.body);
   }
 
-  visitWithClause(WithClause node) {
+  @override
+  void visitWithClause(WithClause node) {
     _visitCombinator(node.withKeyword, node.mixinTypes);
   }
 
-  visitYieldStatement(YieldStatement node) {
+  @override
+  void visitYieldStatement(YieldStatement node) {
     _simpleStatement(node, () {
       token(node.yieldKeyword);
       token(node.star);
@@ -2598,7 +2722,7 @@
 
   /// Visit a [node], and if not null, optionally preceded or followed by the
   /// specified functions.
-  void visit(AstNode node, {void before(), void after()}) {
+  void visit(AstNode node, {void Function() before, void Function() after}) {
     if (node == null) return;
 
     if (before != null) before();
@@ -2634,7 +2758,7 @@
   /// Unlike other annotations, these are allowed to stay on the same line as
   /// the parameter.
   void visitParameterMetadata(
-      NodeList<Annotation> metadata, void visitParameter()) {
+      NodeList<Annotation> metadata, void Function() visitParameter) {
     if (metadata == null || metadata.isEmpty) {
       visitParameter();
       return;
@@ -2817,7 +2941,7 @@
   /// If [beforeBody] is provided, it is invoked before the body is visited.
   void _visitBody(TypeParameterList typeParameters,
       FormalParameterList parameters, FunctionBody body,
-      [beforeBody()]) {
+      [void Function() beforeBody]) {
     // If the body is "=>", add an extra level of indentation around the
     // parameters and a rule that spans the parameters and the "=>". This
     // ensures that if the parameters wrap, they wrap more deeply than the "=>"
@@ -2897,7 +3021,10 @@
 
   /// Visit a list of [nodes] if not null, optionally separated and/or preceded
   /// and followed by the given functions.
-  void visitNodes(Iterable<AstNode> nodes, {before(), between(), after()}) {
+  void visitNodes(Iterable<AstNode> nodes,
+      {void Function() before,
+      void Function() between,
+      void Function() after}) {
     if (nodes == null || nodes.isEmpty) return;
 
     if (before != null) before();
@@ -2912,10 +3039,11 @@
   }
 
   /// Visit a comma-separated list of [nodes] if not null.
-  void visitCommaSeparatedNodes(Iterable<AstNode> nodes, {between()}) {
+  void visitCommaSeparatedNodes(Iterable<AstNode> nodes,
+      {void Function() between}) {
     if (nodes == null || nodes.isEmpty) return;
 
-    if (between == null) between = space;
+    between ??= space;
 
     var first = true;
     for (var node in nodes) {
@@ -2925,7 +3053,7 @@
       visit(node);
 
       // The comma after the node.
-      if (node.endToken.next.lexeme == ",") token(node.endToken.next);
+      if (node.endToken.next.lexeme == ',') token(node.endToken.next);
     }
   }
 
@@ -3128,7 +3256,7 @@
     if (functionKeyword != null) {
       token(functionKeyword);
     } else {
-      _writeText("Function", functionKeywordPosition);
+      _writeText('Function', functionKeywordPosition);
     }
 
     builder.unnest();
@@ -3159,7 +3287,7 @@
     if (equals != null) {
       token(equals);
     } else {
-      _writeText("=", equalsPosition);
+      _writeText('=', equalsPosition);
     }
 
     builder.endRule();
@@ -3246,7 +3374,7 @@
   /// This only looks for comments at the element boundary. Comments within an
   /// element are ignored.
   bool _containsLineComments(Iterable<AstNode> elements, Token rightBracket) {
-    hasLineCommentBefore(token) {
+    bool hasLineCommentBefore(token) {
       var comment = token.precedingComments;
       for (; comment != null; comment = comment.next) {
         if (comment.type == TokenType.SINGLE_LINE_COMMENT) return true;
@@ -3299,7 +3427,7 @@
   /// split.
   void _endLiteralBody(Token rightBracket,
       {Rule ignoredRule, bool forceSplit}) {
-    if (forceSplit == null) forceSplit = false;
+    forceSplit ??= false;
 
     // Put comments before the closing delimiter inside the block.
     var hasLeadingNewline = writePrecedingCommentsAndNewlines(rightBracket);
@@ -3367,7 +3495,7 @@
   ///
   /// Handles nesting if a line break occurs in the statement and writes the
   /// terminating semicolon. Invokes [body] which should write statement itself.
-  void _simpleStatement(AstNode node, body()) {
+  void _simpleStatement(AstNode node, void Function() body) {
     builder.nestExpression();
     body();
 
@@ -3541,7 +3669,7 @@
   /// Does nothing if [token] is `null`. If [before] is given, it will be
   /// executed before the token is outout. Likewise, [after] will be called
   /// after the token is output.
-  void token(Token token, {before(), after()}) {
+  void token(Token token, {void Function() before, void Function() after}) {
     if (token == null) return;
 
     writePrecedingCommentsAndNewlines(token);
@@ -3593,7 +3721,7 @@
       var linesBefore = commentLine - previousLine;
       var flushLeft = _startColumn(comment) == 1;
 
-      if (text.startsWith("///") && !text.startsWith("////")) {
+      if (text.startsWith('///') && !text.startsWith('////')) {
         // Line doc comments are always indented even if they were flush left.
         flushLeft = false;
 
diff --git a/lib/src/style_fix.dart b/lib/src/style_fix.dart
index cda99c7..032cd31 100644
--- a/lib/src/style_fix.dart
+++ b/lib/src/style_fix.dart
@@ -7,22 +7,22 @@
 /// formatting.
 class StyleFix {
   static const docComments = StyleFix._(
-      "doc-comments", 'Use triple slash for documentation comments.');
+      'doc-comments', 'Use triple slash for documentation comments.');
 
   static const functionTypedefs = StyleFix._(
-      "function-typedefs", 'Use new syntax for function type typedefs.');
+      'function-typedefs', 'Use new syntax for function type typedefs.');
 
-  static const namedDefaultSeparator = StyleFix._("named-default-separator",
+  static const namedDefaultSeparator = StyleFix._('named-default-separator',
       'Use "=" as the separator before named parameter default values.');
 
   static const optionalConst = StyleFix._(
-      "optional-const", 'Remove "const" keyword inside constant context.');
+      'optional-const', 'Remove "const" keyword inside constant context.');
 
   static const optionalNew =
-      StyleFix._("optional-new", 'Remove "new" keyword.');
+      StyleFix._('optional-new', 'Remove "new" keyword.');
 
-  static const singleCascadeStatements = StyleFix._("single-cascade-statements",
-      "Remove unnecessary single cascades from expression statements.");
+  static const singleCascadeStatements = StyleFix._('single-cascade-statements',
+      'Remove unnecessary single cascades from expression statements.');
 
   static const all = [
     docComments,
diff --git a/lib/src/whitespace.dart b/lib/src/whitespace.dart
index aff33c1..bf7eab2 100644
--- a/lib/src/whitespace.dart
+++ b/lib/src/whitespace.dart
@@ -26,33 +26,33 @@
 /// encountered to avoid trailing whitespace.
 class Whitespace {
   /// No whitespace.
-  static const none = Whitespace._("none");
+  static const none = Whitespace._('none');
 
   /// A single non-breaking space.
-  static const space = Whitespace._("space");
+  static const space = Whitespace._('space');
 
   /// A single newline.
-  static const newline = Whitespace._("newline");
+  static const newline = Whitespace._('newline');
 
   /// A single newline that takes into account the current expression nesting
   /// for the next line.
-  static const nestedNewline = Whitespace._("nestedNewline");
+  static const nestedNewline = Whitespace._('nestedNewline');
 
   /// A single newline with all indentation eliminated at the beginning of the
   /// next line.
   ///
   /// Used for subsequent lines in a multiline string.
-  static const newlineFlushLeft = Whitespace._("newlineFlushLeft");
+  static const newlineFlushLeft = Whitespace._('newlineFlushLeft');
 
   /// Two newlines, a single blank line of separation.
-  static const twoNewlines = Whitespace._("twoNewlines");
+  static const twoNewlines = Whitespace._('twoNewlines');
 
   /// A split or newline should be output based on whether the current token is
   /// on the same line as the previous one or not.
   ///
   /// In general, we like to avoid using this because it makes the formatter
   /// less prescriptive over the user's whitespace.
-  static const splitOrNewline = Whitespace._("splitOrNewline");
+  static const splitOrNewline = Whitespace._('splitOrNewline');
 
   /// A split or blank line (two newlines) should be output based on whether
   /// the current token is on the same line as the previous one or not.
@@ -62,14 +62,14 @@
   ///
   /// In general, we like to avoid using this because it makes the formatter
   /// less prescriptive over the user's whitespace.
-  static const splitOrTwoNewlines = Whitespace._("splitOrTwoNewlines");
+  static const splitOrTwoNewlines = Whitespace._('splitOrTwoNewlines');
 
   /// One or two newlines should be output based on how many newlines are
   /// present between the next token and the previous one.
   ///
   /// In general, we like to avoid using this because it makes the formatter
   /// less prescriptive over the user's whitespace.
-  static const oneOrTwoNewlines = Whitespace._("oneOrTwoNewlines");
+  static const oneOrTwoNewlines = Whitespace._('oneOrTwoNewlines');
 
   final String name;
 
@@ -92,5 +92,6 @@
 
   const Whitespace._(this.name);
 
+  @override
   String toString() => name;
 }
diff --git a/pubspec.lock b/pubspec.lock
index 8612aab..9aa4aa3 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -1,20 +1,27 @@
 # Generated by pub
 # See https://dart.dev/tools/pub/glossary#lockfile
 packages:
+  _fe_analyzer_shared:
+    dependency: transitive
+    description:
+      name: _fe_analyzer_shared
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.0.0"
   analyzer:
     dependency: "direct main"
     description:
       name: analyzer
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.39.0"
+    version: "0.39.1"
   archive:
     dependency: transitive
     description:
       name: archive
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.10"
+    version: "2.0.11"
   args:
     dependency: "direct main"
     description:
@@ -70,14 +77,14 @@
       name: coverage
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.13.3+1"
+    version: "0.13.3+3"
   crypto:
     dependency: transitive
     description:
       name: crypto
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.3"
+    version: "2.1.4"
   csslib:
     dependency: transitive
     description:
@@ -91,7 +98,7 @@
       name: front_end
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.1.28"
+    version: "0.1.29"
   glob:
     dependency: transitive
     description:
@@ -154,7 +161,7 @@
       name: kernel
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.3.28"
+    version: "0.3.29"
   logging:
     dependency: transitive
     description:
@@ -175,7 +182,7 @@
       name: meta
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.1.7"
+    version: "1.1.8"
   mime:
     dependency: transitive
     description:
@@ -238,7 +245,7 @@
       name: pedantic
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.8.0+1"
+    version: "1.9.0"
   pool:
     dependency: transitive
     description:
@@ -336,21 +343,21 @@
       name: test
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "1.9.3"
+    version: "1.9.4"
   test_api:
     dependency: transitive
     description:
       name: test_api
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.10"
+    version: "0.2.11"
   test_core:
     dependency: transitive
     description:
       name: test_core
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.2.14"
+    version: "0.2.15"
   test_descriptor:
     dependency: "direct dev"
     description:
@@ -378,14 +385,14 @@
       name: vm_service
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.1.1"
+    version: "2.1.3"
   watcher:
     dependency: transitive
     description:
       name: watcher
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.9.7+12"
+    version: "0.9.7+13"
   web_socket_channel:
     dependency: transitive
     description:
diff --git a/test/command_line_test.dart b/test/command_line_test.dart
index daaa75d..fce79bd 100644
--- a/test/command_line_test.dart
+++ b/test/command_line_test.dart
@@ -13,64 +13,64 @@
 import 'utils.dart';
 
 void main() {
-  test("exits with 0 on success", () async {
-    await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
+  test('exits with 0 on success', () async {
+    await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
 
     var process = await runFormatterOnDir();
     await process.shouldExit(0);
   });
 
-  test("exits with 64 on a command line argument error", () async {
-    var process = await runFormatter(["-wat"]);
+  test('exits with 64 on a command line argument error', () async {
+    var process = await runFormatter(['-wat']);
     await process.shouldExit(64);
   });
 
-  test("exits with 65 on a parse error", () async {
-    await d.dir("code", [d.file("a.dart", "herp derp i are a dart")]).create();
+  test('exits with 65 on a parse error', () async {
+    await d.dir('code', [d.file('a.dart', 'herp derp i are a dart')]).create();
 
     var process = await runFormatterOnDir();
     await process.shouldExit(65);
   });
 
-  test("errors if --dry-run and --overwrite are both passed", () async {
-    var process = await runFormatter(["--dry-run", "--overwrite"]);
+  test('errors if --dry-run and --overwrite are both passed', () async {
+    var process = await runFormatter(['--dry-run', '--overwrite']);
     await process.shouldExit(64);
   });
 
-  test("errors if --dry-run and --machine are both passed", () async {
-    var process = await runFormatter(["--dry-run", "--machine"]);
+  test('errors if --dry-run and --machine are both passed', () async {
+    var process = await runFormatter(['--dry-run', '--machine']);
     await process.shouldExit(64);
   });
 
-  test("errors if --machine and --overwrite are both passed", () async {
-    var process = await runFormatter(["--machine", "--overwrite"]);
+  test('errors if --machine and --overwrite are both passed', () async {
+    var process = await runFormatter(['--machine', '--overwrite']);
     await process.shouldExit(64);
   });
 
-  test("errors if --dry-run and --machine are both passed", () async {
-    var process = await runFormatter(["--dry-run", "--machine"]);
+  test('errors if --dry-run and --machine are both passed', () async {
+    var process = await runFormatter(['--dry-run', '--machine']);
     await process.shouldExit(64);
   });
 
-  test("errors if --machine and --overwrite are both passed", () async {
-    var process = await runFormatter(["--machine", "--overwrite"]);
+  test('errors if --machine and --overwrite are both passed', () async {
+    var process = await runFormatter(['--machine', '--overwrite']);
     await process.shouldExit(64);
   });
 
-  test("errors if --stdin-name and a path are both passed", () async {
-    var process = await runFormatter(["--stdin-name=name", "path.dart"]);
+  test('errors if --stdin-name and a path are both passed', () async {
+    var process = await runFormatter(['--stdin-name=name', 'path.dart']);
     await process.shouldExit(64);
   });
 
-  test("--version prints the version number", () async {
-    var process = await runFormatter(["--version"]);
+  test('--version prints the version number', () async {
+    var process = await runFormatter(['--version']);
 
     // Match something roughly semver-like.
-    expect(await process.stdout.next, matches(RegExp(r"\d+\.\d+\.\d+.*")));
+    expect(await process.stdout.next, matches(RegExp(r'\d+\.\d+\.\d+.*')));
     await process.shouldExit(0);
   });
 
-  test("only prints a hidden directory once", () async {
+  test('only prints a hidden directory once', () async {
     await d.dir('code', [
       d.dir('.skip', [
         d.file('a.dart', unformattedSource),
@@ -80,25 +80,25 @@
 
     var process = await runFormatterOnDir();
 
-    expect(await process.stdout.next, startsWith("Formatting directory"));
+    expect(await process.stdout.next, startsWith('Formatting directory'));
     expect(await process.stdout.next,
         "Skipping hidden path ${p.join("code", ".skip")}");
     await process.shouldExit();
   });
 
-  group("--dry-run", () {
-    test("prints names of files that would change", () async {
-      await d.dir("code", [
-        d.file("a_bad.dart", unformattedSource),
-        d.file("b_good.dart", formattedSource),
-        d.file("c_bad.dart", unformattedSource),
-        d.file("d_good.dart", formattedSource)
+  group('--dry-run', () {
+    test('prints names of files that would change', () async {
+      await d.dir('code', [
+        d.file('a_bad.dart', unformattedSource),
+        d.file('b_good.dart', formattedSource),
+        d.file('c_bad.dart', unformattedSource),
+        d.file('d_good.dart', formattedSource)
       ]).create();
 
-      var aBad = p.join("code", "a_bad.dart");
-      var cBad = p.join("code", "c_bad.dart");
+      var aBad = p.join('code', 'a_bad.dart');
+      var cBad = p.join('code', 'c_bad.dart');
 
-      var process = await runFormatterOnDir(["--dry-run"]);
+      var process = await runFormatterOnDir(['--dry-run']);
 
       // The order isn't specified.
       expect(await process.stdout.next, anyOf(aBad, cBad));
@@ -106,37 +106,37 @@
       await process.shouldExit();
     });
 
-    test("does not modify files", () async {
-      await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
+    test('does not modify files', () async {
+      await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
 
-      var process = await runFormatterOnDir(["--dry-run"]);
-      expect(await process.stdout.next, p.join("code", "a.dart"));
+      var process = await runFormatterOnDir(['--dry-run']);
+      expect(await process.stdout.next, p.join('code', 'a.dart'));
       await process.shouldExit();
 
       await d.dir('code', [d.file('a.dart', unformattedSource)]).validate();
     });
   });
 
-  group("--machine", () {
-    test("writes each output as json", () async {
-      await d.dir("code", [
-        d.file("a.dart", unformattedSource),
-        d.file("b.dart", unformattedSource)
+  group('--machine', () {
+    test('writes each output as json', () async {
+      await d.dir('code', [
+        d.file('a.dart', unformattedSource),
+        d.file('b.dart', unformattedSource)
       ]).create();
 
       var jsonA = jsonEncode({
-        "path": p.join("code", "a.dart"),
-        "source": formattedSource,
-        "selection": {"offset": -1, "length": -1}
+        'path': p.join('code', 'a.dart'),
+        'source': formattedSource,
+        'selection': {'offset': -1, 'length': -1}
       });
 
       var jsonB = jsonEncode({
-        "path": p.join("code", "b.dart"),
-        "source": formattedSource,
-        "selection": {"offset": -1, "length": -1}
+        'path': p.join('code', 'b.dart'),
+        'source': formattedSource,
+        'selection': {'offset': -1, 'length': -1}
       });
 
-      var process = await runFormatterOnDir(["--machine"]);
+      var process = await runFormatterOnDir(['--machine']);
 
       // The order isn't specified.
 
@@ -146,34 +146,34 @@
     });
   });
 
-  group("--preserve", () {
-    test("errors if given paths", () async {
-      var process = await runFormatter(["--preserve", "path", "another"]);
+  group('--preserve', () {
+    test('errors if given paths', () async {
+      var process = await runFormatter(['--preserve', 'path', 'another']);
       await process.shouldExit(64);
     });
 
-    test("errors on wrong number of components", () async {
-      var process = await runFormatter(["--preserve", "1"]);
+    test('errors on wrong number of components', () async {
+      var process = await runFormatter(['--preserve', '1']);
       await process.shouldExit(64);
 
-      process = await runFormatter(["--preserve", "1:2:3"]);
+      process = await runFormatter(['--preserve', '1:2:3']);
       await process.shouldExit(64);
     });
 
-    test("errors on non-integer component", () async {
-      var process = await runFormatter(["--preserve", "1:2.3"]);
+    test('errors on non-integer component', () async {
+      var process = await runFormatter(['--preserve', '1:2.3']);
       await process.shouldExit(64);
     });
 
-    test("updates selection", () async {
-      var process = await runFormatter(["--preserve", "6:10", "-m"]);
+    test('updates selection', () async {
+      var process = await runFormatter(['--preserve', '6:10', '-m']);
       process.stdin.writeln(unformattedSource);
       await process.stdin.close();
 
       var json = jsonEncode({
-        "path": "<stdin>",
-        "source": formattedSource,
-        "selection": {"offset": 5, "length": 9}
+        'path': '<stdin>',
+        'source': formattedSource,
+        'selection': {'offset': 5, 'length': 9}
       });
 
       expect(await process.stdout.next, json);
@@ -181,123 +181,123 @@
     });
   });
 
-  group("--indent", () {
-    test("sets the leading indentation of the output", () async {
-      var process = await runFormatter(["--indent", "3"]);
+  group('--indent', () {
+    test('sets the leading indentation of the output', () async {
+      var process = await runFormatter(['--indent', '3']);
       process.stdin.writeln("main() {'''");
       process.stdin.writeln("a flush left multi-line string''';}");
       await process.stdin.close();
 
-      expect(await process.stdout.next, "   main() {");
+      expect(await process.stdout.next, '   main() {');
       expect(await process.stdout.next, "     '''");
       expect(await process.stdout.next, "a flush left multi-line string''';");
-      expect(await process.stdout.next, "   }");
+      expect(await process.stdout.next, '   }');
       await process.shouldExit(0);
     });
 
-    test("errors if the indent is not a non-negative number", () async {
-      var process = await runFormatter(["--indent", "notanum"]);
+    test('errors if the indent is not a non-negative number', () async {
+      var process = await runFormatter(['--indent', 'notanum']);
       await process.shouldExit(64);
 
-      process = await runFormatter(["--preserve", "-4"]);
+      process = await runFormatter(['--preserve', '-4']);
       await process.shouldExit(64);
     });
   });
 
-  group("--set-exit-if-changed", () {
-    test("gives exit code 0 if there are no changes", () async {
-      await d.dir("code", [d.file("a.dart", formattedSource)]).create();
+  group('--set-exit-if-changed', () {
+    test('gives exit code 0 if there are no changes', () async {
+      await d.dir('code', [d.file('a.dart', formattedSource)]).create();
 
-      var process = await runFormatterOnDir(["--set-exit-if-changed"]);
+      var process = await runFormatterOnDir(['--set-exit-if-changed']);
       await process.shouldExit(0);
     });
 
-    test("gives exit code 1 if there are changes", () async {
-      await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
+    test('gives exit code 1 if there are changes', () async {
+      await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
 
-      var process = await runFormatterOnDir(["--set-exit-if-changed"]);
+      var process = await runFormatterOnDir(['--set-exit-if-changed']);
       await process.shouldExit(1);
     });
 
-    test("gives exit code 1 if there are changes even in dry run", () async {
-      await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
+    test('gives exit code 1 if there are changes even in dry run', () async {
+      await d.dir('code', [d.file('a.dart', unformattedSource)]).create();
 
       var process =
-          await runFormatterOnDir(["--set-exit-if-changed", "--dry-run"]);
+          await runFormatterOnDir(['--set-exit-if-changed', '--dry-run']);
       await process.shouldExit(1);
     });
   });
 
-  group("fix", () {
-    test("--fix applies all fixes", () async {
-      var process = await runFormatter(["--fix"]);
-      process.stdin.writeln("foo({a:1}) {");
-      process.stdin.writeln("  new Bar(const Baz(const []));}");
+  group('fix', () {
+    test('--fix applies all fixes', () async {
+      var process = await runFormatter(['--fix']);
+      process.stdin.writeln('foo({a:1}) {');
+      process.stdin.writeln('  new Bar(const Baz(const []));}');
       await process.stdin.close();
 
-      expect(await process.stdout.next, "foo({a = 1}) {");
-      expect(await process.stdout.next, "  Bar(const Baz([]));");
-      expect(await process.stdout.next, "}");
+      expect(await process.stdout.next, 'foo({a = 1}) {');
+      expect(await process.stdout.next, '  Bar(const Baz([]));');
+      expect(await process.stdout.next, '}');
       await process.shouldExit(0);
     });
 
-    test("--fix-named-default-separator", () async {
-      var process = await runFormatter(["--fix-named-default-separator"]);
-      process.stdin.writeln("foo({a:1}) {");
-      process.stdin.writeln("  new Bar();}");
+    test('--fix-named-default-separator', () async {
+      var process = await runFormatter(['--fix-named-default-separator']);
+      process.stdin.writeln('foo({a:1}) {');
+      process.stdin.writeln('  new Bar();}');
       await process.stdin.close();
 
-      expect(await process.stdout.next, "foo({a = 1}) {");
-      expect(await process.stdout.next, "  new Bar();");
-      expect(await process.stdout.next, "}");
+      expect(await process.stdout.next, 'foo({a = 1}) {');
+      expect(await process.stdout.next, '  new Bar();');
+      expect(await process.stdout.next, '}');
       await process.shouldExit(0);
     });
 
-    test("--fix-optional-const", () async {
-      var process = await runFormatter(["--fix-optional-const"]);
-      process.stdin.writeln("foo({a:1}) {");
-      process.stdin.writeln("  const Bar(const Baz());}");
+    test('--fix-optional-const', () async {
+      var process = await runFormatter(['--fix-optional-const']);
+      process.stdin.writeln('foo({a:1}) {');
+      process.stdin.writeln('  const Bar(const Baz());}');
       await process.stdin.close();
 
-      expect(await process.stdout.next, "foo({a: 1}) {");
-      expect(await process.stdout.next, "  const Bar(Baz());");
-      expect(await process.stdout.next, "}");
+      expect(await process.stdout.next, 'foo({a: 1}) {');
+      expect(await process.stdout.next, '  const Bar(Baz());');
+      expect(await process.stdout.next, '}');
       await process.shouldExit(0);
     });
 
-    test("--fix-optional-new", () async {
-      var process = await runFormatter(["--fix-optional-new"]);
-      process.stdin.writeln("foo({a:1}) {");
-      process.stdin.writeln("  new Bar();}");
+    test('--fix-optional-new', () async {
+      var process = await runFormatter(['--fix-optional-new']);
+      process.stdin.writeln('foo({a:1}) {');
+      process.stdin.writeln('  new Bar();}');
       await process.stdin.close();
 
-      expect(await process.stdout.next, "foo({a: 1}) {");
-      expect(await process.stdout.next, "  Bar();");
-      expect(await process.stdout.next, "}");
+      expect(await process.stdout.next, 'foo({a: 1}) {');
+      expect(await process.stdout.next, '  Bar();');
+      expect(await process.stdout.next, '}');
       await process.shouldExit(0);
     });
 
-    test("errors with --fix and specific fix flag", () async {
+    test('errors with --fix and specific fix flag', () async {
       var process =
-          await runFormatter(["--fix", "--fix-named-default-separator"]);
+          await runFormatter(['--fix', '--fix-named-default-separator']);
       await process.shouldExit(64);
     });
   });
 
-  group("with no paths", () {
-    test("errors on --overwrite", () async {
-      var process = await runFormatter(["--overwrite"]);
+  group('with no paths', () {
+    test('errors on --overwrite', () async {
+      var process = await runFormatter(['--overwrite']);
       await process.shouldExit(64);
     });
 
-    test("exits with 65 on parse error", () async {
+    test('exits with 65 on parse error', () async {
       var process = await runFormatter();
-      process.stdin.writeln("herp derp i are a dart");
+      process.stdin.writeln('herp derp i are a dart');
       await process.stdin.close();
       await process.shouldExit(65);
     });
 
-    test("reads from stdin", () async {
+    test('reads from stdin', () async {
       var process = await runFormatter();
       process.stdin.writeln(unformattedSource);
       await process.stdin.close();
@@ -307,15 +307,15 @@
       await process.shouldExit(0);
     });
 
-    test("allows specifying stdin path name", () async {
-      var process = await runFormatter(["--stdin-name=some/path.dart"]);
-      process.stdin.writeln("herp");
+    test('allows specifying stdin path name', () async {
+      var process = await runFormatter(['--stdin-name=some/path.dart']);
+      process.stdin.writeln('herp');
       await process.stdin.close();
 
       expect(await process.stderr.next,
-          "Could not format because the source could not be parsed:");
-      expect(await process.stderr.next, "");
-      expect(await process.stderr.next, contains("some/path.dart"));
+          'Could not format because the source could not be parsed:');
+      expect(await process.stderr.next, '');
+      expect(await process.stderr.next, contains('some/path.dart'));
       await process.stderr.cancel();
       await process.shouldExit(65);
     });
diff --git a/test/fix_test.dart b/test/fix_test.dart
index 787f9c0..028930d 100644
--- a/test/fix_test.dart
+++ b/test/fix_test.dart
@@ -2,7 +2,7 @@
 // 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")
+@TestOn('vm')
 library dart_style.test.fix_test;
 
 import 'package:test/test.dart';
@@ -13,11 +13,11 @@
 
 void main() {
   testFile(
-      "fixes/named_default_separator.unit", [StyleFix.namedDefaultSeparator]);
-  testFile("fixes/doc_comments.stmt", [StyleFix.docComments]);
-  testFile("fixes/function_typedefs.unit", [StyleFix.functionTypedefs]);
-  testFile("fixes/optional_const.unit", [StyleFix.optionalConst]);
-  testFile("fixes/optional_new.stmt", [StyleFix.optionalNew]);
-  testFile("fixes/single_cascade_statements.stmt",
+      'fixes/named_default_separator.unit', [StyleFix.namedDefaultSeparator]);
+  testFile('fixes/doc_comments.stmt', [StyleFix.docComments]);
+  testFile('fixes/function_typedefs.unit', [StyleFix.functionTypedefs]);
+  testFile('fixes/optional_const.unit', [StyleFix.optionalConst]);
+  testFile('fixes/optional_new.stmt', [StyleFix.optionalNew]);
+  testFile('fixes/single_cascade_statements.stmt',
       [StyleFix.singleCascadeStatements]);
 }
diff --git a/test/formatter_test.dart b/test/formatter_test.dart
index 18629d1..e7f10f8 100644
--- a/test/formatter_test.dart
+++ b/test/formatter_test.dart
@@ -2,7 +2,7 @@
 // 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")
+@TestOn('vm')
 library dart_style.test.formatter_test;
 
 import 'package:test/test.dart';
@@ -12,69 +12,69 @@
 import 'utils.dart';
 
 void main() {
-  testDirectory("comments");
-  testDirectory("regression");
-  testDirectory("selections");
-  testDirectory("splitting");
-  testDirectory("whitespace");
+  testDirectory('comments');
+  testDirectory('regression');
+  testDirectory('selections');
+  testDirectory('splitting');
+  testDirectory('whitespace');
 
-  test("throws a FormatterException on failed parse", () {
+  test('throws a FormatterException on failed parse', () {
     var formatter = DartFormatter();
     expect(() => formatter.format('wat?!'),
         throwsA(TypeMatcher<FormatterException>()));
   });
 
-  test("FormatterException.message() does not throw", () {
+  test('FormatterException.message() does not throw', () {
     // This is a regression test for #358 where an error whose position is
     // past the end of the source caused FormatterException to throw.
     try {
-      DartFormatter().format("library");
+      DartFormatter().format('library');
     } on FormatterException catch (err) {
       var message = err.message();
-      expect(message, contains("Could not format"));
+      expect(message, contains('Could not format'));
     }
   });
 
-  test("FormatterException describes parse errors", () {
+  test('FormatterException describes parse errors', () {
     try {
-      DartFormatter().format("""
+      DartFormatter().format('''
 
       var a = some error;
 
       var b = another one;
-      """, uri: "my_file.dart");
+      ''', uri: 'my_file.dart');
 
-      fail("Should throw.");
+      fail('Should throw.');
     } on FormatterException catch (err) {
       var message = err.message();
-      expect(message, contains("my_file.dart"));
-      expect(message, contains("line 2"));
-      expect(message, contains("line 4"));
+      expect(message, contains('my_file.dart'));
+      expect(message, contains('line 2'));
+      expect(message, contains('line 4'));
     }
   });
 
-  test("adds newline to unit", () {
-    expect(DartFormatter().format("var x = 1;"), equals("var x = 1;\n"));
+  test('adds newline to unit', () {
+    expect(DartFormatter().format('var x = 1;'), equals('var x = 1;\n'));
   });
 
-  test("adds newline to unit after trailing comment", () {
-    expect(DartFormatter().format("library foo; //zamm"),
-        equals("library foo; //zamm\n"));
+  test('adds newline to unit after trailing comment', () {
+    expect(DartFormatter().format('library foo; //zamm'),
+        equals('library foo; //zamm\n'));
   });
 
-  test("removes extra newlines", () {
-    expect(DartFormatter().format("var x = 1;\n\n\n"), equals("var x = 1;\n"));
+  test('removes extra newlines', () {
+    expect(DartFormatter().format('var x = 1;\n\n\n'), equals('var x = 1;\n'));
   });
 
-  test("does not add newline to statement", () {
-    expect(DartFormatter().formatStatement("var x = 1;"), equals("var x = 1;"));
+  test('does not add newline to statement', () {
+    expect(DartFormatter().formatStatement('var x = 1;'), equals('var x = 1;'));
   });
 
-  test("fails if anything is after the statement", () {
+  test('fails if anything is after the statement', () {
     try {
-      DartFormatter().formatStatement("var x = 1;;");
+      DartFormatter().formatStatement('var x = 1;;');
 
-      fail("Should throw.");
+      fail('Should throw.');
     } on FormatterException catch (ex) {
       expect(ex.errors.length, equals(1));
       expect(ex.errors.first.offset, equals(10));
@@ -90,34 +90,34 @@
             '   }'));
   });
 
-  group("line endings", () {
-    test("uses given line ending", () {
+  group('line endings', () {
+    test('uses given line ending', () {
       // Use zero width no-break space character as the line ending. We have
       // to use a whitespace character for the line ending as the formatter
       // will throw an error if it accidentally makes non-whitespace changes
       // as will occur
-      var lineEnding = "\t";
-      expect(DartFormatter(lineEnding: lineEnding).format("var i = 1;"),
-          equals("var i = 1;\t"));
+      var lineEnding = '\t';
+      expect(DartFormatter(lineEnding: lineEnding).format('var i = 1;'),
+          equals('var i = 1;\t'));
     });
 
     test('infers \\r\\n if the first newline uses that', () {
-      expect(DartFormatter().format("var\r\ni\n=\n1;\n"),
-          equals("var i = 1;\r\n"));
+      expect(DartFormatter().format('var\r\ni\n=\n1;\n'),
+          equals('var i = 1;\r\n'));
     });
 
     test('infers \\n if the first newline uses that', () {
-      expect(DartFormatter().format("var\ni\r\n=\r\n1;\r\n"),
-          equals("var i = 1;\n"));
+      expect(DartFormatter().format('var\ni\r\n=\r\n1;\r\n'),
+          equals('var i = 1;\n'));
     });
 
     test('defaults to \\n if there are no newlines', () {
-      expect(DartFormatter().format("var i =1;"), equals("var i = 1;\n"));
+      expect(DartFormatter().format('var i =1;'), equals('var i = 1;\n'));
     });
 
     test('handles Windows line endings in multiline strings', () {
       expect(
-          DartFormatter(lineEnding: "\r\n").formatStatement('  """first\r\n'
+          DartFormatter(lineEnding: '\r\n').formatStatement('  """first\r\n'
               'second\r\n'
               'third"""  ;'),
           equals('"""first\r\n'
@@ -130,7 +130,7 @@
     // Use an invalid line ending character to ensure the formatter will
     // attempt to make non-whitespace changes.
     var formatter = DartFormatter(lineEnding: '%');
-    expect(() => formatter.format("var i = 1;"),
+    expect(() => formatter.format('var i = 1;'),
         throwsA(TypeMatcher<UnexpectedOutputException>()));
   });
 }
diff --git a/test/io_test.dart b/test/io_test.dart
index 95daf8a..da5839c 100644
--- a/test/io_test.dart
+++ b/test/io_test.dart
@@ -136,10 +136,10 @@
   if (!Platform.isWindows) {
     // TODO(rnystrom): Figure out Windows equivalent of chmod and get this
     // test running on Windows too.
-    test("reports error if file can not be written", () async {
+    test('reports error if file can not be written', () async {
       await d.file('a.dart', unformattedSource).create();
 
-      Process.runSync("chmod", ["-w", p.join(d.sandbox, 'a.dart')]);
+      Process.runSync('chmod', ['-w', p.join(d.sandbox, 'a.dart')]);
 
       var file = File(p.join(d.sandbox, 'a.dart'));
       processFile(overwriteOptions, file);
diff --git a/test/source_code_test.dart b/test/source_code_test.dart
index 6615768..3895e10 100644
--- a/test/source_code_test.dart
+++ b/test/source_code_test.dart
@@ -9,74 +9,74 @@
 import 'package:dart_style/dart_style.dart';
 
 void main() {
-  var selection = SourceCode("123456;", selectionStart: 3, selectionLength: 2);
-  var noSelection = SourceCode("123456;");
+  var selection = SourceCode('123456;', selectionStart: 3, selectionLength: 2);
+  var noSelection = SourceCode('123456;');
 
   group('constructor', () {
     test('throws on negative start', () {
       expect(() {
-        SourceCode("12345;", selectionStart: -1, selectionLength: 0);
+        SourceCode('12345;', selectionStart: -1, selectionLength: 0);
       }, throwsArgumentError);
     });
 
     test('throws on out of bounds start', () {
       expect(() {
-        SourceCode("12345;", selectionStart: 7, selectionLength: 0);
+        SourceCode('12345;', selectionStart: 7, selectionLength: 0);
       }, throwsArgumentError);
     });
 
     test('throws on negative length', () {
       expect(() {
-        SourceCode("12345;", selectionStart: 1, selectionLength: -1);
+        SourceCode('12345;', selectionStart: 1, selectionLength: -1);
       }, throwsArgumentError);
     });
 
     test('throws on out of bounds length', () {
       expect(() {
-        SourceCode("12345;", selectionStart: 2, selectionLength: 5);
+        SourceCode('12345;', selectionStart: 2, selectionLength: 5);
       }, throwsArgumentError);
     });
 
     test('throws is start is null and length is not', () {
       expect(() {
-        SourceCode("12345;", selectionStart: 0);
+        SourceCode('12345;', selectionStart: 0);
       }, throwsArgumentError);
     });
 
     test('throws is length is null and start is not', () {
       expect(() {
-        SourceCode("12345;", selectionLength: 1);
+        SourceCode('12345;', selectionLength: 1);
       }, throwsArgumentError);
     });
   });
 
   group('textBeforeSelection', () {
     test('gets substring before selection', () {
-      expect(selection.textBeforeSelection, equals("123"));
+      expect(selection.textBeforeSelection, equals('123'));
     });
 
     test('gets entire string if no selection', () {
-      expect(noSelection.textBeforeSelection, equals("123456;"));
+      expect(noSelection.textBeforeSelection, equals('123456;'));
     });
   });
 
   group('selectedText', () {
     test('gets selection substring', () {
-      expect(selection.selectedText, equals("45"));
+      expect(selection.selectedText, equals('45'));
     });
 
     test('gets empty string if no selection', () {
-      expect(noSelection.selectedText, equals(""));
+      expect(noSelection.selectedText, equals(''));
     });
   });
 
   group('textAfterSelection', () {
     test('gets substring after selection', () {
-      expect(selection.textAfterSelection, equals("6;"));
+      expect(selection.textAfterSelection, equals('6;'));
     });
 
     test('gets empty string if no selection', () {
-      expect(noSelection.textAfterSelection, equals(""));
+      expect(noSelection.textAfterSelection, equals(''));
     });
   });
 }
diff --git a/test/string_compare_test.dart b/test/string_compare_test.dart
index 7b4e950..226b421 100644
--- a/test/string_compare_test.dart
+++ b/test/string_compare_test.dart
@@ -2,7 +2,7 @@
 // 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")
+@TestOn('vm')
 library dart_style.test.string_compare_test;
 
 import 'package:test/test.dart';
@@ -10,21 +10,21 @@
 import 'package:dart_style/src/string_compare.dart';
 
 void main() {
-  test("whitespace at end of string", () {
+  test('whitespace at end of string', () {
     expect(equalIgnoringWhitespace('foo bar\n', 'foo bar'), isTrue);
     expect(equalIgnoringWhitespace('foo bar', 'foo bar\n'), isTrue);
     expect(equalIgnoringWhitespace('foo bar \n', 'foo bar'), isTrue);
     expect(equalIgnoringWhitespace('foo bar', 'foo bar \n'), isTrue);
   });
 
-  test("whitespace at start of string", () {
+  test('whitespace at start of string', () {
     expect(equalIgnoringWhitespace('\nfoo bar', 'foo bar'), isTrue);
     expect(equalIgnoringWhitespace('\n foo bar', 'foo bar'), isTrue);
     expect(equalIgnoringWhitespace('foo bar', '\nfoo bar'), isTrue);
     expect(equalIgnoringWhitespace('foo bar', '\n foo bar'), isTrue);
   });
 
-  test("whitespace in the middle of string", () {
+  test('whitespace in the middle of string', () {
     expect(equalIgnoringWhitespace('foobar', 'foo bar'), isTrue);
     expect(equalIgnoringWhitespace('foo bar', 'foobar'), isTrue);
     expect(equalIgnoringWhitespace('foo\tbar', 'foobar'), isTrue);
@@ -33,12 +33,12 @@
     expect(equalIgnoringWhitespace('foobar', 'foo\nbar'), isTrue);
   });
 
-  test("wdentical strings", () {
+  test('wdentical strings', () {
     expect(equalIgnoringWhitespace('foo bar', 'foo bar'), isTrue);
     expect(equalIgnoringWhitespace('', ''), isTrue);
   });
 
-  test("test unicode whitespace characters", () {
+  test('test unicode whitespace characters', () {
     // Dart sources only allow ascii whitespace code points so we
     // should not consider the following strings equal.
     var whitespaceRunes = [
@@ -62,7 +62,7 @@
     }
   });
 
-  test("different strings", () {
+  test('different strings', () {
     expect(equalIgnoringWhitespace('foo bar', 'Foo bar'), isFalse);
     expect(equalIgnoringWhitespace('foo bar', 'foo bars'), isFalse);
     expect(equalIgnoringWhitespace('foo bars', 'foo bar'), isFalse);
diff --git a/test/utils.dart b/test/utils.dart
index 9405130..946a965 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -18,12 +18,12 @@
 const unformattedSource = 'void  main()  =>  print("hello") ;';
 const formattedSource = 'void main() => print("hello");\n';
 
-final _indentPattern = RegExp(r"\(indent (\d+)\)");
-final _fixPattern = RegExp(r"\(fix ([a-x-]+)\)");
+final _indentPattern = RegExp(r'\(indent (\d+)\)');
+final _fixPattern = RegExp(r'\(fix ([a-x-]+)\)');
 
 /// Runs the command line formatter, passing it [args].
 Future<TestProcess> runFormatter([List<String> args]) {
-  if (args == null) args = [];
+  args ??= [];
 
   // Locate the "test" directory. Use mirrors so that this works with the test
   // package, which loads this suite into an isolate.
@@ -32,13 +32,13 @@
       .uri
       .toFilePath());
 
-  var formatterPath = p.normalize(p.join(testDir, "../bin/format.dart"));
+  var formatterPath = p.normalize(p.join(testDir, '../bin/format.dart'));
 
   args.insert(0, formatterPath);
 
   // Use the same package root, if there is one.
   if (Platform.packageConfig != null && Platform.packageConfig.isNotEmpty) {
-    args.insert(0, "--packages=${Platform.packageConfig}");
+    args.insert(0, '--packages=${Platform.packageConfig}');
   }
 
   return TestProcess.start(Platform.executable, args);
@@ -47,8 +47,8 @@
 /// Runs the command line formatter, passing it the test directory followed by
 /// [args].
 Future<TestProcess> runFormatterOnDir([List<String> args]) {
-  if (args == null) args = [];
-  return runFormatter([d.sandbox]..addAll(args));
+  args ??= [];
+  return runFormatter([d.sandbox, ...args]);
 }
 
 /// Run tests defined in "*.unit" and "*.stmt" files inside directory [name].
@@ -65,7 +65,7 @@
   entries.sort((a, b) => a.path.compareTo(b.path));
 
   for (var entry in entries) {
-    if (!entry.path.endsWith(".stmt") && !entry.path.endsWith(".unit")) {
+    if (!entry.path.endsWith('.stmt') && !entry.path.endsWith('.unit')) {
       continue;
     }
 
@@ -88,61 +88,61 @@
   var fixes = <StyleFix>[];
   if (baseFixes != null) fixes.addAll(baseFixes);
 
-  group("$name ${p.basename(path)}", () {
+  group('$name ${p.basename(path)}', () {
     // Explicitly create a File, in case the entry is a Link.
     var lines = File(path).readAsLinesSync();
 
     // The first line may have a "|" to indicate the page width.
     var pageWidth;
-    if (lines[0].endsWith("|")) {
-      pageWidth = lines[0].indexOf("|");
+    if (lines[0].endsWith('|')) {
+      pageWidth = lines[0].indexOf('|');
       lines = lines.skip(1).toList();
     }
 
     var i = 0;
     while (i < lines.length) {
-      var description = lines[i++].replaceAll(">>>", "");
+      var description = lines[i++].replaceAll('>>>', '');
 
       // Let the test specify a leading indentation. This is handy for
       // regression tests which often come from a chunk of nested code.
       var leadingIndent = 0;
       description = description.replaceAllMapped(_indentPattern, (match) {
         leadingIndent = int.parse(match[1]);
-        return "";
+        return '';
       });
 
       // Let the test specify fixes to apply.
       description = description.replaceAllMapped(_fixPattern, (match) {
         fixes.add(StyleFix.all.firstWhere((fix) => fix.name == match[1]));
-        return "";
+        return '';
       });
 
       description = description.trim();
 
-      if (description == "") {
-        description = "line ${i + 1}";
+      if (description == '') {
+        description = 'line ${i + 1}';
       } else {
-        description = "line ${i + 1}: $description";
+        description = 'line ${i + 1}: $description';
       }
 
-      var input = "";
-      while (!lines[i].startsWith("<<<")) {
-        input += lines[i++] + "\n";
+      var input = '';
+      while (!lines[i].startsWith('<<<')) {
+        input += lines[i++] + '\n';
       }
 
-      var expectedOutput = "";
-      while (++i < lines.length && !lines[i].startsWith(">>>")) {
-        expectedOutput += lines[i] + "\n";
+      var expectedOutput = '';
+      while (++i < lines.length && !lines[i].startsWith('>>>')) {
+        expectedOutput += lines[i] + '\n';
       }
 
       // TODO(rnystrom): Stop skipping these tests when possible.
-      if (description.contains("(skip:")) {
-        print("skipping $description");
+      if (description.contains('(skip:')) {
+        print('skipping $description');
         continue;
       }
 
       test(description, () {
-        var isCompilationUnit = p.extension(path) == ".unit";
+        var isCompilationUnit = p.extension(path) == '.unit';
 
         var inputCode =
             _extractSelection(input, isCompilationUnit: isCompilationUnit);
@@ -159,13 +159,13 @@
         // Statements from the formatter (correctly) don't have that, so add
         // one to line up with the expected result.
         var actualText = actual.text;
-        if (!isCompilationUnit) actualText += "\n";
+        if (!isCompilationUnit) actualText += '\n';
 
         // Fail with an explicit message because it's easier to read than
         // the matcher output.
         if (actualText != expected.text) {
-          fail("Formatting did not match expectation. Expected:\n"
-              "${expected.text}\nActual:\n$actualText");
+          fail('Formatting did not match expectation. Expected:\n'
+              '${expected.text}\nActual:\n$actualText');
         }
 
         expect(actual.selectionStart, equals(expected.selectionStart));
@@ -179,11 +179,11 @@
 /// a [SourceCode] with the text (with the selection markers removed) and the
 /// correct selection range.
 SourceCode _extractSelection(String source, {bool isCompilationUnit = false}) {
-  var start = source.indexOf("‹");
-  source = source.replaceAll("‹", "");
+  var start = source.indexOf('‹');
+  source = source.replaceAll('‹', '');
 
-  var end = source.indexOf("›");
-  source = source.replaceAll("›", "");
+  var end = source.indexOf('›');
+  source = source.replaceAll('›', '');
 
   return SourceCode(source,
       isCompilationUnit: isCompilationUnit,
diff --git a/tool/grind.dart b/tool/grind.dart
index 93d8db3..b6b6ee4 100644
--- a/tool/grind.dart
+++ b/tool/grind.dart
@@ -11,29 +11,29 @@
 import 'package:yaml/yaml.dart' as yaml;
 
 /// Matches the version line in dart_style's pubspec.
-final _versionPattern = RegExp(r"^version: .*$", multiLine: true);
+final _versionPattern = RegExp(r'^version: .*$', multiLine: true);
 
-main(List<String> args) => grind(args);
+void main(List<String> args) => grind(args);
 
 @DefaultTask()
 @Task()
-validate() async {
+Future<void> validate() async {
   // Test it.
   await TestRunner().testAsync();
 
   // Make sure it's warning clean.
-  Analyzer.analyze("bin/format.dart", fatalWarnings: true);
+  Analyzer.analyze('bin/format.dart', fatalWarnings: true);
 
   // Format it.
-  Dart.run("bin/format.dart", arguments: ["-w", "."]);
+  Dart.run('bin/format.dart', arguments: ['-w', '.']);
 }
 
 @Task('Publish to npm')
-npm() {
+void npm() {
   var out = 'dist';
 
-  var pubspec = yaml.loadYaml(getFile("pubspec.yaml").readAsStringSync());
-  var homepage = pubspec["homepage"];
+  var pubspec = yaml.loadYaml(getFile('pubspec.yaml').readAsStringSync());
+  var homepage = pubspec['homepage'];
   var fileName = 'index.js';
 
   // Generate modified dart2js output suitable to run on node.
@@ -48,17 +48,17 @@
 
   File('$out/package.json')
       .writeAsStringSync(const JsonEncoder.withIndent('  ').convert({
-    "name": "dart-style",
-    "version": pubspec["version"],
-    "description": pubspec["description"],
-    "main": fileName,
-    "typings": "dart-style.d.ts",
-    "scripts": {"test": "echo \"Error: no test specified\" && exit 1"},
-    "repository": {"type": "git", "url": "git+$homepage"},
-    "author": pubspec["author"],
-    "license": "BSD",
-    "bugs": {"url": "$homepage/issues"},
-    "homepage": homepage
+    'name': 'dart-style',
+    'version': pubspec['version'],
+    'description': pubspec['description'],
+    'main': fileName,
+    'typings': 'dart-style.d.ts',
+    'scripts': {'test': 'echo "Error: no test specified" && exit 1'},
+    'repository': {'type': 'git', 'url': 'git+$homepage'},
+    'author': pubspec['author'],
+    'license': 'BSD',
+    'bugs': {'url': '$homepage/issues'},
+    'homepage': homepage
   }));
   run('npm', arguments: ['publish', out]);
 }
@@ -95,33 +95,33 @@
 ///         pub lish
 @Task()
 @Depends(validate)
-bump() async {
+Future<void> bump() async {
   // Read the version from the pubspec.
-  var pubspecFile = getFile("pubspec.yaml");
+  var pubspecFile = getFile('pubspec.yaml');
   var pubspec = pubspecFile.readAsStringSync();
-  var version = Version.parse(yaml.loadYaml(pubspec)["version"]);
+  var version = Version.parse(yaml.loadYaml(pubspec)['version']);
 
   // Require a "-dev" version since we don't otherwise know what to bump it to.
-  if (!version.isPreRelease) throw "Cannot publish non-dev version $version.";
+  if (!version.isPreRelease) throw 'Cannot publish non-dev version $version.';
 
   // Don't allow versions like "1.2.3-dev+4" because it's not clear if the
   // user intended the "+4" to be discarded or not.
-  if (version.build.isNotEmpty) throw "Cannot publish build version $version.";
+  if (version.build.isNotEmpty) throw 'Cannot publish build version $version.';
 
   var bumped = Version(version.major, version.minor, version.patch);
 
   // Update the version in the pubspec.
-  pubspec = pubspec.replaceAll(_versionPattern, "version: $bumped");
+  pubspec = pubspec.replaceAll(_versionPattern, 'version: $bumped');
   pubspecFile.writeAsStringSync(pubspec);
 
   // Update the version constant in bin/format.dart.
-  var binFormatFile = getFile("bin/format.dart");
+  var binFormatFile = getFile('bin/format.dart');
   var binFormat = binFormatFile.readAsStringSync().replaceAll(
       RegExp(r'const version = "[^"]+";'), 'const version = "$bumped";');
   binFormatFile.writeAsStringSync(binFormat);
 
   // Update the version in the CHANGELOG.
-  var changelogFile = getFile("CHANGELOG.md");
+  var changelogFile = getFile('CHANGELOG.md');
   var changelog = changelogFile
       .readAsStringSync()
       .replaceAll(version.toString(), bumped.toString());
diff --git a/tool/node_format_service.dart b/tool/node_format_service.dart
index cfc5cf1..a877003 100644
--- a/tool/node_format_service.dart
+++ b/tool/node_format_service.dart
@@ -2,6 +2,9 @@
 // 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.
 
+@JS()
+library node_format_service;
+
 import 'dart:math' as math;
 
 import 'package:js/js.dart';
@@ -45,7 +48,7 @@
     }
 
     // If we get here, it couldn't be parsed at all.
-    return FormatResult(code: source, error: "$exception");
+    return FormatResult(code: source, error: '$exception');
   });
 }