forcedotcom / forcedotcom/code-analyzer
[BUG][code-analyzer] sfge: a cast or array load inside a new SObject(Field = ...) constructor aborts the entry point
- 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
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