Skip to content

impl use function#2035

Open
asukaminato0721 wants to merge 12 commits into
facebook:mainfrom
asukaminato0721:364-1
Open

impl use function#2035
asukaminato0721 wants to merge 12 commits into
facebook:mainfrom
asukaminato0721:364-1

Conversation

@asukaminato0721

@asukaminato0721 asukaminato0721 commented Jan 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Fixes part of #364

Pattern matcher and refactor edits targets single‑return, non‑decorated, non‑async top‑level functions with only positional params and return expressions that reference only those params (no and/or/ternary or chained comparisons to avoid evaluation‑order changes).

Hooked into LSP code actions and exposed as refactor.rewrite .

https://rope.readthedocs.io/en/latest/overview.html#use-function-refactoring

Test Plan

Added a multi‑module test and updated LSP capability expectations

@meta-cla meta-cla Bot added the cla signed label Jan 8, 2026
@asukaminato0721 asukaminato0721 marked this pull request as ready for review January 8, 2026 09:11
Copilot AI review requested due to automatic review settings January 8, 2026 09:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR implements a "use function" refactoring feature that identifies expressions matching a function's return pattern and offers to replace them with function calls. The pattern matcher targets single-return, non-decorated, non-async top-level functions with only positional parameters, avoiding boolean operators, ternary expressions, and chained comparisons to prevent evaluation-order changes.

Key changes:

  • Adds pattern matching logic to find and replace expressions that structurally match a function's return expression
  • Intelligently handles imports across modules, reusing existing imports or adding new ones while avoiding name conflicts
  • Exposes the refactoring as a new refactor.rewrite code action kind in the LSP

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
pyrefly/lib/state/lsp/quick_fixes/use_function.rs New file implementing the core pattern matching and refactoring logic
pyrefly/lib/test/lsp/code_actions.rs Adds comprehensive tests covering cross-module refactoring, import reuse, shadowing, repeated parameters, and edge cases
pyrefly/lib/state/lsp/quick_fixes/mod.rs Registers the new use_function module
pyrefly/lib/state/lsp.rs Adds transaction method to access use_function code actions
pyrefly/lib/lsp/non_wasm/server.rs Integrates use_function into code actions handler and advertises REFACTOR_REWRITE capability
pyrefly/lib/test/lsp/lsp_interaction/basic.rs Updates LSP capability expectations to include refactor.rewrite
crates/pyrefly_config/src/config.rs Removes unnecessary lifetime annotation from function signature

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs Outdated
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@meta-codesync

meta-codesync Bot commented Feb 3, 2026

Copy link
Copy Markdown
Contributor

@jvansch1 has imported this pull request. If you are a Meta employee, you can view this in D92157038.

@jvansch1 jvansch1 left a comment

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.

Good start! I have a couple suggestions for some things that we can change.

Also, for these code action PRs it would be helpful if you could either include some instructions for how we can test these in our own IDE(we mostly use VScode) or a video showing how they work.

Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs
@@ -0,0 +1,895 @@
/*

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.

I think most of the functions in this file need a docstring. A lot of these functions are quite large and it is not immediately clear what their purpose is.

Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs Outdated
Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs Outdated
matches
}

fn match_expr(

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.

Can we break this function up into smaller, easier to read functions. This function is over 300 lines and I think it will be easier to maintain if we can break this into smaller functions which are easier to reason about.

Comment thread pyrefly/lib/test/lsp/code_actions.rs Outdated
@asukaminato0721 asukaminato0721 force-pushed the 364-1 branch 2 times, most recently from b923145 to 54fe777 Compare February 3, 2026 19:11
@github-actions

This comment has been minimized.

@asukaminato0721

Copy link
Copy Markdown
Contributor Author
Screencast_20260204_042421_c.webm
@jvansch1

jvansch1 commented Feb 9, 2026

Copy link
Copy Markdown
Contributor

@asukaminato0721 Looks like there is a merge conflict here. Once this gets updated I will do another review of this.

@github-actions

This comment has been minimized.

Comment thread pyrefly/lib/lsp/non_wasm/server.rs Outdated
Comment thread pyrefly/lib/state/lsp/ast_helpers.rs
Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs Outdated
Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs Outdated
Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs Outdated
Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs
selection: TextRange,
) -> Option<Vec<LocalRefactorCodeAction>> {
let module_info = transaction.get_module_info(handle)?;
if transaction.is_third_party_module(&module_info, handle)

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.

We don't have to do it in this PR, but it seems like this a check we are doing often. We check transaction.is_third_party_module(&target_info, &target_handle) && !transaction.is_source_file(&target_info, &target_handle) twice in this function and we also do it in lib/state/lsp.rs. At this point we can probably extract this out to a helper function.

Again not something that I think needs to be done in the PR but something that would improve code quality a bit.

name: String,
params: Vec<String>,
param_set: HashSet<String>,
param_counts: HashMap<String, usize>,

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.

Do we need all these representations of params? I can see how having the counts is useful, but wonder if we really need to have both params as a vector and as a set.

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.

@asukaminato0721 Still wondering about this.

let Stmt::Import(StmtImport { names, .. }) = stmt else {
continue;
};
for alias in names {

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 is very similar to the block on line 405. Maybe a small helper function should be introduced to reduce repeated code? I know we are comparing a module in one case and a function in another case, so if it becomes too complicated to make it one function then I think it is fine to leave as is.


/// Matches dict expressions by comparing key/value pairs.
fn match_dict_expr(
lhs: &ExprDict,

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.

It looks like we have a lot of functions that do very similar things here with the main difference being the type of lhs and rhs. Maybe we could make this a generic function that has a match statement that could properly handle the logic for different types of lhs and rhs?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

A single generic would likely recreate a big match internally

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.

I think a match might be preferable In this case. I guess we either need to have

  1. A lot of smaller functions like this
  2. One huge function containing all the match arms

I don't think either great but if we had to choose one I think what we have here is better since at least we have named functions which describe what is happening. Overall there is a done of duplicate logic so it would be great to see if we could get rid of that if possible.

@github-actions

This comment has been minimized.

@jvansch1 jvansch1 left a comment

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.

Can we also add some tests for the following cases:

  1. A function with zero arguments
  2. A function that has an argument with a default value.
Comment thread pyrefly/lib/state/lsp/quick_fixes/use_function.rs

/// Matches dict expressions by comparing key/value pairs.
fn match_dict_expr(
lhs: &ExprDict,

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.

I think a match might be preferable In this case. I guess we either need to have

  1. A lot of smaller functions like this
  2. One huge function containing all the match arms

I don't think either great but if we had to choose one I think what we have here is better since at least we have named functions which describe what is happening. Overall there is a done of duplicate logic so it would be great to see if we could get rid of that if possible.

name: String,
params: Vec<String>,
param_set: HashSet<String>,
param_counts: HashMap<String, usize>,

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.

@asukaminato0721 Still wondering about this.

@github-actions

This comment has been minimized.

@jvansch1

Copy link
Copy Markdown
Contributor

@asukaminato0721 I did see an issue with import when using this functionality. It seems like we are using the full import name rather than just importing the function we need. It would be nice if we could fix this before we merge this in.

import_issue_use_function.mov
@jvansch1

jvansch1 commented Mar 4, 2026

Copy link
Copy Markdown
Contributor

@asukaminato0721 I am going to try and finalize this review as well. Could you resolve the merge conflict when you have a chance?

@asukaminato0721 asukaminato0721 force-pushed the 364-1 branch 2 times, most recently from 3d2fac8 to 1445f44 Compare March 5, 2026 02:25
@github-actions

This comment has been minimized.

@asukaminato0721

Copy link
Copy Markdown
Contributor Author

done

@github-actions

This comment has been minimized.

@asukaminato0721

Copy link
Copy Markdown
Contributor Author

done

@github-actions

This comment has been minimized.

@github-actions github-actions Bot added size/xl and removed size/xl labels May 22, 2026
@github-actions

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

@github-actions

Copy link
Copy Markdown

According to mypy_primer, this change doesn't affect type check results on a corpus of open source code. ✅

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

4 participants