forcedotcom / forcedotcom/code-analyzer

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

オープン
#2,089 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る
主要言語
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

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。