Skip to content

Handle :local_or_var inside struct dot in SurroundContext.to_binding - #345

Merged
lukaszsamson merged 1 commit into
masterfrom
fix-surround-context-struct-var-dot
Oct 18, 2026
Merged

lukaszsamson merged 1 commit into
masterfrom
fix-surround-context-struct-var-dot

Conversation

@lukaszsamson

Copy link
Copy Markdown
Collaborator

Summary

ElixirSense.Core.SurroundContext.to_binding/2 crashed with a FunctionClauseError when the cursor was on a dotted identifier inside a struct literal, e.g. %nav.foo{}.

Code.Fragment.surround_context/2 reports the inner identifier as {:local_or_var, ~c"nav"}, but inside_dot_to_binding/2 only handled {:var, _}, so no clause matched.

This PR adds {:local_or_var, _} clauses that mirror the existing {:var, _} clauses, including the __MODULE__ special case.

Crash

** (FunctionClauseError) no function clause matching in ElixirSense.Core.SurroundContext.inside_dot_to_binding/2
  elixir_sense/core/surround_context.ex:97: ElixirSense.Core.SurroundContext.inside_dot_to_binding({:local_or_var, ~c"nav"}, nil)
  elixir_sense/core/surround_context.ex:35: ElixirSense.Core.SurroundContext.to_binding_impl/2
  elixir_sense/core/surround_context.ex:6: ElixirSense.Core.SurroundContext.to_binding/2
  language_server/providers/hover/docs.ex:112: ElixirLS.LanguageServer.Providers.Hover.Docs.all/3

Repro

mix run -e 'c = Code.Fragment.surround_context("%nav.foo{}", {1, 7}); IO.inspect ElixirSense.Core.SurroundContext.to_binding(c.context, nil)'

Before this change it raises; after it returns {{:variable, :nav, :any}, :foo}.

Other shapes checked

Probed every cursor column for %nav.foo{}, %nav.foo.bar{}, %mod.foo.bar{}, %foo.bar.baz{}, %x.y{a: 1}, %__MODULE__.foo{}, %__MODULE__.foo.bar{}, %@attr.foo{}, %@a.b.c{}, %Foo.bar{}, %Foo.Bar.baz{}, %Foo.bar().baz{}, %foo().bar{}, %(x).foo{}, %:erlang.foo{}. The only crashing inner shape was {:local_or_var, _}, which the new clauses cover. The other shapes already return a result.

Tests

New test/elixir_sense/core/surround_context_test.exs covers the repro, the other cursor columns, the nested and __MODULE__ variants, and the already-working @attr, alias, and call shapes. Against unmodified master, 9 of these tests fail.

Code.Fragment.surround_context/2 reports the inner identifier of
`%nav.foo{}` as {:local_or_var, ~c"nav"}, but inside_dot_to_binding/2 only
matched {:var, _}, raising FunctionClauseError from the hover provider.
Add {:local_or_var, _} clauses mirroring the {:var, _} ones, including the
__MODULE__ special case.
@lukaszsamson
lukaszsamson merged commit 20b5af9 into master Oct 18, 2026
12 of 18 checks passed
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.

1 participant