Skip to content

perf(prediction): terminate SLL conflicts by context containment - #343

Merged
tinovyatkin merged 6 commits into
mainfrom
issue-334-sll-context-conflict
Aug 12, 2026
Merged

perf(prediction): terminate SLL conflicts by context containment#343
tinovyatkin merged 6 commits into
mainfrom
issue-334-sll-context-conflict

Conversation

@tinovyatkin

@tinovyatkin tinovyatkin commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add a grammar-agnostic prediction-context containment proof for SLL conflicts not settled by the existing cheap checks
  • retain conflict exactness and origin in packed DFA metadata while preserving reference LL diagnostic coordinates
  • keep predicate and precedence-bearing configurations on the established path and cover earlier SLL recovery behavior

Testing

  • cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
  • cargo test --locked --workspace --all-features
  • cargo run --release --quiet -p antlr-rust-runtime-testsuite --bin antlr4-runtime-testsuite
    • 357 passed, 0 failed, 0 skipped

Fixes #334

Summary by CodeRabbit

  • Bug Fixes

    • Improved parser handling of ambiguous decisions, nested context conflicts, and exact SLL conflicts.
    • Refined ambiguity diagnostics to avoid unnecessary full-context retries and report accurate stopping positions.
    • Improved token-deletion recovery while preserving expected parse results and syntax error behavior.
  • Tests

    • Added regression coverage for early SLL termination, context containment, diagnostic reporting, ambiguity handling, and recovery scenarios.

When ordinary state/context conflict detection is inconclusive, join prediction contexts per state and alternative and prove competing contexts are contained by the minimum alternative. Keep predicate and precedence-bearing sets on the established path, preserve LL diagnostic coordinates, and retain exactness in packed DFA metadata.

Add focused containment, near-miss, diagnostic, and invalid-input recovery coverage.

Fixes #334
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

Copy/Paste Detection

Found 23 duplication(s) across 4 changed non-generated Rust file(s) (threshold: 100 tokens).

Show duplications

Found a 21 line (226 tokens) duplication in the following files:

  • Starting at line 15298 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 16703 of crates/antlr-rust-runtime/src/parser.rs
            (9, AtnStateKind::RuleStop),
        ] {
            assert_eq!(atn.add_state(kind, Some(0)).expect("state").index(), state);
        }
        atn.set_left_recursive_rule(0)
            .expect("left-recursive rule start");
        atn.set_precedence_rule_decision(2)
            .expect("precedence decision");
        atn.set_loop_back_state(8, 7).expect("loop-back state");
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![9])
            .expect("rule stop states");
        for state in [1, 2, 3] {
            atn.add_decision_state(state).expect("decision state");
        }
        for (source, target) in [(0, 1), (2, 3), (2, 8), (7, 2), (8, 9)] {
            atn.add_transition(source, ParserTransitionSpec::Epsilon { target })
                .expect("epsilon transition");
        }
        for (source, target, label) in [(1, 2, 1), (1, 2, 2), (4, 6, 4), (5, 6, 3), (6, 7, 1)] {
```rust

---

Found a 25 line (193 tokens) duplication in the following files:
* Starting at line 16632 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 17613 of crates/antlr-rust-runtime/src/parser.rs

```rust
        let mut atn = ParserAtnBuilder::new(1);
        for (state_number, kind) in [
            (0, AtnStateKind::RuleStart),
            (1, AtnStateKind::StarLoopEntry),
            (2, AtnStateKind::Basic),
            (3, AtnStateKind::Basic),
            (4, AtnStateKind::StarLoopBack),
            (5, AtnStateKind::LoopEnd),
            (6, AtnStateKind::RuleStop),
        ] {
            assert_eq!(
                atn.add_state(kind, Some(0)).expect("state").index(),
                state_number
            );
        }
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![6])
            .expect("rule stop states");
        atn.add_decision_state(1).expect("decision state");
        atn.set_loop_back_state(5, 4).expect("loop back state");
        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
            .expect("entry transition");
        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
            .expect("loop body");

Found a 27 line (145 tokens) duplication in the following files:

  • Starting at line 18134 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 18268 of crates/antlr-rust-runtime/src/parser.rs
    fn generated_match_token_recovers_missing_token_from_context_follow() {
        let atn = generated_match_recovery_atn();
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new(
                [None, Some("'X'"), Some("'Y'")],
                [None, Some("X"), Some("Y")],
                [None::<&str>, None, None],
            ),
        );
        let mut parser = BaseParser::new(
            CommonTokenStream::new(Source {
                tokens: vec![TestToken::eof("parser-test", 3, 1, 3)],
                index: 0,
            }),
            data,
        );
        parser.rule_context_stack = vec![
            RuleContextFrame {
                rule_index: 0,
                invoking_state: 0,
            },
            RuleContextFrame {
                rule_index: 1,
                invoking_state: 1,
            },
        ];
```rust

---

Found a 18 line (128 tokens) duplication in the following files:
* Starting at line 16298 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 16323 of crates/antlr-rust-runtime/src/parser.rs

```rust
    fn epsilon_cycle_atn() -> Atn {
        let mut atn = ParserAtnBuilder::new(1);
        for (state_number, kind) in [
            (0, AtnStateKind::RuleStart),
            (1, AtnStateKind::Basic),
            (2, AtnStateKind::RuleStop),
        ] {
            assert_eq!(
                atn.add_state(kind, Some(0)).expect("state").index(),
                state_number
            );
        }
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![2])
            .expect("rule stop states");
        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
            .expect("transition");

Found a 27 line (127 tokens) duplication in the following files:

  • Starting at line 16753 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 16825 of crates/antlr-rust-runtime/src/parser.rs
        let mut atn = ParserAtnBuilder::new(2);
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(0))
                .expect("state")
                .index(),
            0
        );
        assert_eq!(
            atn.add_state(AtnStateKind::BlockStart, Some(0))
                .expect("state")
                .index(),
            1
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            2
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            3
        );
        assert_eq!(
            atn.add_state(AtnStateKind::BlockEnd, Some(0))
```rust

---

Found a 22 line (125 tokens) duplication in the following files:
* Starting at line 16779 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 16851 of crates/antlr-rust-runtime/src/parser.rs

```rust
            atn.add_state(AtnStateKind::BlockEnd, Some(0))
                .expect("state")
                .index(),
            4
        );
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStop, Some(0))
                .expect("state")
                .index(),
            5
        );
        atn.set_rule_to_start_state(vec![0])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![5])
            .expect("rule stop states");
        atn.add_decision_state(1).expect("decision state");
        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
            .expect("transition");
        atn.add_transition(
            1,
            ParserTransitionSpec::Atom {
                target: 2,

Found a 15 line (124 tokens) duplication in the following files:

  • Starting at line 3271 of crates/antlr-rust-runtime/src/atn/parser.rs
  • Starting at line 3317 of crates/antlr-rust-runtime/src/atn/parser.rs
    fn context_prediction_reports_context_sensitivity_for_dfa_conflict() {
        let atn = two_token_decision_atn();
        let mut simulator = ParserAtnSimulator::new(&atn);
        let mut workspace = PredictionWorkspace::default();
        let mut start_configs = AtnConfigSet::new();
        start_configs.add(
            AtnConfig::new(2, 1, EMPTY_CONTEXT, &simulator.store.contexts),
            &mut simulator.store.contexts,
            &mut workspace,
        );
        let start =
            simulator.store.decision_to_dfa[0].add_state(DfaStateBuilder::new(start_configs));
        simulator.store.decision_to_dfa[0].set_start_state(start);

        let mut accept_configs = AtnConfigSet::new();
```rust

---

Found a 18 line (122 tokens) duplication in the following files:
* Starting at line 3907 of crates/antlr-rust-runtime/src/atn/parser.rs
* Starting at line 4058 of crates/antlr-rust-runtime/src/atn/parser.rs

```rust
        atn.set_rule_to_stop_state(vec![7])
            .expect("rule stop states");
        atn.add_decision_state(1).expect("decision state");
        atn.add_transition(0, ParserTransitionSpec::Epsilon { target: 1 })
            .expect("transition");
        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 2 })
            .expect("transition");
        atn.add_transition(1, ParserTransitionSpec::Epsilon { target: 4 })
            .expect("transition");
        atn.add_transition(
            2,
            ParserTransitionSpec::Atom {
                target: 3,
                label: 1,
            },
        )
        .expect("transition");
        atn.add_transition(3, ParserTransitionSpec::Epsilon { target: 6 })

Found a 34 line (119 tokens) duplication in the following files:

  • Starting at line 10882 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 10957 of crates/antlr-rust-runtime/src/parser.rs
                        outcomes.extend(
                            self.recognize_state(
                                atn,
                                RecognizeRequest {
                                    state_number: *target,
                                    stop_state,
                                    index,
                                    rule_start_index,
                                    decision_start_index: next_decision_start_index,
                                    init_action_rules,
                                    predicates,
                                    semantics,
                                    rule_args,
                                    member_actions,
                                    return_actions,
                                    local_int_arg,
                                    member_values: member_values.clone(),
                                    return_values: return_values.clone(),
                                    rule_alt_number: next_alt_number,
                                    track_alt_numbers,
                                    consumed_eof,
                                    committed_decision: transition_committed,
                                    precedence,
                                    depth: depth + 1,
                                    recovery_symbols: epsilon_recovery_symbols.clone(),
                                    recovery_state: epsilon_recovery_state,
                                },
                                visiting,
                                memo,
                                expected,
                            )
                            .into_iter()
                            .map(|mut outcome| {
                                prepend_decision(&mut outcome, decision);
```rust

---

Found a 13 line (117 tokens) duplication in the following files:
* Starting at line 145 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 172 of crates/antlr-rust-runtime/src/parser.rs

```rust
        ordinary $parser:ident, $state:expr, $rule:expr, $allow_fallback:expr,
        $atn:expr, $fatal:path;
        retry [$($retry:tt)*];
        bind ($ctx:ident, $rule_start:ident, $consumed_eof:ident, $sync_error:ident);
        setup { $($setup:tt)* }
        body { $($body:tt)* }
        success { $($success:tt)* }
        recovery { $($recovery:tt)* }
    ) => {
        $crate::__antlr4_rust_generated_rule! {
            @body
            parser $parser;
            enter $parser.base.enter_rule($state, $rule);

Found a 24 line (113 tokens) duplication in the following files:

  • Starting at line 410 of crates/antlr-rust-runtime/src/atn/parser.rs
  • Starting at line 4168 of crates/antlr-rust-runtime/src/atn/parser.rs
impl IntStream for LookaheadIntStream {
    fn consume(&mut self) {
        if self.la(1) != TOKEN_EOF {
            self.index += 1;
        }
    }

    fn la(&mut self, offset: isize) -> i32 {
        if offset <= 0 {
            return 0;
        }
        let offset = offset.cast_unsigned() - 1;
        self.symbols
            .get(self.index + offset)
            .copied()
            .unwrap_or(TOKEN_EOF)
    }

    fn index(&self) -> usize {
        self.index
    }

    fn seek(&mut self, index: usize) {
        self.index = index.min(self.symbols.len());
```rust

---

Found a 15 line (113 tokens) duplication in the following files:
* Starting at line 18193 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 18569 of crates/antlr-rust-runtime/src/parser.rs

```rust
    fn generated_match_token_counts_single_token_deletion_recovery() {
        let atn = generated_match_recovery_atn();
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new(
                [None, Some("'X'"), Some("'Y'"), Some("'Z'")],
                [None, Some("X"), Some("Y"), Some("Z")],
                [None::<&str>, None, None, None],
            ),
        );
        let mut parser = BaseParser::new(
            CommonTokenStream::new(Source {
                tokens: vec![
                    TestToken::new(3).with_text("z"),
                    TestToken::new(2).with_text("y"),

Found a 18 line (112 tokens) duplication in the following files:

  • Starting at line 15108 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 17539 of crates/antlr-rust-runtime/src/parser.rs
            (4, AtnStateKind::Basic, 0),
            (5, AtnStateKind::RuleStop, 0),
            (6, AtnStateKind::RuleStart, 1),
            (7, AtnStateKind::Basic, 1),
            (8, AtnStateKind::RuleStop, 1),
        ] {
            assert_eq!(
                atn.add_state(kind, Some(rule_index))
                    .expect("state")
                    .index(),
                state_number
            );
        }
        atn.set_rule_to_start_state(vec![0, 6])
            .expect("rule start states");
        atn.set_rule_to_stop_state(vec![5, 8])
            .expect("rule stop states");
        atn.add_decision_state(2).expect("decision state");
```rust

---

Found a 12 line (112 tokens) duplication in the following files:
* Starting at line 15351 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 15434 of crates/antlr-rust-runtime/src/parser.rs

```rust
        let mut atn = ParserAtnBuilder::new(1);
        for (state, kind, rule) in [
            (0, AtnStateKind::RuleStart, 0),
            (1, AtnStateKind::StarLoopEntry, 0),
            (2, AtnStateKind::Basic, 0), // ops hub
            (3, AtnStateKind::Basic, 0), // shift prec
            (4, AtnStateKind::Basic, 0), // shift first >
            (5, AtnStateKind::Basic, 0), // shift second >
            (6, AtnStateKind::Basic, 0), // rel prec
            (7, AtnStateKind::Basic, 0), // rel >
            (8, AtnStateKind::LoopEnd, 0),
            (9, AtnStateKind::RuleStop, 0),

Found a 22 line (112 tokens) duplication in the following files:

  • Starting at line 17299 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 17730 of crates/antlr-rust-runtime/src/parser.rs
    fn predicate_after_token_atn() -> Atn {
        let mut atn = ParserAtnBuilder::new(2);
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(0))
                .expect("state")
                .index(),
            0
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            1
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            2
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
```rust

---

Found a 22 line (111 tokens) duplication in the following files:
* Starting at line 15173 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 17299 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 17730 of crates/antlr-rust-runtime/src/parser.rs

```rust
    fn left_recursive_loop_with_caller_follow_atn(caller_symbol: i32) -> Atn {
        let mut atn = ParserAtnBuilder::new(2);
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(0))
                .expect("state")
                .index(),
            0
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            1
        );
        assert_eq!(
            atn.add_state(AtnStateKind::Basic, Some(0))
                .expect("state")
                .index(),
            2
        );
        assert_eq!(
            atn.add_state(AtnStateKind::RuleStart, Some(1))

Found a 14 line (110 tokens) duplication in the following files:

  • Starting at line 17863 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 21972 of crates/antlr-rust-runtime/src/parser.rs
    fn parser_matches_token_and_reports_mismatch() {
        let source = Source {
            tokens: vec![
                TestToken::new(1).with_text("x"),
                TestToken::eof("parser-test", 1, 1, 1),
            ],
            index: 0,
        };
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
        );
        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);
        let matched = parser.match_token(1).expect("token 1 should match");
```rust

---

Found a 13 line (109 tokens) duplication in the following files:
* Starting at line 17863 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 21997 of crates/antlr-rust-runtime/src/parser.rs

```rust
    fn parser_matches_token_and_reports_mismatch() {
        let source = Source {
            tokens: vec![
                TestToken::new(1).with_text("x"),
                TestToken::eof("parser-test", 1, 1, 1),
            ],
            index: 0,
        };
        let data = RecognizerData::new(
            "Mini.g4",
            Vocabulary::new([None, Some("'x'")], [None, Some("X")], [None::<&str>, None]),
        );
        let mut parser = BaseParser::new(CommonTokenStream::new(source), data);

Found a 22 line (108 tokens) duplication in the following files:

  • Starting at line 8672 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 9063 of crates/antlr-rust-runtime/src/parser.rs
    ) -> Option<RecognizeOutcome> {
        let (error_index, message) = self.expected_error_message(rule_index, start_index, expected);
        let diagnostic = diagnostic_for_token(self.token_at(error_index), message);
        let mut next_index = error_index;
        loop {
            let symbol = self.token_type_at(next_index);
            if sync_symbols.contains(&symbol) {
                if next_index == error_index {
                    return None;
                }
                break;
            }
            if symbol == TOKEN_EOF {
                break;
            }
            let after = self.consume_index(next_index, symbol);
            if after == next_index {
                break;
            }
            next_index = after;
        }
        let mut nodes = NodeSeqId::EMPTY;
```rust

---

Found a 15 line (108 tokens) duplication in the following files:
* Starting at line 22268 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 22292 of crates/antlr-rust-runtime/src/parser.rs

```rust
    fn outcome_ties_keep_later_non_recursive_alternative() {
        let arena = RecognitionArena::default();
        let first = RecognizeOutcome {
            index: 1,
            consumed_eof: false,
            alt_number: 0,
            member_values: MemberEnv::new(),
            return_values: BTreeMap::new(),
            diagnostics: DiagnosticSeqId::EMPTY,
            decisions: Vec::new(),
            actions: vec![ParserAction::new(1, 0, 0, None)],
            nodes: NodeSeqId::EMPTY,
        };
        let second = RecognizeOutcome {
            actions: vec![ParserAction::new(2, 0, 0, None)],

Found a 17 line (107 tokens) duplication in the following files:

  • Starting at line 7731 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 8426 of crates/antlr-rust-runtime/src/parser.rs
        let report_unrecovered_error = self.is_top_level_entry();
        let start_state = atn.rule_to_start_state().get(rule_index).ok_or_else(|| {
            AntlrError::Unsupported(format!("rule {rule_index} has no start state"))
        })?;
        let stop_state = atn
            .rule_to_stop_state()
            .get(rule_index)
            .filter(|state| *state != usize::MAX)
            .ok_or_else(|| {
                AntlrError::Unsupported(format!("rule {rule_index} has no stop state"))
            })?;

        let start_index = self.current_visible_index();
        self.clear_prediction_diagnostics();
        self.reset_per_parse_caches();
        self.reset_recognition_arena();
        let caller_follow_state = self.pending_invoking_follow_state(atn);
```rust

---

Found a 13 line (104 tokens) duplication in the following files:
* Starting at line 15286 of crates/antlr-rust-runtime/src/parser.rs
* Starting at line 16690 of crates/antlr-rust-runtime/src/parser.rs

```rust
    fn labeled_left_recursive_operator_atn() -> Atn {
        let mut atn = ParserAtnBuilder::new(4);
        for (state, kind) in [
            (0, AtnStateKind::RuleStart),
            (1, AtnStateKind::BlockStart),
            (2, AtnStateKind::StarLoopEntry),
            (3, AtnStateKind::StarBlockStart),
            (4, AtnStateKind::Basic),
            (5, AtnStateKind::Basic),
            (6, AtnStateKind::Basic),
            (7, AtnStateKind::StarLoopBack),
            (8, AtnStateKind::LoopEnd),
            (9, AtnStateKind::RuleStop),

Found a 13 line (100 tokens) duplication in the following files:

  • Starting at line 7382 of crates/antlr-rust-runtime/src/parser.rs
  • Starting at line 7406 of crates/antlr-rust-runtime/src/parser.rs
        let mut expected = BTreeSet::new();
        for index in (1..self.rule_context_stack.len()).rev() {
            let invoking_state = self.rule_context_stack[index].invoking_state;
            let Ok(state_number) = usize::try_from(invoking_state) else {
                continue;
            };
            let Some(Transition::Rule { follow_state, .. }) = atn
                .state(state_number)
                .and_then(|state| state.transitions().first())
                .map(ParserTransition::data)
            else {
                continue;
            };
```rust

@coderabbitai

coderabbitai Bot commented Aug 12, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@tinovyatkin, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 47 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 490872f8-c507-4e18-97b2-339767b02587

📥 Commits

Reviewing files that changed from the base of the PR and between 9bba7c2 and b3d326a.

⛔ Files ignored due to path filters (1)
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__parser__tests__full_context_exact_conflict_reports_attempt_and_ambiguity.snap is excluded by !**/*.snap
📒 Files selected for processing (1)
  • crates/antlr-rust-runtime/src/parser.rs
📝 Walkthrough

Walkthrough

The parser runtime adds exact and context-containment SLL conflict classification. DFA states preserve this metadata. Full-context diagnostics use containment information to adjust stop indices. Tests cover early termination, recovery, diagnostics, and prediction-context analysis.

Changes

SLL conflict handling

Layer / File(s) Summary
SLL conflict analysis
crates/antlr-rust-runtime/src/prediction.rs
SllConflict and exact_context_sll_conflict classify exact and context-containment conflicts. Tests cover contained, semantically distinct, and unrelated contexts.
DFA conflict metadata
crates/antlr-rust-runtime/src/dfa.rs
DFA states store exact-conflict and context-containment flags in hot-table metadata. Builders, cloning, insertion, and diagnostic views propagate the flags.
Parser prediction and recovery integration
crates/antlr-rust-runtime/src/atn/parser.rs, crates/antlr-rust-runtime/src/parser.rs
SLL prediction uses structured conflict results. Exact conflicts avoid full-context retry. Containment conflicts align SLL and LL diagnostic stop indices. Parser tests cover diagnostic suppression, local ambiguity, and token-deletion recovery.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ParserATNSimulator
  participant exact_context_sll_conflict
  participant ParserDfa
  participant full_context_retry_prediction
  ParserATNSimulator->>exact_context_sll_conflict: Analyze SLL configurations
  exact_context_sll_conflict-->>ParserATNSimulator: Return conflict alternatives and metadata
  ParserATNSimulator->>ParserDfa: Store conflict flags on accept state
  ParserDfa-->>full_context_retry_prediction: Provide containment metadata
  full_context_retry_prediction-->>ParserATNSimulator: Report full-context diagnostic stop index
Loading

Possibly related PRs

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: earlier SLL conflict termination through context containment.
Linked Issues check ✅ Passed The changes implement context-containment termination, exactness tracking, diagnostics, recovery coverage, and zero-skipped conformance requirements for issue #334.
Out of Scope Changes check ✅ Passed The reviewed changes support the linked issue objectives and do not show unrelated code changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-334-sll-context-conflict

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@claude

claude Bot commented Aug 12, 2026

Copy link
Copy Markdown

Claude Code is working…

I'll analyze this and get back to you.

View job run

@codspeed-hq

codspeed-hq Bot commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Merging this PR will not alter performance

✅ 17 untouched benchmarks


Comparing issue-334-sll-context-conflict (b3d326a) with main (512baf3)

Open in CodSpeed

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f4126d9fec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/antlr-rust-runtime/src/atn/parser.rs

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/antlr-rust-runtime/src/atn/parser.rs`:
- Around line 2814-2841: Rename
adaptive_predict_stops_at_an_exact_context_containment_conflict to
adaptive_predict_stops_at_a_context_containment_conflict to match the non-exact
assertion. Add a test covering context_containment_conflict through the
full-context memo path by disabling exact-ambiguity detection, running the same
decision twice, and comparing the memoized retry’s diagnostic result with the
fresh decision.
- Around line 1347-1351: Add an inline comment immediately above the conditional
assignment to `sll_stop_index` explaining that containment ends the SLL walk
earlier than the reference implementation, so replacing the recorded value with
`stop_index` restores the reference diagnostic coordinate.

In `@crates/antlr-rust-runtime/src/parser.rs`:
- Around line 17432-17535: Extract the shared 11-state containment ATN
construction into a reachable builder accepting the block-end-state option and
state 6 transition as parameters. In
crates/antlr-rust-runtime/src/parser.rs#L17432-17535, replace
context_containment_recovery_atn with a call requesting the block end state and
Atom transition labeled TOKEN_EOF; in
crates/antlr-rust-runtime/src/atn/parser.rs#L3758-3853, replace
context_containment_decision_atn with a call requesting no block end state and
an Epsilon transition. Ensure both fixtures use the same shared construction for
all unchanged states and transitions.

In `@crates/antlr-rust-runtime/src/prediction.rs`:
- Around line 1399-1479: Add targeted tests for exact_context_sll_conflict using
two distinct non-stop states so multiple state groups are processed: verify None
when a later group lacks min_alt, verify exact becomes false when groups have
different alternative sets, and verify exact becomes false when
dips_into_outer_context is set. Keep existing assertions for successful
conflicts and non-contained-context declines unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d4a9084f-e261-4e2a-bc3b-da93d3936846

📥 Commits

Reviewing files that changed from the base of the PR and between 512baf3 and f4126d9.

⛔ Files ignored due to path filters (1)
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__parser__tests__committed_sll_containment_conflict_preserves_token_deletion_recovery.snap is excluded by !**/*.snap
📒 Files selected for processing (4)
  • crates/antlr-rust-runtime/src/atn/parser.rs
  • crates/antlr-rust-runtime/src/dfa.rs
  • crates/antlr-rust-runtime/src/parser.rs
  • crates/antlr-rust-runtime/src/prediction.rs

Comment thread crates/antlr-rust-runtime/src/atn/parser.rs
Comment thread crates/antlr-rust-runtime/src/atn/parser.rs
Comment thread crates/antlr-rust-runtime/src/parser.rs
Comment thread crates/antlr-rust-runtime/src/prediction.rs
@codecov

codecov Bot commented Aug 12, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 99.82935% with 1 line in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
crates/antlr-rust-runtime/src/prediction.rs 99.44% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

Share the containment ATN fixture across simulator and committed-parser tests, cover memoized full-context diagnostics and multi-state decline/exactness branches, and document the reference diagnostic coordinate adjustment.
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Chef's kiss.

Reviewed commit: f4126d9fec

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@github-actions

github-actions Bot commented Aug 12, 2026

Copy link
Copy Markdown

📊 Source Code Metrics (this PR vs main)

File Cyclomatic Cognitive Functions LLOC MI
crates/antlr-rust-runtime/src/parser.rs 2446 (main: 2434) 🔴 1614 (main: 1611) 🔴 785 (main: 778) 🔴 5475 (main: 5439) 🔴 0 ⚪
crates/antlr-rust-runtime/src/atn/parser.rs 438 (main: 415) 🔴 309 (main: 298) 🔴 142 (main: 136) 🔴 1262 (main: 1173) 🔴 0 ⚪
crates/antlr-rust-runtime/src/prediction.rs 328 (main: 301) 🔴 217 (main: 186) 🔴 126 (main: 119) 🔴 727 (main: 629) 🔴 0 ⚪
crates/antlr-rust-runtime/src/dfa.rs 207 (main: 201) 🔴 80 (main: 78) 🔴 98 (main: 94) 🔴 377 (main: 369) 🔴 0 ⚪

Generated by mehen v1.8.1 — the code quality watcher.

Treat exact SLL conflicts as terminal exact ambiguities instead of always retrying full-context prediction. This gives the retained exactness metadata its intended runtime role and avoids unnecessary LL work.

Rename the SLL containment regression to state explicitly that earlier commitment enables token-deletion recovery and can change the resulting tree and error text. Use named snapshots for structured conflict values.

The pinned optimized fork computes exactness from each alternative's joined context; retain that behavior.
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

Addressed the review follow-ups on 76be7e43:

  1. EXACT_CONFLICT now has runtime behavior: exact local conflicts return an exact ambiguity without a full-context retry. A direct DFA regression verifies requires_full_context: false, the exact diagnostic, and no memo entry.
  2. I retained the existing exactness formula. The pinned fork initializes joinedCheckContext2 from one config, joins every context for the same (state, alt), and then compares joinedCheckContext.equals(joinedCheckContext2):
    https://github.com/tunnelvisionlabs/antlr4/blob/1de7b24e31bd72ff2da7361465ce8a9d0154acd7/runtime/Java/src/org/antlr/v4/runtime/atn/ParserATNSimulator.java#L2128-L2156
    That is a joined per-alt comparison, not a per-config comparison, so the Rust implementation matches the cited source.
  3. Renamed the SLL recovery regression from preserves to enables. The new commit message states explicitly that earlier SLL commitment can change recovery trees and error text.
  4. Converted the four structured SllConflict expectations to named external insta snapshots.

Validation on the updated tree:

  • cargo clippy --locked --workspace --all-targets --all-features -- -D warnings
  • cargo test --locked --workspace --all-features
  • runtime conformance: 357 passed, 0 failed, 0 skipped

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 76be7e436f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/antlr-rust-runtime/src/atn/parser.rs
Exact local conflicts can skip full-context prediction. Keep generated prediction diagnostics silent in SLL mode so the parser never reports an attempting-full-context event when no LL retry occurred.
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5134dc1337

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread crates/antlr-rust-runtime/src/parser.rs
When an exact local conflict terminates prediction without an LL retry, emit the exact ambiguity diagnostic without a preceding attempting-full-context event. Keep SLL mode silent and cover both diagnostic paths.
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. 🚀

Reviewed commit: 9bba7c2105

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/antlr-rust-runtime/src/atn/parser.rs (1)

1359-1366: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add a contrast test for the non-containment branch of this override.

This override applies only when context_containment_conflict is true. No test exercises the else branch: an ordinary (non-containment) SLL conflict that resolves as an exact ambiguity through the LL retry. Add a test using a non-containment ambiguous ATN with set_exact_ambig_detection(true) that asserts sll_stop_index is not forced to equal ll_stop_index there. This guards the branch condition that distinguishes containment conflicts from ordinary ones.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/antlr-rust-runtime/src/atn/parser.rs` around lines 1359 - 1366, Add a
contrast test for the parser prediction path around the sll_stop_index override,
constructing a non-containment ambiguous ATN with
set_exact_ambig_detection(true) so the LL retry resolves an ordinary exact
ambiguity. Assert that the non-containment branch preserves a distinct
sll_stop_index rather than forcing it to equal ll_stop_index, while leaving the
containment behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/antlr-rust-runtime/src/parser.rs`:
- Around line 5704-5715: Add a test adjacent to
local_exact_conflict_reports_ambiguity_without_full_context_attempt that
constructs an ambiguity diagnostic with requires_full_context true, kind
Ambiguity, and exact true. Assert the LL-retried case emits both the
reportAttemptingFullContext diagnostic and the ambiguity diagnostic, covering
the local_exact_ambiguity boundary in the surrounding parser logic.

---

Outside diff comments:
In `@crates/antlr-rust-runtime/src/atn/parser.rs`:
- Around line 1359-1366: Add a contrast test for the parser prediction path
around the sll_stop_index override, constructing a non-containment ambiguous ATN
with set_exact_ambig_detection(true) so the LL retry resolves an ordinary exact
ambiguity. Assert that the non-containment branch preserves a distinct
sll_stop_index rather than forcing it to equal ll_stop_index, while leaving the
containment behavior unchanged.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 47aacdfc-f648-4037-b5db-9cc539766ef1

📥 Commits

Reviewing files that changed from the base of the PR and between f4126d9 and 9bba7c2.

⛔ Files ignored due to path filters (7)
  • crates/antlr-rust-runtime/src/atn/snapshots/antlr4_runtime__atn__parser__tests__exact_sll_conflict_skips_full_context_retry.snap is excluded by !**/*.snap
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__parser__tests__committed_sll_containment_conflict_enables_token_deletion_recovery.snap is excluded by !**/*.snap
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__parser__tests__local_exact_conflict_reports_ambiguity_without_full_context_attempt.snap is excluded by !**/*.snap
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__prediction__tests__exact_context_conflict_joins_semantically_distinct_configs.snap is excluded by !**/*.snap
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__prediction__tests__exact_context_conflict_marks_different_state_alt_sets_inexact.snap is excluded by !**/*.snap
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__prediction__tests__exact_context_conflict_marks_outer_context_reach_inexact.snap is excluded by !**/*.snap
  • crates/antlr-rust-runtime/src/snapshots/antlr4_runtime__prediction__tests__exact_context_conflict_proves_containment_without_shared_context_ids.snap is excluded by !**/*.snap
📒 Files selected for processing (3)
  • crates/antlr-rust-runtime/src/atn/parser.rs
  • crates/antlr-rust-runtime/src/parser.rs
  • crates/antlr-rust-runtime/src/prediction.rs

Comment thread crates/antlr-rust-runtime/src/parser.rs
Pin the complementary LL-retried exact ambiguity path so it continues to emit both reportAttemptingFullContext and reportAmbiguity while local exact conflicts omit the attempt.
@tinovyatkin

Copy link
Copy Markdown
Contributor Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. What shall we delve into next?

Reviewed commit: b3d326a3d1

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@tinovyatkin
tinovyatkin merged commit 020758b into main Aug 12, 2026
18 of 19 checks passed
@tinovyatkin
tinovyatkin deleted the issue-334-sll-context-conflict branch August 12, 2026 23:07
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.

perf(prediction): use exact context containment for earlier SLL conflict termination

1 participant