Skip to content

Add allow-unwrap-types configuration for unwrap_used and expect_used#16605

Merged
llogiq merged 1 commit intorust-lang:masterfrom
sengmonkham:allow_unwrap_types
Feb 21, 2026
Merged

Add allow-unwrap-types configuration for unwrap_used and expect_used#16605
llogiq merged 1 commit intorust-lang:masterfrom
sengmonkham:allow_unwrap_types

Conversation

@sengmonkham
Copy link
Copy Markdown
Contributor

@sengmonkham sengmonkham commented Feb 21, 2026

#16535

This PR introduces a new configuration option, allow-unwrap-types, which accepts a list of type paths (e.g., ["std::sync::LockResult"]). When configured, calling .unwrap() or .expect() on values of these types will no longer trigger the unwrap_used or expect_used lints.

This is particularly useful for reducing noisy lints on patterns where unwrapping is widely considered acceptable or expected by design, such as obtaining a std::sync::LockResult from Mutex::lock().

Changes Made
Introduced the allow_unwrap_types: Vec<String> field in clippy_config.
Updated the unwrap_expect_used lint to check the underlying evaluated expression ty against the configured allowed types.
Handled robust type alias resolution so that aliases like std::sync::LockResult accurately map to their underlying Adt types during lint evaluation.
Added UI tests in tests/ui-toml/unwrap_used_allowed/ to verify both allowed and disallowed cases.


changelog: [unwrap_used], [expect_used]: Add allow-unwrap-types configuration to allow unwrapping specified types.

@rustbot rustbot added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Feb 21, 2026
@rustbot
Copy link
Copy Markdown
Collaborator

rustbot commented Feb 21, 2026

r? @llogiq

rustbot has assigned @llogiq.
They will have a look at your PR within the next two weeks and either review your PR or reassign to another reviewer.

Use r? to explicitly pick a reviewer

Why was this reviewer chosen?

The reviewer was selected based on:

  • Owners of files modified in this PR: 7 candidates
  • 7 candidates expanded to 7 candidates
  • Random selection from Jarcho, dswij, llogiq, samueltardieu

Copy link
Copy Markdown
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.

The implementation looks good to me. Thank you!

View changes since this review

@llogiq llogiq added this pull request to the merge queue Feb 21, 2026
Merged via the queue into rust-lang:master with commit 8193734 Feb 21, 2026
16 of 17 checks passed
@rustbot rustbot removed the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties label Feb 21, 2026
@blyxyas
Copy link
Copy Markdown
Member

blyxyas commented Feb 25, 2026

We'll have to take another look into this, it's added 30 million instructions TwT (profiled in bumpalo) (that's a 6.2% perf. regression)

@sengmonkham Do you see any way to optimize this PR? From my profilling, it's mainly due to that ty.to_string() usage, and lookup_path_str usage.

@Jarcho
Copy link
Copy Markdown
Contributor

Jarcho commented Feb 26, 2026

This should be pre-resolving the types the same way the disallowed_* lints do.

@sengmonkham
Copy link
Copy Markdown
Contributor Author

We'll have to take another look into this, it's added 30 million instructions TwT (profiled in bumpalo) (that's a 6.2% perf. regression)

@sengmonkham Do you see any way to optimize this PR? From my profilling, it's mainly due to that ty.to_string() usage, and lookup_path_str usage.

Thanks for looking into this and catching the performance regression.

The 6.2% regression is because the hot evaluation loop in unwrap_expect_used::check currently allocates strings (ty.to_string()) and searches the global compiler namespace (clippy_utils::paths::lookup_path_str) for every single .unwrap() and .expect() call across the entire codebase.

I can optimize this by pre-resolving the types exactly the same way the disallowed_* lints do.
Since Methods implements LateLintPass, I can use the check_crate hook to iterate over the allow_unwrap_types strings exactly once per compilation unit. I'll use lookup_path_str here to convert the configuration strings into their exact DefId representations.
I'll store the resolved IDs directly in the Methods struct. I can separate them into:

  • An FxHashSet for standard types/structs/enums (for fast O(1) lookups)
  • A Vec for type aliases (like LockResult) since those require signature verification anyway
    Inside unwrap_expect_used::check, I will completely remove all ty.to_string() allocations and path lookups. Instead, it will be a simple if unwrap_allowed_ids.contains(&adt.did()) mathematical comparison against our pre-resolved cache.

This should bring the performance cost of this configuration practically down to zero during the hot method checks.

Please let me know if this sounds good to you. I'll immediately start working on a PR.

@blyxyas
Copy link
Copy Markdown
Member

blyxyas commented Feb 26, 2026

Sounds good, let's try that (๑•ω•́ฅ✧ Make sure to ping me in that new PR to profile.

github-merge-queue bot pushed a commit that referenced this pull request Mar 3, 2026
…gression (#16652)

Fixes #16605

This PR addresses the 6.2% compilation performance regression introduced
by the original `allow_unwrap_types` implementation (which natively
allocated AST strings on every `unwrap` call via `bumpalo`).

The implementation has been refactored to pre-resolve types securely
without a performance penalty:

**Pre-Resolution Cache via `LateLintPass`**: Instead of executing
`ty.to_string()` and `lookup_path_str` inside the immediate
`unwrap_expect_used` hot check for every method call encountered,
`allow-unwrap-types` configuration strings are now parsed exactly *once*
per crate compilation during the `LateLintPass::check_crate` hook in
`clippy_lints/src/methods/mod.rs`.
**`DefId` Native Storage**: The parsed `DefId` representations are
stored in-memory using highly efficient caching maps directly on the
main `Methods` struct:
- `unwrap_allowed_ids: FxHashSet<DefId>`: Allows instant O(1) lookups
for standard types.
- `unwrap_allowed_aliases: Vec<DefId>`: Tracks type aliases requiring
signature substitution checking later.
**Execution Relief**: Inside `unwrap_expect_used::check()`, string
manipulation is completely removed and `ty::Adt` checking uses
lightning-fast `.contains(&adt.did())` operations to categorically avoid
regression allocation loops. This implementation strongly parallels the
pre-resolution type-routing logic in
`clippy_config::types::create_disallowed_map` without altering the core
`clippy.toml` config requirements.

<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_SUMMARY_START -->

### Summary Notes

-
[Beta-nomination](#16652 (comment))
by [samueltardieu](https://github.com/samueltardieu)

*Managed by `@rustbot`—see
[help](https://forge.rust-lang.org/triagebot/note.html) for details*

<!-- TRIAGEBOT_SUMMARY_END -->
<!-- TRIAGEBOT_END -->
changelog: none
cuviper pushed a commit to cuviper/rust that referenced this pull request Apr 9, 2026
…gression (rust-lang#16652)

Fixes rust-lang/rust-clippy#16605

This PR addresses the 6.2% compilation performance regression introduced
by the original `allow_unwrap_types` implementation (which natively
allocated AST strings on every `unwrap` call via `bumpalo`).

The implementation has been refactored to pre-resolve types securely
without a performance penalty:

**Pre-Resolution Cache via `LateLintPass`**: Instead of executing
`ty.to_string()` and `lookup_path_str` inside the immediate
`unwrap_expect_used` hot check for every method call encountered,
`allow-unwrap-types` configuration strings are now parsed exactly *once*
per crate compilation during the `LateLintPass::check_crate` hook in
`clippy_lints/src/methods/mod.rs`.
**`DefId` Native Storage**: The parsed `DefId` representations are
stored in-memory using highly efficient caching maps directly on the
main `Methods` struct:
- `unwrap_allowed_ids: FxHashSet<DefId>`: Allows instant O(1) lookups
for standard types.
- `unwrap_allowed_aliases: Vec<DefId>`: Tracks type aliases requiring
signature substitution checking later.
**Execution Relief**: Inside `unwrap_expect_used::check()`, string
manipulation is completely removed and `ty::Adt` checking uses
lightning-fast `.contains(&adt.did())` operations to categorically avoid
regression allocation loops. This implementation strongly parallels the
pre-resolution type-routing logic in
`clippy_config::types::create_disallowed_map` without altering the core
`clippy.toml` config requirements.

<!-- TRIAGEBOT_START -->

<!-- TRIAGEBOT_SUMMARY_START -->

### Summary Notes

-
[Beta-nomination](rust-lang/rust-clippy#16652 (comment))
by [samueltardieu](https://github.com/samueltardieu)

*Managed by `@rustbot`—see
[help](https://forge.rust-lang.org/triagebot/note.html) for details*

<!-- TRIAGEBOT_SUMMARY_END -->
<!-- TRIAGEBOT_END -->
changelog: none
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants