Error when Module Attribute in Docstring
Hello there 👋🏼
When a module attribute is used in a docstring, elixir_sense appears to raise an error while building metadata for function definitions. A reduced test case is available here. Anecdotally, this issue affects an entire project when any function utilizes module attributes in this way.
- Elixir 1.16.0
- ElixirLS 0.20.0
- VS Code 1.87.0
- elixir_sense 2.0.0
diff --git a/lib/test.ex b/lib/test.ex
index d0c8c5b..61f44c5 100644
--- a/lib/test.ex
+++ b/lib/test.ex
@@ -1,10 +1,12 @@
defmodule Test do
+ @some_attribute 42
+
@moduledoc """
Documentation for `Test`.
"""
@doc """
- Hello world.
+ Hello world. #{@some_attribute}
## Examples
The error:
[Warn - 6:20:06 PM] ** (ErlangError) Erlang error: "Protocol.UndefinedError during metadata build pre:\nprotocol String.Chars not implemented for {%ArgumentError{message: \"cannot invoke @/1 outside module\"}, [{Kernel, :assert_module_scope, 3, [file: ~c\"lib/kernel.ex\", line: 6555]}, {Kernel, :@, 1, [file: ~c\"expanding macro\"]}, {:elixir_compiler_0, :__FILE__, 1, [file: ~c\"nofile\", line: 9]}, {Kernel, :to_string, 1, [file: ~c\"expanding macro\"]}, {:elixir_compiler_0, :__FILE__, 1, [file: ~c\"nofile\", line: 9]}]} of type Tuple\nast node: {:def, [do: [line: 17, column: 13], end: [line: 19, column: 3], line: 17, column: 3], [{:hello, [line: 17, column: 7], nil}, [do: :world]]}"
(elixir 1.16.0) lib/string/chars.ex:3: String.Chars.impl_for!/1
(elixir 1.16.0) lib/string/chars.ex:22: String.Chars.to_string/1
(elixir_sense 2.0.0) lib/elixir_sense/core/state.ex:1547: ElixirSense.Core.State.format_doc_arg/1
(elixir_sense 2.0.0) lib/elixir_sense/core/state.ex:1504: anonymous fn/2 in ElixirSense.Core.State.reduce_doc_context/1
(elixir 1.16.0) lib/enum.ex:2528: Enum."-reduce/3-lists^foldl/2-0-"/3
(elixir_sense 2.0.0) lib/elixir_sense/core/state.ex:1492: ElixirSense.Core.State.consume_doc_context/1
(elixir_sense 2.0.0) lib/elixir_sense/core/state.ex:842: ElixirSense.Core.State.add_func_to_index/7
(elixir_sense 2.0.0) lib/elixir_sense/core/metadata_builder.ex:309: ElixirSense.Core.MetadataBuilder.pre_func/6
(elixir_sense 2.0.0) lib/elixir_sense/core/metadata_builder.ex:102: ElixirSense.Core.MetadataBuilder.safe_call_pre/2
(elixir 1.16.0) lib/macro.ex:637: anonymous fn/4 in Macro.do_traverse_args/4
(stdlib 3.17.1) lists.erl:1358: :lists.mapfoldl/3
(stdlib 3.17.1) lists.erl:1359: :lists.mapfoldl/3
(elixir 1.16.0) lib/macro.ex:602: Macro.do_traverse/4
(elixir 1.16.0) lib/macro.ex:617: Macro.do_traverse/4
(stdlib 3.17.1) lists.erl:1358: :lists.mapfoldl/3
(elixir 1.16.0) lib/macro.ex:622: Macro.do_traverse/4
@aj-foster That's not a common thing to do. Please send a PR fixing that. You would need to expand @ macro before trying to get the string out of the @doc attribute in format_doc_arg. Otherwise the minimal solution is to catch the error and return a dummy ""
@lukaszsamson See #291 for a PR that solves the immediate issue (the rescue block raised an unrelated error because it failed to interpolate the exception).
I would love to expand @, however, I'm not sure how to reconstruct the Macro.Env necessary to do so. It seems like all of the necessary information is in the state, but I'm unsure how to safely transform it. Do you have any advice?
We are currently discussing this in the context of module attributes, but will this same issue occur for any non-primitive interpolation (ex. a function or macro call)? I'm concerned about the way doc ASTs are evaluated out of context.
Finally, I'm confused about the cause of this issue: did a recent change introduce it? I've certainly used module attributes in docstrings before, with accurate intellisense in VS Code.
I would love to expand @, however, I'm not sure how to reconstruct the Macro.Env necessary to do so. It seems like all of the necessary information is in the state, but I'm unsure how to safely transform it. Do you have any advice?
There is a plan to add Macro.Env building in elixir 1.17. Meanwhile here is the code doing use expansion https://github.com/elixir-lsp/elixir_sense/blob/33eb7ab8348dbe35672f00ae0ecb3a44fb630893/lib/elixir_sense/core/metadata_builder.ex#L1298
We are currently discussing this in the context of module attributes, but will this same issue occur for any non-primitive interpolation (ex. a function or macro call)?
Most of those non standard AST branches are simply omitted. In particular everything in quote is omitted. elixir_sense is not a full blown compiler.
Finally, I'm confused about the cause of this issue: did a recent change introduce it?
Yes, this is an unexpected side effect of https://github.com/elixir-lsp/elixir_sense/pull/288
Since MetadataBuilder has been reimplemented this should no longer be an issue. The AST expansion continues and only a warning is emitted.
Unable to format docstring expression: ...
** (ArgumentError) cannot invoke @/1 outside module
@aj-foster please let me know if your project is still affected