Skip to content

SONARJAVA-6304: Implement S9366: avoid unsupported ChronoUnit values with Instant - #6039

Open
nathsou wants to merge 2 commits into
masterfrom
new-rule/S9366
Open

SONARJAVA-6304: Implement S9366: avoid unsupported ChronoUnit values with Instant#6039
nathsou wants to merge 2 commits into
masterfrom
new-rule/S9366

Conversation

@nathsou

@nathsou nathsou commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Implement S9366 as a semantic SonarJava check for unsupported ChronoUnit constants used with Instant.
  • Cover all supported and unsupported units, static imports, custom-unit boundaries, and missing semantics.
  • Generate rule metadata and add S9366 to Sonar way.

Links

AI disclosure

  • LLM model used for implementation: gpt-5.6-sol

@nathsou nathsou self-assigned this Aug 25, 2026
@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

SONARJAVA-6304

Comment on lines +74 to +78
protected void onMethodInvocationFound(MethodInvocationTree mit) {
if (context.getSemanticModel() == null || mit.arguments().size() < 2) {
return;
}
ExpressionTree argument = mit.arguments().get(1);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Quality: Unreachable guard in onMethodInvocationFound

onMethodInvocationFound is only invoked after MethodMatchers resolved the invocation against java.time.Instant, which requires semantics; with withoutSemantic() the method symbol is unknown and the callback never fires, so context.getSemanticModel() == null can never be true here. Likewise both matchers declare exactly two parameters, so mit.arguments().size() < 2 is never true. The two conditions are unreachable branches (and test_without_semantic does not actually exercise them); dropping them simplifies the check and avoids uncovered branches.

Remove the unreachable semantic/arity guard (the matchers already guarantee both).:

@Override
protected void onMethodInvocationFound(MethodInvocationTree mit) {
  ExpressionTree argument = mit.arguments().get(1);
  Symbol symbol = referencedSymbol(ExpressionUtils.skipParentheses(argument));
  if (isChronoUnitConstant(symbol) && UNSUPPORTED_UNITS.contains(symbol.name())) {
    reportIssue(argument, String.format(""%s" is unsupported by Instant and causes an UnsupportedTemporalTypeException.", symbol.name()));
  }
}
  • Apply fix

Check the box to apply the fix or reply for a change | Was this helpful? React with 👍 / 👎

@gitar-bot

gitar-bot Bot commented Aug 25, 2026

Copy link
Copy Markdown
Code Review 👍 Approved with suggestions 0 resolved / 1 findings

Implements rule S9366 to detect unsupported ChronoUnit values with Instant, complete with thorough test coverage and metadata. Consider removing the unreachable guard in onMethodInvocationFound.

💡 Quality: Unreachable guard in onMethodInvocationFound

📄 java-checks/src/main/java/org/sonar/java/checks/UnsupportedChronoUnitWithInstantCheck.java:74-78 📄 java-checks/src/test/java/org/sonar/java/checks/UnsupportedChronoUnitWithInstantCheckTest.java:36-43

onMethodInvocationFound is only invoked after MethodMatchers resolved the invocation against java.time.Instant, which requires semantics; with withoutSemantic() the method symbol is unknown and the callback never fires, so context.getSemanticModel() == null can never be true here. Likewise both matchers declare exactly two parameters, so mit.arguments().size() < 2 is never true. The two conditions are unreachable branches (and test_without_semantic does not actually exercise them); dropping them simplifies the check and avoids uncovered branches.

Remove the unreachable semantic/arity guard (the matchers already guarantee both).
@Override
protected void onMethodInvocationFound(MethodInvocationTree mit) {
  ExpressionTree argument = mit.arguments().get(1);
  Symbol symbol = referencedSymbol(ExpressionUtils.skipParentheses(argument));
  if (isChronoUnitConstant(symbol) && UNSUPPORTED_UNITS.contains(symbol.name())) {
    reportIssue(argument, String.format(""%s" is unsupported by Instant and causes an UnsupportedTemporalTypeException.", symbol.name()));
  }
}
🤖 Prompt for agents
Code Review: Implements rule S9366 to detect unsupported ChronoUnit values with Instant, complete with thorough test coverage and metadata. Consider removing the unreachable guard in onMethodInvocationFound.

1. 💡 Quality: Unreachable guard in onMethodInvocationFound
   Files: java-checks/src/main/java/org/sonar/java/checks/UnsupportedChronoUnitWithInstantCheck.java:74-78, java-checks/src/test/java/org/sonar/java/checks/UnsupportedChronoUnitWithInstantCheckTest.java:36-43

   `onMethodInvocationFound` is only invoked after `MethodMatchers` resolved the invocation against `java.time.Instant`, which requires semantics; with `withoutSemantic()` the method symbol is unknown and the callback never fires, so `context.getSemanticModel() == null` can never be true here. Likewise both matchers declare exactly two parameters, so `mit.arguments().size() < 2` is never true. The two conditions are unreachable branches (and `test_without_semantic` does not actually exercise them); dropping them simplifies the check and avoids uncovered branches.

   Fix (Remove the unreachable semantic/arity guard (the matchers already guarantee both).):
   @Override
   protected void onMethodInvocationFound(MethodInvocationTree mit) {
     ExpressionTree argument = mit.arguments().get(1);
     Symbol symbol = referencedSymbol(ExpressionUtils.skipParentheses(argument));
     if (isChronoUnitConstant(symbol) && UNSUPPORTED_UNITS.contains(symbol.name())) {
       reportIssue(argument, String.format(""%s" is unsupported by Instant and causes an UnsupportedTemporalTypeException.", symbol.name()));
     }
   }

Implementation Status ✅ 4 of 4 objectives covered
SONARJAVA-6304 - 4 of 4 objectives covered

This PR implements the S9366 rule for unsupported ChronoUnit values with Instant, covering all specified objectives including custom unit exclusion, variable propagation restriction, rule creation, and semantic ChronoUnit verification.

✅ 4 covered here
  • ✅ Exclude custom TemporalUnit implementations from reporting
  • ✅ Exclude value propagation through variables in the initial implementation
  • ✅ Create SonarJava rule S9366 to report known unsupported ChronoUnit constants passed to Instant.plus, Instant.minus, or Instant.until
  • ✅ Report direct enum constants, including statically imported constants, only when semantic resolution confirms the symbol belongs to java.time.temporal.ChronoUnit
Options

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

Comment with these commands to change the behavior for this request:

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

Was this helpful? React with 👍 / 👎 | Gitar

@sonarqube-next

Copy link
Copy Markdown
Contributor

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.

1 participant