From 2e342ef3637543171644d5c6c090cd2e28e413bf Mon Sep 17 00:00:00 2001
From: Marko Mindek <marko.mindek@gmail.com>
Date: Fri, 13 Sep 2024 12:28:35 +0200
Subject: [PATCH 1/3] failing testcase

---
 ...diagnostics_bound_var_in_pattern_maybe.erl | 20 +++++++++++++++++++
 apps/els_lsp/test/els_diagnostics_SUITE.erl   | 20 +++++++++++++++++++
 2 files changed, 40 insertions(+)
 create mode 100644 apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl

diff --git a/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl b/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
new file mode 100644
index 00000000..d6d87743
--- /dev/null
+++ b/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
@@ -0,0 +1,20 @@
+%% https://github.com/erlang-ls/erlang_ls/issues/1527
+%% Only valid for OTP25 or higher
+-module(diagnostics_bound_var_in_pattern_maybe).
+
+-if(?OTP_RELEASE<27).
+-feature(maybe_expr, enable).
+-endif.
+
+-export([foo/0]).
+
+foo() ->
+    maybe
+        foo ?= bar()
+    else
+        e = Error -> Error
+          % ^- Bound variable in pattern: Error
+    end.
+
+bar() ->
+    foo.
diff --git a/apps/els_lsp/test/els_diagnostics_SUITE.erl b/apps/els_lsp/test/els_diagnostics_SUITE.erl
index 1ae1cf2e..e718e2b3 100644
--- a/apps/els_lsp/test/els_diagnostics_SUITE.erl
+++ b/apps/els_lsp/test/els_diagnostics_SUITE.erl
@@ -14,6 +14,7 @@
 -export([
     atom_typo/1,
     bound_var_in_pattern/1,
+    bound_var_in_pattern_maybe/1,
     bound_var_in_pattern_cannot_parse/1,
     compiler/1,
     compiler_with_behaviour/1,
@@ -193,6 +194,15 @@ init_per_testcase(TestCase, Config) when
 ->
     mock_refactorerl(),
     els_test_utils:init_per_testcase(TestCase, Config);
+init_per_testcase(TestCase, Config) when
+    TestCase =:= bound_var_in_pattern_maybe
+->
+    case ?OTP_RELEASE < 25 of
+        true ->
+            {skip, "Maybe expressions are only supported from OTP 25"};
+        false ->
+            Config
+    end;
 init_per_testcase(TestCase, Config) ->
     els_mock_diagnostics:setup(),
     els_test_utils:init_per_testcase(TestCase, Config).
@@ -363,6 +373,16 @@ bound_var_in_pattern(_Config) ->
         end,
     els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints).
 
+%% #1527
+-spec bound_var_in_pattern_maybe(config()) -> ok.
+bound_var_in_pattern_maybe(_Config) ->
+    Path = src_path("diagnostics_bound_var_in_pattern_maybe.erl"),
+    Source = <<"BoundVarInPattern">>,
+    Errors = [],
+    Warnings = [],
+    Hints = [],
+    els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints).
+
 -spec bound_var_in_pattern_cannot_parse(config()) -> ok.
 bound_var_in_pattern_cannot_parse(_Config) ->
     Path = src_path("diagnostics_bound_var_in_pattern_cannot_parse.erl"),

From c0e3323e0e576912cd8b67ae4e92d23e0df8893c Mon Sep 17 00:00:00 2001
From: Marko Mindek <marko.mindek@gmail.com>
Date: Fri, 13 Sep 2024 13:26:13 +0200
Subject: [PATCH 2/3] els_diagnostic_SUITE:bound_var_in_pattern/1 update

---
 .../diagnostics_bound_var_in_pattern_maybe.erl    | 11 ++++++++++-
 apps/els_lsp/test/els_diagnostics_SUITE.erl       | 15 +++++++++++++--
 2 files changed, 23 insertions(+), 3 deletions(-)

diff --git a/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl b/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
index d6d87743..18db9b25 100644
--- a/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
+++ b/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
@@ -6,7 +6,7 @@
 -feature(maybe_expr, enable).
 -endif.
 
--export([foo/0]).
+-export([foo/0, maybe_expr/0]).
 
 foo() ->
     maybe
@@ -18,3 +18,12 @@ foo() ->
 
 bar() ->
     foo.
+
+maybe_expr() ->
+    X = 1,
+    Y = ok,
+    maybe
+        X ?= 1
+    else
+        Y -> Y
+    end.
diff --git a/apps/els_lsp/test/els_diagnostics_SUITE.erl b/apps/els_lsp/test/els_diagnostics_SUITE.erl
index e718e2b3..38f9fc19 100644
--- a/apps/els_lsp/test/els_diagnostics_SUITE.erl
+++ b/apps/els_lsp/test/els_diagnostics_SUITE.erl
@@ -202,7 +202,9 @@ init_per_testcase(TestCase, Config) when
             {skip, "Maybe expressions are only supported from OTP 25"};
         false ->
             Config
-    end;
+    end,
+    els_mock_diagnostics:setup(),
+    els_test_utils:init_per_testcase(TestCase, Config);
 init_per_testcase(TestCase, Config) ->
     els_mock_diagnostics:setup(),
     els_test_utils:init_per_testcase(TestCase, Config).
@@ -380,7 +382,16 @@ bound_var_in_pattern_maybe(_Config) ->
     Source = <<"BoundVarInPattern">>,
     Errors = [],
     Warnings = [],
-    Hints = [],
+    Hints = [
+        #{
+            message => <<"Bound variable in pattern: X">>,
+            range => {{24, 9}, {24, 10}}
+        },
+        #{
+            message => <<"Bound variable in pattern: Y">>,
+            range => {{27, 9}, {27, 10}}
+        }
+    ],
     els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints).
 
 -spec bound_var_in_pattern_cannot_parse(config()) -> ok.

From 827bc107567084f26f02f23f55ab8a3179538e7c Mon Sep 17 00:00:00 2001
From: Marko Mindek <marko.mindek@gmail.com>
Date: Sat, 14 Sep 2024 01:17:31 +0200
Subject: [PATCH 3/3] testcases update, erlfmt ast handle, maybe_match_expr

---
 ...diagnostics_bound_var_in_pattern_maybe.erl | 14 ++++++++++--
 .../els_bound_var_in_pattern_diagnostics.erl  |  4 ++++
 apps/els_lsp/src/els_erlfmt_ast.erl           |  2 ++
 apps/els_lsp/test/els_diagnostics_SUITE.erl   | 22 +++++++++++--------
 rebar.config                                  |  5 ++---
 rebar.lock                                    |  6 ++---
 6 files changed, 36 insertions(+), 17 deletions(-)

diff --git a/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl b/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
index 18db9b25..39ddd60e 100644
--- a/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
+++ b/apps/els_lsp/priv/code_navigation/src/diagnostics_bound_var_in_pattern_maybe.erl
@@ -6,11 +6,12 @@
 -feature(maybe_expr, enable).
 -endif.
 
--export([foo/0, maybe_expr/0]).
+-export([foo/0, maybe_expr/0, no_else/0]).
 
 foo() ->
     maybe
-        foo ?= bar()
+        X ?= bar(),
+        X == foo
     else
         e = Error -> Error
           % ^- Bound variable in pattern: Error
@@ -27,3 +28,12 @@ maybe_expr() ->
     else
         Y -> Y
     end.
+
+no_else() ->
+    Y = 1,
+    maybe
+        {ok, Y} ?= 2,
+        X = 4,
+        Z ?= 8,
+        X == Z
+    end.
diff --git a/apps/els_lsp/src/els_bound_var_in_pattern_diagnostics.erl b/apps/els_lsp/src/els_bound_var_in_pattern_diagnostics.erl
index 4e61a8ce..6154cec7 100644
--- a/apps/els_lsp/src/els_bound_var_in_pattern_diagnostics.erl
+++ b/apps/els_lsp/src/els_bound_var_in_pattern_diagnostics.erl
@@ -115,6 +115,10 @@ find_vars_in_tree(Tree, Acc) ->
             Pattern = erl_syntax:match_expr_pattern(Tree),
             NewAcc = fold_pattern(Pattern, Acc),
             find_vars_in_tree(erl_syntax:match_expr_body(Tree), NewAcc);
+        maybe_match_expr ->
+            Pattern = erl_syntax:maybe_match_expr_pattern(Tree),
+            NewAcc = fold_pattern(Pattern, Acc),
+            find_vars_in_tree(erl_syntax:maybe_match_expr_body(Tree), NewAcc);
         clause ->
             Patterns = erl_syntax:clause_patterns(Tree),
             NewAcc = fold_pattern_list(Patterns, Acc),
diff --git a/apps/els_lsp/src/els_erlfmt_ast.erl b/apps/els_lsp/src/els_erlfmt_ast.erl
index bbff5e11..5de4b3ce 100644
--- a/apps/els_lsp/src/els_erlfmt_ast.erl
+++ b/apps/els_lsp/src/els_erlfmt_ast.erl
@@ -46,6 +46,8 @@ erlfmt_to_st(Node) ->
         %% The special `match` node is encoded as a regular binary operator
         {op, Pos, '=', Left, Right} ->
             erlfmt_to_st_1({match, Pos, Left, Right});
+        {op, Pos, '?=', Left, Right} ->
+            erlfmt_to_st_1({maybe_match, Pos, Left, Right});
         %% The special `catch` node is encoded as a regular unary operator
         {op, Pos, 'catch', Expr} ->
             erlfmt_to_st_1({'catch', Pos, Expr});
diff --git a/apps/els_lsp/test/els_diagnostics_SUITE.erl b/apps/els_lsp/test/els_diagnostics_SUITE.erl
index 38f9fc19..3f656352 100644
--- a/apps/els_lsp/test/els_diagnostics_SUITE.erl
+++ b/apps/els_lsp/test/els_diagnostics_SUITE.erl
@@ -383,15 +383,19 @@ bound_var_in_pattern_maybe(_Config) ->
     Errors = [],
     Warnings = [],
     Hints = [
-        #{
-            message => <<"Bound variable in pattern: X">>,
-            range => {{24, 9}, {24, 10}}
-        },
-        #{
-            message => <<"Bound variable in pattern: Y">>,
-            range => {{27, 9}, {27, 10}}
-        }
-    ],
+                        #{
+                            message => <<"Bound variable in pattern: X">>,
+                            range => {{26, 8}, {26, 9}}
+                        },
+                        #{
+                            message => <<"Bound variable in pattern: Y">>,
+                            range => {{28, 8}, {28, 9}}
+                        },
+                        #{
+                            message => <<"Bound variable in pattern: Y">>,
+                            range => {{34, 13}, {34, 14}}
+                        }
+                    ],
     els_test:run_diagnostics_test(Path, Source, Errors, Warnings, Hints).
 
 -spec bound_var_in_pattern_cannot_parse(config()) -> ok.
diff --git a/rebar.config b/rebar.config
index edfc81c1..e1bbffc3 100644
--- a/rebar.config
+++ b/rebar.config
@@ -2,8 +2,7 @@
     debug_info,
     warnings_as_errors,
     warn_export_vars,
-    warn_unused_import,
-    warn_missing_spec_all
+    warn_unused_import
 ]}.
 
 {deps, [
@@ -15,7 +14,7 @@
     {docsh, "0.7.2"},
     {elvis_core, "~> 3.2.2"},
     {rebar3_format, "0.8.2"},
-    {erlfmt, "1.3.0"},
+    {erlfmt, "1.5.0"},
     {ephemeral, "2.0.4"},
     {tdiff, "0.1.2"},
     {uuid, "2.0.1", {pkg, uuid_erl}},
diff --git a/rebar.lock b/rebar.lock
index 2a76758b..4f7b1faf 100644
--- a/rebar.lock
+++ b/rebar.lock
@@ -3,7 +3,7 @@
  {<<"docsh">>,{pkg,<<"docsh">>,<<"0.7.2">>},0},
  {<<"elvis_core">>,{pkg,<<"elvis_core">>,<<"3.2.2">>},0},
  {<<"ephemeral">>,{pkg,<<"ephemeral">>,<<"2.0.4">>},0},
- {<<"erlfmt">>,{pkg,<<"erlfmt">>,<<"1.3.0">>},0},
+ {<<"erlfmt">>,{pkg,<<"erlfmt">>,<<"1.5.0">>},0},
  {<<"getopt">>,{pkg,<<"getopt">>,<<"1.0.1">>},2},
  {<<"gradualizer">>,
   {git,"https://github.com/josefs/Gradualizer.git",
@@ -28,7 +28,7 @@
  {<<"docsh">>, <<"F893D5317A0E14269DD7FE79CF95FB6B9BA23513DA0480EC6E77C73221CAE4F2">>},
  {<<"elvis_core">>, <<"D5AE5FB7ACDF9D23A2AA3F6E4610490A06F7E8FB33EE65E09C5EA3A0ECF64A73">>},
  {<<"ephemeral">>, <<"B3E57886ADD5D90C82FE3880F5954978222A122CB8BAA123667401BBAAEC51D6">>},
- {<<"erlfmt">>, <<"672994B92B1A809C04C46F0B781B447BF9AB7A515F5856A96177BC1962F100A9">>},
+ {<<"erlfmt">>, <<"5DDECA120A6E8E0A0FAB7D0BB9C2339D841B1C9E51DD135EE583256DEF20DE25">>},
  {<<"getopt">>, <<"C73A9FA687B217F2FF79F68A3B637711BB1936E712B521D8CE466B29CBF7808A">>},
  {<<"jsx">>, <<"20A170ABD4335FC6DB24D5FAD1E5D677C55DADF83D1B20A8A33B5FE159892A39">>},
  {<<"katana_code">>, <<"0C42BDCD7E59995876AED9F678CF62E3D12EF42E0FBB2190556E64BFEBDD15C6">>},
@@ -44,7 +44,7 @@
  {<<"docsh">>, <<"4E7DB461BB07540D2BC3D366B8513F0197712D0495BB85744F367D3815076134">>},
  {<<"elvis_core">>, <<"3786F027751CC265E7389BF5AC1329DB547510D80F499B45EFE771BDAF889B36">>},
  {<<"ephemeral">>, <<"4B293D80F75F9C4575FF4B9C8E889A56802F40B018BF57E74F19644EFEE6C850">>},
- {<<"erlfmt">>, <<"2A84AA1EBA2F4FCD7DD31D5C57E9DE2BC2705DDA18DA4553F27DF7114CFAA052">>},
+ {<<"erlfmt">>, <<"3933A40CFBE790AD94E5B650B36881DE70456319263C1479B556E9AFDBD80C75">>},
  {<<"getopt">>, <<"53E1AB83B9CEB65C9672D3E7A35B8092E9BDC9B3EE80721471A161C10C59959C">>},
  {<<"jsx">>, <<"37BECA0435F5CA8A2F45F76A46211E76418FBEF80C36F0361C249FC75059DC6D">>},
  {<<"katana_code">>, <<"AE3BBACA187511588F69695A9FF22251CB2CC672FDCCC180289779BDD25175EF">>},