aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/edoc/src/edoc_layout.erl2
-rw-r--r--lib/edoc/src/edoc_specs.erl142
2 files changed, 83 insertions, 61 deletions
diff --git a/lib/edoc/src/edoc_layout.erl b/lib/edoc/src/edoc_layout.erl
index 62d5eb9a18..b67ec31ae3 100644
--- a/lib/edoc/src/edoc_layout.erl
+++ b/lib/edoc/src/edoc_layout.erl
@@ -520,7 +520,7 @@ format_spec(Name, Type, Defs, #opts{pretty_printer = erl_pp}=Opts) ->
{R, ".\n"} = etypef(L, O),
[{pre, R}]
catch _:_ ->
- %% Example: "@spec ... -> record(a)"
+ %% Should not happen.
format_spec(Name, Type, Defs, Opts#opts{pretty_printer=''})
end;
format_spec(Sep, Type, Defs, _Opts) ->
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl
index 59f6cb8ddf..eb69058148 100644
--- a/lib/edoc/src/edoc_specs.erl
+++ b/lib/edoc/src/edoc_specs.erl
@@ -295,47 +295,54 @@ arg_name([A | As], Default) ->
is_name(A) ->
is_atom(A).
-d2e({ann_type,_,[V, T0]}) ->
+d2e(T) ->
+ d2e(T, 0).
+
+d2e({ann_type,_,[V, T0]}, Prec) ->
%% Note: the -spec/-type syntax allows annotations everywhere, but
%% EDoc does not. The fact that the annotation is added to the
%% type here does not necessarily mean that it will be used by the
%% layout module.
- T = d2e(T0),
- ?add_t_ann(T, element(3, V));
-d2e({remote_type,_,[{atom,_,M},{atom,_,F},Ts0]}) ->
+ {_L,P,R} = erl_parse:type_inop_prec('::'),
+ T1 = d2e(T0, R),
+ T = ?add_t_ann(T1, element(3, V)),
+ maybe_paren(P, Prec, T); % the only necessary call to maybe_paren()
+d2e({remote_type,_,[{atom,_,M},{atom,_,F},Ts0]}, _Prec) ->
Ts = d2e(Ts0),
typevar_anno(#t_type{name = #t_name{module = M, name = F}, args = Ts}, Ts);
-d2e({type,_,'fun',[{type,_,product,As0},Ran0]}) ->
+d2e({type,_,'fun',[{type,_,product,As0},Ran0]}, _Prec) ->
Ts = [Ran|As] = d2e([Ran0|As0]),
%% Assume that the linter has checked type variables.
typevar_anno(#t_fun{args = As, range = Ran}, Ts);
-d2e({type,_,'fun',[A0={type,_,any},Ran0]}) ->
+d2e({type,_,'fun',[A0={type,_,any},Ran0]}, _Prec) ->
Ts = [A, Ran] = d2e([A0, Ran0]),
typevar_anno(#t_fun{args = [A], range = Ran}, Ts);
-d2e({type,_,'fun',[]}) ->
+d2e({type,_,'fun',[]}, _Prec) ->
#t_type{name = #t_name{name = function}, args = []};
-d2e({type,_,any}) ->
+d2e({type,_,any}, _Prec) ->
#t_var{name = '...'}; % Kludge... not a type variable!
-d2e({type,_,nil,[]}) ->
+d2e({type,_,nil,[]}, _Prec) ->
#t_nil{};
-d2e({paren_type,_,[T]}) ->
- #t_paren{type = d2e(T)};
-d2e({type,_,list,[T0]}) ->
+d2e({paren_type,_,[T]}, Prec) ->
+ d2e(T, Prec);
+d2e({type,_,list,[T0]}, _Prec) ->
T = d2e(T0),
typevar_anno(#t_list{type = T}, [T]);
-d2e({type,_,nonempty_list,[T0]}) ->
+d2e({type,_,nonempty_list,[T0]}, _Prec) ->
T = d2e(T0),
typevar_anno(#t_nonempty_list{type = T}, [T]);
-d2e({type,_,bounded_fun,[T,Gs]}) ->
+d2e({type,_,bounded_fun,[T,Gs]}, _Prec) ->
[F0|Defs] = d2e([T|Gs]),
F = ?set_t_ann(F0, lists:keydelete(type_variables, 1, ?t_ann(F0))),
%% Assume that the linter has checked type variables.
#t_spec{type = typevar_anno(F, [F0]), defs = Defs};
-d2e({type,_,range,[V1,V2]}) ->
+d2e({type,_,range,[V1,V2]}, Prec) ->
+ {_L,P,_R} = erl_parse:type_inop_prec('..'),
{integer,_,I1} = erl_eval:partial_eval(V1),
{integer,_,I2} = erl_eval:partial_eval(V2),
- #t_integer_range{from = I1, to = I2};
-d2e({type,_,constraint,[Sub,Ts0]}) ->
+ T0 = #t_integer_range{from = I1, to = I2},
+ maybe_paren(P, Prec, T0);
+d2e({type,_,constraint,[Sub,Ts0]}, _Prec) ->
case {Sub,Ts0} of
{{atom,_,is_subtype},[{var,_,N},T0]} ->
Ts = [T] = d2e([T0]),
@@ -347,50 +354,60 @@ d2e({type,_,constraint,[Sub,Ts0]}) ->
_ ->
throw_error(get_line(element(2, Sub)), "cannot handle guard", [])
end;
-d2e({type,_,union,Ts0}) ->
- Ts = d2e(Ts0),
- typevar_anno(#t_union{types = Ts}, Ts);
-d2e({type,_,tuple,any}) ->
+d2e({type,_,union,Ts0}, Prec) ->
+ {_L,P,R} = erl_parse:type_inop_prec('|'),
+ Ts = d2e(Ts0, R),
+ T = maybe_paren(P, Prec, #t_union{types = Ts}),
+ typevar_anno(T, Ts);
+d2e({type,_,tuple,any}, _Prec) ->
#t_type{name = #t_name{name = tuple}, args = []};
-d2e({type,_,binary,[Base,Unit]}) ->
- #t_binary{base_size = element(3, Base),
- unit_size = element(3, Unit)};
-d2e({type,_,map,any}) ->
- #t_map{ types = []};
-d2e({type,_,map,Es}) ->
- #t_map{ types = d2e(Es) };
-d2e({type,_,map_field_assoc,[K,V]}) ->
- #t_map_field{ k_type = d2e(K), v_type=d2e(V) };
-d2e({type,_,map_field_exact,K,V}) ->
- #t_map_field{ k_type = d2e(K), v_type=d2e(V) };
-d2e({type,_,tuple,Ts0}) ->
+d2e({type,_,binary,[Base,Unit]}, _Prec) ->
+ {integer,_,B} = erl_eval:partial_eval(Base),
+ {integer,_,U} = erl_eval:partial_eval(Unit),
+ #t_binary{base_size = B, unit_size = U};
+d2e({type,_,map,any}, _Prec) ->
+ #t_map{types = []};
+d2e({type,_,map,Es}, _Prec) ->
+ #t_map{types = d2e(Es) };
+d2e({type,_,map_field_assoc,[K,V]}, Prec) ->
+ T = #t_map_field{k_type = d2e(K), v_type=d2e(V) },
+ {P,_R} = erl_parse:type_preop_prec('#'),
+ maybe_paren(P, Prec, T);
+d2e({type,_,map_field_exact,K,V}, Prec) ->
+ T = #t_map_field{k_type = d2e(K), v_type=d2e(V) },
+ {P,_R} = erl_parse:type_preop_prec('#'),
+ maybe_paren(P, Prec, T);
+d2e({type,_,tuple,Ts0}, _Prec) ->
Ts = d2e(Ts0),
typevar_anno(#t_tuple{types = Ts}, Ts);
-d2e({type,_,record,[Name|Fs0]}) ->
+d2e({type,_,record,[Name|Fs0]}, Prec) ->
Atom = #t_atom{val = element(3, Name)},
Fs = d2e(Fs0),
- typevar_anno(#t_record{name = Atom, fields = Fs}, Fs);
-d2e({type,_,field_type,[Name,Type0]}) ->
- Type = d2e(Type0),
- typevar_anno(#t_field{name = #t_atom{val = element(3, Name)}, type = Type},
- [Type]);
-d2e({typed_record_field,{record_field,L,Name},Type}) ->
- d2e({type,L,field_type,[Name,Type]});
-d2e({typed_record_field,{record_field,L,Name,_E},Type}) ->
- d2e({type,L,field_type,[Name,Type]});
-d2e({record_field,L,_Name,_E}=F) ->
- d2e({typed_record_field,F,{type,L,any,[]}}); % Maybe skip...
-d2e({record_field,L,_Name}=F) ->
- d2e({typed_record_field,F,{type,L,any,[]}}); % Maybe skip...
-d2e({type,_,Name,Types0}) ->
+ {P,_R} = erl_parse:type_preop_prec('#'),
+ T = maybe_paren(P, Prec, #t_record{name = Atom, fields = Fs}),
+ typevar_anno(T, Fs);
+d2e({type,_,field_type,[Name,Type0]}, Prec) ->
+ {_L,P,R} = erl_parse:type_inop_prec('::'),
+ Type = maybe_paren(P, Prec, d2e(Type0, R)),
+ T = #t_field{name = #t_atom{val = element(3, Name)}, type = Type},
+ typevar_anno(T, [Type]);
+d2e({typed_record_field,{record_field,L,Name},Type}, Prec) ->
+ d2e({type,L,field_type,[Name,Type]}, Prec);
+d2e({typed_record_field,{record_field,L,Name,_E},Type}, Prec) ->
+ d2e({type,L,field_type,[Name,Type]}, Prec);
+d2e({record_field,L,_Name,_E}=F, Prec) ->
+ d2e({typed_record_field,F,{type,L,any,[]}}, Prec); % Maybe skip...
+d2e({record_field,L,_Name}=F, Prec) ->
+ d2e({typed_record_field,F,{type,L,any,[]}}, Prec); % Maybe skip...
+d2e({type,_,Name,Types0}, _Prec) ->
Types = d2e(Types0),
typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types);
-d2e({user_type,_,Name,Types0}) ->
+d2e({user_type,_,Name,Types0}, _Prec) ->
Types = d2e(Types0),
typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types);
-d2e({var,_,'_'}) ->
+d2e({var,_,'_'}, _Prec) ->
#t_type{name = #t_name{name = ?TOP_TYPE}};
-d2e({var,_,TypeName}) ->
+d2e({var,_,TypeName}, _Prec) ->
TypeVar = ordsets:from_list([TypeName]),
T = #t_var{name = TypeName},
%% Annotate type variables with the name of the variable.
@@ -398,13 +415,13 @@ d2e({var,_,TypeName}) ->
%% from using the argument name from the source or to invent a new name.
T1 = ?add_t_ann(T, {type_variables, TypeVar}),
?add_t_ann(T1, TypeName);
-d2e(L) when is_list(L) ->
- [d2e(T) || T <- L];
-d2e({atom,_,A}) ->
+d2e(L, Prec) when is_list(L) ->
+ [d2e(T, Prec) || T <- L];
+d2e({atom,_,A}, _Prec) ->
#t_atom{val = A};
-d2e(undefined = U) -> % opaque
+d2e(undefined = U, _Prec) -> % opaque
U;
-d2e(Expr) ->
+d2e(Expr, _Prec) ->
{integer,_,I} = erl_eval:partial_eval(Expr),
#t_integer{val = I}.
@@ -422,6 +439,11 @@ typevars(Ts) ->
get_typevars(Ts) ->
[Vs || T <- Ts, T =/= undefined, {type_variables, Vs} <- ?t_ann(T)].
+maybe_paren(P, Prec, T) when P < Prec ->
+ #t_paren{type = T};
+maybe_paren(_P, _Prec, T) ->
+ T.
+
-record(parms, {tab, warn, file, line}).
%% Expands record references. Explicitly given record fields are kept,
@@ -484,11 +506,11 @@ xrecs(#t_fun{args = Args0, range = Range0}=T, P) ->
Args = xrecs(Args0, P),
Range = xrecs(Range0, P),
T#t_fun{args = Args, range = Range};
-xrecs(#t_map{ types = Ts0 }=T,P) ->
+xrecs(#t_map{types = Ts0 }=T,P) ->
Ts = xrecs(Ts0, P),
- T#t_map{ types = Ts };
-xrecs(#t_map_field{ k_type=Kt, v_type=Vt}=T, P) ->
- T#t_map_field{ k_type=xrecs(Kt,P), v_type=xrecs(Vt,P)};
+ T#t_map{types = Ts };
+xrecs(#t_map_field{k_type=Kt, v_type=Vt}=T, P) ->
+ T#t_map_field{k_type=xrecs(Kt,P), v_type=xrecs(Vt,P)};
xrecs(#t_tuple{types = Types0}=T, P) ->
Types = xrecs(Types0, P),
T#t_tuple{types = Types};