Fix case-sensitive file suffix filtering silently skipping capitalized/mixed-case extensions (#1671)

This commit is contained in:
raman118
2026-07-05 10:42:09 +01:00
committed by safishamsi
parent 94392de640
commit aa1bbdab23
2 changed files with 37 additions and 6 deletions
+10 -5
View File
@@ -15844,7 +15844,7 @@ def _is_cpp_header(path: Path) -> bool:
def _get_extractor(path: Path) -> Any | None:
"""Return the correct extractor function for a file, or None if unsupported."""
if path.name.endswith(".blade.php"):
if path.name.lower().endswith(".blade.php"):
return extract_blade
# MCP config files (.mcp.json, claude_desktop_config.json, ...) are routed
# by filename before generic .json dispatch so they get MCP-aware nodes
@@ -15860,14 +15860,17 @@ def _get_extractor(path: Path) -> Any | None:
# (the suffix map sends `.h` to extract_c, which can't read @interface etc.).
# ObjC sniffing has priority over the C++ sniff: an Objective-C++ header can
# contain both `@interface` and inline C++ (`::`), and it must parse as ObjC.
if path.suffix == ".h":
suffix = path.suffix
if suffix not in _DISPATCH and suffix.lower() in _DISPATCH:
suffix = suffix.lower()
if suffix == ".h":
if _is_objc_header(path):
return extract_objc
# A C++ class header routed to extract_c loses the class entirely (the C
# grammar has no class_specifier). Reroute to extract_cpp (#1547).
if _is_cpp_header(path):
return extract_cpp
return _DISPATCH.get(path.suffix)
return _DISPATCH.get(suffix)
def _safe_extract_with_xaml_root(extractor, path: Path, root: Path) -> dict:
@@ -16646,7 +16649,8 @@ def collect_files(target: Path, *, follow_symlinks: bool = False, root: Path | N
]
for fname in filenames:
p = dp / fname
if p.suffix in _EXTENSIONS and not _ignored(p) and _resolves_under_root(p, containment_root):
suffix = p.suffix
if (suffix in _EXTENSIONS or suffix.lower() in _EXTENSIONS) and not _ignored(p) and _resolves_under_root(p, containment_root):
results.append(p)
return sorted(results)
# Walk with symlink following + cycle detection
@@ -16666,7 +16670,8 @@ def collect_files(target: Path, *, follow_symlinks: bool = False, root: Path | N
]
for fname in filenames:
p = dp / fname
if p.suffix in _EXTENSIONS and not _ignored(p) and _resolves_under_root(p, containment_root):
suffix = p.suffix
if (suffix in _EXTENSIONS or suffix.lower() in _EXTENSIONS) and not _ignored(p) and _resolves_under_root(p, containment_root):
results.append(p)
return sorted(results)
+27 -1
View File
@@ -408,7 +408,8 @@ def _legacy_collect_files(target, *, root=None):
for ext in sorted(extensions):
results.extend(
p for p in target.rglob(f"*{ext}")
if not any(_is_noise_dir(part) for part in p.parts)
if p.suffix == ext
and not any(_is_noise_dir(part) for part in p.parts)
and not (patterns and _is_ignored(p, ignore_root, patterns))
)
return sorted(results)
@@ -1693,3 +1694,28 @@ def test_non_colliding_path_id_is_not_salted(tmp_path):
result = extract([p], cache_root=tmp_path)
file_id = next(n["id"] for n in result["nodes"] if n.get("source_location") == "L1")
assert file_id == make_id(_file_stem(Path("src/auth/session.py"))) == "src_auth_session"
def test_case_insensitive_suffix_filtering(tmp_path):
py_file = tmp_path / "app.PY"
js_file = tmp_path / "script.JS"
ts_file = tmp_path / "lib.Ts"
py_file.write_text("class MyPythonClass:\n pass\n")
js_file.write_text("function myJSFunction() {}\n")
ts_file.write_text("export class MyTSClass {}\n")
collected = collect_files(tmp_path)
collected_names = {f.name for f in collected}
assert "app.PY" in collected_names
assert "script.JS" in collected_names
assert "lib.Ts" in collected_names
result = extract(collected, cache_root=tmp_path)
nodes = result["nodes"]
labels = {n.get("label") for n in nodes if "label" in n}
assert "MyPythonClass" in labels
assert "myJSFunction()" in labels
assert "MyTSClass" in labels