Use CantReadFile in ProcessedOptions

This ensures that we use the same error message when a file is missing.
Also, we handle FormatException in packages configuration files better.

Change-Id: I6e4c063131ca142643e5a49827f105ba0e23d1b0
Reviewed-on: https://dart-review.googlesource.com/74441
Reviewed-by: Aske Simon Christensen <askesc@google.com>
Commit-Queue: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/front_end/lib/src/base/processed_options.dart b/pkg/front_end/lib/src/base/processed_options.dart
index 541a6e5..89060a67 100644
--- a/pkg/front_end/lib/src/base/processed_options.dart
+++ b/pkg/front_end/lib/src/base/processed_options.dart
@@ -44,13 +44,13 @@
         messageInternalProblemProvidedBothCompileSdkAndSdkSummary,
         messageMissingInput,
         noLength,
-        templateCannotReadPackagesFile,
         templateCannotReadSdkSpecification,
+        templateCantReadFile,
         templateInputFileNotFound,
         templateInternalProblemUnsupported,
+        templatePackagesFileFormat,
         templateSdkRootNotFound,
         templateSdkSpecificationNotFound,
-        templateCantReadFile,
         templateSdkSummaryNotFound;
 
 import '../fasta/messages.dart' show getLocation;
@@ -467,20 +467,32 @@
 
   /// Create a [Packages] given the Uri to a `.packages` file.
   Future<Packages> createPackagesFromFile(Uri file) async {
+    List<int> contents;
     try {
-      List<int> contents = await fileSystem.entityForUri(file).readAsBytes();
-      Map<String, Uri> map = package_config.parse(contents, file);
-      _packagesUri = file;
-      return new MapPackages(map);
-    } catch (e) {
-      _packagesUri = null;
-      report(
-          templateCannotReadPackagesFile
-              .withArguments("$e")
-              .withLocation(file, -1, noLength),
-          Severity.error);
-      return Packages.noPackages;
+      // TODO(ahe): We need to compute line endings for this file.
+      contents = await fileSystem.entityForUri(file).readAsBytes();
+    } on FileSystemException catch (e) {
+      reportWithoutLocation(
+          templateCantReadFile.withArguments(file, e.message), Severity.error);
     }
+    if (contents != null) {
+      _packagesUri = file;
+      try {
+        Map<String, Uri> map = package_config.parse(contents, file);
+        return new MapPackages(map);
+      } on FormatException catch (e) {
+        report(
+            templatePackagesFileFormat
+                .withArguments(e.message)
+                .withLocation(file, e.offset, noLength),
+            Severity.error);
+      } catch (e) {
+        reportWithoutLocation(
+            templateCantReadFile.withArguments(file, "$e"), Severity.error);
+      }
+    }
+    _packagesUri = null;
+    return Packages.noPackages;
   }
 
   /// Finds a package resolution strategy using a [FileSystem].
diff --git a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
index 994f876..3699ba1 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -464,26 +464,6 @@
     message: r"""Can't assign to super.""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Template<Message Function(String string)> templateCannotReadPackagesFile =
-    const Template<Message Function(String string)>(
-        messageTemplate: r"""Unable to read '.packages' file:
-  #string.""", withArguments: _withArgumentsCannotReadPackagesFile);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-const Code<Message Function(String string)> codeCannotReadPackagesFile =
-    const Code<Message Function(String string)>(
-  "CannotReadPackagesFile",
-  templateCannotReadPackagesFile,
-);
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
-Message _withArgumentsCannotReadPackagesFile(String string) {
-  return new Message(codeCannotReadPackagesFile,
-      message: """Unable to read '.packages' file:
-  ${string}.""", arguments: {'string': string});
-}
-
-// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<Message Function(String string)>
     templateCannotReadSdkSpecification =
     const Template<Message Function(String string)>(
@@ -5970,6 +5950,26 @@
 }
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<Message Function(String string)> templatePackagesFileFormat =
+    const Template<Message Function(String string)>(
+        messageTemplate: r"""Problem in packages configuration file: #string""",
+        withArguments: _withArgumentsPackagesFileFormat);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String string)> codePackagesFileFormat =
+    const Code<Message Function(String string)>(
+  "PackagesFileFormat",
+  templatePackagesFileFormat,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsPackagesFileFormat(String string) {
+  return new Message(codePackagesFileFormat,
+      message: """Problem in packages configuration file: ${string}""",
+      arguments: {'string': string});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Template<
     Message Function(Uri uri_)> templatePartOfInLibrary = const Template<
         Message Function(Uri uri_)>(
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 55bbfd8..ca30a13 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -271,6 +271,7 @@
 OverrideTypeVariablesMismatch/example: Fail
 PackageNotFound/analyzerCode: Fail
 PackageNotFound/example: Fail
+PackagesFileFormat/analyzerCode: Fail # Analyzer crashes when .packages file has format error
 PartOfLibraryNameMismatch/example: Fail
 PartOfUriMismatch/example: Fail
 PartOfUseUri/example: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 9bbe9e3..ed49e2b 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -2357,9 +2357,6 @@
   script:
     - "main(){ ++f(); }"
 
-CannotReadPackagesFile:
-  template: "Unable to read '.packages' file:\n  #string."
-
 CannotReadSdkSpecification:
   template: "Unable to read the 'libraries.json' specification file:\n  #string."
 
@@ -2781,8 +2778,13 @@
 CantReadFile:
   template: "Error when reading '#uri': #string"
   analyzerCode: URI_DOES_NOT_EXIST
+  external: test/packages_format_error_test.dart
   script: |
     import "non_existing_file.dart";
 
     main() {
     }
+
+PackagesFileFormat:
+  template: "Problem in packages configuration file: #string"
+  external: test/packages_format_error_test.dart
diff --git a/pkg/front_end/test/packages_format_error_test.dart b/pkg/front_end/test/packages_format_error_test.dart
new file mode 100644
index 0000000..f960378
--- /dev/null
+++ b/pkg/front_end/test/packages_format_error_test.dart
@@ -0,0 +1,43 @@
+// Copyright (c) 2018, 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:async_helper/async_helper.dart' show asyncTest;
+
+import 'package:expect/expect.dart' show Expect;
+
+import 'package:front_end/src/api_prototype/compiler_options.dart'
+    show CompilerOptions, FormattedMessage;
+
+import 'package:front_end/src/api_prototype/memory_file_system.dart'
+    show MemoryFileSystem;
+
+import 'package:front_end/src/base/processed_options.dart'
+    show ProcessedOptions;
+
+import 'package:front_end/src/fasta/compiler_context.dart' show CompilerContext;
+
+main() {
+  Uri root = Uri.parse("org-dartlang-test:///");
+  MemoryFileSystem fs = new MemoryFileSystem(root);
+  Uri packages = root.resolve(".packages");
+  fs.entityForUri(packages).writeAsStringSync("bad\n");
+  List<FormattedMessage> messages = <FormattedMessage>[];
+  CompilerContext c =
+      new CompilerContext(new ProcessedOptions(new CompilerOptions()
+        ..fileSystem = fs
+        ..onProblem = (message, severity, context) {
+          messages.add(message);
+        }));
+  asyncTest(() async {
+    await c
+        .runInContext<void>((_) => c.options.createPackagesFromFile(packages));
+    Expect.stringEquals("PackagesFileFormat", messages.single.code.name);
+    messages.clear();
+
+    await c.runInContext<void>(
+        (_) => c.options.createPackagesFromFile(root.resolve("missing-file")));
+    Expect.stringEquals("CantReadFile", messages.single.code.name);
+    messages.clear();
+  });
+}
diff --git a/pkg/front_end/test/src/base/processed_options_test.dart b/pkg/front_end/test/src/base/processed_options_test.dart
index f7f4fcf..13c42fa 100644
--- a/pkg/front_end/test/src/base/processed_options_test.dart
+++ b/pkg/front_end/test/src/base/processed_options_test.dart
@@ -320,7 +320,7 @@
     var uriTranslator = await processed.getUriTranslator();
     expect(uriTranslator.packages.asMap(), isEmpty);
     expect(errors.single.message,
-        startsWith(_stringPrefixOf(templateCannotReadPackagesFile)));
+        startsWith(_stringPrefixOf(templateCantReadFile)));
   }
 
   test_validateOptions_noInputs() async {