@@ -216,35 +216,46 @@ defmodule Exception do
216
216
@ doc """
217
217
Receives a tuple representing a stacktrace entry and formats it.
218
218
"""
219
- def format_stacktrace_entry ( entry )
219
+ def format_stacktrace_entry ( entry ) do
220
+ format_stacktrace_entry_into_fields ( entry )
221
+ |> tuple_to_list
222
+ |> Enum . filter ( fn field -> field && field != "" end )
223
+ |> Enum . join ( " " )
224
+ end
225
+
226
+ @ doc """
227
+ Returns the fields from a single frame in a stack trace as a list of
228
+ `[ app, location, mfa/module/file ]` where all but location can be nil.
229
+ Intended for use inside the Elixir libraries and iex only
230
+ """
220
231
221
232
# From Macro.Env.stacktrace
222
- def format_stacktrace_entry ( { module , :__MODULE__ , 0 , location } ) do
223
- format_location ( location ) <> inspect ( module ) <> " (module)"
233
+ def format_stacktrace_entry_into_fields ( { module , :__MODULE__ , 0 , location } ) do
234
+ { nil , format_location ( location ) , inspect ( module ) <> " (module)" }
224
235
end
225
236
226
237
# From :elixir_compiler_*
227
- def format_stacktrace_entry ( { _module , :__MODULE__ , 1 , location } ) do
228
- format_location ( location ) <> "(module)"
238
+ def format_stacktrace_entry_into_fields ( { _module , :__MODULE__ , 1 , location } ) do
239
+ { nil , format_location ( location ) , "(module)" }
229
240
end
230
241
231
242
# From :elixir_compiler_*
232
- def format_stacktrace_entry ( { _module , :__FILE__ , 1 , location } ) do
233
- format_location ( location ) <> "(file)"
243
+ def format_stacktrace_entry_into_fields ( { _module , :__FILE__ , 1 , location } ) do
244
+ { nil , format_location ( location ) , "(file)" }
234
245
end
235
246
236
- def format_stacktrace_entry ( { module , fun , arity , location } ) do
237
- format_application ( module ) <> format_location ( location ) <> format_mfa ( module , fun , arity )
247
+ def format_stacktrace_entry_into_fields ( { module , fun , arity , location } ) do
248
+ { format_application ( module ) , format_location ( location ) , format_mfa ( module , fun , arity ) }
238
249
end
239
250
240
- def format_stacktrace_entry ( { fun , arity , location } ) do
241
- format_location ( location ) <> format_fa ( fun , arity )
251
+ def format_stacktrace_entry_into_fields ( { fun , arity , location } ) do
252
+ { nil , format_location ( location ) , format_fa ( fun , arity ) }
242
253
end
243
254
244
255
defp format_application ( module ) do
245
256
case :application . get_application ( module ) do
246
- { :ok , app } -> "(" <> atom_to_binary ( app ) <> ") "
247
- :undefined -> ""
257
+ { :ok , app } -> "(" <> atom_to_binary ( app ) <> ")"
258
+ :undefined -> nil
248
259
end
249
260
end
250
261
@@ -261,7 +272,6 @@ defmodule Exception do
261
272
catch
262
273
:stacktrace -> Enum . drop ( :erlang . get_stacktrace , 1 )
263
274
end
264
-
265
275
case trace do
266
276
[ ] -> "\n "
267
277
s -> " " <> Enum . map_join ( s , "\n " , & format_stacktrace_entry ( & 1 ) ) <> "\n "
@@ -303,74 +313,97 @@ defmodule Exception do
303
313
304
314
"""
305
315
def format_fa ( fun , arity ) do
306
- if is_list ( arity ) do
307
- inspected = lc x inlist arity , do: inspect ( x )
308
- "#{ inspect fun } (#{ Enum . join ( inspected , ", " ) } )"
309
- else
310
- "#{ inspect fun } /#{ arity } "
311
- end
316
+ "#{ inspect fun } #{ format_arity ( arity ) } "
312
317
end
313
318
314
319
@ doc """
315
320
Receives a module, fun and arity and formats it
316
321
as shown in stacktraces. The arity may also be a list
317
322
of arguments.
318
-
323
+
319
324
## Examples
320
-
321
325
iex> Exception.format_mfa Foo, :bar, 1
322
326
"Foo.bar/1"
323
327
iex> Exception.format_mfa Foo, :bar, []
324
328
"Foo.bar()"
325
329
iex> Exception.format_mfa nil, :bar, []
326
330
"nil.bar()"
327
331
332
+ Anonymous functions are reported as -func/arity-anonfn-count-,
333
+ where func is the name of the enclosing function. Convert to
334
+ "nth fn in func/arity"
328
335
"""
329
- def format_mfa ( module , fun , arity ) do
330
- fun =
331
- case inspect ( fun ) do
332
- << ?: , erl :: binary >> -> erl
333
- elixir -> elixir
334
- end
335
336
336
- if is_list ( arity ) do
337
- inspected = lc x inlist arity , do: inspect ( x )
338
- "#{ inspect module } .#{ fun } (#{ Enum . join ( inspected , ", " ) } )"
339
- else
340
- "#{ inspect module } .#{ fun } /#{ arity } "
341
- end
337
+ def format_mfa ( module , nil , arity ) ,
338
+ do: do_format_mfa ( module , "nil" , arity )
339
+
340
+ def format_mfa ( module , fun , arity ) when is_atom ( fun ) ,
341
+ do: do_format_mfa ( module , to_string ( fun ) , arity )
342
+
343
+ defp do_format_mfa ( module , fun , arity ) when not ( is_binary ( fun ) ) ,
344
+ do: format_mfa ( module , inspect ( fun ) , arity )
345
+
346
+ defp do_format_mfa ( module , "-" <> fun , arity ) do
347
+ [ outer_fun , "fun" , count , "" ] = String . split ( fun , "-" )
348
+ "#{ format_nth ( count ) } anonymous fn#{ format_arity ( arity ) } in #{ inspect module } .#{ outer_fun } "
342
349
end
343
350
351
+ # Erlang internal
352
+ defp do_format_mfa ( module , ":" <> fun , arity ) ,
353
+ do: format_mfa ( module , maybe_quote_name ( fun ) , arity )
354
+
355
+ defp do_format_mfa ( module , fun , arity ) do
356
+ "#{ inspect module } .#{ maybe_quote_name ( fun ) } #{ format_arity ( arity ) } "
357
+ end
358
+
359
+ defp format_arity ( arity ) when is_list ( arity ) do
360
+ inspected = lc x inlist arity , do: inspect ( x )
361
+ "(#{ Enum . join ( inspected , ", " ) } )"
362
+ end
363
+
364
+ defp format_arity ( arity ) , do: "/#{ arity } "
365
+
366
+ defp format_nth ( "0" ) , do: "first"
367
+ defp format_nth ( "1" ) , do: "second"
368
+ defp format_nth ( "2" ) , do: "third"
369
+ defp format_nth ( n ) , do: "#{ binary_to_integer ( n ) + 1 } th"
370
+
371
+
344
372
@ doc """
345
373
Formats the given file and line as shown in stacktraces.
346
- If any of the values are nil, they are omitted.
374
+ If any of the values are nil, they are omitted. If the
375
+ optional suffix is omitted, a space is appended to
376
+ the result.
347
377
348
378
## Examples
349
379
350
380
iex> Exception.format_file_line("foo", 1)
351
381
"foo:1: "
352
382
383
+ iex> Exception.format_file_line("foo", 1, "")
384
+ "foo:1:"
385
+
353
386
iex> Exception.format_file_line("foo", nil)
354
387
"foo: "
355
388
356
389
iex> Exception.format_file_line(nil, nil)
357
390
""
358
391
359
392
"""
360
- def format_file_line ( file , line ) do
393
+ def format_file_line ( file , line , suffix // " " ) do
361
394
if file do
362
395
if line && line != 0 do
363
- "#{ file } :#{ line } : "
396
+ "#{ file } :#{ line } :#{ suffix } "
364
397
else
365
- "#{ file } : "
398
+ "#{ file } :#{ suffix } "
366
399
end
367
400
else
368
401
""
369
402
end
370
403
end
371
404
372
405
defp format_location ( opts ) do
373
- format_file_line Keyword . get ( opts , :file ) , Keyword . get ( opts , :line )
406
+ format_file_line Keyword . get ( opts , :file ) , Keyword . get ( opts , :line ) , ""
374
407
end
375
408
376
409
defp from_stacktrace ( [ { module , function , args , _ } | _ ] ) when is_list ( args ) do
@@ -384,4 +417,49 @@ defmodule Exception do
384
417
defp from_stacktrace ( _ ) do
385
418
{ nil , nil , nil }
386
419
end
420
+
421
+
422
+ def function_name_pattern do
423
+ % r {
424
+ \A(
425
+ [ \w] + [ ?! ] ?
426
+ | ->
427
+ | <-
428
+ | ::
429
+ | \|{ 1 , 3 }
430
+ | =
431
+ | &&&?
432
+ | <=?
433
+ | >=?
434
+ | === ?
435
+ | !==?
436
+ | =~
437
+ | <<<
438
+ | >>>
439
+ | \+ \+ ?
440
+ | -- ?
441
+ | <>
442
+ | \+
443
+ | -
444
+ | \*
445
+ | //?
446
+ | ^^^
447
+ | !
448
+ | \^
449
+ | &
450
+ | ~~~
451
+ | @
452
+ ) \z} x
453
+ end
454
+
455
+ defp maybe_quote_name ( fun ) do
456
+ name = to_string ( fun )
457
+ if Regex . match? ( function_name_pattern , name ) do
458
+ name
459
+ else
460
+ inspect name
461
+ end
462
+ end
463
+
464
+
387
465
end
0 commit comments