Allow running tests by line and column (#1604)

Fixes https://github.com/dart-lang/test/issues/1579

- Allows querying tests by line & col using test path query strings
- Matches if any frame in the test stack trace matches up (this allows for lots of flexibility in terms of testing styles and parameterized tests).
- If a stack trace formatter is set up for the remote platform, it will use it to format the `trace` sent back for groups/tests that are discovered (enabling browser/node support). 
diff --git a/pkgs/test/CHANGELOG.md b/pkgs/test/CHANGELOG.md
index 7d093a5..ecdaee2 100644
--- a/pkgs/test/CHANGELOG.md
+++ b/pkgs/test/CHANGELOG.md
@@ -1,9 +1,11 @@
 ## 1.19.0-dev
 
-* Support query parameters `name` and `full-name` on test paths, which will
-  apply the filters to only those test suites.
+* Support query parameters `name`, `full-name`, `line`, and `col` on test paths,
+  which will apply the filters to only those test suites.
   * All specified filters must match for a test to run.
   * Global filters (ie: `--name`) are also still respected and must match.
+  * The `line` and `col` will match if any frame from the test trace matches
+    (the test trace is the current stack trace where `test` is invoked).
 * Give a better exception when using `markTestSkipped` outside of a test.
 
 ## 1.18.2
diff --git a/pkgs/test/README.md b/pkgs/test/README.md
index 789cc5f..c63d7fc 100644
--- a/pkgs/test/README.md
+++ b/pkgs/test/README.md
@@ -173,10 +173,6 @@
 will be run. You can also use the `-N` flag to run tests whose names contain a
 plain-text string.
 
-Alternatively, you can filter tests by name only for a specific file/directory
-by specifying a query on a path: `dart test "path/to/test.dart?name=test name"`.
-That ensures that the name filters applies to one path but not others.
-
 By default, tests are run in the Dart VM, but you can run them in the browser as
 well by passing `dart test -p chrome path/to/test.dart`. `test` will take
 care of starting the browser and loading the tests, and all the results will be
@@ -184,6 +180,34 @@
 tests on both platforms with a single command: `dart test -p "chrome,vm"
 path/to/test.dart`.
 
+### Test Path Queries
+
+Some query parameters are supported on test paths, which allow you to filter the
+tests that will run within just those paths. These filters are merged with any
+global options that are passed, and all filters must match for a test to be ran.
+
+- **name**: Works the same as `--name` (simple contains check).
+  - This is the only option that supports more than one entry.
+- **full-name**: Requires an exact match for the name of the test.
+- **line**: Matches any test that originates from this line in the test suite.
+- **col**: Matches any test that originates from this column in the test suite.
+
+**Example Usage**: `dart test "path/to/test.dart?line=10&col=2"`
+
+#### Line/Col Matching Semantics
+
+The `line` and `col` filters match against the current stack trace taken from
+the invocation to the `test` function, and are considered a match if
+**any frame** in the trace meets **all** of the following criteria:
+
+* The URI of the frame matches the root test suite uri.
+  * This means it will not match lines from imported libraries.
+* If both `line` and `col` are passed, both must match **the same frame**.
+* The specific `line` and `col` to be matched are defined by the tools creating
+  the stack trace. This generally means they are 1 based and not 0 based, but
+  this package is not in control of the exact semantics and they may vary based
+  on platform implementations.
+
 ### Sharding Tests
 Tests can also be sharded with the `--total-shards` and `--shard-index` arguments,
 allowing you to split up your test suites and run them separately. For example,
diff --git a/pkgs/test/test/runner/line_and_col_test.dart b/pkgs/test/test/runner/line_and_col_test.dart
new file mode 100644
index 0000000..94b5a83
--- /dev/null
+++ b/pkgs/test/test/runner/line_and_col_test.dart
@@ -0,0 +1,365 @@
+// Copyright (c) 2021, 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.
+
+@TestOn('vm')
+
+import 'package:test_descriptor/test_descriptor.dart' as d;
+
+import 'package:test_core/src/util/exit_codes.dart' as exit_codes;
+import 'package:test/test.dart';
+
+import '../io.dart';
+
+void main() {
+  setUpAll(precompileTestExecutable);
+
+  group('with test.dart?line=<line> query', () {
+    test('selects test with the matching line', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+          test("b", () => throw TestFailure("oh no"));
+          test("c", () {});
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=6']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('selects multiple tests on the same line', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {}); test("b", () {});
+          test("c", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=4']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+2: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('selects groups with a matching line', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          group("a", () {
+            test("b", () {});
+          });
+          group("b", () {
+            test("b", () => throw TestFailure("oh no"));
+          });
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=4']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('No matching tests', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=1']);
+
+      expect(
+        test.stderr,
+        emitsThrough(contains('No tests were found.')),
+      );
+
+      await test.shouldExit(exit_codes.noTestsRan);
+    });
+
+    test('allows the line anywhere in the stack trace', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void runTest(String name) {
+          test(name, () {});
+        }
+
+        void main() {
+          runTest("a");
+          test("b", () {});
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=8']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+  });
+
+  group('with test.dart?col=<col> query', () {
+    test('selects single test with the matching column', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+            test("b", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?col=11']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('selects multiple tests starting on the same column', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+          test("b", () {});
+            test("c", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?col=11']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+2: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('selects groups with a matching column', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          group("a", () {
+            test("b", () {});
+          });
+            group("b", () {
+              test("b", () => throw TestFailure("oh no"));
+            });
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?col=11']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('No matching tests', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?col=1']);
+
+      expect(
+        test.stderr,
+        emitsThrough(contains('No tests were found.')),
+      );
+
+      await test.shouldExit(exit_codes.noTestsRan);
+    });
+
+    test('allows the col anywhere in the stack trace', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void runTest(String name) {
+          test(name, () {});
+        }
+
+        void main() {
+            runTest("a");
+          test("b", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?col=13']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+  });
+
+  group('with test.dart?line=<line>&col=<col> query', () {
+    test('selects test with the matching line and col in the same frame',
+        () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          void runTests() {
+          test("a", () {});test("b", () => throw TestFailure("oh no"));
+          }
+          runTests();
+          test("c", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=5&col=11']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('selects group with the matching line and col', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          group("a", () {
+            test("b", () {});
+            test("c", () {});
+          });
+          group("d", () {
+            test("e", () => throw TestFailure("oh no"));
+          });
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=4&col=11']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+2: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('no matching tests - col doesnt match', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=4&col=1']);
+
+      expect(
+        test.stderr,
+        emitsThrough(contains('No tests were found.')),
+      );
+
+      await test.shouldExit(exit_codes.noTestsRan);
+    });
+
+    test('no matching tests - line doesnt match', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=1&col=11']);
+
+      expect(
+        test.stderr,
+        emitsThrough(contains('No tests were found.')),
+      );
+
+      await test.shouldExit(exit_codes.noTestsRan);
+    });
+
+    test('supports browser tests', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+          test("b", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=4&col=11', '-p', 'chrome']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+
+    test('supports node tests', () async {
+      await d.file('test.dart', '''
+        import 'package:test/test.dart';
+
+        void main() {
+          test("a", () {});
+          test("b", () => throw TestFailure("oh no"));
+        }
+      ''').create();
+
+      var test = await runTest(['test.dart?line=4&col=11', '-p', 'node']);
+
+      expect(
+        test.stdout,
+        emitsThrough(contains('+1: All tests passed!')),
+      );
+
+      await test.shouldExit(0);
+    });
+  });
+}
diff --git a/pkgs/test/test/utils.dart b/pkgs/test/test/utils.dart
index 52661b3..e14fa51 100644
--- a/pkgs/test/test/utils.dart
+++ b/pkgs/test/test/utils.dart
@@ -227,6 +227,8 @@
         BooleanSelector? excludeTags,
         Map<BooleanSelector, SuiteConfiguration>? tags,
         Map<PlatformSelector, SuiteConfiguration>? onPlatform,
+        int? line,
+        int? col,
 
         // Test-level configuration
         Timeout? timeout,
@@ -250,6 +252,8 @@
         excludeTags: excludeTags,
         tags: tags,
         onPlatform: onPlatform,
+        line: line,
+        col: col,
         timeout: timeout,
         verboseTrace: verboseTrace,
         chainStackTraces: chainStackTraces,
diff --git a/pkgs/test_api/CHANGELOG.md b/pkgs/test_api/CHANGELOG.md
index ef875ca..0c5cd35 100644
--- a/pkgs/test_api/CHANGELOG.md
+++ b/pkgs/test_api/CHANGELOG.md
@@ -1,6 +1,8 @@
 ## 0.4.6-dev
 
 * Give a better exception when using `markTestSkipped` outside of a test.
+* Format stack traces if a formatter is available when serializing tests
+  and groups from the remote listener.
 
 ## 0.4.5
 
diff --git a/pkgs/test_api/lib/src/backend/remote_listener.dart b/pkgs/test_api/lib/src/backend/remote_listener.dart
index 20b00c8..65a4ca5 100644
--- a/pkgs/test_api/lib/src/backend/remote_listener.dart
+++ b/pkgs/test_api/lib/src/backend/remote_listener.dart
@@ -187,7 +187,12 @@
       'type': 'group',
       'name': group.name,
       'metadata': group.metadata.serialize(),
-      'trace': group.trace?.toString(),
+      'trace': group.trace == null
+          ? null
+          : StackTraceFormatter.current
+                  ?.formatStackTrace(group.trace!)
+                  .toString() ??
+              group.trace?.toString(),
       'setUpAll': _serializeTest(channel, group.setUpAll, parents),
       'tearDownAll': _serializeTest(channel, group.tearDownAll, parents),
       'entries': group.entries.map((entry) {
@@ -217,7 +222,12 @@
       'type': 'test',
       'name': test.name,
       'metadata': test.metadata.serialize(),
-      'trace': test.trace?.toString(),
+      'trace': test.trace == null
+          ? null
+          : StackTraceFormatter.current
+                  ?.formatStackTrace(test.trace!)
+                  .toString() ??
+              test.trace?.toString(),
       'channel': testChannel.id
     };
   }
diff --git a/pkgs/test_core/CHANGELOG.md b/pkgs/test_core/CHANGELOG.md
index 8585d3c..d735b63 100644
--- a/pkgs/test_core/CHANGELOG.md
+++ b/pkgs/test_core/CHANGELOG.md
@@ -1,9 +1,11 @@
 ## 0.4.6-dev
 
-* Support query parameters `name` and `full-name` on test paths, which will
-  apply the filters to only those test suites.
+* Support query parameters `name`, `full-name`, `line`, and `col` on test paths,
+  which will apply the filters to only those test suites.
   * All specified filters must match for a test to run.
   * Global filters (ie: `--name`) are also still respected and must match.
+  * The `line` and `col` will match if any frame from the test trace matches
+    (the test trace is the current stack trace where `test` is invoked).
 * Support the latest `test_api`.
 
 ## 0.4.5
diff --git a/pkgs/test_core/lib/src/runner.dart b/pkgs/test_core/lib/src/runner.dart
index a9d0651..8828019 100644
--- a/pkgs/test_core/lib/src/runner.dart
+++ b/pkgs/test_core/lib/src/runner.dart
@@ -7,6 +7,8 @@
 
 import 'package:async/async.dart';
 import 'package:boolean_selector/boolean_selector.dart';
+import 'package:path/path.dart' as p;
+import 'package:stack_trace/stack_trace.dart';
 // ignore: deprecated_member_use
 import 'package:test_api/backend.dart'
     show PlatformSelector, Runtime, SuitePlatform;
@@ -256,14 +258,14 @@
   /// suites once they're loaded.
   Stream<LoadSuite> _loadSuites() {
     return StreamGroup.merge(_config.paths.map((pathConfig) {
-      final suiteConfig = pathConfig.testPatterns == null
-          ? _config.suiteDefaults
-          : _config.suiteDefaults.change(
-              patterns: [
-                ..._config.suiteDefaults.patterns,
-                ...pathConfig.testPatterns!
-              ],
-            );
+      final suiteConfig = _config.suiteDefaults.change(
+        patterns: [
+          ..._config.suiteDefaults.patterns,
+          ...?pathConfig.testPatterns
+        ],
+        line: pathConfig.line,
+        col: pathConfig.col,
+      );
 
       if (Directory(pathConfig.testPath).existsSync()) {
         return _loader.loadDir(pathConfig.testPath, suiteConfig);
@@ -298,6 +300,43 @@
             return false;
           }
 
+          // Skip tests that don't start on `line` or `col` if specified.
+          var line = suite.config.line;
+          var col = suite.config.col;
+          if (line != null || col != null) {
+            var trace = test.trace;
+            if (trace == null) {
+              throw StateError(
+                  'Cannot filter by line/column for this test suite, no stack'
+                  'trace available.');
+            }
+            var path = suite.path;
+            if (path == null) {
+              throw StateError(
+                  'Cannot filter by line/column for this test suite, no suite'
+                  'path available.');
+            }
+            var absoluteSuitePath = p.absolute(path);
+
+            bool matchLineAndCol(Frame frame) {
+              if (frame.uri.scheme != 'file' ||
+                  frame.uri.toFilePath() != absoluteSuitePath) {
+                return false;
+              }
+              if (line != null && frame.line != line) {
+                return false;
+              }
+              if (col != null && frame.column != col) {
+                return false;
+              }
+              return true;
+            }
+
+            if (!trace.frames.any(matchLineAndCol)) {
+              return false;
+            }
+          }
+
           return true;
         }));
       });
diff --git a/pkgs/test_core/lib/src/runner/configuration.dart b/pkgs/test_core/lib/src/runner/configuration.dart
index 3c061ea..6286db6 100644
--- a/pkgs/test_core/lib/src/runner/configuration.dart
+++ b/pkgs/test_core/lib/src/runner/configuration.dart
@@ -35,6 +35,8 @@
   const PathConfiguration({
     required this.testPath,
     this.testPatterns,
+    this.line,
+    this.col,
   });
 
   /// The explicit path to a test suite.
@@ -42,6 +44,12 @@
 
   /// Name filters specific to [testPath].
   final List<Pattern>? testPatterns;
+
+  /// Only run tests that originate from this line in the test suite.
+  final int? line;
+
+  /// Only run tests that originate from this column in the test suite.
+  final int? col;
 }
 
 /// A class that encapsulates the command-line configuration of the test runner.
@@ -342,6 +350,8 @@
             excludeTags: excludeTags,
             tags: tags,
             onPlatform: onPlatform,
+            line: null, // Only configurable from the command line
+            col: null, // Only configurable from the command line
 
             // Test-level configuration
             timeout: timeout,
diff --git a/pkgs/test_core/lib/src/runner/configuration/args.dart b/pkgs/test_core/lib/src/runner/configuration/args.dart
index c9cfe9d..71f4da3 100644
--- a/pkgs/test_core/lib/src/runner/configuration/args.dart
+++ b/pkgs/test_core/lib/src/runner/configuration/args.dart
@@ -179,6 +179,8 @@
 
   final names = uri.queryParametersAll['name'];
   final fullName = uri.queryParameters['full-name'];
+  final line = uri.queryParameters['line'];
+  final col = uri.queryParameters['col'];
 
   if (names != null && names.isNotEmpty && fullName != null) {
     throw FormatException(
@@ -191,6 +193,8 @@
     testPatterns: fullName != null
         ? [RegExp('^${RegExp.escape(fullName)}\$')]
         : names?.map((name) => RegExp(name)).toList(),
+    line: line == null ? null : int.parse(line),
+    col: col == null ? null : int.parse(col),
   );
 }
 
diff --git a/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart b/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
index 5d4f5ab..2c9ea13 100644
--- a/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
+++ b/pkgs/test_core/lib/src/runner/plugin/platform_helpers.dart
@@ -55,7 +55,9 @@
     'asciiGlyphs': Platform.isWindows,
     'path': path,
     'collectTraces': Configuration.current.reporter == 'json' ||
-        Configuration.current.fileReporters.containsKey('json'),
+        Configuration.current.fileReporters.containsKey('json') ||
+        suiteConfig.line != null ||
+        suiteConfig.col != null,
     'noRetry': Configuration.current.noRetry,
     'foldTraceExcept': Configuration.current.foldTraceExcept.toList(),
     'foldTraceOnly': Configuration.current.foldTraceOnly.toList(),
diff --git a/pkgs/test_core/lib/src/runner/suite.dart b/pkgs/test_core/lib/src/runner/suite.dart
index 301d415..115fd7d 100644
--- a/pkgs/test_core/lib/src/runner/suite.dart
+++ b/pkgs/test_core/lib/src/runner/suite.dart
@@ -36,7 +36,9 @@
       excludeTags: null,
       tags: null,
       onPlatform: null,
-      metadata: null);
+      metadata: null,
+      line: null,
+      col: null);
 
   /// Whether or not duplicate test (or group) names are allowed within the same
   /// test suite.
@@ -132,6 +134,12 @@
       });
   Set<String>? _knownTags;
 
+  /// Only run tests that originate from this line in a test file.
+  final int? line;
+
+  /// Only run tests that original from this column in a test file.
+  final int? col;
+
   factory SuiteConfiguration(
       {required bool? allowDuplicateTestNames,
       required bool? allowTestRandomization,
@@ -145,6 +153,8 @@
       required BooleanSelector? excludeTags,
       required Map<BooleanSelector, SuiteConfiguration>? tags,
       required Map<PlatformSelector, SuiteConfiguration>? onPlatform,
+      required int? line,
+      required int? col,
 
       // Test-level configuration
       required Timeout? timeout,
@@ -168,6 +178,8 @@
         excludeTags: excludeTags,
         tags: tags,
         onPlatform: onPlatform,
+        line: line,
+        col: col,
         metadata: Metadata(
             timeout: timeout,
             verboseTrace: verboseTrace,
@@ -197,6 +209,8 @@
           BooleanSelector? excludeTags,
           Map<BooleanSelector, SuiteConfiguration>? tags,
           Map<PlatformSelector, SuiteConfiguration>? onPlatform,
+          int? line,
+          int? col,
 
           // Test-level configuration
           Timeout? timeout,
@@ -220,6 +234,8 @@
           excludeTags: excludeTags,
           tags: tags,
           onPlatform: onPlatform,
+          line: line,
+          col: col,
           timeout: timeout,
           verboseTrace: verboseTrace,
           chainStackTraces: chainStackTraces,
@@ -258,7 +274,9 @@
       required BooleanSelector? excludeTags,
       required Map<BooleanSelector, SuiteConfiguration>? tags,
       required Map<PlatformSelector, SuiteConfiguration>? onPlatform,
-      required Metadata? metadata})
+      required Metadata? metadata,
+      required this.line,
+      required this.col})
       : _allowDuplicateTestNames = allowDuplicateTestNames,
         _allowTestRandomization = allowTestRandomization,
         _jsTrace = jsTrace,
@@ -291,6 +309,8 @@
         runtimes: null,
         includeTags: null,
         excludeTags: null,
+        line: null,
+        col: null,
       );
 
   /// Returns an unmodifiable copy of [input].
@@ -333,6 +353,8 @@
         excludeTags: excludeTags.union(other.excludeTags),
         tags: _mergeConfigMaps(tags, other.tags),
         onPlatform: _mergeConfigMaps(onPlatform, other.onPlatform),
+        line: other.line ?? line,
+        col: other.col ?? col,
         metadata: metadata.merge(other.metadata));
     return config._resolveTags();
   }
@@ -354,6 +376,8 @@
       BooleanSelector? excludeTags,
       Map<BooleanSelector, SuiteConfiguration>? tags,
       Map<PlatformSelector, SuiteConfiguration>? onPlatform,
+      int? line,
+      int? col,
 
       // Test-level configuration
       Timeout? timeout,
@@ -379,6 +403,8 @@
         excludeTags: excludeTags ?? this.excludeTags,
         tags: tags ?? this.tags,
         onPlatform: onPlatform ?? this.onPlatform,
+        line: line ?? this.line,
+        col: col ?? this.col,
         metadata: _metadata.change(
             timeout: timeout,
             verboseTrace: verboseTrace,