Skip to content

JavaToLaurelCompiler: fall back to a 1:1 position for unknown line maps - #439

Merged
tautschnig merged 2 commits into
strata-org:mainfrom
tautschnig:pr/linemap-unknown-fallback
Jul 1, 2026
Merged

JavaToLaurelCompiler: fall back to a 1:1 position for unknown line maps#439
tautschnig merged 2 commits into
strata-org:mainfrom
tautschnig:pr/linemap-unknown-fallback

Conversation

@tautschnig

@tautschnig tautschnig commented Jun 9, 2026

Copy link
Copy Markdown
Collaborator

What was changed?

When Strata reports a diagnostic for a node that carries no source location, it serializes it against a synthesized <unknown> source path, which has no entry in our line map. Previously a missing line map threw, masking the real Strata error in the user's verdict.

This PR addresses that at the root: JavaToLaurelCompiler now threads a source range from the originating Java tree into every node it synthesizes — method parameters, return types, and the requires / ensures / loop-invariant clauses (all previously constructed with the no-source builder overloads, i.e. SourceRange.NONE). The loop-invariant ranges pair with Strata's per-invariant source ranges (#1361). So a Strata diagnostic on any node jverify constructs now points at the user's code instead of <unknown>.

For the residual case where a diagnostic still lands on a node jverify does not construct (e.g. Strata's implicit result return binding — see the test below), the <unknown> fallback is kept as defense-in-depth but is now loud: it emits a [jverify] internal: … source-less synthesized node … warning naming it a synthesis bug, instead of silently returning a misleading 1:1. Any other unmapped URI keeps the previous fail-fast behaviour, since that indicates a real line-map collection bug we do not want to hide.

How has this been tested?

UnknownLineMapFallbackTest drives the verifier on a method with a parameter named result (whose Strata result collision is reported against the synthetic <unknown> URI) and asserts the diagnostic surfaces — rather than crashing with Could not find line mapand that the residual fallback is flagged loudly. Full verifier suite green (131 run, 0 failures). Verified it fails (with the crash) when the fallback is removed.

Note: coupling with #431

The result-parameter collision used as the trigger is the same one PR #431 fixes (by renaming the user parameter). The two are complementary — this one makes the error visible (and points jverify-synthesized diagnostics at real locations), #431 removes the collision. Consequently the test relies on pre-#431 behaviour to reach the synthetic-URI path.

Copilot AI review requested due to automatic review settings June 9, 2026 13:51

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Adjusts Java→Laurel analysis error handling so Strata diagnostics tied to synthetic/unknown URIs surface to users instead of being masked by an internal exception.

Changes:

  • Replace a hard failure on missing lineMap with a fallback Position(1, 1) and explanatory comments.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tautschnig
tautschnig force-pushed the pr/linemap-unknown-fallback branch from 308b9d1 to d8af4ee Compare June 9, 2026 14:38

@fabiomadge fabiomadge left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Correct and well-scoped — narrowing the fallback to the synthetic /<unknown> URI (and keeping fail-fast for any other unmapped URI) is the right call; a blanket 1:1 fallback would hide real line-map collection bugs.

Verified it does what it claims: applied it alone and re-ran a case that previously crashed with Could not find line map (a method with a result parameter, from #431). It now surfaces the real Strata diagnostic — Duplicate definition 'result' is already defined in this scope — instead of throwing. So this also unmasks the underlying error in #431's scenario; the two are complementary (this makes it visible, #431 fixes the collision).

Two small things: (1) no test — since the whole point is "diagnostic instead of crash," a test that hits the synthetic path and asserts a user-visible error would lock it (the result-param case reliably triggers it); (2) endsWith("/<unknown>") couples to javac's synthetic filename — fine, but a brittle string match.

@tautschnig
tautschnig force-pushed the pr/linemap-unknown-fallback branch from d8af4ee to 193d5da Compare June 10, 2026 10:31
@tautschnig

Copy link
Copy Markdown
Collaborator Author

Two small things: (1) no test — since the whole point is "diagnostic instead of crash," a test that hits the synthetic path and asserts a user-visible error would lock it (the result-param case reliably triggers it); (2) endsWith("/<unknown>") couples to javac's synthetic filename — fine, but a brittle string match.

Both addressed in 193d5da.

@tautschnig
tautschnig force-pushed the pr/linemap-unknown-fallback branch from 6df0ed4 to 88fcc96 Compare June 25, 2026 11:40
/// to this so the clause refers to the return value correctly.
private static final String LAUREL_RESULT_BINDING = "result";
/// javac's synthetic source path for trees injected by a
/// simplification pass; Strata reports diagnostics for them against

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For context, I think that reporting a diagnostic on a generated node indicates a compiler bug. Do you feel the same?

Secondly, I think that despite the above characterization of a bug, that for debugging purposes, it's better to give all synthesis nodes a source location. That source location should originate from the context that is generating the node. Getting a (1,1) diagnostic at any point is quite frustrating for debugging. What do you think?

@tautschnig tautschnig Jun 30, 2026

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Agreed on both counts.

(1) Yes — a user-facing diagnostic on a source-less generated node is a defect on our side. Here it was mostly that JavaToLaurelCompiler built parameters, return types, and the requires/ensures/invariant clauses through the no-source builder overloads (SourceRange.NONE) despite the originating Java tree having a real range.

(2) Agreed — fixed in 204bd2c: it now threads toSourceRange(<originating tree>) into everything it synthesizes (parameters, return types, requires/ensures/loop-invariant clauses; the invariant ranges pair with #1361). So a diagnostic on any node jverify constructs now points at the user's code.

Honest caveat: this PR's result-collision trigger isn't reported against a node jverify builds — Strata raises it against its own implicit result return binding, which we never construct, so there's no source to thread. For that residual class I kept the fallback but made it loud (a [jverify] internal: … source-less synthesized node … warning naming it a synthesis bug) rather than a silent 1:1 — surfacing (1) instead of hiding it. (#431 removes this collision anyway -- I'll return to that PR next, trying to address it at the Laurel source as suggested in #431.)

tautschnig and others added 2 commits June 30, 2026 11:53
…unknown> URI

Strata reports diagnostics for trees injected by a simplification pass
against a synthesized "<unknown>" source path (extracted to the
SYNTHETIC_UNKNOWN_PATH constant). Previously a missing line map threw,
which masked the real Strata error in the user's verdict.

Return a fallback Position(1,1) for that specific synthetic URI so the
diagnostic surfaces as a user-visible Verifier error with a stable
location (the URI is still reported). Any other unmapped URI keeps the
previous fail-fast behaviour, since that indicates a real line-map
collection bug we do not want to hide behind a misleading 1:1 location.

The synthetic "<unknown>" path is also not a legal filesystem path on
every platform ('<' and '>' are illegal on Windows), so LaurelDriver
must not route it through Paths.get: that threw InvalidPathException on
windows-2022 (while parsing fine on Linux) before the line-map fallback
was ever reached. When Paths.get rejects the path, build the file URI
directly via the multi-argument URI constructor (which percent-encodes
the illegal characters); getPath() decodes them back, so the fallback's
"/<unknown>" path-suffix match still triggers. StrataDiagnostic.filename()
likewise takes the segment after the last '/' instead of
Paths.get(...).getFileName(), which had the same latent Windows crash.

Adds UnknownLineMapFallbackTest, which drives the verifier on a method
with a parameter named `result` (whose Strata `result` collision is
reported against the synthetic <unknown> URI) and asserts the diagnostic
surfaces instead of crashing with "Could not find line map"; and
LaurelDriverUriTest, a cross-platform unit test locking the synthetic
URI construction.

Note: that `result` collision is the same one PR strata-org#431 fixes (by renaming
the user parameter), so the test relies on pre-strata-org#431 behaviour to reach
the synthetic-URI path; the two PRs are complementary (this surfaces the
error, strata-org#431 removes the collision).

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
…ap fallback loud

Addresses review feedback (keyboardDrummer): a diagnostic on a generated node
indicates a synthesis bug, and synthesized nodes should carry a source location
from their generating context rather than degrading to 1:1.

- JavaToLaurelCompiler now threads toSourceRange(...) from the originating Java
  tree into every node it synthesizes: method parameters, return types, and the
  requires / ensures / loop-invariant clauses (previously built with the
  no-source builder overloads, i.e. SourceRange.NONE). Invariant ranges in
  particular pair with Strata's per-invariant source ranges (#1361). So a Strata
  diagnostic on any jverify-synthesized node now points at the user's code.

- The <unknown> line-map fallback is kept as a defense-in-depth net for the
  residual case where a diagnostic still lands on a node jverify does not
  construct -- e.g. the result-parameter collision, which Strata reports against
  its own implicit "result" return binding -- but it is now LOUD: it prints a
  "[jverify] internal: ... source-less synthesized node" warning naming it a
  synthesis bug instead of silently returning a misleading 1:1.

UnknownLineMapFallbackTest is re-pointed to assert both that the diagnostic
surfaces (no crash) and that the residual fallback is flagged loudly. Full
verifier suite green (131 run, 0 failures).

Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
@tautschnig
tautschnig force-pushed the pr/linemap-unknown-fallback branch from 88fcc96 to 204bd2c Compare June 30, 2026 12:25

@keyboardDrummer keyboardDrummer left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Looks good, thanks!


@Test
public void syntheticUnknownUriSurfacesDiagnosticLoudlyInsteadOfCrashing() throws Exception {
String source = """

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I'm guessing this test will break when we solve the Laurel/Strata bug, right?

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

It actually should, yes.

@tautschnig
tautschnig merged commit 6e0357e into strata-org:main Jul 1, 2026
2 checks passed
@tautschnig
tautschnig deleted the pr/linemap-unknown-fallback branch July 1, 2026 08:01
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.

4 participants