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

fix suggestion in option_map_or_none #7971

Merged
merged 11 commits into from
Nov 17, 2021

Conversation

togami2864
Copy link
Contributor

fix: #7960
changelog: change suggestion in the lint rule option_map_or_none

@rust-highfive
Copy link

r? @llogiq

(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 Nov 13, 2021
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.

I think this introduces a change of behavior. Please add a test case with the type of the operation given, e.g. change let _ = .. to let _: Option<i32> or some such.

@@ -7,10 +7,10 @@ fn main() {

// Check `OPTION_MAP_OR_NONE`.
// Single line case.
let _ = opt.and_then(|x| Some(x + 1));
let _ = opt.map(|x| Some(x + 1));
Copy link
Contributor

Choose a reason for hiding this comment

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

I think you should also remove the Some( ), otherwise you'll change the type from Option<_> to Option<Option<_>>.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Is it correct that I should somehow remove Some ( ) in this line?

let func_snippet = snippet(cx, map_arg.span, "..");

Copy link
Contributor

Choose a reason for hiding this comment

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

Exactly. The problem here is that while this should usually be Some(_) which is an ExprCall(..) with a single item in args, it could also be any other expression that evaluates to an Option, e.g. { let foo = Some(bar); foo } or function_returning_option().

The right way to do this is to check if this is a ExprKind::Call to Some(_), then remove the call and use the zeroeth argument with the map, or using the original expression with and_then otherwise.

Copy link
Contributor Author

@togami2864 togami2864 Nov 15, 2021

Choose a reason for hiding this comment

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

Now, I don't understand how to identify whether Some is in the code or not.

What I did

case1:

I tried to identify it by match with expr.kind and write like this.

if_chain! {
                if let hir::ExprKind::MethodCall(path, _, _,_ ) = &expr.kind;
                if let hir::ExprKind::Path(ref qpath) = path[0].kind;
                 .....
            }

But I couldn't get the path of Some( ) in Closure which is expected as the oneth arg in map_or.

case2:

I tried to identify it by match with map_arg.

map_arg: &'tcx hir::Expr<'_>,

However, the type of map_arg differs depending on the code you lint. (in this case ExprKind::Closure or ExprKind::Path.)

In both cases, I couldn't specify Some() in code.
Would you have any advice? Due to my limited understanding, it is possible that my approach is fundamentally wrong.

Copy link
Contributor

Choose a reason for hiding this comment

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

First, this is not an ExprKind::MethodCall, but an ExprKind::Call. The rest has been done before, e.g. in clippy_lints/src/methods/chars_cmp.rs#L22-26.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks.
I tried to output expr.kind, but it seems to be judged as ExprKind::MethodCall🤔

println!({:?}, expr.kind)

Result

MethodCall(PathSegment { ident: map_or#0, hir_id: Some(HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 26 }), res: Some(Err), args: None, infer_args: true }, tests/ui/option_map_or_none.rs:13:30: 13:36 (#0), [Expr { hir_id: HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 28 }, kind: Path(Resolved(None, Path { span: tests/ui/option_map_or_none.rs:13:26: 13:29 (#0), res: Local(HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 7 }), segments: [PathSegment { ident: opt#0, hir_id: Some(HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 27 }), res: Some(Local(HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 7 })), args: None, infer_args: true }] })), span: tests/ui/option_map_or_none.rs:13:26: 13:29 (#0) }, Expr { hir_id: HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 30 }, kind: Path(Resolved(None, Path { span: tests/ui/option_map_or_none.rs:13:37: 13:41 (#0), res: Def(Ctor(Variant, Const), DefId(2:36962 ~ core[489a]::option::Option::None::{constructor#0})), segments: [PathSegment { ident: None#0, hir_id: Some(HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 29 }), res: Some(Err), args: None, infer_args: true }] })), span: tests/ui/option_map_or_none.rs:13:37: 13:41 (#0) }, Expr { hir_id: HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 41 }, kind: Closure(Ref, FnDecl { inputs: [Ty { hir_id: HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 40 }, kind: Infer, span: tests/ui/option_map_or_none.rs:13:44: 13:45 (#0) }], output: DefaultReturn(tests/ui/option_map_or_none.rs:13:47: 13:47 (#0)), c_variadic: false, implicit_self: None }, BodyId { hir_id: HirId { owner: DefId(0:3 ~ option_map_or_none[aa57]::main), local_id: 39 } }, tests/ui/option_map_or_none.rs:13:43: 13:46 (#0), None), span: tests/ui/option_map_or_none.rs:13:43: 13:58 (#0) }], tests/ui/option_map_or_none.rs:13:30: 13:59 (#0))

Copy link
Contributor

Choose a reason for hiding this comment

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

I wasn't talking about the .map_or(..) call expr, but the 2nd argument, which is a closure of which you need to get the Body first. Sorry I forgot about that.

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 is already looking quite good. I only found one final problem with blocks in closures, then it should be ready to merge.

clippy_lints/src/methods/option_map_or_none.rs Outdated Show resolved Hide resolved
@llogiq
Copy link
Contributor

llogiq commented Nov 17, 2021

Great! I think this is ready for merging now. Thank you for your patience in seeing this through.

@bors r+

@bors
Copy link
Collaborator

bors commented Nov 17, 2021

📌 Commit 8e317f5 has been approved by llogiq

@bors
Copy link
Collaborator

bors commented Nov 17, 2021

⌛ Testing commit 8e317f5 with merge 6ac42fe...

@bors
Copy link
Collaborator

bors commented Nov 17, 2021

☀️ Test successful - checks-action_dev_test, checks-action_remark_test, checks-action_test
Approved by: llogiq
Pushing 6ac42fe to master...

@bors bors merged commit 6ac42fe into rust-lang:master Nov 17, 2021
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.

The suggested code by option_map_or_none is warned again in some case
4 participants