forcedotcom / forcedotcom/code-analyzer

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

Abierto
#2,089 0 comentarios 0 reacciones 0 asignados Ver en GitHub
Lenguaje dominante
TypeScript
Estrellas
240
Forks
52
Merge medio
1 d 23 h
PR fusionados (30 d)
5

Descripción

### 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

Guía de contribución

Abrir la guía de contribución

Línea de trabajo

Comienza con SFVertexFactory.java, CastExpressionVertex.java, ArrayLoadExpressionVertex.java y ChainedVertex.java, centrándote en los constructores y en la ruta de carga citada en el informe. Reproduce ambos ejemplos de Apex con el comando sf code-analyzer proporcionado y verifica después que cada punto de entrada se analiza normalmente, sin InternalExecutionError ni ningún hallazgo ausente.

Escrito por el modelo de indexación a partir del texto del issue.

Evaluación

Stack tecnológico
java
Área
tooling
Tipo de issue
Error
Dificultad
3/5
Tiempo estimado
1-2 días
Estado de actividad
Activo
Claridad
Bien especificado
Aptitud para principiantes
72/100

Recibe los nuevos issues en tu correo

Un resumen breve de issues de GitHub para principiantes.