fix: harden typed metrics review findings
This commit is contained in:
@@ -10,15 +10,18 @@ from pathlib import Path
|
||||
|
||||
MAX_FILES = 20_000
|
||||
MAX_FILE_BYTES = 4 * 1024 * 1024
|
||||
ALLOWED_ADAPTERS = {
|
||||
"crates/crank-observability/src/instrumentation.rs",
|
||||
"crates/crank-observability/src/prometheus.rs",
|
||||
}
|
||||
PATTERNS = (
|
||||
re.compile(r"(?<![A-Za-z0-9_])(?:::)?metrics\s*::\s*(?:counter|gauge|histogram|describe_counter|describe_gauge|describe_histogram)\s*!"),
|
||||
re.compile(r"\b(?:pub\s+)?use\s+(?:::)?metrics\s*(?:::\s*(?:\{|counter|gauge|histogram)|\s+as\s+)"),
|
||||
re.compile(
|
||||
r"(?<![A-Za-z0-9_])(?:::)?metrics\s*::\s*(?:counter|gauge|histogram|describe_counter|describe_gauge|describe_histogram|recorder|with_local_recorder|Key|Recorder)\b"
|
||||
),
|
||||
re.compile(
|
||||
r"\b(?:pub\s+)?use\s+(?:::)?metrics\s*(?:\s+as\s+\w+|::\s*(?:Unit|counter|gauge|histogram|Recorder|Key|\{\s*(?:counter|gauge|histogram|Recorder|Key)))"
|
||||
),
|
||||
re.compile(r"\bextern\s+crate\s+metrics(?:\s+as\s+\w+)?"),
|
||||
re.compile(r"\b(?:set_global_recorder|PrometheusRecorder|DebuggingRecorder)\b"),
|
||||
re.compile(r"\b(?:set_global_recorder|set_boxed_recorder|PrometheusRecorder|DebuggingRecorder|Recorder\s*::|register_counter|register_gauge|register_histogram)\b"),
|
||||
)
|
||||
DESCRIBE_ONLY = re.compile(
|
||||
r"(?<![A-Za-z0-9_])metrics\s*::\s*describe_(?:counter|gauge|histogram)\s*!"
|
||||
)
|
||||
|
||||
|
||||
@@ -58,25 +61,108 @@ def resolve(root: Path, supplied: str) -> tuple[str, Path] | None:
|
||||
return relative.as_posix(), canonical
|
||||
|
||||
|
||||
def strip_non_code(text: str) -> str:
|
||||
"""Remove comments and literals while preserving newlines and token separation."""
|
||||
output = list(text)
|
||||
index = 0
|
||||
block_depth = 0
|
||||
while index < len(text):
|
||||
if block_depth:
|
||||
if text.startswith("/*", index):
|
||||
block_depth += 1
|
||||
output[index:index + 2] = " "
|
||||
index += 2
|
||||
elif text.startswith("*/", index):
|
||||
block_depth -= 1
|
||||
output[index:index + 2] = " "
|
||||
index += 2
|
||||
else:
|
||||
if text[index] != "\n":
|
||||
output[index] = " "
|
||||
index += 1
|
||||
continue
|
||||
if text.startswith("//", index):
|
||||
end = text.find("\n", index)
|
||||
end = len(text) if end < 0 else end
|
||||
for cursor in range(index, end):
|
||||
output[cursor] = " "
|
||||
index = end
|
||||
continue
|
||||
if text.startswith("/*", index):
|
||||
block_depth = 1
|
||||
output[index:index + 2] = " "
|
||||
index += 2
|
||||
continue
|
||||
raw = re.match(r"(?:br|r)(?P<hashes>#{0,16})\"", text[index:])
|
||||
if raw:
|
||||
terminator = '"' + raw.group("hashes")
|
||||
end = text.find(terminator, index + raw.end())
|
||||
end = len(text) if end < 0 else end + len(terminator)
|
||||
for cursor in range(index, end):
|
||||
if text[cursor] != "\n":
|
||||
output[cursor] = " "
|
||||
index = end
|
||||
continue
|
||||
prefix = 1 if text.startswith("b\"", index) or text.startswith("b'", index) else 0
|
||||
quote_index = index + prefix
|
||||
is_string = quote_index < len(text) and text[quote_index] == '"'
|
||||
is_char = quote_index < len(text) and text[quote_index] == "'" and re.match(
|
||||
r"'(?:\\.|[^\\'\n])'", text[quote_index:]
|
||||
)
|
||||
if is_string or is_char:
|
||||
quote = text[quote_index]
|
||||
end = quote_index + 1
|
||||
escaped = False
|
||||
while end < len(text):
|
||||
char = text[end]
|
||||
end += 1
|
||||
if escaped:
|
||||
escaped = False
|
||||
elif char == "\\":
|
||||
escaped = True
|
||||
elif char == quote:
|
||||
break
|
||||
for cursor in range(index, end):
|
||||
if text[cursor] != "\n":
|
||||
output[cursor] = " "
|
||||
index = end
|
||||
continue
|
||||
index += 1
|
||||
return "".join(output)
|
||||
|
||||
|
||||
def is_test_path(logical: str) -> bool:
|
||||
parts = Path(logical).parts
|
||||
return bool(parts and parts[0] == "tests") or (
|
||||
len(parts) >= 4 and parts[0] in {"apps", "crates"} and parts[2] == "tests"
|
||||
)
|
||||
|
||||
|
||||
def scan(logical: str, path: Path) -> bool:
|
||||
if logical.startswith("crates/crank-metrics/"):
|
||||
return False
|
||||
if "/tests/" in logical or logical.startswith("tests/"):
|
||||
if is_test_path(logical):
|
||||
return False
|
||||
try:
|
||||
if path.stat().st_size > MAX_FILE_BYTES:
|
||||
return True
|
||||
text = path.read_text(encoding="utf-8")
|
||||
text = strip_non_code(path.read_text(encoding="utf-8"))
|
||||
except (OSError, UnicodeError):
|
||||
return True
|
||||
for pattern in PATTERNS:
|
||||
if not pattern.search(text):
|
||||
continue
|
||||
if logical in ALLOWED_ADAPTERS and pattern is PATTERNS[0] and "describe_" in pattern.search(text).group(0):
|
||||
continue
|
||||
if logical == "crates/crank-observability/src/prometheus.rs" and pattern is PATTERNS[3]:
|
||||
continue
|
||||
return True
|
||||
matches = [match for pattern in PATTERNS for match in pattern.finditer(text)]
|
||||
if not matches:
|
||||
return False
|
||||
if logical == "crates/crank-observability/src/instrumentation.rs":
|
||||
residual = DESCRIBE_ONLY.sub("", text)
|
||||
residual = re.sub(r"\buse\s+metrics\s*::\s*Unit\s*;", "", residual)
|
||||
return any(pattern.search(residual) for pattern in PATTERNS)
|
||||
if logical == "crates/crank-observability/src/prometheus.rs":
|
||||
allowed = ("PrometheusRecorder",)
|
||||
residual = text
|
||||
for token in allowed:
|
||||
residual = residual.replace(token, "")
|
||||
return any(pattern.search(residual) for pattern in PATTERNS)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -91,6 +177,9 @@ def main(argv: list[str]) -> int:
|
||||
if len(supplied) > MAX_FILES:
|
||||
print("error: metrics boundary input limit exceeded", file=sys.stderr)
|
||||
return 1
|
||||
if args.files is not None and not supplied:
|
||||
print("error: metrics boundary explicit file list is empty", file=sys.stderr)
|
||||
return 1
|
||||
violations: list[str] = []
|
||||
for index, item in enumerate(sorted(set(supplied))):
|
||||
resolved = resolve(root, item)
|
||||
|
||||
Reference in New Issue
Block a user