(source: string)
| 479 | * class. |
| 480 | */ |
| 481 | export function blankCppAnnotationMacroCalls(source: string): string { |
| 482 | if (!/^[ \t]*[A-Z][A-Z0-9_]{2,}\s*\(/m.test(source)) return source; |
| 483 | const chars = source.split(''); |
| 484 | const re = /^([ \t]*)([A-Z][A-Z0-9_]{2,})(\s*)\(/gm; |
| 485 | let m: RegExpExecArray | null; |
| 486 | while ((m = re.exec(source)) !== null) { |
| 487 | const macroStart = m.index + (m[1] ?? '').length; // skip leading indent |
| 488 | let i = m.index + m[0].length - 1; // index of the opening '(' |
| 489 | let depth = 0; |
| 490 | let end = -1; |
| 491 | for (; i < source.length; i++) { |
| 492 | const c = source[i]; |
| 493 | if (c === '"' || c === "'") { |
| 494 | const quote = c; |
| 495 | i++; |
| 496 | while (i < source.length && source[i] !== quote) { |
| 497 | if (source[i] === '\\') i++; |
| 498 | i++; |
| 499 | } |
| 500 | continue; |
| 501 | } |
| 502 | if (c === '(') depth++; |
| 503 | else if (c === ')') { |
| 504 | depth--; |
| 505 | if (depth === 0) { end = i + 1; break; } |
| 506 | } |
| 507 | } |
| 508 | if (end < 0) continue; |
| 509 | let j = end; |
| 510 | while (j < source.length && /\s/.test(source[j] as string)) j++; |
| 511 | const after = source[j]; |
| 512 | // Only markup is followed by the declaration it decorates; a statement call |
| 513 | // (`;`), init-list item (`,`/`{`), or expression fragment (operator) is not. |
| 514 | if (!after || !/[A-Za-z_~#]/.test(after)) continue; |
| 515 | for (let k = macroStart; k < end; k++) { |
| 516 | if (chars[k] !== '\n' && chars[k] !== '\r') chars[k] = ' '; |
| 517 | } |
| 518 | re.lastIndex = end; |
| 519 | } |
| 520 | return chars.join(''); |
| 521 | } |
| 522 | |
| 523 | /** |
| 524 | * Blank a macro that is the ONLY token on its line — no parens, no semicolon: |
no test coverage detected