From 33c6c496abfcd5bd2e165b558a033c5b80b81020 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 30 Sep 2026 10:27:36 +0200 Subject: [PATCH 1/2] Do not end type directives at the dot of a range The type-directive end pattern `(\)?)\s*+(\.)` matched the first dot of a range such as `1..10`, closing the meta.type.erlang region early. A fun type after the range was then no longer covered by the function type injection, fell through to the fun expression rules and scoped the rest of the file as the body of a fun: -type t() :: 1..10 | fun(() -> ok). Only end the directive at a dot followed by whitespace, a comment or the end of the line, which is how Erlang itself terminates a form. --- Erlang.plist | 2 +- tests/fun_expression.erl | 18 ++++++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/Erlang.plist b/Erlang.plist index af4f60b..ed07298 100644 --- a/Erlang.plist +++ b/Erlang.plist @@ -3787,7 +3787,7 @@ comment Directive carrying types; meta.type.erlang marks the region where the function type rule is injected end - (\)?)\s*+(\.) + (\)?)\s*+(\.)(?=\s|%|$) endCaptures 1 diff --git a/tests/fun_expression.erl b/tests/fun_expression.erl index 6dd0513..ba489f3 100644 --- a/tests/fun_expression.erl +++ b/tests/fun_expression.erl @@ -73,3 +73,21 @@ h() -> fun ((X)) -> X end. -callback cb() -> fun((a) -> b). % ^^^ source.erlang meta.directive.erlang meta.type.erlang entity.name.function.erlang + +%% A range at the top level of a type does not end the type region: only a dot +%% followed by whitespace, a comment or the end of the line ends the form. +-type ranged_fun() :: 1..10 | fun(() -> ok). +% ^^^ source.erlang meta.directive.erlang meta.type.erlang entity.name.function.erlang +% ^ source.erlang meta.directive.erlang meta.type.erlang punctuation.section.directive.end.erlang + +-spec ranged_spec(X, Y) -> ok when X :: 0..255, Y :: fun(() -> ok). +% ^^^ source.erlang meta.directive.erlang meta.type.erlang entity.name.function.erlang +ranged_spec(_, _) -> ok. +%<----------- source.erlang meta.function.erlang entity.name.function.definition.erlang + +-type ranged_fun_commented() :: 1..10 | fun(() -> ok).% comment +% ^^^ source.erlang meta.directive.erlang meta.type.erlang entity.name.function.erlang +% ^ source.erlang meta.directive.erlang meta.type.erlang punctuation.section.directive.end.erlang + +k() -> ok. +%<- source.erlang meta.function.erlang entity.name.function.definition.erlang From afa7744d4c1a4ca07e96a3bbbb127d222edbe920 Mon Sep 17 00:00:00 2001 From: Roberto Aloi Date: Wed, 30 Sep 2026 10:27:54 +0200 Subject: [PATCH 2/2] Treat -nominal as a type directive Nominal types (OTP 28) were handled by the generic directive rule, so they never got the meta.type.erlang scope. A fun type in one fell through to the fun expression rules and scoped the rest of the file as the body of a fun: -nominal n() :: fun(() -> ok). Add nominal to the directives matched by type-directive. --- Erlang.plist | 2 +- tests/fun_expression.erl | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Erlang.plist b/Erlang.plist index ed07298..de33086 100644 --- a/Erlang.plist +++ b/Erlang.plist @@ -3765,7 +3765,7 @@ type-directive begin - ^\s*+(-)\s*+(type|opaque|spec|callback)(?![a-zA-Z\d@_])\s*+(\(?) + ^\s*+(-)\s*+(type|opaque|nominal|spec|callback)(?![a-zA-Z\d@_])\s*+(\(?) beginCaptures 1 diff --git a/tests/fun_expression.erl b/tests/fun_expression.erl index ba489f3..655b8e9 100644 --- a/tests/fun_expression.erl +++ b/tests/fun_expression.erl @@ -91,3 +91,11 @@ ranged_spec(_, _) -> ok. k() -> ok. %<- source.erlang meta.function.erlang entity.name.function.definition.erlang + +%% Nominal types (OTP 28) carry types too. +-nominal nominal_fun() :: fun(() -> ok). +%^^^^^^^ source.erlang meta.directive.erlang meta.type.erlang keyword.control.directive.erlang +% ^^^ source.erlang meta.directive.erlang meta.type.erlang entity.name.function.erlang + +l() -> ok. +%<- source.erlang meta.function.erlang entity.name.function.definition.erlang