Ban using contracts as namespaces (#428)

* Ban calling contracts functions as functions namespaces

* Ban using contracts as namespaces

* Add tests

* Update CHANGELOG

* Separate guards with a semicolon
This commit is contained in:
Gaith Hallak 2022-11-23 12:03:24 +03:00 committed by GitHub
parent 256df25af4
commit 5adeb6c93e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 28 additions and 1 deletions

View File

@ -17,6 +17,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Type definitions serialised to ACI as `typedefs` field instead of `type_defs` to increase compatibility.
- Check contracts and entrypoints modifiers when implementing interfaces.
- Contracts can no longer be used as namespaces.
### Removed
### Fixed
- Typechecker crashes if Chain.create or Chain.clone are used without arguments.

View File

@ -229,7 +229,7 @@ force_bind_fun(X, Type, Env = #env{ what = What }) ->
NoCode = get_option(no_code, false),
Entry = if X == "init", What == contract, not NoCode ->
{reserved_init, Ann, Type};
What == contract_interface -> {contract_fun, Ann, Type};
What == contract; What == contract_interface -> {contract_fun, Ann, Type};
true -> {Ann, Type}
end,
on_current_scope(Env, fun(Scope = #scope{ funs = Funs }) ->
@ -426,6 +426,7 @@ lookup_env(Env, Kind, Ann, Name) ->
lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes = Scopes }, Kind, Ann, QName) ->
Qual = lists:droplast(QName),
Name = lists:last(QName),
QNameIsEvent = lists:suffix(["Chain", "event"], QName),
AllowPrivate = lists:prefix(Qual, Current),
%% Get the scope
case maps:get(Qual, Scopes, false) of
@ -441,6 +442,8 @@ lookup_env1(#env{ namespace = Current, used_namespaces = UsedNamespaces, scopes
{reserved_init, Ann1, Type} ->
type_error({cannot_call_init_function, Ann}),
{QName, {Ann1, Type}}; %% Return the type to avoid an extra not-in-scope error
{contract_fun, Ann1, Type} when AllowPrivate orelse QNameIsEvent ->
{QName, {Ann1, Type}};
{contract_fun, Ann1, Type} ->
type_error({contract_treated_as_namespace, Ann, QName}),
{QName, {Ann1, Type}};
@ -1254,6 +1257,10 @@ check_usings(Env = #env{ used_namespaces = UsedNamespaces }, [{using, Ann, Con,
create_type_errors(),
type_error({using_undefined_namespace, Ann, qname(Con)}),
destroy_and_report_type_errors(Env);
#scope{kind = contract} ->
create_type_errors(),
type_error({using_undefined_namespace, Ann, qname(Con)}),
destroy_and_report_type_errors(Env);
Scope ->
Nsp = case Parts of
none ->

View File

@ -1175,6 +1175,13 @@ failing_contracts() ->
[<<?Pos(5,16)
"`f` must be payable because the entrypoint `f` in the interface `I` is payable">>
])
, ?TYPE_ERROR(calling_child_contract_entrypoint,
[<<?Pos(5,20)
"Invalid call to contract entrypoint `F.g`.\n"
"It must be called as `c.g` for some `c : F`.">>])
, ?TYPE_ERROR(using_contract_as_namespace,
[<<?Pos(5,3)
"Cannot use undefined namespace F">>])
].
validation_test_() ->

View File

@ -0,0 +1,5 @@
contract F =
entrypoint g() = 1
main contract C =
entrypoint f() = F.g()

View File

@ -0,0 +1,7 @@
contract F =
entrypoint g() = 1
main contract C =
using F for [g]
entrypoint f() = g()