SonarQube quality rules for newer Java versions - An Overview
Muhammad Usman
Software Engineer - Creating Value, Building Trust, and Solving Problems
Quality check is essentially a crucial step in the product development life cycle for every industry. It is considered necessary to meet all the quality checks that have been defined for a specific product. If any of the checks are being missed or the product is violating any rule you stop the product from the production line.
Software products are built or written in coding languages; these languages are considered raw material for modern software product development. SonarQube is a static code analyzer that analyses the quality of software product (code) before it deploys to production.?
SonarQube defines the standards/rules for each language. Usually, these quality checks are imposed through the CI/CD pipeline of software development, if the software is found violating any of the roles defined by sonar it stops the product from CI/CD pipeline (product line).
Sonar Qube is one of the crucial quality standards that can be configured on your local environment, IDE, or local sonar server.
SonarQube is evolving as underlying languages are evolved. After major releases of Java 8, java11, and Java 13, new rules have been added by sonarQube, so before implementing sonarQube with your code you need to learn about those rules.?
The SonarQube Quality Model divides rules into three categories: Bugs, Vulnerabilities, and Code Smells.?
Code Smell?
A maintainability issue that makes your code confusing and difficult to maintain.??
For example, Java 11 (Java LTS release) introduced local variable type inference that should be used to declare a variable when the variable type is obvious or could be inferred by reading the expression.?So Instead using and declaring local variable with var, explicit type declaration can be a code smell.
Bug?
A coding mistake can lead to an error or unexpected behavior at runtime. for example, a rule Consumed Stream pipelines should not be reused i.e. once the terminal operation has been performed on stream it considered as consumed, so consumed stream should not be reused.??
A stream implementation may throw IllegalStateException if it detects that the stream is being reused.?
Vulnerability?
A point in your code that's open to attack.?
For example, your program can be vulnerable if you are constructing DB queries through user-provided input parameters without sanitizing the input, It can read, modify, or delete sensitive information from the database and sometimes even shut it down or execute arbitrary operating system commands.?
Each issue type is further divided into five severities, Blocker, Critical, Major, Minor, and Info.?
I will cover a few of the latest sonar roles that have been added after java 8 and onward releases. Each rule will be described as follows.
Rule Number Rule Description [Rule Type, Severity of Rule Type]
RSPEC-6212 Local-Variable Type Inference should be used [Code Smell, Info]
Local-Variable Type Inference aka var is the new addition in the Java language after Java 11. var can be used in a local variable declaration instead of the variable’s type. With var, the Java compiler infers the type of the variable at compile-time, using type information obtained from the variable’s initializer. The inferred type is then used as the static type of the variable.
This rule reports an issue when
Noncompliant Code Example
MyClass myClass = new MyClass(
int i = 10; // Type is self-explanatory
MyClass something = MyClass.getMyClass(); // Type is already mentioned in the initializer
MyClass myClass = get(); // Type is already mentioned in the name;)
Compliant Solution
领英推荐
var myClass = new MyClass()
var i = 10;
var something = MyClass.getMyClass();
var myClass = get();;
RSPEC-3958 Intermediate Stream methods should not be left unused [Bug, Major]?
There are two types of stream operations; intermediate operations and terminal operations. filter, map, and flatMap are intermediate operations that return another stream, Intermediate operations are lazy meaning without terminal operations stream serves no purpose. Terminal operations count, collect, find, etc return the concrete result.?
This rule reports an issue when the stream is being used in code without any terminal operations performed.
Noncompliant Code Example
widgets.stream().filter(b -> b.getColor() == RED); // Noncompliant
Compliant Solution
int sum = widgets.stream(
.filter(b -> b.getColor() == RED)
.mapToInt(b -> b.getWeight())
.sum();
Stream<Widget> pipeline = widgets.stream()
.filter(b -> b.getColor() == GREEN)
.mapToInt(b -> b.getWeight());
sum = pipeline.sum();)
RSPEC-3959 Consumed Stream pipelines should not be reused [Bug, Major]?
A stream pipeline consists of a stream source, followed by zero or more intermediate operations, and a terminal operation. Once the terminal operation is called, the stream pipeline is considered consumed.
This rule reports a problem when you try to reuse streams. Reusing the consumed stream can cause unexpected results. A stream implementation may throw IllegalStateException if it detects that the stream is being reused.?
Noncompliant Code Example
Stream<Widget> pipeline = widgets.stream().filter(b -> b.getColor() == RED)
int sum1 = pipeline.sum();
int sum2 = pipeline.mapToInt(b -> b.getWeight()).sum(); // Noncompliant;
RSPEC-3631 "Arrays.stream" should be used for primitive arrays [Code Smell, Major]?
Arrays.asList(T ... a).stream() and Arrays.stream(array) are utility methods of java.util.Arrays class.
Arrays.asList will force the construction of a list of boxed types, and then use that list as a stream. Arrays.stream uses the appropriate primitive stream type (IntStream, LongStream, DoubleStream)?
Noncompliant Code Example
Arrays.asList("a1", "a2", "b1", "c2", "c1").stream(
.filter(...)
.forEach(...);
Arrays.asList(1, 2, 3, 4).stream() // Noncompliant
.filter(...)
.forEach(...);)
Compliant solution
Arrays.asList("a1", "a2", "b1", "c2", "c1").stream(
.filter(...)
.forEach(...);
int[] intArray = new int[]{1, 2, 3, 4};
Arrays.stream(intArray)
.filter(...)
.forEach(...);)
RSPEC-2203 "collect" should be used with "Streams" instead of "list::add" [Code Smell, Minor]?
Streams provide a declarative style of coding. Code written in streams is more readable and understandable as compared to the imperative style of coding. Mixing streams with non-stream operations adds confusion and you missed the advantages the stream provides.
Collect is a terminal operation that is automatically thread-safe and parallelizable.?
Noncompliant Code Example
List<String> bookNames = new ArrayList<>()
books.stream().filter(book -> book.getIsbn().startsWith("0"))
.map(Book::getTitle)
.forEach(bookNames::add); // Noncompliant;
Compliant solution
List<String> bookNames = books.stream().filter(book -> book.getIsbn().startsWith("0")
.map(Book::getTitle)
.collect(Collectors.toList());)