Problem in MethodMatcher while adding custom rule

Hi,
I’m trying to create my first custom rule, based on sonar-java/docs/CUSTOM_RULES_101.md at master · SonarSource/sonar-java · GitHub.

This is the class I created:

package org.sonar.samples.java.checks;

import org.sonar.check.Rule;
import org.sonar.java.checks.methods.AbstractMethodDetection;
import org.sonar.plugins.java.api.semantic.MethodMatchers;
import org.sonar.plugins.java.api.tree.MethodInvocationTree;

import static org.sonar.plugins.java.api.semantic.MethodMatchers.ANY;

@Rule(key = "MyFirstCustomRule")
public class MyFirstCustomCheck extends AbstractMethodDetection {

  private static final String[] ASSERTION_CLASSES = {
    // JUnit4
    "org.junit.Assert", "junit.framework.TestCase",
    // JUnit4 (deprecated)
    "junit.framework.Assert",
    // JUnit5
    "org.junit.jupiter.api.Assertions" };

  @Override
  protected MethodMatchers getMethodInvocationMatchers() {
    return MethodMatchers.create()
      .ofTypes(ASSERTION_CLASSES)
      .names("assertEquals")
      .addParametersMatcher(ANY, ANY)
      .build();

  }

  @Override
  protected void onMethodInvocationFound(MethodInvocationTree mit) {
    //reportIssue(ExpressionUtils.methodName(mit), "Use \"org.assertj.core.api.Assertions.assertThat\" instead." + context.getJavaVersion()
    reportIssue(mit.methodSelect(), "Use \"org.assertj.core.api.Assertions.assertThat\" instead." + context.getJavaVersion()
      .java8CompatibilityMessage());
  }

}

This is the test class:

package org.sonar.samples.java.checks;

import org.junit.jupiter.api.Test;
import org.sonar.java.checks.verifier.CheckVerifier;

class MyFirstCustomCheckTest {
  

  @Test
  void aaa() {

    CheckVerifier.newVerifier()
      //.onFile(FILENAME)
      .onFile("src/test/files/MyFirstCustomCheck.java")
      .withCheck(new MyFirstCustomCheck())
      //.withJavaVersion(8)
      .verifyIssues();

  }

}

And the test file:

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.assertj.core.api.Assertions.assertThat;

public class MyClass {

   int result = 5;

   void t1(){
     assertEquals(5, result); // Noncompliant
     assertThat(result).isEqualTo(5);
   }
}

I debug the code, and in the MethodMatchersBuilder, the first line does not match, when it should.

    public boolean matches(MethodInvocationTree mit) {
        IdentifierTree id = getIdentifier(mit);
        return this.matches(id.symbol(), getCallSiteType(mit));
    }

I see that id.symbol returns an UnknownSymbol, while in another test from the workspace I have downloaded it returns a JMethodSymbol.

What am I doing wrong?
Thanks!

1 Like

Hi Pep.
In your pom.xml file for the custom plugin you should have a dependency-plugin configured like so:

<!-- only required to run UT - these are UT dependencies -->
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-dependency-plugin</artifactId>
        <executions>
          <execution>
            <id>copy</id>
            <phase>test-compile</phase>
            <goals>
              <goal>copy</goal>
            </goals>
            <configuration>
              <artifactItems>
......
.....
.....

If your custom rules rely on the semantics of some specific library, you should add these dependencies in this configuration, so that in your unit test, you will be able to provide a custom classpath, like:

CheckVerifier.newVerifier()
      .onFile("src/test/files/MyFirstCustomCheck.java")
      .withCheck(new MyFirstCustomCheck())
      .withClassPath(FilesUtils.getClassPath("target/test-jars"))
      .verifyIssues();

Where the test-jars directory is where the pom configuration is storing all the dependencies required for unit tests.

<!-- You should see this at the end of the maven-dependency-plugin configuration -->
<outputDirectory>${project.build.directory}/test-jars</outputDirectory>

I noticed that we are not very clear on this part in the documentation, so I created a ticket to improve that!

Have a nice day :slight_smile:

Hi Leonardo,
Thanks for your help, it worked!

It should be nice to update the documentation, so it will be clear for the future readers.

Regards,
Pep

2 Likes