aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBjörn Gustavsson <[email protected]>2012-02-08 11:29:40 +0100
committerBjörn Gustavsson <[email protected]>2012-02-08 11:29:40 +0100
commita00d45d0a952802523bc14ddbd27f2891efe5e23 (patch)
tree2a446a555aa11106a0f40e240bc14c7e9e5b7fcb
parentd0394af6541d99bd72192d35fc4986016c3a6589 (diff)
parent0309c473dc5a5a8ee36d9eac1568035e19413147 (diff)
downloadotp-a00d45d0a952802523bc14ddbd27f2891efe5e23.tar.gz
otp-a00d45d0a952802523bc14ddbd27f2891efe5e23.tar.bz2
otp-a00d45d0a952802523bc14ddbd27f2891efe5e23.zip
Merge branch 'bjorn/bif-test-cases'
* bjorn/bif-test-cases: bif_SUITE: Test for suitable "shadowed by erl_bif_types" comments bif_SUITE: Add test to ensure that all guard BIFs have type info Replace autoimport_SUITE with bif_SUITE:auto_imports/1 bif_SUITE: Add test case for specs and BIF stubs
-rw-r--r--erts/emulator/test/bif_SUITE.erl281
-rw-r--r--erts/test/Makefile10
-rw-r--r--erts/test/autoimport_SUITE.erl196
-rw-r--r--erts/test/autoimport_SUITE_data/dummy.txt19
4 files changed, 256 insertions, 250 deletions
diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl
index c7617d3b90..90c89cc12b 100644
--- a/erts/emulator/test/bif_SUITE.erl
+++ b/erts/emulator/test/bif_SUITE.erl
@@ -25,7 +25,9 @@
init_per_group/2,end_per_group/2,
init_per_testcase/2,end_per_testcase/2,
display/1, display_huge/0,
- types/1,
+ erl_bif_types/1,guard_bifs_in_erl_bif_types/1,
+ shadow_comments/1,
+ specs/1,improper_bif_stubs/1,auto_imports/1,
t_list_to_existing_atom/1,os_env/1,otp_7526/1,
binary_to_atom/1,binary_to_existing_atom/1,
atom_to_binary/1,min_max/1]).
@@ -33,7 +35,9 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
- [types, t_list_to_existing_atom, os_env, otp_7526,
+ [erl_bif_types, guard_bifs_in_erl_bif_types, shadow_comments,
+ specs, improper_bif_stubs, auto_imports,
+ t_list_to_existing_atom, os_env, otp_7526,
display,
atom_to_binary, binary_to_atom, binary_to_existing_atom,
min_max].
@@ -86,33 +90,20 @@ deeep(N,Acc) ->
deeep(N) ->
deeep(N,[hello]).
+erl_bif_types(Config) when is_list(Config) ->
+ ensure_erl_bif_types_compiled(),
-types(Config) when is_list(Config) ->
- c:l(erl_bif_types),
- case erlang:function_exported(erl_bif_types, module_info, 0) of
- false ->
- %% Fail cleanly.
- ?line ?t:fail("erl_bif_types not compiled");
- true ->
- types_1()
- end.
-
-types_1() ->
- ?line List0 = erlang:system_info(snifs),
+ List0 = erlang:system_info(snifs),
%% Ignore missing type information for hipe BIFs.
- ?line List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+ List = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
- case [MFA || MFA <- List, not known_types(MFA)] of
- [] ->
- types_2(List);
- BadTypes ->
- io:put_chars("No type information:\n"),
- io:format("~p\n", [lists:sort(BadTypes)]),
- ?line ?t:fail({length(BadTypes),bifs_without_types})
- end.
+ KnownTypes = [MFA || MFA <- List, known_types(MFA)],
+ io:format("There are ~p BIFs with type information in erl_bif_types.",
+ [length(KnownTypes)]),
+ erl_bif_types_2(KnownTypes).
-types_2(List) ->
+erl_bif_types_2(List) ->
BadArity = [MFA || {M,F,A}=MFA <- List,
begin
Types = erl_bif_types:arg_types(M, F, A),
@@ -120,14 +111,14 @@ types_2(List) ->
end],
case BadArity of
[] ->
- types_3(List);
+ erl_bif_types_3(List);
[_|_] ->
io:put_chars("Bifs with bad arity\n"),
io:format("~p\n", [BadArity]),
?line ?t:fail({length(BadArity),bad_arity})
end.
-types_3(List) ->
+erl_bif_types_3(List) ->
BadSmokeTest = [MFA || {M,F,A}=MFA <- List,
begin
try erl_bif_types:type(M, F, A) of
@@ -151,9 +142,220 @@ types_3(List) ->
?line ?t:fail({length(BadSmokeTest),bad_smoke_test})
end.
+guard_bifs_in_erl_bif_types(_Config) ->
+ ensure_erl_bif_types_compiled(),
+
+ List0 = erlang:system_info(snifs),
+ List = [{F,A} || {erlang,F,A} <- List0,
+ erl_internal:guard_bif(F, A)],
+ Not = [FA || {F,A}=FA <- List,
+ not erl_bif_types:is_known(erlang, F, A)],
+ case Not of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["Dialyzer requires that all guard BIFs "
+ "have type information in erl_bif_types.\n\n"
+ "The following guard BIFs have no type information "
+ "in erl_bif_types:\n\n",
+ [io_lib:format(" ~p/~p\n", [F,A]) || {F,A} <- Not]]),
+ ?t:fail()
+ end.
+
+shadow_comments(_Config) ->
+ ensure_erl_bif_types_compiled(),
+
+ List0 = erlang:system_info(snifs),
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+ List = [MFA || MFA <- List1, not is_operator(MFA)],
+ HasTypes = [MFA || {M,F,A}=MFA <- List,
+ erl_bif_types:is_known(M, F, A)],
+ Path = get_code_path(),
+ BifRel = sofs:relation(HasTypes, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Specs0 = [extract_specs(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Specs = lists:append(Specs0),
+ SpecFuns0 = [F || {F,_} <- Specs],
+ SpecFuns = sofs:relation(SpecFuns0, [{m,f,a}]),
+ HasTypesAndSpecs = sofs:intersection(BifRel, SpecFuns),
+ Commented0 = lists:append([extract_comments(Mod, Path) ||
+ Mod <- BifModules]),
+ Commented = sofs:relation(Commented0, [{m,f,a}]),
+ {NoComments0,_,NoBifSpecs0} =
+ sofs:symmetric_partition(HasTypesAndSpecs, Commented),
+ NoComments = sofs:to_external(NoComments0),
+ NoBifSpecs = sofs:to_external(NoBifSpecs0),
+
+ case NoComments of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["If a BIF stub has both a spec and has type information in "
+ "erl_bif_types, there *must*\n"
+ "be a comment in the source file to make that immediately "
+ "obvious.\n\nThe following comments are missing:\n\n",
+ [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n",
+ [M,F,A]) || {M,F,A} <- NoComments]]),
+ ?t:fail()
+ end,
+
+ case NoBifSpecs of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars(
+ ["The following functions have \"shadowed\" comments "
+ "claiming that there is type information in erl_bif_types,\n"
+ "but actually there is no such type information.\n\n"
+ "Therefore, the following comments should be removed:\n\n",
+ [io_lib:format("%% Shadowed by erl_bif_types: ~p:~p/~p\n",
+ [M,F,A]) || {M,F,A} <- NoBifSpecs]]),
+ ?t:fail()
+ end.
+
+extract_comments(Mod, Path) ->
+ Beam = which(Mod, Path),
+ SrcDir = filename:join(filename:dirname(filename:dirname(Beam)), "src"),
+ Src = filename:join(SrcDir, atom_to_list(Mod) ++ ".erl"),
+ {ok,Bin} = file:read_file(Src),
+ Lines0 = binary:split(Bin, <<"\n">>, [global]),
+ Lines1 = [T || <<"%% Shadowed by erl_bif_types: ",T/binary>> <- Lines0],
+ {ok,ReMFA} = re:compile("([^:]*):([^/]*)/(\\d*)"),
+ Lines = [L || L <- Lines1, re:run(L, ReMFA, [{capture,[]}]) =:= match],
+ [begin
+ {match,[M,F,A]} = re:run(L, ReMFA, [{capture,all_but_first,list}]),
+ {list_to_atom(M),list_to_atom(F),list_to_integer(A)}
+ end || L <- Lines].
+
+ensure_erl_bif_types_compiled() ->
+ c:l(erl_bif_types),
+ case erlang:function_exported(erl_bif_types, module_info, 0) of
+ false ->
+ %% Fail cleanly.
+ ?t:fail("erl_bif_types not compiled");
+ true ->
+ ok
+ end.
+
known_types({M,F,A}) ->
erl_bif_types:is_known(M, F, A).
+specs(_) ->
+ List0 = erlang:system_info(snifs),
+
+ %% Ignore missing type information for hipe BIFs.
+ List1 = [MFA || {M,_,_}=MFA <- List0, M =/= hipe_bifs],
+
+ %% Ignore all operators.
+ List = [MFA || MFA <- List1, not is_operator(MFA)],
+
+ %% Extract specs from the abstract code for all BIFs.
+ Path = get_code_path(),
+ BifRel = sofs:relation(List, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Specs0 = [extract_specs(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Specs = lists:append(Specs0),
+ BifSet = sofs:set(List, [function]),
+ SpecRel0 = sofs:relation(Specs, [{function,spec}]),
+ SpecRel = sofs:restriction(SpecRel0, BifSet),
+
+ %% Find BIFs without specs.
+ NoSpecs0 = sofs:difference(BifSet, sofs:domain(SpecRel)),
+ NoSpecs = sofs:to_external(NoSpecs0),
+ case NoSpecs of
+ [] ->
+ ok;
+ [_|_] ->
+ io:put_chars("The following BIFs don't have specs:\n"),
+ [print_mfa(MFA) || MFA <- NoSpecs],
+ ?t:fail()
+ end.
+
+is_operator({erlang,F,A}) ->
+ erl_internal:arith_op(F, A) orelse
+ erl_internal:bool_op(F, A) orelse
+ erl_internal:comp_op(F, A) orelse
+ erl_internal:list_op(F, A) orelse
+ erl_internal:send_op(F, A);
+is_operator(_) -> false.
+
+extract_specs(M, Abstr) ->
+ [{make_mfa(M, Name),Spec} || {attribute,_,spec,{Name,Spec}} <- Abstr].
+
+make_mfa(M, {F,A}) -> {M,F,A};
+make_mfa(M, {M,_,_}=MFA) -> MFA.
+
+improper_bif_stubs(_) ->
+ Bifs0 = erlang:system_info(snifs),
+ Bifs = [MFA || {M,_,_}=MFA <- Bifs0, M =/= hipe_bifs],
+ Path = get_code_path(),
+ BifRel = sofs:relation(Bifs, [{m,f,a}]),
+ BifModules = sofs:to_external(sofs:projection(1, BifRel)),
+ AbstrByModule = [extract_abstract(Mod, Path) || Mod <- BifModules],
+ Funcs0 = [extract_functions(Mod, Abstr) ||
+ {Mod,Abstr} <- AbstrByModule],
+ Funcs = lists:append(Funcs0),
+ BifSet = sofs:set(Bifs, [function]),
+ FuncRel0 = sofs:relation(Funcs, [{function,code}]),
+ FuncRel = sofs:restriction(FuncRel0, BifSet),
+ [check_stub(MFA, Body) || {MFA,Body} <- sofs:to_external(FuncRel)],
+ ok.
+
+auto_imports(_Config) ->
+ Path = get_code_path(),
+ {erlang,Abstr} = extract_abstract(erlang, Path),
+ SpecFuns = [Name || {attribute,_,spec,{Name,_}} <- Abstr],
+ auto_imports(SpecFuns, 0).
+
+auto_imports([{F,A}|T], Errors) ->
+ case erl_internal:bif(F, A) of
+ false ->
+ io:format("~p/~p: not auto-imported, but spec claims it "
+ "is auto-imported", [F,A]),
+ auto_imports(T, Errors+1);
+ true ->
+ auto_imports(T, Errors)
+ end;
+auto_imports([{erlang,F,A}|T], Errors) ->
+ case erl_internal:bif(F, A) of
+ false ->
+ auto_imports(T, Errors);
+ true ->
+ io:format("~p/~p: auto-imported, but "
+ "spec claims it is *not* auto-imported", [F,A]),
+ auto_imports(T, Errors+1)
+ end;
+auto_imports([], 0) ->
+ ok;
+auto_imports([], Errors) ->
+ ?t:fail({Errors,inconsistencies}).
+
+extract_functions(M, Abstr) ->
+ [{{M,F,A},Body} || {function,_,F,A,Body} <- Abstr].
+
+check_stub({erlang,apply,3}, _) ->
+ ok;
+check_stub({_,F,A}, B) ->
+ try
+ [{clause,_,Args,[],Body}] = B,
+ A = length(Args),
+ [{call,_,{remote,_,{atom,_,erlang},{atom,_,nif_error}},[_]}] = Body
+ catch
+ _:_ ->
+ io:put_chars("Invalid body for the following BIF stub:\n"),
+ Func = {function,0,F,A,B},
+ io:put_chars(erl_pp:function(Func)),
+ io:nl(),
+ io:put_chars("The body should be: erlang:nif_error(undef)"),
+ ?t:fail()
+ end.
+
t_list_to_existing_atom(Config) when is_list(Config) ->
?line all = list_to_existing_atom("all"),
?line ?MODULE = list_to_existing_atom(?MODULE_STRING),
@@ -442,3 +644,30 @@ min_max(Config) when is_list(Config) ->
id(I) -> I.
+%% Get code path, including the path for the erts application.
+get_code_path() ->
+ case code:lib_dir(erts) of
+ {error,bad_name} ->
+ Erts = filename:join([code:root_dir(),"erts","preloaded","ebin"]),
+ [Erts|code:get_path()];
+ _ ->
+ code:get_path()
+ end.
+
+which(Mod, Path) ->
+ which_1(atom_to_list(Mod) ++ ".beam", Path).
+
+which_1(Base, [D|Ds]) ->
+ Path = filename:join(D, Base),
+ case filelib:is_regular(Path) of
+ true -> Path;
+ false -> which_1(Base, Ds)
+ end.
+print_mfa({M,F,A}) ->
+ io:format("~p:~p/~p", [M,F,A]).
+
+extract_abstract(Mod, Path) ->
+ Beam = which(Mod, Path),
+ {ok,{Mod,[{abstract_code,{raw_abstract_v1,Abstr}}]}} =
+ beam_lib:chunks(Beam, [abstract_code]),
+ {Mod,Abstr}.
diff --git a/erts/test/Makefile b/erts/test/Makefile
index 68be3f2178..f79bb6e5f0 100644
--- a/erts/test/Makefile
+++ b/erts/test/Makefile
@@ -28,7 +28,6 @@ EBIN = .
# ----------------------------------------------------
MODULES= \
- autoimport_SUITE \
erlc_SUITE \
install_SUITE \
nt_SUITE \
@@ -39,14 +38,11 @@ MODULES= \
erlexec_SUITE \
z_SUITE
-ERL_XML = $(ERL_TOP)/erts/doc/src/erlang.xml
-ERL_XML_TARGET = autoimport_SUITE_data/erlang.xml
-
ERL_FILES= $(MODULES:%=%.erl)
TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR))
-EXTRA_FILES = install_SUITE_data/install_bin $(ERL_XML_TARGET)
+EXTRA_FILES = install_SUITE_data/install_bin
# ----------------------------------------------------
# Release directory specification
@@ -68,10 +64,6 @@ install_SUITE_data/install_bin: ../../make/install_bin
rm -f $@
cp -p $< $@
-$(ERL_XML_TARGET): $(ERL_XML)
- rm -f $@
- cp -p $< $@
-
clean:
rm -f $(TARGET_FILES) $(EXTRA_FILES)
rm -f core *~
diff --git a/erts/test/autoimport_SUITE.erl b/erts/test/autoimport_SUITE.erl
deleted file mode 100644
index 71ed5204b1..0000000000
--- a/erts/test/autoimport_SUITE.erl
+++ /dev/null
@@ -1,196 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2011. 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/.
-%%
-%% 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.
-%%
-%% %CopyrightEnd%
-%%
-%%% Purpose: Test erlang.xml re autoimports
--module(autoimport_SUITE).
-
--include_lib("test_server/include/test_server.hrl").
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
- init_per_group/2,end_per_group/2,
- init_per_testcase/2,end_per_testcase/2,
- autoimports/1]).
--define(TEST_TIMEOUT, ?t:seconds(180)).
-
-suite() -> [{ct_hooks,[ts_install_cth]}].
-
-all() ->
- [autoimports].
-
-groups() ->
- [].
-
-init_per_suite(Config) ->
- Config.
-
-end_per_suite(_Config) ->
- ok.
-
-init_per_group(_GroupName, Config) ->
- Config.
-
-end_per_group(_GroupName, Config) ->
- Config.
-
-
-init_per_testcase(_Func, Config) ->
- Dog = test_server:timetrap(?TEST_TIMEOUT),
- [{watchdog, Dog} | Config].
-
-end_per_testcase(_Func, Config) ->
- Dog = ?config(watchdog, Config),
- catch test_server:timetrap_cancel(Dog),
- ok.
-
-
-autoimports(suite) ->
- [];
-autoimports(doc) ->
- ["Check that erlang.xml documents autoimports correctly"];
-autoimports(Config) when is_list(Config) ->
- ?line XML = filename:join([?config(data_dir,Config),"erlang.xml"]),
- ?line case xml(XML) of
- [] ->
- ok;
- List ->
- lists:foreach(fun({[],F,A}) ->
- io:format("erlang:~s/~p is wrongly "
- "documented as ~s/~p~n",
- [F,A,F,A]);
- ({"erlang",F,A}) ->
- io:format("~s/~p is wrongly "
- "documented as "
- "erlang:~s/~p~n",
- [F,A,F,A])
- end,List),
- ?t:fail({wrong_autoimports,List})
- end.
-
-%%
-%% Ugly chunk of code to heuristically analyze the erlang.xml
-%% documentation file. Don't view this as an example...
-%%
-
-xml(XMLFile) ->
- {ok,File} = file:open(XMLFile,[read]),
- xskip_to_funcs(file:read_line(File),File),
- DocData = xloop(file:read_line(File),File),
- true = DocData =/= [],
- file:close(File),
- analyze(DocData).
-
-%% Skip lines up to and including the <funcs> tag.
-xskip_to_funcs({ok,Line},File) ->
- case re:run(Line,"\\<funcs\\>",[{capture,none}]) of
- nomatch ->
- xskip_to_funcs(file:read_line(File),File);
- match ->
- ok
- end.
-
-xloop({ok,Line},File) ->
- case re:run(Line,"\\<name\\>",[{capture,none}]) of
- nomatch ->
- xloop(file:read_line(File),File);
- match ->
- X = re:replace(Line,"\\).*",")",[{return,list}]),
- Y = re:replace(X,".*\\>","",[{return,list}]),
- Mod = get_module(Y),
- Rest1 = fstrip(Mod++":",Y),
- Func = get_function(Rest1),
- Rest2 = fstrip(Func++"(", Rest1),
- Argc = count_args(Rest2,1,0,false),
- [{Mod,Func,Argc} |
- xloop(file:read_line(File),File)]
- end;
-xloop(_,_) ->
- [].
-
-analyze([{[],Func,Arity}|T]) ->
- case erl_internal:bif(list_to_atom(Func),Arity) of
- true ->
- analyze(T);
- false ->
- [{[],Func,Arity} |
- analyze(T)]
- end;
-analyze([{"erlang",Func,Arity}|T]) ->
- case erl_internal:bif(list_to_atom(Func),Arity) of
- true ->
- [{"erlang",Func,Arity}|analyze(T)];
- false ->
- analyze(T)
- end;
-analyze([_|T]) ->
- analyze(T);
-analyze([]) ->
- [].
-
-
-count_args([],_,N,false) ->
- N;
-count_args([],_,N,true) ->
- N+1;
-count_args(_,0,N,true) ->
- N+1;
-count_args(_,0,N,false) ->
- N;
-count_args([Par|T],Level,N,Got) when (Par =:= 40) or
- (Par =:= 123) or (Par =:= 91) ->
- count_args(T,Level+1,N,(Level =:= 1) or Got);
-count_args([41|T],1,N,true) ->
- count_args(T,0,N+1,false);
-count_args([Par|T],Level,N, Got) when (Par =:= 41) or
- (Par =:= 125) or (Par =:= 93) ->
- count_args(T,Level-1,N,Got);
-count_args([$,|T],1,N,true) ->
- count_args(T,1,N+1,false);
-count_args([$ |T],A,B,C) ->
- count_args(T,A,B,C);
-count_args([_|T],1,N,_) ->
- count_args(T,1,N,true);
-count_args([_|T],A,B,C) ->
- count_args(T,A,B,C).
-
-fstrip([],X) ->
- X;
-fstrip(_,[]) ->
- [];
-fstrip([H|T1],[H|T2]) ->
- fstrip(T1,T2);
-fstrip(_,L) ->
- L.
-
-get_module(X) ->
- get_module(X,[]).
-get_module([],_) ->
- [];
-get_module([$:|_],Acc) ->
- lists:reverse(Acc);
-get_module([40|_],_) -> %(
- [];
-get_module([H|T],Acc) ->
- get_module(T,[H|Acc]).
-
-get_function(X) ->
- get_function(X,[]).
-get_function([],_) ->
- [];
-get_function([40|_],Acc) -> %(
- lists:reverse(Acc);
-get_function([H|T],Acc) ->
- get_function(T,[H|Acc]).
diff --git a/erts/test/autoimport_SUITE_data/dummy.txt b/erts/test/autoimport_SUITE_data/dummy.txt
deleted file mode 100644
index 972643e527..0000000000
--- a/erts/test/autoimport_SUITE_data/dummy.txt
+++ /dev/null
@@ -1,19 +0,0 @@
-%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 1998-2010. 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/.
-%%
-%% 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.
-%%
-%% %CopyrightEnd%
-%%
-%% Purpouse: Dummy