From 60850289f9c3ddba5f9be3ce4eda90de66921a60 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20=C5=9Awi=C4=85tkowski?= Date: Wed, 28 Jan 2026 14:12:29 +0100 Subject: [PATCH] fix(engine): handle matches against any struct --- .../indexer/extractors/struct_reference.ex | 6 ++++ .../extractors/struct_reference_test.exs | 36 +++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/apps/engine/lib/engine/search/indexer/extractors/struct_reference.ex b/apps/engine/lib/engine/search/indexer/extractors/struct_reference.ex index 1733684f..fa61d2fd 100644 --- a/apps/engine/lib/engine/search/indexer/extractors/struct_reference.ex +++ b/apps/engine/lib/engine/search/indexer/extractors/struct_reference.ex @@ -81,6 +81,12 @@ defmodule Engine.Search.Indexer.Extractors.StructReference do Analyzer.current_module(reducer.analysis, Reducer.position(reducer)) end + # Any-struct pattern: %_{} + defp expand_alias(:_, _reducer), do: :ignored + + # Struct pattern with variable binding: %struct_name{} + defp expand_alias(atom, _reducer) when is_atom(atom), do: :ignored + defp expand_alias(alias, %Reducer{} = reducer) do {line, column} = reducer.position diff --git a/apps/engine/test/engine/search/indexer/extractors/struct_reference_test.exs b/apps/engine/test/engine/search/indexer/extractors/struct_reference_test.exs index 96f3774d..a083e2b9 100644 --- a/apps/engine/test/engine/search/indexer/extractors/struct_reference_test.exs +++ b/apps/engine/test/engine/search/indexer/extractors/struct_reference_test.exs @@ -55,6 +55,24 @@ defmodule Engine.Search.Indexer.Extractors.StructReferenceTest do assert decorate(doc, struct.range) == "variable = «%MyStruct{}»" end + test "in an any-struct match on the left side and struct on the right" do + {:ok, [struct], doc} = ~q[%_{} = %MyStruct{}] |> index() + + assert struct.type == :struct + assert struct.subtype == :reference + assert struct.subject == Subject.module(MyStruct) + assert decorate(doc, struct.range) == "%_{} = «%MyStruct{}»" + end + + test "in an any-struct match with binding on the left side and struct on the right" do + {:ok, [struct], doc} = ~q[%str_name{} = %MyStruct{}] |> index() + + assert struct.type == :struct + assert struct.subtype == :reference + assert struct.subject == Subject.module(MyStruct) + assert decorate(doc, struct.range) == "%str_name{} = «%MyStruct{}»" + end + test "in a struct reference in params" do {:ok, [struct], doc} = ~q[ @@ -69,6 +87,24 @@ defmodule Engine.Search.Indexer.Extractors.StructReferenceTest do assert decorate(doc, struct.range) == ~S[def my_fn(«%MyStruct{}» = first) do] end + test "in an any-struct pattern in params" do + assert {:ok, [], _doc} = + ~q[ + def my_fn(%_{} = first) do + end + ] + |> index() + end + + test "in an any-struct pattern with binding in params" do + assert {:ok, [], _doc} = + ~q[ + def my_fn(%struct_name{} = first) do + end + ] + |> index() + end + test "in nested struct references" do {:ok, [outer, inner], doc} = ~q[