aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/src/dialyzer_races.erl
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/src/dialyzer_races.erl')
-rw-r--r--lib/dialyzer/src/dialyzer_races.erl175
1 files changed, 123 insertions, 52 deletions
diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl
index 5857f7a03d..4972967960 100644
--- a/lib/dialyzer/src/dialyzer_races.erl
+++ b/lib/dialyzer/src/dialyzer_races.erl
@@ -1,20 +1,20 @@
%% -*- erlang-indent-level: 2 -*-
%%-----------------------------------------------------------------------
%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2008-2009. All Rights Reserved.
-%%
+%%
+%% Copyright Ericsson AB 2008-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%
%%
@@ -50,8 +50,10 @@
-define(local, 5).
-define(no_arg, no_arg).
-define(no_label, no_label).
+-define(bypassed, bypassed).
-define(WARN_WHEREIS_REGISTER, warn_whereis_register).
+-define(WARN_WHEREIS_UNREGISTER, warn_whereis_unregister).
-define(WARN_ETS_LOOKUP_INSERT, warn_ets_lookup_insert).
-define(WARN_MNESIA_DIRTY_READ_WRITE, warn_mnesia_dirty_read_write).
-define(WARN_NO_WARN, warn_no_warn).
@@ -64,27 +66,29 @@
-type mfa_or_funlbl() :: label() | mfa().
--type label_type() :: label() | [label()] | {label()} | ?no_label.
--type args() :: [label_type() | [string()]].
--type core_vars() :: cerl:cerl() | ?no_arg.
--type var_to_map() :: core_vars() | [cerl:cerl()].
--type core_args() :: [core_vars()] | 'empty'.
--type op() :: 'bind' | 'unbind'.
+-type label_type() :: label() | [label()] | {label()} | ?no_label.
+-type args() :: [label_type() | [string()]].
+-type core_vars() :: cerl:cerl() | ?no_arg | ?bypassed.
+-type var_to_map1() :: core_vars() | [cerl:cerl()].
+-type var_to_map2() :: cerl:cerl() | [cerl:cerl()] | ?bypassed.
+-type core_args() :: [core_vars()] | 'empty'.
+-type op() :: 'bind' | 'unbind'.
-type dep_calls() :: 'whereis' | 'ets_lookup' | 'mnesia_dirty_read'.
--type warn_calls() :: 'register' | 'ets_insert' | 'mnesia_dirty_write'.
--type call() :: 'whereis' | 'register' | 'ets_new' | 'ets_lookup'
- | 'ets_insert' | 'mnesia_dirty_read1'
+-type warn_calls() :: 'register' | 'unregister' | 'ets_insert'
+ | 'mnesia_dirty_write'.
+-type call() :: 'whereis' | 'register' | 'unregister' | 'ets_new'
+ | 'ets_lookup' | 'ets_insert' | 'mnesia_dirty_read1'
| 'mnesia_dirty_read2' | 'mnesia_dirty_write1'
| 'mnesia_dirty_write2' | 'function_call'.
--type race_tag() :: 'whereis_register' | 'ets_lookup_insert'
- | 'mnesia_dirty_read_write'.
+-type race_tag() :: 'whereis_register' | 'whereis_unregister'
+ | 'ets_lookup_insert' | 'mnesia_dirty_read_write'.
--record(beg_clause, {arg :: var_to_map(),
- pats :: var_to_map(),
+-record(beg_clause, {arg :: var_to_map1(),
+ pats :: var_to_map1(),
guard :: cerl:cerl()}).
--record(end_clause, {arg :: var_to_map(),
- pats :: var_to_map(),
+-record(end_clause, {arg :: var_to_map1(),
+ pats :: var_to_map1(),
guard :: cerl:cerl()}).
-record(end_case, {clauses :: [#end_clause{}]}).
-record(curr_fun, {status :: 'in' | 'out',
@@ -98,15 +102,15 @@
args :: args(),
arg_types :: [erl_types:erl_type()],
vars :: [core_vars()],
- state :: _,
+ state :: _, %% XXX: recursive
file_line :: file_line(),
var_map :: dict()}).
-record(fun_call, {caller :: mfa_or_funlbl(),
callee :: mfa_or_funlbl(),
arg_types :: [erl_types:erl_type()],
vars :: [core_vars()]}).
--record(let_tag, {var :: var_to_map(),
- arg :: var_to_map()}).
+-record(let_tag, {var :: var_to_map1(),
+ arg :: var_to_map1()}).
-record(warn_call, {call_name :: warn_calls(),
args :: args(),
var_map :: dict()}).
@@ -180,6 +184,14 @@ store_race_call(Fun, ArgTypes, Args, FileLine, State) ->
fun_mfa = CurrFun, fun_label = CurrFunLabel},
{[#warn_call{call_name = register, args = VarArgs}|
RaceList], RaceListSize + 1, [RaceFun|RaceTags], no_t};
+ {erlang, unregister, 1} ->
+ VarArgs = format_args(Args, ArgTypes, CleanState, unregister),
+ RaceFun = #race_fun{mfa = Fun, args = VarArgs,
+ arg_types = ArgTypes, vars = Args,
+ file_line = FileLine, index = RaceListSize,
+ fun_mfa = CurrFun, fun_label = CurrFunLabel},
+ {[#warn_call{call_name = unregister, args = VarArgs}|
+ RaceList], RaceListSize + 1, [RaceFun|RaceTags], no_t};
{erlang, whereis, 1} ->
VarArgs = format_args(Args, ArgTypes, CleanState, whereis),
{[#dep_call{call_name = whereis, args = VarArgs,
@@ -280,6 +292,7 @@ race(State) ->
RaceWarnTag =
case Fun of
{erlang, register, 2} -> ?WARN_WHEREIS_REGISTER;
+ {erlang, unregister, 1} -> ?WARN_WHEREIS_UNREGISTER;
{ets, insert, 2} -> ?WARN_ETS_LOOKUP_INSERT;
{mnesia, dirty_write, _A} -> ?WARN_MNESIA_DIRTY_READ_WRITE
end,
@@ -287,7 +300,7 @@ race(State) ->
state__renew_curr_fun(CurrFun,
state__renew_curr_fun_label(CurrFunLabel,
state__renew_race_list(lists:nthtail(length(RaceList) - Index,
- RaceList), State))),
+ RaceList), State))),
DepList = fixup_race_list(RaceWarnTag, VarArgs, State1),
{State2, RaceWarn} =
get_race_warn(Fun, Args, ArgTypes, DepList, State),
@@ -309,6 +322,7 @@ fixup_race_list(RaceWarnTag, WarnVarArgs, State) ->
RaceTag =
case RaceWarnTag of
?WARN_WHEREIS_REGISTER -> whereis_register;
+ ?WARN_WHEREIS_UNREGISTER -> whereis_unregister;
?WARN_ETS_LOOKUP_INSERT -> ets_lookup_insert;
?WARN_MNESIA_DIRTY_READ_WRITE -> mnesia_dirty_read_write
end,
@@ -320,11 +334,9 @@ fixup_race_list(RaceWarnTag, WarnVarArgs, State) ->
lists:reverse(NewRaceList), [], CurrFun,
WarnVarArgs, RaceWarnTag, dict:new(),
[], [], [], 2 * ?local, NewState),
- Parents =
- fixup_race_backward(CurrFun, Calls, Calls, [], ?local),
+ Parents = fixup_race_backward(CurrFun, Calls, Calls, [], ?local),
UParents = lists:usort(Parents),
- Filtered =
- filter_parents(UParents, UParents, Digraph),
+ Filtered = filter_parents(UParents, UParents, Digraph),
NewParents =
case lists:member(CurrFun, Filtered) of
true -> Filtered;
@@ -401,8 +413,7 @@ fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls, Code, RaceList,
false ->
{ok, Fun} = Name,
{ok, Int} = Label,
- case dict:find(Fun,
- dialyzer_callgraph:get_race_code(Callgraph)) of
+ case dict:find(Fun, dialyzer_callgraph:get_race_code(Callgraph)) of
error ->
{NewCurrFun, NewCurrFunLabel, NewCalls, Tail, NewRaceList,
NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes,
@@ -459,7 +470,8 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
case Head of
#dep_call{call_name = whereis} ->
case RaceWarnTag of
- ?WARN_WHEREIS_REGISTER ->
+ WarnWhereis when WarnWhereis =:= ?WARN_WHEREIS_REGISTER orelse
+ WarnWhereis =:= ?WARN_WHEREIS_UNREGISTER ->
{[Head#dep_call{var_map = RaceVarMap}|RaceList],
[], NestingLevel, false};
_Other ->
@@ -493,9 +505,11 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
_Other ->
{RaceList, [], NestingLevel, false}
end;
- #warn_call{call_name = register} ->
+ #warn_call{call_name = RegCall} when RegCall =:= register orelse
+ RegCall =:= unregister ->
case RaceWarnTag of
- ?WARN_WHEREIS_REGISTER ->
+ WarnWhereis when WarnWhereis =:= ?WARN_WHEREIS_REGISTER orelse
+ WarnWhereis =:= ?WARN_WHEREIS_UNREGISTER ->
{[Head#warn_call{var_map = RaceVarMap}|RaceList],
[], NestingLevel, false};
_Other ->
@@ -575,6 +589,10 @@ fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList,
{[#warn_call{call_name = register, args = WarnVarArgs,
var_map = RaceVarMap}],
NewDepList};
+ whereis_unregister ->
+ {[#warn_call{call_name = unregister, args = WarnVarArgs,
+ var_map = RaceVarMap}],
+ NewDepList};
ets_lookup_insert ->
NewWarnCall =
[#warn_call{call_name = ets_insert, args = WarnVarArgs,
@@ -760,6 +778,19 @@ get_deplist_paths(RaceList, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
_ ->
{[Vars, WVA2, WVA3, WVA4], false}
end;
+ ?WARN_WHEREIS_UNREGISTER ->
+ [WVA1, WVA2] = WarnVarArgs1,
+ Vars =
+ lists:flatten(
+ [find_all_bound_vars(V, RaceVarMap1) || V <- WVA1]),
+ case {Vars, CurrLevel} of
+ {[], 0} ->
+ {WarnVarArgs, true};
+ {[], _} ->
+ {WarnVarArgs, false};
+ _ ->
+ {[Vars, WVA2], false}
+ end;
?WARN_ETS_LOOKUP_INSERT ->
[WVA1, WVA2, WVA3, WVA4] = WarnVarArgs1,
Vars1 =
@@ -805,8 +836,9 @@ get_deplist_paths(RaceList, WarnVarArgs, RaceWarnTag, RaceVarMap, CurrLevel,
get_deplist_paths(Tail, WarnVarArgs2, RaceWarnTag, RaceVarMap1,
CurrLevel1, PublicTables, NamedTables)
end;
- #warn_call{call_name = register, args = WarnVarArgs1,
- var_map = RaceVarMap1} ->
+ #warn_call{call_name = RegCall, args = WarnVarArgs1,
+ var_map = RaceVarMap1} when RegCall =:= register orelse
+ RegCall =:= unregister ->
case compare_first_arg(WarnVarArgs, WarnVarArgs1, RaceVarMap1) of
true -> {[], false, false};
NewWarnVarArgs ->
@@ -1416,7 +1448,8 @@ lists_get(N, List) -> lists:nth(N, List).
refine_race(RaceCall, WarnVarArgs, RaceWarnTag, DependencyList, RaceVarMap) ->
case RaceWarnTag of
- ?WARN_WHEREIS_REGISTER ->
+ WarnWhereis when WarnWhereis =:= ?WARN_WHEREIS_REGISTER orelse
+ WarnWhereis =:= ?WARN_WHEREIS_UNREGISTER ->
case RaceCall of
#dep_call{call_name = ets_lookup} ->
DependencyList;
@@ -1660,6 +1693,20 @@ compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) ->
end
end;
+ ?WARN_WHEREIS_UNREGISTER ->
+ [VA1, VA2] = VarArgs,
+ [WVA1, WVA2] = WarnVarArgs,
+ case any_args(VA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ case any_args(WVA2) of
+ true -> compare_var_list(VA1, WVA1, RaceVarMap);
+ false ->
+ compare_var_list(VA1, WVA1, RaceVarMap) orelse
+ compare_argtypes(VA2, WVA2)
+
+ end
+ end;
?WARN_ETS_LOOKUP_INSERT ->
[VA1, VA2, VA3, VA4] = VarArgs,
[WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
@@ -1683,8 +1730,8 @@ compare_types(VarArgs, WarnVarArgs, RaceWarnTag, RaceVarMap) ->
true ->
compare_var_list(VA3, WVA3, RaceVarMap);
false ->
- compare_var_list(VA3, WVA3, RaceVarMap)
- orelse compare_argtypes(VA4, WVA4)
+ compare_var_list(VA3, WVA3, RaceVarMap) orelse
+ compare_argtypes(VA4, WVA4)
end
end);
?WARN_MNESIA_DIRTY_READ_WRITE ->
@@ -1818,6 +1865,7 @@ ets_tuple_argtypes1(Str, Tuple, TupleList, NestingLevel) ->
end
end.
+format_arg(?bypassed) -> ?no_label;
format_arg(Arg) ->
case cerl:type(Arg) of
var -> cerl_trees:get_label(Arg);
@@ -1845,9 +1893,13 @@ format_args_1([Arg], [Type], CleanState) ->
[format_arg(Arg), format_type(Type, CleanState)];
format_args_1([Arg|Args], [Type|Types], CleanState) ->
List =
- case cerl:is_literal(Arg) of
- true -> [?no_label, format_cerl(Arg)];
- false -> [format_arg(Arg), format_type(Type, CleanState)]
+ case Arg =:= ?bypassed of
+ true -> [?no_label, format_type(Type, CleanState)];
+ false ->
+ case cerl:is_literal(Arg) of
+ true -> [?no_label, format_cerl(Arg)];
+ false -> [format_arg(Arg), format_type(Type, CleanState)]
+ end
end,
List ++ format_args_1(Args, Types, CleanState).
@@ -1859,6 +1911,9 @@ format_args_2(StrArgList, Call) ->
register ->
lists_key_replace(2, StrArgList,
string:tokens(lists:nth(2, StrArgList), " |"));
+ unregister ->
+ lists_key_replace(2, StrArgList,
+ string:tokens(lists:nth(2, StrArgList), " |"));
ets_new ->
StrArgList1 = lists_key_replace(2, StrArgList,
string:tokens(lists:nth(2, StrArgList), " |")),
@@ -1919,10 +1974,11 @@ mnesia_tuple_argtypes(TupleStr) ->
[TupleStr2|_T] = string:tokens(TupleStr1, " ,"),
lists:flatten(string:tokens(TupleStr2, " |")).
--spec race_var_map(var_to_map(), cerl:cerl() | [cerl:cerl()], dict(), op()) -> dict().
+-spec race_var_map(var_to_map1(), var_to_map2(), dict(), op()) -> dict().
race_var_map(Vars1, Vars2, RaceVarMap, Op) ->
- case Vars1 =:= ?no_arg of
+ case Vars1 =:= ?no_arg orelse Vars1 =:= ?bypassed
+ orelse Vars2 =:= ?bypassed of
true -> RaceVarMap;
false ->
case is_list(Vars1) andalso is_list(Vars2) of
@@ -2076,7 +2132,7 @@ race_var_map_guard(Arg, Pats, Guard, RaceVarMap, Op) ->
{RaceVarMap1, RemoveClause orelse RemoveClause1}.
race_var_map_guard_helper1(Arg, Pats, RaceVarMap, Op) ->
- case Arg =:= ?no_arg of
+ case Arg =:= ?no_arg orelse Arg =:= ?bypassed of
true -> {RaceVarMap, false};
false ->
case cerl:type(Arg) of
@@ -2139,7 +2195,7 @@ unbind_dict_vars(Var1, Var2, RaceVarMap) ->
true ->
unbind_dict_vars(Var1, Var2,
bind_dict_vars_list(Var1, Labels -- [Var2],
- dict:erase(Var1, RaceVarMap)));
+ dict:erase(Var1, RaceVarMap)));
false ->
unbind_dict_vars_helper(Labels, Var1, Var2, RaceVarMap)
end
@@ -2171,6 +2227,10 @@ var_analysis(FunDefArgs, FunCallArgs, WarnVarArgs, RaceWarnTag) ->
[WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
ArgNos = lists_key_members_lists(WVA1, FunDefArgs),
[[lists_get(N, FunCallArgs) || N <- ArgNos], WVA2, WVA3, WVA4];
+ ?WARN_WHEREIS_UNREGISTER ->
+ [WVA1, WVA2] = WarnVarArgs,
+ ArgNos = lists_key_members_lists(WVA1, FunDefArgs),
+ [[lists_get(N, FunCallArgs) || N <- ArgNos], WVA2];
?WARN_ETS_LOOKUP_INSERT ->
[WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
ArgNos1 = lists_key_members_lists(WVA1, FunDefArgs),
@@ -2185,8 +2245,7 @@ var_analysis(FunDefArgs, FunCallArgs, WarnVarArgs, RaceWarnTag) ->
var_type_analysis(FunDefArgs, FunCallTypes, WarnVarArgs, RaceWarnTag,
RaceVarMap, CleanState) ->
- FunVarArgs = format_args(FunDefArgs, FunCallTypes, CleanState,
- function_call),
+ FunVarArgs = format_args(FunDefArgs, FunCallTypes, CleanState, function_call),
case RaceWarnTag of
?WARN_WHEREIS_REGISTER ->
[WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
@@ -2197,6 +2256,15 @@ var_type_analysis(FunDefArgs, FunCallTypes, WarnVarArgs, RaceWarnTag,
NewWVA2 = string:tokens(lists:nth(N + 1, FunVarArgs), " |"),
[Vars, NewWVA2, WVA3, WVA4]
end;
+ ?WARN_WHEREIS_UNREGISTER ->
+ [WVA1, WVA2] = WarnVarArgs,
+ Vars = find_all_bound_vars(WVA1, RaceVarMap),
+ case lists_key_member_lists(Vars, FunVarArgs) of
+ 0 -> [Vars, WVA2];
+ N when is_integer(N) ->
+ NewWVA2 = string:tokens(lists:nth(N + 1, FunVarArgs), " |"),
+ [Vars, NewWVA2]
+ end;
?WARN_ETS_LOOKUP_INSERT ->
[WVA1, WVA2, WVA3, WVA4] = WarnVarArgs,
Vars1 = find_all_bound_vars(WVA1, RaceVarMap),
@@ -2278,6 +2346,10 @@ get_race_warnings_helper(Warnings, State) ->
get_reason(lists:keysort(7, DepList),
"might fail due to a possible race condition "
"caused by its combination with ");
+ ?WARN_WHEREIS_UNREGISTER ->
+ get_reason(lists:keysort(7, DepList),
+ "might fail due to a possible race condition "
+ "caused by its combination with ");
?WARN_ETS_LOOKUP_INSERT ->
get_reason(lists:keysort(7, DepList),
"might have an unintended effect due to " ++
@@ -2335,7 +2407,7 @@ state__add_race_warning(State, RaceWarn, RaceWarnTag, FileLine) ->
%%%
%%% ===========================================================================
--spec beg_clause_new(var_to_map(), var_to_map(), cerl:cerl()) ->
+-spec beg_clause_new(var_to_map1(), var_to_map1(), cerl:cerl()) ->
#beg_clause{}.
beg_clause_new(Arg, Pats, Guard) ->
@@ -2351,7 +2423,7 @@ cleanup(#races{race_list = RaceList}) ->
end_case_new(Clauses) ->
#end_case{clauses = Clauses}.
--spec end_clause_new(var_to_map(), var_to_map(), cerl:cerl()) ->
+-spec end_clause_new(var_to_map1(), var_to_map1(), cerl:cerl()) ->
#end_clause{}.
end_clause_new(Arg, Pats, Guard) ->
@@ -2387,7 +2459,7 @@ get_race_list(#races{race_list = RaceList}) ->
get_race_list_size(#races{race_list_size = RaceListSize}) ->
RaceListSize.
--spec let_tag_new(var_to_map(), var_to_map()) -> #let_tag{}.
+-spec let_tag_new(var_to_map1(), var_to_map1()) -> #let_tag{}.
let_tag_new(Var, Arg) ->
#let_tag{var = Var, arg = Arg}.
@@ -2422,5 +2494,4 @@ put_race_analysis(Analysis, Races) ->
races().
put_race_list(RaceList, RaceListSize, Races) ->
- Races#races{race_list = RaceList,
- race_list_size = RaceListSize}.
+ Races#races{race_list = RaceList, race_list_size = RaceListSize}.