Sonar custom rule: Check for annotation in invoked method or load class

Hello,

we are using an annotation to mark in our code methods and classes that will be removed in the near future. The annotation is very similar to @deprecated, the only difference is that our annotation has a deletion date, when the code will be removed. I thought that it would be easy to write a custom sonar rule similar to CallToDeprecatedMethodCheck, that would mark all invokings of deprecated methods which will be removed in the next month. But I utterly failed. In other words, in the base.jar there is a class

public class DeprecatedMethod {

	@CodeWillBeRemoved(... deletionDate = "2022-06-01")
	@Deprecated
	public void doIt() {
	}
}

And a second Jar containing the class which will be analysed:

public class TestClass {

	public void callDeprecated() {
		DeprecatedMethod my = new DeprecatedMethod();
		my.doIt();
	}
}

What I would like to achieve is that in TestClass my.doIt(); will be flagged as a blocker if analysed after 2022-05-01 (because then the deletion date is only one month away).
In my custom rule, when examining the call doIt, I can get the fully qualified name of the class on which the method is called, but I’m neither able to get any annotations on the method doIt() nor could I load the class DeprecatedMethod to check for annotations via reflection. Is it completely futile what I’m trying to achieve or am I missing something. Any hints are greatly welcome.

Best regards
Florian

Hello @schwarfl and welcome to the SonarSource community.

At first, glance, what you are trying to achieve should be possible.
You can access annotations from the metadata of a symbol. Here is what it could look like:

  private Optional<String> getDate(MethodInvocationTree mit) {
    List<SymbolMetadata.AnnotationValue> valuesForAnnotation = mit.symbol().metadata().valuesForAnnotation("checks.CodeWillBeRemoved"); // TODO: change to fully qualified name
    if (valuesForAnnotation == null) {
      return Optional.empty();
    }
    return valuesForAnnotation.stream()
      .filter(annotationValue -> "deletionDate".equals(annotationValue.name()))
      .map(annotationValue -> (String) annotationValue.value())
      .findFirst();
  }

In addition, if the symbol is unknown in unit tests, you should probably have a look at this post:

Hope it helps.
Best,
Quentin

1 Like

Thank you very much for your response. I tried something like the code you suggested, but you are right, since my JUnit test with the JavaCheckVerifier failed, I didn’t try it in sonarqube. But now I tried it on sonarqube and there it worked! So obviously my test environment is wrong.
Thank you so much for your help!

Best regards
Florian

1 Like

This topic was automatically closed 7 days after the last reply. New replies are no longer allowed.