Skip to content

Implement Interchangeable_params lint#16973

Draft
jcarey9149 wants to merge 1 commit into
rust-lang:masterfrom
jcarey9149:interchangeable_params
Draft

Implement Interchangeable_params lint#16973
jcarey9149 wants to merge 1 commit into
rust-lang:masterfrom
jcarey9149:interchangeable_params

Conversation

@jcarey9149

@jcarey9149 jcarey9149 commented May 7, 2026

Copy link
Copy Markdown
Contributor

changelog: [interchangable_params]: Add interchangable_params lint

Fixes #16735

This PR implements issue #16735, which suggests a new lint that examines the types of parameters of a function and suggests that any repeated types get newtype values. Doing this will help prevent accidentally changing the order of the parameters by helping the compiler detect the issue.

@rustbot rustbot added needs-fcp PRs that add, remove, or rename lints and need an FCP S-waiting-on-review Status: Awaiting review from the assignee but also interested parties labels May 7, 2026
@rustbot

rustbot commented May 7, 2026

Copy link
Copy Markdown
Collaborator

r? @samueltardieu

rustbot has assigned @samueltardieu.
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

@github-actions

github-actions Bot commented May 7, 2026

Copy link
Copy Markdown

Lintcheck changes for 9d6ccc8

Lint Added Removed Changed
clippy::interchangeable_params 273 0 0

This comment will be updated if you push new changes

@samueltardieu samueltardieu left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The lint is not ready yet, as it has many issues:

  • It suggests creating struct in an impl block, which is not possible. Even if the structs were created outside the impl block, the lint would fail on things like
    impl Foo {
        fn f(self, other1: &Self, other2: &Self) {}
    }
    as it would suggest wrapping Self, which is not possible outside the impl. The situation would also be identical with any dependent type, or with generic parameters.
  • It will suggest creating new struct from the parameter's capitalized name, which may conflict with the existing types, as in
    fn f(string: String, a: String);
    which would fail on the recursive struct String(String); type definition.
  • Generic parameters cannot be used outside the function definition. For example,
    fn do_nothing<T>(a: T, b: T) {}
    would suggest
    struct X(T);
    struct Y(T);
    fn do_nothing<T>(x: X, y: Y) {}
    which is not valid Rust.

Also, in general, this lint does too many snippet manipulation and string creation. It would probably be better to use the clippy_utils::source functions to get references to the source, and bail out if any snippet cannot be obtained.

Parsing for { or ( might also not be robust in presence of inline comments inside the function definiiton.

View changes since this review

Comment thread clippy_lints/src/interchangable_params.rs Outdated
Comment thread clippy_lints/src/interchangable_params.rs Outdated
}
// get the span for the function declaration part (,minus the body)
let fnspan = if let Some(snippet) = snippet_opt(cx, span) {
let snippet = snippet.split('{').nth(0).unwrap_or("").trim_end();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Using .split_once() would avoid unnecessary work. Also, this seems to be used to get the indentation only, which should be done only when emitting the lint and doesn't require manipulating the span.

Also, in which case wouldn't the snippet contain {? In this case, wouldn't it be better to bail out from the lint?

span
};
// and the part of the line before the arguments
let fnstart = if let Some(snippet) = snippet_opt(cx, span) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ditto, only used when emitting the lint, should be deferred. And same question about bailing out if snippet_opt() returns None.

Comment thread clippy_lints/src/interchangable_params.rs Outdated
};
// and the part of the line before the arguments
let fnstart = if let Some(snippet) = snippet_opt(cx, span) {
let snippet = snippet.split('(').nth(0).unwrap_or("").trim_end();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Again, split_once() would be better.

cx,
INTERCHANGABLE_PARAMS,
fnspan,
"interchangable paramters",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Two typos: "interchangeable parameters". The message is not clear enough though.

let mut names: Vec<String> = Vec::new();
// first get the list of types.
for argtype in fndecl.inputs {
let mut snip = (*snippet(cx, argtype.span, "..")).to_string();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why use the textual representation instead of the TyKind? Wouldn't that be possible?

@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 8, 2026
@rustbot

rustbot commented May 8, 2026

Copy link
Copy Markdown
Collaborator

Reminder, once the PR becomes ready for a review, use @rustbot ready.

@jcarey9149

Copy link
Copy Markdown
Contributor Author

Thank you for your feedback.

@jcarey9149 jcarey9149 marked this pull request as draft May 8, 2026 15:00
declare_lint_pass!(InterchangableParams => [INTERCHANGABLE_PARAMS]);

impl<'tcx> LateLintPass<'tcx> for InterchangableParams {
fn check_fn(

@ada4a ada4a May 11, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This currently lints in closures, as seen in:

warning: interchangable paramters
   --> target/lintcheck/sources/ahash-0.8.11/src/random_state.rs:265:19
    |
265 |         let mix = |l: u64, r: u64| {
    |                   ^^^^^^^^^^^^^^^^
    |
help: consider writing
    |
265 ~         let mix = struct L(u64);
266 +         struct R(u64);
267 +         /* .. */
268 +         |l: u64, r: u64| {
269 ~             let mut h = hasher.clone(l: L, r: R) {
    |

Closure are most often just used as local helpers, so I'd argue that adding newtypes for their parameters isn't really worth it.

View changes since the review

@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch from 65f590a to 3d0f880 Compare May 13, 2026 15:51
@jcarey9149

Copy link
Copy Markdown
Contributor Author

Not ready for primetime yet. Changed strategy, detects impl functions and closures. Not making suggestions yet.

@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch 2 times, most recently from 3a84457 to d81c12a Compare May 17, 2026 17:11
@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch 7 times, most recently from b62f943 to 264f71c Compare May 23, 2026 16:59
@rustbot

This comment has been minimized.

@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch 2 times, most recently from 43d618d to 06b5535 Compare May 25, 2026 14:14
@rustbot

This comment has been minimized.

@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch 3 times, most recently from ca0f923 to 307cf87 Compare June 5, 2026 18:56
@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch from 307cf87 to 32dc2ff Compare June 8, 2026 19:25
@jcarey9149 jcarey9149 force-pushed the interchangeable_params branch from 32dc2ff to db73821 Compare June 10, 2026 16:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-fcp PRs that add, remove, or rename lints and need an FCP 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.

New lint: interchangeable_params flag functions where 2+ params share the same type

4 participants