Skip to content

Make coverage more similar to the one in Scala 2 #23722

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

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jchyb
Copy link
Contributor

@jchyb jchyb commented Aug 12, 2025

Closes #21877

  • removes coverage of inlined nodes (as mentioned in the accompanying comment, those are impossible to represent in most cases)
  • adds coverage for Literals (ones directly in Apply are omitted)
  • removes coverage of throw contents
  • if apply node is tagged, we do not tag it's prefix, outside of other prefixing Apply's arguments (eg. when we tag a+b+c we do not redundantly tag a+b)
  • allows instrumenting synthetic method calls (like apply of a case

After all of these changes the statements tagged are much more similar to Scala 2, let's look at the #21877 minimisation:

  • Scala 2:
Zrzut ekranu 2025-08-12 o 17 07 31 Zrzut ekranu 2025-08-12 o 17 07 46
  • Scala 3:
Zrzut ekranu 2025-08-12 o 17 08 48 Zrzut ekranu 2025-08-12 o 17 08 55

There are some differences still remaining, most notably the tagging the DefDefs and its default parameters, but I left them for now, as those seem more useful than harmful.

BEcouse of those changed most of the .covergae files had to be regenerated, however I want through each and every diff to make sure that all of those changes there are expected.

Additionally, this PR also fixes #21695 (issue with certain generated Block nodes not having assigned the correct type, causing later undefined errors).

@jchyb jchyb force-pushed the fix-coverage-s2-s3 branch from fb9a3c7 to da43ab4 Compare August 13, 2025 09:25
@jchyb jchyb requested a review from KacperFKorban August 14, 2025 09:27
Copy link
Member

@KacperFKorban KacperFKorban left a comment

Choose a reason for hiding this comment

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

The changes look good.

I didn't fully understand one part of the PR, so I left a question about that.

@@ -145,6 +145,29 @@ class InstrumentCoverage extends MacroTransform with IdentityDenotTransformer:
val span = pos.span.toSynthetic
invokeCall(statementId, span)

private def transformApplyArgs(trees: List[Tree])(using Context): List[Tree] =
if (allConstArgs(trees)) trees else transform(trees)
Copy link
Member

Choose a reason for hiding this comment

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

nit: chould you change to the new if-then-else syntax?

Comment on lines 151 to 169
private def transformInnerApply(tree: Tree)(using Context): Tree = tree match
case a: Apply if a.fun.symbol == defn.StringContextModule_apply =>
a
case a: Apply =>
cpy.Apply(a)(
transformInnerApply(a.fun),
transformApplyArgs(a.args)
)
case a: TypeApply =>
cpy.TypeApply(a)(
transformInnerApply(a.fun),
transformApplyArgs(a.args)
)
case s: Select =>
cpy.Select(s)(transformInnerApply(s.qualifier), s.name)
case i: (Ident | This) => i
case other => transform(other)
Copy link
Member

Choose a reason for hiding this comment

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

Question: I don't fully understand the motivation for this. Could you give a motivating code example that shows the intended coverage change?

Is it simply something of the shape: f(args1)(args2)(args3)(args4)? In that case, can something like a Typed node cause the logic to run away from this function into the main transform function?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, its so that f(args1)(args2)(args3)(args4), f(args1)(args2)(args3), f(args1)(args2) f(args1), f don't all get redundantly tagged (the args will still be, if they are not literals). The main annoyance was with math/logic operators, like for a + b + c, where a+b would also get redundantly tagged. This will not defend against something like a + (b + c), where the second apply node is in the args field - but that is also precisely how it works in scala 2.

I tested the Typed example now (something like (f(0): (Int => Unit))(1)), and it surprisingly manages to ignore the prefixing Apply in Scala 2, but not in this PR, so I'll will fix this now - thank you for the catch!

jchyb added 2 commits August 14, 2025 18:20
* remove coverage of inlined nodes (as mentioned in the accompanying
comment, those are impossible to represent in most cases)
* add coverage for Literals (ones directly in Apply are omitted)
* remove coverage of `throw` contents
* if apply node is tagged, do not tag it's prefix, outside of other
prefixing Apply's arguments (eg. when we tag `a+b+c` we do not
redundantly tag `a+b`)
* allow instrumenting synthetic method calls (like apply of a case
class)

Also fixes issue with certain generated Block nodes not having assigned
the correct type
@jchyb jchyb force-pushed the fix-coverage-s2-s3 branch from da43ab4 to 2c479e1 Compare August 14, 2025 16:20
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.

Different coverage results for scala 2 and scala 3 undefined error if enable coverage
2 participants