Reland "Add support for recording context information with analysis errors, with one example"

This reverts commit b9bf239c6549f7a8bbe435d748576b9d00b1ba94.

Reason for revert: The angular and angular plugins have been updated so as to make this safe.

Original change's description:
> Revert "Add support for recording context information with analysis errors, with one example"
> 
> This reverts commit 6114435ea5e70eb08a79ff4d432827f5d43991aa.
> 
> Reason for revert: Broke angular and angular_analyzer_plugin
> 
> Original change's description:
> > Add support for recording context information with analysis errors, with one example
> > 
> > Change-Id: Iba1c48163ce264d85a68bcb9f70e5025a7cdbfbb
> > Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100563
> > Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
> > Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
> 
> TBR=scheglov@google.com,brianwilkerson@google.com
> 
> Change-Id: I275c6e4473de73199ea9b5e490379ba58aec1d43
> No-Presubmit: true
> No-Tree-Checks: true
> No-Try: true
> Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/100628
> Reviewed-by: Paul Berry <paulberry@google.com>
> Commit-Queue: Paul Berry <paulberry@google.com>

TBR=paulberry@google.com,scheglov@google.com,brianwilkerson@google.com

# Not skipping CQ checks because original CL landed > 1 day ago.

Change-Id: Ib1b7e63a459e63bcbf02f26bcf167ddad45295b7
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/101004
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Reviewed-by: Paul Berry <paulberry@google.com>
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Commit-Queue: Brian Wilkerson <brianwilkerson@google.com>
diff --git a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
index 6c73922..cf2f06f 100644
--- a/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
+++ b/pkg/analysis_server/lib/src/services/completion/statement/statement_completion.dart
@@ -324,12 +324,6 @@
           }
           _removeError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "']'");
           _removeError(ScannerErrorCode.EXPECTED_TOKEN, partialMatch: "']'");
-          var ms =
-              _findError(ParserErrorCode.EXPECTED_TOKEN, partialMatch: "';'");
-          if (ms != null) {
-            // Ensure the semicolon gets inserted in the correct location.
-            ms.offset = loc - 1;
-          }
         }
       }
     }
diff --git a/pkg/analysis_server/test/protocol_server_test.dart b/pkg/analysis_server/test/protocol_server_test.dart
index 48bac74..7bc7663 100644
--- a/pkg/analysis_server/test/protocol_server_test.dart
+++ b/pkg/analysis_server/test/protocol_server_test.dart
@@ -4,16 +4,17 @@
 
 import 'dart:mirrors';
 
-import 'package:analysis_server/src/protocol_server.dart';
+import 'package:analysis_server/src/protocol_server.dart'
+    hide DiagnosticMessage;
 import 'package:analysis_server/src/services/search/search_engine.dart';
 import 'package:analyzer/dart/ast/ast.dart' as engine;
 import 'package:analyzer/dart/element/element.dart' as engine;
 import 'package:analyzer/dart/element/type.dart' as engine;
+import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart' as engine;
 import 'package:analyzer/src/dart/error/lint_codes.dart';
 import 'package:analyzer/src/error/codes.dart' as engine;
 import 'package:analyzer/src/generated/source.dart' as engine;
-import 'package:analyzer_plugin/protocol/protocol_common.dart';
 import 'package:test/test.dart';
 import 'package:test_reflective_loader/test_reflective_loader.dart';
 
@@ -264,6 +265,18 @@
 
   MockAnalysisError(
       this.source, this.errorCode, this.offset, this.length, this.message);
+
+  @override
+  List<DiagnosticMessage> get contextMessages => null;
+
+  @override
+  String get correctionMessage => null;
+
+  @override
+  DiagnosticMessage get problemMessage => null;
+
+  @override
+  Severity get severity => null;
 }
 
 class MockErrorCode implements engine.ErrorCode {
diff --git a/pkg/analysis_server/test/services/linter/linter_test.dart b/pkg/analysis_server/test/services/linter/linter_test.dart
index 3965bba..14b0054 100644
--- a/pkg/analysis_server/test/services/linter/linter_test.dart
+++ b/pkg/analysis_server/test/services/linter/linter_test.dart
@@ -76,5 +76,8 @@
 
 class _TestSource implements Source {
   @override
+  String get fullName => '/package/lib/test.dart';
+
+  @override
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
diff --git a/pkg/analyzer/lib/diagnostic/diagnostic.dart b/pkg/analyzer/lib/diagnostic/diagnostic.dart
new file mode 100644
index 0000000..43b099a
--- /dev/null
+++ b/pkg/analyzer/lib/diagnostic/diagnostic.dart
@@ -0,0 +1,49 @@
+// 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.
+
+/// A diagnostic, as defined by the [Diagnostic Design Guidelines][guidelines]:
+///
+/// > An indication of a specific problem at a specific location within the
+/// > source code being processed by a development tool.
+///
+/// Clients may not extend, implement or mix-in this class.
+///
+/// [guidelines]: ../doc/diagnostics.md
+abstract class Diagnostic {
+  /// A list of messages that provide context for understanding the problem
+  /// being reported. The list will be empty if there are no such messages.
+  List<DiagnosticMessage> get contextMessages;
+
+  /// A description of how to fix the problem, or `null` if there is no such
+  /// description.
+  String get correctionMessage;
+
+  /// A message describing what is wrong and why.
+  DiagnosticMessage get problemMessage;
+
+  /// The severity associated with the diagnostic.
+  Severity get severity;
+}
+
+/// A single message associated with a [Diagnostic], consisting of the text of
+/// the message and the location associated with it.
+///
+/// Clients may not extend, implement or mix-in this class.
+abstract class DiagnosticMessage {
+  /// The absolute and normalized path of the file associated with this message.
+  String get filePath;
+
+  /// The length of the source range associated with this message.
+  int get length;
+
+  /// The text of the message.
+  String get message;
+
+  /// The zero-based offset from the start of the file to the beginning of the
+  /// source range associated with this message.
+  int get offset;
+}
+
+/// An indication of the severity of a [Diagnostic].
+enum Severity { error, warning, info }
diff --git a/pkg/analyzer/lib/error/error.dart b/pkg/analyzer/lib/error/error.dart
index 02b5923..59e0168 100644
--- a/pkg/analyzer/lib/error/error.dart
+++ b/pkg/analyzer/lib/error/error.dart
@@ -4,8 +4,10 @@
 
 import 'dart:collection';
 
+import 'package:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/listener.dart';
 import 'package:analyzer/src/dart/scanner/scanner.dart' show ScannerErrorCode;
+import 'package:analyzer/src/diagnostic/diagnostic.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/java_core.dart';
 import 'package:analyzer/src/generated/parser.dart' show ParserErrorCode;
@@ -750,7 +752,7 @@
  *
  * See [AnalysisErrorListener].
  */
-class AnalysisError {
+class AnalysisError implements Diagnostic {
   /**
    * An empty array of errors used when no errors are expected.
    */
@@ -788,10 +790,9 @@
    */
   final ErrorCode errorCode;
 
-  /**
-   * The localized error message.
-   */
-  String _message;
+  DiagnosticMessage _problemMessage;
+
+  List<DiagnosticMessage> _contextMessages;
 
   /**
    * The correction to be displayed for this error, or `null` if there is no
@@ -805,43 +806,40 @@
   final Source source;
 
   /**
-   * The character offset from the beginning of the source (zero based) where
-   * the error occurred.
-   */
-  int offset = 0;
-
-  /**
-   * The number of characters from the offset to the end of the source which
-   * encompasses the compilation error.
-   */
-  int length = 0;
-
-  /**
-   * A flag indicating whether this error can be shown to be a non-issue because
-   * of the result of type propagation.
-   */
-  bool isStaticOnly = false;
-
-  /**
    * Initialize a newly created analysis error. The error is associated with the
    * given [source] and is located at the given [offset] with the given
    * [length]. The error will have the given [errorCode] and the list of
-   * [arguments] will be used to complete the message.
+   * [arguments] will be used to complete the message and correction. If any
+   * [contextMessages] are provided, they will be recorded with the error.
    */
-  AnalysisError(this.source, this.offset, this.length, this.errorCode,
-      [List<Object> arguments]) {
-    this._message = formatList(errorCode.message, arguments);
+  AnalysisError(this.source, int offset, int length, this.errorCode,
+      [List<Object> arguments, List<DiagnosticMessage> contextMessages]) {
+    String message = formatList(errorCode.message, arguments);
     String correctionTemplate = errorCode.correction;
     if (correctionTemplate != null) {
       this._correction = formatList(correctionTemplate, arguments);
     }
+    _problemMessage = new DiagnosticMessageImpl(
+        filePath: source?.fullName,
+        length: length,
+        message: message,
+        offset: offset);
+    _contextMessages = contextMessages;
   }
 
   /**
    * Initialize a newly created analysis error with given values.
    */
-  AnalysisError.forValues(this.source, this.offset, this.length, this.errorCode,
-      this._message, this._correction);
+  AnalysisError.forValues(this.source, int offset, int length, this.errorCode,
+      String message, this._correction) {
+    _problemMessage = new DiagnosticMessageImpl(
+        filePath: source?.fullName,
+        length: length,
+        message: message,
+        offset: offset);
+  }
+
+  List<DiagnosticMessage> get contextMessages => _contextMessages ?? const [];
 
   /**
    * Return the template used to create the correction to be displayed for this
@@ -851,18 +849,70 @@
   String get correction => _correction;
 
   @override
+  String get correctionMessage => _correction;
+
+  @override
   int get hashCode {
     int hashCode = offset;
-    hashCode ^= (_message != null) ? _message.hashCode : 0;
+    hashCode ^= (message != null) ? message.hashCode : 0;
     hashCode ^= (source != null) ? source.hashCode : 0;
     return hashCode;
   }
 
   /**
+   * Return `true` if this error can be shown to be a non-issue because of the
+   * result of type propagation.
+   */
+  @Deprecated(
+      'Type propagation is no longer performed, so this will never be true')
+  bool get isStaticOnly => false;
+
+  @Deprecated(
+      'Type propagation is no longer performed, so this can never be true')
+  void set isStaticOnly(bool value) {}
+
+  /**
+   * The number of characters from the offset to the end of the source which
+   * encompasses the compilation error.
+   */
+  int get length => _problemMessage.length;
+
+  /**
    * Return the message to be displayed for this error. The message should
    * indicate what is wrong and why it is wrong.
    */
-  String get message => _message;
+  String get message => _problemMessage.message;
+
+  /**
+   * The character offset from the beginning of the source (zero based) where
+   * the error occurred.
+   */
+  int get offset => _problemMessage.offset;
+
+  /**
+   * The character offset from the beginning of the source (zero based) where
+   * the error occurred.
+   */
+  @Deprecated('Set the offset when the error is created')
+  set offset(int offset) {}
+
+  @override
+  DiagnosticMessage get problemMessage => _problemMessage;
+
+  @override
+  Severity get severity {
+    switch (errorCode.errorSeverity) {
+      case ErrorSeverity.ERROR:
+        return Severity.error;
+      case ErrorSeverity.WARNING:
+        return Severity.warning;
+      case ErrorSeverity.INFO:
+        return Severity.info;
+      default:
+        throw new StateError(
+            'Invalid error severity: ${errorCode.errorSeverity}');
+    }
+  }
 
   @override
   bool operator ==(Object other) {
@@ -878,11 +928,8 @@
       if (offset != other.offset || length != other.length) {
         return false;
       }
-      if (isStaticOnly != other.isStaticOnly) {
-        return false;
-      }
       // Deep checks.
-      if (_message != other._message) {
+      if (message != other.message) {
         return false;
       }
       if (source != other.source) {
@@ -904,7 +951,7 @@
     buffer.write(offset + length - 1);
     buffer.write("): ");
     //buffer.write("(" + lineNumber + ":" + columnNumber + "): ");
-    buffer.write(_message);
+    buffer.write(message);
     return buffer.toString();
   }
 
diff --git a/pkg/analyzer/lib/src/diagnostic/diagnostic.dart b/pkg/analyzer/lib/src/diagnostic/diagnostic.dart
new file mode 100644
index 0000000..d45ad279
--- /dev/null
+++ b/pkg/analyzer/lib/src/diagnostic/diagnostic.dart
@@ -0,0 +1,30 @@
+// 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:analyzer/diagnostic/diagnostic.dart';
+import 'package:meta/meta.dart';
+
+/// A concrete implementation of a diagnostic message.
+class DiagnosticMessageImpl implements DiagnosticMessage {
+  @override
+  final String filePath;
+
+  @override
+  final int length;
+
+  @override
+  final String message;
+
+  @override
+  final int offset;
+
+  /// Initialize a newly created message to represent a [message] reported in
+  /// the file at the given [filePath] at the given [offset] and with the given
+  /// [length].
+  DiagnosticMessageImpl(
+      {@required this.filePath,
+      @required this.length,
+      @required this.message,
+      @required this.offset});
+}
diff --git a/pkg/analyzer/lib/src/diagnostic/diagnostic_factory.dart b/pkg/analyzer/lib/src/diagnostic/diagnostic_factory.dart
new file mode 100644
index 0000000..7b359dd
--- /dev/null
+++ b/pkg/analyzer/lib/src/diagnostic/diagnostic_factory.dart
@@ -0,0 +1,49 @@
+// 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:analyzer/dart/ast/ast.dart';
+import 'package:analyzer/dart/element/element.dart';
+import 'package:analyzer/diagnostic/diagnostic.dart';
+import 'package:analyzer/error/error.dart';
+import 'package:analyzer/source/line_info.dart';
+import 'package:analyzer/src/diagnostic/diagnostic.dart';
+import 'package:analyzer/src/error/codes.dart';
+import 'package:analyzer/src/generated/source.dart';
+
+/// A factory used to create diagnostics.
+class DiagnosticFactory {
+  /// Initialize a newly created diagnostic factory.
+  DiagnosticFactory();
+
+  /// Return a diagnostic indicating that the given [identifier] was referenced
+  /// before it was declared.
+  AnalysisError referencedBeforeDeclaration(
+      Source source, Identifier identifier,
+      {Element element}) {
+    String name = identifier.name;
+    Element staticElement = element ?? identifier.staticElement;
+    List<DiagnosticMessage> contextMessages;
+    int declarationOffset = staticElement.nameOffset;
+    if (declarationOffset >= 0 && staticElement != null) {
+      CompilationUnitElement unit = staticElement
+          .getAncestor((element) => element is CompilationUnitElement);
+      CharacterLocation location = unit.lineInfo.getLocation(declarationOffset);
+      contextMessages = [
+        new DiagnosticMessageImpl(
+            filePath: source.fullName,
+            message:
+                "The declaration of '$name' is on line ${location.lineNumber}.",
+            offset: declarationOffset,
+            length: staticElement.nameLength)
+      ];
+    }
+    return new AnalysisError(
+        source,
+        identifier.offset,
+        identifier.length,
+        CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
+        [name],
+        contextMessages);
+  }
+}
diff --git a/pkg/analyzer/lib/src/generated/error_verifier.dart b/pkg/analyzer/lib/src/generated/error_verifier.dart
index fa6ce99..f1d4d06 100644
--- a/pkg/analyzer/lib/src/generated/error_verifier.dart
+++ b/pkg/analyzer/lib/src/generated/error_verifier.dart
@@ -21,6 +21,7 @@
 import 'package:analyzer/src/dart/element/inheritance_manager2.dart';
 import 'package:analyzer/src/dart/element/member.dart';
 import 'package:analyzer/src/dart/element/type.dart';
+import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/error/literal_element_verifier.dart';
 import 'package:analyzer/src/error/pending_error.dart';
@@ -4994,10 +4995,8 @@
         _hiddenElements != null &&
         _hiddenElements.contains(node.staticElement) &&
         node.parent is! CommentReference) {
-      _errorReporter.reportErrorForNode(
-          CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
-          node,
-          [node.name]);
+      _errorReporter.reportError(new DiagnosticFactory()
+          .referencedBeforeDeclaration(_errorReporter.source, node));
     }
   }
 
diff --git a/pkg/analyzer/lib/src/generated/resolver.dart b/pkg/analyzer/lib/src/generated/resolver.dart
index bc9d8dc..94b83c9 100644
--- a/pkg/analyzer/lib/src/generated/resolver.dart
+++ b/pkg/analyzer/lib/src/generated/resolver.dart
@@ -27,6 +27,7 @@
 import 'package:analyzer/src/dart/element/type.dart';
 import 'package:analyzer/src/dart/resolver/exit_detector.dart';
 import 'package:analyzer/src/dart/resolver/scope.dart';
+import 'package:analyzer/src/diagnostic/diagnostic_factory.dart';
 import 'package:analyzer/src/error/codes.dart';
 import 'package:analyzer/src/generated/constant.dart';
 import 'package:analyzer/src/generated/element_resolver.dart';
@@ -6594,8 +6595,8 @@
         } else if (element is LocalVariableElement ||
             (element is FunctionElement &&
                 element.enclosingElement is ExecutableElement)) {
-          reportErrorForNode(CompileTimeErrorCode.REFERENCED_BEFORE_DECLARATION,
-              typeName, [typeName.name]);
+          errorListener.onError(new DiagnosticFactory()
+              .referencedBeforeDeclaration(source, typeName, element: element));
         } else {
           reportErrorForNode(
               StaticWarningCode.NOT_A_TYPE, typeName, [typeName.name]);
diff --git a/pkg/analyzer/lib/src/summary2/ast_resolver.dart b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
index 12ef69d..bf3ac6d 100644
--- a/pkg/analyzer/lib/src/summary2/ast_resolver.dart
+++ b/pkg/analyzer/lib/src/summary2/ast_resolver.dart
@@ -66,5 +66,9 @@
 }
 
 class _FakeSource implements Source {
+  @override
+  String get fullName => '/package/lib/test.dart';
+
+  @override
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
diff --git a/pkg/analyzer_cli/test/driver_test.dart b/pkg/analyzer_cli/test/driver_test.dart
index 054521d..fa1cd22 100644
--- a/pkg/analyzer_cli/test/driver_test.dart
+++ b/pkg/analyzer_cli/test/driver_test.dart
@@ -789,24 +789,6 @@
     });
   }
 
-  test_pubspecYaml() async {
-    await withTempDirAsync((tempDir) async {
-      String filePath = path.join(tempDir, AnalysisEngine.PUBSPEC_YAML_FILE);
-      new File(filePath).writeAsStringSync('''
-name: foo
-flutter:
-  assets:
-    doesNotExist.gif
-''');
-      await drive(filePath);
-      expect(
-          bulletToDash(outSink),
-          contains(
-              "warning - The value of the 'asset' field is expected to be a list of relative file paths"));
-      expect(exitCode, 0);
-    });
-  }
-
   test_manifestFileChecks() async {
     await withTempDirAsync((tempDir) async {
       String filePath =
@@ -830,6 +812,24 @@
       expect(exitCode, 0);
     });
   }
+
+  test_pubspecYaml() async {
+    await withTempDirAsync((tempDir) async {
+      String filePath = path.join(tempDir, AnalysisEngine.PUBSPEC_YAML_FILE);
+      new File(filePath).writeAsStringSync('''
+name: foo
+flutter:
+  assets:
+    doesNotExist.gif
+''');
+      await drive(filePath);
+      expect(
+          bulletToDash(outSink),
+          contains(
+              "warning - The value of the 'asset' field is expected to be a list of relative file paths"));
+      expect(exitCode, 0);
+    });
+  }
 }
 
 @reflectiveTest
@@ -965,5 +965,8 @@
   TestSource();
 
   @override
+  String get fullName => '/package/lib/test.dart';
+
+  @override
   noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
 }
diff --git a/pkg/analyzer_cli/test/mocks.dart b/pkg/analyzer_cli/test/mocks.dart
index 3d3ad59..ab4e74f 100644
--- a/pkg/analyzer_cli/test/mocks.dart
+++ b/pkg/analyzer_cli/test/mocks.dart
@@ -2,6 +2,7 @@
 // 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:analyzer/diagnostic/diagnostic.dart';
 import 'package:analyzer/error/error.dart';
 import 'package:analyzer/source/line_info.dart';
 import 'package:analyzer/src/generated/engine.dart';
@@ -30,7 +31,19 @@
   MockAnalysisError(this.source, this.errorCode, this.offset, this.message);
 
   @override
+  List<DiagnosticMessage> get contextMessages => null;
+
+  @override
   String get correction => null;
+
+  @override
+  String get correctionMessage => null;
+
+  @override
+  DiagnosticMessage get problemMessage => null;
+
+  @override
+  Severity get severity => null;
 }
 
 class MockAnalysisErrorInfo implements AnalysisErrorInfo {