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