Move to a new architecture for handling command line options and output.

There are no behavioral changes here. It just rearranges the code and
replaces the old OutputReporter abstraction with separate Output, Show,
and Summary classes. The new CLI will use these to let users switch
those options independently of each other.
diff --git a/bin/format.dart b/bin/format.dart
index 834dddd..eff4870 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -7,6 +7,9 @@
 import 'package:args/args.dart';
 import 'package:dart_style/src/cli/formatter_options.dart';
 import 'package:dart_style/src/cli/options.dart';
+import 'package:dart_style/src/cli/output.dart';
+import 'package:dart_style/src/cli/show.dart';
+import 'package:dart_style/src/cli/summary.dart';
 import 'package:dart_style/src/io.dart';
 import 'package:dart_style/src/style_fix.dart';
 
@@ -32,19 +35,11 @@
     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.');
-  }
-
   try {
     selection = parseSelection(argResults, 'preserve');
-  } on FormatException catch (_) {
-    usageError(
-        parser,
-        '--preserve must be a colon-separated pair of integers, was '
-        '"${argResults['preserve']}".');
+  } on FormatException catch (exception) {
+    usageError(parser, exception.message);
   }
 
   if (argResults['dry-run'] && argResults['overwrite']) {
@@ -58,12 +53,16 @@
     usageError(parser, 'Cannot use --$chosen and --$other at the same time.');
   }
 
-  var reporter = OutputReporter.print;
+  var show = Show.overwrite;
+  var summary = Summary.none;
+  var output = Output.show;
+  var setExitIfChanged = false;
   if (argResults['dry-run']) {
     checkForReporterCollision('dry-run', 'overwrite');
     checkForReporterCollision('dry-run', 'machine');
 
-    reporter = OutputReporter.dryRun;
+    show = Show.dryRun;
+    output = Output.none;
   } else if (argResults['overwrite']) {
     checkForReporterCollision('overwrite', 'machine');
 
@@ -72,18 +71,14 @@
           'Cannot use --overwrite without providing any paths to format.');
     }
 
-    reporter = OutputReporter.overwrite;
+    output = Output.write;
   } else if (argResults['machine']) {
-    reporter = OutputReporter.printJson;
+    output = Output.json;
   }
 
-  if (argResults['profile']) {
-    reporter = ProfileReporter(reporter);
-  }
+  if (argResults['profile']) summary = Summary.profile();
 
-  if (argResults['set-exit-if-changed']) {
-    reporter = SetExitReporter(reporter);
-  }
+  setExitIfChanged = argResults['set-exit-if-changed'];
 
   int pageWidth;
   try {
@@ -124,11 +119,15 @@
     usageError(parser, 'Cannot pass --stdin-name when not reading from stdin.');
   }
 
-  var options = FormatterOptions(reporter,
+  var options = FormatterOptions(
       indent: indent,
       pageWidth: pageWidth,
       followLinks: followLinks,
-      fixes: fixes);
+      fixes: fixes,
+      show: show,
+      output: output,
+      summary: summary,
+      setExitIfChanged: setExitIfChanged);
 
   if (argResults.rest.isEmpty) {
     formatStdin(options, selection, argResults['stdin-name'] as String);
@@ -136,9 +135,7 @@
     formatPaths(options, argResults.rest);
   }
 
-  if (argResults['profile']) {
-    (reporter as ProfileReporter).showProfile();
-  }
+  options.summary.show();
 }
 
 /// Prints [error] and usage help then exits with exit code 64.
diff --git a/lib/src/cli/formatter_options.dart b/lib/src/cli/formatter_options.dart
index 6250e88..9ee0ca5 100644
--- a/lib/src/cli/formatter_options.dart
+++ b/lib/src/cli/formatter_options.dart
@@ -4,20 +4,19 @@
 
 library dart_style.src.formatter_options;
 
-import 'dart:convert';
 import 'dart:io';
 
 import '../source_code.dart';
 import '../style_fix.dart';
+import 'output.dart';
+import 'show.dart';
+import 'summary.dart';
 
 // Note: The following line of code is modified by tool/grind.dart.
 const dartStyleVersion = '1.3.3';
 
 /// Global options that affect how the formatter produces and uses its outputs.
 class FormatterOptions {
-  /// The [OutputReporter] used to show the formatting results.
-  final OutputReporter reporter;
-
   /// The number of spaces of indentation to prefix the output with.
   final int indent;
 
@@ -31,223 +30,69 @@
   /// The style fixes to apply while formatting.
   final Iterable<StyleFix> fixes;
 
-  FormatterOptions(this.reporter,
+  /// Which affected files should be shown.
+  final Show show;
+
+  /// Where formatted code should be output.
+  final Output output;
+
+  final Summary summary;
+
+  /// Sets the exit code to 1 if any changes are made.
+  final bool setExitIfChanged;
+
+  FormatterOptions(
       {this.indent = 0,
       this.pageWidth = 80,
       this.followLinks = false,
-      this.fixes});
-}
-
-/// How the formatter reports the results it produces.
-abstract class OutputReporter {
-  /// Prints only the names of files whose contents are different from their
-  /// formatted version.
-  static final OutputReporter dryRun = _DryRunReporter();
-
-  /// Prints the formatted results of each file to stdout.
-  static final OutputReporter print = _PrintReporter();
-
-  /// Prints the formatted result and selection info of each file to stdout as
-  /// a JSON map.
-  static final OutputReporter printJson = _PrintJsonReporter();
-
-  /// Overwrites each file with its formatted result.
-  static final OutputReporter overwrite = _OverwriteReporter();
-
-  /// Describe the directory whose contents are about to be processed.
-  void showDirectory(String path) {}
-
-  /// Describe the symlink at [path] that wasn't followed.
-  void showSkippedLink(String path) {}
-
-  /// Describe the hidden [path] that wasn't processed.
-  void showHiddenPath(String path) {}
+      this.fixes,
+      this.show = Show.changed,
+      this.output = Output.write,
+      this.summary = Summary.none,
+      this.setExitIfChanged = false});
 
   /// Called when [file] is about to be formatted.
-  void beforeFile(File file, String label) {}
+  void beforeFile(File file, String label) {
+    summary.beforeFile(file, label);
+  }
 
-  /// Describe the processed file at [path] whose formatted result is [output].
+  /// Describe the processed file at [path] with formatted [result]s.
   ///
   /// If the contents of the file are the same as the formatted output,
   /// [changed] will be false.
-  void afterFile(File file, String label, SourceCode output, {bool changed});
-}
+  void afterFile(File file, String displayPath, SourceCode result,
+      {bool changed}) {
+    summary.afterFile(this, file, displayPath, result, changed: changed);
 
-/// 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);
-  }
-}
-
-/// Prints the formatted results of each file to stdout.
-class _PrintReporter extends OutputReporter {
-  @override
-  void showDirectory(String path) {
-    print('Formatting directory $path:');
-  }
-
-  @override
-  void showSkippedLink(String path) {
-    print('Skipping link $path');
-  }
-
-  @override
-  void showHiddenPath(String 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);
-  }
-}
-
-/// 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
-    // command line, this will never be used, which is why it's hard-coded to
-    // -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 ?? -1,
-        'length': output.selectionLength ?? -1
-      }
-    }));
-  }
-}
-
-/// Overwrites each file with its formatted result.
-class _OverwriteReporter extends _PrintReporter {
-  @override
-  void afterFile(File file, String label, SourceCode output, {bool changed}) {
+    // Save the results to disc.
+    var overwritten = false;
     if (changed) {
-      try {
-        file.writeAsStringSync(output.text);
-        print('Formatted $label');
-      } on FileSystemException catch (err) {
-        stderr.writeln('Could not overwrite $label: '
-            '${err.osError.message} (error code ${err.osError.errorCode})');
-      }
-    } else {
-      print('Unchanged $label');
+      overwritten = output.writeFile(file, displayPath, result);
     }
+
+    // Show the user.
+    if (show.file(displayPath, changed: changed, overwritten: overwritten)) {
+      output.showFile(displayPath, result);
+    }
+
+    // Set the exit code.
+    if (setExitIfChanged && changed) exitCode = 1;
   }
-}
 
-/// Base clase for a reporter that decorates an inner reporter.
-abstract class _ReporterDecorator implements OutputReporter {
-  final OutputReporter _inner;
-
-  _ReporterDecorator(this._inner);
-
-  @override
+  /// Describes the directory whose contents are about to be processed.
   void showDirectory(String path) {
-    _inner.showDirectory(path);
+    if (output != Output.json) {
+      show.directory(path);
+    }
   }
 
-  @override
+  /// Describes the symlink at [path] that wasn't followed.
   void showSkippedLink(String path) {
-    _inner.showSkippedLink(path);
+    show.skippedLink(path);
   }
 
-  @override
+  /// Describes the hidden [path] that wasn't processed.
   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);
-  }
-}
-
-/// A decorating reporter that reports how long it took for format each file.
-class ProfileReporter extends _ReporterDecorator {
-  /// The files that have been started but have not completed yet.
-  ///
-  /// Maps a file label to the time that it started being formatted.
-  final Map<String, DateTime> _ongoing = {};
-
-  /// The elapsed time it took to format each completed file.
-  final Map<String, Duration> _elapsed = {};
-
-  /// The number of files that completed so fast that they aren't worth
-  /// tracking.
-  int _elided = 0;
-
-  ProfileReporter(OutputReporter inner) : super(inner);
-
-  /// Show the times for the slowest files to format.
-  void showProfile() {
-    // Everything should be done.
-    assert(_ongoing.isEmpty);
-
-    var files = _elapsed.keys.toList();
-    files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a]));
-
-    for (var file in files) {
-      print('${_elapsed[file]}: $file');
-    }
-
-    if (_elided >= 1) {
-      var s = _elided > 1 ? 's' : '';
-      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();
-  }
-
-  /// Describe the processed file at [path] whose formatted result is [output].
-  ///
-  /// 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) {
-      _elapsed[label] = elapsed;
-    } else {
-      _elided++;
-    }
-
-    super.afterFile(file, label, output, changed: changed);
-  }
-}
-
-/// A decorating reporter that sets the exit code to 1 if any changes are made.
-class SetExitReporter extends _ReporterDecorator {
-  SetExitReporter(OutputReporter inner) : super(inner);
-
-  /// Describe the processed file at [path] whose formatted result is [output].
-  ///
-  /// 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;
-
-    super.afterFile(file, label, output, changed: changed);
+    show.hiddenPath(path);
   }
 }
diff --git a/lib/src/cli/output.dart b/lib/src/cli/output.dart
new file mode 100644
index 0000000..da8d241
--- /dev/null
+++ b/lib/src/cli/output.dart
@@ -0,0 +1,78 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'dart:convert';
+import 'dart:io';
+
+import '../source_code.dart';
+
+/// Where formatted code results should go.
+class Output {
+  /// Overwrite files on disc.
+  static const Output write = _WriteOutput();
+
+  /// Print the code to the terminal as human-friendly text.
+  static const Output show = _ShowOutput();
+
+  /// Print the code to the terminal as JSON.
+  static const Output json = _JsonOutput();
+
+  /// Do nothing. (Used when the user just wants the list of files that would
+  /// be changed.)
+  static const Output none = Output._();
+
+  const Output._();
+
+  /// Write the file to disc.
+  bool writeFile(File file, String displayPath, SourceCode result) => false;
+
+  /// Print the file to the terminal in some way.
+  void showFile(String path, SourceCode result) {}
+}
+
+class _WriteOutput extends Output {
+  const _WriteOutput() : super._();
+
+  @override
+  bool writeFile(File file, String displayPath, SourceCode result) {
+    try {
+      file.writeAsStringSync(result.text);
+    } on FileSystemException catch (err) {
+      stderr.writeln('Could not overwrite $displayPath: '
+          '${err.osError.message} (error code ${err.osError.errorCode})');
+    }
+
+    return true;
+  }
+}
+
+class _ShowOutput extends Output {
+  const _ShowOutput() : super._();
+
+  @override
+  void showFile(String path, SourceCode result) {
+    // Don't add an extra newline.
+    stdout.write(result.text);
+  }
+}
+
+class _JsonOutput extends Output {
+  const _JsonOutput() : super._();
+
+  @override
+  void showFile(String path, SourceCode result) {
+    // 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
+    // command line, this will never be used, which is why it's hard-coded to
+    // -1, -1. If we add support for passing in a selection, put the real
+    // result here.
+    print(jsonEncode({
+      'path': path,
+      'source': result.text,
+      'selection': {
+        'offset': result.selectionStart ?? -1,
+        'length': result.selectionLength ?? -1
+      }
+    }));
+  }
+}
diff --git a/lib/src/cli/show.dart b/lib/src/cli/show.dart
new file mode 100644
index 0000000..30017ba
--- /dev/null
+++ b/lib/src/cli/show.dart
@@ -0,0 +1,98 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'package:path/path.dart' as p;
+
+/// Which file paths should be printed.
+abstract class Show {
+  /// Only files whose formatting changed.
+  static const Show changed = _ChangedShow();
+
+  /// The legacy dartfmt output style when overwriting files.
+  static const Show overwrite = _OverwriteShow();
+
+  /// The legacy dartfmt output style in "--dry-run".
+  static const Show dryRun = _DryRunShow();
+
+  const Show._();
+
+  /// The display path to show for [file] which is in [directory].
+  ///
+  /// In the old CLI, this does not include [directory], since the directory
+  /// name is printed separately. The new CLI only prints file paths, so this
+  /// includes the root directory to disambiguate which directory the file is
+  /// in.
+  String displayPath(String directory, String file) => p.normalize(file);
+
+  /// Describes a file that was processed.
+  ///
+  /// Returns whether or not this file should be displayed.
+  bool file(String path, {bool changed, bool overwritten}) => false;
+
+  /// Describes the directory whose contents are about to be processed.
+  void directory(String path) {}
+
+  /// Describes the symlink at [path] that wasn't followed.
+  void skippedLink(String path) {}
+
+  /// Describes the hidden [path] that wasn't processed.
+  void hiddenPath(String path) {}
+
+  void _showFileChange(String path, {bool overwritten}) {
+    if (overwritten) {
+      print('Formatted $path');
+    } else {
+      print('Changed $path');
+    }
+  }
+}
+
+class _ChangedShow extends Show {
+  const _ChangedShow() : super._();
+
+  @override
+  bool file(String path, {bool changed, bool overwritten}) {
+    if (changed) _showFileChange(path, overwritten: overwritten);
+    return changed;
+  }
+}
+
+class _OverwriteShow extends Show {
+  const _OverwriteShow() : super._();
+
+  @override
+  String displayPath(String directory, String file) =>
+      p.relative(file, from: directory);
+
+  @override
+  bool file(String path, {bool changed, bool overwritten}) => true;
+
+  @override
+  void directory(String directory) {
+    print('Formatting directory $directory:');
+  }
+
+  @override
+  void skippedLink(String path) {
+    print('Skipping link $path');
+  }
+
+  @override
+  void hiddenPath(String path) {
+    print('Skipping hidden path $path');
+  }
+}
+
+class _DryRunShow extends Show {
+  const _DryRunShow() : super._();
+
+  @override
+  String displayPath(String directory, String file) =>
+      p.relative(file, from: directory);
+
+  @override
+  bool file(String path, {bool changed, bool overwritten}) {
+    if (changed) print(path);
+    return true;
+  }
+}
diff --git a/lib/src/cli/summary.dart b/lib/src/cli/summary.dart
new file mode 100644
index 0000000..ab5013e
--- /dev/null
+++ b/lib/src/cli/summary.dart
@@ -0,0 +1,88 @@
+// Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+import 'dart:io';
+
+import '../source_code.dart';
+import 'formatter_options.dart';
+
+/// The kind of summary shown after all formatting is complete.
+class Summary {
+  static const Summary none = Summary._();
+
+  /// Creates a Summary that captures profiling information.
+  ///
+  /// Mostly for internal use.
+  static Summary profile() => _ProfileSummary();
+
+  const Summary._();
+
+  /// Called when [file] is about to be formatted.
+  void beforeFile(File file, String displayPath) {}
+
+  /// Describe the processed file at [path] whose formatted result is [output].
+  ///
+  /// If the contents of the file are the same as the formatted output,
+  /// [changed] will be false.
+  void afterFile(FormatterOptions options, File file, String displayPath,
+      SourceCode output,
+      {bool changed}) {}
+
+  void show() {}
+}
+
+/// Reports how long it took for format each file.
+class _ProfileSummary implements Summary {
+  /// The files that have been started but have not completed yet.
+  ///
+  /// Maps a file label to the time that it started being formatted.
+  final Map<String, DateTime> _ongoing = {};
+
+  /// The elapsed time it took to format each completed file.
+  final Map<String, Duration> _elapsed = {};
+
+  /// The number of files that completed so fast that they aren't worth
+  /// tracking.
+  int _elided = 0;
+
+  /// Show the times for the slowest files to format.
+  @override
+  void show() {
+    // Everything should be done.
+    assert(_ongoing.isEmpty);
+
+    var files = _elapsed.keys.toList();
+    files.sort((a, b) => _elapsed[b].compareTo(_elapsed[a]));
+
+    for (var file in files) {
+      print('${_elapsed[file]}: $file');
+    }
+
+    if (_elided >= 1) {
+      var s = _elided > 1 ? 's' : '';
+      print('...$_elided more file$s each took less than 10ms.');
+    }
+  }
+
+  /// Called when [file] is about to be formatted.
+  @override
+  void beforeFile(File file, String displayPath) {
+    _ongoing[displayPath] = DateTime.now();
+  }
+
+  /// Describe the processed file at [path] whose formatted result is [output].
+  ///
+  /// If the contents of the file are the same as the formatted output,
+  /// [changed] will be false.
+  @override
+  void afterFile(FormatterOptions options, File file, String displayPath,
+      SourceCode output,
+      {bool changed}) {
+    var elapsed = DateTime.now().difference(_ongoing.remove(displayPath));
+    if (elapsed.inMilliseconds >= 10) {
+      _elapsed[displayPath] = elapsed;
+    } else {
+      _elided++;
+    }
+  }
+}
diff --git a/lib/src/io.dart b/lib/src/io.dart
index c6a70c4..893ad95 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -31,14 +31,14 @@
         pageWidth: options.pageWidth,
         fixes: options.fixes);
     try {
-      options.reporter.beforeFile(null, name);
+      options.beforeFile(null, name);
       var source = SourceCode(input.toString(),
           uri: name,
           selectionStart: selectionStart,
           selectionLength: selectionLength);
       var output = formatter.formatSource(source);
-      options.reporter
-          .afterFile(null, name, output, changed: source.text != output.text);
+      options.afterFile(null, name, output,
+          changed: source.text != output.text);
       return;
     } on FormatterException catch (err) {
       stderr.writeln(err.message());
@@ -81,7 +81,7 @@
 /// Returns `true` if successful or `false` if an error occurred in any of the
 /// files.
 bool processDirectory(FormatterOptions options, Directory directory) {
-  options.reporter.showDirectory(directory.path);
+  options.showDirectory(directory.path);
 
   var success = true;
   var shownHiddenPaths = <String>{};
@@ -91,17 +91,17 @@
   entries.sort((a, b) => a.path.compareTo(b.path));
 
   for (var entry in entries) {
-    var relative = p.relative(entry.path, from: directory.path);
+    var displayPath = options.show.displayPath(directory.path, entry.path);
 
     if (entry is Link) {
-      options.reporter.showSkippedLink(relative);
+      options.showSkippedLink(displayPath);
       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 parts = p.split(p.relative(entry.path, from: directory.path));
     var hiddenIndex;
     for (var i = 0; i < parts.length; i++) {
       if (parts[i].startsWith('.')) {
@@ -115,12 +115,12 @@
       // show the directory name once instead of once for each file.
       var hiddenPath = p.joinAll(parts.take(hiddenIndex + 1));
       if (shownHiddenPaths.add(hiddenPath)) {
-        options.reporter.showHiddenPath(hiddenPath);
+        options.showHiddenPath(hiddenPath);
       }
       continue;
     }
 
-    if (!processFile(options, entry, label: relative)) success = false;
+    if (!processFile(options, entry, displayPath: displayPath)) success = false;
   }
 
   return success;
@@ -129,8 +129,8 @@
 /// Runs the formatter on [file].
 ///
 /// Returns `true` if successful or `false` if an error occurred.
-bool processFile(FormatterOptions options, File file, {String label}) {
-  label ??= file.path;
+bool processFile(FormatterOptions options, File file, {String displayPath}) {
+  displayPath ??= file.path;
 
   var formatter = DartFormatter(
       indent: options.indent,
@@ -138,10 +138,10 @@
       fixes: options.fixes);
   try {
     var source = SourceCode(file.readAsStringSync(), uri: file.path);
-    options.reporter.beforeFile(file, label);
+    options.beforeFile(file, displayPath);
     var output = formatter.formatSource(source);
-    options.reporter
-        .afterFile(file, label, output, changed: source.text != output.text);
+    options.afterFile(file, displayPath, output,
+        changed: source.text != output.text);
     return true;
   } on FormatterException catch (err) {
     var color = Platform.operatingSystem != 'windows' &&
@@ -149,11 +149,11 @@
 
     stderr.writeln(err.message(color: color));
   } on UnexpectedOutputException catch (err) {
-    stderr.writeln('''Hit a bug in the formatter when formatting $label.
+    stderr.writeln('''Hit a bug in the formatter when formatting $displayPath.
 $err
 Please report at github.com/dart-lang/dart_style/issues.''');
   } catch (err, stack) {
-    stderr.writeln('''Hit a bug in the formatter when formatting $label.
+    stderr.writeln('''Hit a bug in the formatter when formatting $displayPath.
 Please report at github.com/dart-lang/dart_style/issues.
 $err
 $stack''');
diff --git a/test/io_test.dart b/test/io_test.dart
index 7f4ce8b..57f1dcc 100644
--- a/test/io_test.dart
+++ b/test/io_test.dart
@@ -17,10 +17,8 @@
 import 'utils.dart';
 
 void main() {
-  var overwriteOptions = FormatterOptions(OutputReporter.overwrite);
-
-  var followOptions =
-      FormatterOptions(OutputReporter.overwrite, followLinks: true);
+  var overwriteOptions = FormatterOptions();
+  var followOptions = FormatterOptions(followLinks: true);
 
   test('handles directory ending in ".dart"', () async {
     await d.dir('code.dart', [