@@ -1466,15 +1466,34 @@ defmodule Kernel do
14661466
14671467 """
14681468 @ spec raise ( binary | atom | tuple ) :: no_return
1469- defmacro raise ( msg ) when is_binary ( msg ) do
1470- quote do
1471- :erlang . error RuntimeError . exception ( message: unquote ( msg ) )
1469+ defmacro raise ( msg ) do
1470+ # Try to figure out the type at compilation time
1471+ # to avoid dead code and make dialyzer happy.
1472+ msg = case not is_binary ( msg ) and bootstraped? ( Macro.Env ) do
1473+ true -> Macro . expand ( msg , __CALLER__ )
1474+ false -> msg
14721475 end
1473- end
14741476
1475- defmacro raise ( { tag , _ , _ } = exception ) when tag == :<<>> or tag == :<> do
1476- quote do
1477- :erlang . error RuntimeError . exception ( message: unquote ( exception ) )
1477+ case msg do
1478+ msg when is_binary ( msg ) ->
1479+ quote do
1480+ :erlang . error RuntimeError . exception ( message: unquote ( msg ) )
1481+ end
1482+ { :<<>> , _ , _ } = msg ->
1483+ quote do
1484+ :erlang . error RuntimeError . exception ( message: unquote ( msg ) )
1485+ end
1486+ alias when is_atom ( alias ) ->
1487+ quote do
1488+ :erlang . error unquote ( alias ) . exception ( [ ] )
1489+ end
1490+ _ ->
1491+ quote do
1492+ case unquote ( msg ) do
1493+ msg when is_binary ( msg ) -> :erlang . error RuntimeError . exception ( message: msg )
1494+ msg -> :erlang . error msg . exception ( [ ] )
1495+ end
1496+ end
14781497 end
14791498 end
14801499
@@ -1486,8 +1505,7 @@ defmodule Kernel do
14861505 structure.
14871506
14881507 Any module defined via `defexception` automatically
1489- defines both `exception(args)` and `exception(args, current)`
1490- that creates a new and updates the given exception.
1508+ implements `exception(args)` callback expected by `raise/2`.
14911509
14921510 ## Examples
14931511
@@ -1496,35 +1514,9 @@ defmodule Kernel do
14961514
14971515 """
14981516 @ spec raise ( tuple | atom , list ) :: no_return
1499- defmacro raise ( exception , args // [ ] )
1500-
1501- defmacro raise ( { :{} , _ , _ } = exception , args ) do
1502- quote do
1503- :erlang . error unquote ( exception ) . exception ( unquote ( args ) )
1504- end
1505- end
1506-
1507- defmacro raise ( { :__aliases__ , _ , _ } = exception , args ) do
1508- quote do
1509- :erlang . error unquote ( exception ) . exception ( unquote ( args ) )
1510- end
1511- end
1512-
1513- defmacro raise ( exception , args ) when is_atom ( exception ) do
1514- quote do
1515- :erlang . error unquote ( exception ) . exception ( unquote ( args ) )
1516- end
1517- end
1518-
15191517 defmacro raise ( exception , args ) do
15201518 quote do
1521- exception = unquote ( exception )
1522- case exception do
1523- e when is_binary ( e ) ->
1524- :erlang . error RuntimeError . exception ( message: exception )
1525- _ ->
1526- :erlang . error exception . exception ( unquote ( args ) )
1527- end
1519+ :erlang . error unquote ( exception ) . exception ( unquote ( args ) )
15281520 end
15291521 end
15301522
@@ -1551,8 +1543,10 @@ defmodule Kernel do
15511543 may change the `System.stacktrace` value.
15521544 """
15531545 @ spec raise ( tuple | atom , list , list ) :: no_return
1554- def raise ( exception , args , stacktrace ) do
1555- :erlang . raise :error , exception . exception ( args ) , stacktrace
1546+ defmacro raise ( exception , args , stacktrace ) do
1547+ quote do
1548+ :erlang . raise :error , unquote ( exception ) . exception ( unquote ( args ) ) , unquote ( stacktrace )
1549+ end
15561550 end
15571551
15581552 @ doc """
@@ -3182,18 +3176,47 @@ defmodule Kernel do
31823176 Record. defrecordp ( name , Macro . expand ( tag , __CALLER__ ) , fields )
31833177 end
31843178
3185- @ doc """
3179+ @doc % S """
31863180 Defines an exception.
31873181
3188- Exceptions are simply records and therefore `defexception/3` has
3189- the same API and similar behavior to `defrecord/3` with two notable
3190- differences:
3182+ Exceptions are simply records with three differences:
3183+
3184+ 1. Exceptions are required to defined a function `exception/1`
3185+ that receives keyword arguments and returns the exception.
3186+ This function is a callback usually invoked by `raise/2`;
3187+
3188+ 2. Exceptions are required to provide a `message` field.
3189+ This field must return a String with a formatted error message;
3190+
3191+ 3. Unlike records, exceptions are documented by default.
3192+
3193+ Since exceptions are records, `defexception/3` has exactly
3194+ the same API as `defrecord/3`.
3195+
3196+ ## Raising exceptions
31913197
3192- 1) Unlike records, exceptions are documented by default;
3198+ The most common way to raise an exception is via the raise
3199+ function:
3200+
3201+ defexception MyException, [:message]
3202+ raise MyException,
3203+ message: "did not get what expected, got: #{ inspect value } "
3204+
3205+ In many cases though, it is more convenient to just pass the
3206+ expected value to raise and generate the message in the `exception/1`
3207+ callback:
3208+
3209+ defexception MyException, [:message] do
3210+ def exception(opts) do
3211+ msg = "did not get what expected, got: #{ inspect opts [ :actual ] } "
3212+ MyException[message: msg]
3213+ end
3214+ end
31933215
3194- 2) Exceptions **must** implement `message/1` -- a function that returns a
3195- string;
3216+ raise MyException, actual: value
31963217
3218+ The example above is the preferred mechanism for customizing
3219+ exception messages.
31973220 """
31983221 defmacro defexception( name, fields, do_block // [ ] ) do
31993222 { fields, do_block } =
@@ -3204,14 +3227,15 @@ defmodule Kernel do
32043227
32053228 do_block = Keyword . put ( do_block , :do , quote do
32063229 @ moduledoc nil
3207- record_type message: binary
3230+ record_type message: String . t
32083231
32093232 @ doc false
32103233 def exception ( args ) , do: new ( args )
32113234
32123235 @ doc false
32133236 def exception ( args , self ) , do: update ( args , self )
32143237
3238+ defoverridable exception: 1 , exception: 2
32153239 unquote ( Keyword . get do_block , :do )
32163240 end )
32173241
0 commit comments