From d66bc8561265e699df4706e88611b67d243ba933 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Fri, 21 Sep 2012 15:43:33 +0200 Subject: Warn for opaque types that are not exported --- lib/stdlib/src/erl_lint.erl | 35 ++++++++++++++++++++++++++++++----- lib/stdlib/test/erl_lint_SUITE.erl | 23 +++++++++++++++++++---- 2 files changed, 49 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index 97dacac0a4..a03abfaf80 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -365,6 +365,9 @@ format_error(callback_wrong_arity) -> format_error({imported_predefined_type, Name}) -> io_lib:format("referring to built-in type ~w as a remote type; " "please take out the module name", [Name]); +format_error({not_exported_opaque, {TypeName, Arity}}) -> + io_lib:format("opaque type ~w~s is not exported", + [TypeName, gen_type_paren(Arity)]); %% --- obsolete? unused? --- format_error({format_error, {Fmt, Args}}) -> io_lib:format(Fmt, Args); @@ -851,7 +854,8 @@ post_traversal_check(Forms, St0) -> StC = check_untyped_records(Forms, StB), StD = check_on_load(StC), StE = check_unused_records(Forms, StD), - check_callback_information(StE). + StF = check_local_opaque_types(StE), + check_callback_information(StF). %% check_behaviour(State0) -> State %% Check that the behaviour attribute is valid. @@ -2554,15 +2558,18 @@ find_field(_F, []) -> error. %% Attr :: 'type' | 'opaque' %% Checks that a type definition is valid. +-record(typeinfo, {attr, line}). + type_def(_Attr, _Line, {record, _RecName}, Fields, [], St0) -> %% The record field names and such are checked in the record format. %% We only need to check the types. Types = [T || {typed_record_field, _, T} <- Fields], check_type({type, -1, product, Types}, St0); -type_def(_Attr, Line, TypeName, ProtoType, Args, St0) -> +type_def(Attr, Line, TypeName, ProtoType, Args, St0) -> TypeDefs = St0#lint.types, Arity = length(Args), TypePair = {TypeName, Arity}, + Info = #typeinfo{attr = Attr, line = Line}, case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of true -> case dict:is_key(TypePair, default_types()) of @@ -2572,7 +2579,7 @@ type_def(_Attr, Line, TypeName, ProtoType, Args, St0) -> true -> Warn = {new_builtin_type, TypePair}, St1 = add_warning(Line, Warn, St0), - NewDefs = dict:store(TypePair, Line, TypeDefs), + NewDefs = dict:store(TypePair, Info, TypeDefs), CheckType = {type, -1, product, [ProtoType|Args]}, check_type(CheckType, St1#lint{types=NewDefs}); false -> @@ -2581,7 +2588,7 @@ type_def(_Attr, Line, TypeName, ProtoType, Args, St0) -> false -> add_error(Line, {redefine_type, TypePair}, St0) end; false -> - NewDefs = dict:store(TypePair, Line, TypeDefs), + NewDefs = dict:store(TypePair, Info, TypeDefs), CheckType = {type, -1, product, [ProtoType|Args]}, check_type(CheckType, St0#lint{types=NewDefs}) end. @@ -2895,7 +2902,7 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> fun(_Type, -1, AccSt) -> %% Default type AccSt; - (Type, FileLine, AccSt) -> + (Type, #typeinfo{line = FileLine}, AccSt) -> case loc(FileLine) of {FirstFile, _} -> case gb_sets:is_member(Type, UsedTypes) of @@ -2914,6 +2921,24 @@ check_unused_types(Forms, #lint{usage=Usage, types=Ts, exp_types=ExpTs}=St) -> St end. +check_local_opaque_types(St) -> + #lint{types=Ts, exp_types=ExpTs} = St, + FoldFun = + fun(_Type, -1, AccSt) -> + %% Default type + AccSt; + (_Type, #typeinfo{attr = type}, AccSt) -> + AccSt; + (Type, #typeinfo{attr = opaque, line = FileLine}, AccSt) -> + case gb_sets:is_element(Type, ExpTs) of + true -> AccSt; + false -> + Warn = {not_exported_opaque,Type}, + add_warning(FileLine, Warn, AccSt) + end + end, + dict:fold(FoldFun, St, Ts). + %% icrt_clauses(Clauses, In, ImportVarTable, State) -> %% {NewVts,State}. diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 944d8ebd6a..2a9163b6c4 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -50,7 +50,8 @@ unsafe_vars_try/1, guard/1, otp_4886/1, otp_4988/1, otp_5091/1, otp_5276/1, otp_5338/1, otp_5362/1, otp_5371/1, otp_7227/1, otp_5494/1, otp_5644/1, otp_5878/1, - otp_5917/1, otp_6585/1, otp_6885/1, export_all/1, + otp_5917/1, otp_6585/1, otp_6885/1, otp_10436/1, + export_all/1, bif_clash/1, behaviour_basic/1, behaviour_multiple/1, otp_7550/1, @@ -80,7 +81,7 @@ all() -> unsafe_vars, unsafe_vars2, unsafe_vars_try, guard, otp_4886, otp_4988, otp_5091, otp_5276, otp_5338, otp_5362, otp_5371, otp_7227, otp_5494, otp_5644, - otp_5878, otp_5917, otp_6585, otp_6885, export_all, + otp_5878, otp_5917, otp_6585, otp_6885, otp_10436, export_all, bif_clash, behaviour_basic, behaviour_multiple, otp_7550, otp_8051, format_warn, {group, on_load}, too_many_arguments]. @@ -2386,6 +2387,20 @@ otp_6885(Config) when is_list(Config) -> []} = run_test2(Config, Ts, []), ok. +otp_10436(doc) -> + "OTP-6885. Warnings for opaque types."; +otp_10436(suite) -> []; +otp_10436(Config) when is_list(Config) -> + Ts = <<"-module(otp_10436). + -export_type([t1/0]). + -opaque t1() :: {i, integer()}. + -opaque t2() :: {a, atom()}. + ">>, + {warnings,[{4,erl_lint,{not_exported_opaque,{t2,0}}}, + {4,erl_lint,{unused_type,{t2,0}}}]} = + run_test2(Config, Ts, []), + ok. + export_all(doc) -> "OTP-7392. Warning for export_all."; export_all(Config) when is_list(Config) -> @@ -2834,10 +2849,10 @@ otp_8051(doc) -> otp_8051(Config) when is_list(Config) -> Ts = [{otp_8051, <<"-opaque foo() :: bar(). + -export_type([foo/0]). ">>, [], - {error,[{1,erl_lint,{undefined_type,{bar,0}}}], - [{1,erl_lint,{unused_type,{foo,0}}}]}}], + {errors,[{1,erl_lint,{undefined_type,{bar,0}}}],[]}}], ?line [] = run(Config, Ts), ok. -- cgit v1.2.3 From 2660d9b52c668e85d987208f8118b415a19c4ea5 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Sat, 22 Sep 2012 19:29:03 +0200 Subject: Warn for underspecified opaque types --- lib/stdlib/src/erl_lint.erl | 30 ++++++++++++++++++++++++------ lib/stdlib/test/erl_lint_SUITE.erl | 8 ++++++++ 2 files changed, 32 insertions(+), 6 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index a03abfaf80..1e5f962375 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -368,6 +368,9 @@ format_error({imported_predefined_type, Name}) -> format_error({not_exported_opaque, {TypeName, Arity}}) -> io_lib:format("opaque type ~w~s is not exported", [TypeName, gen_type_paren(Arity)]); +format_error({underspecified_opaque, {TypeName, Arity}}) -> + io_lib:format("opaque type ~w~s is underspecified and therefore meaningless", + [TypeName, gen_type_paren(Arity)]); %% --- obsolete? unused? --- format_error({format_error, {Fmt, Args}}) -> io_lib:format(Fmt, Args); @@ -2570,6 +2573,12 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) -> Arity = length(Args), TypePair = {TypeName, Arity}, Info = #typeinfo{attr = Attr, line = Line}, + StoreType = + fun(St) -> + NewDefs = dict:store(TypePair, Info, TypeDefs), + CheckType = {type, -1, product, [ProtoType|Args]}, + check_type(CheckType, St#lint{types=NewDefs}) + end, case (dict:is_key(TypePair, TypeDefs) orelse is_var_arity_type(TypeName)) of true -> case dict:is_key(TypePair, default_types()) of @@ -2579,20 +2588,29 @@ type_def(Attr, Line, TypeName, ProtoType, Args, St0) -> true -> Warn = {new_builtin_type, TypePair}, St1 = add_warning(Line, Warn, St0), - NewDefs = dict:store(TypePair, Info, TypeDefs), - CheckType = {type, -1, product, [ProtoType|Args]}, - check_type(CheckType, St1#lint{types=NewDefs}); + StoreType(St1); false -> add_error(Line, {builtin_type, TypePair}, St0) end; false -> add_error(Line, {redefine_type, TypePair}, St0) end; false -> - NewDefs = dict:store(TypePair, Info, TypeDefs), - CheckType = {type, -1, product, [ProtoType|Args]}, - check_type(CheckType, St0#lint{types=NewDefs}) + St1 = case + Attr =:= opaque andalso + is_underspecified(ProtoType, Arity) + of + true -> + Warn = {underspecified_opaque, TypePair}, + add_warning(Line, Warn, St0); + false -> St0 + end, + StoreType(St1) end. +is_underspecified({type,_,term,[]}, 0) -> true; +is_underspecified({type,_,any,[]}, 0) -> true; +is_underspecified(_ProtType, _Arity) -> false. + check_type(Types, St) -> {SeenVars, St1} = check_type(Types, dict:new(), St), dict:fold(fun(Var, {seen_once, Line}, AccSt) -> diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 2a9163b6c4..90a37f6441 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -2399,6 +2399,14 @@ otp_10436(Config) when is_list(Config) -> {warnings,[{4,erl_lint,{not_exported_opaque,{t2,0}}}, {4,erl_lint,{unused_type,{t2,0}}}]} = run_test2(Config, Ts, []), + Ts2 = <<"-module(otp_10436_2). + -export_type([t1/0, t2/0]). + -opaque t1() :: term(). + -opaque t2() :: any(). + ">>, + {warnings,[{3,erl_lint,{underspecified_opaque,{t1,0}}}, + {4,erl_lint,{underspecified_opaque,{t2,0}}}]} = + run_test2(Config, Ts2, []), ok. export_all(doc) -> -- cgit v1.2.3 From 6674cff5db0c281d309723f25106dd84c33246b1 Mon Sep 17 00:00:00 2001 From: Hans Bolinder Date: Thu, 27 Sep 2012 09:31:06 +0200 Subject: Refine a few opaque types --- lib/stdlib/doc/src/ets.xml | 8 ++++---- lib/stdlib/src/binary.erl | 2 +- lib/stdlib/src/ets.erl | 4 ++-- lib/stdlib/src/io_lib.erl | 7 +++++-- 4 files changed, 12 insertions(+), 9 deletions(-) (limited to 'lib') diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 7880bf8fbb..abaf64fb91 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -145,6 +145,10 @@

A match specification, see above.

+ + +

A compiled match specification.

+
@@ -766,8 +770,6 @@ ets:is_compiled_ms(Broken). - - A compiled match specification. Compiles a match specification into its internal representation

This function transforms a @@ -791,8 +793,6 @@ ets:is_compiled_ms(Broken). - - A compiled match specification. Performs matching, using a compiled match_spec, on a list of tuples

This function executes the matching specified in a diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index 0e95372a76..1ed3422bc5 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -21,7 +21,7 @@ %% Implemented in this module: -export([split/2,split/3,replace/3,replace/4]). --opaque cp() :: tuple(). +-opaque cp() :: {'am' | 'bm', binary()}. -type part() :: {Start :: non_neg_integer(), Length :: integer()}. %%% BIFs. diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index 817b397cc4..ee6cff1b75 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -42,7 +42,7 @@ -export([i/0, i/1, i/2, i/3]). --export_type([tab/0, tid/0, match_spec/0]). +-export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0]). %%----------------------------------------------------------------------------- @@ -445,7 +445,7 @@ update_element(_, _, _) -> %%% End of BIFs --opaque comp_match_spec() :: any(). %% this one is REALLY opaque +-opaque comp_match_spec() :: binary(). %% this one is REALLY opaque -spec match_spec_run(List, CompiledMatchSpec) -> list() when List :: [tuple()], diff --git a/lib/stdlib/src/io_lib.erl b/lib/stdlib/src/io_lib.erl index 0252cdf742..ab62b72519 100644 --- a/lib/stdlib/src/io_lib.erl +++ b/lib/stdlib/src/io_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -82,7 +82,10 @@ -type chars() :: [char() | chars()]. -type depth() :: -1 | non_neg_integer(). --opaque continuation() :: {_, _, _, _}. % XXX: refine +-opaque continuation() :: {Format :: string(), + Stack :: chars(), + Nchars :: non_neg_integer(), + Results :: [term()]}. %%---------------------------------------------------------------------- -- cgit v1.2.3