forcedotcom / forcedotcom/code-analyzer

[BUG][code-analyzer] sfge: a guard condition on a SOQL result aborts the entry point (TodoException in SchemaBasedValidationAnalyzer)

オープン
#2,095 コメント 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`

`SchemaBasedValidationAnalyzer.getDerivedApexValue` accepts four `ApexValue` kinds and throws for everything else:

```java
// SchemaBasedValidationAnalyzer.java:85-91
if (!(apexValue instanceof ApexBooleanValue)
&& !(apexValue instanceof ApexCustomValue)
&& !(apexValue instanceof ApexForLoopValue)
&& !(apexValue instanceof ApexSingleValue)) {
throw new TodoException(
"What should I do if ApexValue from StandardCondition is not an ApexBooleanValue, "
+ "ApexCustomValue, ApexForLoopValue, or ApexSingleValue: ApexValue=" ...
```

`ApexSoqlValue` reaches it easily: `ApexSoqlValue.apply` returns `Optional.empty()` for every method it does not model, so a condition calling an unmodelled method on a SOQL-derived value resolves back to the `ApexSoqlValue` itself.

The triggering shape is "query with `WITH USER_MODE`, guard, loop, branch on a field, DML" — the single most common shape in a Lightning controller.

### Output / Logs

```shell
TodoException: What should I do if ApexValue from StandardCondition is not an ApexBooleanValue,
ApexCustomValue, ApexForLoopValue, or ApexSingleValue: ApexValue=ApexValue(ApexSoqlValue)
{status=INITIALIZED, declarationVertex=null, valueVertex=SoqlExpression{...Query=[ SELECT Id, Title,
FileType, Checksum FROM ContentVersion WHERE Id IN :versionIds WITH USER_MODE ]...}, resolvedValues={},
returnedFrom=null, invocableExpression=null, method=null}, parent=Unknown{conditionType=UNKNOWN, ...},
vertex=MethodCallExpressionVertex{fullMethodName=file.FileType.startsWith, ...}:
com.salesforce.rules.fls.apex.operations.SchemaBasedValidationAnalyzer.getDerivedApexValue(SchemaBasedValidationAnalyzer.java:89);
com.salesforce.rules.fls.apex.operations.SchemaBasedValidationAnalyzer.getDerivedApexValue(SchemaBasedValidationAnalyzer.java:117);
com.salesforce.rules.fls.apex.operations.SchemaBasedValidationAnalyzer.checkForValidation(SchemaBasedValidationAnalyzer.java:74); ...
```

### Steps To Reproduce

1. Create an empty SFDX project (`sfdx-project.json` with a single `force-app` package directory).
2. Add `force-app/main/default/classes/SoqlDerivedCondition.cls` with the class shown below, plus a standard `SoqlDerivedCondition.cls-meta.xml` (apiVersion 62.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. The run reports an `InternalExecutionError` for the entry point instead of analysing it. That entry point yields no `ApexFlsViolation` findings at all, and nothing in the summary indicates coverage was lost.

```apex
public with sharing class SoqlDerivedCondition {
@AuraEnabled
public static void run(Set versionIds) {
List fileData = [
SELECT Id, Title, FileType, Checksum
FROM ContentVersion
WHERE Id IN :versionIds
WITH USER_MODE
];
if (fileData == null || fileData.isEmpty()) {
return;
}
List out = new List();
for (ContentVersion file : fileData) {
String endpoint = 'https://example.com/';
if (file.FileType.startsWith('image')) {
endpoint += 'images';
} else {
endpoint += 'documents';
}
file.Title = endpoint;
out.add(file);
}
update out;
}
}
```

### Expected Behavior

A condition the analyzer cannot interpret is not evidence of anything — it should contribute no schema-based validation and let evaluation continue. The sibling branch twenty lines above already does exactly that:

```java
if (!apexValueOptional.isPresent()) {
// If standard condition does not resolve to an ApexValue, there isn't much we can do
return results;
}
```

Throwing instead costs the whole entry point's coverage over a condition that was never going to be a sanitizer.

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

5 occurrences across 3 distinct `@AuraEnabled` entry points in our codebase.

Reported verbatim as #1747 in February 2025 and closed as a duplicate of #1497. #1497 has since been closed too, so there is no longer an open home for it.

### Workaround

Hoist the field into a local before the condition (`String fileType = file.FileType;` then branch on `fileType`). Fragile — it depends on which method the condition calls.

### Urgency

Moderate

コントリビューションガイド

コントリビューションガイドを開く

調査の方向性

SchemaBasedValidationAnalyzer.java から始め、特に getDerivedApexValue と、レポートで説明されている ApexSoqlValue のパスを確認してください。提供された SoqlDerivedCondition エントリポイントに対して sfge コマンドを再現します。解釈できない条件によってエントリポイントが中断されず、InternalExecutionError なしで分析が継続すれば完了です。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
java
領域
devtools
issue の種類
バグ
難易度
3/5
見積もり時間
1〜2日
活発さ
活発
明瞭さ
明確に書かれている
初心者へのやさしさ
72/100

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

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