a06f331eb8
CI / benchmark (push) Has been skipped
install-script / posix-syntax (push) Successful in 6m1s
CI / build-onnx (push) Failing after 6m43s
init-smoke / dry-run (push) Failing after 15m57s
security / govulncheck (push) Has been cancelled
security / trivy-fs (push) Has been cancelled
CI / test (1.26, ubuntu-latest) (push) Has been cancelled
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
CI / test (1.26, macos-latest) (push) Has been cancelled
CI / build-windows (push) Has been cancelled
CI / lint (push) Has been cancelled
install-script / powershell-syntax (push) Has been cancelled
install-script / install (macos-14) (push) Has been cancelled
install-script / install (ubuntu-latest) (push) Has been cancelled
76 lines
2.3 KiB
Go
76 lines
2.3 KiB
Go
package languages
|
|
|
|
// blankConditionalDirectives rewrites C-family conditional-compilation
|
|
// directive lines (#if / #elif / #else / #endif) into runs of spaces while
|
|
// preserving every byte offset and newline. A conditional directive that
|
|
// would otherwise detach an enclosing declaration — the classic case being a
|
|
// #if inside an enum member list that breaks the surrounding class's member
|
|
// block, silently dropping most of its methods — can no longer corrupt the
|
|
// parse, yet the code in every branch survives as plain source and all node
|
|
// ranges stay byte-exact.
|
|
//
|
|
// Only the four conditional directives are blanked. Non-conditional directives
|
|
// (#region / #endregion / #pragma / #nullable / #define / #undef / #line /
|
|
// #warning / #error) are left untouched. The returned slice always has the
|
|
// same length as src.
|
|
func blankConditionalDirectives(src []byte) []byte {
|
|
out := make([]byte, len(src))
|
|
copy(out, src)
|
|
for i, n := 0, len(out); i < n; {
|
|
lineStart := i
|
|
j := i
|
|
for j < n && out[j] != '\n' {
|
|
j++
|
|
}
|
|
if isConditionalDirectiveLine(out[lineStart:j]) {
|
|
for k := lineStart; k < j; k++ {
|
|
out[k] = ' '
|
|
}
|
|
}
|
|
if j < n {
|
|
i = j + 1
|
|
} else {
|
|
i = j
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// isConditionalDirectiveLine reports whether line (a single source line with no
|
|
// trailing newline) is a #if / #elif / #else / #endif preprocessor directive.
|
|
// A directive's '#' must be the first non-whitespace byte on the line — which
|
|
// is exactly why a '#if' appearing inside a string literal never matches, since
|
|
// the first non-whitespace byte there is the opening quote. C# permits
|
|
// whitespace between the '#' and the keyword, so that is tolerated too.
|
|
func isConditionalDirectiveLine(line []byte) bool {
|
|
i := 0
|
|
for i < len(line) && (line[i] == ' ' || line[i] == '\t') {
|
|
i++
|
|
}
|
|
if i >= len(line) || line[i] != '#' {
|
|
return false
|
|
}
|
|
i++
|
|
for i < len(line) && (line[i] == ' ' || line[i] == '\t') {
|
|
i++
|
|
}
|
|
kwStart := i
|
|
for i < len(line) && isPreprocIdentByte(line[i]) {
|
|
i++
|
|
}
|
|
switch string(line[kwStart:i]) {
|
|
case "if", "elif", "else", "endif":
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
|
|
// isPreprocIdentByte reports whether b can appear in a preprocessor directive
|
|
// keyword (ASCII letters, digits, underscore).
|
|
func isPreprocIdentByte(b byte) bool {
|
|
return b == '_' ||
|
|
(b >= 'a' && b <= 'z') ||
|
|
(b >= 'A' && b <= 'Z') ||
|
|
(b >= '0' && b <= '9')
|
|
}
|