﻿# Development of a new static analyzer: PVS\-Studio Java

PVS\-Studio static analyzer is known in the C, C\+\+, and C\# worlds as a tool for detecting errors and potential vulnerabilities\. However, we have few clients from the financial sector, because it turned out that now Java and IBM RPG \(\!\) are highly demanded there\. As for us, we would like to be closer to the Enterprise world, so after some consideration we decided to start creating Java analyzer\. 

![0572_Java_analyzer/image1.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image1.png)

## Introduction

Sure, we had some concerns\. It is quite simple to carve out a niche of IBM RPG analyzers\. I am not even sure that there are decent tools for static analysis for this language\. In Java world, things are completely different\. There is already a range of tools for static analysis, and to get ahead, you need to create a really powerful and cool tool\.

Nevertheless, our company had experience of using several tools for static analysis of Java, and we are convinced that many things can be implemented better\. 

In addition, we had an idea how to tap the full power of our C\+\+ analyzer into the Java analyzer\. But first things first\.

## Tree

![0572_Java_analyzer/image2.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image2.png)

First, it was necessary to decide how we would get a syntax tree and semantic model\. 

Syntax tree is the base element around which the analyzer is built\. When running checks, the analyzer traverses the tree and reviews its separate nodes\. It is practically impossible to perform serious static analysis without such a tree\.  For example, a search for bugs using regular expressions is [futile](https://pvs-studio.com/en/blog/posts/cpp/0087/)\.

It also should be noted that only the syntax tree is not enough\. The analyzer requires semantic information as well\. For example, we need to know the types of all elements in the tree, be able to jump to a declaration of a variable, etc\.

We reviewed several options for obtaining syntax tree and semantic model:

* [ANTLR](https://www.antlr.org/) \(with grammar for Java\)
* [JavaParser and JavaSymbolSolver](https://javaparser.org/)
* [Eclipse's ASTParser from Eclipse JDT](https://www.eclipse.org/jdt/)
* [Spoon](http://spoon.gforge.inria.fr/)

We gave up the idea of using ANTLR almost at once; as it would unreasonably complicate the development of the analyzer \(semantic analysis would have been implemented on our own\)\.  Eventually we decided to settle on the Spoon library:

* It is not just a parser, but also a whole ecosystem, that provides not only a parsing tree, but also abilities for semantic analysis\. For example, it allows getting information about variables types, move to variable declaration, and get information about a parent class and so on\. 
* It is based on Eclipse JDT and can compile code\. 
* Supports the latest Java version and is constantly updated\.
* Presentable documentation and intuitive API\.

Here is an example of a metamodel that Spoon provides, and with which we work when creating diagnostic rules:

![0572_Java_analyzer/image3.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image3.png)

This metamodel corresponds to the following code:

```cpp
class TestClass
{
  void test(int a, int b)
  {
    int x = (a + b) * 4;
    System.out.println(x);
  }
}
```

One of the nice features of Spoon is that it simplifies the syntax tree \(by removing and adding nodes\) to make it easier to work with it\. With this, semantic equivalence of a simplified metamodel to a source metamodel is guaranteed\.

For us this means, for example, that we do not need to care about skipping the redundant parentheses when traversing the tree\. In addition, each expression is placed in the block, imports are expended, and some more similar simplifications are performed\.

For example, the following code:

```cpp
for (int i = ((0)); (i < 10); i++)
  if (cond)
    return (((42)));
```

will be formatted to the one as follows:

```cpp
for (int i = 0; i < 10; i++)
{
  if (cond)
  {
    return 42;
  }
}
```

Based on syntax tree, a pattern\-based analysis is performed\. This is a search for errors in the source code of a program by known code patterns containing an error\. In the simplest case, the analyzer performs a search in the tree for places, similar to an error, according to the rules described in the appropriate diagnostic\. The number of such patterns is large and their complexity can vary greatly\.

The simplest example of detectable error using pattern\-based analysis is the following code from a jMonkeyEngine project:

```cpp
if (p.isConnected()) {
    log.log(Level.FINE, "Connection closed:{0}.", p);
}
else {
    log.log(Level.FINE, "Connection closed:{0}.", p);
}
```

Blocks _then_ and _else_ of the operator _if_ fully coincide\. Most likely, there is a logic error\.

Here is another similar example from the Hive project:

```cpp
if (obj instanceof Number) {
  // widening conversion
  return ((Number) obj).doubleValue();
} else if (obj instanceof HiveDecimal) {     // <=
  return ((HiveDecimal) obj).doubleValue();
} else if (obj instanceof String) {
  return Double.valueOf(obj.toString());
} else if (obj instanceof Timestamp) {
  return new TimestampWritable((Timestamp)obj).getDouble();
} else if (obj instanceof HiveDecimal) {     // <=
  return ((HiveDecimal) obj).doubleValue();
} else if (obj instanceof BigDecimal) {
  return ((BigDecimal) obj).doubleValue();
}
```

In this code, there are two identical conditions in a sequence of type _if \(\.\.\.\.\) else  if \(\.\.\.\.\) else if \(\.\.\.\.\)_\. This code fragment is worth checking for logical error, or the duplicated code should be removed\. 

## Data\-flow analysis

In addition to the syntax tree and semantic model, the analyzer requires a mechanism for [data flow analysis](https://pvs-studio.com/en/blog/terms/7004/)\.

Data flow analysis enables you to calculate the possible values of variables and expressions in each point of the program and, thanks to that, find errors\.  We call these possible values 'virtual values'\.

Virtual values are created for variables, classes' fields, parameters of methods and other things at first reference\. If it is the assignment, Data Flow mechanism computes a virtual value by analyzing the expression at the right; otherwise, all the valid range of values for this variable type is taken as a virtual value\. For example:

```cpp
void func(byte x)  // x: [-128..127]
{
  int y = 5;       // y: [5]
  ...
}
```

At each change of a variable value, Data Flow mechanism recalculates the virtual value\. For example:

```cpp
void func()
{
  int x = 5; // x: [5]
  x += 7;    // x: [12]
  ...
}
```

Data Flow mechanism also handles control statements:

```cpp
void func(int x) // x: [-2147483648..2147483647]
{
  if (x > 3)
  {
    // x: [4..2147483647]
    if (x < 10)
    {
      // x: [4..9]
    }
  }
  else
  {
    // x: [-2147483648..3]
  }
  ...
}
```

In this example, when entering a function, there is no any information about a range of values of the variable _x_, so the range is set according to the type of a variable \(from\-2147483648 to 2147483647\)\. Then the first conditional block places a restriction _x_ \> 3 and ranges merge\. As a result, the range of values for _x _in _then_ block is as follows: from 4 up to 2147483647 and in the _else_ block is varies from \-2147483648 to 3\. The second condition _x_ < 10 is handled similarly\. 

Besides, there has to be the ability to perform purely symbolic computations\. The simplest example:

```cpp
void f1(int a, int b, int c)
{
  a = c;
  b = c;
  if (a == b)   // <= always true
    ....
}
```

Here the variable _a _is assigned a value _c_, the variable _b_ is also assigned the value _c_, then_ a_ and _b_ are compared\. In this case, to find an error, it is enough to just remember the fragment of the tree that corresponds to the right side\.

Here is a slightly more complicated example with symbolic computations:

```cpp
void f2(int a, int b, int c)
{
  if (a < b)
  {
    if (b < c)
    {
      if (c < a)   // <= always false
        ....
    }
  }
}
```

In such cases, we have to do with solving a system of inequalities in a symbolic form\.

Data Flow mechanism helps the analyzer to find errors that are quite difficult to detect using a pattern\-based analysis\.

Such errors include:

* Overflows;
* Array index out of bounds; 
* Access by null or potentially null reference;
* Pointless conditions \(always true/false\);
* Memory and resource leaks;
* Division by zero;
* And some others\.

Data Flow Analysis is especially important when searching for vulnerabilities\. For example, if a certain program receives input from a user, there is a chance that the input will be used to cause a denial of service, or to gain control over the system\. Examples may include errors leading to buffer overflows on some data inputs or, for example, SQL injections\. In both cases, you need to track data flow and possible values for variables, so that the static analyzer could be able to detect such errors and vulnerabilities\.  

I should say that the mechanism of Data flow analysis is a complex and extensive mechanism, but in this article, I touched only the basics of it\.

Let's see some examples of errors that can be detected using Data Flow mechanism\. 

Hive project:

```cpp
public static boolean equal(byte[] arg1, final int start1,
                            final int len1, byte[] arg2,
                            final int start2, final int len2) {
    if (len1 != len2) { // <=
      return false;
    }
    if (len1 == 0) {
      return true;
    }
    ....
    if (len1 == len2) { // <=
      ....
    }
}
```

The condition _len1_ _\=\=_ _len2_ is always executed, because the opposite check has already been executed above\.

Another example from the same project:

```cpp
if (instances != null) { // <=
  Set<String> oldKeys = new HashSet<>(instances.keySet());
  if (oldKeys.removeAll(latestKeys)) {
    ....
  }
  this.instances.keySet().removeAll(oldKeys);
  this.instances.putAll(freshInstances);
} else {
  this.instances.putAll(freshInstances); // <=
}
```

Here in the block _else _null pointer dereference occurs for sure\. Note: _instances _here is the same thing as _this\.instances_\.

An example from the JMonkeyEngine project:

```cpp
public static int convertNewtKey(short key) {
    ....
    if (key >= 0x10000) {
        return key - 0x10000;
    }
    return 0;
}
```

Here the variable _key_ is compared with the number 65536, however, it is of the type _short_, and the maximum possible value for _short_ is 32767\. Accordingly, the condition is never executed\.

One more example from the Jenkins project:

```cpp
public final R getSomeBuildWithWorkspace() 
{
    int cnt = 0;
    for (R b = getLastBuild();
         cnt < 5 && b ! = null;
         b = b.getPreviousBuild()) 
    {
        FilePath ws = b.getWorkspace();
        if (ws != null) return b;
    }
    return null;
}
```

In this code, the variable _cnt_ was introduced to limit the number of steps to five, but a developer forgot to increment it, which resulted in a useless check\.

## Annotations mechanism

In addition, the analyzer needs a mechanism of annotations\. Annotations are a markup system that provides the analyzer with extra information on the used methods and classes in addition to the data that can be obtained by the analysis of their signatures\. Markup is done manually; this is a long and time\-consuming process, because to achieve the best results one has to annotate a large number of standard Java classes and methods\. It also makes sense to perform annotation of popular libraries\. Overall, annotations can be regarded as a knowledge base of the analyzer about contracts of standard methods and classes\.

Here's a small sample of an error that can be detected by using annotations:

```cpp
int test(int a, int b) {
  ...
  return Math.max(a, a);
}
```

In this example, the same variable \(passed as a first argument\) was passed as the second argument of the method _Math\.max_ because of a typo\. Such an expression is meaningless and suspicious\.

Static analyzer may issue a warning for such code, as it's aware of the fact that arguments of the method _Math\.max_ always have to be different\.

Going forward, here are a few examples of our markup of built\-in classes and methods \(code in C\+\+\):

```cpp
Class("java.lang.Math")
  - Function("abs", Type::Int32)
      .Pure()
      .Set(FunctionClassification::NoDiscard)
      .Returns(Arg1, [](const Int &v) 
      { return v.Abs(); })

  - Function("max", Type::Int32, Type::Int32)
      .Pure()
      .Set(FunctionClassification::NoDiscard)
      .Requires(NotEquals(Arg1, Arg2)
      .Returns(Arg1, Arg2, [](const Int &v1, const Int &v2)
      { return v1.Max(v2); })

Class("java.lang.String", TypeClassification::String)
  - Function("split", Type::Pointer)
      .Pure()
      .Set(FunctionClassification::NoDiscard)
      .Requires(NotNull(Arg1))
      .Returns(Ptr(NotNullPointer))

Class("java.lang.Object")
    - Function("equals", Type::Pointer)
        .Pure()
        .Set(FunctionClassification::NoDiscard)
        .Requires(NotEquals(This, Arg1))

Class("java.lang.System")
  - Function("exit", Type::Int32)
      .Set(FunctionClassification::NoReturn)
```

Explanations:

* _Class_ is a class being annotated;
* _Function_ is a method of the annotated class;
* _Pure_ is the annotation, indicating that a method is pure, i\.e\. deterministic and does not have side effects;
* _Set_ is a set of an arbitrary flag for the method\.
* _FunctionClassification::NoDiscard_ is a flag indicating that the return value of the method must be used;
* _FunctionClassification::NoReturn_ is a flag that indicates that the method does not return control;
* _Arg1_, _Arg2_, _\.\.\._, _ArgN_ \- method arguments;
* _Returns_ is the return value of the method;
* _Requires_ is a contract for a method\.

It is worth noting that, in addition to manual markup, there is another approach to annotating, which is an automatic inference of contracts based on bytecode\. It is clear that such an approach allows to obtain only certain types of contracts, but, however, it enables to receive additional information from all dependencies, not just from those that were annotated manually\.

By the way, there is already a tool that is able to infer the contracts like _@Nullable_, _@NotNull_ based on bytecode \- [FABA](https://github.com/ilya-klyuchnikov/faba)\. As far as I understand, the derivative of the FABA is used in IntelliJ IDEA\.

At the moment, we are also considering the ability to add the bytecode analysis for obtaining contracts of all methods, as these contracts could well complement our manual annotations\.

Diagnostic rules often refer to the annotations\. In addition to diagnostics, annotations are used by the Data Flow mechanism\. For example, using the annotation method _java\.lang\.Math\.abs_, it can accurately calculate the absolute value of a number\. And we don't have to write any additional code for that, we only need to correctly annotate a method\.

Let's consider the example of an error which can be found due to annotations from the Hibernate project:

```cpp
public boolean equals(Object other) {
    if (other instanceof Id) {
        Id that = (Id) other;
        return purchaseSequence.equals(this.purchaseSequence) &&
            that.purchaseNumber == this.purchaseNumber;
    }
    else {
        return false;
    }
}
```

In this code, the method _equals\(\)_ compares the object _purchaseSequence_ with itself\. Most likely, this is a typo and _that\.purchaseSequence _should be written on the right, but not _purchaseSequence_\.

## How Dr\. Frankenstein assembled the analyzer from pieces

![0572_Java_analyzer/image4.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image4.png)

Since Data Flow and annotation mechanisms themselves are not strongly tied to a specific language, it was decided to re\-use these mechanisms from our C\+\+ analyzer\. This lets us obtain the whole power of the C\+\+ analyzer in our Java analyzer within a short time\. In addition, this decision was also influenced by the fact that these mechanisms were written in modern C\+\+ with a bunch of metaprogramming and template magic, and therefore these solutions are not very suitable for porting into another language\.

In order to connect the Java part with the C\+\+ kernel, we decided to use [SWIG \(Simplified Wrapper and Interface Generator\)](http://www.swig.org/) which is a tool for automatic generation of wrappers and interfaces for bounding C and C\+\+ programs, with programs, written in other languages\. SWIG generates code in [JNI \(Java Native Interface\)](https://en.wikipedia.org/wiki/Java_Native_Interface) for Java\. 

SWIG is great for cases when there is already a large amount of C\+\+ code that needs to be integrated in a Java project\.

Let me give you a small example of working with SWIG\. Let's suppose we have a C\+\+ class that we want to use in a Java project:

CoolClass\.h

```cpp
class CoolClass
{
public:
  int val;
  CoolClass(int val);
  void printMe();
};
```

CoolClass\.cpp

```cpp
#include <iostream>
#include "CoolClass.h"

CoolClass::CoolClass(int v) : val(v) {}

void CoolClass::printMe()
{
  std::cout << "val: " << val << '\n';
}
```

You must first create a [SWIG interface file](http://www.swig.org/Doc3.0/SWIG.html#SWIG_nn47) with a description of all the exported functions and classes\. Also, perform additional settings in this file, if necessary\.

Example\.i

```cpp
%module MyModule
%{
#include "CoolClass.h"
%}
%include "CoolClass.h"
```

After that, you can run SWIG:

```cpp
$ swig -c++ -java Example.i
```

It will generate the following files: 

* CoolClass\.java is a class, with which we will work directly in a Java project;
* MyModule\.java is a module class that contains all free functions and variables;
* MyModuleJNI\.java \- Java wrappers;
* Example\_wrap\.cxx \- C\+\+ wrappers\. 

Now you just need to add the resultant \.java files in the Java project and the \.cxx file in the C\+\+ project\.

Finally, you need to compile the C\+\+ project as a DLL and load it in the Java project using _System\.loadLibary\(\)_:

App\.java

```cpp
class App {
  static {
    System.loadLibary("example");
  }
  public static void main(String[] args) {
    CoolClass obj = new CoolClass(42);
    obj.printMe();
  }
}
```

Schematically, this can be represented as follows:

![0572_Java_analyzer/image5.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image5.png)

Sure, in a real project, things are not that simple and you have to upscale efforts:

* In order to use template classes and methods from C\+\+, you must instantiate them for all template parameters by using the directive _%template_;
* In some cases, you may need to catch exceptions that are thrown from the C\+\+ part in the Java part\. By default, SWIG doesn't catch exceptions from C\+\+ \(segfault occurs\), however, it is possible to do this using the directive _%exception_;
* SWIG allows extending the C\+\+ code on the Java side, using the directive _%extend_\. As for us, in our project we add the method _toString\(\)_ to virtual values to see them in the Java debugger;
* In order to emulate the RAII behavior from C\+\+, the interface _AutoClosable_ is implemented\.
* Directors mechanism allows using a cross\-language polymorphism;
* For types that are allocated only inside C\+\+ \(in its own memory pool\), constructors and finalizers are removed to improve performance\. The garbage collector will ignore these types\.

You can learn more about all of these mechanisms in the [SWIG documentation](http://www.swig.org/Doc3.0/SWIGDocumentation.html)\.

Our analyzer is built using gradle, which calls CMake, which, in turns, calls SWIG and builds the C\+\+ part\. For programmers it happens almost imperceptibly, so we experience no particular inconvenience when developing\.

The core of our C\+\+ analyzer is built under Windows, Linux and macOS, so the Java analyzer also works in these operating systems\.

## What is a diagnostic rule?

We write diagnostics themselves and code for analysis in Java\. It is stemmed from the close interaction with the Spoon\. Each diagnostic rule represents a visitor with overloaded methods in which the elements interesting for us are traversed:

![0572_Java_analyzer/image6.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image6.png)

For example, this is how a V6004 diagnostic frame looks like:

```cpp
class V6004 extends PvsStudioRule
{
  ....
  @Override
  public void visitCtIf(CtIf ifElement)
  {
    // if ifElement.thenStatement statement is equivalent to
    //    ifElement.elseStatement statement => add warning V6004
  }
}
```

## Plugins

For simple static analyzer integration in the project, we've developed plugins for build systems Maven and Gradle\. A user just needs to add our plugin to the project\.

For Gradle:

```cpp
....
apply plugin: com.pvsstudio.PvsStudioGradlePlugin
pvsstudio {
    outputFile = 'path/to/output.json'
    ....
}
```

For Maven:

```cpp
....
<plugin>
    <groupId>com.pvsstudio</groupId>
    <artifactId>pvsstudio-maven-plugin</artifactId>
    <version>0.1</version>
    <configuration>
        <analyzer>
            <outputFile>path/to/output.json</outputFile>
            ....
        </analyzer>
    </configuration>
</plugin>
```

After that, the plugin itself will receive the project structure and start the analysis\.

In addition, we have developed a plugin prototype for IntelliJ IDEA\.

![0572_Java_analyzer/image7.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image7.png)

In addition, this plugin works in Android Studio\.

Plugin for Eclipse is now under development\.

## Incremental analysis

We have provided the incremental analysis mode that allows checking only modified files, which significantly reduces the time for analysis\. Thanks to that, developers will be able to run the analysis as often as necessary\.

Incremental analysis involves several stages:

* Caching of the Spoon metamodel;
* Rebuilding of the modified part of the metamodel;
* Analysis of the changed files\.

## Our testing system

To test the Java analyzer on real projects we wrote special tools to work with the database of open source projects\. It was written in Python \+ Tkinter and is cross\-platform\.

It works in the following way:

* The tested project of a certain version is loaded from a repository on GitHub;
* The project is built;
* Our plugin is added in _pom\.xml _or _build\.gradle_ \(using git apply\);
* Static analyzer is started using the plugin;
* The resulting report is compared with the etalon for this project\.

Such approach ensures that good warnings will not disappear because of changes in the analyzer code\. The following illustration shows the interface of our utility for testing\.

![0572_Java_analyzer/image8.png](https://import.viva64.com/docx/blog/0572_Java_analyzer/image8.png)

Red highlights are the projects, whose reports have differences with the etalon\. The Approve button allows saving the current version of the report as an etalon\. 

## Examples of errors

By tradition, I will demonstrate several errors from different open source projects that our Java analyzer has detected\. In the future, we plan to write articles with a more detailed report on each project\.

### Hibernate Project

**PVS\-Studio** **warning:** V6009 Function 'equals' receives odd arguments\. Inspect arguments: this, 1\. PurchaseRecord\.java 57

```cpp
public boolean equals(Object other) {
    if (other instanceof Id) {
        Id that = (Id) other;
        return purchaseSequence.equals(this.purchaseSequence) &&
            that.purchaseNumber == this.purchaseNumber;
    }
    else {
        return false;
    }
}
```

In this code, the method _equals\(\)_ compares the object _purchaseSequence_ with itself\. Most likely, this is a typo and _that\.purchaseSequence, _not _purchaseSequence_ should be written on the right\.

**PVS\-Studio** **warning:** V6009 Function 'equals' receives odd arguments\. Inspect arguments: this, 1\. ListHashcodeChangeTest\.java 232

```cpp
public void removeBook(String title) {
    for( Iterator<Book> it = books.iterator(); it.hasNext(); ) {
        Book book = it.next();
        if ( title.equals( title ) ) {
            it.remove();
        }
    }
}
```

A triggering, similar to the previous one \- _book\.title, _not_ title_ has to be on the right\. 

### Hive project

**PVS\-Studio** **warning:** V6007 Expression 'colOrScalar1\.equals\("Column"\)' is always false\. GenVectorCode\.java 2768

**PVS\-Studio** **warning:** V6007 Expression 'colOrScalar1\.equals\("Scalar"\)' is always false\. GenVectorCode\.java 2774

**PVS\-Studio** **warning:** V6007 Expression 'colOrScalar1\.equals\("Column"\)' is always false\. GenVectorCode\.java 2785

```cpp
String colOrScalar1 = tdesc[4];
....
if (colOrScalar1.equals("Col") &&
    colOrScalar1.equals("Column")) {
    ....
} else if (colOrScalar1.equals("Col") &&
           colOrScalar1.equals("Scalar")) {
    ....
} else if (colOrScalar1.equals("Scalar") &&
           colOrScalar1.equals("Column")) {
    ....
}
```

Here the operators were obviously confused and '_&&' _was used instead of '_\|\|'\._

### JavaParser project

**PVS\-Studio** **warning:** V6001 There are identical sub\-expressions 'tokenRange\.getBegin\(\)\.getRange\(\)\.isPresent\(\)' to the left and to the right of the '&&' operator\. Node\.java 213

```cpp
public Node setTokenRange(TokenRange tokenRange)
{
  this.tokenRange = tokenRange;
  if (tokenRange == null || 
      !(tokenRange.getBegin().getRange().isPresent() &&
        tokenRange.getBegin().getRange().isPresent()))
  {
    range = null;
  }
  else 
  {
    range = new Range(
        tokenRange.getBegin().getRange().get().begin,
        tokenRange.getEnd().getRange().get().end);
  }
  return this;
}
```

The analyzer has detected that on the left and the right of the operator _&&_ there are identical expressions \(besides that, all methods in the chain are pure\)\. Most likely, in the second case, _tokenRange\.getEnd\(\) _has to be used rather than _tokenRange\.getBegin\(\)_\.

**PVS\-Studio** **warning:** V6016 Suspicious access to element of 'typeDeclaration\.getTypeParameters\(\)' object by a constant index inside a loop\. ResolvedReferenceType\.java 265

```cpp
if (!isRawType()) {
  for (int i=0; i<typeDeclaration.getTypeParams().size(); i++) {
    typeParametersMap.add(
       new Pair<>(typeDeclaration.getTypeParams().get(0),
                  typeParametersValues().get(i)));
    }
}
```

The analyzer has detected a suspicious access to the element of a collection by constant index inside the loop\. Perhaps there is an error in this code\.

### Jenkins project

**PVS\-Studio** **warning:** V6007 Expression 'cnt < 5' is always true\. AbstractProject\.java 557

```cpp
public final R getSomeBuildWithWorkspace() 
{
    int cnt = 0;
    for (R b = getLastBuild();
         cnt < 5 && b ! = null;
         b = b.getPreviousBuild()) 
    {
        FilePath ws = b.getWorkspace();
        if (ws != null) return b;
    }
    return null;
}
```

In this code, the variable _cnt_ was introduced to limit the number of traverses to five, but a developer forgot to increment it, which resulted in a useless check\.

### Spark Project

**PVS\-Studio** **warning:** V6007 Expression 'sparkApplications \!\= null' is always true\. SparkFilter\.java 127

```cpp
if (StringUtils.isNotBlank(applications)) 
{
  final String[] sparkApplications = applications.split(",");

  if (sparkApplications != null && sparkApplications.length > 0)
  {
    ...
  }
}
```

Check of the result, returned by the _split _method_, _for _null_ is meaningless, because this method always returns a collection and never returns _null_\.

### Spoon project

**PVS\-Studio** **warning:** V6001 There are identical sub\-expressions '\!m\.getSimpleName\(\)\.startsWith\("set"\)' to the left and to the right of the '&&' operator\. SpoonTestHelpers\.java 108

```cpp
if (!m.getSimpleName().startsWith("set") &&
    !m.getSimpleName().startsWith("set")) {
    continue;
}
```

In this code, there are identical expressions on the left and right of the && operator \(in addition to that, all methods in the chain are pure\)\. Most likely, there is a logic error in the code\.

**PVS\-Studio** **warning:** V6007 Expression 'idxOfScopeBoundTypeParam \>\= 0' is always true\. MethodTypingContext\.java 243

```cpp
private boolean
isSameMethodFormalTypeParameter(....) {
  ....
  int idxOfScopeBoundTypeParam = getIndexOfTypeParam(....);
  if (idxOfScopeBoundTypeParam >= 0) { // <=
    int idxOfSuperBoundTypeParam = getIndexOfTypeParam(....);
    if (idxOfScopeBoundTypeParam >= 0) { // <=
      return idxOfScopeBoundTypeParam == idxOfSuperBoundTypeParam;
    }
  }
  ....
}
```

Here the author of the code made a typo and wrote _idxOfScopeBoundTypeParam _instead of_ idxOfSuperBoundTypeParam\._



### Spring Security project

**PVS\-Studio** **warning:** V6001 There are identical sub\-expressions to the left and to the right of the '\|\|' operator\. Check lines: 38, 39\. AnyRequestMatcher\.java 38

```cpp
@Override
@SuppressWarnings("deprecation")
public boolean equals(Object obj) {
    return obj instanceof AnyRequestMatcher ||
    obj instanceof security.web.util.matcher.AnyRequestMatcher;
}
```

The triggering is similar to the previous one \- the name of the same class is written in different ways\. 

**PVS\-Studio** **warning:** V6006 The object was created but it is not being used\. The 'throw' keyword could be missing\. DigestAuthenticationFilter\.java 434

```cpp
if (!expectedNonceSignature.equals(nonceTokens[1])) {
  new BadCredentialsException(
       DigestAuthenticationFilter.this.messages
       .getMessage("DigestAuthenticationFilter.nonceCompromised",
                    new Object[] { nonceAsPlainText },
                    "Nonce token compromised {0}"));
}
```

In this code, a developer forgot to add the _throw_ before the exception\. As a result, the object of the exception _BadCredentialsException_ is created, but is not used, i\.e\., no exception is thrown\.

**PVS\-Studio** **warning:** V6030 The method located to the right of the '\|' operators will be called regardless of the value of the left operand\. Perhaps, it is better to use '\|\|'\. RedirectUrlBuilder\.java 38

```cpp
public void setScheme(String scheme) {
    if (!("http".equals(scheme) | "https".equals(scheme))) {
        throw new IllegalArgumentException("...");
    }
    this.scheme = scheme;
}
```

In this code, the usage of the operator _\|_ is undue, because when the right part will be calculated, even if the left part is already true\. In this case, it has no practical meaning, so the operator _\|_ has to be replaced with _\|\|_\.

### IntelliJ IDEA Project

**PVS\-Studio** **warning:** V6008 Potential null dereference of 'editor'\. IntroduceVariableBase\.java:609

```cpp
final PsiElement nameSuggestionContext = 
  editor == null ? null : file.findElementAt(...);  // <=
final RefactoringSupportProvider supportProvider =
  LanguageRefactoringSupport.INSTANCE.forLanguage(...);
final boolean isInplaceAvailableOnDataContext =
  supportProvider != null &&
  editor.getSettings().isVariableInplaceRenameEnabled() &&  // <=
...
```

The analyzer has detected that in this code a dereference of a null pointer _editor_ may occur\.  It is necessary to add additional check\.

**PVS\-Studio** **warning:** V6007 Expression is always false\. RefResolveServiceImpl\.java:814

```cpp
@Override
public boolean contains(@NotNull VirtualFile file) {
    ....
    return false & !myProjectFileIndex.isUnderSourceRootOfType(....);
}
```

It is difficult for me to say what the author had in mind, but this looks very suspicious\. Even if there is no error here, this place should be rewritten to not to confuse the analyzer and other programmers\. 

**PVS\-Studio** **warning:** V6007 Expression 'result\[0\]' is always false\. CopyClassesHandler\.java:298

```cpp
final boolean[] result = new boolean[] {false};   // <=
Runnable command = () -> {
  PsiDirectory target;
  if (targetDirectory instanceof PsiDirectory) {
    target = (PsiDirectory)targetDirectory;
  } else {
    target = WriteAction.compute(() -> 
      ((MoveDestination)targetDirectory).getTargetDirectory(
          defaultTargetDirectory));
  }
  try {
    Collection<PsiFile> files = 
        doCopyClasses(classes, map, copyClassName, target, project);
    if (files != null) {
      if (openInEditor) {
        for (PsiFile file : files) {
          CopyHandler.updateSelectionInActiveProjectView(
              file,
              project,
              selectInActivePanel);
        }
        EditorHelper.openFilesInEditor(
            files.toArray(PsiFile.EMPTY_ARRAY));
      }
    }
  }
  catch (IncorrectOperationException ex) {
    Messages.showMessageDialog(project,
        ex.getMessage(),
        RefactoringBundle.message("error.title"),
        Messages.getErrorIcon());
  }
};
CommandProcessor processor = CommandProcessor.getInstance();
processor.executeCommand(project, command, commandName, null);

if (result[0]) {   // <=
  ToolWindowManager.getInstance(project).invokeLater(() -> 
      ToolWindowManager.getInstance(project)
          .activateEditorComponent());
}
```

I suspect that here one forgot to somehow change the value in _result_\. Because of this, the analyzer reports that the check _if \(result\[0\]\)_ is meaningless\.

## Conclusion

Java development is very versatile\. It includes desktop, android, web and much more, so we have plenty of room for activities\. First and foremost, of course, we will develop the areas that are most in demand\.

Here are our plans for the near future:

* The inference of annotations from bytecode;
* Integration into Ant projects \(does anybody still use it in 2018?\);
* Plugin for Eclipse \(currently in the development process\);
* More diagnostics and annotations;
* Improvement of Data Flow\.

Also I'd like to suggest readers taking part in testing alfa\-version of our Java analyzer when it becomes available\. For this, write to our [support](https://pvs-studio.com/en/about-feedback/)\. We'll add your contacts to our list and will write you, when the first alfa\-version is ready\.