Skip to content

Add manual_as_slice lint #14809

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

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open

Conversation

nils-degroot
Copy link

@nils-degroot nils-degroot commented May 15, 2025

This pr adds the manual_as_slice lint.

closes: #7633


changelog: add [manual_as_slice] lint

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label May 15, 2025
Copy link
Contributor

@llogiq llogiq left a comment

Choose a reason for hiding this comment

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

This looks like a good starting point. I'd want some more test cases, and there is some duplication we can remove, but otherwise this seems mostly merge-worthy. I'd like to rename the lint though, it's too wordy. How about manual_as_slice? That fits very well into our naming structure. Also please squash your commits when ready.

Comment on lines 44 to 67
match mutability {
Mutability::Not => {
span_lint_and_sugg(
cx,
AS_SLICE_INSTEAD_OF_REFERENCE_FULL_RANGE,
expr.span,
"use `.as_slice()` instead of full range slice",
"try",
format!("{snippet}.as_slice()"),
app,
);
},
Mutability::Mut => {
span_lint_and_sugg(
cx,
AS_SLICE_INSTEAD_OF_REFERENCE_FULL_RANGE,
expr.span,
"use `.as_mut_slice()` instead of full range slice",
"try",
format!("{snippet}.as_mut_slice()"),
app,
);
},
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
match mutability {
Mutability::Not => {
span_lint_and_sugg(
cx,
AS_SLICE_INSTEAD_OF_REFERENCE_FULL_RANGE,
expr.span,
"use `.as_slice()` instead of full range slice",
"try",
format!("{snippet}.as_slice()"),
app,
);
},
Mutability::Mut => {
span_lint_and_sugg(
cx,
AS_SLICE_INSTEAD_OF_REFERENCE_FULL_RANGE,
expr.span,
"use `.as_mut_slice()` instead of full range slice",
"try",
format!("{snippet}.as_mut_slice()"),
app,
);
},
}
let (msg, sugg) = match mutability {
Mutability::Not => (
"use `.as_slice()` instead of full range slice",
format!("{snippet}.as_slice()"),
),
Mutability::Mut => (
"use `.as_mut_slice()` instead of full range slice",
format!("{snippet}.as_mut_slice()"),
),
};
span_lint_and_sugg(
cx,
AS_SLICE_INSTEAD_OF_REFERENCE_FULL_RANGE,
expr.span,
msg,
"try",
sugg,
app,
);

//~^ as_slice_instead_of_reference_full_range

let slice: &[u8] = &nested_1::nested_2::SOME_VALUE[..];
//~^ as_slice_instead_of_reference_full_range
Copy link
Contributor

Choose a reason for hiding this comment

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

I'd like to see some tests where the array and the slice come from different expansion contexts (e.g. a macro_rules! that does the slicing to an argument).

Also a test where we slice something that is already a slice instead of an array, e.g. let slice: &[u8] = b"foo"; &foo[..].

@rustbot rustbot added S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status) and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 16, 2025
@nils-degroot
Copy link
Author

I like manual_as_slice, it shows the intended behavior for the lint. I'll make the changes

@nils-degroot nils-degroot force-pushed the master branch 2 times, most recently from 1a8a066 to dc4e07b Compare May 16, 2025 13:51
@llogiq
Copy link
Contributor

llogiq commented May 16, 2025

I think you need to re-bless the tests.

@llogiq llogiq changed the title changelog: Add as_slice_instead_of_reference_full_range lint Add manual_as_slice lint May 16, 2025

macro_rules! perform_the_slice {
($a:expr) => {
[1, 2, 3].as_slice()
Copy link
Contributor

Choose a reason for hiding this comment

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

This is not OK. The lint has changed behavior. You can see this if you call perform_the_slice!([42]) after line 36.

A solution to this problem is to use a MultiSpan suggestion. To do that, the suggestion in the lint can be a Vec<(Span, String)>, so you can build a vec of two elements, the first being the span of the ampersand with an empty String, and the second being the span of the [..] with ".as_slice()". We should however be sure that both of those come from the same expansion context before making the suggestion, otherwise we may end up at the same problem.

Copy link
Contributor

@samueltardieu samueltardieu left a comment

Choose a reason for hiding this comment

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

You should at least check that:

  • the resulting type is a slice
  • the .as_slice()/.as_mut_slice() methods exist on the original type

or restrict the original type to those coming from core/alloc.

Otherwise, you take the risk of having a type implement [..] for something else (let's say, in a domain-specific-language) without having .as_slice() available. Here is an example:

#![feature(new_range_api)]
use std::ops::Index;
use std::range::RangeBounds;

struct Count;
impl<R: RangeBounds<()>> Index<R> for Count {
    type Output = ();
    fn index(&self, _: R) -> &Self::Output {
        &()
    }
}

fn main() {
    _ = &Count[..];
}

It will suggest to use Count.as_slice() even though this method does not exist on Count.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action from the author. (Use `@rustbot ready` to update this status)
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Suggest usage of array.as_slice() (instead of &array[..]) and array.as_mut_slice()
4 participants