fix: enforce fn-head nested parens at parse time#118
Open
mkaput wants to merge 4 commits intoelixir-tools:mainfrom
Open
fix: enforce fn-head nested parens at parse time#118mkaput wants to merge 4 commits intoelixir-tools:mainfrom
mkaput wants to merge 4 commits intoelixir-tools:mainfrom
Conversation
I dove way to deep into the rabbithole with these tests. They popped for me through proptests, I used LLMs to find all these variations, and I fixed them by having an LLM port Elixir's parser logic. 1. **`stab_expr` fn-clause head shapes** Ported concept: separate and normalize bare heads vs parenthesized heads before building `->` clauses. Source: [`elixir_parser.yrl` `stab_expr` rules](https://github.com/elixir-lang/elixir/blob/v1.18.2/lib/elixir/src/elixir_parser.yrl#L340-L353) 2. **`when` vs `<-` / `\\` precedence behavior in fn heads** Ported concept: preserve operator precedence model, and lower guard parsing precedence only for specific simple fn-head forms. Source: [precedence table](https://github.com/elixir-lang/elixir/blob/v1.18.2/lib/elixir/src/elixir_parser.yrl#L59-L63) 3. **`unwrap_when`-style reassociation** Ported concept: reassociate trailing `when` in head args so non-`when` operators bind to the last head arg/guard the same way compiler parser does. Source: [`unwrap_when/1`](https://github.com/elixir-lang/elixir/blob/v1.18.2/lib/elixir/src/elixir_parser.yrl#L1135-L1141) 4. **Nested-parens rejection semantics (`unexpected parentheses`)** Ported concept: reject nested parenthesized fn-head arg forms during fn-head construction, but keep Spitfire recovery by recording errors and continuing AST construction.
Recent fn-head parsing changes introduced infix reassociation branches that assume comma/when operands always carry list arguments. In the absinthe matrix CI run, a malformed intermediate AST used , which passed the guard and crashed in . This change hardens the parser by requiring list operands before reassociation in , and by making fn-head nested-parens validation a no-op for non-list argument payloads. This preserves existing behavior for valid AST shapes while preventing parser crashes on recoverable malformed intermediates encountered during large real-world repo parsing.
Add a focused parser parity regression for the snippet that triggered the CI crash: comma = ascii_char([?,]) This comes from absinthe-graphql/absinthe lexer code and protects against regressions where an identifier named `comma` can produce malformed intermediate lhs state during infix parsing.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I dove way to deep into the rabbithole with these tests. They popped for me through proptests, I used LLMs to find all these variations, and I fixed them by having an LLM port Elixir's parser logic.
stab_exprfn-clause head shapesPorted concept: separate and normalize bare heads vs parenthesized heads before building
->clauses.Source:
elixir_parser.yrlstab_exprruleswhenvs<-/\\precedence behavior in fn headsPorted concept: preserve operator precedence model, and lower guard parsing precedence only for specific simple fn-head forms.
Source: precedence table
unwrap_when-style reassociationPorted concept: reassociate trailing
whenin head args so non-whenoperators bind to the last head arg/guard the same way compiler parser does.Source:
unwrap_when/1Nested-parens rejection semantics (
unexpected parentheses)Ported concept: reject nested parenthesized fn-head arg forms during fn-head construction, but keep Spitfire recovery by recording errors and continuing AST construction.