@@ -216,35 +216,46 @@ defmodule Exception do
216216 @ doc """
217217 Receives a tuple representing a stacktrace entry and formats it.
218218 """
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+ """
220231
221232 # 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)" }
224235 end
225236
226237 # 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)" }
229240 end
230241
231242 # 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)" }
234245 end
235246
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 ) }
238249 end
239250
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 ) }
242253 end
243254
244255 defp format_application ( module ) do
245256 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
248259 end
249260 end
250261
@@ -261,7 +272,6 @@ defmodule Exception do
261272 catch
262273 :stacktrace -> Enum . drop ( :erlang . get_stacktrace , 1 )
263274 end
264-
265275 case trace do
266276 [ ] -> "\n "
267277 s -> " " <> Enum . map_join ( s , "\n " , & format_stacktrace_entry ( & 1 ) ) <> "\n "
@@ -303,74 +313,97 @@ defmodule Exception do
303313
304314 """
305315 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 ) } "
312317 end
313318
314319 @ doc """
315320 Receives a module, fun and arity and formats it
316321 as shown in stacktraces. The arity may also be a list
317322 of arguments.
318-
323+
319324 ## Examples
320-
321325 iex> Exception.format_mfa Foo, :bar, 1
322326 "Foo.bar/1"
323327 iex> Exception.format_mfa Foo, :bar, []
324328 "Foo.bar()"
325329 iex> Exception.format_mfa nil, :bar, []
326330 "nil.bar()"
327331
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"
328335 """
329- def format_mfa ( module , fun , arity ) do
330- fun =
331- case inspect ( fun ) do
332- << ?: , erl :: binary >> -> erl
333- elixir -> elixir
334- end
335336
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 } "
342349 end
343350
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+
344372 @ doc """
345373 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.
347377
348378 ## Examples
349379
350380 iex> Exception.format_file_line("foo", 1)
351381 "foo:1: "
352382
383+ iex> Exception.format_file_line("foo", 1, "")
384+ "foo:1:"
385+
353386 iex> Exception.format_file_line("foo", nil)
354387 "foo: "
355388
356389 iex> Exception.format_file_line(nil, nil)
357390 ""
358391
359392 """
360- def format_file_line ( file , line ) do
393+ def format_file_line ( file , line , suffix // " " ) do
361394 if file do
362395 if line && line != 0 do
363- "#{ file } :#{ line } : "
396+ "#{ file } :#{ line } :#{ suffix } "
364397 else
365- "#{ file } : "
398+ "#{ file } :#{ suffix } "
366399 end
367400 else
368401 ""
369402 end
370403 end
371404
372405 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 ) , ""
374407 end
375408
376409 defp from_stacktrace ( [ { module , function , args , _ } | _ ] ) when is_list ( args ) do
@@ -384,4 +417,49 @@ defmodule Exception do
384417 defp from_stacktrace ( _ ) do
385418 { nil , nil , nil }
386419 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+
387465end
0 commit comments