Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -1,6 +1,6 @@
{
"ruleKey": "S1481",
"hasTruePositives": true,
"falseNegatives": 10,
"falseNegatives": 8,
"falsePositives": 0
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
package checks;


public class UnusedVariablesFPCheck {
public class DeobfuscatedUpdateManager {
// @formatter:off
// uncommenting the following code makes the issue disappear, as the semantic is fully resolved
// private interface DataContainer {
// Iterable<ItemElement> getItems();
// }
//
// private interface ModelObject {
// void performAction();
// }
//
// private interface ItemElement {
// ModelObject getDataModel();
// }
//
// private static class SystemConfig {
// static ConfigMode getMode() {
// return ConfigMode.ENABLED;
// }
// }
//
// private enum ConfigMode {
// ENABLED,
// DISABLED
// }
//
// static class A {
// interface GenericCallback<T> { }
// }
// @formatter:on

void processUpdates(
DataContainer container
// REMARK : the issue arises from the A.GenericCallback<ModelObject> callback that is not even used (indirect type resolution problem)
, A.GenericCallback<X> callback
) {
for (ItemElement element : container.getItems()) {
if (SystemConfig.getMode() == ConfigMode.ENABLED) {
ModelObject dataModel = element.getDataModel(); // Compliant - false positive was raised here, dataModel is used in the next line
dataModel.performAction();
}
}
}

}

static class StringConcatenation {
// @formatter:off
// uncommenting the following code makes the issue disappear, as the semantic is fully resolved
// private class AClass {
// private class BClass<T> {
// public T b;
// }
// }
// @formatter:on

public String doSomething(AClass.BClass<String> instance) {
String c = "Hi"; // Compliant - false positive was raised here, c is used in the next line
return instance.b + c;
}
}

/*
A user reported a FP on enhanced switch statements like the one below.
However I was not able to reproduce it in a minimal example.
https://community.sonarsource.com/t/false-positive-for-s1854-unused-assignments-should-be-removed/114110/12

static class EnhancedSwitch {
// private enum DocumentStatus {
// DOC01,
// DOC02
// }
//
// private interface Document {
// void setStatus(DocumentStatus status);
// }
//
// private interface Event {
// }
//
// private class SimpleStatusChangedEvent implements Event {
// }
//
// private class NeedClientRecheckEvent implements Event {
// }
// private interface DocumentRepository {
// void save(Document document);
// }

void ko(Event event, Document document, DocumentRepository documentRepository) {
final DocumentStatus status = switch (event) {
case SimpleStatusChangedEvent ignored -> DocumentStatus.DOC01;
case NeedClientRecheckEvent ignored -> DocumentStatus.DOC02;
};
document.setStatus(status);
// ...
documentRepository.save(document);
}

}*/
}
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,15 @@ void test_non_compiling() {
.verifyIssues();
}

@Test
void test_incomplete_semantic() {
CheckVerifier.newVerifier()
.onFile(TestUtils.nonCompilingTestSourcesPath("checks/UnusedVariablesFPCheck.java"))
.withJavaVersion(14)
.withCheck(new DeadStoreCheck())
.verifyNoIssues();
}

private static class EraseSymbols extends BaseTreeVisitor {

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ public final boolean isTypeSymbol() {
}

@Override
public final boolean isMethodSymbol() {
public boolean isMethodSymbol() {
return false;
}

Expand Down Expand Up @@ -340,6 +340,11 @@ public boolean isVarArgsMethod() {
public boolean isNativeMethod() {
return false;
}

@Override
public boolean isMethodSymbol() {
return true;
}
}

public static final class UnknownType implements Type {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1285,7 +1285,7 @@ private java.util.Collection<UnknownClass.Entry<E>> samples() {

assertThat(recovered.isTypeSymbol()).isFalse();
assertThat(recovered.isVariableSymbol()).isFalse();
assertThat(recovered.isMethodSymbol()).isFalse();
assertThat(recovered.isMethodSymbol()).isTrue();
assertThat(recovered.isPackageSymbol()).isFalse();

assertThat(recovered.isAbstract()).isFalse();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,6 @@ private static void assertCommonProperties(Symbol unknownSymbol) {
assertThat(unknownSymbol.isPackageSymbol()).isFalse();
assertThat(unknownSymbol.isTypeSymbol()).isFalse();
assertThat(unknownSymbol.isVariableSymbol()).isFalse();
assertThat(unknownSymbol.isMethodSymbol()).isFalse();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't read this carefully, but I'm wondering why this is removed instead of reverting the condition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added it back

Copy link
Contributor Author

@rombirli rombirli Jan 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Edit :
this line 174 is inside of function private static void assertCommonProperties(Symbol unknownSymbol) which is used by all kinds of unknown symbols tests (unknown_type_symbol, unknown_method_symbol, ...), the isMethodSymbol function is no longer final in the parent abstact class UnknownSymbol , the result can be overriden differently in each child class (UnkownTypeSymbol, UnknownMethodSymbol).


assertThat(unknownSymbol.isStatic()).isFalse();
assertThat(unknownSymbol.isFinal()).isFalse();
Expand Down
Loading