Skip to content

Commit 21e5431

Browse files
committed
Auto Generate Safety Docs from Tags; multiple attrs on the same node
1 parent c73221b commit 21e5431

File tree

1 file changed

+80
-49
lines changed

1 file changed

+80
-49
lines changed

text/0000-safety-tags.md

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,54 @@ against [Semantic Versioning][semver].
279279

280280
[semver]: https://doc.rust-lang.org/cargo/reference/semver.html
281281

282+
NOTE:
283+
* `requires` or `checked` can be specified multiple times, and they will be merged together.
284+
* Duplicate tags in `requires` will trigger errors.
285+
* Duplicate tags in `checked` will trigger warning-by-default diagnostics.
286+
* the scope of a tag is limited to the defined unsafe function, so identical tag name on different
287+
unsafe functions won't affect with each other.
288+
289+
## Auto Generate Safety Docs from Tags
290+
291+
Since tag definitions duplicate safety comments, we propose `rustdoc` can recognize
292+
`#[safety::requires]` attributes and render them into safety docs.
293+
294+
For `ptr::read`, the existing comments are replaced with safety tags:
295+
296+
```rust
297+
/// # Safety
298+
/// Behavior is undefined if any of the following conditions are violated:
299+
/// * `src` must be [valid] for reads.
300+
/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the case.
301+
/// * `src` must point to a properly initialized value of type `T`.
302+
/// # Examples
303+
pub const unsafe fn read<T>(src: *const T) -> T { ... }
304+
```
305+
306+
```rust
307+
/// # Safety
308+
/// Behavior is undefined if any of the following conditions are violated:
309+
#[safety::requires {
310+
ValidPtr = "`src` must be [valid] for reads";
311+
Aligned = "`src` must be properly aligned. Use [`read_unaligned`] if this is not the case";
312+
Initialized = "`src` must point to a properly initialized value of type `T`"
313+
}]
314+
/// # Examples
315+
pub const unsafe fn read<T>(src: *const T) -> T { ... }
316+
```
317+
318+
Each `Tag = "desc"` item is rendered as `` `Tag`: desc `` list item.
319+
320+
```rust
321+
/// # Safety
322+
/// Behavior is undefined if any of the following conditions are violated:
323+
/// * `ValidPtr`: `src` must be [valid] for reads.
324+
/// * `Aligned`: `src` must be properly aligned. Use [`read_unaligned`] if this is not the case.
325+
/// * `Initialized`: `src` must point to a properly initialized value of type `T`.
326+
/// # Examples
327+
pub const unsafe fn read<T>(src: *const T) -> T { ... }
328+
```
329+
282330
# Reference-level explanation
283331
[reference-level-explanation]: #reference-level-explanation
284332

@@ -300,6 +348,7 @@ their call sites. To enable experimentation, a nightly-only library feature
300348
Procedure:
301349

302350
1. Validate `#[safety::requires]` only appears on unsafe functions if the attribute exists.
351+
- Merge tags in multiple `requires` on the same function. Emit error if tag name duplicates.
303352
2. Validate `#[safety::checked]` on HIR nodes whose `ExprKind` is one of
304353
- **direct unsafe nodes**: `Call`, `MethodCall` that invoke an unsafe function/method, or
305354
- **indirect unsafe nodes**: `Block` (unsafe), `Let`, `Assign`, `AssignOp`.
@@ -312,8 +361,10 @@ Procedure:
312361
3. Any node that carries `#[safety::checked]` must contain **exactly one** unsafe call/method;
313362
otherwise emit a diagnostic. *(We intentionally stop at this simple rule; splitting complex
314363
unsafe expressions into separate annotated nodes is considered good style.)*
315-
4. Make sure tags in `#[safety::checked]` correspond to their definitions.
316-
5. Diagnostics are emitted at the current Clippy lint level (warning or error).
364+
4. Merge tags in multiple `checked` on the same node. Emit a diagnostic if tag name duplicates.
365+
5. Make sure checked tags correspond to their definitions. Emit a diagnostic if the tag doesn't
366+
have a definition on the call.
367+
6. Diagnostics are emitted at the current Clippy lint level (warning or error).
317368

318369
[HIR ExprKind]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_hir/hir/enum.ExprKind.html
319370

@@ -328,6 +379,26 @@ documentation hover in `#[safety::checked]` as per tag definitions on unsafe cal
328379
Maybe some logics on safety tags like collecting tag definitions need to be extracted to a shared
329380
crate for both Clippy and Rust-Analyzer to use.
330381

382+
## Implementation in Rustdoc
383+
384+
Treat `#[safety::requires]` tool attributes on unsafe functions as `#[doc]` attributes, and extract
385+
tag names and definitions to render as item list:
386+
387+
```rust
388+
#[safety::requires(Tag1 = "definition1")]
389+
#[safety::requires(Tag2 = "definition2")]
390+
```
391+
392+
will be rendered if in markdown syntax
393+
394+
```md
395+
* `Tag1`: definition1
396+
* `Tag2`: definition2
397+
```
398+
399+
It'd be good if tag names have a special css class like background color to be attractive. Tag
400+
styling is not required in this RFC, and can be implemented later as an improvement.
401+
331402
# Drawbacks
332403
[drawbacks]: #drawbacks
333404

@@ -358,12 +429,17 @@ to implement them.
358429
be tied to internal APIs and specific toolchains. Extending Rust-Analyzer is therefore the only
359430
practical way to give users first-class IDE support.
360431

432+
4. Avoid safety comment duplication. Tag definitions and safety requirements share identical prose,
433+
so we only need one way to render them. Generating safety docs through safety tags prevents
434+
verbosity and inconsistency.
435+
361436
We therefore seek approvals from the following teams:
362437

363438
1. **Library team** – to allow the tagging of unsafe operations and to expose tag items as public
364439
APIs.
365-
2. **Clippy team** – to integrate tag checking into the linter.
366-
3. **Rust-Analyzer team** – to add IDE support for tags.
440+
2. **Clippy team** – to integrate tag checking into the linter.
441+
3. **Rust-Analyzer team** – to add IDE support for tags.
442+
3. **Rustdoc team** – to render tags to docs.
367443
4. **Compiler team** – to reserve the `safety` namespace and gate the feature via
368444
`#![feature(safety_tags)]` for the namespace and tag APIs in standard libraries.
369445

@@ -521,51 +597,6 @@ struct, enum, or union is neither needed nor permitted.
521597
# Future possibilities
522598
[future-possibilities]: #future-possibilities
523599

524-
## Better Rustdoc Rendering
525-
526-
Because tags are surfaced as a part of API, rustdoc can render documentation of tags by displaying
527-
each tag name, its optional description, and possible deprecated state below the tagged function or
528-
other unsafe item.
529-
530-
## Generate Safety Docs from Tags
531-
532-
We can take structured safety comments one step further by turning the explanatory prose into
533-
explicit tag reasons.
534-
535-
For `ptr::read`, the existing comments are replaced with safety tags:
536-
537-
```rust
538-
/// * `src` must be [valid] for reads.
539-
/// * `src` must be properly aligned. Use [`read_unaligned`] if this is not the case.
540-
/// * `src` must point to a properly initialized value of type `T`.
541-
pub const unsafe fn read<T>(src: *const T) -> T { ... }
542-
```
543-
544-
```rust
545-
#[safety {
546-
ValidPtr = "`src` must be [valid] for reads";
547-
Aligned = "`src` must be properly aligned. Use [`read_unaligned`] if this is not the case";
548-
Initialized = "`src` must point to a properly initialized value of type `T`"
549-
}]
550-
pub const unsafe fn read<T>(src: *const T) -> T { ... }
551-
```
552-
553-
`#[safety]` becomes a procedural macro that expands to both `#[doc]` attributes and the
554-
`#[safety::requires]` attribute.
555-
556-
```rust
557-
/// # Safety
558-
///
559-
/// - ValidPtr: `src` must be [valid] for reads
560-
/// - Aligned: `src` must be properly aligned. Use [`read_unaligned`] if this is not the case
561-
/// - Initialized: `src` must point to a properly initialized value of type `T`
562-
#[safety::requires { ValidPtr = "...", Aligned = "...", Initialized = "..." }]
563-
pub const unsafe fn read<T>(src: *const T) -> T { ... }
564-
```
565-
566-
With support for tag arguments, safety documentation can be made more precise and contextual by
567-
dynamically injecting the argument values into the reason strings.
568-
569600
## Discharge One Tag from `any = { Option1, Option2 }`
570601

571602
Sometimes it’s useful to declare a set of safety tags on an unsafe function while discharging only

0 commit comments

Comments
 (0)