-
-
Notifications
You must be signed in to change notification settings - Fork 670
fix(linter/jsx-handler-name): improve handler name position in error messages #14174
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
base: main
Are you sure you want to change the base?
fix(linter/jsx-handler-name): improve handler name position in error messages #14174
Conversation
…led before the check using this regex.
because the extracted handler name will not have the "props." part.
How to use the Graphite Merge QueueAdd either label to this PR to merge it via the merge queue:
You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. |
There was a problem hiding this comment.
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 improves the error message positioning for the jsx-handler-names linter rule by making the underline point specifically to the event handler name rather than the entire expression. The change enhances the precision of error reporting by extracting and highlighting only the relevant handler name portion.
Key changes:
- Refactored handler name extraction logic to return both the name and its specific span
- Modified error diagnostic to use the precise handler name span instead of the entire expression container span
- Added support for detecting "props" handlers to allow proper validation
Reviewed Changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
File | Description |
---|---|
crates/oxc_linter/src/rules/react/jsx_handler_names.rs | Refactored handler name extraction with precise span tracking and added props handler detection |
crates/oxc_linter/src/snapshots/react_jsx_handler_names.snap | Updated test snapshots showing improved error message positioning |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
CodSpeed Instrumentation Performance ReportMerging #14174 will not alter performanceComparing Summary
Footnotes
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
72594b0
to
4af3a1a
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
4af3a1a
to
11f2e81
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
member_expr: &StaticMemberExpression, | ||
ctx: &LintContext, | ||
) -> (CompactStr, Span, bool) { | ||
let name = member_expr.property.name.as_str(); | ||
let span = member_expr.property.span; | ||
match &member_expr.object { | ||
Expression::ThisExpression(_) => (name.into(), span, false), | ||
Expression::Identifier(ident) => { | ||
let obj_name = ident.name.as_str(); | ||
(name.into(), span, obj_name == "props") | ||
} | ||
Expression::StaticMemberExpression(expr) => { | ||
let (obj_name, _, _) = get_event_handler_name_from_static_member_expression(expr, ctx); | ||
(name.into(), span, obj_name == "props") | ||
} | ||
_ => (ctx.source_range(member_expr.span).into(), member_expr.span, false), | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This recursive call could lead to deep recursion for deeply nested member expressions. Consider adding a recursion depth limit or iterative implementation to prevent stack overflow.
member_expr: &StaticMemberExpression, | |
ctx: &LintContext, | |
) -> (CompactStr, Span, bool) { | |
let name = member_expr.property.name.as_str(); | |
let span = member_expr.property.span; | |
match &member_expr.object { | |
Expression::ThisExpression(_) => (name.into(), span, false), | |
Expression::Identifier(ident) => { | |
let obj_name = ident.name.as_str(); | |
(name.into(), span, obj_name == "props") | |
} | |
Expression::StaticMemberExpression(expr) => { | |
let (obj_name, _, _) = get_event_handler_name_from_static_member_expression(expr, ctx); | |
(name.into(), span, obj_name == "props") | |
} | |
_ => (ctx.source_range(member_expr.span).into(), member_expr.span, false), | |
} | |
mut member_expr: &StaticMemberExpression, | |
ctx: &LintContext, | |
) -> (CompactStr, Span, bool) { | |
let name = member_expr.property.name.as_str(); | |
let span = member_expr.property.span; | |
let mut obj = &member_expr.object; | |
// Track if the deepest object is "props" | |
let mut is_props = false; | |
let mut obj_name: Option<&str> = None; | |
loop { | |
match obj { | |
Expression::ThisExpression(_) => { | |
obj_name = Some("this"); | |
break; | |
} | |
Expression::Identifier(ident) => { | |
obj_name = Some(ident.name.as_str()); | |
is_props = ident.name.as_str() == "props"; | |
break; | |
} | |
Expression::StaticMemberExpression(expr) => { | |
// Go deeper | |
obj = &expr.object; | |
obj_name = Some(expr.property.name.as_str()); | |
is_props = expr.property.name.as_str() == "props"; | |
// Continue loop | |
} | |
_ => { | |
obj_name = None; | |
break; | |
} | |
} | |
} | |
let obj_name_str = obj_name.unwrap_or_else(|| ctx.source_range(member_expr.span).as_ref()); | |
(name.into(), span, is_props) |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Modified not to use either recursive function calls or a loop.
oxc/crates/oxc_linter/src/rules/react/jsx_handler_names.rs
Lines 381 to 401 in 1c77a4b
fn get_event_handler_name_from_static_member_expression( | |
member_expr: &StaticMemberExpression, | |
) -> (CompactStr, Span, bool) { | |
let name = member_expr.property.name.as_str(); | |
let span = member_expr.property.span; | |
match &member_expr.object { | |
Expression::Identifier(ident) => { | |
let obj_name = ident.name.as_str(); | |
(name.into(), span, obj_name == "props") // props.handleChange or obj.handleChange | |
} | |
Expression::StaticMemberExpression(expr) => { | |
if let Expression::ThisExpression(_) = &expr.object { | |
let obj_name = expr.property.name.as_str(); | |
(name.into(), span, obj_name == "props") // this.props.handleChange or this.obj.handleChange | |
} else { | |
(name.into(), span, false) // foo.props.handleChange, props.foo.handleChange, foo.bar.handleChange, etc. | |
} | |
} | |
_ => (name.into(), span, false), // this.handleChange | |
} | |
} |
11f2e81
to
b585030
Compare
5748611
to
116260b
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull Request Overview
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
_ => { | ||
if !self.check_local_variables && !value_expr.is_member_expression() { | ||
return; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using shrink(1)
on the expression container span may not provide the correct handler name span for all expression types. This fallback should either be more specific about span calculation or have a comment explaining why shrink(1)
is appropriate here.
} | |
} | |
// For other expression types, we use shrink(1) to approximate the handler name span, | |
// as these are typically simple expressions (e.g., literals, calls) where the handler name | |
// is expected to be at the end of the expression container. If more specific handling is needed, | |
// it should be added here. |
Copilot uses AI. Check for mistakes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Added a comment.
oxc/crates/oxc_linter/src/rules/react/jsx_handler_names.rs
Lines 307 to 308 in 1c77a4b
// For other expressions types, use the whole content inside the braces as the handler name, | |
// which will be marked as a bad handler name if the prop key is an event handler prop. |
f9f431d
to
1c77a4b
Compare
With this change, when an event handler name is detected, the underline in the error message will point only to the event handler name.