Setting up custom sonar rule for Annotations

I currently played through the Custom Rules 101 and it worked great (havent imported it into our project yet though)
But now I wanted to create my first actual rule which is about analyzing the annotations a method has and whats inside the brackets of this annotation.
ive found that inside of SymbolMetadata.java there is “isAnnotatedWith” and “valuesForAnnotation” but I dont quite understand where to get the content of the brackets of an anotation.

To specify I want to check for @Transactional and see if it has "rollbackFor = " inside the brackets.

A nother question following up:
Is there a blank template which has all necessary classes built in, where you just would need to add the classes described in the tutorial. Because the Custom Rules 101 template has a lot of stuff and rules already built in, which I dont need.

Thank you!

I’m still looking for an answer on what files you need from the Custom Rules 101 template to have a working .jar file to put into your own project.

Furthermore, I figured out on how to get the parameters for @Transactional. The only problem beeing now is that I’m missing a way to detect what class the “rollbackFor” is defined, since you can only get firstToken() and lastToken().

@Override
    public void visitMethod(MethodTree tree) {
        for (AnnotationInstance annotation : tree.symbol().metadata().annotations()) {
            if (annotation.symbol().name().equals(TRANSACTIONAL)) {
                List<AnnotationTree> annotationTree = tree.modifiers().annotations();
                for (AnnotationTree aTree : annotationTree) {
                    Arguments arguments = aTree.arguments();
                    for (int i = 0; i < arguments.size(); i++) {
                        ExpressionTree a = arguments.get(i);
//                        arguments.get(i).firstToken().text() = rollbackFor
//                        arguments.get(i).lastToken().text()) = "class";
                        if (arguments.get(i).firstToken().text().equals("rollbackFor")) {
                            return;
                        }
                    }
                }
                context.reportIssue(this, tree, "Annotation @Transactional is missing rollbackFor");
            }
        }     
    }

The method to test is marked with:
@Transactional (rollbackFor = Exception.class)
But as you see, the lastToken() only finds “class” from after the “.” instead of “Exception.class” after the “=”.
Is there a way to get the “Exception” part of the parameter? From what I see it’s impossible since you only have firstToken() and lastToken() and nothing in between.

Thank you for your help!

Hello @Sven.G, welcome to SonarSource community.

When working with custom rules a nice source of information is the Java analyzer itself, with hundreds of examples! I understand that the whole code is quite complex to understand, but many rule implementation themselves are not that complex, and I’m sure you can already find many information there.

For example, you correctly identified that valuesForAnnotation could be useful. By looking at usages, I ended up on this code. By looking at the rule test file, it turns out that this code detects @Deprecated(forRemoval = true). I believe you can take inspiration from it. :wink:

In addition, as a general comment when writing rules, you will hardly ever have to rely on tokens. Always try to rely on Tree and helper methods.

Is there a blank template which has all necessary classes built in, where you just would need to add the classes described in the tutorial.

I understand the point, we will take it into account when we will eventually rework this tutorial. In the meantime, I believe identifying what is extra or not is not a big deal.

1 Like

Thank you @Quentin this helps out a lot! These information were exactly what I needed to go further.

Thank you for taking the time for explaining and giving information on where to gather more knowledge! I really appreciate it!

1 Like

Hello @Quentin I have build my project around your suggestions, but I’m currently struggeling at a key point and were out of ideas now.
using valuesForAnnotation("org.springframework.transaction.annotation.Transactional") always results in returning null. After checking several classes like SpringIncompatibleTransactionalCheck which uses the same annotation.
I even copied the code in there but for all I get for the methodsPropagationMap is the methodName and null.
Where is the magical difference that my project doesn’t know @Transactional but everything else does?

Thank you once again in advance!

I guess that you are facing such problem in unit tests?

My guess is that you are missing test sources external binaries. See this part of the tutorial: How to test sources requiring external binaries (Note: clicking on the link does not always bring you to the correct category).

Basically, you have to add the dependency you are using in your tests in there. Also, make sure that the maven changes are loaded before running the tests. Last note: also make sure you are correctly setting the class path during the test.

1 Like

You’re a live safer @Quentin. The class path was exactly what I missed. I have added the external binaries before but It didnt work. Setting the path was the deal.
Now I can finally get to the real deal. Thanks so much!!

1 Like

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