Skip to content

RIS-492 Create rule S8265: Zone IDs passed to "ZoneId.of()" should be valid#5677

Draft
romainbrenguier wants to merge 5 commits into
masterfrom
romain/s8265
Draft

RIS-492 Create rule S8265: Zone IDs passed to "ZoneId.of()" should be valid#5677
romainbrenguier wants to merge 5 commits into
masterfrom
romain/s8265

Conversation

@romainbrenguier

Copy link
Copy Markdown
Contributor

Rule: S8265 - Zone IDs passed to "ZoneId.of()" should be valid

Type: BUG
Severity: Critical
Tags: datetime, pitfall

Description

This rule detects invalid time zone identifier strings passed to ZoneId.of() method.

Implementation Details

  • Extends AbstractMethodDetection to detect ZoneId.of(String) calls
  • Validates string literals against:
    • IANA Time Zone Database identifiers (using ZoneId.getAvailableZoneIds())
    • Fixed offset formats (+05:30, -08:00, etc.)
    • Special identifiers (UTC, GMT, Z, UT)
    • Zone offset IDs (UTC+1, GMT-5)
  • Provides helpful suggestions for common mistakes using Levenshtein distance
  • Maps three-letter abbreviations (PST, EST, etc.) to correct zone IDs

Test Coverage

All tests pass:

  • Invalid zone IDs detection
  • Three-letter abbreviations with suggestions
  • Typo detection with suggestions
  • Case sensitivity validation
  • Dynamic string handling (no false positives)

Related Issues


🤖 Generated with Claude Code (claude.com/code)

romainbrenguier and others added 4 commits June 18, 2026 09:44
Generated rule stubs and RSPEC specification for detecting invalid zone ID
strings passed to ZoneId.of() method, which would throw ZoneRulesException
at runtime.

Generated files:
- ValidZoneIdCheck.java: Check implementation stub
- ValidZoneIdCheckTest.java: Test stub
- ValidZoneIdCheckSample.java: Sample test cases
- S8265.html/json: RSPEC specification from PR 5849

Rule metadata:
- Type: BUG
- Severity: Critical
- Impact: RELIABILITY HIGH
- Jira: RIS-492
- RSPEC PR: SonarSource/rspec#5849

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created plan_rule.py script to generate planning artifacts using Claude API
- Generated technical_spec_S8265.md with implementation approach and patterns
- Generated comprehensive test cases in ValidZoneIdCheckSample.java
- Test cases cover noncompliant (invalid zones, abbreviations, typos) and compliant (IANA zones, offsets, special IDs) scenarios

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created implement_rule.py script to generate Java implementation using Claude API
- Implemented ValidZoneIdCheck extending AbstractMethodDetection
- Validates string literals passed to ZoneId.of() against IANA database
- Includes Levenshtein distance algorithm for typo suggestions
- Maps common abbreviations (PST, EST, etc.) to correct zone IDs
- Updated test cases to match actual validation behavior
- All tests pass successfully

Implementation features:
- Detects invalid zone IDs, three-letter abbreviations, typos
- Provides helpful suggestions for common mistakes
- Skips dynamic strings to avoid false positives
- Validates against IANA zones, fixed offsets, special IDs

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Added markdown code fence cleanup in plan_rule.py
- Ensured newline at end of generated files
- Updated technical specification with clearer descriptions

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Jun 18, 2026

Copy link
Copy Markdown

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.

16 issue(s) found across 4 file(s):

Rule File Line Message
python:S3457 create_pr.py 155 Add replacement fields or use a normal string instead of an f-string.
python:S3457 create_pr.py 156 Add replacement fields or use a normal string instead of an f-string.
python:S3457 create_pr.py 157 Add replacement fields or use a normal string instead of an f-string.
python:S3457 create_pr.py 158 Add replacement fields or use a normal string instead of an f-string.
python:S1172 implement_rule.py 56 Remove the unused function parameter "html_content".
python:S3457 implement_rule.py 168 Add replacement fields or use a normal string instead of an f-string.
python:S3457 implement_rule.py 170 Add replacement fields or use a normal string instead of an f-string.
python:S3457 implement_rule.py 171 Add replacement fields or use a normal string instead of an f-string.
python:S3457 implement_rule.py 172 Add replacement fields or use a normal string instead of an f-string.
java:S1192 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java 47 Define a constant instead of duplicating this literal "America/Los_Angeles" 3 times.
java:S1192 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java 49 Define a constant instead of duplicating this literal "America/New_York" 3 times.
java:S1192 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java 51 Define a constant instead of duplicating this literal "America/Chicago" 3 times.
java:S1192 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java 53 Define a constant instead of duplicating this literal "America/Denver" 3 times.
java:S2325 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java 126 Make "suggestAlternative" a "static" method.
python:S3457 plan_rule.py 186 Add replacement fields or use a normal string instead of an f-string.
python:S3457 plan_rule.py 189 Add replacement fields or use a normal string instead of an f-string.

Analyzed by SonarQube Agentic Analysis in 7.1 s

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

RIS-492

- Created create_pr.py adapted from sonar-skunk's create-pr task
- Uses gh CLI to create draft pull requests
- Generates detailed PR description with implementation details
- Handles existing PR detection and reuse
- Successfully created PR #5677 for S8265

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Comment on lines +38 to +44
private static final Pattern FIXED_OFFSET_PATTERN = Pattern.compile(
"^[+-]\\d{1,2}(:\\d{2}(:\\d{2})?)?$|^[+-]\\d{4}$"
);

private static final Pattern ZONE_OFFSET_PATTERN = Pattern.compile(
"^(UTC|GMT)[+-]\\d{1,2}(:\\d{2})?$"
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Bug: Valid "UT"-prefixed offsets flagged as invalid (false positive)

ZoneId.of() accepts three offset prefixes: UTC, GMT, and UT (e.g. ZoneId.of("UT+1") is valid). ZONE_OFFSET_PATTERN only matches ^(UTC|GMT)[+-]..., so UT+1/UT-5 are reported as invalid even though they are accepted at runtime. Because this rule is typed BUG/Critical, a false positive here is harmful. The pattern also rejects valid forms that ZoneId.of accepts, e.g. prefixed offsets with seconds (UTC+05:30:00) and bare-digit offsets such as +053000/+053000 style 6-digit offsets, since FIXED_OFFSET_PATTERN only allows the [+-]\d{4} form. Consider delegating validation to the JDK (e.g. try ZoneId.of / ZoneOffset.of in a guarded manner) instead of hand-rolled regexes to keep behavior aligned with the runtime.

Was this helpful? React with 👍 / 👎

Comment on lines +126 to +135
private String suggestAlternative(String invalidZoneId) {
if (COMMON_CORRECTIONS.containsKey(invalidZoneId)) {
return COMMON_CORRECTIONS.get(invalidZoneId);
}

return VALID_ZONE_IDS.stream()
.min(Comparator.comparingInt(valid -> levenshteinDistance(invalidZoneId, valid)))
.filter(closest -> levenshteinDistance(invalidZoneId, closest) <= 3)
.orElse(null);
}

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: Suggested zone ID is non-deterministic (HashSet iteration order)

suggestAlternative picks the closest valid zone via VALID_ZONE_IDS.stream().min(comparingInt(levenshtein)). ZoneId.getAvailableZoneIds() returns an unordered Set (HashSet), so when multiple zone IDs tie at the minimum Levenshtein distance, min() returns whichever appears first in iteration order. That order is not guaranteed across JDK versions / tz-database updates. The expected messages in the test sample hardcode specific suggestions (e.g. ZoneId.of("")Did you mean "GB"?, and the typo cases), which makes the test brittle and likely to break on a different JDK/tz version. For an empty string, many 2-character zone IDs tie, so the chosen suggestion is effectively arbitrary. Sort deterministically (e.g. tie-break by natural string order) and/or suppress suggestions when the distance is large/ambiguous to keep messages stable.

Deterministic tie-breaking and single filter; avoids HashSet-order dependence.:

return VALID_ZONE_IDS.stream()
  .filter(valid -> levenshteinDistance(invalidZoneId, valid) <= 3)
  .min(Comparator
    .comparingInt((String valid) -> levenshteinDistance(invalidZoneId, valid))
    .thenComparing(Comparator.naturalOrder()))
  .orElse(null);
  • Apply fix

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

Comment on lines +131 to +134
return VALID_ZONE_IDS.stream()
.min(Comparator.comparingInt(valid -> levenshteinDistance(invalidZoneId, valid)))
.filter(closest -> levenshteinDistance(invalidZoneId, closest) <= 3)
.orElse(null);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Performance: suggestAlternative recomputes Levenshtein over all zone IDs repeatedly

For every invalid string literal, suggestAlternative streams over all ~600 entries of VALID_ZONE_IDS. The min(Comparator.comparingInt(... levenshteinDistance ...)) recomputes the (O(len^2)) Levenshtein distance on every pairwise comparison, and then .filter(closest -> levenshteinDistance(...) <= 3) computes it again for the winner. This is wasteful: the comparator evaluates the key function multiple times per element. Prefer materializing the distance once per candidate (e.g. map to a pair, or filter-then-min as in the suggested fix) so each zone ID's distance is computed at most once.

Was this helpful? React with 👍 / 👎

Comment thread plan_rule.py
Comment on lines +1 to +15
#!/usr/bin/env python3
"""
Plan rule implementation for S8265 ValidZoneIdCheck.
Generates technical specification and test cases based on RSPEC.
"""

import json
import os
import sys
from pathlib import Path
from anthropic import Anthropic

def load_rspec_files():
"""Load RSPEC HTML and metadata files."""
html_path = Path("sonar-java-plugin/src/main/resources/org/sonar/l10n/java/rules/java/S8265.html")

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: AI scaffolding scripts and generated spec committed to the repo

plan_rule.py, implement_rule.py, and technical_spec_S8265.md are code-generation helper scripts/notes (they call the Anthropic API to author the check) committed at the repository root. These are developer scaffolding rather than product code: they are not wired into the build, reference an incorrect rule key in the spec (S6885/InvalidZoneIdCheck vs the actual S8265/ValidZoneIdCheck), and would normally not belong in the plugin repo. Consider removing them from the PR (or moving them to a tooling location that is clearly out of the shipped artifact) before merge.

Was this helpful? React with 👍 / 👎


return dp[s1.length()][s2.length()];
}
} No newline at end of file

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: Missing trailing newline at end of ValidZoneIdCheck.java

ValidZoneIdCheck.java ends without a trailing newline (\ No newline at end of file). Most files in this codebase end with a newline and license/format checks may flag this. Add a final newline.

Was this helpful? React with 👍 / 👎

Comment thread create_pr.py
Comment on lines +115 to +129
if result.returncode != 0:
# Check if error is due to existing PR
if "already exists" in result.stderr.lower():
print("PR already exists for this branch")
existing_pr = check_existing_pr(branch_name)
if existing_pr:
print(f"Existing PR: {existing_pr['url']}")
return existing_pr['url']
else:
print(f"Error creating PR: {result.stderr}")
sys.exit(1)

pr_url = result.stdout.strip()
print(f"Created draft PR: {pr_url}")
return pr_url

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Bug: create_pr.py prints empty PR URL when 'already exists' but lookup fails

In create_pull_request, when gh pr create fails with returncode != 0 and stderr contains "already exists" but check_existing_pr returns None (e.g., the PR is on a different base, closed, or not returned by the list query), the code does not return or exit. Execution falls through to pr_url = result.stdout.strip(), which is empty on failure, and prints Created draft PR: with no URL — silently reporting success despite no PR being created/found. Add an explicit return/exit in the already exists branch when no existing PR is found.

Exit instead of falling through to an empty URL when the existing PR cannot be found.:

if result.returncode != 0:
    if "already exists" in result.stderr.lower():
        print("PR already exists for this branch")
        existing_pr = check_existing_pr(branch_name)
        if existing_pr:
            print(f"Existing PR: {existing_pr['url']}")
            return existing_pr['url']
        print("Could not locate the existing PR")
        sys.exit(1)
    print(f"Error creating PR: {result.stderr}")
    sys.exit(1)
  • Apply fix

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

@sonarqube-next

Copy link
Copy Markdown

Quality Gate failed Quality Gate failed

Failed conditions
5 New issues

See analysis details on SonarQube

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE SonarQube for IDE

@gitar-bot

gitar-bot Bot commented Jun 18, 2026

Copy link
Copy Markdown
CI failed: The build succeeded, but generated a diff artifact indicating that the autoscan output changed due to the implementation of the new rule S8265.

Overview

The build completed successfully, but the automated regression testing (autoscan) detected a diff in the output. This is a common occurrence when introducing a new static analysis rule, as it expects the new rule to identify additional violations in test code.

Failures

Autoscan output mismatch (confidence: high)

  • Type: other
  • Affected jobs: 82085573278
  • Related to change: yes
  • Root cause: The implementation of rule S8265 ('Zone IDs passed to ZoneId.of() should be valid') has introduced new findings in the test suite that deviate from the previously recorded 'expected' state.
  • Suggested fix: Review the generated artifact 'diff_autoscan.html' to confirm that the new violations are correctly identified by S8265. If the findings are correct, update the test resources (expected files) to incorporate these changes.

Summary

  • Change-related failures: 1 (autoscan diff mismatch caused by new rule S8265).
  • Infrastructure/flaky failures: 0
  • Recommended action: Manually verify the findings in the diff_autoscan.html artifact provided by the CI. If the new rule behaves as expected, commit the updated test expectation files to the branch.
Code Review ⚠️ Changes requested 0 resolved / 6 findings

Implements S8265 to detect invalid ZoneId strings, but requires addressing false positives for UT-prefixed offsets, non-deterministic suggestion logic, and the inclusion of unnecessary AI scaffolding scripts.

⚠️ Bug: Valid "UT"-prefixed offsets flagged as invalid (false positive)

📄 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:38-44 📄 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:103-108

ZoneId.of() accepts three offset prefixes: UTC, GMT, and UT (e.g. ZoneId.of("UT+1") is valid). ZONE_OFFSET_PATTERN only matches ^(UTC|GMT)[+-]..., so UT+1/UT-5 are reported as invalid even though they are accepted at runtime. Because this rule is typed BUG/Critical, a false positive here is harmful. The pattern also rejects valid forms that ZoneId.of accepts, e.g. prefixed offsets with seconds (UTC+05:30:00) and bare-digit offsets such as +053000/+053000 style 6-digit offsets, since FIXED_OFFSET_PATTERN only allows the [+-]\d{4} form. Consider delegating validation to the JDK (e.g. try ZoneId.of / ZoneOffset.of in a guarded manner) instead of hand-rolled regexes to keep behavior aligned with the runtime.

⚠️ Quality: Suggested zone ID is non-deterministic (HashSet iteration order)

📄 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:126-135 📄 java-checks-test-sources/default/src/main/java/checks/ValidZoneIdCheckSample.java:8 📄 java-checks-test-sources/default/src/main/java/checks/ValidZoneIdCheckSample.java:26-33

suggestAlternative picks the closest valid zone via VALID_ZONE_IDS.stream().min(comparingInt(levenshtein)). ZoneId.getAvailableZoneIds() returns an unordered Set (HashSet), so when multiple zone IDs tie at the minimum Levenshtein distance, min() returns whichever appears first in iteration order. That order is not guaranteed across JDK versions / tz-database updates. The expected messages in the test sample hardcode specific suggestions (e.g. ZoneId.of("")Did you mean "GB"?, and the typo cases), which makes the test brittle and likely to break on a different JDK/tz version. For an empty string, many 2-character zone IDs tie, so the chosen suggestion is effectively arbitrary. Sort deterministically (e.g. tie-break by natural string order) and/or suppress suggestions when the distance is large/ambiguous to keep messages stable.

Deterministic tie-breaking and single filter; avoids HashSet-order dependence.
return VALID_ZONE_IDS.stream()
  .filter(valid -> levenshteinDistance(invalidZoneId, valid) <= 3)
  .min(Comparator
    .comparingInt((String valid) -> levenshteinDistance(invalidZoneId, valid))
    .thenComparing(Comparator.naturalOrder()))
  .orElse(null);
💡 Performance: suggestAlternative recomputes Levenshtein over all zone IDs repeatedly

📄 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:131-134

For every invalid string literal, suggestAlternative streams over all ~600 entries of VALID_ZONE_IDS. The min(Comparator.comparingInt(... levenshteinDistance ...)) recomputes the (O(len^2)) Levenshtein distance on every pairwise comparison, and then .filter(closest -> levenshteinDistance(...) <= 3) computes it again for the winner. This is wasteful: the comparator evaluates the key function multiple times per element. Prefer materializing the distance once per candidate (e.g. map to a pair, or filter-then-min as in the suggested fix) so each zone ID's distance is computed at most once.

💡 Quality: AI scaffolding scripts and generated spec committed to the repo

📄 plan_rule.py:1-15 📄 implement_rule.py:1-15 📄 technical_spec_S8265.md:23-24 📄 technical_spec_S8265.md:375-376 📄 create_pr.py:1-11 📄 create_pr.py:95-99

plan_rule.py, implement_rule.py, and technical_spec_S8265.md are code-generation helper scripts/notes (they call the Anthropic API to author the check) committed at the repository root. These are developer scaffolding rather than product code: they are not wired into the build, reference an incorrect rule key in the spec (S6885/InvalidZoneIdCheck vs the actual S8265/ValidZoneIdCheck), and would normally not belong in the plugin repo. Consider removing them from the PR (or moving them to a tooling location that is clearly out of the shipped artifact) before merge.

💡 Quality: Missing trailing newline at end of ValidZoneIdCheck.java

📄 java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:159

ValidZoneIdCheck.java ends without a trailing newline (\ No newline at end of file). Most files in this codebase end with a newline and license/format checks may flag this. Add a final newline.

💡 Bug: create_pr.py prints empty PR URL when 'already exists' but lookup fails

📄 create_pr.py:115-129

In create_pull_request, when gh pr create fails with returncode != 0 and stderr contains "already exists" but check_existing_pr returns None (e.g., the PR is on a different base, closed, or not returned by the list query), the code does not return or exit. Execution falls through to pr_url = result.stdout.strip(), which is empty on failure, and prints Created draft PR: with no URL — silently reporting success despite no PR being created/found. Add an explicit return/exit in the already exists branch when no existing PR is found.

Exit instead of falling through to an empty URL when the existing PR cannot be found.
if result.returncode != 0:
    if "already exists" in result.stderr.lower():
        print("PR already exists for this branch")
        existing_pr = check_existing_pr(branch_name)
        if existing_pr:
            print(f"Existing PR: {existing_pr['url']}")
            return existing_pr['url']
        print("Could not locate the existing PR")
        sys.exit(1)
    print(f"Error creating PR: {result.stderr}")
    sys.exit(1)
🤖 Prompt for agents
Code Review: Implements S8265 to detect invalid ZoneId strings, but requires addressing false positives for UT-prefixed offsets, non-deterministic suggestion logic, and the inclusion of unnecessary AI scaffolding scripts.

1. ⚠️ Bug: Valid "UT"-prefixed offsets flagged as invalid (false positive)
   Files: java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:38-44, java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:103-108

   `ZoneId.of()` accepts three offset prefixes: `UTC`, `GMT`, and `UT` (e.g. `ZoneId.of("UT+1")` is valid). `ZONE_OFFSET_PATTERN` only matches `^(UTC|GMT)[+-]...`, so `UT+1`/`UT-5` are reported as invalid even though they are accepted at runtime. Because this rule is typed BUG/Critical, a false positive here is harmful. The pattern also rejects valid forms that `ZoneId.of` accepts, e.g. prefixed offsets with seconds (`UTC+05:30:00`) and bare-digit offsets such as `+053000`/`+053000` style 6-digit offsets, since `FIXED_OFFSET_PATTERN` only allows the `[+-]\d{4}` form. Consider delegating validation to the JDK (e.g. try `ZoneId.of` / `ZoneOffset.of` in a guarded manner) instead of hand-rolled regexes to keep behavior aligned with the runtime.

2. ⚠️ Quality: Suggested zone ID is non-deterministic (HashSet iteration order)
   Files: java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:126-135, java-checks-test-sources/default/src/main/java/checks/ValidZoneIdCheckSample.java:8, java-checks-test-sources/default/src/main/java/checks/ValidZoneIdCheckSample.java:26-33

   `suggestAlternative` picks the closest valid zone via `VALID_ZONE_IDS.stream().min(comparingInt(levenshtein))`. `ZoneId.getAvailableZoneIds()` returns an unordered `Set` (HashSet), so when multiple zone IDs tie at the minimum Levenshtein distance, `min()` returns whichever appears first in iteration order. That order is not guaranteed across JDK versions / tz-database updates. The expected messages in the test sample hardcode specific suggestions (e.g. `ZoneId.of("")` → `Did you mean "GB"?`, and the typo cases), which makes the test brittle and likely to break on a different JDK/tz version. For an empty string, many 2-character zone IDs tie, so the chosen suggestion is effectively arbitrary. Sort deterministically (e.g. tie-break by natural string order) and/or suppress suggestions when the distance is large/ambiguous to keep messages stable.

   Fix (Deterministic tie-breaking and single filter; avoids HashSet-order dependence.):
   return VALID_ZONE_IDS.stream()
     .filter(valid -> levenshteinDistance(invalidZoneId, valid) <= 3)
     .min(Comparator
       .comparingInt((String valid) -> levenshteinDistance(invalidZoneId, valid))
       .thenComparing(Comparator.naturalOrder()))
     .orElse(null);

3. 💡 Performance: suggestAlternative recomputes Levenshtein over all zone IDs repeatedly
   Files: java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:131-134

   For every invalid string literal, `suggestAlternative` streams over all ~600 entries of `VALID_ZONE_IDS`. The `min(Comparator.comparingInt(... levenshteinDistance ...))` recomputes the (O(len^2)) Levenshtein distance on every pairwise comparison, and then `.filter(closest -> levenshteinDistance(...) <= 3)` computes it again for the winner. This is wasteful: the comparator evaluates the key function multiple times per element. Prefer materializing the distance once per candidate (e.g. map to a pair, or filter-then-min as in the suggested fix) so each zone ID's distance is computed at most once.

4. 💡 Quality: AI scaffolding scripts and generated spec committed to the repo
   Files: plan_rule.py:1-15, implement_rule.py:1-15, technical_spec_S8265.md:23-24, technical_spec_S8265.md:375-376, create_pr.py:1-11, create_pr.py:95-99

   `plan_rule.py`, `implement_rule.py`, and `technical_spec_S8265.md` are code-generation helper scripts/notes (they call the Anthropic API to author the check) committed at the repository root. These are developer scaffolding rather than product code: they are not wired into the build, reference an incorrect rule key in the spec (`S6885`/`InvalidZoneIdCheck` vs the actual `S8265`/`ValidZoneIdCheck`), and would normally not belong in the plugin repo. Consider removing them from the PR (or moving them to a tooling location that is clearly out of the shipped artifact) before merge.

5. 💡 Quality: Missing trailing newline at end of ValidZoneIdCheck.java
   Files: java-checks/src/main/java/org/sonar/java/checks/ValidZoneIdCheck.java:159

   `ValidZoneIdCheck.java` ends without a trailing newline (`\ No newline at end of file`). Most files in this codebase end with a newline and license/format checks may flag this. Add a final newline.

6. 💡 Bug: create_pr.py prints empty PR URL when 'already exists' but lookup fails
   Files: create_pr.py:115-129

   In `create_pull_request`, when `gh pr create` fails with returncode != 0 and stderr contains "already exists" but `check_existing_pr` returns None (e.g., the PR is on a different base, closed, or not returned by the list query), the code does not return or exit. Execution falls through to `pr_url = result.stdout.strip()`, which is empty on failure, and prints `Created draft PR: ` with no URL — silently reporting success despite no PR being created/found. Add an explicit return/exit in the `already exists` branch when no existing PR is found.

   Fix (Exit instead of falling through to an empty URL when the existing PR cannot be found.):
   if result.returncode != 0:
       if "already exists" in result.stderr.lower():
           print("PR already exists for this branch")
           existing_pr = check_existing_pr(branch_name)
           if existing_pr:
               print(f"Existing PR: {existing_pr['url']}")
               return existing_pr['url']
           print("Could not locate the existing PR")
           sys.exit(1)
       print(f"Error creating PR: {result.stderr}")
       sys.exit(1)

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.
Unblock → Override a blocking verdict and allow merging.

Comment with these commands to change:

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

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.

1 participant