Skip to content

Commit

Permalink
Linter: Split ink_linting into two libraries (#2032)
Browse files Browse the repository at this point in the history
* feat(linter): Split `ink_linting` into two libraries

Split `ink_linting` to `ink_linting_mandatory` and `ink_linting`.

Mandatory lints will be integrated in the `cargo-build` build
process in use-ink/cargo-contract#1412.

Extra lints are optional and could be run by the contract developer to
highlight possible issues with secure coding style and to check the
compliance with best practices.

For more information about this design decision, see: use-ink/cargo-contract#1412 (comment).

Closes #2006

* chore: Update CHANGELOG

* feat: Run linting tests in github-actions

* fix(ci): Add caching for `linting` builds

* fix(ci): gh-actions syntax

* fix(ci)

* fix(ci): Remove `--locked` flag for linting

This is necessary, because the lockfile needs to be updated since we run
build with a different `rust-toolchain.yml`.
  • Loading branch information
jubnzv authored Jan 3, 2024
1 parent ec9b925 commit 306145c
Show file tree
Hide file tree
Showing 28 changed files with 224 additions and 92 deletions.
9 changes: 6 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ jobs:
for crate in ${ALSO_WASM_CRATES}; do
cargo check --verbose --no-default-features --target wasm32-unknown-unknown \
--manifest-path ./crates/${crate}/Cargo.toml;
done
done
- name: Check RISCV
if: ${{ matrix.type == 'RISCV' }}
Expand All @@ -238,7 +238,7 @@ jobs:
for crate in ${ALSO_WASM_CRATES}; do
cargo check --verbose --no-default-features --target $RISCV_TARGET -Zbuild-std="core,alloc" \
--manifest-path ./crates/${crate}/Cargo.toml;
done
done
dylint:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -336,7 +336,9 @@ jobs:
- name: Cache
uses: Swatinem/rust-cache@v2
with:
cache-directories: ${{ env.CARGO_TARGET_DIR }}
cache-directories: |
${{ env.CARGO_TARGET_DIR }}
${{ env.CARGO_TARGET_DIR }}/linting
- name: Rust Info
uses: ./.github/rust-info
Expand All @@ -353,6 +355,7 @@ jobs:
run: |
cargo test --verbose --all-features --no-fail-fast --workspace --locked
cargo test --verbose --all-features --no-fail-fast --workspace --doc --locked
pushd linting && cargo test --verbose --all-features --no-fail-fast --workspace && popd
docs:
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Support multiple chain extensions - [#1958](https://github.com/paritytech/ink/pull/1958)
- New example of how to use multiple chain extensions in one contract.
- Affects the usage of the `#[ink::chain_extension]` macro and the definition of the chain extension.
- Split up `ink_linting` to mandatory and extra libraries - [#2032](https://github.com/paritytech/ink/pull/2032)


## Version 5.0.0-alpha
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ members = [
]
exclude = [
"integration-tests",
"linting",
]

[workspace.package]
Expand Down
91 changes: 9 additions & 82 deletions linting/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,85 +1,12 @@
[package]
name = "ink_linting"
version = "5.0.0-rc"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
publish = false

license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/paritytech/ink"
documentation = "https://docs.rs/ink_linting"
homepage = "https://github.com/paritytech/ink"
description = "Linting tool for ink! smart contracts."
keywords = ["parity", "blockchain", "ink", "smart contracts", "substrate"]
include = ["Cargo.toml", "*.rs", "LICENSE"]

[lib]
crate-type = ["cdylib"]

[dependencies]
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "1d334696587ac22b3a9e651e7ac684ac9e0697b2" }
dylint_linting = "2.1.12"
if_chain = "1.0.2"
log = "0.4.14"
regex = "1.5.4"

[dev-dependencies]
dylint_testing = "2.1.12"

# The following are ink! dependencies, they are only required for the `ui` tests.
ink_env = { path = "../crates/env", default-features = false }
ink = { path = "../crates/ink", default-features = false, features = ["std"] }
ink_metadata = { path = "../crates/metadata", default-features = false }
ink_primitives = { path = "../crates/primitives", default-features = false }
ink_storage = { path = "../crates/storage", default-features = false }
scale = { package = "parity-scale-codec", version = "3.4", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"] }

# For the moment we have to include the tests as examples and
# then use `dylint_testing::ui_test_examples`.
#
# The reason is that the `dylint_testing` API currently does not
# provide any other option to run the tests on those files
# *while giving us the option to specify the dependencies*.
#
# Those files require the ink! dependencies though, by having
# them as examples here, they inherit the `dev-dependencies`.
[[example]]
name = "primitive_topic_pass"
path = "ui/pass/primitive_topic.rs"
[[example]]
name = "primitive_topic_fail"
path = "ui/fail/primitive_topic.rs"
[[example]]
name = "storage_never_freed_pass"
path = "ui/pass/storage_never_freed.rs"
[[example]]
name = "storage_never_freed_fail"
path = "ui/fail/storage_never_freed.rs"
[[example]]
name = "strict_balance_equality_pass"
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

[workspace]
resolver = "2"
members = [
"mandatory",
"extra",
]

[features]
default = ["std"]
std = [
"ink_metadata/std",
"ink_env/std",
"ink_storage/std",
"ink_primitives/std",
"scale/std",
"scale-info/std",
[workspace.metadata.dylint]
libraries = [
{ path = "mandatory" },
{ path = "extra" },
]
ink-as-dependency = []
13 changes: 9 additions & 4 deletions linting/README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
# ink! linting rules

This crate uses [`dylint`](https://github.com/trailofbits/dylint) to define custom
linting rules for [ink!](https://github.com/paritytech/ink);
linting rules for [ink!](https://github.com/paritytech/ink).

It is not part of the workspace because it needs a custom linker to be built.

The lints are written against a fixed toolchain version because they are using unstable
APIs. This is why we have a toolchain file here.
APIs. This is why we have [a toolchain file](./rust-toolchain.toml) here.

You can use it by running `cargo dylint` after adding this to your `Cargo.toml`:
This crate contains two libraries:
* [`mandatory`](./mandatory) lints are integrated into the ink! smart contracts' build process, adding custom compilation errors to `cargo-build`.
* [`extra`](./extra) lints are designed to check for secure coding style in smart contracts and highlight potential issues. These are optional and intended for use by the contract developer to improve the security properties of their project.

You can use them by running `cargo dylint` after adding this to your `Cargo.toml`:

```toml
[workspace.metadata.dylint]
libraries = [
{ git = "https://github.com/paritytech/ink.git", pattern = "linting/" },
{ git = "https://github.com/paritytech/ink.git", pattern = "linting/mandatory" },
{ git = "https://github.com/paritytech/ink.git", pattern = "linting/extra" },
]
```
79 changes: 79 additions & 0 deletions linting/extra/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
[package]
name = "ink_linting"
version = "5.0.0-rc"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
publish = false

license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/paritytech/ink"
documentation = "https://docs.rs/ink_linting"
homepage = "https://github.com/paritytech/ink"
description = "Extra linting rules for ink! smart contracts"
keywords = ["parity", "blockchain", "ink", "smart contracts", "substrate"]
include = ["Cargo.toml", "*.rs", "LICENSE"]

[lib]
crate-type = ["cdylib"]

[dependencies]
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "1d334696587ac22b3a9e651e7ac684ac9e0697b2" }
dylint_linting = "2.1.12"
if_chain = "1.0.2"
log = "0.4.14"
regex = "1.5.4"

[dev-dependencies]
dylint_testing = "2.1.12"
# The following are ink! dependencies, they are only required for the `ui` tests.
ink_env = { path = "../../crates/env", default-features = false }
ink = { path = "../../crates/ink", default-features = false, features = ["std"] }
ink_metadata = { path = "../../crates/metadata", default-features = false }
ink_primitives = { path = "../../crates/primitives", default-features = false }
ink_storage = { path = "../../crates/storage", default-features = false }
scale = { package = "parity-scale-codec", version = "3.4", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"] }

# For the moment we have to include the tests as examples and
# then use `dylint_testing::ui_test_examples`.
#
# The reason is that the `dylint_testing` API currently does not
# provide any other option to run the tests on those files
# *while giving us the option to specify the dependencies*.
#
# Those files require the ink! dependencies though, by having
# them as examples here, they inherit the `dev-dependencies`.
[[example]]
name = "primitive_topic_pass"
path = "ui/pass/primitive_topic.rs"
[[example]]
name = "primitive_topic_fail"
path = "ui/fail/primitive_topic.rs"
[[example]]
name = "storage_never_freed_pass"
path = "ui/pass/storage_never_freed.rs"
[[example]]
name = "storage_never_freed_fail"
path = "ui/fail/storage_never_freed.rs"
[[example]]
name = "strict_balance_equality_pass"
path = "ui/pass/strict_balance_equality.rs"
[[example]]
name = "strict_balance_equality_fail"
path = "ui/fail/strict_balance_equality.rs"

[package.metadata.rust-analyzer]
rustc_private = true

[features]
default = ["std"]
std = [
"ink_metadata/std",
"ink_env/std",
"ink_storage/std",
"ink_primitives/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
1 change: 1 addition & 0 deletions linting/extra/LICENSE
2 changes: 2 additions & 0 deletions linting/extra/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Extra ink! linting rules
These lints are designed to check for secure coding style in smart contracts and highlight potential issues. These are optional and intended for use by the contract developer to improve the security properties of their project.
File renamed without changes.
3 changes: 0 additions & 3 deletions linting/src/lib.rs → linting/extra/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ 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;
Expand All @@ -47,13 +46,11 @@ 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]
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
64 changes: 64 additions & 0 deletions linting/mandatory/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
[package]
name = "ink_linting_mandatory"
version = "5.0.0-rc"
authors = ["Parity Technologies <admin@parity.io>"]
edition = "2021"
publish = false

license = "Apache-2.0"
readme = "README.md"
repository = "https://github.com/paritytech/ink"
documentation = "https://docs.rs/ink_linting"
homepage = "https://github.com/paritytech/ink"
description = "Mandatory ink! linting rules integrated in contracts' build process"
keywords = ["parity", "blockchain", "ink", "smart contracts", "substrate"]
include = ["Cargo.toml", "*.rs", "LICENSE"]

[lib]
crate-type = ["cdylib"]

[dependencies]
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "1d334696587ac22b3a9e651e7ac684ac9e0697b2" }
dylint_linting = "2.1.12"
if_chain = "1.0.2"
log = "0.4.14"
regex = "1.5.4"

[dev-dependencies]
dylint_testing = "2.1.12"
# The following are ink! dependencies, they are only required for the `ui` tests.
ink_env = { path = "../../crates/env", default-features = false }
ink = { path = "../../crates/ink", default-features = false, features = ["std"] }
ink_metadata = { path = "../../crates/metadata", default-features = false }
ink_primitives = { path = "../../crates/primitives", default-features = false }
ink_storage = { path = "../../crates/storage", default-features = false }
scale = { package = "parity-scale-codec", version = "3.4", default-features = false, features = ["derive"] }
scale-info = { version = "2.6", default-features = false, features = ["derive"] }

# For the moment we have to include the tests as examples and
# then use `dylint_testing::ui_test_examples`.
#
# The reason is that the `dylint_testing` API currently does not
# provide any other option to run the tests on those files
# *while giving us the option to specify the dependencies*.
#
# Those files require the ink! dependencies though, by having
# them as examples here, they inherit the `dev-dependencies`.
[[example]]
name = "no_main_pass"
path = "ui/pass/no_main.rs"

[package.metadata.rust-analyzer]
rustc_private = true

[features]
default = ["std"]
std = [
"ink_metadata/std",
"ink_env/std",
"ink_storage/std",
"ink_primitives/std",
"scale/std",
"scale-info/std",
]
ink-as-dependency = []
1 change: 1 addition & 0 deletions linting/mandatory/LICENSE
2 changes: 2 additions & 0 deletions linting/mandatory/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Mandatory ink! linting rules
dylint-based lints integrated into the ink! contracts' build process, intended to generate custom compilation errors.
Loading

0 comments on commit 306145c

Please sign in to comment.