Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add underlining to safe-init stack traces #14683

Merged
merged 1 commit into from
Apr 5, 2022

Conversation

Xavientois
Copy link
Contributor

@Xavientois Xavientois commented Mar 13, 2022

This underlines the source positions in the safe-init stack traces similarly to how the MessageRendering will underline its stack positions.

Before, a safe-init error might look like this:

[error] -- Error: /******/dotty/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala:1032:22
[error] 1032 |      "inlineVars" -> inlineVars
[error]      |                      ^^^^^^^^^^
[error]      |Cannot prove that the value is fully initialized. Only initialized values may be used as arguments
[error]      |
[error]      |The unsafe promotion may cause the following problem:
[error]      |Calling the external method method apply may cause initialization errors. Calling trace:
[error]      | -> "inlineVars" -> inlineVars	[ PatternMatcher.scala:1032 ]
[error]      |  -> Inliner(plan)	[ PatternMatcher.scala:700 ]
[error]      |   -> case plan: TestPlan => apply(plan)	[ PatternMatcher.scala:492 ]
[error]      |    -> plan.scrutinee = apply(plan.scrutinee)	[ PatternMatcher.scala:472 ]
[error]      |     -> def apply(tree: Tree): Tree = treeMap.transform(tree)	[ PatternMatcher.scala:470 ]
[error]      |      -> if (toDrop(sym)) transform(initializer(sym))	[ PatternMatcher.scala:675 ]

Now, the same error looks like this:

[error] -- Error: /******/dotty/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala:1032:22
[error] 1032 |      "inlineVars" -> inlineVars
[error]      |                      ^^^^^^^^^^
[error]      |Cannot prove that the value is fully initialized. Only initialized values may be used as arguments
[error]      |
[error]      |The unsafe promotion may cause the following problem:
[error]      |Calling the external method method apply may cause initialization errors. Calling trace:
[error]      |-> "inlineVars" -> inlineVars	[ PatternMatcher.scala:1032 ]
[error]      |                   ^^^^^^^^^^
[error]      |-> Inliner(plan)	[ PatternMatcher.scala:700 ]
[error]      |   ^^^^^^^^^^^^^
[error]      |-> case plan: TestPlan => apply(plan)	[ PatternMatcher.scala:492 ]
[error]      |                          ^^^^^^^^^^^
[error]      |-> plan.scrutinee = apply(plan.scrutinee)	[ PatternMatcher.scala:472 ]
[error]      |                    ^^^^^^^^^^^^^^^^^^^^^
[error]      |-> def apply(tree: Tree): Tree = treeMap.transform(tree)	[ PatternMatcher.scala:470 ]
[error]      |                                 ^^^^^^^^^^^^^^^^^^^^^^^
[error]      |-> if (toDrop(sym)) transform(initializer(sym))	[ PatternMatcher.scala:675 ]

This should improve the readability of the safe-init error messages.

Review by @liufengyun

@Xavientois Xavientois force-pushed the underline-safe-init-trace branch 2 times, most recently from 84f8fc3 to 620d3f7 Compare March 14, 2022 00:57
@prolativ prolativ self-requested a review March 14, 2022 09:58
@olhotak
Copy link
Contributor

olhotak commented Mar 14, 2022

There are some failures on the community build.

I like this and could merge after those are fixed, but I wonder if we should discuss a couple things.

First, does indenting each line a bit more than the previous line still make sense?

Second, can we add some text to each line indicating the relationship of each line to the next (i.e. method call, field read, inlined method, etc.)? That would probably be a separate PR though.

@liufengyun What do you think?

@Xavientois
Copy link
Contributor Author

First, does indenting each line a bit more than the previous line still make sense?

I still like the indenting, but it does appear less necessary for legibility given the increased spacing after this change. I don't think it hurts to keep it, though. If you think it makes more sense to remove it, I can do that in this PR or it could happen in a separate PR after the fact.

Second, can we add some text to each line indicating the relationship of each line to the next (i.e. method call, field read, inlined method, etc.)? That would probably be a separate PR though.

I am planning to try implementing this in a separate PR.

@Xavientois Xavientois force-pushed the underline-safe-init-trace branch from 620d3f7 to 1199e30 Compare March 14, 2022 12:29
@smarter
Copy link
Member

smarter commented Mar 14, 2022

similarly to how the MessageRendering will underline its stack positions.

Could MessageRendering be re-used instead? It supports displaying a stack of lines since #14002 /cc @nicolasstucki

@Xavientois
Copy link
Contributor Author

Xavientois commented Mar 14, 2022

similarly to how the MessageRendering will underline its stack positions.

Could MessageRendering be re-used instead? It supports displaying a stack of lines since #14002 /cc @nicolasstucki

I considered implementing it that way, but due to the indentation and trimming of the source lines in the trace, all but one of the lines of the positionMarker function were different.

If we wanted to re-use MessageRendering to completely handle this case, it would require a more involved re-work of the safe-init error message code and the MessageRendering code. The implementation from MessageRendering appears to specifically handle the case of inlined function calls, so it would need to be modified to handle the special case of the safe-init trace. We would also need to modify the implementation of Diagnostic to be able to store a stack of positions rather than just a single SourcePosition. I would be open to trying to implement this, but it would be beyond the scope of this PR.

@Xavientois Xavientois force-pushed the underline-safe-init-trace branch 2 times, most recently from 356a34d to 0cfbd11 Compare March 14, 2022 14:58
Copy link
Contributor

@liufengyun liufengyun left a comment

Choose a reason for hiding this comment

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

LGTM, thank you @Xavientois 🎉

First, does indenting each line a bit more than the previous line still make sense?

I'm open to remove it if it is not useful.

@Xavientois Xavientois force-pushed the underline-safe-init-trace branch 4 times, most recently from c74bd98 to 08bcec6 Compare March 18, 2022 17:52
@@ -211,8 +211,8 @@ class SourceFile(val file: AbstractFile, computeContent: => Array[Char]) extends
def startColumnPadding(offset: Int): String = {
var idx = startOfLine(offset)
val pad = new StringBuilder
while (idx != offset) {
pad.append(if (idx < length && content()(idx) == '\t') '\t' else ' ')
while (idx != offset && idx < length && idx < content().length) {
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 handles the case when content().length != length

Copy link
Contributor

Choose a reason for hiding this comment

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

@nicolasstucki Could you have a look at this change?

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't see how content().length != length is possible. calculateLineIndicesFromContents() has:

val cs = content()
...
buf += cs.length

and length calls lineIndicesCache.last.

@Xavientois do you have a concrete test case for which content().length != length?

Copy link
Contributor

Choose a reason for hiding this comment

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

It seems content().length != length happens when length is computed from Tasty using setLineIndicesFromLineSizes instead of using calculateLineIndicesFromContents.

@olhotak
Copy link
Contributor

olhotak commented Mar 30, 2022

@Xavientois : After rebasing the PR on main and git submodule update, I was able to reproduce the exception locally:
[error] java.lang.ArrayIndexOutOfBoundsException: Index 3375 out of bounds for length 0

This underlines the source positions in the safe-init stack traces similarly to how the MessageRendering will underline its stack positions.

Before, a safe-init error might look like this:
```
[error] -- Error: /******/dotty/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala:1032:22
[error] 1032 |      "inlineVars" -> inlineVars
[error]      |                      ^^^^^^^^^^
[error]      |Cannot prove that the value is fully initialized. Only initialized values may be used as arguments
[error]      |
[error]      |The unsafe promotion may cause the following problem:
[error]      |Calling the external method method apply may cause initialization errors. Calling trace:
[error]      | -> "inlineVars" -> inlineVars	[ PatternMatcher.scala:1032 ]
[error]      |  -> Inliner(plan)	[ PatternMatcher.scala:700 ]
[error]      |   -> case plan: TestPlan => apply(plan)	[ PatternMatcher.scala:492 ]
[error]      |    -> plan.scrutinee = apply(plan.scrutinee)	[ PatternMatcher.scala:472 ]
[error]      |     -> def apply(tree: Tree): Tree = treeMap.transform(tree)	[ PatternMatcher.scala:470 ]
[error]      |      -> if (toDrop(sym)) transform(initializer(sym))	[ PatternMatcher.scala:675 ]
```

Now, the same error looks like this:
```
[error] -- Error: /******/dotty/compiler/src/dotty/tools/dotc/transform/PatternMatcher.scala:1032:22
[error] 1032 |      "inlineVars" -> inlineVars
[error]      |                      ^^^^^^^^^^
[error]      |Cannot prove that the value is fully initialized. Only initialized values may be used as arguments
[error]      |
[error]      |The unsafe promotion may cause the following problem:
[error]      |Calling the external method method apply may cause initialization errors. Calling trace:
[error]      |-> "inlineVars" -> inlineVars	[ PatternMatcher.scala:1032 ]
[error]      |                   ^^^^^^^^^^
[error]      |-> Inliner(plan)	[ PatternMatcher.scala:700 ]
[error]      |   ^^^^^^^^^^^^^
[error]      |-> case plan: TestPlan => apply(plan)	[ PatternMatcher.scala:492 ]
[error]      |                          ^^^^^^^^^^^
[error]      |-> plan.scrutinee = apply(plan.scrutinee)	[ PatternMatcher.scala:472 ]
[error]      |                    ^^^^^^^^^^^^^^^^^^^^^
[error]      |-> def apply(tree: Tree): Tree = treeMap.transform(tree)	[ PatternMatcher.scala:470 ]
[error]      |                                 ^^^^^^^^^^^^^^^^^^^^^^^
[error]      |-> if (toDrop(sym)) transform(initializer(sym))	[ PatternMatcher.scala:675 ]
```

This should improve the readability of the safe-init error messages.

Review by @liufengyun
@Xavientois Xavientois force-pushed the underline-safe-init-trace branch from 286b518 to 4c75c8d Compare April 4, 2022 18:46
@Xavientois Xavientois requested a review from olhotak April 4, 2022 21:03
Copy link
Contributor

@olhotak olhotak left a comment

Choose a reason for hiding this comment

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

LGTM

@olhotak olhotak dismissed prolativ’s stale review April 5, 2022 09:33

The community build tests now pass with the fix to startColumnPadding following the same idiom as in column.

@olhotak olhotak merged commit d8e50f2 into scala:main Apr 5, 2022
@Xavientois Xavientois deleted the underline-safe-init-trace branch April 5, 2022 14:48
@Kordyjan Kordyjan added this to the 3.1.3 milestone Aug 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants