@@ -47,42 +47,42 @@ declare_oxc_lint!(
4747
4848// Create a static set for all function names
4949static FUNCTION_NAMES : phf:: Set < & ' static str > = phf:: phf_set! {
50- // Compare function names
51- "is" ,
52- "equal" ,
53- "notEqual" ,
54- "strictEqual" ,
55- "notStrictEqual" ,
56- "propertyVal" ,
57- "notPropertyVal" ,
58- "not" ,
59- "include" ,
60- "property" ,
61- "toBe" ,
62- "toHaveBeenCalledWith" ,
63- "toContain" ,
64- "toContainEqual" ,
65- "toEqual" ,
66- "same" ,
67- "notSame" ,
68- "strictSame" ,
69- "strictNotSame" ,
70- // `array.push(undefined)`
71- "push" ,
72- // `array.unshift(undefined)`
73- "unshift" ,
74- // `array.includes(undefined)`
75- "includes" ,
76- // `set.add(undefined)`
77- "add" ,
78- // `set.has(undefined)`
79- "has" ,
80- // `map.set(foo, undefined)`
81- "set" ,
82- // `React.createContext(undefined)`
83- "createContext" ,
84- // https://vuejs.org/api/reactivity-core.html#ref
85- "ref" ,
50+ // Compare function names
51+ "is" ,
52+ "equal" ,
53+ "notEqual" ,
54+ "strictEqual" ,
55+ "notStrictEqual" ,
56+ "propertyVal" ,
57+ "notPropertyVal" ,
58+ "not" ,
59+ "include" ,
60+ "property" ,
61+ "toBe" ,
62+ "toHaveBeenCalledWith" ,
63+ "toContain" ,
64+ "toContainEqual" ,
65+ "toEqual" ,
66+ "same" ,
67+ "notSame" ,
68+ "strictSame" ,
69+ "strictNotSame" ,
70+ // `array.push(undefined)`
71+ "push" ,
72+ // `array.unshift(undefined)`
73+ "unshift" ,
74+ // `array.includes(undefined)`
75+ "includes" ,
76+ // `set.add(undefined)`
77+ "add" ,
78+ // `set.has(undefined)`
79+ "has" ,
80+ // `map.set(foo, undefined)`
81+ "set" ,
82+ // `React.createContext(undefined)`
83+ "createContext" ,
84+ // https://vuejs.org/api/reactivity-core.html#ref
85+ "ref" ,
8686} ;
8787
8888fn is_match_ignore_func_name ( name : & str ) -> bool {
@@ -118,6 +118,19 @@ fn is_undefined(arg: &Argument) -> bool {
118118 false
119119}
120120
121+ fn is_has_function_return_type ( node : & AstNode , ctx : & LintContext < ' _ > ) -> bool {
122+ let Some ( parent_node) = ctx. nodes ( ) . parent_node ( node. id ( ) ) else {
123+ return false ;
124+ } ;
125+ match parent_node. kind ( ) {
126+ AstKind :: ArrowFunctionExpression ( arrow_func_express) => {
127+ arrow_func_express. return_type . is_some ( )
128+ }
129+ AstKind :: Function ( func) => func. return_type . is_some ( ) ,
130+ _ => is_has_function_return_type ( parent_node, ctx) ,
131+ }
132+ }
133+
121134impl Rule for NoUselessUndefined {
122135 fn from_configuration ( value : serde_json:: Value ) -> Self {
123136 let check_arguments =
@@ -139,6 +152,9 @@ impl Rule for NoUselessUndefined {
139152 match parent_node_kind {
140153 // `return undefined`
141154 AstKind :: ReturnStatement ( ret_stmt) => {
155+ if is_has_function_return_type ( parent_node, ctx) {
156+ return ;
157+ }
142158 ctx. diagnostic_with_fix (
143159 no_useless_undefined_diagnostic ( undefined_literal. span ) ,
144160 |fixer| {
@@ -190,6 +206,10 @@ impl Rule for NoUselessUndefined {
190206 return ;
191207 } ;
192208
209+ if is_has_function_return_type ( parent_node, ctx) {
210+ return ;
211+ }
212+
193213 ctx. diagnostic_with_fix (
194214 no_useless_undefined_diagnostic ( undefined_literal. span ) ,
195215 |fixer| fixer. replace ( func_body. span , "{}" ) ,
@@ -208,6 +228,9 @@ impl Rule for NoUselessUndefined {
208228 if variable_declarator. kind == VariableDeclarationKind :: Const {
209229 return ;
210230 }
231+ if is_has_function_return_type ( parent_node, ctx) {
232+ return ;
233+ }
211234 return ctx. diagnostic_with_fix (
212235 no_useless_undefined_diagnostic ( undefined_literal. span ) ,
213236 |fixer| {
@@ -222,6 +245,9 @@ impl Rule for NoUselessUndefined {
222245 AstKind :: AssignmentPattern ( assign_pattern) => {
223246 let left = & assign_pattern. left ;
224247 let delete_span = Span :: new ( left. span ( ) . end , undefined_literal. span . end ) ;
248+ if is_has_function_return_type ( parent_node, ctx) {
249+ return ;
250+ }
225251 return ctx. diagnostic_with_fix (
226252 no_useless_undefined_diagnostic ( undefined_literal. span ) ,
227253 |fixer| fixer. delete_range ( delete_span) ,
@@ -353,9 +379,223 @@ fn test() {
353379 // `checkArrowFunctionBody: false`
354380 ( r"const foo = () => undefined" , options_ignore_arrow_function_body( ) ) ,
355381 ( r"const x = { a: undefined }" , None ) ,
382+ // https://github.com/zeit/next.js/blob/3af0fe5cf2542237f34d106872d104c3606b1858/packages/next/build/utils.ts#L620
383+ ( r"prerenderPaths?.add(entry)" , None ) ,
384+ (
385+ r#"
386+ function getThing(): string | undefined {
387+ if (someCondition) {
388+ return "hello world";
389+ }
390+
391+ return undefined;
392+ }
393+ "# ,
394+ None ,
395+ ) ,
396+ (
397+ r#"
398+ function getThing(): string | undefined {
399+ if (someCondition) {
400+ return "hello world";
401+ } else if (anotherCondition) {
402+ return undefined;
403+ }
404+
405+ return undefined;
406+ }
407+ "# ,
408+ None ,
409+ ) ,
410+ ( r"const foo = (): undefined => {return undefined;}" , None ) ,
411+ ( r"const foo = (): undefined => undefined;" , None ) ,
412+ ( r"const foo = (): string => undefined;" , None ) ,
413+ ( r"const foo = function (): undefined {return undefined}" , None ) ,
414+ ( r"export function foo(): undefined {return undefined}" , None ) ,
415+ (
416+ r"
417+ const object = {
418+ method(): undefined {
419+ return undefined;
420+ }
421+ }
422+ " ,
423+ None ,
424+ ) ,
425+ (
426+ r"
427+ class A {
428+ method(): undefined {
429+ return undefined;
430+ }
431+ }
432+ " ,
433+ None ,
434+ ) ,
435+ (
436+ r"
437+ const A = class A {
438+ method(): undefined {
439+ return undefined
440+ }
441+ };
442+ " ,
443+ None ,
444+ ) ,
445+ (
446+ r"
447+ class A {
448+ static method(): undefined {
449+ return undefined
450+ }
451+ }
452+ " ,
453+ None ,
454+ ) ,
455+ (
456+ r"
457+ class A {
458+ get method(): undefined {
459+ return undefined;
460+ }
461+ }
462+ " ,
463+ None ,
464+ ) ,
465+ (
466+ r"
467+ class A {
468+ static get method(): undefined {
469+ return undefined;
470+ }
471+ }
472+ " ,
473+ None ,
474+ ) ,
475+ (
476+ r"
477+ class A {
478+ #method(): undefined {
479+ return undefined;
480+ }
481+ }
482+ " ,
483+ None ,
484+ ) ,
485+ (
486+ r"
487+ class A {
488+ private method(): undefined {
489+ return undefined;
490+ }
491+ }
492+ " ,
493+ None ,
494+ ) ,
495+ ( r"createContext<T>(undefined);" , None ) ,
496+ ( r"React.createContext<T>(undefined);" , None ) ,
497+ ( r"const x = { a: undefined }" , None ) ,
498+ (
499+ r"
500+ const y: any = {}
501+ y.foo = undefined
502+ " ,
503+ None ,
504+ ) ,
505+ (
506+ r"
507+ class Foo {
508+ public x: number | undefined = undefined
509+ }
510+ " ,
511+ None ,
512+ ) ,
356513 ] ;
357514
358- let fail = vec ! [ ( r"let foo = undefined;" , None ) ] ;
515+ let fail = vec ! [
516+ (
517+ r"
518+ foo(
519+ undefined,
520+ bar,
521+ undefined,
522+ undefined,
523+ undefined,
524+ undefined,
525+ )
526+ " ,
527+ None ,
528+ ) ,
529+ ( r"function foo([bar = undefined] = []) {}" , None ) ,
530+ (
531+ r"
532+ foo(
533+ undefined,
534+ bar,
535+ undefined,
536+ undefined,
537+ undefined,
538+ undefined,
539+ )
540+ " ,
541+ None ,
542+ ) ,
543+ ( "function foo([bar = undefined] = []) {}" , None ) ,
544+ ( "foo(bar, undefined, undefined);" , None ) ,
545+ ( "let a = undefined, b = 2;" , None ) ,
546+ // TODO: Handle parenthesized undefined
547+ /* (
548+ r#"
549+ function foo() {
550+ return /* */ (
551+ /* */
552+ (
553+ /* */
554+ undefined
555+ /* */
556+ )
557+ /* */
558+ ) /* */ ;
559+ }
560+ "#,
561+ None,
562+ ),
563+ (
564+ r#"
565+ function * foo() {
566+ yield /* */ (
567+ /* */
568+ (
569+ /* */
570+ undefined
571+ /* */
572+ )
573+ /* */
574+ ) /* */ ;
575+ }
576+ "#,
577+ None,
578+ ),
579+ (
580+ r#"
581+ const foo = () => /* */ (
582+ /* */
583+ (
584+ /* */
585+ undefined
586+ /* */
587+ )
588+ /* */
589+ );
590+ "#,
591+ None,
592+ ), */
593+ ( "foo.bind(undefined)" , None ) ,
594+ ( "bind(foo, undefined)" , None ) ,
595+ ( "foo.bind?.(bar, undefined)" , None ) ,
596+ ( "foo[bind](bar, undefined)" , None ) ,
597+ ( "foo.notBind(bar, undefined)" , None ) ,
598+ ] ;
359599
360600 let fix = vec ! [
361601 ( r"function foo() {return undefined;}" , r"function foo() {return;}" , None ) ,
@@ -396,7 +636,34 @@ fn test() {
396636 ( "function foo({bar = undefined} = {}) {}" , "function foo({bar} = {}) {}" , None ) ,
397637 ( "function foo([bar = undefined]) {}" , "function foo([bar]) {}" , None ) ,
398638 ( "function foo([bar = undefined] = []) {}" , "function foo([bar] = []) {}" , None ) ,
639+ // TODO: maybe fix into `x?: string`?
640+ (
641+ "function foo(x: string | undefined = undefined) { }" ,
642+ "function foo(x: string | undefined) { }" ,
643+ None ,
644+ ) ,
399645 ( "return undefined;" , "return;" , None ) ,
646+ (
647+ r"
648+ function foo():undefined {
649+ function nested() {
650+ return undefined;
651+ }
652+
653+ return nested();
654+ }
655+ " ,
656+ r"
657+ function foo():undefined {
658+ function nested() {
659+ return;
660+ }
661+
662+ return nested();
663+ }
664+ " ,
665+ None ,
666+ ) ,
400667 ] ;
401668
402669 Tester :: new ( NoUselessUndefined :: NAME , pass, fail) . expect_fix ( fix) . test_and_snapshot ( ) ;
0 commit comments