diff --git a/CHANGELOG.md b/CHANGELOG.md index 29471ad9400..d90b21267d9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - [E2E] Call builders and extra gas margin option - [#1917](https://github.com/paritytech/ink/pull/1917) - Linter: `storage_never_freed` lint - [#1932](https://github.com/paritytech/ink/pull/1932) - Linter: `strict_balance_equality` lint - [#1914](https://github.com/paritytech/ink/pull/1914) +- Linter: `no_main` lint - [#2001](https://github.com/paritytech/ink/pull/2001) - Clean E2E configuration parsing - [#1922](https://github.com/paritytech/ink/pull/1922) - Make `set_code_hash` generic - [#1906](https://github.com/paritytech/ink/pull/1906) diff --git a/linting/Cargo.toml b/linting/Cargo.toml index ff2a4228e16..77c41f32056 100644 --- a/linting/Cargo.toml +++ b/linting/Cargo.toml @@ -63,6 +63,9 @@ path = "ui/pass/strict_balance_equality.rs" [[example]] name = "strict_balance_equality_fail" path = "ui/fail/strict_balance_equality.rs" +[[example]] +name = "no_main_pass" +path = "ui/pass/no_main.rs" [package.metadata.rust-analyzer] rustc_private = true diff --git a/linting/src/lib.rs b/linting/src/lib.rs index fdb19ab04b7..e9b7673e1bd 100644 --- a/linting/src/lib.rs +++ b/linting/src/lib.rs @@ -32,6 +32,7 @@ extern crate rustc_session; extern crate rustc_span; mod ink_utils; +mod no_main; mod primitive_topic; mod storage_never_freed; mod strict_balance_equality; @@ -46,11 +47,13 @@ pub fn register_lints( primitive_topic::PRIMITIVE_TOPIC, storage_never_freed::STORAGE_NEVER_FREED, strict_balance_equality::STRICT_BALANCE_EQUALITY, + no_main::NO_MAIN, ]); lint_store.register_late_pass(|_| Box::new(primitive_topic::PrimitiveTopic)); lint_store.register_late_pass(|_| Box::new(storage_never_freed::StorageNeverFreed)); lint_store .register_late_pass(|_| Box::new(strict_balance_equality::StrictBalanceEquality)); + lint_store.register_early_pass(|| Box::new(no_main::NoMain)); } #[test] diff --git a/linting/src/no_main.rs b/linting/src/no_main.rs new file mode 100644 index 00000000000..3fff050b3b4 --- /dev/null +++ b/linting/src/no_main.rs @@ -0,0 +1,85 @@ +// Copyright (C) Parity Technologies (UK) Ltd. +// +// 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. + +use ast::{ + AttrStyle, + Crate, +}; +use clippy_utils::diagnostics::span_lint_and_help; +use if_chain::if_chain; +use rustc_ast as ast; +use rustc_lint::{ + EarlyContext, + EarlyLintPass, + LintContext, +}; +use rustc_middle::lint::in_external_macro; +use rustc_session::{ + declare_lint, + declare_lint_pass, +}; +use rustc_span::sym; + +declare_lint! { + /// ## What it does + /// Checks if a contract is annotated with the `no_main` inner attribute. + /// + /// ## Why is this necessary? + /// Contracts must be annotated with `no_main` inner attribute when compiled for on-chain + /// execution. + /// + /// ## Example + /// + /// ```rust + /// // Bad: Contract does not contain the `no_main` attribute, so it cannot be compiled to Wasm + /// #![cfg_attr(not(feature = "std"), no_std)] + /// #[ink::contract] + /// mod my_contract { /* ... */ } + /// ``` + /// + /// Use instead: + /// + /// ```rust + /// #![cfg_attr(not(feature = "std"), no_std, no_main)] + /// #[ink::contract] + /// mod my_contract { /* ... */ } + /// ``` + pub NO_MAIN, + Deny, + "contract must be annotated with the `no_main` inner attribute" +} + +declare_lint_pass!(NoMain => [NO_MAIN]); + +impl EarlyLintPass for NoMain { + fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &Crate) { + // `no_main` is an `Inner` attribute of `#![cfg_attr(...)]` + if krate.attrs.iter().all(|attr| { + if_chain! { + if !in_external_macro(cx.sess(), attr.span); + if let AttrStyle::Inner = attr.style; + if attr.has_name(sym::no_main); + then { false } else { true }} + }) { + span_lint_and_help( + cx, + NO_MAIN, + krate.spans.inner_span, + "contract must be annotated with the `no_main` inner attribute", + None, + "consider annotating contract with `#![cfg_attr(not(feature = \"std\"), no_std, no_main)]` or `#![no_main]`" + ) + } + } +} diff --git a/linting/ui/fail/primitive_topic.rs b/linting/ui/fail/primitive_topic.rs index 6c0cb5d00e2..59580d3d8b8 100644 --- a/linting/ui/fail/primitive_topic.rs +++ b/linting/ui/fail/primitive_topic.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_main)] + pub type TyAlias1 = i32; pub type TyAlias2 = TyAlias1; diff --git a/linting/ui/fail/primitive_topic.stderr b/linting/ui/fail/primitive_topic.stderr index 634dacf4b8f..6fcf4c5efc6 100644 --- a/linting/ui/fail/primitive_topic.stderr +++ b/linting/ui/fail/primitive_topic.stderr @@ -1,5 +1,5 @@ error: using `#[ink(topic)]` for a field with a primitive number type - --> $DIR/primitive_topic.rs:11:9 + --> $DIR/primitive_topic.rs:13:9 | LL | value_1: u8, | ^^^^^^^^^^^ help: consider removing `#[ink(topic)]`: `value_1: u8` @@ -7,19 +7,19 @@ LL | value_1: u8, = note: `-D primitive-topic` implied by `-D warnings` error: using `#[ink(topic)]` for a field with a primitive number type - --> $DIR/primitive_topic.rs:14:9 + --> $DIR/primitive_topic.rs:16:9 | LL | value_2: Balance, | ^^^^^^^^^^^^^^^^ help: consider removing `#[ink(topic)]`: `value_2: Balance` error: using `#[ink(topic)]` for a field with a primitive number type - --> $DIR/primitive_topic.rs:17:9 + --> $DIR/primitive_topic.rs:19:9 | LL | value_3: crate::TyAlias1, | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `#[ink(topic)]`: `value_3: crate::TyAlias1` error: using `#[ink(topic)]` for a field with a primitive number type - --> $DIR/primitive_topic.rs:20:9 + --> $DIR/primitive_topic.rs:22:9 | LL | value_4: crate::TyAlias2, | ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider removing `#[ink(topic)]`: `value_4: crate::TyAlias2` diff --git a/linting/ui/fail/storage_never_freed.rs b/linting/ui/fail/storage_never_freed.rs index d0fd1cdfa66..5140d9144ee 100644 --- a/linting/ui/fail/storage_never_freed.rs +++ b/linting/ui/fail/storage_never_freed.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), no_main)] #![cfg_attr(dylint_lib = "ink_linting", deny(storage_never_freed))] pub type MapAlias1 = ink::storage::Mapping; pub type MapAlias2 = MapAlias1; diff --git a/linting/ui/fail/storage_never_freed.stderr b/linting/ui/fail/storage_never_freed.stderr index 33dc0b438d1..25c952e1dce 100644 --- a/linting/ui/fail/storage_never_freed.stderr +++ b/linting/ui/fail/storage_never_freed.stderr @@ -1,18 +1,18 @@ error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:17:9 + --> $DIR/storage_never_freed.rs:18:9 | LL | map_1: Mapping, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: consider adding operations to remove elements available to the user note: the lint level is defined here - --> $DIR/storage_never_freed.rs:1:46 + --> $DIR/storage_never_freed.rs:2:46 | LL | #![cfg_attr(dylint_lib = "ink_linting", deny(storage_never_freed))] | ^^^^^^^^^^^^^^^^^^^ error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:18:9 + --> $DIR/storage_never_freed.rs:19:9 | LL | map_2: Mapping, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -20,7 +20,7 @@ LL | map_2: Mapping, = help: consider adding operations to remove elements available to the user error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:19:9 + --> $DIR/storage_never_freed.rs:20:9 | LL | map_3: Mapping, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -28,7 +28,7 @@ LL | map_3: Mapping, = help: consider adding operations to remove elements available to the user error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:20:9 + --> $DIR/storage_never_freed.rs:21:9 | LL | map_alias: MapAlias2, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -36,7 +36,7 @@ LL | map_alias: MapAlias2, = help: consider adding operations to remove elements available to the user error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:14:9 + --> $DIR/storage_never_freed.rs:15:9 | LL | vec_1: Vec, | ^^^^^^^^^^^^^^^^^^^^^ @@ -44,7 +44,7 @@ LL | vec_1: Vec, = help: consider adding operations to remove elements available to the user error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:15:9 + --> $DIR/storage_never_freed.rs:16:9 | LL | vec_2: Vec, | ^^^^^^^^^^^^^^^^ @@ -52,7 +52,7 @@ LL | vec_2: Vec, = help: consider adding operations to remove elements available to the user error: field's storage cannot be freed - --> $DIR/storage_never_freed.rs:16:9 + --> $DIR/storage_never_freed.rs:17:9 | LL | vec_subscription: Vec, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/linting/ui/fail/strict_balance_equality.rs b/linting/ui/fail/strict_balance_equality.rs index 7d022f84119..6f31e837e92 100644 --- a/linting/ui/fail/strict_balance_equality.rs +++ b/linting/ui/fail/strict_balance_equality.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_main)] + #[ink::contract] pub mod strict_balance_equality { #[ink(storage)] diff --git a/linting/ui/fail/strict_balance_equality.stderr b/linting/ui/fail/strict_balance_equality.stderr index 2ea711eafa1..ee67592ef72 100644 --- a/linting/ui/fail/strict_balance_equality.stderr +++ b/linting/ui/fail/strict_balance_equality.stderr @@ -1,5 +1,5 @@ error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:57:16 + --> $DIR/strict_balance_equality.rs:59:16 | LL | if self.env().balance() == 10 { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` @@ -7,67 +7,67 @@ LL | if self.env().balance() == 10 { /* ... */ } = note: `-D strict-balance-equality` implied by `-D warnings` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:58:16 + --> $DIR/strict_balance_equality.rs:60:16 | LL | if value == 11 { /* ... */ } | ^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:59:16 + --> $DIR/strict_balance_equality.rs:61:16 | LL | if self.env().balance() == threshold { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:62:16 + --> $DIR/strict_balance_equality.rs:64:16 | LL | if self.get_balance_1() == 10 { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:63:16 + --> $DIR/strict_balance_equality.rs:65:16 | LL | if self.get_balance_2() == 10 { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:64:16 + --> $DIR/strict_balance_equality.rs:66:16 | LL | if self.get_balance_3() == 10 { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:65:16 + --> $DIR/strict_balance_equality.rs:67:16 | LL | if self.get_balance_recursive(&10) == 10 { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:69:16 + --> $DIR/strict_balance_equality.rs:71:16 | LL | if self.cmp_balance_1(&10) { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:70:16 + --> $DIR/strict_balance_equality.rs:72:16 | LL | if self.cmp_balance_2(&self.env().balance(), &threshold) { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:71:16 + --> $DIR/strict_balance_equality.rs:73:16 | LL | if self.cmp_balance_3(self.env().balance(), threshold) { /* ... */ } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:76:16 + --> $DIR/strict_balance_equality.rs:78:16 | LL | if res_1 == 10 { /* ... */ } | ^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` error: dangerous strict balance equality - --> $DIR/strict_balance_equality.rs:79:16 + --> $DIR/strict_balance_equality.rs:81:16 | LL | if res_2 == 10 { /* ... */ } | ^^^^^^^^^^^ help: consider using non-strict equality operators instead: `<`, `>` diff --git a/linting/ui/pass/no_main.rs b/linting/ui/pass/no_main.rs new file mode 100644 index 00000000000..382e3613d87 --- /dev/null +++ b/linting/ui/pass/no_main.rs @@ -0,0 +1,17 @@ +#![cfg_attr(not(feature = "std"), no_std, no_main)] + +#[ink::contract] +pub mod no_main { + #[ink(storage)] + pub struct NoMain {} + impl NoMain { + #[ink(constructor)] + pub fn new() -> Self { + Self {} + } + #[ink(message)] + pub fn do_nothing(&mut self) {} + } +} + +fn main() {} diff --git a/linting/ui/pass/primitive_topic.rs b/linting/ui/pass/primitive_topic.rs index 6cc54280723..a58e4d5ee2c 100644 --- a/linting/ui/pass/primitive_topic.rs +++ b/linting/ui/pass/primitive_topic.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_main)] + #[ink::contract] pub mod primitive_topic { diff --git a/linting/ui/pass/storage_never_freed.rs b/linting/ui/pass/storage_never_freed.rs index aa14f2fd873..27563140df0 100644 --- a/linting/ui/pass/storage_never_freed.rs +++ b/linting/ui/pass/storage_never_freed.rs @@ -1,3 +1,4 @@ +#![cfg_attr(not(feature = "std"), no_main)] #![cfg_attr(dylint_lib = "ink_linting", deny(storage_never_freed))] pub type MapAlias = ink::storage::Mapping; diff --git a/linting/ui/pass/strict_balance_equality.rs b/linting/ui/pass/strict_balance_equality.rs index 2d3167c04ed..18eea954837 100644 --- a/linting/ui/pass/strict_balance_equality.rs +++ b/linting/ui/pass/strict_balance_equality.rs @@ -1,3 +1,5 @@ +#![cfg_attr(not(feature = "std"), no_main)] + #[ink::contract] pub mod strict_balance_equality { #[ink(storage)]