Java custom rule writing without exploring the Syntax Tree

Is there any way to traverse the whole file without using any specific tree in SonarJava plugin?Like currently if i need to find specific variable name then i have to traverse the variable tree but if i don’t want to travel any tree rather than i want to search for that specific variable name using regular expression?So how to approach ?

Hello,

When implementing a Java custom rule, nothing forces you to use a BaseTreeVisitor or IssuableSubscriptionVisitor, you can perfectly only implement the JavaFileScanner interface, which will give you access to the content of the file:

package org.sonar.samples.java.checks;

import org.sonar.check.Rule;
import org.sonar.plugins.java.api.JavaFileScanner;
import org.sonar.plugins.java.api.JavaFileScannerContext;

@Rule(key = "MyCheck")
public class MyCheck implements JavaFileScanner {

  @Override
  public void scanFile(JavaFileScannerContext context) {
    context.getFileContent(); // to retrieve the full content of the file as a String
    context.getFileLines();   // to retrieve the content of each lines of the file, as a String
  }
}

Now, depending of what you need to do (especially if you try to be precise in the rule issue locations), not using the Tree is most probably only going to be a pain on the long run.
Identifying a variable in a whole file without information from syntax tree or semantic, but only relying on a regex, may also become quite tricky for big files.

Cheers,
Michael

Note: Regarding format of your message, please note that giving context (versions of analyzers, SQ?) and clearly expressing questions is usually quite appreciated, like usual introductions and conclusions are (greetings? thanks?)

3 Likes

Hi Michael,
Thanks a lot for your help. I will keep in mind the things you have mentioned at the end. I am currently using SQ-6.7, Scanner-3.2. One more thing is for javascript plugin, is there any interface like javafilescanner which can help to achieve the same thing.Thanks in advance!

1 Like

Hey,

I’m not familiar with SonarJS API, but it seems to me that you should also be able to access the content without necessarily explore the tree, by accessing the context, from the scanFile method.

For instance:

import java.io.IOException;
import java.util.List;
import org.sonar.check.Rule;
import org.sonar.javascript.visitors.Issues;
import org.sonar.plugins.javascript.api.visitors.DoubleDispatchVisitorCheck;
import org.sonar.plugins.javascript.api.visitors.Issue;
import org.sonar.plugins.javascript.api.visitors.TreeVisitorContext;

@Rule(key = "MyCheck")
public class MyCheck extends DoubleDispatchVisitorCheck {

  private Issues issues = new Issues(this);

  @Override
  public List<Issue> scanFile(TreeVisitorContext context) {

    try {
      context.getJavaScriptFile().contents(); // retrieve the content of the file
    } catch (IOException e) {
      // do something...
    }

    return issues.getList();
  }
}

Note that, again, by relying directly on the file content, you lose a lot of the interest of the analyzer (symbol table, semantic, tokens). Moreover, using the Tree structure is usually way easier to access precise part of the code, and write rules.

Hope this helps,
Michael

1 Like

Hi Michael,
Thank you for your response.As a novice guy,I have tried to analyse the code snippet which you have shared but how we are going to raise issue through this approach as earlier we used to check the condition and then if it is true then we use to call addIssue(Tree,MESSAGE); but in your context how we are going to raise and which method to call and what parameters we need to pass for raising the issue at specific location. For example: I am trying to find toUpperCase()/toLowerCase() in javascript file and if it is there i need to raise the issue.So going with your approach i wrote something like this:

import java.io.IOException;
import java.util.List;
import org.sonar.check.Rule;
import org.sonar.javascript.visitors.Issues;
import org.sonar.plugins.javascript.api.visitors.DoubleDispatchVisitorCheck;
import org.sonar.plugins.javascript.api.visitors.Issue;
import org.sonar.plugins.javascript.api.visitors.TreeVisitorContext;

@Rule(key = "MyCheck")
public class UpperLowerCheck extends DoubleDispatchVisitorCheck {

  private Issues issues = new Issues(this);

  @Override
  public List<Issue> scanFile(TreeVisitorContext context) {

   
    	if(context.getJavaScriptFile().contents().contains("toUpperCase") || context.getJavaScriptFile().contents().contains("toLowerCase")) {
    		//addIssue(Tree,MESSAGE); How to proceed further after this approach.
    	

    return issues.getList();
  }
}

Hello,

Okay, so that’s exactly why you should NOT rely on string content of files to write such checks. With this approach, it is simply not possible, or at best not efficient at all. Using content of file for such use cases is a dead end to me, and does not worth investigating further.

If you want to write rules to target some very specific part of code (like variable or method/function invocations), and then raise issue accordingly, you have no other choice than following the syntax. And rely on it.

Please have a look at the tutorial on how to write rules (Writing Custom Java Rules 101). It will introduce you to the concept of syntax tree and how to interact with it. This is covering java rules, but the exact same principle can be applied on JavaScript rules, with a few adaptations.

Please take the time to experiment with the structure of the syntax tree and the various plugin API. Write examples, and print the structure and tree Kinds if it helps. The Java and JavaScript plugins are open sources, please also have a look at the other rules which have been implemented and use them as inspiration. Some are most probably doing very similar things that what you try to achieve.

Eventually, you will then be able to write a rule which target explicitly function calls (Subscription based visitor?), then look at the name of the function, and if it’s toUpperCase or toLowerCase, then you will be able to directly raise issue on the tree.

Regards,
Michael

1 Like

Hi Michael,

Thank you for your response.I have gone through all the documentation which you have referred and currently going through all the rules of SonarJS plugin which is available on git-hub but i am not able to find the kind of tree which i need to traverse for getting this kind of value. For instance:

  • abc.toUpperCase()
  • console.log(abc.toUpperCase())
  • foo.toUpperCase().length

Using tree.is(Kind.DOT_MEMBEREXPRESSION) (part of MemberExpressionTree) i am able to get abc.toUpperCase() and console.log(abc.toUpperCase()) but i am not able to pick foo.toUpperCase().length which has toUpperCase like *.toUpperCase().* in middle of the dots.

So any suggestion on this or is there any other way to target this kind of requirement?

Just one more info this kind of thing(abc.toUpperCase()) can be categorized into which kind even in terms of java as in JavaScript there are:

  • functiondefinition,
  • functiondeclaration,
  • functionexpression,
  • newfunction,
  • functiongenerator,
  • arrowfunction.

Thanks in Advance

Hi @vicky

Could you start a new thread? (as what you are asking is pretty far from this thread topic)
Please take your time to explain your approach and what worked/didn’t work

Regards, Elena