Skip to content

Conversation

@dnfield
Copy link
Contributor

@dnfield dnfield commented Jan 7, 2020

Description

Adds a StackTrace parser (fixes #4021) and fixes up the way we print out "this is likely a bug in the framework" message (fixes #18412).

The main goal here is to change the way we print out requests to file a bug on assertion errors. It currently always prints that message because Dart has changed the way it throws assertions (there's an additional frame before the one we used to look for). However, even fixing that still isn't quite right. The new logic here will only print the message on an AssertionError that has at least two frames of framework code before any other user code - the idea being that we either need to add a new assert to the framework, or stop violating our own asserts in such a case.

Related Issues

#4021
#18412

Tests

I added the following tests:

Tests for stack parsing
Tests for checking our fault/not our fault on stack traces.

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the Flutter Style Guide, including Features we expect every widget to implement.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation (doc comments with ///).
  • All existing and new tests are passing.
  • The analyzer (flutter analyze --flutter-repo) does not report any problems on my PR.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read Handling breaking changes.

  • We do not consider this a breaking change, because although it changes tests, it's only changing the shape of an error message, and so shouldn't nbe considered breaking (/cc @Hixie)

@dnfield dnfield requested review from Hixie and goderbauer January 7, 2020 20:53
@fluttergithubbot fluttergithubbot added the framework flutter/packages/flutter repository. See also f: labels. label Jan 7, 2020
@dnfield dnfield requested a review from gspencergoog January 7, 2020 20:54
final StackTrace sampleStack = await getSampleStack();

test('Error reporting - pretest', () async {
setUp(() async {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because we need this before any individual test. When we eventually randomize our tests this will be important.

};
});

tearDown(() async {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto. Without these changes, none of these tests can be run independently.


@override
bool operator ==(Object other) {
return other is StackFrame &&
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we should compare the runtimeType here; see style guide for a template

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. We should update our style guide - @a14n changed the way this works for removing the avoid_as lint and this + the runtimeType check is actually the convention used now.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please don't hesitate to update the style guide.

}

@override
String toString() => '$runtimeType{#$number, $packageScheme:$package/$packagePath:$line:$column, className: $className, method: $method}';
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

trivial nit: ( instead of { is our convention

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@Hixie
Copy link
Contributor

Hixie commented Jan 7, 2020

LGTM

@Hixie
Copy link
Contributor

Hixie commented Jan 7, 2020

If you want to overachieve even more, there's other code in the framework that does stack parsing, like defaultStackFilter.


@override
String toString() => '$runtimeType{#$number, $packageScheme:$package/$packagePath:$line:$column, className: $className, method: $method}';
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would be nice to have a way to print a frame in a manner that matches what the compiler does, so that you could take a List, filter it, then reprint it and it would come out in the same format as the original, just with some frames omitted.

also, check that this works on web and in AOT mode -- maybe the web stack has differently-shaped stack traces, and I know the AOT compiler has different details (e.g. omits line numbers in some cases?).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the line number thing was fixed (saw issues for that), but I'll check.

We could also just stuff the whole original string into the object instead of reconstructing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AOT is missing columns. Will fix.

Copy link
Member

@goderbauer goderbauer left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NICE!

/// final List<StackFrame> currentFrames = StackFrame.fromStackTrace(StackTrace.current);
/// ```
///
/// To create a traversable parsed stack.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe all text that's outside the dart code block will render above the code in dart docs. So this may look awkward there. I would rephrase this to have the prose text all above the code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done.


/// A stack frame representing an asynchronous suspension.
static const StackFrame asynchronousSuspension = StackFrame(
-1,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this also be a named parameter? (currently, I'd have to click through to StackFrame to learn what the -1 means.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had it as named but it felt awkward. I could make it named again.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could also just use nulls, fwiw.

@dnfield
Copy link
Contributor Author

dnfield commented Jan 8, 2020

I've updated this for defaultStackFilter.

@fluttergithubbot
Copy link
Contributor

This pull request is not suitable for automatic merging in its current state.

  • Please get at least one approved review before re-applying this label. Reviewers: If you left a comment approving, please use the "approve" review action instead.

@Hixie
Copy link
Contributor

Hixie commented Jan 8, 2020

LGTM
LGTM
LGTM

...silly bot doesn't like my stamps. *pout*

@dnfield
Copy link
Contributor Author

dnfield commented Jan 8, 2020

I may want to add one more thing to this before landing. I think we should keep the original source line - it would make resolving #4174 easier

@Hixie
Copy link
Contributor

Hixie commented Jan 8, 2020

You can also just do that in a separate PR (e.g. as part of #4174 if you're interested in that one).

@dnfield
Copy link
Contributor Author

dnfield commented Jan 8, 2020

Yeah I'll do that in a separate PR. I'm not sure I'll actually need the source line, but since the parsing is lossy it seems useful to have. Parsing stack traces shouldn't need to worry too much about an extra copy of a string anyway.

@@ -0,0 +1,230 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2020?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All files in this repo are dated to 2014 to keep things consistent

? RegExp(r'^(package:.+) (\d+):(\d+)\s+(.+)$')
: RegExp(r'^(.+) (\d+):(\d+)\s+(.+)$');
final Match match = parser.firstMatch(line);
assert(match != null, 'Expecgted $line to match $parser.');
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo: Expected

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw this right after landing :( will open a PR to fix, thanks for catching that

@dnfield dnfield merged commit 505af78 into flutter:master Jan 8, 2020
@dnfield dnfield deleted the stack_assert branch January 8, 2020 06:50
column == other.column &&
className == other.className &&
method == other.method &&
source == other.source;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I promoted a style it would be to prefer something like:

    return other is StackFrame
        && other.number == number
        && other.package == package
        && other.line == line
...

(starting line with && other.)

It's the pattern mostly used inside operator== methods.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is what the style guide actually now says - I just did it wrong here. I'll update it in the other PR.


test('Identifies user fault', () {
// User fault because they called `new Text(null)` from their own code.
final StackTrace stack = StackTrace.fromString('''#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The first line could also be on a newline for readability (the new line followin ''' in multiline string is ignored)

test('Identifies our fault', () {
// Our fault because we should either have an assertion in `text_helper.dart`
// or we should make sure not to pass bad values into new Text.
final StackTrace stack = StackTrace.fromString('''#0 _AssertionError._doThrowNew (dart:core-patch/errors_patch.dart:42:39)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Aug 2, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

framework flutter/packages/flutter repository. See also f: labels.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Hide "this could be a framework bug" message when first stack frame is not framework factor out the stack parsing nonsense into a reusable library

7 participants