forcedotcom / forcedotcom/code-analyzer
[BUG][code-analyzer] sfge: a cast or array load inside a new SObject(Field = ...) constructor aborts the entry point
- 主要言語
- TypeScript
- スター
- 240
- フォーク
- 52
- 平均マージ
- 1日 23時間
- マージ済み PR(30日)
- 5
説明
### 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
コントリビューションガイド
調査の方向性
SFVertexFactory.java、CastExpressionVertex.java、ArrayLoadExpressionVertex.java、ChainedVertex.java から始め、レポートで引用されているコンストラクターとロードパスに注目してください。提供されている sf code-analyzer コマンドで両方の Apex の例を再現し、その後、各エントリーポイントが InternalExecutionError や finding の欠落なしに正常に解析されることを確認してください。
索引モデルが issue の本文から書いたものです。
評価
- 技術スタック
- java
- 領域
- tooling
- issue の種類
- バグ
- 難易度
- 3/5
- 見積もり時間
- 1〜2日
- 活発さ
- 活発
- 明瞭さ
- 明確に書かれている
- 初心者へのやさしさ
- 72/100