diff options
Diffstat (limited to 'lib/edoc/src/edoc_specs.erl')
-rw-r--r-- | lib/edoc/src/edoc_specs.erl | 188 |
1 files changed, 105 insertions, 83 deletions
diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 211a354c74..eb69058148 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2014. All Rights Reserved. +%% Copyright Ericsson AB 1996-2015. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -58,7 +58,7 @@ type(Form, TypeDocs) -> end, {#t_name{name = N}, T, As, Doc0} end, - #tag{name = type, line = element(2, Type), + #tag{name = type, line = get_line(element(2, Type)), origin = code, data = {#t_typedef{name = TypeName, args = d2e(Args), @@ -71,7 +71,7 @@ type(Form, TypeDocs) -> spec(Form, Clause) -> {Name, _Arity, TypeSpecs} = get_spec(Form), TypeSpec = lists:nth(Clause, TypeSpecs), - #tag{name = spec, line = element(2, TypeSpec), + #tag{name = spec, line = get_line(element(2, TypeSpec)), origin = code, data = aspec(d2e(TypeSpec), Name)}. @@ -83,7 +83,7 @@ dummy_spec(Form) -> {#t_name{name = Name}, Arity, TypeSpecs} = get_spec(Form), As = string:join(lists:duplicate(Arity, "_X"), ","), S = lists:flatten(io_lib:format("~p(~s) -> true\n", [Name, As])), - #tag{name = spec, line = element(2, hd(TypeSpecs)), + #tag{name = spec, line = get_line(element(2, hd(TypeSpecs))), origin = code, data = S}. -spec docs(Forms::[syntaxTree()], @@ -140,7 +140,7 @@ find_type_docs([F | Fs], Cs, Fun) -> %% Postcomments before the dot after the typespec are ignored. C2 = [C1 | [C || C <- erl_syntax:get_postcomments(F), - get_line(erl_syntax:get_pos(C)) >= LastTypeLine]], + erl_syntax:get_pos(C) >= LastTypeLine]], C3 = collect_comments(Fs, LastTypeLine), #tag{data = Doc0} = Fun(lists:reverse(C2 ++ C3), LastTypeLine), case strip(Doc0) of % Strip away "f(). \n" @@ -157,7 +157,7 @@ find_type_docs([F | Fs], Cs, Fun) -> collect_comments([], _Line) -> []; collect_comments([F | Fs], Line) -> - L1 = get_line(erl_syntax:get_pos(F)), + L1 = erl_syntax:get_pos(F), if L1 =:= Line + 1; L1 =:= Line -> % a separate postcomment @@ -190,29 +190,26 @@ get_name_and_last_line(F) -> {Name, Data} = erl_syntax_lib:analyze_wild_attribute(F), type = edoc_specs:tag(Name), Attr = {attribute, erl_syntax:get_pos(F), Name, Data}, - Ref = make_ref(), - Fun = fun(L) -> {Ref, get_line(L)} end, + Fun = fun(A) -> + Line = get_line(A), + case get('$max_line') of + Max when Max < Line -> + _ = put('$max_line', Line); + _ -> + ok + end + end, + undefined = put('$max_line', 0), + _ = erl_parse:map_anno(Fun, Attr), + Line = erase('$max_line'), TypeName = case Data of {N, _T, As} when is_atom(N) -> % skip records {N, length(As)} end, - Line = gll(erl_lint:modify_line(Attr, Fun), Ref), {TypeName, Line}. -gll({Ref, Line}, Ref) -> - Line; -gll([], _Ref) -> - 0; -gll(List, Ref) when is_list(List) -> - lists:max([gll(E, Ref) || E <- List]); -gll(Tuple, Ref) when is_tuple(Tuple) -> - gll(tuple_to_list(Tuple), Ref); -gll(_, _) -> - 0. - -get_line(Pos) -> - {line, Line} = erl_scan:attributes_info(Pos, line), - Line. +get_line(Anno) -> + erl_anno:line(Anno). %% Collect all Erlang types. Types in comments (@type) shadow Erlang %% types (-spec/-opaque). @@ -298,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]), @@ -348,49 +352,62 @@ d2e({type,_,constraint,[Sub,Ts0]}) -> Ts = [ST,T] = d2e([ST0,T0]), #t_def{name = ST, type = typevar_anno(T, Ts)}; _ -> - throw_error(element(2, Sub), "cannot handle guard", []) + 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({var,_,'_'}) -> +d2e({user_type,_,Name,Types0}, _Prec) -> + Types = d2e(Types0), + typevar_anno(#t_type{name = #t_name{name = Name}, args = Types}, Types); +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}; |