aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
authorHans Bolinder <[email protected]>2015-05-22 14:59:05 +0200
committerHans Bolinder <[email protected]>2015-06-12 11:44:26 +0200
commitb4374be62fcb48ae404b4c7eedacb69ecb5442ad (patch)
tree330979c18404e15cb5174f0ff7c7fec09c328876 /lib
parentca1c1f8d1a4fe4f7b19b9959c48bd64915215f24 (diff)
downloadotp-b4374be62fcb48ae404b4c7eedacb69ecb5442ad.tar.gz
otp-b4374be62fcb48ae404b4c7eedacb69ecb5442ad.tar.bz2
otp-b4374be62fcb48ae404b4c7eedacb69ecb5442ad.zip
edoc: Add parentheses to Erlang types and specs when needed
Before OTP 18, parentheses are kept by the Erlang Parser, and converted by EDoc ('paren_type' to #t_paren{}). As of OTP 18, the parser no longer keeps parentheses, why EDoc needs to insert them when converting Erlang types and specs to EDoc types and specs. As it seems, it is only annotations that sometimes require parentheses.
Diffstat (limited to 'lib')
-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};