Xpath expression that takes a method parameter is being used only in the log call

53 Views Asked by At

I want to create a PMD rule using the XPATH expression with the following scenario:

When a parameter of a method is only used in log like log.info, log.error, log.warn or log.debug from @Slf4j I want XPATH to catch that case. To prevent a parameter not being used (including for logs)

The AST tree generated was this:

└─ CompilationUnit
   └─ ClassOrInterfaceDeclaration
      ├─ ModifierList
      └─ ClassOrInterfaceBody
         ├─ MethodDeclaration
         │  ├─ ModifierList
         │  ├─ VoidType
         │  ├─ FormalParameters
         │  │  └─ FormalParameter
         │  │     ├─ ModifierList
         │  │     ├─ ClassOrInterfaceType
         │  │     └─ VariableDeclaratorId
         │  └─ Block
         │     └─ ExpressionStatement
         │        └─ MethodCall
         │           ├─ AmbiguousName
         │           └─ ArgumentList
         │              └─ VariableAccess
         └─ MethodDeclaration
            ├─ ModifierList
            ├─ VoidType
            ├─ FormalParameters
            └─ Block
               └─ ExpressionStatement
                  └─ MethodCall
                     ├─ AmbiguousName
                     └─ ArgumentList
public void test(final String value) {
    log.info(value);
    
    // must take
  }

  public void method(final String value) {
    log.info(value); 
    
    this.otherMethod(value);
    
    // must not take
  }

  public void method() {
    log.info("value");

    // must not take
  }

I've tried many things, the last attempt was this but without success:

//MethodDeclaration[string(FormalParameters/FormalParameter/VariableDeclaratorId/@name) = string(//MethodCall/ArgumentList/VariableAccess/@name) and Block/ExpressionStatement/MethodCall]
1

There are 1 best solutions below

2
LMC On

Given this java code

public class PMDSource{

public void detectMe(final String value) {
    log.info(value);
    // must take
  }

  public void doNotDetectMe1(final String value) {
    log.info(value); 
    this.otherMethod(value);
    // must not take
  }

  public void doNotDetectMe2() {
    log.info("value");
    // must not take
  }
  
  public void detectMe2(final Integer speed) {
    log.info(speed);
    // must take
  }
}

This XPath expression will detect detectMeand detectMe2 methods

//MethodDeclaration[FormalParameters[@Size=1]/FormalParameter/VariableDeclaratorId/@Name = ./Block[@Size=1]//MethodCall[AmbiguousName/@Name ="log"]/ArgumentList/VariableAccess/@Name]

Can be tested generating an AST XML dump

pmd ast-dump --format xml --language java --file PMDSource.java > PMDSource.java.xml

And then testing the XPath

xmllint --xpath '//MethodDeclaration[FormalParameters[@Size=1]/FormalParameter/VariableDeclaratorId/@Name = ./Block[@Size=1]//MethodCall[AmbiguousName/@Name ="log"]/ArgumentList/VariableAccess/@Name]/@Name' PMDSource.java.xml

Result

 Name="detectMe"
 Name="detectMe2"

Xpath expression parts applied as comments

/* get Name of only formal parameter
 * //MethodDeclaration[FormalParameters[@Size=1]/FormalParameter/VariableDeclaratorId/@Name
 */
public void detectMe(final String value) {
    /* get Name of log.info() only argument
     * ./Block[@Size=1]//MethodCall[AmbiguousName/@Name ="log"]/ArgumentList/VariableAccess/@Name]
     * the dot in ./Block makes it relative to MethodDeclaration node
     */
    log.info(value);
  }

Right side of the equal could also be more precise by adding possible names as (@MethodName="info" or @MethodName="debug")

./Block[@Size=1]//MethodCall[(@MethodName="info" or @MethodName="debug") and AmbiguousName/@Name ="log"