Preserve stack traces for lint exceptions.

Currently, stack traces are overriden when exceptions are propagated
through a `LintRuleExceptionHandler`. This change allows a
`LintRuleExceptionHandler` to return `false`, indicating that callers
should rethrow the exception themselves, preserving the stack trace.

Change-Id: Ia7c97f01774d6e5d1b6cd8740cdd1128a0fa714e
Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/244942
Reviewed-by: Konstantin Shcheglov <scheglov@google.com>
Reviewed-by: Phil Quitslund <pquitslund@google.com>
Commit-Queue: Phil Quitslund <pquitslund@google.com>
diff --git a/pkg/analyzer/lib/src/dart/ast/utilities.dart b/pkg/analyzer/lib/src/dart/ast/utilities.dart
index 65a3293..f024ba6 100644
--- a/pkg/analyzer/lib/src/dart/ast/utilities.dart
+++ b/pkg/analyzer/lib/src/dart/ast/utilities.dart
@@ -1384,7 +1384,10 @@
 
   /// A method that can be passed to the `LinterVisitor` constructor to handle
   /// exceptions that occur during linting.
-  void logException(
+  ///
+  /// Returns `true` if the exception was fully handled, and `false` if the
+  /// exception should be rethrown.
+  bool logException(
       AstNode node, Object visitor, dynamic exception, StackTrace stackTrace) {
     StringBuffer buffer = StringBuffer();
     buffer.write('Exception while using a ${visitor.runtimeType} to visit a ');
@@ -1402,9 +1405,7 @@
     // TODO(39284): should this exception be silent?
     AnalysisEngine.instance.instrumentationService.logException(
         SilentException(buffer.toString(), exception, stackTrace));
-    if (propagateExceptions) {
-      throw exception;
-    }
+    return !propagateExceptions;
   }
 }
 
diff --git a/pkg/analyzer/lib/src/lint/linter_visitor.dart b/pkg/analyzer/lib/src/lint/linter_visitor.dart
index b08cc00..3383df0 100644
--- a/pkg/analyzer/lib/src/lint/linter_visitor.dart
+++ b/pkg/analyzer/lib/src/lint/linter_visitor.dart
@@ -9,7 +9,10 @@
 import 'package:analyzer/src/services/lint.dart';
 
 /// The type of the function that handles exceptions in lints.
-typedef LintRuleExceptionHandler = void Function(
+///
+/// Returns `true` if the exception was fully handled, or `false` if
+/// the exception should be rethrown.
+typedef LintRuleExceptionHandler = bool Function(
     AstNode node, LintRule linter, dynamic exception, StackTrace stackTrace);
 
 /// The AST visitor that runs handlers for nodes from the [registry].
@@ -769,7 +772,10 @@
       try {
         node.accept(subscription.visitor);
       } catch (exception, stackTrace) {
-        exceptionHandler(node, subscription.linter, exception, stackTrace);
+        if (!exceptionHandler(
+            node, subscription.linter, exception, stackTrace)) {
+          rethrow;
+        }
       }
       timer?.stop();
     }