[frontend] report transformation error messages through library loader

Change-Id: I7280d02e42785742fc800f7e44b1913408dcada5
Reviewed-on: https://dart-review.googlesource.com/c/90460
Commit-Queue: Daco Harkes <dacoharkes@google.com>
Reviewed-by: Peter von der Ahé <ahe@google.com>
diff --git a/pkg/compiler/lib/src/kernel/dart2js_target.dart b/pkg/compiler/lib/src/kernel/dart2js_target.dart
index 9ab34db..7b2863e 100644
--- a/pkg/compiler/lib/src/kernel/dart2js_target.dart
+++ b/pkg/compiler/lib/src/kernel/dart2js_target.dart
@@ -74,8 +74,12 @@
   bool get errorOnUnexactWebIntLiterals => true;
 
   @override
-  void performModularTransformationsOnLibraries(ir.Component component,
-      CoreTypes coreTypes, ClassHierarchy hierarchy, List<ir.Library> libraries,
+  void performModularTransformationsOnLibraries(
+      ir.Component component,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      List<ir.Library> libraries,
+      DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {}
 
   @override
diff --git a/pkg/compiler/lib/src/kernel/loader.dart b/pkg/compiler/lib/src/kernel/loader.dart
index 19891b3..7b56f28 100644
--- a/pkg/compiler/lib/src/kernel/loader.dart
+++ b/pkg/compiler/lib/src/kernel/loader.dart
@@ -13,7 +13,7 @@
 
 import 'package:front_end/src/api_unstable/dart2js.dart' as fe;
 import 'package:kernel/kernel.dart' hide LibraryDependency, Combinator;
-import 'package:kernel/target/targets.dart';
+import 'package:kernel/target/targets.dart' hide DiagnosticReporter;
 
 import '../../compiler_new.dart' as api;
 import '../common/tasks.dart' show CompilerTask, Measurer;
diff --git a/pkg/dev_compiler/lib/src/kernel/target.dart b/pkg/dev_compiler/lib/src/kernel/target.dart
index 8652983..fdabc09 100644
--- a/pkg/dev_compiler/lib/src/kernel/target.dart
+++ b/pkg/dev_compiler/lib/src/kernel/target.dart
@@ -70,8 +70,12 @@
   bool get enableNoSuchMethodForwarders => true;
 
   @override
-  void performModularTransformationsOnLibraries(Component component,
-      CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
+  void performModularTransformationsOnLibraries(
+      Component component,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      List<Library> libraries,
+      DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {
     this.hierarchy = hierarchy;
     for (var library in libraries) {
diff --git a/pkg/front_end/lib/src/api_unstable/vm.dart b/pkg/front_end/lib/src/api_unstable/vm.dart
index 8ca86a7..6564a5b 100644
--- a/pkg/front_end/lib/src/api_unstable/vm.dart
+++ b/pkg/front_end/lib/src/api_unstable/vm.dart
@@ -51,7 +51,11 @@
         templateConstEvalNegativeShift,
         templateConstEvalNonConstantLiteral,
         templateConstEvalNonConstantVariableGet,
-        templateConstEvalZeroDivisor;
+        templateConstEvalZeroDivisor,
+        templateFfiAnnotationMissing,
+        templateFfiTypeInvalid,
+        templateFfiTypeMismatch,
+        templateFfiTypeOpaque;
 
 export '../fasta/hybrid_file_system.dart' show HybridFileSystem;
 
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 43f48ce..d4a2582 100644
--- a/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
+++ b/pkg/front_end/lib/src/fasta/fasta_codes_generated.dart
@@ -3347,6 +3347,129 @@
   -h        Display this message (add -v for information about all options).""");
 
 // DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String
+            name)> templateFfiAnnotationMissing = const Template<
+        Message Function(String name)>(
+    messageTemplate:
+        r"""Field '#name' is missing an annotation to declare its C++ type,dart:ffi structs (Pointer<Void>) cannot have regular Dart fields.""",
+    withArguments: _withArgumentsFfiAnnotationMissing);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name)> codeFfiAnnotationMissing =
+    const Code<Message Function(String name)>(
+  "FfiAnnotationMissing",
+  templateFfiAnnotationMissing,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiAnnotationMissing(String name) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  return new Message(codeFfiAnnotationMissing,
+      message:
+          """Field '${name}' is missing an annotation to declare its C++ type,dart:ffi structs (Pointer<Void>) cannot have regular Dart fields.""",
+      arguments: {'name': name});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(DartType _type)> templateFfiTypeInvalid = const Template<
+        Message Function(DartType _type)>(
+    messageTemplate:
+        r"""Expected type '#type' to be a valid and instantiated subtype of '_NativeType'.""",
+    withArguments: _withArgumentsFfiTypeInvalid);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(DartType _type)> codeFfiTypeInvalid =
+    const Code<Message Function(DartType _type)>(
+  "FfiTypeInvalid",
+  templateFfiTypeInvalid,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiTypeInvalid(DartType _type) {
+  TypeLabeler labeler = new TypeLabeler();
+  List<Object> typeParts = labeler.labelType(_type);
+  String type = typeParts.join();
+  return new Message(codeFfiTypeInvalid,
+      message:
+          """Expected type '${type}' to be a valid and instantiated subtype of '_NativeType'.""" +
+              labeler.originMessages,
+      arguments: {'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        DartType _type,
+        DartType _type2,
+        DartType
+            _type3)> templateFfiTypeMismatch = const Template<
+        Message Function(DartType _type, DartType _type2, DartType _type3)>(
+    messageTemplate:
+        r"""Expected type '#type' to be '#type2', which is the Dart type corresponding to '#type3'.""",
+    withArguments: _withArgumentsFfiTypeMismatch);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(DartType _type, DartType _type2, DartType _type3)>
+    codeFfiTypeMismatch = const Code<
+        Message Function(DartType _type, DartType _type2, DartType _type3)>(
+  "FfiTypeMismatch",
+  templateFfiTypeMismatch,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiTypeMismatch(
+    DartType _type, DartType _type2, DartType _type3) {
+  TypeLabeler labeler = new TypeLabeler();
+  List<Object> typeParts = labeler.labelType(_type);
+  List<Object> type2Parts = labeler.labelType(_type2);
+  List<Object> type3Parts = labeler.labelType(_type3);
+  String type = typeParts.join();
+  String type2 = type2Parts.join();
+  String type3 = type3Parts.join();
+  return new Message(codeFfiTypeMismatch,
+      message:
+          """Expected type '${type}' to be '${type2}', which is the Dart type corresponding to '${type3}'.""" +
+              labeler.originMessages,
+      arguments: {'type': _type, 'type2': _type2, 'type3': _type3});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Template<
+    Message Function(
+        String name,
+        DartType
+            _type)> templateFfiTypeOpaque = const Template<
+        Message Function(String name, DartType _type)>(
+    messageTemplate:
+        r"""Method '#name' cannot be called on something of type '#type' as this type is opaque.""",
+    withArguments: _withArgumentsFfiTypeOpaque);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+const Code<Message Function(String name, DartType _type)> codeFfiTypeOpaque =
+    const Code<Message Function(String name, DartType _type)>(
+  "FfiTypeOpaque",
+  templateFfiTypeOpaque,
+);
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
+Message _withArgumentsFfiTypeOpaque(String name, DartType _type) {
+  if (name.isEmpty) throw 'No name provided';
+  name = demangleMixinApplicationName(name);
+  TypeLabeler labeler = new TypeLabeler();
+  List<Object> typeParts = labeler.labelType(_type);
+  String type = typeParts.join();
+  return new Message(codeFfiTypeOpaque,
+      message:
+          """Method '${name}' cannot be called on something of type '${type}' as this type is opaque.""" +
+              labeler.originMessages,
+      arguments: {'name': name, 'type': _type});
+}
+
+// DO NOT EDIT. THIS FILE IS GENERATED. SEE TOP OF FILE.
 const Code<Null> codeFieldInitializedOutsideDeclaringClass =
     messageFieldInitializedOutsideDeclaringClass;
 
diff --git a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
index fc4788c..96f4adb 100644
--- a/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
+++ b/pkg/front_end/lib/src/fasta/kernel/kernel_target.dart
@@ -39,6 +39,8 @@
 
 import 'package:kernel/type_algebra.dart' show substitute;
 
+import 'package:kernel/target/targets.dart' show DiagnosticReporter;
+
 import 'package:kernel/type_environment.dart' show TypeEnvironment;
 
 import 'package:kernel/transformations/constants.dart' as constants
@@ -54,6 +56,10 @@
 
 import '../dill/dill_member_builder.dart' show DillMemberBuilder;
 
+import '../fasta_codes.dart' show Message, LocatedMessage;
+
+import '../loader.dart' show Loader;
+
 import '../messages.dart'
     show
         FormattedMessage,
@@ -753,7 +759,11 @@
       ticker.logMs("Evaluated constants");
     }
     backendTarget.performModularTransformationsOnLibraries(
-        component, loader.coreTypes, loader.hierarchy, loader.libraries,
+        component,
+        loader.coreTypes,
+        loader.hierarchy,
+        loader.libraries,
+        new KernelDiagnosticReporter(loader),
         logger: (String msg) => ticker.logMs(msg));
   }
 
@@ -818,3 +828,15 @@
   }
   return null;
 }
+
+class KernelDiagnosticReporter
+    extends DiagnosticReporter<Message, LocatedMessage> {
+  final Loader<Library> loader;
+
+  KernelDiagnosticReporter(this.loader);
+
+  void report(Message message, int charOffset, int length, Uri fileUri,
+      {List<LocatedMessage> context}) {
+    loader.addProblem(message, charOffset, noLength, fileUri, context: context);
+  }
+}
diff --git a/pkg/front_end/messages.status b/pkg/front_end/messages.status
index 445d797..b88b3a1 100644
--- a/pkg/front_end/messages.status
+++ b/pkg/front_end/messages.status
@@ -173,6 +173,10 @@
 FastaUsageLong/example: Fail
 FastaUsageShort/analyzerCode: Fail
 FastaUsageShort/example: Fail
+FfiAnnotationMissing/analyzerCode : Fail
+FfiTypeInvalid/analyzerCode: Fail
+FfiTypeMismatch/analyzerCode: Fail
+FfiTypeOpaque/analyzerCode: Fail
 FieldInitializedOutsideDeclaringClass/script1: Fail
 FieldInitializerOutsideConstructor/script1: Fail
 FinalAndCovariant/script2: Fail
diff --git a/pkg/front_end/messages.yaml b/pkg/front_end/messages.yaml
index 7b717b1..79f6350 100644
--- a/pkg/front_end/messages.yaml
+++ b/pkg/front_end/messages.yaml
@@ -3409,3 +3409,23 @@
 StaticAndInstanceConflictCause:
   template: "This is the instance member."
   severity: CONTEXT
+
+FfiTypeMismatch:
+  # Used by dart:ffi
+  template: "Expected type '#type' to be '#type2', which is the Dart type corresponding to '#type3'."
+  external: test/ffi_test.dart
+
+FfiTypeInvalid:
+  # Used by dart:ffi
+  template: "Expected type '#type' to be a valid and instantiated subtype of '_NativeType'."
+  external: test/ffi_test.dart
+
+FfiTypeOpaque:
+  # Used by dart:ffi
+  template: "Method '#name' cannot be called on something of type '#type' as this type is opaque."
+  external: test/ffi_test.dart
+
+FfiAnnotationMissing:
+  # Used by dart:ffi
+  template: "Field '#name' is missing an annotation to declare its C++ type,dart:ffi structs (Pointer<Void>) cannot have regular Dart fields."
+  external: test/ffi_test.dart
diff --git a/pkg/front_end/test/fasta/testing/suite.dart b/pkg/front_end/test/fasta/testing/suite.dart
index 02a8e3a..6689d9f 100644
--- a/pkg/front_end/test/fasta/testing/suite.dart
+++ b/pkg/front_end/test/fasta/testing/suite.dart
@@ -18,7 +18,8 @@
 
 import 'package:kernel/kernel.dart' show loadComponentFromBytes;
 
-import 'package:kernel/target/targets.dart' show TargetFlags;
+import 'package:kernel/target/targets.dart'
+    show TargetFlags, DiagnosticReporter;
 
 import 'package:testing/testing.dart'
     show
@@ -423,12 +424,16 @@
   String get name => "vm";
 
   @override
-  void performModularTransformationsOnLibraries(Component component,
-      CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
+  void performModularTransformationsOnLibraries(
+      Component component,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      List<Library> libraries,
+      DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {
     if (enabled) {
       super.performModularTransformationsOnLibraries(
-          component, coreTypes, hierarchy, libraries,
+          component, coreTypes, hierarchy, libraries, diagnosticReporter,
           logger: logger);
     }
   }
diff --git a/pkg/front_end/test/ffi_test.dart b/pkg/front_end/test/ffi_test.dart
new file mode 100644
index 0000000..f032f75
--- /dev/null
+++ b/pkg/front_end/test/ffi_test.dart
@@ -0,0 +1,10 @@
+// Copyright (c) 2019, 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:expect/expect.dart';
+
+main() {
+  // TODO(dacoharkes): implement dart:ffi tests
+  Expect.isTrue(true);
+}
diff --git a/pkg/kernel/lib/target/targets.dart b/pkg/kernel/lib/target/targets.dart
index 007fc9e..f64802f 100644
--- a/pkg/kernel/lib/target/targets.dart
+++ b/pkg/kernel/lib/target/targets.dart
@@ -36,6 +36,11 @@
   return builder(flags);
 }
 
+abstract class DiagnosticReporter<M, C> {
+  void report(M message, int charOffset, int length, Uri fileUri,
+      {List<C> context});
+}
+
 /// A target provides backend-specific options for generating kernel IR.
 abstract class Target {
   String get name;
@@ -94,8 +99,12 @@
   void performOutlineTransformations(Component component) {}
 
   /// Perform target-specific modular transformations on the given libraries.
-  void performModularTransformationsOnLibraries(Component component,
-      CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
+  void performModularTransformationsOnLibraries(
+      Component component,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      List<Library> libraries,
+      DiagnosticReporter diagnosticReporter,
       {void logger(String msg)});
 
   /// Perform target-specific modular transformations on the given program.
@@ -196,8 +205,12 @@
   bool get legacyMode => flags.legacyMode;
   String get name => 'none';
   List<String> get extraRequiredLibraries => <String>[];
-  void performModularTransformationsOnLibraries(Component component,
-      CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
+  void performModularTransformationsOnLibraries(
+      Component component,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      List<Library> libraries,
+      DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {}
 
   @override
diff --git a/pkg/vm/lib/target/vm.dart b/pkg/vm/lib/target/vm.dart
index a43e07c..0a33c87 100644
--- a/pkg/vm/lib/target/vm.dart
+++ b/pkg/vm/lib/target/vm.dart
@@ -68,8 +68,12 @@
       ];
 
   @override
-  void performModularTransformationsOnLibraries(Component component,
-      CoreTypes coreTypes, ClassHierarchy hierarchy, List<Library> libraries,
+  void performModularTransformationsOnLibraries(
+      Component component,
+      CoreTypes coreTypes,
+      ClassHierarchy hierarchy,
+      List<Library> libraries,
+      DiagnosticReporter diagnosticReporter,
       {void logger(String msg)}) {
     transformMixins.transformLibraries(this, coreTypes, hierarchy, libraries,
         doSuperResolution: false /* resolution is done in Dart VM */);