aboutsummaryrefslogtreecommitdiffstats
path: root/lib/dialyzer/test
diff options
context:
space:
mode:
Diffstat (limited to 'lib/dialyzer/test')
-rw-r--r--lib/dialyzer/test/Makefile3
-rw-r--r--lib/dialyzer/test/abstract_SUITE.erl3
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/results/weird6
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl18
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl14
-rw-r--r--lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl19
-rw-r--r--lib/dialyzer/test/plt_SUITE.erl37
-rw-r--r--lib/dialyzer/test/typer_SUITE.erl158
8 files changed, 246 insertions, 12 deletions
diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile
index 0d8fba438c..43c8a61ce1 100644
--- a/lib/dialyzer/test/Makefile
+++ b/lib/dialyzer/test/Makefile
@@ -13,7 +13,8 @@ AUXILIARY_FILES=\
file_utils.erl\
dialyzer_SUITE.erl\
abstract_SUITE.erl\
- plt_SUITE.erl
+ plt_SUITE.erl\
+ typer_SUITE.erl
# ----------------------------------------------------
# Release directory specification
diff --git a/lib/dialyzer/test/abstract_SUITE.erl b/lib/dialyzer/test/abstract_SUITE.erl
index 0e84dfab24..37fb39cf27 100644
--- a/lib/dialyzer/test/abstract_SUITE.erl
+++ b/lib/dialyzer/test/abstract_SUITE.erl
@@ -83,7 +83,8 @@ generated_case(Config) when is_list(Config) ->
Config, [], []),
ok.
-test(Prog, Config, COpts, DOpts) ->
+test(Prog0, Config, COpts, DOpts) ->
+ Prog = erl_parse:anno_from_term(Prog0),
{ok, BeamFile} = compile(Config, Prog, COpts),
run_dialyzer(Config, succ_typings, [BeamFile], DOpts).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/weird b/lib/dialyzer/test/opaque_SUITE_data/results/weird
new file mode 100644
index 0000000000..d7f57cd152
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/results/weird
@@ -0,0 +1,6 @@
+
+weird_warning1.erl:15: Matching of pattern {'a', Dict} tagged with a record name violates the declared type of #b{q::queue:queue(_)}
+weird_warning2.erl:13: Matching of pattern <{'b', Queue}, Key, Value> tagged with a record name violates the declared type of <#a{d::dict:dict(_,_)},'my_key','my_value'>
+weird_warning3.erl:14: The call weird_warning3:add_element(#a{d::queue:queue(_)},'my_key','my_value') does not have a term of type #a{d::dict:dict(_,_)} | #b{q::queue:queue(_)} (with opaque subterms) as 1st argument
+weird_warning3.erl:16: The attempt to match a term of type #a{d::queue:queue(_)} against the pattern {'a', Dict} breaks the opacity of queue:queue(_)
+weird_warning3.erl:18: Matching of pattern {'b', Queue} tagged with a record name violates the declared type of #a{d::queue:queue(_)}
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl
new file mode 100644
index 0000000000..094138e72b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning1.erl
@@ -0,0 +1,18 @@
+-module(weird_warning1).
+-export([public_func/0]).
+
+-record(a, {
+ d = dict:new() :: dict:dict()
+ }).
+
+-record(b, {
+ q = queue:new() :: queue:queue()
+ }).
+
+public_func() ->
+ add_element(#b{}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl
new file mode 100644
index 0000000000..4e4512157b
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning2.erl
@@ -0,0 +1,14 @@
+-module(weird_warning2).
+-export([public_func/0]).
+
+-record(a, {d = dict:new() :: dict:dict()}).
+
+-record(b, {q = queue:new() :: queue:queue()}).
+
+public_func() ->
+ add_element(#a{}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl
new file mode 100644
index 0000000000..b70ca645cb
--- /dev/null
+++ b/lib/dialyzer/test/opaque_SUITE_data/src/weird/weird_warning3.erl
@@ -0,0 +1,19 @@
+-module(weird_warning3).
+-export([public_func/0]).
+
+-record(a, {
+ d = dict:new() :: dict:dict()
+ }).
+
+-record(b, {
+ q = queue:new() :: queue:queue()
+ }).
+
+public_func() ->
+ %% Notice that t_to_string() will create "#a{d::queue:queue(_)}".
+ add_element({a, queue:new()}, my_key, my_value).
+
+add_element(#a{d = Dict}, Key, Value) ->
+ dict:store(Key, Value, Dict);
+add_element(#b{q = Queue}, Key, Value) ->
+ queue:in({Key, Value}, Queue).
diff --git a/lib/dialyzer/test/plt_SUITE.erl b/lib/dialyzer/test/plt_SUITE.erl
index fbfa979e1b..92c63bdb0c 100644
--- a/lib/dialyzer/test/plt_SUITE.erl
+++ b/lib/dialyzer/test/plt_SUITE.erl
@@ -259,24 +259,41 @@ remove_plt(Config) ->
{init_plt, Plt}] ++ Opts),
ok.
+%% ERL-283, OTP-13979. As of OTP-14323 this test no longer does what
+%% it is designed to do--the linter stops every attempt to run the
+%% checks of Dialyzer's on bad dialyzer attributes. For the time
+%% being, the linter's error message are checked instead. The test
+%% needs to be updated when/if the Dialyzer can analyze Core Erlang
+%% without compiling abstract code.
bad_dialyzer_attr(Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ Source = lists:concat([dial, ".erl"]),
+ Filename = filename:join(PrivDir, Source),
+ ok = dialyzer_common:check_plt(PrivDir),
+ PltFilename = dialyzer_common:plt_file(PrivDir),
+ Opts = [{files, [Filename]},
+ {check_plt, false},
+ {from, src_code},
+ {init_plt, PltFilename}],
+
Prog1 = <<"-module(dial).
-dialyzer({no_return, [undef/0]}).">>,
- {ok, Beam1} = compile(Config, Prog1, dial, []),
+ ok = file:write_file(Filename, Prog1),
{dialyzer_error,
- "Analysis failed with error:\n"
- "Could not scan the following file(s):\n"
- " Unknown function undef/0 in line " ++ _} =
- (catch run_dialyzer(plt_build, [Beam1], [])),
+ "Analysis failed with error:\n" ++ Str1} =
+ (catch dialyzer:run(Opts)),
+ P1 = string:str(Str1, "dial.erl:2: function undef/0 undefined"),
+ true = P1 > 0,
Prog2 = <<"-module(dial).
-dialyzer({no_return, [{undef,1,2}]}).">>,
- {ok, Beam2} = compile(Config, Prog2, dial, []),
+ ok = file:write_file(Filename, Prog2),
{dialyzer_error,
- "Analysis failed with error:\n"
- "Could not scan the following file(s):\n"
- " Bad function {undef,1,2} in line " ++ _} =
- (catch run_dialyzer(plt_build, [Beam2], [])),
+ "Analysis failed with error:\n" ++ Str2} =
+ (catch dialyzer:run(Opts)),
+ P2 = string:str(Str2, "dial.erl:2: badly formed dialyzer "
+ "attribute: {no_return,{undef,1,2}}"),
+ true = P2 > 0,
ok.
diff --git a/lib/dialyzer/test/typer_SUITE.erl b/lib/dialyzer/test/typer_SUITE.erl
new file mode 100644
index 0000000000..da5b961643
--- /dev/null
+++ b/lib/dialyzer/test/typer_SUITE.erl
@@ -0,0 +1,158 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2017. All Rights Reserved.
+%%
+%% 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
+%%
+%% 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%
+%%
+-module(typer_SUITE).
+
+-export([all/0,suite/0,groups/0,init_per_suite/1,end_per_suite/1,
+ init_per_group/2,end_per_group/2,
+ smoke/1]).
+
+-include_lib("common_test/include/ct.hrl").
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ [smoke].
+
+groups() ->
+ [].
+
+init_per_suite(Config) ->
+ OutDir = proplists:get_value(priv_dir, Config),
+ case dialyzer_common:check_plt(OutDir) of
+ fail -> {skip, "Plt creation/check failed."};
+ ok -> [{dialyzer_options, []}|Config]
+ end.
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+smoke(Config) ->
+ Code = <<"-module(typer_test_module).
+ -compile([export_all,nowarn_export_all]).
+ a(L) ->
+ L ++ [1,2,3].">>,
+ PrivDir = proplists:get_value(priv_dir, Config),
+ Src = filename:join(PrivDir, "typer_test_module.erl"),
+ ok = file:write_file(Src, Code),
+ Args = "--plt " ++ PrivDir ++ "dialyzer_plt",
+ Res = ["^$",
+ "^%% File:",
+ "^%% ----",
+ "^-spec a",
+ "^_OK_"],
+ run(Config, Args, Src, Res),
+ ok.
+
+typer() ->
+ case os:find_executable("typer") of
+ false ->
+ ct:fail("Can't find typer");
+ Typer ->
+ Typer
+ end.
+
+%% Runs a command.
+
+run(Config, Args0, Name, Expect) ->
+ Args = Args0 ++ " " ++ Name,
+ Result = run_command(Config, Args),
+ verify_result(Result, Expect).
+
+verify_result(Result, Expect) ->
+ Messages = split(Result, [], []),
+ io:format("Result: ~p", [Messages]),
+ io:format("Expected: ~p", [Expect]),
+ match_messages(Messages, Expect).
+
+split([$\n|Rest], Current, Lines) ->
+ split(Rest, [], [lists:reverse(Current)|Lines]);
+split([$\r|Rest], Current, Lines) ->
+ split(Rest, Current, Lines);
+split([Char|Rest], Current, Lines) ->
+ split(Rest, [Char|Current], Lines);
+split([], [], Lines) ->
+ lists:reverse(Lines);
+split([], Current, Lines) ->
+ split([], [], [lists:reverse(Current)|Lines]).
+
+match_messages([Msg|Rest1], [Regexp|Rest2]) ->
+ case re:run(Msg, Regexp, [{capture,none}, unicode]) of
+ match ->
+ ok;
+ nomatch ->
+ io:format("Not matching: ~s\n", [Msg]),
+ io:format("Regexp : ~s\n", [Regexp]),
+ ct:fail(message_mismatch)
+ end,
+ match_messages(Rest1, Rest2);
+match_messages([], [Expect|Rest]) ->
+ ct:fail({too_few_messages, [Expect|Rest]});
+match_messages([Msg|Rest], []) ->
+ ct:fail({too_many_messages, [Msg|Rest]});
+match_messages([], []) ->
+ ok.
+
+%% Runs the command using os:cmd/1.
+%%
+%% Returns the output from the command (as a list of characters with
+%% embedded newlines). The very last line will indicate the
+%% exit status of the command, where _OK_ means zero, and _ERROR_
+%% a non-zero exit status.
+
+run_command(Config, Args) ->
+ TmpDir = filename:join(proplists:get_value(priv_dir, Config), "tmp"),
+ file:make_dir(TmpDir),
+ {RunFile, Run, Script} = run_command(TmpDir, os:type(), Args),
+ ok = file:write_file(filename:join(TmpDir, RunFile),
+ unicode:characters_to_binary(Script)),
+ io:format("~ts\n", [Script]),
+ os:cmd(Run).
+
+run_command(Dir, {win32, _}, Args) ->
+ BatchFile = filename:join(Dir, "run.bat"),
+ Run = re:replace(filename:rootname(BatchFile), "/", "\\",
+ [global,{return,list}]),
+ Typer = typer(),
+ {BatchFile,
+ Run,
+ ["@echo off\r\n",
+ "\"",Typer,"\" ",Args, "\r\n",
+ "if errorlevel 1 echo _ERROR_\r\n",
+ "if not errorlevel 1 echo _OK_\r\n"]};
+run_command(Dir, {unix, _}, Args) ->
+ TyperDir = filename:dirname(typer()),
+ Name = filename:join(Dir, "run"),
+ {Name,
+ "/bin/sh " ++ Name,
+ ["#!/bin/sh\n",
+ "PATH=\"",TyperDir,":$PATH\"\n",
+ "typer ",Args,"\n",
+ "case $? in\n",
+ " 0) echo '_OK_';;\n",
+ " *) echo '_ERROR_';;\n",
+ "esac\n"]};
+run_command(_Dir, Other, _Args) ->
+ ct:fail("Don't know how to test exit code for ~p", [Other]).