Let users control name shown in errors when reading from stdin. (#740)

* Let users control name shown in errors when reading from stdin.

Fix #739.

* Explicit cast from dynamic.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7aa33cf..9946158 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,6 @@
 # 1.2.0
 
+* Add `--stdin-name` to specify name shown when reading from stdin (#739).
 * Add `--fix-doc-comments` to turn `/** ... */` doc comments into `///` (#730).
 * Add support for new mixin syntax (#727).
 * Remove `const` in all metadata annotations with --fix-optional-const` (#720).
diff --git a/bin/format.dart b/bin/format.dart
index 45756a3..243cbbb 100644
--- a/bin/format.dart
+++ b/bin/format.dart
@@ -60,6 +60,10 @@
           "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.addFlag("profile", negatable: false, hide: true);
   parser.addFlag("transform", abbr: "t", negatable: false, hide: true);
@@ -134,7 +138,7 @@
     reporter = new SetExitReporter(reporter);
   }
 
-  var pageWidth;
+  int pageWidth;
   try {
     pageWidth = int.parse(argResults["line-length"]);
   } on FormatException catch (_) {
@@ -144,8 +148,7 @@
         '"${argResults['line-length']}".');
   }
 
-  var indent;
-
+  int indent;
   try {
     indent = int.parse(argResults["indent"]);
     if (indent < 0 || indent.toInt() != indent) throw new FormatException();
@@ -170,6 +173,10 @@
     }
   }
 
+  if (argResults.wasParsed("stdin-name") && !argResults.rest.isEmpty) {
+    usageError(parser, "Cannot pass --stdin-name when not reading from stdin.");
+  }
+
   var options = new FormatterOptions(reporter,
       indent: indent,
       pageWidth: pageWidth,
@@ -177,7 +184,7 @@
       fixes: fixes);
 
   if (argResults.rest.isEmpty) {
-    formatStdin(options, selection);
+    formatStdin(options, selection, argResults["stdin-name"] as String);
   } else {
     formatPaths(options, argResults.rest);
   }
@@ -200,7 +207,7 @@
 }
 
 /// Reads input from stdin until it's closed, and the formats it.
-void formatStdin(FormatterOptions options, List<int> selection) {
+void formatStdin(FormatterOptions options, List<int> selection, String name) {
   var selectionStart = 0;
   var selectionLength = 0;
 
@@ -216,14 +223,14 @@
         pageWidth: options.pageWidth,
         fixes: options.fixes);
     try {
-      options.reporter.beforeFile(null, "<stdin>");
+      options.reporter.beforeFile(null, name);
       var source = new SourceCode(input.toString(),
-          uri: "stdin",
+          uri: name,
           selectionStart: selectionStart,
           selectionLength: selectionLength);
       var output = formatter.formatSource(source);
-      options.reporter.afterFile(null, "<stdin>", output,
-          changed: source.text != output.text);
+      options.reporter
+          .afterFile(null, name, output, changed: source.text != output.text);
       return;
     } on FormatterException catch (err) {
       stderr.writeln(err.message());
diff --git a/test/command_line_test.dart b/test/command_line_test.dart
index a2543c3..f85db29 100644
--- a/test/command_line_test.dart
+++ b/test/command_line_test.dart
@@ -21,7 +21,7 @@
   });
 
   test("exits with 64 on a command line argument error", () async {
-    var process = await runFormatterOnDir(["-wat"]);
+    var process = await runFormatter(["-wat"]);
     await process.shouldExit(64);
   });
 
@@ -33,40 +33,35 @@
   });
 
   test("errors if --dry-run and --overwrite are both passed", () async {
-    await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
-
-    var process = await runFormatterOnDir(["--dry-run", "--overwrite"]);
+    var process = await runFormatter(["--dry-run", "--overwrite"]);
     await process.shouldExit(64);
   });
 
   test("errors if --dry-run and --machine are both passed", () async {
-    await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
-
-    var process = await runFormatterOnDir(["--dry-run", "--machine"]);
-    await process.shouldExit(64);
-  });
-
-  test("errors if --machine and --overwrite are both passed", () async {
-    await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
-
-    var process = await runFormatterOnDir(["--machine", "--overwrite"]);
-    await process.shouldExit(64);
-  });
-
-  test("errors if --dry-run and --machine are both passed", () async {
-    await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
-
     var process = await runFormatter(["--dry-run", "--machine"]);
     await process.shouldExit(64);
   });
 
   test("errors if --machine and --overwrite are both passed", () async {
-    await d.dir("code", [d.file("a.dart", unformattedSource)]).create();
-
     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"]);
+    await process.shouldExit(64);
+  });
+
+  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"]);
+    await process.shouldExit(64);
+  });
+
   test("--version prints the version number", () async {
     var process = await runFormatter(["--version"]);
 
@@ -311,5 +306,18 @@
       expect(await process.stdout.next, formattedSource.trimRight());
       await process.shouldExit(0);
     });
+
+    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"));
+      process.stderr.cancel();
+      await process.shouldExit(65);
+    });
   });
 }