(file: string, inherited: Truth, includeLine?: number)
| 267 | return /^\w+$/.test(text) ? definitions.get(text)?.value : undefined; |
| 268 | }; |
| 269 | const scan = (file: string, inherited: Truth, includeLine?: number): void => { |
| 270 | if (inherited === false || scanning.has(file) || once.get(file) === true) return; |
| 271 | scanning.add(file); |
| 272 | let active: Truth = inherited; |
| 273 | const frames: Array<{ parent: Truth; taken: Truth }> = []; |
| 274 | for (const ev of summarize(file, context, cache)) { |
| 275 | const line = includeLine ?? ev.line; |
| 276 | if (ev.kind === 'branch') { |
| 277 | if (ev.op === 'if' || ev.op === 'ifdef' || ev.op === 'ifndef') { |
| 278 | const known = definitions.get(ev.expression.trim())?.defined; |
| 279 | let selected = ev.op === 'if' ? condition(ev.expression) : ev.op === 'ifndef' ? not(known) : known; |
| 280 | if (selected === undefined && ev.guard) selected = true; |
| 281 | frames.push({ parent: active, taken: selected }); |
| 282 | active = and(active, selected); |
| 283 | } else if (ev.op === 'endif') { |
| 284 | active = frames.pop()?.parent ?? inherited; |
| 285 | } else { |
| 286 | const frame = frames[frames.length - 1]; |
| 287 | if (frame) { |
| 288 | const test = ev.op === 'else' ? true : condition(ev.expression); |
| 289 | active = and(frame.parent, and(not(frame.taken), test)); |
| 290 | frame.taken = or(frame.taken, test); |
| 291 | } |
| 292 | } |
| 293 | continue; |
| 294 | } |
| 295 | if (active === false) continue; |
| 296 | if (ev.kind === 'once') { |
| 297 | once.set(file, or(once.get(file) ?? false, active)); |
| 298 | continue; |
| 299 | } |
| 300 | if (ev.kind === 'include') { |
| 301 | const target = resolveInclude(file, ev.quote, ev.spec, language, context, cache); |
| 302 | if (target) scan(target, active, line); |
| 303 | continue; |
| 304 | } |
| 305 | const prior = definitions.get(ev.name); |
| 306 | const defining = ev.kind === 'define'; |
| 307 | const macro = defining && ev.functionLike && !ev.wrapsItself; |
| 308 | const now = active === true ? macro : prior?.macro === macro ? macro : undefined; |
| 309 | // A name no directive has touched is unknown, not undefined: the build |
| 310 | // can set it on the command line. So an `#undef` under an undecidable |
| 311 | // `#if` leaves it unknown (#2069); only a certain one clears it. |
| 312 | definitions.set(ev.name, { |
| 313 | defined: defining ? or(prior?.defined, active) : and(prior?.defined, not(active)), |
| 314 | value: defining && active === true ? condition(ev.value) : undefined, |
| 315 | macro: now, |
| 316 | }); |
| 317 | if (ev.functionLike) macroNames.add(ev.name); |
| 318 | if (macroNames.has(ev.name)) { |
| 319 | const events = timeline.get(ev.name) ?? []; |
| 320 | events.push({ line, defined: now }); |
| 321 | timeline.set(ev.name, events); |
| 322 | } |
| 323 | } |
| 324 | scanning.delete(file); |
| 325 | }; |
| 326 |
no test coverage detected