diff options
Diffstat (limited to 'lib/edoc/src/edoc_specs.erl')
| -rw-r--r-- | lib/edoc/src/edoc_specs.erl | 207 | 
1 files changed, 115 insertions, 92 deletions
| diff --git a/lib/edoc/src/edoc_specs.erl b/lib/edoc/src/edoc_specs.erl index 211a354c74..bb98e8b04f 100644 --- a/lib/edoc/src/edoc_specs.erl +++ b/lib/edoc/src/edoc_specs.erl @@ -1,18 +1,19 @@  %%  %% %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 -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at  %% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. +%%     http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License.  %%  %% %CopyrightEnd% @@ -58,7 +59,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 +72,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 +84,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 +141,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 +158,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 +191,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 +296,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 +353,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 +416,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 +440,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 +507,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}; | 
