Add compiler warnings (#346)

* Add compiler warnings

Add include_type annotation to position

Add warning for unused includes

Add warning for unused stateful annotation

Add warning for unused functions

Add warning for shadowed variables

Add division by zero warning

Add warning for negative spends

Add warning for unused variables

Add warning for unused parameters

Change the ets table type to set for unused vars

Add warning for unused type defs

Move unused variables warning to the top level

Temporarily disable unused functions warnings

Add all kinds of warnings to a single ets table

Enable warnings separately through options

Use when_option instead of enabled_warnings

Turn warnings into type errors with warn_error option

Enable warning package warn_all

Re-enable unused functions warnings

Report warnings as type errors in a separate function

Make unused_function a recognized warning

Report warnings as a result of compilation

Fix tests and error for unknown warnings options

Fix dialyzer warnings

Do not show warning for variables called "_"

Move warnings handling into a separate module

Do not show warning for unused public functions in namespaces

Add src file name to unused include warning

Mark public functions in namespaces as used

Add tests for added warnings

Add warning for unused return value

Add test for turning warnings into type errors

* Update CHANGELOG
This commit is contained in:
Gaith Hallak
2021-11-24 11:46:21 +02:00
committed by GitHub
parent 98a4049f03
commit fe5f5545d3
10 changed files with 451 additions and 28 deletions
+88 -1
View File
@@ -46,7 +46,7 @@ simple_compile_test_() ->
end} ] ++
[ {"Testing error messages of " ++ ContractName,
fun() ->
Errors = compile(aevm, ContractName),
Errors = compile(aevm, ContractName, [warn_all, warn_error]),
check_errors(ExpectedErrors, Errors)
end} ||
{ContractName, ExpectedErrors} <- failing_contracts() ] ++
@@ -88,6 +88,11 @@ simple_compile_test_() ->
?assertMatch({_, _, true}, {SizeDeadCode, SizeNoDeadCode, SizeDeadCode + Delta < SizeNoDeadCode}),
ok
end} || Backend <- [aevm, fate] ] ++
[ {"Testing warning messages",
fun() ->
#{ warnings := Warnings } = compile(Backend, "warnings", [warn_all]),
check_warnings(warnings(), Warnings)
end} || Backend <- [aevm, fate] ] ++
[].
%% Check if all modules in the standard library compile
@@ -119,6 +124,15 @@ check_errors(Expect0, Actual0) ->
{Missing, Extra} -> ?assertEqual(Missing, Extra)
end.
check_warnings(Expect0, Actual0) ->
Expect = lists:sort(Expect0),
Actual = [ list_to_binary(string:trim(aeso_warnings:pp(Warn))) || Warn <- Actual0 ],
case {Expect -- Actual, Actual -- Expect} of
{[], Extra} -> ?assertMatch({unexpected, []}, {unexpected, Extra});
{Missing, []} -> ?assertMatch({missing, []}, {missing, Missing});
{Missing, Extra} -> ?assertEqual(Missing, Extra)
end.
compile(Backend, Name) ->
compile(Backend, Name,
[{include, {file_system, [aeso_test_utils:contract_path()]}}]).
@@ -228,6 +242,49 @@ debug_mode_contracts() ->
-define(TYPE_ERROR(Name, Errs), ?ERROR("Type", Name, Errs)).
-define(PARSE_ERROR(Name, Errs), ?ERROR("Parse", Name, Errs)).
-define(PosW(Kind, File, Line, Col), (list_to_binary(Kind))/binary, " in '",
(list_to_binary(File))/binary, ".aes' at line " ??Line ", col " ??Col ":\n").
-define(PosW(Line, Col), ?PosW(__Kind, __File, Line, Col)).
-define(WARNING(Name, Warns),
(fun() ->
__Kind = "Warning",
__File = ??Name,
Warns
end)()).
warnings() ->
?WARNING(warnings,
[<<?PosW(0, 0)
"The file Triple.aes is included but not used">>,
<<?PosW(13, 3)
"The function h is defined at line 13, column 3 but never used">>,
<<?PosW(19, 3)
"The type unused_type is defined at line 19, column 3 but never used">>,
<<?PosW(23, 54)
"Negative spend at line 23, column 54">>,
<<?PosW(27, 9)
"The definition of x at line 27, column 9 shadows an older definition at line 26, column 9">>,
<<?PosW(30, 36)
"Division by zero at line 30, column 36">>,
<<?PosW(32, 3)
"The function unused_stateful is unnecessarily marked as stateful at line 32, column 3">>,
<<?PosW(35, 31)
"The variable unused_arg is defined at line 35, column 31 but never used">>,
<<?PosW(36, 9)
"The variable unused_var is defined at line 36, column 9 but never used">>,
<<?PosW(41, 3)
"The function unused_function is defined at line 41, column 3 but never used">>,
<<?PosW(42, 3)
"The function recursive_unused_function is defined at line 42, column 3 but never used">>,
<<?PosW(43, 3)
"The function called_unused_function1 is defined at line 43, column 3 but never used">>,
<<?PosW(44, 3)
"The function called_unused_function2 is defined at line 44, column 3 but never used">>,
<<?PosW(48, 5)
"Unused return value at line 48, column 5">>
]).
failing_contracts() ->
{ok, V} = aeso_compiler:numeric_version(),
Version = list_to_binary(string:join([integer_to_list(N) || N <- V], ".")),
@@ -817,6 +874,36 @@ failing_contracts() ->
[<<?Pos(4,24)
"Cannot unify string\n and bool\nwhen checking the type of the expression at line 4, column 24\n \"y\" : string\nagainst the expected type\n bool">>
])
, ?TYPE_ERROR(warnings,
[<<?Pos(0, 0)
"The file Triple.aes is included but not used">>,
<<?Pos(13, 3)
"The function h is defined at line 13, column 3 but never used">>,
<<?Pos(19, 3)
"The type unused_type is defined at line 19, column 3 but never used">>,
<<?Pos(23, 54)
"Negative spend at line 23, column 54">>,
<<?Pos(27, 9)
"The definition of x at line 27, column 9 shadows an older definition at line 26, column 9">>,
<<?Pos(30, 36)
"Division by zero at line 30, column 36">>,
<<?Pos(32, 3)
"The function unused_stateful is unnecessarily marked as stateful at line 32, column 3">>,
<<?Pos(35, 31)
"The variable unused_arg is defined at line 35, column 31 but never used">>,
<<?Pos(36, 9)
"The variable unused_var is defined at line 36, column 9 but never used">>,
<<?Pos(41, 3)
"The function unused_function is defined at line 41, column 3 but never used">>,
<<?Pos(42, 3)
"The function recursive_unused_function is defined at line 42, column 3 but never used">>,
<<?Pos(43, 3)
"The function called_unused_function1 is defined at line 43, column 3 but never used">>,
<<?Pos(44, 3)
"The function called_unused_function2 is defined at line 44, column 3 but never used">>,
<<?Pos(48, 5)
"Unused return value at line 48, column 5">>
])
].
-define(Path(File), "code_errors/" ??File).
+49
View File
@@ -0,0 +1,49 @@
// Used include
include "Pair.aes"
// Unused include
include "Triple.aes"
namespace UnusedNamespace =
function f() = 1 + g()
// Used in f
private function g() = 2
// Unused
private function h() = 3
contract Warnings =
type state = int
type unused_type = bool
entrypoint init(p) = Pair.fst(p) + Pair.snd(p)
stateful entrypoint negative_spend(to : address) = Chain.spend(to, -1)
entrypoint shadowing() =
let x = 1
let x = 2
x
entrypoint division_by_zero(x) = x / 0
stateful entrypoint unused_stateful() = 1
stateful entrypoint used_stateful(x : int) = put(x)
entrypoint unused_variables(unused_arg : int) =
let unused_var = 10
let z = 20
z
// Unused functions
function unused_function() = ()
function recursive_unused_function() = recursive_unused_function()
function called_unused_function1() = called_unused_function2()
function called_unused_function2() = called_unused_function1()
function rv() = 1
entrypoint unused_return_value() =
rv()
2