Skip to content

SONARJAVA-6779 Implement new rule S9343: Methods should not increase accessibility when overriding or hiding - #5941

Open
romainbrenguier wants to merge 7 commits into
masterfrom
new-rule/SONARJAVA-6779-S9343
Open

SONARJAVA-6779 Implement new rule S9343: Methods should not increase accessibility when overriding or hiding#5941
romainbrenguier wants to merge 7 commits into
masterfrom
new-rule/SONARJAVA-6779-S9343

Conversation

@romainbrenguier

Copy link
Copy Markdown
Contributor

Add rule S9343 which detects methods that increase accessibility when overriding instance methods or hiding static methods in parent classes.

Add rule S9343 which detects methods that increase accessibility when
overriding instance methods or hiding static methods in parent classes.
@hashicorp-vault-sonar-prod

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

Copy link
Copy Markdown
Contributor

SONARJAVA-6779

🤖 Generated with GitHub Actions
@github-actions

Copy link
Copy Markdown
Contributor

Ruling needs updating. A fix PR has been created: #5944

Please review and merge it into your branch.

…tests

- Fix FP: skip package-private static methods in different packages (not inherited/hidden)
- Fix FN: filter out interface owners when checking overridden symbols
  so that superclass override is detected even when interface entry comes first
- Remove unnecessary cast to MethodSymbol in accessLevel call
- Extract common access comparison into reportIfAccessIncreased to reduce duplication
- Use switch expression for accessLevelName
- Add test cases: interface+superclass override, different param types,
  private parent static method, multi-level static hierarchy

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Ruling Diff Summary

Detected changes in 5 rule files: 0 issues removed, 181 issues added.

S9343 (java) on commons-beanutils - 0 issues removed, 77 issues added - new ruling file

Added src/main/java/org/apache/commons/beanutils2/BeanMap.java (line 209)

       204 |      * @return a cloned instance of this bean map
       205 |      * @throws CloneNotSupportedException if the underlying bean
       206 |      * cannot be cloned
       207 |      */
       208 |     @Override
>>>    209 |     public Object clone() throws CloneNotSupportedException {
       210 |         final BeanMap newMap = (BeanMap)super.clone();
       211 | 
       212 |         if(bean == null) {
       213 |             // no bean, just an empty bean map at the moment.  return a newly
       214 |             // cloned and empty bean map.

Added src/test/java/org/apache/commons/beanutils2/BasicDynaBeanTestCase.java (line 102)

        97 | 
        98 |     /**
        99 |      * Set up instance variables required by this test case.
       100 |      */
       101 |     @Override
>>>    102 |     public void setUp() throws Exception {
       103 | 
       104 |         // Instantiate a new DynaBean instance
       105 |         final DynaClass dynaClass = createDynaClass();
       106 |         bean = dynaClass.newInstance();
       107 | 

Added src/test/java/org/apache/commons/beanutils2/BasicDynaBeanTestCase.java (line 161)

       156 | 
       157 |     /**
       158 |      * Tear down instance variables required by this test case.
       159 |      */
       160 |     @Override
>>>    161 |     public void tearDown() {
       162 | 
       163 |         bean = null;
       164 | 
       165 |     }
       166 | 

Added src/test/java/org/apache/commons/beanutils2/BeanComparatorTestCase.java (line 63)

        58 | 
        59 |     /**
        60 |      * Set up instance variables required by this test case.
        61 |      */
        62 |     @Override
>>>     63 |     public void setUp() {
        64 |         bean = new TestBean();
        65 |         alphaBean1 = new AlphaBean("alphaBean1");
        66 |         alphaBean2 = new AlphaBean("alphaBean2");
        67 | 
        68 | 

Added src/test/java/org/apache/commons/beanutils2/BeanComparatorTestCase.java (line 83)

        78 | 
        79 |     /**
        80 |      * Tear down instance variables required by this test case.
        81 |      */
        82 |     @Override
>>>     83 |     public void tearDown() {
        84 |         bean = null;
        85 |         alphaBean1 = null;
        86 |         alphaBean2 = null;
        87 |     }
        88 | 

Added src/test/java/org/apache/commons/beanutils2/BeanUtilsBenchCase.java (line 84)

        79 | 
        80 |     /**
        81 |      * Set up instance variables required by this test case.
        82 |      */
        83 |     @Override
>>>     84 |     public void setUp() throws Exception {
        85 | 
        86 |         // Set up loop counter (if property specified)
        87 |         final String prop = System.getProperty("counter");
        88 |         if (prop != null) {
        89 |             counter = Long.parseLong(prop);

Added src/test/java/org/apache/commons/beanutils2/BeanUtilsBenchCase.java (line 159)

       154 | 
       155 |     /**
       156 |      * Tear down instance variables required by this test case.
       157 |      */
       158 |     @Override
>>>    159 |     public void tearDown() {
       160 | 
       161 |         dynaClass = null;
       162 |         inBean = null;
       163 |         inDyna = null;
       164 |         inMap = null;

Added src/test/java/org/apache/commons/beanutils2/BeanUtilsTestCase.java (line 128)

       123 | 
       124 |     /**
       125 |      * Set up instance variables required by this test case.
       126 |      */
       127 |     @Override
>>>    128 |     public void setUp() {
       129 |         ConvertUtils.deregister();
       130 |         BeanUtilsBean.setInstance(new BeanUtilsBean());
       131 |         setUpShared();
       132 |     }
       133 | 

Added src/test/java/org/apache/commons/beanutils2/BeanUtilsTestCase.java (line 168)

       163 | 
       164 |     /**
       165 |      * Tear down instance variables required by this test case.
       166 |      */
       167 |     @Override
>>>    168 |     public void tearDown() {
       169 |         bean = null;
       170 |     }
       171 | 
       172 | 
       173 |     // ------------------------------------------------ Individual Test Methods

Added src/test/java/org/apache/commons/beanutils2/BeanificationTestCase.java (line 68)

        63 | 
        64 |     /**
        65 |      * Set up instance variables required by this test case.
        66 |      */
        67 |     @Override
>>>     68 |     public void setUp() {
        69 | 
        70 |         ConvertUtils.deregister();
        71 | 
        72 |     }
        73 | 

Added src/test/java/org/apache/commons/beanutils2/BeanificationTestCase.java (line 87)

        82 | 
        83 |     /**
        84 |      * Tear down instance variables required by this test case.
        85 |      */
        86 |     @Override
>>>     87 |     public void tearDown() {
        88 |         // No action required
        89 |     }
        90 | 
        91 | 
        92 |     // ------------------------------------------------ Individual Test Methods

Added src/test/java/org/apache/commons/beanutils2/ConstructorUtilsTestCase.java (line 55)

        50 | 
        51 |     /**
        52 |      * Set up instance variables required by this test case.
        53 |      */
        54 |     @Override
>>>     55 |     public void setUp() throws Exception {
        56 |         super.setUp();
        57 |     }
        58 | 
        59 | 
        60 |     /**

Added src/test/java/org/apache/commons/beanutils2/ConstructorUtilsTestCase.java (line 71)

        66 | 
        67 |     /**
        68 |      * Tear down instance variables required by this test case.
        69 |      */
        70 |     @Override
>>>     71 |     public void tearDown() throws Exception {
        72 |         super.tearDown();
        73 |     }
        74 | 
        75 | 
        76 |     // ------------------------------------------------ Individual Test Methods

Added src/test/java/org/apache/commons/beanutils2/ConvertUtilsTestCase.java (line 67)

        62 | 
        63 |     /**
        64 |      * Set up instance variables required by this test case.
        65 |      */
        66 |     @Override
>>>     67 |     public void setUp() {
        68 | 
        69 |         ConvertUtils.deregister();
        70 | 
        71 |     }
        72 | 

Added src/test/java/org/apache/commons/beanutils2/ConvertUtilsTestCase.java (line 86)

        81 | 
        82 |     /**
        83 |      * Tear down instance variables required by this test case.
        84 |      */
        85 |     @Override
>>>     86 |     public void tearDown() {
        87 |         // No action required
        88 |     }
        89 | 
        90 | 
        91 |     // ------------------------------------------------ Individual Test Methods
S9343 (java) on eclipse-jetty - 0 issues removed, 35 issues added - new ruling file

Added jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java (line 127)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java (line 137)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java (line 448)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/SocketChannelEndPoint.java (line 200)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/SocketChannelEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java (line 384)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java (line 1240)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java (line 1347)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java)

Added jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java (line 711)

(source file not found at this revision: jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java)

Added jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java (line 115)

(source file not found at this revision: jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java)

Added jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java (line 164)

(source file not found at this revision: jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java (line 757)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java (line 921)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java (line 1516)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java (line 380)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java (line 98)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java)
S9343 (java) on eclipse-jetty-similar-to-main - 0 issues removed, 23 issues added - new ruling file

Added jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java (line 127)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java (line 137)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java (line 448)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ByteArrayEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/SocketChannelEndPoint.java (line 200)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/SocketChannelEndPoint.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java (line 384)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java (line 1240)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java)

Added jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java (line 1347)

(source file not found at this revision: jetty-io/src/main/java/org/eclipse/jetty/io/ssl/SslConnection.java)

Added jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java (line 711)

(source file not found at this revision: jetty-io/src/test/java/org/eclipse/jetty/io/SocketChannelEndPointTest.java)

Added jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java (line 115)

(source file not found at this revision: jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java)

Added jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java (line 164)

(source file not found at this revision: jetty-jmx/src/main/java/org/eclipse/jetty/jmx/ConnectorServer.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java (line 757)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java (line 921)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java (line 1516)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/HttpOutput.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java (line 380)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java)

Added jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java (line 98)

(source file not found at this revision: jetty-server/src/main/java/org/eclipse/jetty/server/handler/ResourceHandler.java)
S9343 (java) on guava - 0 issues removed, 39 issues added - new ruling file

Added src/com/google/common/base/Splitter.java (line 184)

       179 |         new Strategy() {
       180 |           @Override
       181 |           public SplittingIterator iterator(Splitter splitter, CharSequence toSplit) {
       182 |             return new SplittingIterator(splitter, toSplit) {
       183 |               @Override
>>>    184 |               public int separatorStart(int start) {
       185 |                 int separatorLength = separator.length();
       186 | 
       187 |                 positions:
       188 |                 for (int p = start, last = toSplit.length() - separatorLength; p <= last; p++) {
       189 |                   for (int i = 0; i < separatorLength; i++) {

Added src/com/google/common/base/Splitter.java (line 200)

       195 |                 }
       196 |                 return -1;
       197 |               }
       198 | 
       199 |               @Override
>>>    200 |               public int separatorEnd(int separatorPosition) {
       201 |                 return separatorPosition + separator.length();
       202 |               }
       203 |             };
       204 |           }
       205 |         });

Added src/com/google/common/base/Splitter.java (line 236)

       231 |           @Override
       232 |           public SplittingIterator iterator(final Splitter splitter, CharSequence toSplit) {
       233 |             final Matcher matcher = separatorPattern.matcher(toSplit);
       234 |             return new SplittingIterator(splitter, toSplit) {
       235 |               @Override
>>>    236 |               public int separatorStart(int start) {
       237 |                 return matcher.find(start) ? matcher.start() : -1;
       238 |               }
       239 | 
       240 |               @Override
       241 |               public int separatorEnd(int separatorPosition) {

Added src/com/google/common/collect/AbstractMapBasedMultimap.java (line 1270)

      1265 |     AsMap(Map<K, Collection<V>> submap) {
      1266 |       this.submap = submap;
      1267 |     }
      1268 | 
      1269 |     @Override
>>>   1270 |     protected Set<Entry<K, Collection<V>>> createEntrySet() {
      1271 |       return new AsMapEntries();
      1272 |     }
      1273 | 
      1274 |     // The following methods are included for performance.
      1275 | 

Added src/com/google/common/collect/ConcurrentHashMultiset.java (line 491)

       486 |       }
       487 |     };
       488 |   }
       489 | 
       490 |   @Override
>>>    491 |   public Set<Multiset.Entry<E>> createEntrySet() {
       492 |     return new EntrySet();
       493 |   }
       494 | 
       495 |   @Override
       496 |   int distinctElements() {

Added src/com/google/common/collect/ConsumingQueueIterator.java (line 43)

        38 |   ConsumingQueueIterator(Queue<T> queue) {
        39 |     this.queue = checkNotNull(queue);
        40 |   }
        41 | 
        42 |   @Override
>>>     43 |   public T computeNext() {
        44 |     return queue.isEmpty() ? endOfData() : queue.remove();
        45 |   }
        46 | }

Added src/com/google/common/collect/ForwardingNavigableMap.java (line 284)

       279 |     NavigableMap<K, V> forward() {
       280 |       return ForwardingNavigableMap.this;
       281 |     }
       282 | 
       283 |     @Override
>>>    284 |     protected Iterator<Entry<K, V>> entryIterator() {
       285 |       return new Iterator<Entry<K, V>>() {
       286 |         private Entry<K, V> toRemove = null;
       287 |         private Entry<K, V> nextOrNull = forward().lastEntry();
       288 | 
       289 |         @Override

Added src/com/google/common/collect/Maps.java (line 776)

       771 |       this.set = checkNotNull(set);
       772 |       this.function = checkNotNull(function);
       773 |     }
       774 | 
       775 |     @Override
>>>    776 |     public Set<K> createKeySet() {
       777 |       return removeOnlySet(backingSet());
       778 |     }
       779 | 
       780 |     @Override
       781 |     Collection<V> createValues() {

Added src/com/google/common/collect/Maps.java (line 823)

       818 |     public void clear() {
       819 |       backingSet().clear();
       820 |     }
       821 | 
       822 |     @Override
>>>    823 |     protected Set<Entry<K, V>> createEntrySet() {
       824 |       @WeakOuter
       825 |       class EntrySetImpl extends EntrySet<K, V> {
       826 |         @Override
       827 |         Map<K, V> map() {
       828 |           return AsMapView.this;

Added src/com/google/common/collect/Maps.java (line 2724)

      2719 |       super(unfiltered, entryPredicate);
      2720 |       this.keyPredicate = keyPredicate;
      2721 |     }
      2722 | 
      2723 |     @Override
>>>   2724 |     protected Set<Entry<K, V>> createEntrySet() {
      2725 |       return Sets.filter(unfiltered.entrySet(), predicate);
      2726 |     }
      2727 | 
      2728 |     @Override
      2729 |     Set<K> createKeySet() {

Added src/com/google/common/collect/Multimaps.java (line 128)

       123 |         Supplier<? extends Collection<V>> factory) {
       124 |       super(map);
       125 |       this.factory = checkNotNull(factory);
       126 |     }
       127 | 
>>>    128 |     @Override protected Collection<V> createCollection() {
       129 |       return factory.get();
       130 |     }
       131 | 
       132 |     // can't use Serialization writeMultimap and populateMultimap methods since
       133 |     // there's no way to generate the empty backing map.

Added src/com/google/common/collect/Multimaps.java (line 210)

       205 |         Supplier<? extends List<V>> factory) {
       206 |       super(map);
       207 |       this.factory = checkNotNull(factory);
       208 |     }
       209 | 
>>>    210 |     @Override protected List<V> createCollection() {
       211 |       return factory.get();
       212 |     }
       213 | 
       214 |     /** @serialData the factory and the backing map */
       215 |     @GwtIncompatible("java.io.ObjectOutputStream")

Added src/com/google/common/collect/Multimaps.java (line 288)

       283 |         Supplier<? extends Set<V>> factory) {
       284 |       super(map);
       285 |       this.factory = checkNotNull(factory);
       286 |     }
       287 | 
>>>    288 |     @Override protected Set<V> createCollection() {
       289 |       return factory.get();
       290 |     }
       291 | 
       292 |     /** @serialData the factory and the backing map */
       293 |     @GwtIncompatible("java.io.ObjectOutputStream")

Added src/com/google/common/collect/Sets.java (line 723)

       718 |     return new SetView<E>() {
       719 |       @Override public Iterator<E> iterator() {
       720 |         final Iterator<? extends E> itr1 = set1.iterator();
       721 |         final Iterator<? extends E> itr2 = set2.iterator();
       722 |         return new AbstractIterator<E>() {
>>>    723 |           @Override public E computeNext() {
       724 |             while (itr1.hasNext()) {
       725 |               E elem1 = itr1.next();
       726 |               if (!set2.contains(elem1)) {
       727 |                 return elem1;
       728 |               }

Added src/com/google/common/collect/SortedLists.java (line 115)

       110 |      * Return the index of the first list element that compares as greater than the key, or {@code
       111 |      * list.size()} if there is no such element.
       112 |      */
       113 |     FIRST_AFTER {
       114 |       @Override
>>>    115 |       public <E> int resultIndex(
       116 |           Comparator<? super E> comparator, E key, List<? extends E> list, int foundIndex) {
       117 |         return LAST_PRESENT.resultIndex(comparator, key, list, foundIndex) + 1;
       118 |       }
       119 |     },
       120 |     /**
S9343 (java) on sonar-server - 0 issues removed, 7 issues added - new ruling file

Added src/main/java/org/sonar/server/computation/task/projectanalysis/formula/coverage/LinesAndConditionsWithUncoveredVariationCounter.java (line 34)

(source file not found at this revision: src/main/java/org/sonar/server/computation/task/projectanalysis/formula/coverage/LinesAndConditionsWithUncoveredVariationCounter.java)

Added src/main/java/org/sonar/server/platform/Platform.java (line 115)

(source file not found at this revision: src/main/java/org/sonar/server/platform/Platform.java)

Added src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java (line 73)

(source file not found at this revision: src/main/java/org/sonar/server/platform/platformlevel/PlatformLevel1.java)

Added src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java (line 50)

(source file not found at this revision: src/main/java/org/sonar/server/plugins/StaticResourcesServlet.java)

Added src/main/java/org/sonar/server/ws/ServletRequest.java (line 91)

(source file not found at this revision: src/main/java/org/sonar/server/ws/ServletRequest.java)

Added src/test/java/org/sonar/server/platform/ServerTesterPlatform.java (line 33)

(source file not found at this revision: src/test/java/org/sonar/server/platform/ServerTesterPlatform.java)

Added src/test/java/org/sonar/server/platform/ServerTesterPlatform.java (line 41)

(source file not found at this revision: src/test/java/org/sonar/server/platform/ServerTesterPlatform.java)

🤖 Generated with GitHub Actions
@github-actions

Copy link
Copy Markdown
Contributor

Ruling needs updating. A fix PR has been created: #5947

Please review and merge it into your branch.

romainbrenguier and others added 3 commits August 18, 2026 13:28
…cleanup, and test coverage

- Remove invalid `implements Runnable2` from SuperWithProtected (interface
  methods are implicitly public, so declaring execute2() as protected caused
  a compilation error)
- Guard against unknown access levels in reportIfAccessIncreased to prevent
  false positives on unresolved symbols
- Eliminate duplicate cast to Symbol.MethodSymbol by introducing a local
  variable in checkStaticMethodHiding
- Add non-compiling test for unknown parameter types and partial semantics
- Add bytecode parent test (ClassLoader.findClass) to cover the null
  declaration branch in reportIfAccessIncreased

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract hasSameParameterTypes into MethodTreeUtils to eliminate
cross-file duplication with StaticMethodHidingCheck. Replace local
getPackage/samePackage with JUtils.getPackage. Add test cases for
static hiding edge cases (field name collision, instance vs static,
parameter count mismatch) and interface+class hierarchy overrides.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…erage

Move illegal static-hiding-instance test case to non-compiling samples to
fix compilation error that broke annotation resolution across all test files
(causing S9149 StaticMethodHidingCheckTest to fail with 4 false positives).

Extract findHiddenStaticMethod helper into MethodTreeUtils to eliminate
duplication between S9343 and S9149 static method hiding traversal logic.
Add additional edge-case test scenarios for coverage.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@gitar-bot

gitar-bot Bot commented Aug 18, 2026

Copy link
Copy Markdown
Code Review ✅ Approved 3 resolved / 3 findings

Implements new rule S9343 to detect methods that inappropriately increase accessibility, addressing the static hiding path, instance override inspection, and unknown parent access level issues. No issues found.

✅ 3 resolved
Edge Case: Static hiding path ignores package visibility across packages

📄 java-checks/src/main/java/org/sonar/java/checks/MethodOverrideAccessibilityCheck.java:67-81 📄 java-checks/src/main/java/org/sonar/java/checks/MethodOverrideAccessibilityCheck.java:71-73
In checkStaticMethodHiding, a superclass static method is treated as "hidden" whenever it is non-private and matches the name/signature. But a package-private static method in a superclass located in a different package is not inherited or hidden by the subclass method — the subclass declares an independent method. This produces a false positive (e.g. Increase of accessibility from "package-private" to "public" when hiding method) for legitimate code where parent and child are in different packages. Guard the package-private case by checking that the parent method's package matches the child's package before flagging, similar to how the instance path relies on overriddenSymbols() which already accounts for package visibility.

Edge Case: Instance override only inspects overriddenSymbols.get(0)

📄 java-checks/src/main/java/org/sonar/java/checks/MethodOverrideAccessibilityCheck.java:51-65
checkInstanceMethodOverride only examines the first entry of overriddenSymbols() and returns early when that entry's owner is an interface. When a method both implements an interface method and overrides a superclass method (e.g. class B extends A implements I where A.m is protected and the method is made public), the interface entry may come first, causing the widening of the class method to go unreported (false negative). Iterate over overriddenSymbols() to find the nearest non-interface overridden method rather than only checking index 0.

Edge Case: Unknown parent access level can trigger "from unknown" report

📄 java-checks/src/main/java/org/sonar/java/checks/MethodOverrideAccessibilityCheck.java:95-100 📄 java-checks/src/main/java/org/sonar/java/checks/MethodOverrideAccessibilityCheck.java:113-119
In reportIfAccessIncreased, accessLevel() returns -1 when a symbol's visibility cannot be classified. For static hiding, the candidate only passes the !isPrivate/!isPackageVisibility filters but could still fall through all of accessLevel()'s branches (returning -1). Since childLevel (e.g. 3 for public) > -1, this reports a misleading "Increase of accessibility from "unknown" to "public"". Guard against unknown levels by returning early when parentLevel < 0 (or childLevel < 0) so incompletely-resolved symbols don't produce false positives.

Implementation Status ✅ 1 / 1 issues implemented
SONARJAVA-6779 — 1 / 1 objectives

The PR successfully implements the new rule S9343 to detect when methods increase accessibility during overriding or hiding, complete with check logic, tests, documentation, and ruling results.

✅ 1 complete
  • ✅ Implement new rule S9343: Methods should not increase accessibility when overriding or hiding
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

@romainbrenguier
romainbrenguier marked this pull request as ready for review August 18, 2026 13:50

@nathsou nathsou left a comment

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.

Requesting changes: these three blockers create unavoidable false positives or publish rule assets that diverge from the S9343 RSPEC. Please address them before merge.

}

private void checkInstanceMethodOverride(MethodTree methodTree, Symbol.MethodSymbol methodSymbol) {
List<Symbol.MethodSymbol> overriddenSymbols = methodSymbol.overriddenSymbols();

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.

[P1] Exclude java.lang.Object#clone() from this rule. Object.clone() is protected, while a public override is the conventional usable clone API and is the compliant style documented by S2157. This lookup selects the non-interface Object#clone() and reports a protected-to-public increase; the new ruling baseline already contains that false positive at BeanMap.java:209.

private void checkInstanceMethodOverride(MethodTree methodTree, Symbol.MethodSymbol methodSymbol) {
List<Symbol.MethodSymbol> overriddenSymbols = methodSymbol.overriddenSymbols();
overriddenSymbols.stream()
.filter(s -> !s.owner().isInterface())

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.

[P1] Do not discard a public superinterface before deciding whether the access increase is actionable. In the new AbstractDoer / ConcreteDoer sample, ConcreteDoer#doIt must be public to implement Doer#doIt; keeping the protected superclass visibility does not compile. Filtering interfaces here makes the rule report an impossible-to-fix issue. Compare against the most accessible overridden declaration (or skip when an interface requires the child visibility).

"func": "Constant\/Issue",
"constantCost": "5min"
},
"tags": [

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.

[P1] Regenerate the plugin rule assets from the S9343 RSPEC branch instead of committing hand-written copies. This directory's README declares these files generated; draft RSPEC #7894 has tags encapsulation, design, and pitfall, plus MODULAR (not CLEAR). The committed HTML also omits RSPEC's potential-impact section and other explanatory text. Generate from rule/add-RSPEC-S9343 and commit the resulting JSON/HTML/profile assets.

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