Skip to content

Commit d71802f

Browse files
Use marker trait for finding ink! storage struct during code analysis (#2499)
* Use marker trait for finding ink! storage struct during code analysis * Update changelog * chore: fix `6.0.0-alpha` headings
1 parent 02a1d05 commit d71802f

File tree

3 files changed

+99
-21
lines changed

3 files changed

+99
-21
lines changed

CHANGELOG.md

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
[Unreleased]
88

9+
### Changed
10+
11+
- Use marker trait for finding ink! storage `struct` during code analysis - [2499](https://github.com/use-ink/ink/pull/2499)
12+
913
## Version 6.0.0-alpha
1014

1115
This is our first alpha release for ink! v6. We release it together
@@ -210,12 +214,12 @@ As before, it functions as a simple local development node.
210214
It contains `pallet-revive` in a default configuration.
211215
You can find binary releases of the node [here](https://github.com/use-ink/ink-node/releases).
212216

213-
## Changed
217+
### Changed
214218
- Restrict which `cfg` attributes can be used ‒ [#2313](https://github.com/use-ink/ink/pull/2313)
215219
- More idiomatic return types for metadata getters - [#2398](https://github.com/use-ink/ink/pull/2398)
216220
- Define static distributed events slice in `ink` crate - [#2487](https://github.com/use-ink/ink/pull/2487)
217221

218-
## Added
222+
### Added
219223
- Support for `caller_is_root` - [#2332](https://github.com/use-ink/ink/pull/2332)
220224
- Allow setting features for contract build in E2E tests - [#2460](https://github.com/use-ink/ink/pull/2460)
221225
- Improve support for Solidity ABI calling conventions - [#2411](https://github.com/use-ink/ink/pull/2411)
@@ -224,7 +228,7 @@ You can find binary releases of the node [here](https://github.com/use-ink/ink-n
224228
- Documentation for contract abi arg and provided Rust/ink! to Solidity type mappings - [2463](https://github.com/use-ink/ink/pull/2463)
225229
- Implement `SolDecode`, `SolTypeDecode` and support `SolBytes` for boxed slices - [2476](https://github.com/use-ink/ink/pull/2476)
226230

227-
## Fixed
231+
### Fixed
228232
- [E2E] Have port parsing handle comma-separated list ‒ [#2336](https://github.com/use-ink/ink/pull/2336)
229233
- Always use ink! ABI/ SCALE codec for constructor and instantiation related builders and utilities - [#2474](https://github.com/use-ink/ink/pull/2474)
230234
- Get rid of "extrinsic for call failed: Pallet error: Revive::AccountAlreadyMapped" - [2483](https://github.com/use-ink/ink/pull/2483)

crates/ink/codegen/src/generator/storage.rs

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,13 +81,16 @@ impl Storage<'_> {
8181
///
8282
/// # Developer Note
8383
///
84-
/// The `fortanix` config attribute is used here to convey the
85-
/// information that the generated struct is an ink! storage struct to `dylint`.
84+
/// The `__ink_StorageMarker` trait marks the generated `struct` as an ink! storage
85+
/// `struct`.
8686
///
87-
/// We decided on this attribute to mark the function, as it has to be a
88-
/// key-value pair that is well known to `cargo`. `fortanix` seems like an obscure
89-
/// vendor, for which it is highly unlikely that someone will ever compile
90-
/// a contract for.
87+
/// This marker trait can be used to find the ink! storage struct during code analysis
88+
/// stages of the compilation pipeline (e.g. in the `ink_linting` infrastructure).
89+
///
90+
/// This approach is similar to Rust's use of [marker][rust-markers] traits
91+
/// to express that a type satisfies some property.
92+
///
93+
/// [rust-markers]: https://doc.rust-lang.org/std/marker/index.html
9194
fn generate_storage_struct(&self) -> TokenStream2 {
9295
let storage = self.contract.module().storage();
9396
let span = storage.span();
@@ -99,7 +102,6 @@ impl Storage<'_> {
99102
#(#attrs)*
100103
#[::ink::storage_item]
101104
#[cfg_attr(test, derive(::core::fmt::Debug))]
102-
#[cfg(not(target_vendor = "fortanix"))]
103105
pub struct #ident #generics {
104106
#( #fields ),*
105107
}
@@ -108,6 +110,10 @@ impl Storage<'_> {
108110
impl ::ink::reflect::ContractName for #ident {
109111
const NAME: &'static str = ::core::stringify!(#ident);
110112
}
113+
114+
#[allow(non_camel_case_types)]
115+
trait __ink_StorageMarker {}
116+
impl __ink_StorageMarker for #ident {}
111117
};
112118
)
113119
}

linting/utils/src/lib.rs

Lines changed: 79 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,11 @@ pub use parity_clippy_utils as clippy;
3535
use clippy::match_def_path;
3636
use if_chain::if_chain;
3737
use rustc_hir::{
38+
def::DefKind,
39+
def_id::{
40+
DefId,
41+
LOCAL_CRATE,
42+
},
3843
ExprKind,
3944
HirId,
4045
ItemId,
@@ -45,11 +50,39 @@ use rustc_hir::{
4550
TyKind,
4651
};
4752
use rustc_lint::LateContext;
53+
use rustc_middle::ty::{
54+
fast_reject::SimplifiedType,
55+
TyCtxt,
56+
};
57+
58+
/// Returns `DefId` of the `__ink_StorageMarker` marker trait (if any).
59+
///
60+
/// # Developer Note
61+
///
62+
/// In ink! 6.0.0, our code generation implements the marker trait `__ink_StorageMarker`
63+
/// for the `#[ink(storage)]` annotated `struct`.
64+
///
65+
/// This marker trait can be used to find the ink! storage struct.
66+
///
67+
/// This approach is similar to Rust's use of [marker][rust-markers] traits
68+
/// to express that a type satisfies some property.
69+
///
70+
/// [rust-markers]: https://doc.rust-lang.org/std/marker/index.html
71+
fn storage_marker_trait(tcx: TyCtxt) -> Option<&DefId> {
72+
tcx.traits(LOCAL_CRATE).iter().find(|trait_def_id| {
73+
tcx.item_name(**trait_def_id).as_str() == "__ink_StorageMarker"
74+
})
75+
}
4876

4977
/// Returns `true` iff the ink storage attribute is defined for the given HIR
5078
///
5179
/// # Developer Note
5280
///
81+
/// **IMPORTANT**: The description below doesn't apply to ink! 6.0.0,
82+
/// because this mechanism is now replaced with the more idiomatic approach
83+
/// of using a "marker" trait to identify the ink! storage `struct`
84+
/// (see [`storage_marker_trait`] for details).
85+
///
5386
/// In ink! 5.0.0 our code generation added the annotation
5487
/// `#[cfg(not(feature = "__ink_dylint_Storage"))] to contracts. This
5588
/// allowed dylint to identify the storage struct in a contract.
@@ -74,20 +107,55 @@ fn has_storage_attr(cx: &LateContext, hir: HirId) -> bool {
74107
attrs.contains(INK_STORAGE_1) || attrs.contains(INK_STORAGE_2)
75108
}
76109

110+
/// Returns `DefId` of the `struct` annotated with `#[ink(storage)]` (if any).
111+
///
112+
/// See [`storage_marker_trait`].
113+
pub fn find_storage_struct_def(tcx: TyCtxt) -> Option<&DefId> {
114+
let storage_marker_trait_id = storage_marker_trait(tcx)?;
115+
let storage_marker_impls = tcx
116+
.trait_impls_of(storage_marker_trait_id)
117+
.non_blanket_impls();
118+
debug_assert_eq!(
119+
storage_marker_impls.len(),
120+
1,
121+
"Expected exactly one implementation of the `__ink_StorageMarker` marker trait"
122+
);
123+
let (storage_ty, _) = storage_marker_impls.first()?;
124+
let SimplifiedType::Adt(adt_def_id) = storage_ty else {
125+
return None;
126+
};
127+
matches!(tcx.def_kind(adt_def_id), DefKind::Struct).then_some(adt_def_id)
128+
}
129+
77130
/// Returns `ItemId` of the structure annotated with `#[ink(storage)]`
78131
pub fn find_storage_struct(cx: &LateContext, item_ids: &[ItemId]) -> Option<ItemId> {
79-
item_ids
80-
.iter()
81-
.find(|&item_id| {
82-
let item = cx.tcx.hir_item(*item_id);
83-
if_chain! {
84-
if has_storage_attr(cx, item.hir_id());
85-
if let ItemKind::Struct(..) = item.kind;
86-
then { true } else { false }
87-
132+
let tcx = cx.tcx;
133+
// Finds storage item using `__ink_StorageMarker` marker trait for ink! 6.0.0
134+
// (see [`storage_marker_trait`] for details).
135+
let storage_item_id = find_storage_struct_def(tcx)
136+
.and_then(|def_id| def_id.as_local())
137+
.map(|local_def_id| tcx.local_def_id_to_hir_id(local_def_id))
138+
.map(|hir_id| {
139+
// See `Item::hir_id` for reverse operation.
140+
ItemId {
141+
owner_id: hir_id.owner,
88142
}
89-
})
90-
.copied()
143+
});
144+
storage_item_id.or_else(|| {
145+
// Fallback for ink! 5.0.0 (see [`has_storage_attr`] for details).
146+
item_ids
147+
.iter()
148+
.find(|&item_id| {
149+
let item = cx.tcx.hir_item(*item_id);
150+
if_chain! {
151+
if has_storage_attr(cx, item.hir_id());
152+
if let ItemKind::Struct(..) = item.kind;
153+
then { true } else { false }
154+
155+
}
156+
})
157+
.copied()
158+
})
91159
}
92160

93161
/// Returns `ItemId`s defined inside the code block of `const _: () = {}`.

0 commit comments

Comments
 (0)