Don't dump a stack trace on the user if the formatter had bad output.

R=jacobr@google.com

Review-Url: https://codereview.chromium.org//2837143003 .
diff --git a/bin/format.dart b/bin/format.dart
index 3de323c..df2b678 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -8,7 +8,7 @@
 import 'package:args/args.dart';
 
 import 'package:dart_style/src/dart_formatter.dart';
-import 'package:dart_style/src/formatter_exception.dart';
+import 'package:dart_style/src/exceptions.dart';
 import 'package:dart_style/src/formatter_options.dart';
 import 'package:dart_style/src/io.dart';
 import 'package:dart_style/src/source_code.dart';
diff --git a/lib/dart_style.dart b/lib/dart_style.dart
index 6ecc7c3..7fb9d30 100644
--- a/lib/dart_style.dart
+++ b/lib/dart_style.dart
@@ -5,5 +5,5 @@
 library dart_style;
 
 export 'src/dart_formatter.dart';
-export 'src/formatter_exception.dart';
+export 'src/exceptions.dart';
 export 'src/source_code.dart';
diff --git a/lib/src/dart_formatter.dart b/lib/src/dart_formatter.dart
index 24d9483..2e75e62 100644
--- a/lib/src/dart_formatter.dart
+++ b/lib/src/dart_formatter.dart
@@ -15,7 +15,7 @@
 import 'package:analyzer/src/string_source.dart';
 
 import 'error_listener.dart';
-import 'formatter_exception.dart';
+import 'exceptions.dart';
 import 'source_code.dart';
 import 'source_visitor.dart';
 import 'string_compare.dart' as string_compare;
@@ -131,8 +131,7 @@
     var visitor = new SourceVisitor(this, lineInfo, source);
     var output = visitor.run(node);
     if (!string_compare.equalIgnoringWhitespace(source.text, output.text)) {
-      throw new FormatException('Cannot safely format file as formatting '
-          'would cause non-whitespace change(s).');
+      throw new UnexpectedOutputException(source.text, output.text);
     }
 
     return output;
diff --git a/lib/src/error_listener.dart b/lib/src/error_listener.dart
index 0c0be61..75958ba 100644
--- a/lib/src/error_listener.dart
+++ b/lib/src/error_listener.dart
@@ -6,7 +6,7 @@
 
 import 'package:analyzer/analyzer.dart';
 
-import 'formatter_exception.dart';
+import 'exceptions.dart';
 
 /// A simple [AnalysisErrorListener] that just collects the reported errors.
 class ErrorListener implements AnalysisErrorListener {
diff --git a/lib/src/formatter_exception.dart b/lib/src/exceptions.dart
similarity index 81%
rename from lib/src/formatter_exception.dart
rename to lib/src/exceptions.dart
index 6696aa4..41756af 100644
--- a/lib/src/formatter_exception.dart
+++ b/lib/src/exceptions.dart
@@ -51,3 +51,22 @@
 
   String toString() => message();
 }
+
+/// Exception thrown when the internal sanity check that only whitespace
+/// changes are made fails.
+class UnexpectedOutputException implements Exception {
+  /// The source being formatted.
+  final String _input;
+
+  /// The resulting output.
+  final String _output;
+
+  UnexpectedOutputException(this._input, this._output);
+
+  String toString() {
+    return """The formatter produced unexpected output. Input was:
+$_input
+Which formatted to:
+$_output""";
+  }
+}
diff --git a/lib/src/io.dart b/lib/src/io.dart
index 51f3805..eeaa532 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -9,7 +9,7 @@
 import 'package:path/path.dart' as p;
 
 import 'dart_formatter.dart';
-import 'formatter_exception.dart';
+import 'exceptions.dart';
 import 'formatter_options.dart';
 import 'source_code.dart';
 
@@ -81,9 +81,13 @@
         stdioType(stderr) == StdioType.TERMINAL;
 
     stderr.writeln(err.message(color: color));
+  } on UnexpectedOutputException catch (err) {
+    stderr.writeln('''Hit a bug in the formatter when formatting $label.
+$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.
-Please report at: github.com/dart-lang/dart_style/issues
+Please report at github.com/dart-lang/dart_style/issues.
 $err
 $stack''');
   }
diff --git a/test/formatter_test.dart b/test/formatter_test.dart
index ddf3b47..d71b747 100644
--- a/test/formatter_test.dart
+++ b/test/formatter_test.dart
@@ -132,12 +132,12 @@
     });
   });
 
-  test('throws a FormatterException on non-whitespace changes', () {
+  test('throws an UnexpectedOutputException on non-whitespace changes', () {
     // Use an invalid line ending character to ensure the formatter will
     // attempt to make non-whitespace changes.
     var formatter = new DartFormatter(lineEnding: '%');
     expect(() => formatter.format("var i = 1;"),
-        throwsA(new isInstanceOf<FormatException>()));
+        throwsA(new isInstanceOf<UnexpectedOutputException>()));
   });
 }