diff --git a/Cargo.lock b/Cargo.lock index 217888d..07f0f0b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -205,9 +205,9 @@ checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "hashbrown" -version = "0.15.5" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" [[package]] name = "heck" @@ -217,9 +217,9 @@ checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "indexmap" -version = "2.11.0" +version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2481980430f9f78649238835720ddccc57e52df14ffce1c6f37391d61b563e9" +checksum = "6717a8d2a5a929a1a2eb43a12812498ed141a0bcfb7e8f7844fbdbe4303bba9f" dependencies = [ "equivalent", "hashbrown", @@ -499,6 +499,15 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_spanned" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e24345aa0fe688594e73770a5f6d1b216508b4f93484c0026d521acd30134392" +dependencies = [ + "serde_core", +] + [[package]] name = "similar" version = "2.7.0" @@ -525,6 +534,7 @@ dependencies = [ "rustc_utils", "serde", "sniff-test-attrs", + "toml 0.9.8", ] [[package]] @@ -581,11 +591,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" dependencies = [ "serde", - "serde_spanned", - "toml_datetime", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", "toml_edit", ] +[[package]] +name = "toml" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0dc8b1fb61449e27716ec0e1bdf0f6b8f3e8f6b05391e8497b8b6d7804ea6d8" +dependencies = [ + "indexmap", + "serde_core", + "serde_spanned 1.0.3", + "toml_datetime 0.7.3", + "toml_parser", + "toml_writer", + "winnow 0.7.13", +] + [[package]] name = "toml_datetime" version = "0.6.11" @@ -595,6 +620,15 @@ dependencies = [ "serde", ] +[[package]] +name = "toml_datetime" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" @@ -603,11 +637,26 @@ checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap", "serde", - "serde_spanned", - "toml_datetime", - "winnow", + "serde_spanned 0.6.9", + "toml_datetime 0.6.11", + "winnow 0.5.40", +] + +[[package]] +name = "toml_parser" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +dependencies = [ + "winnow 0.7.13", ] +[[package]] +name = "toml_writer" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df8b2b54733674ad286d16267dcfc7a71ed5c776e4ac7aa3c3e2561f7c637bf2" + [[package]] name = "unicode-ident" version = "1.0.18" @@ -800,3 +849,9 @@ checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" dependencies = [ "memchr", ] + +[[package]] +name = "winnow" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21a0236b59786fed61e2a80582dd500fe61f18b5dca67a4a067d0bc9039339cf" diff --git a/crates/sniff-test/Cargo.toml b/crates/sniff-test/Cargo.toml index dfe9a24..f603ee1 100644 --- a/crates/sniff-test/Cargo.toml +++ b/crates/sniff-test/Cargo.toml @@ -16,6 +16,7 @@ serde = { version = "1", features = ["derive"] } regex = "1.11.2" anyhow = "1.0.100" itertools = "0.14.0" +toml = "0.9.8" log = "0.4.28" [build-dependencies] diff --git a/crates/sniff-test/src/annotations/mod.rs b/crates/sniff-test/src/annotations/mod.rs index 57ad0a5..e351667 100644 --- a/crates/sniff-test/src/annotations/mod.rs +++ b/crates/sniff-test/src/annotations/mod.rs @@ -1,6 +1,9 @@ //! The utilities needed to find and parse code annotations. use crate::{ - annotations::doc::{Attributeable, DocStr, get_doc_str}, + annotations::{ + doc::{Attributeable, DocStr, get_doc_str}, + toml::TomlAnnotation, + }, properties::Property, }; use regex::Regex; @@ -18,6 +21,7 @@ use std::{any::TypeId, borrow::Borrow, collections::HashMap, fmt::Debug, hash::H mod doc; mod span; +pub mod toml; #[derive(Debug)] pub enum PropertyViolation { @@ -67,17 +71,22 @@ impl DefAnnotation { /// annotated. pub fn parse_fn_def( tcx: TyCtxt<'_>, + toml_annotation: &TomlAnnotation, fn_def: impl Into, property: P, ) -> Option { - // TODO: add yash's logic here for checking the override toml file first. - - // 1. get the doc string + // 1. Get the DefId let fn_def: rustc_span::def_id::DefId = fn_def.into(); - let doc_str = get_doc_str(fn_def, tcx)?; - // 2. parse the doc string based on the property - parse_fn_def_src(doc_str, property) + // 2. Check if we have a TOML override for this function + if let Some(toml_str) = toml_annotation.get_requirements_string(&tcx.def_path_str(fn_def)) { + parse_fn_def_toml(toml_str, property) + } else { + // 3. No TOML override found, get the doc string from source code + let doc_str = get_doc_str(fn_def, tcx)?; + // 4. parse the doc string based on the property + parse_fn_def_src(doc_str, property) + } } fn parent_block_expr<'tcx>( @@ -136,3 +145,15 @@ fn parse_fn_def_src(doc_str: DocStr, property: P) -> Option(toml_str: &str, property: P) -> Option { + property + .fn_def_regex() + .find(toml_str) + .map(|found| DefAnnotation { + property_name: P::property_name(), + local_violation_annotation: PropertyViolation::Unconditional, + text: toml_str[found.end()..].to_string(), + source: AnnotationSource::TomlOverride, + }) +} diff --git a/crates/sniff-test/src/annotations/toml.rs b/crates/sniff-test/src/annotations/toml.rs new file mode 100644 index 0000000..7007f8e --- /dev/null +++ b/crates/sniff-test/src/annotations/toml.rs @@ -0,0 +1,104 @@ +//! Module for parsing annotations from TOML files. +//! Each top-level TOML table key is a function name with a `requirements` string. +//! ```toml +//! [function_name] +//! requirements = """ +//! # Safety +//! * 'requirement 1': Description of requirement 1 +//! * 'requirement 2': Description of requirement 2 +//! """ +//! ``` + +use std::collections::HashMap; + +use rustc_span::{ + DUMMY_SP, + source_map::{Spanned, respan}, +}; + +use crate::annotations::DefAnnotation; + +/// Struct encapsulating annotations parsed from a TOML file. +#[derive(Default)] +pub struct TomlAnnotation { + function_to_requirements_string: HashMap, +} + +/// Errors that can occur when parsing TOML annotations. +#[derive(Debug)] +pub enum TomlParseError { + Io(std::io::Error), + Toml(toml::de::Error), + Schema(String), +} + +impl From for TomlParseError { + fn from(err: std::io::Error) -> Self { + TomlParseError::Io(err) + } +} + +impl From for TomlParseError { + fn from(err: toml::de::Error) -> Self { + TomlParseError::Toml(err) + } +} + +impl TomlAnnotation { + /// Parses a TOML annotation file and returns a [`TomlAnnotation`] struct. + /// Fails on any errors, never returning partial results. + /// If the file does not exist, returns an empty [`TomlAnnotation`]. + /// TODO: Use real spans if possible. + pub fn from_file>(path: P) -> Result { + // Get the contents of the TOML file + let text = match std::fs::read_to_string(path) { + Ok(text) => text, + Err(e) if e.kind() == std::io::ErrorKind::NotFound => { + // File does not exist, return empty annotations + return Ok(TomlAnnotation::default()); + } + Err(e) => return Err(TomlParseError::Io(e)), + }; + + // Parse the TOML file into a map from function names to requirement strings + let value: toml::Value = toml::from_str(&text)?; + let Some(table) = value.as_table() else { + return Err(TomlParseError::Schema( + "Expected a TOML table at the top level".to_string(), + )); + }; + + // Parse each function's requirements + let mut function_to_requirements_string: HashMap = HashMap::new(); + for (function_name, value) in table { + let Some(inner_table) = value.as_table() else { + return Err(TomlParseError::Schema(format!( + "Expected a TOML table for function {function_name}" + ))); + }; + let Some(requirements_value) = inner_table.get("requirements") else { + return Err(TomlParseError::Schema(format!( + "Expected a 'requirements' string for function {function_name}" + ))); + }; + let Some(requirements_string) = requirements_value.as_str() else { + return Err(TomlParseError::Schema(format!( + "Expected 'requirements' to be a string for function {function_name}" + ))); + }; + + function_to_requirements_string + .insert(function_name.clone(), requirements_string.to_string()); + } + + // Return the parsed annotations + Ok(TomlAnnotation { + function_to_requirements_string, + }) + } + + /// Retrieves the requirements for a given function name, if any. + pub fn get_requirements_string(&self, function_name: &str) -> Option<&String> { + self.function_to_requirements_string.get(function_name) + } +} diff --git a/crates/sniff-test/src/check/mod.rs b/crates/sniff-test/src/check/mod.rs index 32c950f..d307aa5 100644 --- a/crates/sniff-test/src/check/mod.rs +++ b/crates/sniff-test/src/check/mod.rs @@ -1,15 +1,21 @@ -use itertools::Itertools; -use rustc_errors::{Diag, DiagCtxtHandle}; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::TyCtxt; -use rustc_span::{ErrorGuaranteed, source_map::Spanned, sym::todo_macro}; - use crate::{ - annotations::{self, parse_expr}, + annotations::{ + self, parse_expr, + toml::{TomlAnnotation, TomlParseError}, + }, properties::{self, Axiom, FoundAxiom, Property}, reachability::{self, CallsWObligations, LocallyReachable}, utils::SniffTestDiagnostic, }; +use itertools::Itertools; +use rustc_errors::{Diag, DiagCtxtHandle}; +use rustc_hir::def_id::{DefId, LocalDefId}; +use rustc_middle::ty::TyCtxt; +use rustc_span::{ + DUMMY_SP, ErrorGuaranteed, + source_map::{Spanned, respan}, + sym::todo_macro, +}; mod err; mod expr; @@ -19,6 +25,20 @@ pub fn check_properly_annotated( tcx: TyCtxt, property: P, ) -> Result<(), ErrorGuaranteed> { + // Parse TOML annotations from file + let toml_path = "sniff-test.toml"; + let toml_annotations = match TomlAnnotation::from_file(toml_path) { + Ok(annotations) => annotations, + Err(e) => { + tcx.dcx() + .struct_warn(format!( + "Failed to parse TOML annotations from {toml_path}: {e:?}" + )) + .emit(); + TomlAnnotation::default() + } + }; + let entry = reachability::analysis_entry_points::

(tcx); // Debug print all our entries and where they are in the src @@ -40,7 +60,7 @@ pub fn check_properly_annotated( // For all reachable local function definitions, ensure their axioms align with their annotations. for func in reachable { - check_function_properties(tcx, func, property)?; + check_function_properties(tcx, &toml_annotations, func, property)?; } Ok(()) @@ -48,11 +68,12 @@ pub fn check_properly_annotated( fn check_function_properties( tcx: TyCtxt, + toml_annotations: &TomlAnnotation, func: LocallyReachable, property: P, ) -> Result<(), ErrorGuaranteed> { // Look for the local annotation - let annotation = annotations::parse_fn_def(tcx, func.reach, property); + let annotation = annotations::parse_fn_def(tcx, toml_annotations, func.reach, property); // If the function we're analyzing is directly annotated, we trust the user's annotation // and don't need to analyze its body locally. Vitally, we'll still explore functions it calls @@ -71,10 +92,11 @@ fn check_function_properties( log::debug!("fn {:?} has obligations {:?}", func.reach, annotation); // Find all calls that have obligations. - let unjustified_calls = reachability::find_calls_w_obligations(tcx, &func, property) - // Filter those with only callsites that haven't been justified. - .filter_map(only_unjustified_callsites(tcx, func.reach, property)) - .collect::>(); + let unjustified_calls = + reachability::find_calls_w_obligations(tcx, toml_annotations, &func, property) + // Filter those with only callsites that haven't been justified. + .filter_map(only_unjustified_callsites(tcx, func.reach, property)) + .collect::>(); // If we have obligations, we've dismissed them diff --git a/crates/sniff-test/src/reachability/calls.rs b/crates/sniff-test/src/reachability/calls.rs index 2e27890..a1c7f21 100644 --- a/crates/sniff-test/src/reachability/calls.rs +++ b/crates/sniff-test/src/reachability/calls.rs @@ -1,6 +1,6 @@ //! Finds the 'bad' functions that should be annotated -use crate::annotations::{self, DefAnnotation, parse_fn_def}; +use crate::annotations::{self, DefAnnotation, parse_fn_def, toml::TomlAnnotation}; use crate::properties::Property; use crate::reachability::LocallyReachable; use std::collections::HashMap; @@ -23,11 +23,11 @@ pub struct CallsWObligations { fn call_has_obligations( tcx: TyCtxt, + toml_annotations: &TomlAnnotation, property: P, ) -> impl Fn((&DefId, &Vec)) -> Option { move |(to_def_id, from_spans)| { - let annotation = parse_fn_def(tcx, *to_def_id, property)?; - + let annotation = parse_fn_def(tcx, toml_annotations, *to_def_id, property)?; if annotation.creates_obligation() { Some(CallsWObligations { call_to: *to_def_id, @@ -42,11 +42,12 @@ fn call_has_obligations( pub fn find_calls_w_obligations( tcx: TyCtxt, + toml_annotations: &TomlAnnotation, locally_reachable: &LocallyReachable, property: P, ) -> impl Iterator { locally_reachable .calls_to .iter() - .filter_map(call_has_obligations(tcx, property)) + .filter_map(call_has_obligations(tcx, toml_annotations, property)) } diff --git a/tests/toml/fail_external/Cargo.lock b/tests/toml/fail_external/Cargo.lock new file mode 100644 index 0000000..9a2d460 --- /dev/null +++ b/tests/toml/fail_external/Cargo.lock @@ -0,0 +1,41 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "invalid_simple" +version = "0.1.0" +dependencies = [ + "sniff-test-attrs", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "sniff-test-attrs" +version = "0.1.0" +dependencies = [ + "quote", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" diff --git a/tests/toml/fail_external/Cargo.toml b/tests/toml/fail_external/Cargo.toml new file mode 100644 index 0000000..6f3b543 --- /dev/null +++ b/tests/toml/fail_external/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "invalid_simple" +version = "0.1.0" +edition = "2024" + +[dependencies] +sniff-test-attrs = { path = "../../../crates/sniff-test-attrs" } + +[workspace] \ No newline at end of file diff --git a/tests/toml/fail_external/pass_simple.snap b/tests/toml/fail_external/pass_simple.snap new file mode 100644 index 0000000..685329e --- /dev/null +++ b/tests/toml/fail_external/pass_simple.snap @@ -0,0 +1,8 @@ +--- +source: tests/lib.rs +--- +exit_code = 0 +stdout = ''' +compilation successful!! +''' +stderr = '' diff --git a/tests/toml/fail_external/sniff-test.toml b/tests/toml/fail_external/sniff-test.toml new file mode 100644 index 0000000..212eb9e --- /dev/null +++ b/tests/toml/fail_external/sniff-test.toml @@ -0,0 +1,5 @@ +["std::thread::sleep"] +requirements = """ +# Safety +* 'non-blocking': Function must not be called in a routine which cannot block. +""" \ No newline at end of file diff --git a/tests/toml/fail_external/src/main.rs b/tests/toml/fail_external/src/main.rs new file mode 100644 index 0000000..6556825 --- /dev/null +++ b/tests/toml/fail_external/src/main.rs @@ -0,0 +1,7 @@ +use std::thread; +use std::time::Duration; + +#[sniff_test_attrs::check_unsafe] +fn main() { + thread::sleep(Duration::from_millis(100)); +} diff --git a/tests/toml/pass_external/Cargo.lock b/tests/toml/pass_external/Cargo.lock new file mode 100644 index 0000000..9a2d460 --- /dev/null +++ b/tests/toml/pass_external/Cargo.lock @@ -0,0 +1,41 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "invalid_simple" +version = "0.1.0" +dependencies = [ + "sniff-test-attrs", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "sniff-test-attrs" +version = "0.1.0" +dependencies = [ + "quote", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" diff --git a/tests/toml/pass_external/Cargo.toml b/tests/toml/pass_external/Cargo.toml new file mode 100644 index 0000000..6f3b543 --- /dev/null +++ b/tests/toml/pass_external/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "invalid_simple" +version = "0.1.0" +edition = "2024" + +[dependencies] +sniff-test-attrs = { path = "../../../crates/sniff-test-attrs" } + +[workspace] \ No newline at end of file diff --git a/tests/toml/pass_external/pass_simple.snap b/tests/toml/pass_external/pass_simple.snap new file mode 100644 index 0000000..685329e --- /dev/null +++ b/tests/toml/pass_external/pass_simple.snap @@ -0,0 +1,8 @@ +--- +source: tests/lib.rs +--- +exit_code = 0 +stdout = ''' +compilation successful!! +''' +stderr = '' diff --git a/tests/toml/pass_external/sniff-test.toml b/tests/toml/pass_external/sniff-test.toml new file mode 100644 index 0000000..212eb9e --- /dev/null +++ b/tests/toml/pass_external/sniff-test.toml @@ -0,0 +1,5 @@ +["std::thread::sleep"] +requirements = """ +# Safety +* 'non-blocking': Function must not be called in a routine which cannot block. +""" \ No newline at end of file diff --git a/tests/toml/pass_external/src/main.rs b/tests/toml/pass_external/src/main.rs new file mode 100644 index 0000000..b8815cd --- /dev/null +++ b/tests/toml/pass_external/src/main.rs @@ -0,0 +1,9 @@ +use std::thread; +use std::time::Duration; + +#[sniff_test_attrs::check_unsafe] +fn main() { + /// Safety: + /// - non-blocking: this function can block + thread::sleep(Duration::from_millis(100)); +} diff --git a/tests/toml/pass_local/Cargo.lock b/tests/toml/pass_local/Cargo.lock new file mode 100644 index 0000000..9a2d460 --- /dev/null +++ b/tests/toml/pass_local/Cargo.lock @@ -0,0 +1,41 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "invalid_simple" +version = "0.1.0" +dependencies = [ + "sniff-test-attrs", +] + +[[package]] +name = "proc-macro2" +version = "1.0.101" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89ae43fd86e4158d6db51ad8e2b80f313af9cc74f5c0e03ccb87de09998732de" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "sniff-test-attrs" +version = "0.1.0" +dependencies = [ + "quote", +] + +[[package]] +name = "unicode-ident" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63a545481291138910575129486daeaf8ac54aee4387fe7906919f7830c7d9d" diff --git a/tests/toml/pass_local/Cargo.toml b/tests/toml/pass_local/Cargo.toml new file mode 100644 index 0000000..6f3b543 --- /dev/null +++ b/tests/toml/pass_local/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "invalid_simple" +version = "0.1.0" +edition = "2024" + +[dependencies] +sniff-test-attrs = { path = "../../../crates/sniff-test-attrs" } + +[workspace] \ No newline at end of file diff --git a/tests/toml/pass_local/pass_simple.snap b/tests/toml/pass_local/pass_simple.snap new file mode 100644 index 0000000..685329e --- /dev/null +++ b/tests/toml/pass_local/pass_simple.snap @@ -0,0 +1,8 @@ +--- +source: tests/lib.rs +--- +exit_code = 0 +stdout = ''' +compilation successful!! +''' +stderr = '' diff --git a/tests/toml/pass_local/sniff-test.toml b/tests/toml/pass_local/sniff-test.toml new file mode 100644 index 0000000..c8498ee --- /dev/null +++ b/tests/toml/pass_local/sniff-test.toml @@ -0,0 +1,6 @@ +["foo"] +requirements = """ +# Unsafe +- non-null: ptr must be non-null +- aligned: ptr must be aligned for an i32 +""" \ No newline at end of file diff --git a/tests/toml/pass_local/src/main.rs b/tests/toml/pass_local/src/main.rs new file mode 100644 index 0000000..b047116 --- /dev/null +++ b/tests/toml/pass_local/src/main.rs @@ -0,0 +1,15 @@ +unsafe fn foo(ptr: *const i32) -> i32 { + unsafe { *ptr } +} + +#[sniff_test_attrs::check_unsafe] +fn main() { + let x = 1; + + unsafe { + /// Safety: + /// * non-null: a pointer that comes from a reference is trivially non-null + /// * aligned: a pointer that comes from a reference is trivially aligned + foo(&raw const x); + } +}