Sonar Java

repository·master·Indexed 22 days ago

https://github.com/sonarsource/sonar-java

A specialized code analyzer for Java projects providing deep insights into code quality and security. It features over 600 rules for bug detection and code smells, calculates metrics like cognitive complexity, and supports custom rule implementation via the SonarSource Analyzer for Java API. The project includes comprehensive testing suites such as Sanity, Ruling, and Autoscan tests, and requires JDK 21 or 26 depending on the module.

Tokens
10.2K
Snippets
30
Records
52
Agent score
79%

What's inside sonar-java

  1. Overview of Sonar Java Analyzer

    master

    Sonar Java is a code analyzer for Java projects designed to provide integrated code quality and security analysis.

    Key features include:

    • 600+ rules: Over 150 bug detection rules and 350+ code smells.
    • Metrics: Calculation of metrics such as cognitive complexity and number of lines.
    • Test Coverage: Support for importing test coverage reports.
    • Custom Rules: Ability to implement custom rules for specific project needs.
  2. Work with parameterized types and generics

    master

    When working with generics, be aware of the distinction between raw types and parameterized types.

    In recent versions, the semantic engine returns parameterized types with identity substitution. If you need to compare a type against its unparameterized (erased) version, use the .erasure() method on the Type object.

    Example of retrieving the erased type:

    @Rule(key = "MyFirstCustomRule")
    public class MyFirstCustomCheck extends IssuableSubscriptionVisitor {
    
        @Override
        public List<Kind> nodesToVisit() {
            return ImmutableList.of(Kind.METHOD);
        }
    
        @Override
        public void visitNode(Tree tree) {
            MethodTree method = (MethodTree) tree;
            MethodSymbol symbol = method.symbol();
            
            Type returnType = symbol.returnType().type();
            // For "MyClass<Integer> foo()", returnType is a ParametrizedTypeJavaType
        
            // Getting back the previous behavior (ClassJavaType):
            Type erasedType = returnType.erasure();
        }
    }
  3. Handle rule state in IssuableSubscriptionVisitor

    master

    While it is strongly advised to avoid stateful rules, if your rule requires cleaning up state, do not override scanFile() or scanTree() (these are deprecated and can cause memory leaks). Instead, use the following lifecycle methods:

    • setContext(): Called prior to the exploration of a file.
    • leaveFile(): Called after ending the exploration of a file.
  4. How Quality Profile JSON files are generated

    master

    The profile definitions are converted into JSON files during the Maven build process using the ProfileJsonGenerator. The generator reads the directory structure and produces JSON files at the following locations:

    • target/generated-resources/profiles/org/sonar/l10n/java/rules/java/Sonar_way_profile.json
    • target/generated-resources/profiles/org/sonar/l10n/java/rules/java/Sonar_agentic_AI_profile.json

    These generated files are automatically packaged into the plugin JAR.

  5. How IssuableSubscriptionVisitor works

    master

    IssuableSubscriptionVisitor is a class used to implement rules based on a subscription mechanism. It allows you to specify which types of nodes in the Syntax Tree the rule should react to.

    Key Methods

    • nodesToVisit(): Returns a List<Kind> specifying the node types (from org.sonar.plugins.java.api.tree.Tree.Kind) that the rule subscribes to.
    • visitNode(Tree tree): The method you override to implement the rule logic. It is called whenever the analyzer encounters a node of a kind returned by nodesToVisit().
    • reportIssue(Tree tree, String message): Used to report an issue on a specific tree node with a custom message.

    Implementation Pattern

    When overriding visitNode(Tree tree), you should cast the tree to the specific interface associated with the Kind you subscribed to (e.g., MethodTree for Kind.METHOD). If you subscribed to multiple kinds, use tree.is(Kind.X) before casting.

    Comparison with BaseTreeVisitor

    • IssuableSubscriptionVisitor: Best for quick, simple rules that only need to focus on specific node types.
    • BaseTreeVisitor: Best when you need fine-tuned control over the entire file traversal, as it provides a visit() method for every kind of syntax tree.
  6. Use the Semantic API to access type information

    master

    While the Syntax Tree provides the structure of the code, the Semantic Model provides information about the meaning of the code, such as types, owners, and usages. To access this, use the org.sonar.plugins.java.api.semantic package.

    Accessing Semantic Data

    1. Get the Symbol: From a syntax tree node (like MethodTree), call .symbol() to get its corresponding Symbol.
    2. Retrieve Types: Use the symbol to get type information. For example, a MethodSymbol can provide parameterTypes() and returnType().
    3. Compare Types: Use the Type.is(String fullyQualifiedName) method to check if two types are identical.

    Example: Comparing Method Parameter and Return Types

    @Override
    public void visitNode(Tree tree) {
      MethodTree method = (MethodTree) tree;
      if (method.parameters().size() == 1) {
        Symbol.MethodSymbol symbol = method.symbol();
        Type firstParameterType = symbol.parameterTypes().get(0);
        Type returnType = symbol.returnType().type();
        
        if (returnType.is(firstParameterType.fullyQualifiedName())) {
          reportIssue(method.simpleName(), "Never do that!");
        }
      }
    }
  7. Understand the license resource structure in the plugin JAR

    master

    The SonarJava plugin JAR includes a licenses/ directory containing .txt files for all used library licenses.

    Note: If a library provides its license in .html format, it must be overwritten with a .txt version to comply with the plugin's requirement that all licenses in the licenses/ folder be text files. The logic for identifying and overwriting these HTML files is managed within the plugin module's pom.xml during the build process.

  8. When to use java-checks-test-sources for rule samples

    master

    The java-checks-test-sources module is designed for rule test samples that must behave like regular Maven sources. Use this module instead of java-checks/src/test/files when a sample:

    • Needs the classpath or dependencies prepared by java-checks-test-sources.
    • Should be compiled as part of one of the dedicated test-source modules.
    • Is intentionally non-compiling and should live under src/main/files/non-compiling.
    • Targets a specific dedicated module such as default, java-17, spring-3.2, or spring-web-4.0.

    In java-checks tests, these files are typically loaded using:

    • TestUtils.mainCodeSourcesPath(...)
    • TestUtils.testCodeSourcesPath(...)
    • TestUtils.nonCompilingTestSourcesPath(...).

    Use java-checks/src/test/files only for fixtures that do not belong to these Maven modules, such as parser-only inputs or ad hoc verifier fixtures.

  9. Add a rule to a Quality Profile

    master

    To include a rule in a quality profile, create an empty file named exactly after the rule key within the desired profile directory.

    Example: To add rule S8910 to both the sonar_way and sonar_agentic_ai profiles, run:

    touch sonar-java-plugin/src/main/resources/profiles/sonar_way/S8910
    touch sonar-java-plugin/src/main/resources/profiles/sonar_agentic_ai/S8910
  10. Run the Autoscan Test

    master

    Autoscan tests detect differences in issues found with vs. without bytecode to identify false positives or false negatives.

    Step 1: Compile test sources Ensure java-checks-test-sources is compiled using Java 26:

    mvn clean compile

    Step 2: Execute autoscan Move to the its/autoscan folder and run using Java 21:

    # cd its/autoscan
    mvn clean package --batch-mode --errors --show-version \
       --activate-profiles it-autoscan \
      -Dsonar.runtimeVersion=LATEST_RELEASE

    Results are located in its/autoscan/target/actual. Compare java-checks-test-sources-mvn against java-checks-test-sources-no-binaries to analyze differences.