From 344f187ec3488cbc7b5a3d75bd8cc08f3b4fefab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Tue, 10 Jan 2012 14:17:43 +0100 Subject: bif_SUITE: Add test case for specs and BIF stubs Rename the existing types/1 test case to erl_bif_types/1 to make it clearer what it does. Also no longer test for missing type information for BIFs (since it has become optional), but only check that the information provided seems to be consistent. Introduce the specs/1 test case to ensure that all BIFs have specs. Also introduce improper_bif_stubs/1 to check for proper stubs. Since the BEAM loader will now silently allow stubs for BIFs, we want to be particular about exactly what a stub look like, so that an Erlang function is not unintentionally overridden by a BIF. --- erts/emulator/test/bif_SUITE.erl | 144 +++++++++++++++++++++++++++++++++------ 1 file changed, 125 insertions(+), 19 deletions(-) diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index c7617d3b90..eaececdafa 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -25,7 +25,7 @@ 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,specs/1,improper_bif_stubs/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 +33,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [types, t_list_to_existing_atom, os_env, otp_7526, + [erl_bif_types, specs, improper_bif_stubs, + t_list_to_existing_atom, os_env, otp_7526, display, atom_to_binary, binary_to_atom, binary_to_existing_atom, min_max]. @@ -86,33 +87,28 @@ deeep(N,Acc) -> deeep(N) -> deeep(N,[hello]). - -types(Config) when is_list(Config) -> +erl_bif_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() + erl_bif_types_1() end. -types_1() -> - ?line List0 = erlang:system_info(snifs), +erl_bif_types_1() -> + 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 +116,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 @@ -154,6 +150,89 @@ types_3(List) -> 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. + +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 +521,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}. -- cgit v1.2.3 From befc18e6aeb5313f5e3c98c9e3f3fb2a9214f4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Wed, 1 Feb 2012 17:04:44 +0100 Subject: Replace autoimport_SUITE with bif_SUITE:auto_imports/1 erts/test/autoimport_SUITE tested that auto-import information in erl_internal:bif/2 was consistent with the documentation. It did it by scanning erlang.xml. Since the documentation is now based on the specs in erlang.erl, we should now test consistency of the specs and erl_internal:bif/2. Since anyone that adds a new BIF runs the emulator test suite, it makes sense to do this test in bif_SUITE in the emulator test suite. --- erts/emulator/test/bif_SUITE.erl | 33 ++++- erts/test/Makefile | 10 +- erts/test/autoimport_SUITE.erl | 196 ------------------------------ erts/test/autoimport_SUITE_data/dummy.txt | 19 --- 4 files changed, 32 insertions(+), 226 deletions(-) delete mode 100644 erts/test/autoimport_SUITE.erl delete mode 100644 erts/test/autoimport_SUITE_data/dummy.txt diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index eaececdafa..3172d75e10 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -25,7 +25,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, display/1, display_huge/0, - erl_bif_types/1,specs/1,improper_bif_stubs/1, + erl_bif_types/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 +33,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [erl_bif_types, specs, improper_bif_stubs, + [erl_bif_types, 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, @@ -213,6 +213,35 @@ improper_bif_stubs(_) -> [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]. 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 -- cgit v1.2.3 From e657b0907f988812ecd90d79dd0acee1c7d3223a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Thu, 2 Feb 2012 14:17:19 +0100 Subject: bif_SUITE: Add test to ensure that all guard BIFs have type info --- erts/emulator/test/bif_SUITE.erl | 47 ++++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 11 deletions(-) diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 3172d75e10..1255ff3c38 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -25,7 +25,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2, display/1, display_huge/0, - erl_bif_types/1,specs/1,improper_bif_stubs/1,auto_imports/1, + erl_bif_types/1,guard_bifs_in_erl_bif_types/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 +34,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [erl_bif_types, specs, improper_bif_stubs, auto_imports, + [erl_bif_types, guard_bifs_in_erl_bif_types, + 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, @@ -88,16 +90,8 @@ deeep(N) -> deeep(N,[hello]). erl_bif_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 -> - erl_bif_types_1() - end. + ensure_erl_bif_types_compiled(), -erl_bif_types_1() -> List0 = erlang:system_info(snifs), %% Ignore missing type information for hipe BIFs. @@ -147,6 +141,37 @@ erl_bif_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. + +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). -- cgit v1.2.3 From 0309c473dc5a5a8ee36d9eac1568035e19413147 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Gustavsson?= <bjorn@erlang.org> Date: Thu, 2 Feb 2012 15:19:47 +0100 Subject: bif_SUITE: Test for suitable "shadowed by erl_bif_types" comments --- erts/emulator/test/bif_SUITE.erl | 71 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 70 insertions(+), 1 deletion(-) diff --git a/erts/emulator/test/bif_SUITE.erl b/erts/emulator/test/bif_SUITE.erl index 1255ff3c38..90c89cc12b 100644 --- a/erts/emulator/test/bif_SUITE.erl +++ b/erts/emulator/test/bif_SUITE.erl @@ -26,6 +26,7 @@ init_per_testcase/2,end_per_testcase/2, display/1, display_huge/0, 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, @@ -34,7 +35,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [erl_bif_types, guard_bifs_in_erl_bif_types, + [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, @@ -162,6 +163,74 @@ guard_bifs_in_erl_bif_types(_Config) -> ?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 -- cgit v1.2.3