Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

undocumented_unsafe_blocks does not trigger on unsafe trait impls #8761

Merged
merged 5 commits into from
May 16, 2022

Conversation

tamaroning
Copy link
Contributor

@tamaroning tamaroning commented Apr 29, 2022

Closes #8505

changelog: This lint checks unsafe impls NOT from macro expansions and checks ones in macro declarations.
unsafe impls from macro invocations don't trigger the lint for now.
This lint checks unsafe impls from/not from macro expansions

@rust-highfive
Copy link

r? @giraffate

(rust-highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Apr 29, 2022
@giraffate
Copy link
Contributor

Sorry, I can't take much time this week, but next week I can take time for a review.

@Jarcho Could you do a review on this?

item_and_spans.push((item, item.span));
}
// Sort items by start position
item_and_spans.sort_by_key(|e| e.1.lo());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Item's are already in declarations order, so there's no need to sort.

@@ -86,6 +87,65 @@ impl LateLintPass<'_> for UndocumentedUnsafeBlocks {
);
}
}

fn check_mod(&mut self, cx: &LateContext<'_>, module: &'_ hir::Mod<'_>, mod_span: Span, hir_id: hir::HirId) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be using check_item rather than check_mod. Items can appear within functions as well as modules. A combination of clippy_utils::get_parent_node and scanning for the id of the contained item can be used to get the span to use for the search bounds.

} else {
// unsafe impl S {} /* comment */ unsafe impl T {}
// ^-------------^
item_and_spans[idx - 1].1.between(*item_span)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will break if the previous item is from a macro expansion. You'll need to use rustc_span::hygiene::walk_chain(item_and_spans[idx - 1].1, SyntaxContext::root())

for (idx, (item, item_span)) in item_and_spans.iter().enumerate() {
if let hir::ItemKind::Impl(imple) = &item.kind
&& imple.unsafety == hir::Unsafety::Unsafe
&& !item_span.from_expansion()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional extension here to handle macro expansion from the same crate. See block_has_safety_comment for how that's done for unsafe blocks.

A call to is_unsafe_from_proc_macro is needed. You can change the function to take a Span rather than a Block.


if let Ok(start) = source_map.lookup_line(span_before_impl.lo())
&& let Ok(end) = source_map.lookup_line(span_before_impl.hi())
&& let Some(src) = start.sf.src.as_deref()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You'll need to add a check here that the file is the same for both line lookups. This is more a sanity check then anything. Two spans from the same SyntaxContext in the same module should be in the same file, but I can't say for certain that's the case.

&& !is_lint_allowed(cx, UNDOCUMENTED_UNSAFE_BLOCKS, hir_id)
{
// Checks if the lines immediately preceding the impl contain a safety comment.
let impl_has_safety_comment = {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor nit here. This block isn't needed. The first statement can be moved out, then the if assigned directly.

@bors
Copy link
Collaborator

bors commented May 5, 2022

☔ The latest upstream changes (presumably #8788) made this pull request unmergeable. Please resolve the merge conflicts.

@tamaroning
Copy link
Contributor Author

Thanks for your review @Jarcho
I fixed code.


/// Checks if the lines immediately preceding the item contain a safety comment.
fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
if span_from_macro_expansion_has_safety_comment(cx, item.span) || span_in_body_has_safety_comment(cx, item.span) {
Copy link
Contributor

@Jarcho Jarcho May 9, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be returning false if the item is within a function, but doesn't have a safety comment. e.g.

unsafe trait Foo {}
fn f() {
    Struct S;
    // no comment
    unsafe impl Foo for S {}
}

Not a particularly big deal on it's own, but it contributes to the ICE later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still duplicate work here. Easiest fix would be to remove the call to span_in_body_has_safety_comment as it's handled afterwards when matching on Node::Stmt(_).

let mut hi = false;
if let Node::Item(parent_item) = parent_node {
if let ItemKind::Mod(parent_mod) = &parent_item.kind {
for (idx, item_id) in parent_mod.item_ids.iter().enumerate() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be better to use Iterator::find_map here. the extraction of either Span::lo or Span::hi could be done here as well.

This should get rid of the need to have span_before_item and hi as mutable.

}
}
}
let span_before_item = span_before_item.unwrap();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can fail when the impl is inside a function and I think the crate root.

let span_before_item = span_before_item.unwrap();

let source_map = cx.sess().source_map();
if let Some(item_span) = walk_span_to_context(item.span, SyntaxContext::root())
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

walk_span_to_context isn't needed here. item.span is already at the root context.

@tamaroning
Copy link
Contributor Author

tamaroning commented May 10, 2022

Thanks for your review @Jarcho
I fixed all and added some test cases which contain impls in a function and ones in the crate root.

Copy link
Contributor

@Jarcho Jarcho left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just two more small things. I think it's good otherwise, but I'll take one more look at it later. Thanks for keeping up with the review.


/// Checks if the lines immediately preceding the item contain a safety comment.
fn item_has_safety_comment(cx: &LateContext<'_>, item: &hir::Item<'_>) -> bool {
if span_from_macro_expansion_has_safety_comment(cx, item.span) || span_in_body_has_safety_comment(cx, item.span) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's still duplicate work here. Easiest fix would be to remove the call to span_in_body_has_safety_comment as it's handled afterwards when matching on Node::Stmt(_).

if item.span.ctxt() == SyntaxContext::root() {
if let Some(parent_node) = get_parent_node(cx.tcx, item.hir_id()) {
let comment_start;
match parent_node {
Copy link
Contributor

@Jarcho Jarcho May 11, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Small nit. This could be let comment_start = match parent_node { The delayed initialization makes it seem like something complicated is coming up.

@tamaroning
Copy link
Contributor Author

Thanks! I removed the call to span_in_body_has_safety_comment, but I am not sure impls in a body
always has a Node::Stmt(..) as a parent node and a Node::Block(..) as a grandparent node.

@Jarcho
Copy link
Contributor

Jarcho commented May 13, 2022

The only way an item can appear in a function is through StmtKind::Item, and the only way a statement can appear is through a block.

@Jarcho
Copy link
Contributor

Jarcho commented May 15, 2022

Everything looks good now. Good job.

@giraffate

@tamaroning
Copy link
Contributor Author

Thank you so much! @Jarcho

@giraffate
Copy link
Contributor

@bors r=Jarcho

@tamaroning @Jarcho It looks good, thanks!

@bors
Copy link
Collaborator

bors commented May 15, 2022

📌 Commit 9e66f2d has been approved by Jarcho

bors added a commit that referenced this pull request May 15, 2022
`undocumented_unsafe_blocks` does not trigger on unsafe trait impls

Closes #8505

changelog:
~~`unsafe impl`s from macro invocations don't trigger the lint for now.~~
~~This lint checks unsafe impls from/not from macro expansions~~
This lint checks unsafe impls NOT from macro expansions and checks ones in macro declarations.
@bors
Copy link
Collaborator

bors commented May 15, 2022

⌛ Testing commit 9e66f2d with merge df43ccb...

@bors
Copy link
Collaborator

bors commented May 15, 2022

💔 Test failed - checks-action_test

@Jarcho
Copy link
Contributor

Jarcho commented May 16, 2022

You'll need to add changelog: ... on a single line in the PR body (first post).

@giraffate
Copy link
Contributor

@bors retry

@bors
Copy link
Collaborator

bors commented May 16, 2022

⌛ Testing commit 9e66f2d with merge a1632ff...

@bors
Copy link
Collaborator

bors commented May 16, 2022

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: Jarcho
Pushing a1632ff to master...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-review Status: Awaiting review from the assignee but also interested parties
Projects
None yet
Development

Successfully merging this pull request may close these issues.

undocumented_unsafe_blocks does not trigger on unsafe trait impls
5 participants