fhirpath-ts 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -5
- package/dist/analyzer/analyze-dto.d.ts.map +1 -1
- package/dist/analyzer/analyze-dto.js +14 -8
- package/dist/analyzer/analyze-dto.js.map +1 -1
- package/dist/analyzer/analyze.d.ts +23 -12
- package/dist/analyzer/analyze.d.ts.map +1 -1
- package/dist/analyzer/analyze.js +138 -46
- package/dist/analyzer/analyze.js.map +1 -1
- package/dist/analyzer/declarations.d.ts +6 -0
- package/dist/analyzer/declarations.d.ts.map +1 -1
- package/dist/analyzer/declarations.js +7 -0
- package/dist/analyzer/declarations.js.map +1 -1
- package/dist/analyzer/expression-policy.d.ts +88 -14
- package/dist/analyzer/expression-policy.d.ts.map +1 -1
- package/dist/analyzer/expression-policy.js +296 -41
- package/dist/analyzer/expression-policy.js.map +1 -1
- package/dist/analyzer/operator-rules.d.ts.map +1 -1
- package/dist/analyzer/operator-rules.js +7 -6
- package/dist/analyzer/operator-rules.js.map +1 -1
- package/dist/analyzer/signatures.d.ts +49 -0
- package/dist/analyzer/signatures.d.ts.map +1 -1
- package/dist/analyzer/signatures.js +76 -31
- package/dist/analyzer/signatures.js.map +1 -1
- package/dist/analyzer/source-options.d.ts +17 -0
- package/dist/analyzer/source-options.d.ts.map +1 -0
- package/dist/analyzer/source-options.js +3 -0
- package/dist/analyzer/source-options.js.map +1 -0
- package/dist/api/dto.d.ts +9 -4
- package/dist/api/dto.d.ts.map +1 -1
- package/dist/api/dto.js +14 -3
- package/dist/api/dto.js.map +1 -1
- package/dist/api/strict.d.ts.map +1 -1
- package/dist/api/strict.js +8 -3
- package/dist/api/strict.js.map +1 -1
- package/dist/cli/dto-check.d.ts +9 -0
- package/dist/cli/dto-check.d.ts.map +1 -1
- package/dist/cli/dto-check.js +49 -6
- package/dist/cli/dto-check.js.map +1 -1
- package/dist/cli/fhirpath-check.js +161 -31
- package/dist/cli/fhirpath-check.js.map +1 -1
- package/dist/eslint/index.d.ts.map +1 -1
- package/dist/eslint/index.js +13 -8
- package/dist/eslint/index.js.map +1 -1
- package/dist/r4/generated/resources-data.d.ts +6 -2
- package/dist/r4/generated/resources-data.d.ts.map +1 -1
- package/dist/r4/generated/resources-data.js +626 -28627
- package/dist/r4/generated/resources-data.js.map +1 -1
- package/dist/r4/generated/types-data.d.ts +6 -2
- package/dist/r4/generated/types-data.d.ts.map +1 -1
- package/dist/r4/generated/types-data.js +84 -2204
- package/dist/r4/generated/types-data.js.map +1 -1
- package/dist/r4/index.d.ts.map +1 -1
- package/dist/r4/index.js +10 -4
- package/dist/r4/index.js.map +1 -1
- package/dist/r4/model-data.d.ts +17 -0
- package/dist/r4/model-data.d.ts.map +1 -1
- package/dist/r4/model-data.js +118 -1
- package/dist/r4/model-data.js.map +1 -1
- package/dist/sites/index.d.ts +31 -2
- package/dist/sites/index.d.ts.map +1 -1
- package/dist/sites/index.js +351 -30
- package/dist/sites/index.js.map +1 -1
- package/dist/typed/infer.d.ts +2 -0
- package/dist/typed/infer.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/analyzer/analyze-dto.ts +19 -9
- package/src/analyzer/analyze.ts +186 -54
- package/src/analyzer/declarations.ts +12 -0
- package/src/analyzer/expression-policy.ts +423 -47
- package/src/analyzer/operator-rules.ts +7 -6
- package/src/analyzer/signatures.ts +100 -36
- package/src/analyzer/source-options.ts +19 -0
- package/src/api/dto.ts +35 -9
- package/src/api/strict.ts +8 -3
- package/src/cli/dto-check.ts +56 -6
- package/src/cli/fhirpath-check.ts +180 -34
- package/src/eslint/index.ts +15 -12
- package/src/r4/generated/resources-data.ts +624 -28628
- package/src/r4/generated/types-data.ts +82 -2205
- package/src/r4/index.ts +11 -5
- package/src/r4/model-data.ts +127 -0
- package/src/sites/index.ts +428 -40
- package/src/typed/infer.ts +2 -2
|
@@ -5,32 +5,43 @@
|
|
|
5
5
|
* exit status. Use `--no-import` for source only and `--dtos` to change the DTO
|
|
6
6
|
* module glob.
|
|
7
7
|
*
|
|
8
|
-
* Usage: fhirpath-check [--dtos <glob>]... [--no-import] <file...>
|
|
8
|
+
* Usage: fhirpath-check [--dtos <glob>]... [--no-import] [--local-imports] [--strict] <file...>
|
|
9
9
|
*/
|
|
10
10
|
/* v8 ignore file -- covered end-to-end as a subprocess in fhirpath-check.test.ts */
|
|
11
11
|
import { readFileSync } from 'node:fs'
|
|
12
|
+
import { dirname, resolve } from 'node:path'
|
|
12
13
|
|
|
13
14
|
import ts from 'typescript'
|
|
14
15
|
|
|
15
16
|
import { analyzeSite } from '../analyzer/analyze.ts'
|
|
16
17
|
import { r4Model } from '../r4/index.ts'
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
import { createSiteScanner, type ExpressionSite, type SiteScanner, type SiteScanResult } from '../sites/index.ts'
|
|
19
|
+
import {
|
|
20
|
+
checkDtoModules,
|
|
21
|
+
DEFAULT_DTO_GLOB,
|
|
22
|
+
type DtoCheckResult,
|
|
23
|
+
type DtoFinding,
|
|
24
|
+
EngineMergeError,
|
|
25
|
+
} from './dto-check.ts'
|
|
21
26
|
|
|
22
27
|
interface Args {
|
|
23
28
|
files: string[]
|
|
24
29
|
dtoGlobs: string[]
|
|
25
30
|
imports: boolean
|
|
31
|
+
localImports: boolean
|
|
32
|
+
strict: boolean
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
function parseArgs(argv: readonly string[]): Args {
|
|
29
|
-
const args: Args = { files: [], dtoGlobs: [], imports: true }
|
|
36
|
+
const args: Args = { files: [], dtoGlobs: [], imports: true, localImports: false, strict: false }
|
|
30
37
|
for (let index = 0; index < argv.length; index++) {
|
|
31
38
|
const arg = argv[index]!
|
|
32
39
|
if (arg === '--no-import') {
|
|
33
40
|
args.imports = false
|
|
41
|
+
} else if (arg === '--local-imports') {
|
|
42
|
+
args.localImports = true
|
|
43
|
+
} else if (arg === '--strict') {
|
|
44
|
+
args.strict = true
|
|
34
45
|
} else if (arg === '--dtos') {
|
|
35
46
|
const glob = argv[++index]
|
|
36
47
|
if (glob === undefined) {
|
|
@@ -47,17 +58,89 @@ function parseArgs(argv: readonly string[]): Args {
|
|
|
47
58
|
|
|
48
59
|
const args = parseArgs(process.argv.slice(2))
|
|
49
60
|
if (args.files.length === 0 && !args.imports) {
|
|
50
|
-
console.error('usage: fhirpath-check [--dtos <glob>]... [--no-import] <file...>')
|
|
61
|
+
console.error('usage: fhirpath-check [--dtos <glob>]... [--no-import] [--local-imports] [--strict] <file...>')
|
|
51
62
|
process.exit(2)
|
|
52
63
|
}
|
|
64
|
+
const dtoGlobs = args.dtoGlobs.length > 0 ? args.dtoGlobs : [DEFAULT_DTO_GLOB]
|
|
65
|
+
|
|
66
|
+
/** A type-aware program lets the site finder follow imported and aliased engine receivers. */
|
|
67
|
+
function sourceProgram(files: readonly string[], configPath: string | undefined): ts.Program {
|
|
68
|
+
const defaults: ts.CompilerOptions = {
|
|
69
|
+
allowJs: true,
|
|
70
|
+
skipLibCheck: true,
|
|
71
|
+
target: ts.ScriptTarget.Latest,
|
|
72
|
+
}
|
|
73
|
+
const nodeNext: ts.CompilerOptions = {
|
|
74
|
+
module: ts.ModuleKind.NodeNext,
|
|
75
|
+
moduleResolution: ts.ModuleResolutionKind.NodeNext,
|
|
76
|
+
}
|
|
77
|
+
let options: ts.CompilerOptions = { ...defaults, ...nodeNext }
|
|
78
|
+
if (configPath !== undefined) {
|
|
79
|
+
const config = ts.readConfigFile(configPath, ts.sys.readFile)
|
|
80
|
+
if (config.error === undefined) {
|
|
81
|
+
// Resolve `extends`/`paths` from the config's own directory, and layer
|
|
82
|
+
// the declared options over the defaults instead of replacing them —
|
|
83
|
+
// a `strict`-only tsconfig must not lose the module resolution the
|
|
84
|
+
// checker needs to follow package exports. The NodeNext pair applies
|
|
85
|
+
// only when the config declares neither half, so an explicit
|
|
86
|
+
// module/moduleResolution keeps TypeScript's own pairing rules.
|
|
87
|
+
const declared = ts.parseJsonConfigFileContent(
|
|
88
|
+
config.config,
|
|
89
|
+
ts.sys,
|
|
90
|
+
dirname(configPath),
|
|
91
|
+
undefined,
|
|
92
|
+
configPath
|
|
93
|
+
).options
|
|
94
|
+
const resolution = declared.module === undefined && declared.moduleResolution === undefined ? nodeNext : {}
|
|
95
|
+
options = { ...defaults, ...resolution, ...declared }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return ts.createProgram({ rootNames: files.map(file => resolve(file)), options })
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* One semantic scanner per tsconfig project. A monorepo command may select
|
|
103
|
+
* files from packages whose aliases and module settings differ, so each file
|
|
104
|
+
* starts its config search in its own directory. Files sharing a config share
|
|
105
|
+
* one Program and TypeChecker.
|
|
106
|
+
*/
|
|
107
|
+
function sourceScanners(files: readonly string[]): ReadonlyMap<string, SiteScanner> {
|
|
108
|
+
const filesByConfig = new Map<string | undefined, string[]>()
|
|
109
|
+
for (const file of files) {
|
|
110
|
+
const absolute = resolve(file)
|
|
111
|
+
const configPath = ts.findConfigFile(dirname(absolute), ts.sys.fileExists)
|
|
112
|
+
const grouped = filesByConfig.get(configPath)
|
|
113
|
+
if (grouped === undefined) {
|
|
114
|
+
filesByConfig.set(configPath, [absolute])
|
|
115
|
+
} else {
|
|
116
|
+
grouped.push(absolute)
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const scannersByFile = new Map<string, SiteScanner>()
|
|
121
|
+
for (const [configPath, groupedFiles] of filesByConfig) {
|
|
122
|
+
const scanner = createSiteScanner(ts, sourceProgram(groupedFiles, configPath))
|
|
123
|
+
for (const file of groupedFiles) {
|
|
124
|
+
scannersByFile.set(file, scanner)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return scannersByFile
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const syntaxScanner = createSiteScanner(ts)
|
|
131
|
+
let semanticScannersByFile: ReadonlyMap<string, SiteScanner> = new Map()
|
|
53
132
|
|
|
54
133
|
let failures = 0
|
|
134
|
+
let warnings = 0
|
|
55
135
|
|
|
56
|
-
function report(location: string, diagnostic: { severity:
|
|
57
|
-
|
|
136
|
+
function report(location: string, diagnostic: { severity: 'error' | 'warning'; code: string; message: string }): void {
|
|
137
|
+
const severity = args.strict && diagnostic.severity === 'warning' ? 'error' : diagnostic.severity
|
|
138
|
+
if (severity === 'error') {
|
|
58
139
|
failures += 1
|
|
140
|
+
} else {
|
|
141
|
+
warnings += 1
|
|
59
142
|
}
|
|
60
|
-
const prefix =
|
|
143
|
+
const prefix = severity === 'warning' ? 'warning:' : ''
|
|
61
144
|
console.error(`${location} [${prefix}${diagnostic.code}] ${diagnostic.message}`)
|
|
62
145
|
}
|
|
63
146
|
|
|
@@ -73,33 +156,85 @@ function positionIn(site: { line: number; column: number }, span: { line: number
|
|
|
73
156
|
return `${line}:${column}`
|
|
74
157
|
}
|
|
75
158
|
|
|
76
|
-
/**
|
|
77
|
-
const
|
|
159
|
+
/** Source scans cached so each file is read once. */
|
|
160
|
+
const scansByFile = new Map<string, SiteScanResult>()
|
|
78
161
|
|
|
79
|
-
function
|
|
80
|
-
const cached =
|
|
162
|
+
function scanOf(file: string): SiteScanResult {
|
|
163
|
+
const cached = scansByFile.get(file)
|
|
81
164
|
if (cached !== undefined) {
|
|
82
165
|
return cached
|
|
83
166
|
}
|
|
84
|
-
const
|
|
85
|
-
|
|
86
|
-
|
|
167
|
+
const absolute = resolve(file)
|
|
168
|
+
const scanner = semanticScannersByFile.get(absolute) ?? syntaxScanner
|
|
169
|
+
const scan = scanner(readFileSync(file, 'utf8'), absolute, {
|
|
170
|
+
...(args.localImports && { localImports: true }),
|
|
171
|
+
})
|
|
172
|
+
scansByFile.set(file, scan)
|
|
173
|
+
return scan
|
|
87
174
|
}
|
|
88
175
|
|
|
89
|
-
|
|
176
|
+
function sitesOf(file: string): ExpressionSite[] {
|
|
177
|
+
return scanOf(file).sites
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Extract source sites before executing project modules. Analysis waits until
|
|
181
|
+
// after the import pass so those sites can use the engines' real environment.
|
|
90
182
|
for (const file of args.files) {
|
|
91
|
-
let sites: ExpressionSite[]
|
|
92
183
|
try {
|
|
93
|
-
|
|
184
|
+
scanOf(file)
|
|
94
185
|
} catch (error) {
|
|
95
186
|
console.error(`fhirpath-check: cannot read ${file}: ${error instanceof Error ? error.message : String(error)}`)
|
|
96
187
|
process.exit(2)
|
|
97
188
|
}
|
|
189
|
+
}
|
|
190
|
+
const unresolvedFiles = args.localImports
|
|
191
|
+
? []
|
|
192
|
+
: args.files.filter(file => scanOf(file).skipped.some(skipped => skipped.reason === 'unrecognized-receiver'))
|
|
193
|
+
if (unresolvedFiles.length > 0) {
|
|
194
|
+
// Pay the Program/TypeChecker cost only when the syntax-only pass found a
|
|
195
|
+
// receiver it could not prove. Most source files need no compiler graph.
|
|
196
|
+
semanticScannersByFile = sourceScanners(unresolvedFiles)
|
|
197
|
+
scansByFile.clear()
|
|
198
|
+
for (const file of args.files) {
|
|
199
|
+
scanOf(file)
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
let dtoResult: DtoCheckResult | undefined
|
|
204
|
+
if (args.imports) {
|
|
205
|
+
try {
|
|
206
|
+
dtoResult = await checkDtoModules(dtoGlobs, process.cwd())
|
|
207
|
+
} catch (error) {
|
|
208
|
+
if (error instanceof EngineMergeError) {
|
|
209
|
+
console.error(`fhirpath-check: ${error.message}`)
|
|
210
|
+
} else {
|
|
211
|
+
console.error(
|
|
212
|
+
`fhirpath-check: cannot import DTO modules (${dtoGlobs.join(', ')}): ${error instanceof Error ? error.message : String(error)}`
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
process.exit(2)
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Check source literals first.
|
|
220
|
+
for (const file of args.files) {
|
|
221
|
+
const sites = sitesOf(file)
|
|
98
222
|
for (const site of sites) {
|
|
99
|
-
for (const diagnostic of analyzeSite(site, {
|
|
223
|
+
for (const diagnostic of analyzeSite(site, {
|
|
224
|
+
model: r4Model,
|
|
225
|
+
...dtoResult?.sourceOptions,
|
|
226
|
+
reportUnchecked: true,
|
|
227
|
+
})) {
|
|
100
228
|
report(`${file}:${positionIn(site, diagnostic.span)}`, diagnostic)
|
|
101
229
|
}
|
|
102
230
|
}
|
|
231
|
+
for (const skipped of scanOf(file).skipped) {
|
|
232
|
+
report(`${file}:${skipped.line}:${skipped.column}`, {
|
|
233
|
+
severity: 'warning',
|
|
234
|
+
code: 'skipped',
|
|
235
|
+
message: skipped.message,
|
|
236
|
+
})
|
|
237
|
+
}
|
|
103
238
|
}
|
|
104
239
|
|
|
105
240
|
/**
|
|
@@ -124,26 +259,35 @@ function locate(finding: DtoFinding): string {
|
|
|
124
259
|
}
|
|
125
260
|
|
|
126
261
|
// --- 2. the project's DTO modules, imported ---
|
|
127
|
-
if (
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
262
|
+
if (dtoResult !== undefined) {
|
|
263
|
+
const result = dtoResult
|
|
264
|
+
for (const file of result.files) {
|
|
265
|
+
let scan: SiteScanResult
|
|
266
|
+
try {
|
|
267
|
+
scan = scanOf(file)
|
|
268
|
+
} catch {
|
|
269
|
+
continue
|
|
270
|
+
}
|
|
271
|
+
for (const dto of scan.dtoDeclarations) {
|
|
272
|
+
if (!dto.loadable) {
|
|
273
|
+
report(`${file}:${dto.line}:${dto.column}`, {
|
|
274
|
+
severity: 'warning',
|
|
275
|
+
code: 'unloaded-dto',
|
|
276
|
+
message: `DTO ${dto.name} is module-local and was not loaded for full analysis; export it or an extending class`,
|
|
277
|
+
})
|
|
278
|
+
}
|
|
279
|
+
}
|
|
137
280
|
}
|
|
138
281
|
for (const finding of result.findings) {
|
|
139
282
|
// With no engine in reach, an unresolved column call is unresolvable rather
|
|
140
283
|
// than wrong, so it is reported without failing the run.
|
|
141
284
|
const unresolvable = result.engines === 0 && finding.code === 'unknown-function'
|
|
142
|
-
|
|
285
|
+
const reported = unresolvable ? { ...finding, severity: 'warning' as const } : finding
|
|
286
|
+
report(locate(finding), reported)
|
|
143
287
|
}
|
|
144
288
|
if (result.files.length === 0) {
|
|
145
289
|
console.error(
|
|
146
|
-
`fhirpath-check: no DTO modules matched ${
|
|
290
|
+
`fhirpath-check: no DTO modules matched ${dtoGlobs.join(', ')} — DTOs live in *.dto.ts, or pass --dtos <glob>`
|
|
147
291
|
)
|
|
148
292
|
} else if (result.dtos.length === 0) {
|
|
149
293
|
console.error(`fhirpath-check: ${result.files.length} DTO module(s) matched but export no DTO class`)
|
|
@@ -165,4 +309,6 @@ if (failures > 0) {
|
|
|
165
309
|
console.error(`fhirpath-check: ${failures} problem(s) found`)
|
|
166
310
|
process.exit(1)
|
|
167
311
|
}
|
|
168
|
-
console.log(
|
|
312
|
+
console.log(
|
|
313
|
+
warnings > 0 ? `fhirpath-check: ${warnings} warning(s), no errors found` : 'fhirpath-check: no problems found'
|
|
314
|
+
)
|
package/src/eslint/index.ts
CHANGED
|
@@ -6,6 +6,7 @@ import type * as ESTree from 'estree'
|
|
|
6
6
|
import { analyzeSite, type DeclaredFunction, type DeclaredVariable } from '../analyzer/analyze.ts'
|
|
7
7
|
import {
|
|
8
8
|
CALL_SITES,
|
|
9
|
+
callExpressionCandidates,
|
|
9
10
|
type CallSitePolicy,
|
|
10
11
|
type ClassHeritage,
|
|
11
12
|
columnFunctionDeclaration,
|
|
@@ -14,7 +15,6 @@ import {
|
|
|
14
15
|
DTO_BASE_NAME,
|
|
15
16
|
dtoRootsOf,
|
|
16
17
|
type ExpressionAst,
|
|
17
|
-
expressionEntries,
|
|
18
18
|
type FileColumnFunction,
|
|
19
19
|
isCheckedCall,
|
|
20
20
|
isCheckedTag,
|
|
@@ -22,7 +22,6 @@ import {
|
|
|
22
22
|
type LocalModuleOptions,
|
|
23
23
|
rootOf,
|
|
24
24
|
type SiteContext,
|
|
25
|
-
siteContext,
|
|
26
25
|
type SourceBindings,
|
|
27
26
|
TAG_NAME,
|
|
28
27
|
} from '../analyzer/expression-policy.ts'
|
|
@@ -32,10 +31,13 @@ function isStringLiteral(node: ESTree.Node): node is ESTree.SimpleLiteral & { va
|
|
|
32
31
|
return node.type === 'Literal' && typeof node.value === 'string'
|
|
33
32
|
}
|
|
34
33
|
|
|
35
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Statically-known property key: `path` in `{ path: ... }`, `{ 'path': ... }`,
|
|
36
|
+
* or `{ ['path']: ... }`; undefined for other computed keys.
|
|
37
|
+
*/
|
|
36
38
|
function propertyKeyName(property: ESTree.Property): string | undefined {
|
|
37
39
|
if (property.computed) {
|
|
38
|
-
return undefined
|
|
40
|
+
return isStringLiteral(property.key) ? property.key.value : undefined
|
|
39
41
|
}
|
|
40
42
|
if (property.key.type === 'Identifier') {
|
|
41
43
|
return property.key.name
|
|
@@ -49,8 +51,10 @@ const estreeAst: ExpressionAst<ESTree.Node> = {
|
|
|
49
51
|
boolean: node => (node.type === 'Literal' && typeof node.value === 'boolean' ? node.value : undefined),
|
|
50
52
|
properties: node =>
|
|
51
53
|
node.type === 'ObjectExpression'
|
|
52
|
-
? node.properties.
|
|
53
|
-
property.type === 'Property'
|
|
54
|
+
? node.properties.map(property =>
|
|
55
|
+
property.type === 'Property'
|
|
56
|
+
? { name: propertyKeyName(property), value: property.value }
|
|
57
|
+
: { name: undefined, value: property.argument, spread: true as const }
|
|
54
58
|
)
|
|
55
59
|
: undefined,
|
|
56
60
|
elements: node => (node.type === 'ArrayExpression' ? node.elements.filter(element => element !== null) : undefined),
|
|
@@ -198,7 +202,6 @@ const noInvalidExpressions: Rule.RuleModule = {
|
|
|
198
202
|
policy: CallSitePolicy
|
|
199
203
|
name: string
|
|
200
204
|
receiverRoot: string | undefined
|
|
201
|
-
argument: ESTree.Node
|
|
202
205
|
/** The call itself, for the argument that may name the type it runs against. */
|
|
203
206
|
node: ESTree.CallExpression
|
|
204
207
|
/** The class the call sits in, resolved to a root once the whole file is known. */
|
|
@@ -325,7 +328,6 @@ const noInvalidExpressions: Rule.RuleModule = {
|
|
|
325
328
|
policy,
|
|
326
329
|
name,
|
|
327
330
|
receiverRoot: receiverRoot(callee),
|
|
328
|
-
argument,
|
|
329
331
|
node,
|
|
330
332
|
enclosing: policy.rootFromClass === true ? enclosingClass(ancestors) : undefined,
|
|
331
333
|
})
|
|
@@ -359,14 +361,15 @@ const noInvalidExpressions: Rule.RuleModule = {
|
|
|
359
361
|
if (!isCheckedCall(call.policy, call.name, call.receiverRoot, bindings)) {
|
|
360
362
|
continue
|
|
361
363
|
}
|
|
362
|
-
const
|
|
364
|
+
for (const candidate of callExpressionCandidates<ESTree.Node>(
|
|
363
365
|
call.policy,
|
|
364
366
|
index => call.node.arguments[index],
|
|
365
367
|
rootOf(call.enclosing, dtoRoots),
|
|
366
368
|
estreeAst
|
|
367
|
-
)
|
|
368
|
-
|
|
369
|
-
|
|
369
|
+
)) {
|
|
370
|
+
if (candidate.expression !== undefined) {
|
|
371
|
+
checkAt(candidate.node, candidate.expression, candidate.context)
|
|
372
|
+
}
|
|
370
373
|
}
|
|
371
374
|
}
|
|
372
375
|
},
|