Skip to content

Fix CodeQL note-severity alerts: shadowed locals, and the array cases of the debug formatter - #848

Merged
vharseko merged 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:codeql/type-tests-and-shadowing
Aug 5, 2026
Merged

Fix CodeQL note-severity alerts: shadowed locals, and the array cases of the debug formatter#848
vharseko merged 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:codeql/type-tests-and-shadowing

Conversation

@vharseko

@vharseko vharseko commented Aug 4, 2026

Copy link
Copy Markdown
Member

Addresses the seven java/local-shadows-field alerts and the java/chained-type-tests group. The one chain which was not dispatch over a fixed API turned out to be missing a case.

Locals which shadow the field they are about to fill, or the field next to them (7)

  • TopologyMsg — the decoding constructor builds replicaInfos and rsInfos in locals of exactly those names, then assigns the fields from them. The locals become newReplicaInfos and newRsInfos, so this. is no longer what tells the two apart.
  • PatternIP.evaluate() — the local ipType holds the address family of the remote address and is compared against the field, which holds the family of the rule: if (ipType != this.ipType). The local is now remoteIpType and the comparison reads remoteIpType != ipType.
  • NewGroupPanel.createLayout() — the local JLabel[] labels holds the three labels being laid out, while the field of the same name holds four and is what setText() walks later. The local becomes layoutLabels.
  • BrowseEntriesPanel — the reader Runnable reports its outcome through a field t, and the surrounding catch (Throwable t) shadowed it. The catch parameter is now th, matching the inner catch a few lines above.
  • PerformanceRunner.WorkerThread.run() — the local connection is the connection used for one operation, the field is the connection the thread was given (null when it must borrow one per operation). The local becomes connectionToUse, which also removes the need for this. in if (this.connection == null).
  • AggregationPropertyDefinition — in the catch block a local message shadowed the field of the same name that the try block adds to the unacceptable reasons. It becomes errorMessage.

None of these change behaviour: every reference already resolved to what the code intended, which is why they are alerts about names rather than bugs.

The one type-test chain worth collapsing — and the type it had forgotten

DebugMessageFormatter.decorateArg() tested for Object[] and then for each primitive array type in turn, dispatching to seven decorateArrayArg() overloads whose bodies were byte-for-byte identical. short[] was missing from the chain, so a short[] argument was never decorated and a debug log line showed [S@6d06d69c instead of its contents.

The seven overloads become one method taking Object and walking the array through java.lang.reflect.Array, which covers every component type including the forgotten one. Object[] keeps its own branch, because its elements are decorated recursively and the elements of a primitive array never need that. The chain goes from ten type tests to three.

DebugMessageFormatter had no tests. The new DebugMessageFormatterTest pins the formatting of each array type, of nested maps, lists and object arrays, and of the fallback taken when the format string does not match its arguments — 13 cases.

The other 15 chains, left as they are

They all dispatch over a closed family of types owned by an API which cannot grow a visitor:

  • OperationsSdkConnectionAdapter, HTTPClientConnection, BoundedWorkQueueStrategy dispatch over AbandonOperationUnbindOperation.
  • Replication messagesServerReader and ReplicationDomain dispatch over ReplicationMsg subtypes.
  • SDK requestsRequests.copyOfRequest() (21 tests), Connections, and AuthRate dispatch over the public Request hierarchy; adding an accept(RequestVisitor) to it is an API break.
  • JAAS callbacksSASLBindClientImpl.handle(Callback[]) dispatches over javax.security.auth.callback types, which are third-party.
  • JAXB typesDSMLServlet.performLDAPRequest() dispatches over the DSML request classes, which are generated from the schema.
  • Configuration and schemaConfigFromConnection, ConfigFromFile, BrowseSchemaPanel and SchemaElementComboBoxCellRenderer dispatch over the generated *Cfg/*CfgClient interfaces and over the SDK schema elements.
  • Value typesMonitorData.add() picks a syntax from the runtime class of a monitor value, relying on instanceof matching subclasses (Integer through Number), which a class-keyed map would not do.

While reading them I checked each chain for the failure this alert often hides — a subtype tested after its supertype, which makes the later branch dead. There is none: in ServerReader, the types tested after RoutableMsg (ResetGenerationIdMsg, WindowProbeMsg, TopologyMsg, ChangeStatusMsg, ChangeTimeHeartbeatMsg, StopMsg) all extend ReplicationMsg directly, and in MonitorData the Number subclasses are tested before Number. Every chain either ends in an else or falls through to a documented result.

Testing

  • opendj-config 547 tests (covering AggregationPropertyDefinition) and opendj-ldap-toolkit 291 tests (covering PerformanceRunner) — all passing.
  • opendj-server-legacy (-Pprecommit): SynchronizationMsgTest (75), ProtocolCompatibilityTest (58) and ModifyDNMsgTest (5) encode and decode TopologyMsg across protocol versions, IPTestCase (58) covers the IP bind rules of PatternIP, and DebugMessageFormatterTest (13) with DebugLogPublisherTest (6) cover the formatter — 215 tests, all passing.
  • The two control panel changes are in Swing code with no test coverage; both are renames the compiler resolves.

…he debug formatter

CodeQL java/local-shadows-field and java/chained-type-tests.

Seven locals carried the name of a field in scope: the two collections
TopologyMsg decodes before assigning them to the fields, the address
family PatternIP.evaluate() computes for the remote address and compares
against the rule's, the three labels NewGroupPanel lays out next to the
field holding four, the Throwable a catch clause hid from the Runnable
reporting through it, the per-operation connection of a worker thread,
and a message built in a catch block. All are renamed after what they
hold; no reference changes meaning.

DebugMessageFormatter dispatched over eight array types to seven
decorateArrayArg() overloads with identical bodies, and short[] was not
among them - such an argument reached the log undecorated, as [S@6d06d69c.
A single method walking the array through java.lang.reflect.Array replaces
all seven and covers every component type. Object[] keeps its branch since
its elements are decorated in turn.

The class had no tests; DebugMessageFormatterTest now covers each array
type, nested maps, lists and object arrays, and the concatenation
fallback taken when the format string does not match its arguments.

The remaining fifteen type-test chains dispatch over operations,
replication messages, SDK requests, JAAS callbacks, generated JAXB and
configuration types: none can grow a visitor without breaking an API, and
none of them tests a subtype after its supertype.
@vharseko
vharseko requested a review from maximthomas August 4, 2026 11:42
@vharseko vharseko added java Pull requests that update java code bug tests Test suites: fixing, enabling, un-disabling labels Aug 4, 2026
@vharseko
vharseko merged commit 74fe31b into OpenIdentityPlatform:master Aug 5, 2026
17 checks passed
@vharseko
vharseko deleted the codeql/type-tests-and-shadowing branch August 5, 2026 08:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug java Pull requests that update java code tests Test suites: fixing, enabling, un-disabling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants