mirror of
https://github.com/safishamsi/graphify.git
synced 2026-09-23 14:05:43 +00:00
fix(extract): don't force-parse MATLAB .m through the Objective-C grammar (#1702)
`.m` is shared by Objective-C implementation files and MATLAB, but the extractor dispatch routed every `.m` to extract_objc unconditionally. Feeding real MATLAB to the Objective-C tree-sitter grammar yields root.has_error and garbage nodes/edges — worse than skipping, because it pollutes the graph with wrong data. _get_extractor now content-sniffs `.m` the same way `.h` already is: a genuine Objective-C `.m` carries an ObjC directive (@implementation/@interface/@import/ #import) and still routes to extract_objc; a `.m` without one (MATLAB, Octave) gets no extractor, so it is surfaced by the no-AST-extractor warning (#1689) instead of mis-parsed. `.mm` is unambiguously Objective-C++ and is left untouched. This stops the wrong-by-omission garbage; wiring a real tree-sitter-matlab extractor (the issue's primary ask) remains a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
ac6bb274a1
commit
733ad08852
@@ -16063,6 +16063,20 @@ _CPP_HEADER_MARKERS = (
|
||||
)
|
||||
|
||||
|
||||
def _is_objc_source(path: Path) -> bool:
|
||||
"""Whether a `.m` file is Objective-C rather than MATLAB/Octave (#1702).
|
||||
|
||||
`.m` is shared by Objective-C implementation files and MATLAB (also Octave).
|
||||
The suffix map routes `.m` to extract_objc unconditionally, which force-parses
|
||||
MATLAB through the Objective-C tree-sitter grammar and emits garbage nodes/edges
|
||||
(worse than skipping). A genuine ObjC `.m` always carries an ObjC directive
|
||||
(@implementation/@interface/@import/#import); MATLAB has none of them. Reuses
|
||||
the same marker set as the `.h` sniff. `.mm` is unambiguously Objective-C++ and
|
||||
is not sniffed.
|
||||
"""
|
||||
return _is_objc_header(path)
|
||||
|
||||
|
||||
def _is_cpp_header(path: Path) -> bool:
|
||||
"""Whether a `.h` file is C++ rather than plain C (#1547).
|
||||
|
||||
@@ -16106,6 +16120,13 @@ def _get_extractor(path: Path) -> Any | None:
|
||||
# grammar has no class_specifier). Reroute to extract_cpp (#1547).
|
||||
if _is_cpp_header(path):
|
||||
return extract_cpp
|
||||
# `.m` is Objective-C OR MATLAB. extract_objc unconditionally would force-parse
|
||||
# MATLAB through the ObjC grammar into garbage (#1702). Route to extract_objc
|
||||
# only when the file actually looks like Objective-C; otherwise leave it without
|
||||
# an extractor (surfaced by the no-AST-extractor warning, #1689) rather than
|
||||
# mis-parsed. `.mm` is unambiguously Objective-C++ and stays on extract_objc.
|
||||
if suffix == ".m" and not _is_objc_source(path):
|
||||
return None
|
||||
# Extensionless files: resolve by shebang, mirroring detect.classify_file.
|
||||
# Without this, detect labels e.g. `#!/usr/bin/env bash` CLIs as code but
|
||||
# extraction returns no extractor and the file silently contributes nothing.
|
||||
|
||||
@@ -1813,3 +1813,34 @@ def test_extract_progress_final_line_uses_consistent_denominator(tmp_path, capsy
|
||||
# final progress line reports the uncached count (100), not the total (105)
|
||||
assert "100/100 uncached files (100%)" in out
|
||||
assert "105/105 files" not in out, "final line must not switch to total_files (#1693)"
|
||||
|
||||
|
||||
def test_get_extractor_routes_matlab_m_away_from_objc(tmp_path):
|
||||
# #1702: .m is shared by Objective-C and MATLAB. A real ObjC .m still routes to
|
||||
# extract_objc, but a MATLAB .m must NOT be force-parsed by the ObjC grammar
|
||||
# (which produces garbage) — it gets no extractor instead.
|
||||
from graphify.extract import _get_extractor, extract_objc
|
||||
|
||||
objc = tmp_path / "Foo.m"
|
||||
objc.write_text('#import "Foo.h"\n@implementation Foo\n- (void)bar {}\n@end\n')
|
||||
matlab_fn = tmp_path / "solver.m"
|
||||
matlab_fn.write_text("function y = solver(x)\n y = x + 1;\nend\n")
|
||||
matlab_cls = tmp_path / "Model.m"
|
||||
matlab_cls.write_text("classdef Model\n methods\n function run(obj); end\n end\nend\n")
|
||||
mm = tmp_path / "x.mm"
|
||||
mm.write_text("#import <F/F.h>\n@implementation X\n@end\n")
|
||||
|
||||
assert _get_extractor(objc) is extract_objc # real ObjC .m -> objc
|
||||
assert _get_extractor(matlab_fn) is None # MATLAB function -> no garbage
|
||||
assert _get_extractor(matlab_cls) is None # MATLAB classdef -> no garbage
|
||||
assert _get_extractor(mm) is extract_objc # .mm is unambiguously ObjC++
|
||||
|
||||
|
||||
def test_matlab_m_not_extracted_as_garbage(tmp_path, capsys):
|
||||
# End to end: a MATLAB .m produces no (garbage) nodes and is surfaced by the
|
||||
# no-AST-extractor warning (#1702 + #1689), rather than mis-parsed as ObjC.
|
||||
m = tmp_path / "controller.m"
|
||||
m.write_text("function u = controller(x)\n u = -x;\nend\n")
|
||||
result = extract([m], cache_root=tmp_path)
|
||||
assert result["nodes"] == [] # no garbage ObjC nodes
|
||||
assert "no AST extractor" in capsys.readouterr().err # surfaced, not silent
|
||||
|
||||
Reference in New Issue
Block a user