Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
9b60268
SONARJAVA-4368 Wip upgrade of ECJ to 3.31.0
dorian-burihabwa-sonarsource Nov 29, 2022
9f368e0
SONARJAVA-4368 Restore old guarded pattern syntax in SE test
dorian-burihabwa-sonarsource Nov 29, 2022
11c9361
SONARJAVA-4368 Remove TODO comments
dorian-burihabwa-sonarsource Nov 29, 2022
61dc5d0
SONARJAVA-4368 setup local libraries to temporariy store eclipse snap…
Wohops Nov 30, 2022
f16a1dd
SONARJAVA-4368 add eclipse.jdt.core
Wohops Nov 30, 2022
5c5f586
SONARJAVA-4368 add all missing poms with version 4.26.0-SNAPSHOT
Wohops Nov 30, 2022
8c6210b
SONARJAVA-4368 drop useless files
Wohops Nov 30, 2022
cd17163
SONARJAVA-4368 drop more useless files
Wohops Nov 30, 2022
b2f8734
Replace '&&' usage by 'when'
chrislain-razafimahefa-sonarsource Dec 1, 2022
9128774
SONARJAVA-4368 WIP add recordpattern type to public API
dorian-burihabwa-sonarsource Dec 1, 2022
e732ff7
Start adding RecordPattern
chrislain-razafimahefa-sonarsource Dec 1, 2022
a308248
[NOT WORKING] Add test
chrislain-razafimahefa-sonarsource Dec 1, 2022
d2c356a
SONARJAVA-4368 Drop again more useless files
Wohops Dec 1, 2022
316e14f
SONARJAVA-4368 Fixes from review
dorian-burihabwa-sonarsource Dec 5, 2022
fb202f3
SONARJAVA-4368 Fixes from review 2: electric boogaloo
dorian-burihabwa-sonarsource Dec 5, 2022
f4f4a28
Add annotations
Wohops Dec 6, 2022
9998e2f
SONARJAVA-4368 Workaround CFG and SE to support patterns
Wohops Dec 6, 2022
eb49b00
SONARJAVA-4368 Fix UnusedLocalVariableCheck
Wohops Dec 6, 2022
cabe978
SONARJAVA-4368 Fix QuickFixHelper
Wohops Dec 7, 2022
50a0818
SONARJAVA-4368 Fix unit tests
Wohops Dec 7, 2022
a58f9cd
SONARJAVA-4368 Fix coverage
Wohops Dec 7, 2022
09314c8
SONARJAVA-4368 Drop local repository and rely on released artifact
Wohops Dec 7, 2022
ca4bf64
SONARJAVA-4368 Document public API changes
dorian-burihabwa-sonarsource Dec 7, 2022
6192e21
SONARJAVA-4368 Fix broken sentence in new API documentation
dorian-burihabwa-sonarsource Dec 7, 2022
ad7c2fb
Fix dependencies and comments
Wohops Dec 8, 2022
edc7a60
Fix typo in documentation
Wohops Dec 8, 2022
11aa3a2
Fix inconsistency in java versions handling
Wohops Dec 8, 2022
b7f1b27
Fix documentation
Wohops Dec 8, 2022
1a94f0a
Fix SanityCheck
Wohops Dec 8, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -250,21 +250,6 @@ public void instanceOf() {
}
}

public void instanceOfPatternMathing() {
Object object = new Object();
// Java 16 pattern matching instance of
if (object instanceof String s) { // Compliant
} else if (object instanceof Integer i) { // Compliant
}

if (object instanceof String str) {
// str inherits the contraints from objects
if (str == null) { // Noncompliant {{Change this condition so that it does not always evaluate to "false"}}

}
}
}

public void literals() {
// literals
if (!true) { // Noncompliant {{Change this condition so that it does not always evaluate to "false"}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ static String switch_sealed_class_null_default_sub_classes(Shape shape) {
return switch (shape) {
case null -> "null case";
case Triangle t -> String.format("triangle (%d,%d,%d)", t.a(), t.b(), t.c());
case Rectangle r && r.volume() > 42 -> String.format("big rectangle of volume %d!", r.volume());
case Rectangle r when r.volume() > 42 -> String.format("big rectangle of volume %d!", r.volume());
case Square s -> "Square!";
case Rectangle r -> String.format("Rectangle (%d,%d)", r.base, r.height);
default -> "default case";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package symbolicexecution.checks;

public class ConditionAlwaysTrueOrFalseCheckWithPattern {

public void instanceOfPatternMathing() {
Object object = new Object();
// Java 16 pattern matching instance of
if (object instanceof String s) { // Compliant
} else if (object instanceof Integer i) { // Compliant
}

if (object instanceof String str) {
// str inherits the contraints from objects
if (str == null) { // Noncompliant {{Change this condition so that it does not always evaluate to "false"}}
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@
import javax.annotation.Nullable;
import org.sonar.java.annotations.Beta;
import org.sonar.java.annotations.VisibleForTesting;
import org.sonarsource.analyzer.commons.collections.ListUtils;
import org.sonar.java.model.DefaultJavaFileScannerContext;
import org.sonar.java.model.JavaTree;
import org.sonar.java.reporting.InternalJavaIssueBuilder;
Expand All @@ -47,6 +46,7 @@
import org.sonar.plugins.java.api.tree.SyntaxToken;
import org.sonar.plugins.java.api.tree.Tree;
import org.sonar.plugins.java.api.tree.VariableTree;
import org.sonarsource.analyzer.commons.collections.ListUtils;


/**
Expand Down Expand Up @@ -172,7 +172,7 @@ private static List<? extends Tree> getSiblings(VariableTree current) {
case CATCH:
case LAMBDA_EXPRESSION:
case FOR_EACH_STATEMENT:
case PATTERN_INSTANCE_OF:
case TYPE_PATTERN:
return Collections.emptyList();
case CLASS:
case ENUM:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,6 @@
import java.util.Collections;
import java.util.List;
import java.util.Optional;

import org.sonar.check.Rule;
import org.sonar.java.checks.helpers.QuickFixHelper;
import org.sonar.java.checks.helpers.UnresolvedIdentifiersVisitor;
Expand Down Expand Up @@ -168,7 +167,7 @@ private static Optional<AnalyzerMessage.TextSpan> getQuickFixTextSpan(VariableTr
// If the variable is last in the list, we need to retrieve the preceding comma
SyntaxToken precedingComma = variables.get(variables.indexOf(variable) - 1).lastToken();
return Optional.of(AnalyzerMessage.textSpanBetween(precedingComma, lastToken));
} else if (parent.is(Tree.Kind.PATTERN_INSTANCE_OF)) {
} else if (parent.is(Tree.Kind.TYPE_PATTERN)) {
return Optional.of(AnalyzerMessage.textSpanFor(lastToken));
}
return Optional.empty();
Expand Down
14 changes: 9 additions & 5 deletions java-checks/src/test/java/org/sonar/java/checks/SanityTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
import org.sonar.java.model.JavaVersionImpl;
import org.sonar.java.testing.VisitorsBridgeForTests;
import org.sonar.plugins.java.api.JavaCheck;
import org.sonar.plugins.java.api.JavaVersion;

import static java.nio.charset.StandardCharsets.UTF_8;
import static org.assertj.core.api.Assertions.assertThat;
Expand Down Expand Up @@ -131,7 +132,7 @@ void test() throws Exception {
.filter(SanityTest::isTypeResolutionError)
.collect(Collectors.toList());

assertThat(errorLogs).hasSize(24);
assertThat(errorLogs).hasSize(26);

List<LogAndArguments> remainingErrors = new ArrayList<>(errorLogs);
remainingErrors.removeAll(parsingErrors);
Expand Down Expand Up @@ -162,15 +163,17 @@ void test() throws Exception {
"play.mvc.Http$CookieBuilder");

assertThat(parsingErrors)
.hasSize(8)
.hasSize(10)
.map(LogAndArguments::getFormattedMsg)
.allMatch(log ->
// ECJ error message
log.startsWith("Parse error at line ")
// analyzer error message mentioning the file
|| log.contains("KeywordAsIdentifierCheck")
|| log.contains("EmptyStatementsInImportsBug")
|| log.contains("RestrictedIdentifiersUsageCheck"));
|| log.contains("RestrictedIdentifiersUsageCheck")
// jdk 19 preview feature
|| log.contains("SwitchWithPatterns"));

}

Expand Down Expand Up @@ -246,11 +249,12 @@ private static List<File> getClassPath() {
}

private static List<SanityCheckException> scanFiles(File moduleBaseDir, List<InputFile> inputFiles, List<JavaCheck> checks, List<File> classpath) {
List<SanityCheckException> exceptions = new ArrayList<>();
SonarComponents sonarComponents = sonarComponents(moduleBaseDir, inputFiles);
JavaVersion javaVersion = new JavaVersionImpl(/* explicitly not set the version so preview features are not enabled */);
VisitorsBridgeForTests visitorsBridge = new VisitorsBridgeForTests(checks, classpath, sonarComponents, javaVersion);
List<SanityCheckException> exceptions = new ArrayList<>();
for (InputFile inputFile : inputFiles) {
try {
VisitorsBridgeForTests visitorsBridge = new VisitorsBridgeForTests(checks, classpath, sonarComponents, new JavaVersionImpl(JavaVersionImpl.MAX_SUPPORTED));
JavaAstScanner.scanSingleFileForTests(inputFile, visitorsBridge, null);
} catch (Throwable e) {
exceptions.add(new SanityCheckException(inputFile, e));
Expand Down
74 changes: 57 additions & 17 deletions java-frontend/src/main/java/org/sonar/java/cfg/CFG.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
*/
package org.sonar.java.cfg;

import org.sonar.java.Preconditions;
import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
Expand All @@ -34,7 +33,7 @@
import java.util.stream.Collectors;
import javax.annotation.CheckForNull;
import javax.annotation.Nullable;
import org.sonarsource.analyzer.commons.collections.ListUtils;
import org.sonar.java.Preconditions;
import org.sonar.java.model.ExpressionUtils;
import org.sonar.java.model.JavaTree;
import org.sonar.plugins.java.api.cfg.ControlFlowGraph;
Expand Down Expand Up @@ -71,6 +70,8 @@
import org.sonar.plugins.java.api.tree.NullPatternTree;
import org.sonar.plugins.java.api.tree.ParenthesizedTree;
import org.sonar.plugins.java.api.tree.PatternInstanceOfTree;
import org.sonar.plugins.java.api.tree.PatternTree;
import org.sonar.plugins.java.api.tree.RecordPatternTree;
import org.sonar.plugins.java.api.tree.ReturnStatementTree;
import org.sonar.plugins.java.api.tree.StatementTree;
import org.sonar.plugins.java.api.tree.SwitchExpressionTree;
Expand All @@ -86,6 +87,7 @@
import org.sonar.plugins.java.api.tree.VariableTree;
import org.sonar.plugins.java.api.tree.WhileStatementTree;
import org.sonar.plugins.java.api.tree.YieldStatementTree;
import org.sonarsource.analyzer.commons.collections.ListUtils;

public class CFG implements ControlFlowGraph {

Expand Down Expand Up @@ -614,23 +616,11 @@ private void build(Tree tree) {
currentBlock.elements.add(tree);
break;
case NULL_PATTERN:
currentBlock.elements.add(((NullPatternTree) tree).nullLiteral());
currentBlock.elements.add(tree);
break;
case TYPE_PATTERN:
buildVariable(((TypePatternTree) tree).patternVariable());
currentBlock.elements.add(tree);
break;
case GUARDED_PATTERN:
GuardedPatternTree guardedPatternTree = (GuardedPatternTree) tree;
// reverted order
build(guardedPatternTree.expression());
build(guardedPatternTree.pattern());
currentBlock.elements.add(tree);
break;
case RECORD_PATTERN:
case DEFAULT_PATTERN:
// do nothing - handled when building the switch
currentBlock.elements.add(tree);
buildPattern((PatternTree) tree);
break;
default:
throw new UnsupportedOperationException(tree.kind().name() + " " + ((JavaTree) tree).getLine());
Expand Down Expand Up @@ -1130,7 +1120,7 @@ private void buildInstanceOf(InstanceOfTree instanceOfTree) {

private void buildInstanceOf(PatternInstanceOfTree instanceOfTree) {
currentBlock.elements.add(instanceOfTree);
build(instanceOfTree.variable());
build(instanceOfTree.pattern());
build(instanceOfTree.expression());
}

Expand Down Expand Up @@ -1208,4 +1198,54 @@ public void setMethodSymbol(Symbol.MethodSymbol methodSymbol) {
this.methodSymbol = methodSymbol;
}

private void buildPattern(PatternTree tree) {
switch (tree.kind()) {
case NULL_PATTERN:
buildNullPattern((NullPatternTree) tree);
break;
case TYPE_PATTERN:
buildTypePattern((TypePatternTree) tree);
break;
case GUARDED_PATTERN:
buildGuardedPattern((GuardedPatternTree) tree);
break;
case RECORD_PATTERN:
buildRecordPattern((RecordPatternTree) tree);
break;
case DEFAULT_PATTERN:
// do nothing - handled when building the switch
currentBlock.elements.add(tree);
break;
default:
throw new UnsupportedOperationException(tree.kind().name() + " " + ((JavaTree) tree).getLine());
}
}

private void buildNullPattern(NullPatternTree tree) {
currentBlock.elements.add(tree.nullLiteral());
currentBlock.elements.add(tree);
}

private void buildTypePattern(TypePatternTree tree) {
buildVariable(tree.patternVariable());
currentBlock.elements.add(tree);
}

private void buildGuardedPattern(GuardedPatternTree tree) {
// reverted order
build(tree.expression());
build(tree.pattern());
currentBlock.elements.add(tree);
}

private void buildRecordPattern(RecordPatternTree tree) {
IdentifierTree name = tree.name();
if (name != null) {
build(name);
}
build(tree.patterns());
build(tree.type());
currentBlock.elements.add(tree);
}

}
16 changes: 14 additions & 2 deletions java-frontend/src/main/java/org/sonar/java/model/JParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@
import org.sonar.java.model.pattern.DefaultPatternTreeImpl;
import org.sonar.java.model.pattern.GuardedPatternTreeImpl;
import org.sonar.java.model.pattern.NullPatternTreeImpl;
import org.sonar.java.model.pattern.RecordPatternTreeImpl;
import org.sonar.java.model.pattern.TypePatternTreeImpl;
import org.sonar.java.model.statement.AssertStatementTreeImpl;
import org.sonar.java.model.statement.BlockTreeImpl;
Expand Down Expand Up @@ -1433,11 +1434,21 @@ private PatternTree convertPattern(Pattern p) {
switch (p.getNodeType()) {
case ASTNode.TYPE_PATTERN:
return new TypePatternTreeImpl(convertVariable(((TypePattern) p).getPatternVariable()));
case ASTNode.RECORD_PATTERN:
RecordPattern recordPattern = (RecordPattern) p;
List<PatternTree> nestedPatterns = recordPattern.patterns().stream()
.map(this::convertPattern)
.collect(Collectors.toList());

TypeTree patternType = convertType(recordPattern.getPatternType());
IdentifierTreeImpl recordName = recordPattern.getPatternName() != null ? convertSimpleName(recordPattern.getPatternName()) : null;
return new RecordPatternTreeImpl(patternType, nestedPatterns, recordName);
case ASTNode.GUARDED_PATTERN:
GuardedPattern g = (GuardedPattern) p;
return new GuardedPatternTreeImpl(
convertPattern(g.getPattern()),
firstTokenBefore(g.getExpression(), TerminalTokens.TokenNameAND_AND),
// FIXME java 19 support: should be "TerminalTokens.TokenNameRestrictedIdentifierWhen" instead of "TerminalTokens.TokenNameIdentifier"
firstTokenBefore(g.getExpression(), TerminalTokens.TokenNameIdentifier),
convertExpression(g.getExpression()));
case ASTNode.NULL_PATTERN:
// It is not clear how to reach this one, it seems to be possible only with badly constructed AST
Expand Down Expand Up @@ -2133,7 +2144,8 @@ private InstanceOfTreeImpl convertInstanceOf(InstanceofExpression e) {
private InstanceOfTreeImpl convertInstanceOf(PatternInstanceofExpression e) {
Expression leftOperand = e.getLeftOperand();
InternalSyntaxToken instanceofToken = firstTokenAfter(leftOperand, TerminalTokens.TokenNameinstanceof);
return new InstanceOfTreeImpl(convertExpression(leftOperand), instanceofToken, convertVariable(e.getRightOperand()));
//FIXME future versions of ECJ are likely to return a Pattern rather than a SingleVariableDeclaration
return new InstanceOfTreeImpl(convertExpression(leftOperand), instanceofToken, new TypePatternTreeImpl(convertVariable(e.getRightOperand())));
}

private LambdaExpressionTreeImpl convertLambdaExpression(LambdaExpression e) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,10 @@ private static void parse(ASTParser astParser, InputFile inputFile, JavaVersion

@VisibleForTesting
static boolean shouldEnablePreviewFlag(JavaVersion currentVersion) {
if (currentVersion.isNotSet()) {
// only enable preview features if java version is explicitly set
return false;
}
// We enable the preview feature flag even if the version is not officially supported, in order to have the best chances to parse the code.
return currentVersion.asInt() >= MAXIMUM_SUPPORTED_JAVA_VERSION.asInt();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public class JavaVersionImpl implements JavaVersion {
private static final int JAVA_16 = 16;
private static final int JAVA_17 = 17;
private static final int JAVA_18 = 18;
public static final int MAX_SUPPORTED = JAVA_18;
private static final int JAVA_19 = 19;
public static final int MAX_SUPPORTED = JAVA_19;

private final int javaVersion;

Expand Down
Loading