Skip to content

Extend UnusedLocalVariable to support pattern variables #15085

@mahfouz72

Description

@mahfouz72

child of #14942
Check doc: https://checkstyle.org/checks/coding/unusedlocalvariable.html#UnusedLocalVariable


From Check Description :

Checks that a local variable is declared and/or assigned, but not used. Doesn't support pattern variables yet

We should extend UnusedLocalVariable to support pattern variables. The unused pattern variables should be unnamed patterns or unnamed pattern variables. This Improves the readability of record patterns by eliding unnecessary nested type patterns. The main goal is to ensure that unused pattern variables are detected and violated, prompting developers to either use them or declare them as unnamed variables.

To be clear a pattern is a local variable.
Reference: https://docs.oracle.com/javase/specs/jls/se17/html/jls-14.html#jls-14.30:~:text=A%20local%20variable%20declaration%20can%20appear%20in%20the%20following%20locations%3A

A local variable declaration can appear in the following locations:

  • a local variable declaration statement in a block (§14.4.2)
  • the header of a basic for statement (§14.14.1)

  • the header of an enhanced for statement (§14.14.2)

  • the resource specification of a try-with-resources statement (§14.20.3)

Examples of expected behaviour when we add support for pattern variables

  1. Switch and case labels
sealed abstract class Ball permits RedBall, GreenBall { }
final  class RedBall   extends Ball { }
final  class GreenBall extends Ball { }
record Box<T extends Ball>(T content) { }

public class Test {
    public void PatternVariableInCaseLabels(Ball ball) {
        switch (ball) {
            case RedBall redBall -> process(ball); // expected violation, unused variable 'redBall'
            case GreenBall greenBall -> stopProcessing(ball);  // expected violation, unused variable 'greenBall'
        }
        // after fix
        switch (ball) {
            case RedBall _ -> process(ball);
            case GreenBall _ -> stopProcessing(ball);
        }
    }

    public void PatternVariableRecordPatternCaseLabels(Box<? extends Ball> box) {
        switch (box) {
            case Box(RedBall redBall) -> process(box); // expected violation, unused variable 'redBall'
            case Box(GreenBall greenBall) -> stopProcessing(box);  // expected violation, unused variable 'greenBall'
        }
        switch (box) {
            case Box(RedBall _) -> process(box); 
            case Box(GreenBall _) -> stopProcessing(box);  
        }

    }
    void process(Object obj) {}
    void stopProcessing(Object obj) {}
}
<!DOCTYPE module PUBLIC
          "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
          "https://checkstyle.org/dtds/configuration_1_3.dtd">

<module name="Checker">
  <module name="TreeWalker">
    <module name="UnusedLocalVariable"/>
  </module>
</module>
PS D:\CS\test> java  -jar checkstyle-10.17.0-all.jar -c config.xml src/Test.java
Starting audit...
Audit done.
PS D:\CS\test> 

  1. Contitional Statments
sealed abstract class Ball permits RedBall, GreenBall { }
final  class RedBall   extends Ball { }
final  class GreenBall extends Ball { }
record Box<T extends Ball>(T content) { }
record ColoredPoint(int x, int y, String color) { }
record Rectangle(ColoredPoint upperLeft, ColoredPoint lowerRight) { }


public class Test {

    public void PatternVariableConditionalStatements(Ball ball) {
        if (ball instanceof RedBall redBall) { // expected violation, unused variable 'redBall'
            process(ball);
        } else if (ball instanceof GreenBall greenBall) { // expected violation, unused variable 'greenBall'
            stopProcessing(ball);
        }
        // fix
        if (ball instanceof RedBall _) {
            process(ball);
        } else if (ball instanceof GreenBall _) {
            stopProcessing(ball);
        }
    }

    public void PatternVariableConditionalStatements2(Ball ball) {
        if (!(ball instanceof RedBall redBall)) {
            process(ball);  // red ball is not in this scope
        } else {
             process(redBall);  // ok red ball is used
        }
    }

    public void PatternVariableInRecordPatternInConditionalStatements(Box<? extends Ball> box) {
        if (box instanceof Box(RedBall redBall)) { // expected violation, unused variable 'redBall'
            process(box);
        } else if (box instanceof Box(GreenBall greenBall)) { // expected violation, unused variable 'greenBall'
            stopProcessing(box);
        }
        // fix
        if (box instanceof Box(RedBall _)) {
            process(box);
        } else if (box instanceof Box(GreenBall _)) {
            stopProcessing(box);
        }
       
     public void PatternVariableNestedRecordPatterns(Object o) {
         if (o instanceof Rectangle(ColoredPoint(int p, int x, String c), ColoredPoint lr)) {
             System.out.println(p + "" + x + c);
         }
         // fix
         if (o instanceof Rectangle(ColoredPoint(int p, int x, String c), ColoredPoint _)) {
             System.out.println(p + "" + x + c);
         }
         // another fix
         if (o instanceof Rectangle(ColoredPoint(int p, int x, String c), _)) {
             System.out.println(p + "" + x + c);
         }
      }
    }
}
PS D:\CS\test> cat config.xml                                                   
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
          "-//Checkstyle//DTD Checkstyle Configuration 1.3//EN"
          "https://checkstyle.org/dtds/configuration_1_3.dtd">

<module name="Checker">
  <module name="TreeWalker">
    <module name="UnusedLocalVariable"/>
  </module>
</module>
PS D:\CS\test> java  -jar checkstyle-10.17.0-all.jar -c config.xml src/Test.java
Starting audit...
Audit done.




pattern variables within conditional statements are subject to flow sensitivity. This means that the scope of these variables depend on the control flow of the program.

Reference for the pattern variable scope:

Metadata

Metadata

Assignees

No one assigned

    Labels

    approvedbugfalse negativeissues where check should place violations on code, but does not

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions