Skip to content

SONARJAVA-6305 Fix the expected exceptions filter to work without semantic information#5626

Merged
aurelien-coet-sonarsource merged 9 commits into
masterfrom
ac/SONARJAVA-6305-2
May 27, 2026
Merged

SONARJAVA-6305 Fix the expected exceptions filter to work without semantic information#5626
aurelien-coet-sonarsource merged 9 commits into
masterfrom
ac/SONARJAVA-6305-2

Conversation

@aurelien-coet-sonarsource
Copy link
Copy Markdown
Contributor

@aurelien-coet-sonarsource aurelien-coet-sonarsource commented May 22, 2026


Summary by Gitar

  • Refactored checks:
    • Renamed InstantConversionsCheck to DateTimeConversionsCheck to reflect its broadened scope.
  • Implemented new logic:
    • Added detection for invalid date-time conversions involving LocalDate, LocalTime, and ZonedDateTime types.
    • Enhanced ExpectedExceptionFilter to allow these conversions to work correctly without full semantic information.
  • Expanded test coverage:
    • Added DateTimeConversionsCheckSample and DateTimeConversionsCheckTest to verify the new detection logic.
    • Added additional test cases in ExpectedExceptionFilter.java to validate filter behavior with various exception types and signatures.

This will update automatically on new commits.

@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud Bot commented May 22, 2026

Agentic Analysis: Early Results

Agentic Analysis and Context Augmentation are available on your project. Here are some issues that could have been prevented. Follow the links to learn how to put them into action.

5 issue(s) found across 1 file(s):

Rule File Line Message
java:S2201 java-checks/src/test/files/filters/ExpectedExceptionFilter.java 157 The return value of "from" must be used.
java:S2201 java-checks/src/test/files/filters/ExpectedExceptionFilter.java 166 The return value of "from" must be used.
java:S2201 java-checks/src/test/files/filters/ExpectedExceptionFilter.java 171 The return value of "from" must be used.
java:S2201 java-checks/src/test/files/filters/ExpectedExceptionFilter.java 187 The return value of "from" must be used.
java:S2201 java-checks/src/test/files/filters/ExpectedExceptionFilter.java 192 The return value of "from" must be used.

Analyzed by SonarQube Agentic Analysis in 5.5 s

@hashicorp-vault-sonar-prod
Copy link
Copy Markdown
Contributor

hashicorp-vault-sonar-prod Bot commented May 22, 2026

SONARJAVA-6305

Comment thread java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java Outdated
Comment thread java-checks/src/test/files/filters/ExpectedExceptionFilter.java Outdated
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 26, 2026

Code Review ✅ Approved 2 resolved / 2 findings

Refactored the expected exceptions filter to function without semantic information and renamed the conversion check to broaden its scope. Addressed an unchecked argument index access and a duplicate test method name.

✅ 2 resolved
Quality: Duplicate method name in test file

📄 java-checks/src/test/files/filters/ExpectedExceptionFilter.java:158 📄 java-checks/src/test/files/filters/ExpectedExceptionFilter.java:162
The test file defines two methods both named methodAnnotationWithoutSemantics (lines 158 and 162). While Java allows method overloading, these methods have the same signature (both take no arguments and return void), which will cause a compilation error.

Edge Case: Unchecked argument index access may throw IndexOutOfBoundsException

📄 java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java:128 📄 java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java:137-138 📄 java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java:130-135
The refactoring replaced MethodMatchers (which validated parameter count via addParametersMatcher) with name-based matching, but didn't add equivalent bounds checks on argument lists. If a method with the same name but a different signature is encountered (plausible since this is name-only matching), arguments.get(1) or arguments.get(0) will throw IndexOutOfBoundsException.

Three sites are affected:

  • Line 128: catchThrowableOfType accesses arguments.get(1) without checking arguments.size() >= 2
  • Line 137: assertThatExceptionOfType/thenExceptionOfType accesses arguments.get(0) without checking non-empty
  • Line 138: isThrownBy accesses mit.arguments().get(0) without checking non-empty

Since the filter now intentionally matches by name alone, it's more likely to encounter unexpected method signatures.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Copy link
Copy Markdown
Contributor

@NoemieBenard NoemieBenard left a comment

Choose a reason for hiding this comment

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

Overall LGTM and should work for the FPs that we have, just some comments

}
}

// When semantic information is missing, the filter should conservatively activate.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is it really about semantic here ?

Copy link
Copy Markdown
Contributor Author

@aurelien-coet-sonarsource aurelien-coet-sonarsource May 27, 2026

Choose a reason for hiding this comment

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

Yes, here this is an example that wouldn't even compile, because @Test is used without a fully qualified path and there is no import for the annotation, so when the filter processes the annotation it doesn't know its actual type. However, since the annotation looks like a test annotation that matches the criteria for the filter, we still want to trigger it.

Comment on lines +185 to +198
@Test(expected = DateTimeException)
void wrongExpectedType() {
Instant.from(date); // WithIssue
}

@Test(expect = DateTimeException.class)
void wrongAnnotationAttribute() {
Instant.from(date); // WithIssue
}

@Test
void wrongAssertMethodSignature() {
assertThrows(Instant.from(date)); // WithIssue
catchThrowableOfType(Instant.from(date)); // WithIssue
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if we are not being too strict with these tests, we could prevent the filter from activating on patterns that we don't recognize even though they are correct. (but maybe we can keep it as it is for now, WDYT?)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I think we don't want to trigger the filter in cases where we do not have enough information to know what specific exceptions are expected. Here, we cannot know if the intent of the user was to assert that the call throws a DateTimeException or one of its super/sub-classes. WDYT ?

Copy link
Copy Markdown
Contributor

@NoemieBenard NoemieBenard May 27, 2026

Choose a reason for hiding this comment

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

Yes, but we could say that we don't raise if there is any indication that an exception is expected, risking false negatives but avoiding false positives on specific behavior intended by the developer (but I am not sure this can happen). Anyway I think for now the way you did it is fine and we'll have feedback later if there are any issues

Comment on lines 208 to 213
@@ -195,42 +214,38 @@ private static Optional<Type> classLiteralType(ExpressionTree expression) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This looks nicer IMO:

Suggested change
if (expression instanceof MemberSelectExpressionTree memberSelect
&& "class".equals(memberSelect.identifier().name())) {
return Optional.of(memberSelect.expression().symbolType());
}

import org.sonar.plugins.java.api.tree.UnionTypeTree;

import static org.sonar.java.checks.helpers.MethodTreeUtils.consecutiveMethodInvocation;
import static org.sonar.java.checks.helpers.UnitTestUtils.isTryCatchFail;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The unused import should be removed

@sonarqube-next
Copy link
Copy Markdown

Copy link
Copy Markdown
Contributor

@NoemieBenard NoemieBenard left a comment

Choose a reason for hiding this comment

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

LGTM

@aurelien-coet-sonarsource aurelien-coet-sonarsource merged commit c04a0eb into master May 27, 2026
18 checks passed
@aurelien-coet-sonarsource aurelien-coet-sonarsource deleted the ac/SONARJAVA-6305-2 branch May 27, 2026 07:46
@gitar-bot
Copy link
Copy Markdown

gitar-bot Bot commented May 27, 2026

Code Review ✅ Approved 2 resolved / 2 findings

Updates the expected exceptions filter to support non-semantic analysis and renames the conversion check for broader scope, addressing previous index access and naming concerns. No further issues found.

✅ 2 resolved
Quality: Duplicate method name in test file

📄 java-checks/src/test/files/filters/ExpectedExceptionFilter.java:158 📄 java-checks/src/test/files/filters/ExpectedExceptionFilter.java:162
The test file defines two methods both named methodAnnotationWithoutSemantics (lines 158 and 162). While Java allows method overloading, these methods have the same signature (both take no arguments and return void), which will cause a compilation error.

Edge Case: Unchecked argument index access may throw IndexOutOfBoundsException

📄 java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java:128 📄 java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java:137-138 📄 java-checks/src/main/java/org/sonar/java/filters/ExpectedExceptionFilter.java:130-135
The refactoring replaced MethodMatchers (which validated parameter count via addParametersMatcher) with name-based matching, but didn't add equivalent bounds checks on argument lists. If a method with the same name but a different signature is encountered (plausible since this is name-only matching), arguments.get(1) or arguments.get(0) will throw IndexOutOfBoundsException.

Three sites are affected:

  • Line 128: catchThrowableOfType accesses arguments.get(1) without checking arguments.size() >= 2
  • Line 137: assertThatExceptionOfType/thenExceptionOfType accesses arguments.get(0) without checking non-empty
  • Line 138: isThrownBy accesses mit.arguments().get(0) without checking non-empty

Since the filter now intentionally matches by name alone, it's more likely to encounter unexpected method signatures.

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants