1
1
defmodule ExDoc.Language.Source do
2
2
@ moduledoc false
3
3
4
+ def anno_line ( line ) when is_integer ( line ) , do: abs ( line )
5
+ def anno_line ( anno ) , do: anno |> :erl_anno . line ( ) |> abs ( )
6
+
7
+ def anno_file ( anno ) do
8
+ case :erl_anno . file ( anno ) do
9
+ :undefined ->
10
+ nil
11
+
12
+ file ->
13
+ String.Chars . to_string ( file )
14
+ end
15
+ end
16
+
4
17
@ doc """
5
18
Get abstract code and basedir for a module
6
19
@@ -18,7 +31,7 @@ defmodule ExDoc.Language.Source do
18
31
end
19
32
20
33
defp expand_records_in_types ( abst_code ) do
21
- ## Find all records in ast and collect any fields with type declarations
34
+ # Find all records in ast and collect any fields with type declarations
22
35
records =
23
36
filtermap_ast ( abst_code , nil , fn
24
37
{ :attribute , anno , :record , { name , fields } } ->
@@ -40,7 +53,7 @@ defmodule ExDoc.Language.Source do
40
53
end )
41
54
|> Map . new ( )
42
55
43
- ## Expand records in all specs, callbacks, types and opaques
56
+ # Expand records in all specs, callbacks, types and opaques
44
57
filtermap_ast ( abst_code , nil , fn
45
58
{ :attribute , anno , kind , { mfa , ast } } when kind in [ :spec , :callback ] ->
46
59
ast = Enum . map ( ast , & expand_records ( & 1 , records ) )
@@ -62,16 +75,16 @@ defmodule ExDoc.Language.Source do
62
75
{ :ann_type , anno , [ name , expand_records ( type , records ) ] }
63
76
end
64
77
65
- ## When we encounter a record, we fetch the type definitions in the record and
66
- ## merge then with the type. If there are duplicates we take the one in the type
67
- ## declaration
78
+ # When we encounter a record, we fetch the type definitions in the record and
79
+ # merge then with the type. If there are duplicates we take the one in the type
80
+ # declaration
68
81
defp expand_records ( { :type , anno , :record , [ { :atom , _ , record } = name | args ] } , records ) do
69
82
args =
70
83
( args ++ Map . get ( records , record , [ ] ) )
71
84
|> Enum . uniq_by ( fn { :type , _ , :field_type , [ { :atom , _ , name } | _ ] } -> name end )
72
85
73
- ## We delete the record from the map so that recursive
74
- ## record definitions are not expanded.
86
+ # We delete the record from the map so that recursive
87
+ # record definitions are not expanded.
75
88
records = Map . delete ( records , record )
76
89
77
90
{ :type , anno , :record , expand_records ( [ name | args ] , records ) }
@@ -90,32 +103,31 @@ defmodule ExDoc.Language.Source do
90
103
end
91
104
92
105
@ doc """
93
- Get the basedir of a module
106
+ Fetches the basedir of a module.
94
107
95
108
The basedir is the cwd of the Elixir/Erlang compiler when compiling the module.
96
109
All `-file` attributes in the module is relative to this directory.
97
110
"""
98
- def get_basedir ( abst_code , module ) do
99
- ## We look for the first -file attribute to see what the source file that
100
- ## was compiled is called. Both Erlang and Elixir places one at the top.
111
+ def fetch_basedir! ( abst_code , module ) do
112
+ # We look for the first -file attribute to see what the source file that
113
+ # was compiled is called. Both Erlang and Elixir places one at the top.
101
114
filename =
102
115
Enum . find_value ( abst_code , fn
103
116
{ :attribute , _anno , :file , { filename , _line } } ->
104
117
filename
105
118
106
119
_ ->
107
120
nil
108
- end )
121
+ end ) || raise "could not find base directory for #{ inspect ( module ) } "
109
122
110
- ## The first -file attribute will be either relative or absolute
111
- ## depending on whether the compiler was called with an absolute
112
- ## or relative path.
123
+ # The first -file attribute will be either relative or absolute
124
+ # depending on whether the compiler was called with an absolute
125
+ # or relative path.
113
126
if Path . type ( filename ) == :relative do
114
- ## If the compiler was called with a relative path, then any other
115
- ## relative -file attribute will be relative to the same directory.
116
- ## We use `module_info(:compile)[:source]` to get an absolute path
117
- ## to the source file and calculate the basedir from that
118
-
127
+ # If the compiler was called with a relative path, then any other
128
+ # relative -file attribute will be relative to the same directory.
129
+ # We use `module_info(:compile)[:source]` to get an absolute path
130
+ # to the source file and calculate the basedir from that
119
131
compile_source =
120
132
cond do
121
133
source = module . module_info ( :compile ) [ :source ] ->
@@ -143,27 +155,27 @@ defmodule ExDoc.Language.Source do
143
155
|> Enum . drop ( Path . split ( filename ) |> Enum . count ( ) |> Kernel . * ( - 1 ) )
144
156
|> Path . join ( )
145
157
else
146
- ## If an absolute path was used, then any relative -file attribute
147
- ## is relative to the directory of the source file
158
+ # If an absolute path was used, then any relative -file attribute
159
+ # is relative to the directory of the source file
148
160
Path . dirname ( filename )
149
161
end
150
162
end
151
163
152
- def get_module_location ( abst_code , source_basedir , module ) do
164
+ def fetch_module_location! ( abst_code , source_basedir , module ) do
153
165
find_ast ( abst_code , source_basedir , fn
154
166
{ :attribute , anno , :module , ^ module } ->
155
167
{ anno_file ( anno ) , anno_line ( anno ) }
156
168
157
169
_ ->
158
170
nil
159
- end )
171
+ end ) || raise "could not find module definition for #{ inspect ( module ) } "
160
172
end
161
173
162
- def get_function_location ( module_data , { name , arity } ) do
174
+ def fetch_function_location! ( module_data , { name , arity } ) do
163
175
find_ast ( module_data . private . abst_code , module_data . source_basedir , fn
164
176
{ :function , anno , ^ name , ^ arity , _ } -> { anno_file ( anno ) , anno_line ( anno ) }
165
177
_ -> nil
166
- end )
178
+ end ) || raise "could not find function definition for #{ name } / #{ arity } "
167
179
end
168
180
169
181
# Returns a map of {name, arity} => spec.
@@ -178,7 +190,7 @@ defmodule ExDoc.Language.Source do
178
190
|> Map . new ( )
179
191
end
180
192
181
- def get_type_from_module_data ( module_data , name , arity ) do
193
+ def fetch_type! ( module_data , name , arity ) do
182
194
find_ast ( module_data . private . abst_code , module_data . source_basedir , fn
183
195
{ :attribute , anno , type , { ^ name , _ , args } = spec } = attr ->
184
196
if type in [ :opaque , :type ] and length ( args ) == arity do
@@ -193,7 +205,7 @@ defmodule ExDoc.Language.Source do
193
205
194
206
_ ->
195
207
nil
196
- end )
208
+ end ) || raise "could not find type definition for #{ name } / #{ arity } "
197
209
end
198
210
199
211
def get_callbacks ( abst_code , source_basedir ) do
@@ -215,20 +227,16 @@ defmodule ExDoc.Language.Source do
215
227
216
228
def get_optional_callbacks ( _module , _type ) , do: [ ]
217
229
218
- def find_ast ( ast , source_basedir , fun ) do
219
- filtermap_ast ( ast , source_basedir , fun ) |> hd ( )
230
+ defp find_ast ( ast , source_basedir , fun ) do
231
+ filtermap_ast ( ast , source_basedir , fun ) |> List . first ( )
220
232
end
221
233
222
- @ doc """
223
- Does a filtermap operation over the forms in an abstract syntax tree with
224
- updated anno for each form pointing to the correct file.
225
- """
226
234
# The file which a form belongs to is decided by the previous :file
227
235
# attribute in the AST. The :file can be either relative, or absolute
228
236
# depending on how the file was included. So when traversing the AST
229
237
# we need to keep track of the :file attributes and update the anno
230
238
# with the correct file.
231
- def filtermap_ast ( ast , source_basedir , fun ) do
239
+ defp filtermap_ast ( ast , source_basedir , fun ) do
232
240
Enum . reduce ( ast , { nil , [ ] } , fn
233
241
{ :attribute , _anno , :file , { filename , _line } } = entry , { _file , acc } ->
234
242
{ if Path . type ( filename ) == :relative && source_basedir do
@@ -262,17 +270,4 @@ defmodule ExDoc.Language.Source do
262
270
|> elem ( 1 )
263
271
|> Enum . reverse ( )
264
272
end
265
-
266
- def anno_line ( line ) when is_integer ( line ) , do: abs ( line )
267
- def anno_line ( anno ) , do: anno |> :erl_anno . line ( ) |> abs ( )
268
-
269
- def anno_file ( anno ) do
270
- case :erl_anno . file ( anno ) do
271
- :undefined ->
272
- nil
273
-
274
- file ->
275
- String.Chars . to_string ( file )
276
- end
277
- end
278
273
end
0 commit comments