forcedotcom / forcedotcom/code-analyzer

[BUG][code-analyzer] sfge: a cast or array load inside a new SObject(Field = ...) constructor aborts the entry point

Aperta
#2,089 0 commenti 0 reazioni 0 assegnatari Vedi su GitHub
Lingua principale
TypeScript
Stelle
240
Fork
52
Merge medio
1g 23h
PR unite (30g)
5

Descrizione

### Have you tried to resolve this issue yourself first?

- [x] I confirm I have gone through the above steps and still have an issue to report.

### Bug Description

**Engine:** `sfge` (Salesforce Graph Engine) · **Rule:** `ApexFlsViolation` (DevPreview) · **Selector:** `--rule-selector sfge`

A cast or an array load used as a value inside a `new SObject(Field = ...)` constructor aborts path evaluation. Both are the same underlying contract problem in `SFVertexFactory`.

`NewKeyValueObjectExpressionVertex` loads its item vertices with a supplemental parameter (`NewKeyValueObjectExpressionVertex.java:57-65` passes `ExpressionType.KEY_VALUE`). `SFVertexFactory.load` then *requires* a two-argument constructor:

```java
// SFVertexFactory.java:231-238
if (supplementalParam != null) {
Constructor constructor = optClazz.get().getDeclaredConstructor(Map.class, Object.class);
result = (T) constructor.newInstance(vertexProperties, supplementalParam);
}
```

`getDeclaredConstructor` does not search superclasses.

**A. Cast.** `CastExpressionVertex` declares only `(Map)`, so the lookup throws `NoSuchMethodException`, rewrapped as `UnexpectedException` at `SFVertexFactory.java:245`.

```apex
Account a = new Account(Name = (String) rawName);
```

**B. Array load.** `ArrayLoadExpressionVertex` *has* the two-argument constructor but rejects the supplemental parameter outright:

```java
// ArrayLoadExpressionVertex.java:34-38
ArrayLoadExpressionVertex(Map properties, Object supplementalParam) {
super(properties, TRAVERSAL_CONSUMER);
if (supplementalParam != null) {
throw new UnexpectedException(supplementalParam);
}
}
```

Since `ChainedVertex(Map, Object)` discards the supplemental parameter anyway — its own constructor is annotated `// TODO: supplementalParam is ignored` — there is nothing for that branch to protect. Note the user-visible message for B is `null`; the informative `KEY_VALUE` payload only reaches the sfge log.

### Output / Logs

```shell
# A — cast as a key-value item
UnexpectedException: com.salesforce.graph.vertex.CastExpressionVertex.(java.util.Map, java.lang.Object):
com.salesforce.graph.vertex.SFVertexFactory.load(SFVertexFactory.java:245);
com.salesforce.graph.vertex.SFVertexFactory$1.apply(SFVertexFactory.java:186);
com.salesforce.graph.cache.AbstractVertexCacheImpl.get(AbstractVertexCacheImpl.java:101);
com.salesforce.graph.vertex.LazyVertexList.initialize(LazyVertexList.java:47)

# B — array load as a key-value item (violation message is just "null")
UnexpectedException: null: com.salesforce.graph.vertex.SFVertexFactory.load(SFVertexFactory.java:245); ...
Caused by: com.salesforce.exception.UnexpectedException: KEY_VALUE
at com.salesforce.graph.vertex.ArrayLoadExpressionVertex.(ArrayLoadExpressionVertex.java:37)
```

### Steps To Reproduce

1. Create an empty SFDX project (`sfdx-project.json` with a single `force-app` package directory).
2. Add both classes under `force-app/main/default/classes/`, each with a standard `.cls-meta.xml` (apiVersion 62.0):

```apex
public with sharing class CastInKeyValue {
@AuraEnabled
public static void run(Object rawName) {
Account a = new Account(Name = (String) rawName);
insert a;
}
}
```
```apex
public with sharing class ArrayLoadInKeyValue {
private static final List PARTS = String.valueOf(ArrayLoadInKeyValue.class).split('[.]', 2);
@AuraEnabled
public static void run() {
insert new Account(Name = PARTS[0]);
}
}
```
3. Add `code-analyzer.yml`:
```yaml
engines:
sfge:
java_thread_timeout: 900000
java_thread_count: 4
```
4. Run `sf code-analyzer run --rule-selector sfge --workspace . --config-file code-analyzer.yml`.
5. Both classes report an `InternalExecutionError` and yield no findings for their entry point.

### Expected Behavior

Both should analyse normally. `new Account(Name = (String) x)` and `new Account(Name = list[0])` are ordinary Apex.

The narrow fix for A is one constructor, mirroring the eleven vertex classes that already have it (`BinaryExpressionVertex`, `BooleanExpressionVertex`, `MethodCallExpressionVertex`, …). Since `ChainedVertex(Map, Object)` ignores the parameter, it is a pure delegation. The fix for B is to drop the `throw` on that branch.

A more durable fix would be for `SFVertexFactory.load` to fall back to the one-argument constructor when the two-argument form is absent, rather than requiring every vertex type to opt in.

### Operating System

macOS 26.5.2

### Salesforce CLI Version

@salesforce/cli/2.147.7 darwin-arm64 node-v24.5.0

### Code Analyzer Plugin (code-analyzer) Version

code-analyzer 5.15.0

### Node Version

v24.5.0

### Java Version

openjdk version "11.0.32" 2026-07-21

### Python Version

N/A

### Additional Context (Screenshots, Files, etc)

**Blast radius.** `CastExpression` and `ArrayLoadExpression` are not the only vertices that can appear as a key-value item. Auditing `graph/vertex/` on `code-analyzer-core@dev`, **15 other concrete `ChainedVertex` descendants also lack the two-argument constructor** (and have no `Builder.create(Map, Object)` to stand in), so they should fail the same way: `AssignmentExpressionVertex`, `BindExpressionsVertex`, `ClassRefExpressionVertex`, `EmptyReferenceExpressionVertex`, `InstanceOfExpressionVertex`, `MapEntryNodeVertex`, `NewKeyValueObjectExpressionVertex`, `NewObjectExpressionVertex`, `ParameterVertex`, `PostfixExpressionVertex`, `ReferenceExpressionVertex`, `SoqlExpressionVertex`, `SuperMethodCallExpressionVertex`, `ThisMethodCallExpressionVertex`, `TriggerVariableExpressionVertex`.

Related: #886 reported a `CastExpressionVertex` `UnexpectedException` in 2022 from a different path (`MethodPathBuilderVisitor` on `switch on (String) x`); #973 reported the array-load half. Both were closed in the 2026-06-30 pre-v5 sweep; this is a different call path on 5.15.0.

### Workaround

Hoist the expression into a local first:

```apex
String name = (String) rawName;
Account a = new Account(Name = name);
```

Works, but it means the constructor-initialiser syntax cannot be used with a cast or an index anywhere in the codebase.

### Urgency

Moderate

Guida per i contributori

Apri la guida per i contributori

Direzione di ricerca

Start with SFVertexFactory.java, CastExpressionVertex.java, ArrayLoadExpressionVertex.java, and ChainedVertex.java, focusing on the constructors and load path cited in the report. Reproduce both Apex examples with the provided sf code-analyzer command, then verify that each entry point analyzes normally without InternalExecutionError or a missing finding.

Scritto dal modello di indicizzazione a partire dal testo della issue.

Valutazione

Stack tecnologico
java
Ambito
tooling
Tipo di issue
Bug
Difficoltà
3/5
Tempo stimato
1-2 giorni
Stato di attività
Attiva
Chiarezza
Specificata chiaramente
Idoneità per principianti
72/100

Ricevi le nuove issue nella tua casella

Un breve riepilogo di issue GitHub adatte ai principianti.