diff --git a/demo/rustc_driver/Cargo.toml b/demo/rustc_driver/Cargo.toml index 2644956..1f77e8c 100644 --- a/demo/rustc_driver/Cargo.toml +++ b/demo/rustc_driver/Cargo.toml @@ -9,5 +9,9 @@ path = "src/bin/cargo-safe-tool.rs" [dependencies] +[workspace] +members = ["safety-tool-lib","safety-tool-macro", "safety-tool-parser"] +exclude = ["tests/basic"] + [package.metadata.rust-analyzer] rustc_private = true diff --git a/demo/rustc_driver/run.sh b/demo/rustc_driver/run.sh index c75e407..f264eea 100755 --- a/demo/rustc_driver/run.sh +++ b/demo/rustc_driver/run.sh @@ -4,8 +4,8 @@ set -ex export LD_LIBRARY_PATH=$(rustc --print sysroot)/lib cargo build -export TAG_STD=$PWD/target/debug/safe-tool -export CARGO_TAG_STD=$PWD/target/debug/cargo-safe-tool +export SAFE_TOOL=$PWD/target/debug/safe-tool +export CARGO_SAFE_TOOL=$PWD/target/debug/cargo-safe-tool # Test basic demo cd ./tests/basic @@ -13,5 +13,5 @@ cd ./tests/basic cargo clean # Analyze the lib and bin crates. -# Same as `cargo tag-std` when tag-std and cargo-tag-std are installed. -$CARGO_TAG_STD +# Same as `cargo safe-tool` when tag-std and cargo-safe-tool are installed. +$CARGO_SAFE_TOOL diff --git a/demo/rustc_driver/safety-tool-lib/Cargo.toml b/demo/rustc_driver/safety-tool-lib/Cargo.toml new file mode 100644 index 0000000..6b2acab --- /dev/null +++ b/demo/rustc_driver/safety-tool-lib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "safety-tool-lib" +version = "0.1.0" +edition = "2024" + +[dependencies] +safety-tool-macro = { path = "../safety-tool-macro" } +safety-tool-parser = { path = "../safety-tool-parser" } diff --git a/demo/rustc_driver/safety-tool-lib/src/lib.rs b/demo/rustc_driver/safety-tool-lib/src/lib.rs new file mode 100644 index 0000000..3a89cb5 --- /dev/null +++ b/demo/rustc_driver/safety-tool-lib/src/lib.rs @@ -0,0 +1,5 @@ +pub use safety_tool_parser; + +pub mod safety { + pub use safety_tool_macro::{hazard, option, precond}; +} diff --git a/demo/rustc_driver/safety-tool-macro/Cargo.toml b/demo/rustc_driver/safety-tool-macro/Cargo.toml new file mode 100644 index 0000000..234b66a --- /dev/null +++ b/demo/rustc_driver/safety-tool-macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "safety-tool-macro" +version = "0.1.0" +edition = "2024" + +[lib] +proc-macro = true + +[dependencies] +safety-tool-parser = { path = "../safety-tool-parser" } + +[dev-dependencies] +expect-test = "1.5.1" diff --git a/demo/rustc_driver/safety-tool-macro/src/lib.rs b/demo/rustc_driver/safety-tool-macro/src/lib.rs new file mode 100644 index 0000000..f0c6858 --- /dev/null +++ b/demo/rustc_driver/safety-tool-macro/src/lib.rs @@ -0,0 +1,33 @@ +use proc_macro::TokenStream; +use safety_tool_parser::{ + precond::{FnItem, SafetyAttrArgs}, + syn::*, +}; + +#[proc_macro_attribute] +pub fn precond(attr: TokenStream, item: TokenStream) -> TokenStream { + generate("precond", attr, item) +} + +#[proc_macro_attribute] +pub fn hazard(attr: TokenStream, item: TokenStream) -> TokenStream { + generate("hazard", attr, item) +} + +#[proc_macro_attribute] +pub fn option(attr: TokenStream, item: TokenStream) -> TokenStream { + generate("option", attr, item) +} + +fn generate(kind: &str, attr: TokenStream, item: TokenStream) -> TokenStream { + let item = parse_macro_input!(item as ItemFn); + let attr = parse_macro_input!(attr as SafetyAttrArgs); + + let doc_comments = attr.generate_doc_comments(); + let safety_tool_attr = attr.generate_safety_tool_attribute(kind); + + let mut fn_item = FnItem::new(item); + fn_item.insert_attributes_to_the_back(doc_comments); + fn_item.insert_attributes_to_the_back(safety_tool_attr); + fn_item.into_token_stream().into() +} diff --git a/demo/rustc_driver/safety-tool-macro/tests/memo.rs b/demo/rustc_driver/safety-tool-macro/tests/memo.rs new file mode 100644 index 0000000..cbde952 --- /dev/null +++ b/demo/rustc_driver/safety-tool-macro/tests/memo.rs @@ -0,0 +1,10 @@ +use expect_test::expect_file; +use std::process::Command; + +#[test] +fn basic() { + let stdout = + Command::new("cargo").args(["expand", "--test", "memo_testcase"]).output().unwrap(); + let expanded = std::str::from_utf8(&stdout.stdout).unwrap(); + expect_file!["snapshots/memo_testcase.rs"].assert_eq(expanded); +} diff --git a/demo/rustc_driver/safety-tool-macro/tests/memo_testcase.rs b/demo/rustc_driver/safety-tool-macro/tests/memo_testcase.rs new file mode 100644 index 0000000..e77f7ca --- /dev/null +++ b/demo/rustc_driver/safety-tool-macro/tests/memo_testcase.rs @@ -0,0 +1,60 @@ +#![feature(register_tool)] +#![register_tool(Safety)] +#![allow(dead_code, non_snake_case)] +use safety_tool_macro as safety; + +#[safety::precond(Property, memo = "This is a single line.")] +fn single_line() {} + +#[safety::hazard(Property, memo = "Line 1.\nLine 2.")] +fn multi_lines() {} + +#[safety::option( + Property, + memo = " +Line 1. +Line 2." +)] +fn multi_lines2() {} + +/// Line 1. +#[safety::precond( + Property, + memo = " + Line 2. + Line 3." +)] +fn multi_lines3() {} + +#[doc = " Line 1."] +#[safety::hazard( + Property, + memo = " + Line 2. + Line 3." +)] +#[safety::option(Property, memo = "Line 4.")] +fn multi_lines4() {} + +// WARNING: dont't put `#[doc]` (i.e. `///`) after `#[safety(memo)]` +// because the doc order will be messed up. + +#[safety::precond( + Property, + memo = " + Line 1. + Line 2." +)] +#[doc = " Line 3."] // don't do this. +fn multi_lines3_DONT_DO_THIS() {} + +/// Line 1. +#[safety::precond(Property, memo = "Line 2.\nLine 3.")] +#[doc = " Line 4."] // don't do this. +fn multi_lines4_DONT_DO_THIS() {} + +/// Line 1. +#[safety::precond(Property, memo = "Line 2.\nLine 3.")] +#[safety::precond(Property, memo = "Line 4.")] +#[doc = " Line 5."] // don't do this. +fn multi_lines5_DONT_DO_THIS() {} diff --git a/demo/rustc_driver/safety-tool-macro/tests/snapshots/memo_testcase.rs b/demo/rustc_driver/safety-tool-macro/tests/snapshots/memo_testcase.rs new file mode 100644 index 0000000..3ef2535 --- /dev/null +++ b/demo/rustc_driver/safety-tool-macro/tests/snapshots/memo_testcase.rs @@ -0,0 +1,66 @@ +#![feature(prelude_import)] +#![feature(register_tool)] +#![register_tool(Safety)] +#![allow(dead_code, non_snake_case)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; +use safety_tool_macro as safety; +/// This is a single line. +#[Safety::inner(Property, kind = "precond", memo = "This is a single line.")] +fn single_line() {} +/// Line 1. +/// Line 2. +#[Safety::inner(Property, kind = "hazard", memo = "Line 1.\nLine 2.")] +fn multi_lines() {} +/// Line 1. +/// Line 2. +#[Safety::inner(Property, kind = "option", memo = " +Line 1. +Line 2.")] +fn multi_lines2() {} +/// Line 1. +/// Line 2. +/// Line 3. +#[Safety::inner(Property, kind = "precond", memo = " + Line 2. + Line 3.")] +fn multi_lines3() {} +/// Line 1. +/// Line 2. +/// Line 3. +#[Safety::inner(Property, kind = "hazard", memo = " + Line 2. + Line 3.")] +/// Line 4. +#[Safety::inner(Property, kind = "option", memo = "Line 4.")] +fn multi_lines4() {} +/// Line 3. +/// Line 1. +/// Line 2. +#[Safety::inner(Property, kind = "precond", memo = " + Line 1. + Line 2.")] +fn multi_lines3_DONT_DO_THIS() {} +/// Line 1. +/// Line 4. +/// Line 2. +/// Line 3. +#[Safety::inner(Property, kind = "precond", memo = "Line 2.\nLine 3.")] +fn multi_lines4_DONT_DO_THIS() {} +/// Line 1. +/// Line 5. +/// Line 2. +/// Line 3. +#[Safety::inner(Property, kind = "precond", memo = "Line 2.\nLine 3.")] +/// Line 4. +#[Safety::inner(Property, kind = "precond", memo = "Line 4.")] +fn multi_lines5_DONT_DO_THIS() {} +#[rustc_main] +#[coverage(off)] +#[doc(hidden)] +pub fn main() -> () { + extern crate test; + test::test_main_static(&[]) +} diff --git a/demo/rustc_driver/safety-tool-parser/Cargo.toml b/demo/rustc_driver/safety-tool-parser/Cargo.toml new file mode 100644 index 0000000..77504c3 --- /dev/null +++ b/demo/rustc_driver/safety-tool-parser/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "safety-tool-parser" +version = "0.1.0" +edition = "2024" + +[dependencies] +quote = "1" +syn = { version = "2", features = ["extra-traits", "full"] } +proc-macro2 = "1" diff --git a/demo/rustc_driver/safety-tool-parser/src/lib.rs b/demo/rustc_driver/safety-tool-parser/src/lib.rs new file mode 100644 index 0000000..7103346 --- /dev/null +++ b/demo/rustc_driver/safety-tool-parser/src/lib.rs @@ -0,0 +1,4 @@ +pub use quote; +pub use syn; + +pub mod precond; diff --git a/demo/rustc_driver/safety-tool-parser/src/precond/keep_doc_order.rs b/demo/rustc_driver/safety-tool-parser/src/precond/keep_doc_order.rs new file mode 100644 index 0000000..447ad16 --- /dev/null +++ b/demo/rustc_driver/safety-tool-parser/src/precond/keep_doc_order.rs @@ -0,0 +1,40 @@ +//! `#[safety]` attribute will generate safety doc (`#[doc]`), which will bring interactions +//! with `#[doc]` from user. +//! +//! Consider the following doc strings: +//! +//! ```ignore +//! /// Beginning +//! #[safety(memo = "safety doc")] +//! /// Tailling +//! ``` +//! +//! It'd be pretty bad if the order is messed up. +//! +//! Thus this module should solve this problem by ensuring the doc attribute +//! emission is inserted to the tail. Because attribute macros are handled +//! from top to buttom, this hopefully keeps doc orders. + +use proc_macro2::TokenStream; +use quote::ToTokens; +use syn::{parse::Parser, *}; + +pub struct FnItem { + pub fn_: ItemFn, +} + +impl FnItem { + pub fn new(fn_: ItemFn) -> Self { + FnItem { fn_ } + } + + pub fn insert_attributes_to_the_back(&mut self, tokens: TokenStream) { + // Double check the given tokens are attributes. + let attrs = Attribute::parse_outer.parse2(tokens).unwrap(); + self.fn_.attrs.extend(attrs); + } + + pub fn into_token_stream(self) -> TokenStream { + self.fn_.to_token_stream() + } +} diff --git a/demo/rustc_driver/safety-tool-parser/src/precond/mod.rs b/demo/rustc_driver/safety-tool-parser/src/precond/mod.rs new file mode 100644 index 0000000..99b8870 --- /dev/null +++ b/demo/rustc_driver/safety-tool-parser/src/precond/mod.rs @@ -0,0 +1,139 @@ +use proc_macro2::TokenStream; +use quote::{ToTokens, quote}; +use std::collections::BTreeSet; +use syn::{ + parse::{Parse, ParseStream}, + punctuated::Punctuated, + *, +}; + +mod utils; + +mod keep_doc_order; +pub use keep_doc_order::FnItem; + +#[cfg(test)] +mod tests; + +// ******************** Attribute Parsing ******************** + +#[derive(Debug)] +pub struct SafetyAttr { + pub attr: Attribute, + pub args: SafetyAttrArgs, +} + +impl Parse for SafetyAttr { + fn parse(input: ParseStream) -> Result { + let mut attrs = Attribute::parse_outer(input)?; + let attr = attrs.remove(0); + let args = attr.parse_args()?; + Ok(SafetyAttr { attr, args }) + } +} + +type ListNamedArgs = Punctuated; + +#[derive(Debug)] +pub struct SafetyAttrArgs { + pub expr: Expr, + pub comma: Option, + pub named: ListNamedArgs, +} + +impl Parse for SafetyAttrArgs { + fn parse(input: ParseStream) -> Result { + Ok(SafetyAttrArgs { + expr: input.parse()?, + comma: input.parse()?, + named: Punctuated::parse_separated_nonempty(input)?, + }) + } +} + +impl SafetyAttrArgs { + pub fn generate_doc_comments(&self) -> TokenStream { + NamedArgsSet::new(&self.named).generate_doc_comments() + } + + pub fn generate_safety_tool_attribute(&self, kind: &str) -> TokenStream { + let Self { expr, named, .. } = self; + quote! { + #[Safety::inner(#expr, kind = #kind, #named)] + } + } +} + +#[derive(Debug)] +pub struct NamedArgs { + pub name: Ident, + pub eq: Token![=], + pub expr: Expr, +} + +impl Parse for NamedArgs { + fn parse(input: ParseStream) -> Result { + Ok(NamedArgs { name: input.parse()?, eq: input.parse()?, expr: input.parse()? }) + } +} + +impl ToTokens for NamedArgs { + fn to_tokens(&self, tokens: &mut TokenStream) { + self.name.to_tokens(tokens); + self.eq.to_tokens(tokens); + self.expr.to_tokens(tokens); + } +} + +// ******************** Attribute Analyzing ******************** + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum NamedArg { + Kind(String), + Memo(String), +} + +impl NamedArg { + fn new(ident: &Ident, expr: &Expr) -> Self { + if ident == "memo" + && let Expr::Lit(lit) = expr + && let Lit::Str(memo) = &lit.lit + { + return NamedArg::Memo(memo.value()); + } + + if ident == "kind" + && let Expr::Lit(lit) = expr + && let Lit::Str(kind) = &lit.lit + { + return NamedArg::Memo(kind.value()); + } + + panic!("{ident:?} is not a supported ident.\nCurrently supported named arguments: memo.") + } + + /// Like generate rustdoc attributes to display doc comment in rustdoc HTML. + fn generate_doc_comments(&self) -> TokenStream { + match self { + NamedArg::Memo(memo) => utils::memo(memo), + _ => TokenStream::new(), + } + } +} + +#[derive(Debug)] +struct NamedArgsSet { + set: BTreeSet, +} + +impl NamedArgsSet { + fn new(named_args: &ListNamedArgs) -> Self { + NamedArgsSet { + set: named_args.iter().map(|arg| NamedArg::new(&arg.name, &arg.expr)).collect(), + } + } + + fn generate_doc_comments(&self) -> TokenStream { + self.set.iter().flat_map(NamedArg::generate_doc_comments).collect() + } +} diff --git a/demo/rustc_driver/safety-tool-parser/src/precond/tests.rs b/demo/rustc_driver/safety-tool-parser/src/precond/tests.rs new file mode 100644 index 0000000..8937a26 --- /dev/null +++ b/demo/rustc_driver/safety-tool-parser/src/precond/tests.rs @@ -0,0 +1,8 @@ +use super::*; + +#[test] +fn precond_align() { + let attr = r#"#[safety::precond(Align(self.ptr, T), memo = "reason")]"#; + let safety_attr: SafetyAttr = parse_str(attr).unwrap(); + dbg!(&safety_attr); +} diff --git a/demo/rustc_driver/safety-tool-parser/src/precond/utils.rs b/demo/rustc_driver/safety-tool-parser/src/precond/utils.rs new file mode 100644 index 0000000..39a5d11 --- /dev/null +++ b/demo/rustc_driver/safety-tool-parser/src/precond/utils.rs @@ -0,0 +1,15 @@ +use proc_macro2::TokenStream; +use quote::quote; + +/// memo = "reason" will generate `///reason`. which is a bit ugly. +/// So this function trim all whitespaces, and add single one for +/// each line. +pub fn memo(s: &str) -> TokenStream { + s.trim() + .lines() + .map(|line| { + let doc = format!(" {}", line.trim()); + quote!(#[doc = #doc]) + }) + .collect() +} diff --git a/demo/rustc_driver/src/bin/cargo-safe-tool.rs b/demo/rustc_driver/src/bin/cargo-safe-tool.rs index 006b3c7..d9f2821 100644 --- a/demo/rustc_driver/src/bin/cargo-safe-tool.rs +++ b/demo/rustc_driver/src/bin/cargo-safe-tool.rs @@ -1,26 +1,26 @@ use std::{env::var, process::Command}; fn main() { - // Search cargo-tag-std and tag-std CLI through environment variables, + // Search cargo-safe-tool and safe-tool CLI through environment variables, // or just use the name if absent. - let cargo_tag_std = var("CARGO_TAG_STD").unwrap_or_else(|_| "cargo-tag-std".to_owned()); - let tag_std = var("TAG_STD").unwrap_or_else(|_| "tag-std".to_owned()); + let cargo_safe_tool = &*var("CARGO_SAFE_TOOL").unwrap_or_else(|_| "cargo-safe-tool".to_owned()); + let safe_tool = &*var("SAFE_TOOL").unwrap_or_else(|_| "safe-tool".to_owned()); - let mut args = std::env::args().collect::>(); + let args = std::env::args().collect::>(); if args.len() == 2 && args[1].as_str() == "-vV" { // cargo invokes `rustc -vV` first run("rustc", &["-vV".to_owned()], &[]); } else if std::env::var("WRAPPER").as_deref() == Ok("1") { // then cargo invokes `rustc - --crate-name ___ --print=file-names` - if args[1] == "-" { - // `rustc -` is a substitute file name from stdin - args[1] = "src/lib.rs".to_owned(); - } + // if args[1] == "-" { + // // `rustc -` is a substitute file name from stdin + // args[1] = "src/main.rs".to_owned(); + // } - run(&tag_std, &args[1..], &[]); + run(safe_tool, &args[1..], &[]); } else { - run("cargo", &["build"].map(String::from), &[("RUSTC", &cargo_tag_std), ("WRAPPER", "1")]); + run("cargo", &["build"].map(String::from), &[("RUSTC", cargo_safe_tool), ("WRAPPER", "1")]); } } @@ -35,6 +35,6 @@ fn run(cmd: &str, args: &[String], vars: &[(&str, &str)]) { .wait() .unwrap(); if !status.success() { - eprintln!("[error] {cmd}: args={args:?} vars={vars:?}"); + eprintln!("[error] {cmd}: args={args:#?} vars={vars:?}"); } } diff --git a/demo/rustc_driver/src/main.rs b/demo/rustc_driver/src/main.rs index 85d83e6..2320e98 100644 --- a/demo/rustc_driver/src/main.rs +++ b/demo/rustc_driver/src/main.rs @@ -14,8 +14,12 @@ use rustc_data_structures::fx::FxHashSet; use rustc_hir::Attribute; use rustc_middle::ty::TyCtxt; use stable_mir::{ - CrateDef, CrateItem, ItemKind, - mir::{MirVisitor, mono::Instance, visit::Location}, + CrateDef, ItemKind, + mir::{ + MirVisitor, + mono::{Instance, InstanceKind}, + visit::Location, + }, rustc_internal::internal, ty::Ty, }; @@ -28,9 +32,6 @@ fn main() { }); } -const REGISTER_TOOL_ATTR: &str = "#[safety"; -const REGISTER_TOOL: &str = "safety"; - fn analyze(tcx: TyCtxt) { let mut reachability = Reachability::default(); let local_items = stable_mir::all_local_items(); @@ -39,8 +40,8 @@ fn analyze(tcx: TyCtxt) { for fun in functions { let instance = match Instance::try_from(*fun) { Ok(instance) => instance, - Err(err) => { - eprintln!("Failed to get the instance for {fun:?}:\n{err:?}"); + Err(_) => { + // eprintln!("Failed to get the instance for {fun:?}:\n{err:?}"); continue; } }; @@ -65,7 +66,10 @@ struct Reachability { impl Reachability { fn add_instance(&mut self, instance: Instance) { - if self.instances.insert(instance) { + if self.instances.insert(instance) + && instance.has_body() + && matches!(instance.kind, InstanceKind::Item) + { // recurse if this is the first time of insertion if let Some(body) = instance.body() { self.visit_body(&body); @@ -75,18 +79,14 @@ impl Reachability { fn print_tag_std_attrs(&self, tcx: TyCtxt) { for instance in &self.instances { - // Only user defined instances can be converted. - match CrateItem::try_from(*instance) { - Ok(item) => print_tag_std_attrs(instance, item), - Err(_) => { - // Resort to internal API for unsupported StableMir conversions. - print_tag_std_attrs_through_internal_apis(tcx, instance); - } - } + // Resort to internal API for all attrs, rather than tool attrs. + print_tag_std_attrs_through_internal_apis(tcx, instance); } } } +const REGISTER_TOOL: &str = "Safety"; + fn print_tag_std_attrs_through_internal_apis(tcx: TyCtxt<'_>, instance: &Instance) { let def_id = internal(tcx, instance.def.def_id()); let tool_attrs = tcx.get_all_attrs(def_id).filter(|attr| { @@ -99,7 +99,7 @@ fn print_tag_std_attrs_through_internal_apis(tcx: TyCtxt<'_>, instance: &Instanc }); for attr in tool_attrs { println!( - "(internal api) {fn_name:?} ({span:?}) => {attr:?}\n", + "{fn_name:?} ({span:?})\n => {attr:?}\n", fn_name = instance.name(), span = instance.def.span().diagnostic(), attr = rustc_hir_pretty::attribute_to_string(&tcx, attr) @@ -107,21 +107,6 @@ fn print_tag_std_attrs_through_internal_apis(tcx: TyCtxt<'_>, instance: &Instanc } } -fn print_tag_std_attrs(instance: &Instance, item: CrateItem) { - let tool_attrs = item - .all_tool_attrs() - .into_iter() - .filter(|attr| attr.as_str().starts_with(REGISTER_TOOL_ATTR)); - for tag_attr in tool_attrs { - println!( - "(stable mir) {fn_name:?} ({span:?}) => {attr:?}\n", - fn_name = instance.name(), - span = instance.def.span().diagnostic(), - attr = tag_attr.as_str() - ); - } -} - impl MirVisitor for Reachability { fn visit_ty(&mut self, ty: &Ty, _: Location) { if let Some((fn_def, args)) = ty.kind().fn_def() { diff --git a/demo/rustc_driver/tests/basic/Cargo.toml b/demo/rustc_driver/tests/basic/Cargo.toml index fdd32e9..60b4415 100644 --- a/demo/rustc_driver/tests/basic/Cargo.toml +++ b/demo/rustc_driver/tests/basic/Cargo.toml @@ -4,3 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] +safety-tool-lib = { path = "../../safety-tool-lib" } diff --git a/demo/rustc_driver/tests/basic/macro-expanded/cargo-safe-tool.txt b/demo/rustc_driver/tests/basic/macro-expanded/cargo-safe-tool.txt new file mode 100644 index 0000000..3962bcc --- /dev/null +++ b/demo/rustc_driver/tests/basic/macro-expanded/cargo-safe-tool.txt @@ -0,0 +1,37 @@ +********* "unicode_ident" [Rlib] has reached 12 instances ********* +********* "build_script_build" [Executable] has reached 156 instances ********* +********* "proc_macro2" [Rlib] has reached 1106 instances ********* +********* "quote" [Rlib] has reached 505 instances ********* +********* "syn" [Rlib] has reached 6924 instances ********* +********* "safety_tool_parser" [Rlib] has reached 707 instances ********* +********* "safety_tool_macro" [ProcMacro] has reached 35 instances ********* +********* "safety_tool_lib" [Rlib] has reached 0 instances ********* +********* "demo" [Rlib] has reached 8 instances ********* +"test" ("src/lib.rs:12:1: 12:26") + => "#[Safety::inner(UnReachable, kind = \"precond\", memo =\n\"It's undefined behavior to reach code marked with this intrinsic function.\")]\n" + +"MyStruct::get" ("src/lib.rs:40:5: 40:42") + => "#[Safety::inner(Init(self.ptr, u8, self.len), kind = \"precond\", memo =\n\"The ptr must be initialized first!\")]\n" + +"MyStruct::get" ("src/lib.rs:40:5: 40:42") + => "#[Safety::inner(InBound(self.ptr, u8, self.len), kind = \"precond\", memo =\n\"The ptr must be within the length.\")]\n" + +"MyStruct::get" ("src/lib.rs:40:5: 40:42") + => "#[Safety::inner(ValidNum(self.len * sizeof(u8), [0, isize :: MAX]), kind =\n\"precond\", memo =\n\"Slice length can't exceed isize::MAX due to allocation limit.\")]\n" + +"MyStruct::get" ("src/lib.rs:40:5: 40:42") + => "#[Safety::inner(Alias(self.ptr), kind = \"hazard\", memo =\n\"Make sure don't alias the ptr.\")]\n" + +********* "demo" [Executable] has reached 18 instances ********* +"demo::MyStruct::get" ("/home/gh-zjp-CN/tag-std/demo/rustc_driver/tests/basic/src/lib.rs:40:5: 40:42") + => "#[Safety::inner(Init(self.ptr, u8, self.len), kind = \"precond\", memo =\n\"The ptr must be initialized first!\")]\n" + +"demo::MyStruct::get" ("/home/gh-zjp-CN/tag-std/demo/rustc_driver/tests/basic/src/lib.rs:40:5: 40:42") + => "#[Safety::inner(InBound(self.ptr, u8, self.len), kind = \"precond\", memo =\n\"The ptr must be within the length.\")]\n" + +"demo::MyStruct::get" ("/home/gh-zjp-CN/tag-std/demo/rustc_driver/tests/basic/src/lib.rs:40:5: 40:42") + => "#[Safety::inner(ValidNum(self.len * sizeof(u8), [0, isize :: MAX]), kind =\n\"precond\", memo =\n\"Slice length can't exceed isize::MAX due to allocation limit.\")]\n" + +"demo::MyStruct::get" ("/home/gh-zjp-CN/tag-std/demo/rustc_driver/tests/basic/src/lib.rs:40:5: 40:42") + => "#[Safety::inner(Alias(self.ptr), kind = \"hazard\", memo =\n\"Make sure don't alias the ptr.\")]\n" + diff --git a/demo/rustc_driver/tests/basic/macro-expanded/lib.rs b/demo/rustc_driver/tests/basic/macro-expanded/lib.rs new file mode 100644 index 0000000..aa03690 --- /dev/null +++ b/demo/rustc_driver/tests/basic/macro-expanded/lib.rs @@ -0,0 +1,55 @@ +#![feature(prelude_import)] +#![feature(register_tool)] +#![register_tool(Safety)] +#![allow(clippy::missing_safety_doc, clippy::mut_from_ref, internal_features)] +#![feature(core_intrinsics)] +#[prelude_import] +use std::prelude::rust_2024::*; +#[macro_use] +extern crate std; +use safety_tool_lib::safety; +/// It's undefined behavior to reach code marked with this intrinsic function. +#[Safety::inner( + UnReachable, + kind = "precond", + memo = "It's undefined behavior to reach code marked with this intrinsic function." +)] +pub unsafe fn test() -> ! { + unsafe { std::intrinsics::unreachable() } +} +pub struct MyStruct { + ptr: *mut u8, + len: usize, +} +impl MyStruct { + pub fn from(p: *mut u8, l: usize) -> MyStruct { + MyStruct { ptr: p, len: l } + } + /// The ptr must be initialized first! + #[Safety::inner( + Init(self.ptr, u8, self.len), + kind = "precond", + memo = "The ptr must be initialized first!" + )] + /// The ptr must be within the length. + #[Safety::inner( + InBound(self.ptr, u8, self.len), + kind = "precond", + memo = "The ptr must be within the length." + )] + /// Slice length can't exceed isize::MAX due to allocation limit. + #[Safety::inner( + ValidNum(self.len*sizeof(u8), [0, isize::MAX]), + kind = "precond", + memo = "Slice length can't exceed isize::MAX due to allocation limit." + )] + /// Make sure don't alias the ptr. + #[Safety::inner( + Alias(self.ptr), + kind = "hazard", + memo = "Make sure don't alias the ptr." + )] + pub unsafe fn get(&self) -> &mut [u8] { + unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) } + } +} diff --git a/demo/rustc_driver/tests/basic/src/lib.rs b/demo/rustc_driver/tests/basic/src/lib.rs index 23b9b8a..7b9fee2 100644 --- a/demo/rustc_driver/tests/basic/src/lib.rs +++ b/demo/rustc_driver/tests/basic/src/lib.rs @@ -1,9 +1,16 @@ #![feature(register_tool)] -#![register_tool(safety)] +#![register_tool(Safety)] +#![allow(clippy::missing_safety_doc, clippy::mut_from_ref, internal_features)] +#![feature(core_intrinsics)] -#[safety::precond(!Reachable())] -pub unsafe fn test() { - println!("unreachable!"); +use safety_tool_lib::safety; + +#[safety::precond( + UnReachable, + memo = "It's undefined behavior to reach code marked with this intrinsic function." +)] +pub unsafe fn test() -> ! { + unsafe { std::intrinsics::unreachable() } } pub struct MyStruct { @@ -14,10 +21,22 @@ impl MyStruct { pub fn from(p: *mut u8, l: usize) -> MyStruct { MyStruct { ptr: p, len: l } } - #[safety::precond::Init(self.ptr, u8, self.len)] - #[safety::precond::InBound(self.ptr, u8, self.len)] - #[safety::precond::ValidNum(self.len*sizeof(u8), [0,isize::MAX])] - #[safety::hazard::Alias(self.ptr)] + #[safety::precond( + Init(self.ptr, u8, self.len), + memo = "The ptr must be initialized first!" + )] + #[safety::precond( + InBound(self.ptr, u8, self.len), + memo = "The ptr must be within the length." + )] + #[safety::precond( + ValidNum(self.len*sizeof(u8), [0,isize::MAX]), + memo = "Slice length can't exceed isize::MAX due to allocation limit." + )] + #[safety::hazard( + Alias(self.ptr), + memo = "Make sure don't alias the ptr." + )] pub unsafe fn get(&self) -> &mut [u8] { unsafe { std::slice::from_raw_parts_mut(self.ptr, self.len) } }