@@ -120,42 +120,52 @@ def changed_paths(root: Path, args: argparse.Namespace) -> Tuple[Dict[str, str],
120120 return parse_name_status (output )
121121
122122
123- def parse_added_lines (diff_text : str ) -> List [DiffLine ]:
124- lines : List [DiffLine ] = []
125- path = ""
123+ def parse_changed_lines (diff_text : str ) -> Tuple [List [DiffLine ], List [DiffLine ]]:
124+ added : List [DiffLine ] = []
125+ removed : List [DiffLine ] = []
126+ old_path = ""
127+ new_path = ""
128+ old_line : Optional [int ] = None
126129 new_line : Optional [int ] = None
127- hunk_re = re .compile (r"@@ -\d+(?:,\d+)? \+(\d+)(?:,( \d+) )? @@" )
130+ hunk_re = re .compile (r"@@ -( \d+) (?:,\d+)? \+(\d+)(?:,\d+)? @@" )
128131 for raw in diff_text .splitlines ():
132+ if raw .startswith ("--- a/" ):
133+ old_path = raw [6 :]
134+ continue
129135 if raw .startswith ("+++ b/" ):
130- path = raw [6 :]
136+ new_path = raw [6 :]
131137 continue
132- if raw .startswith ("+++ " ):
133- path = raw [4 :]
138+ if raw .startswith ("--- " ) or raw .startswith ("+++ " ):
134139 continue
135140 match = hunk_re .match (raw )
136141 if match :
137- new_line = int (match .group (1 ))
142+ old_line = int (match .group (1 ))
143+ new_line = int (match .group (2 ))
144+ continue
145+ if old_line is None or new_line is None :
138146 continue
139- if new_line is None :
147+ if raw . startswith ( " \\ " ) :
140148 continue
141149 if raw .startswith ("+" ) and not raw .startswith ("+++" ):
142- lines .append (DiffLine (path , new_line , raw [1 :]))
150+ added .append (DiffLine (new_path , new_line , raw [1 :]))
143151 new_line += 1
144152 elif raw .startswith ("-" ) and not raw .startswith ("---" ):
145- continue
153+ removed .append (DiffLine (old_path , old_line , raw [1 :]))
154+ old_line += 1
146155 else :
156+ old_line += 1
147157 new_line += 1
148- return lines
158+ return added , removed
149159
150160
151- def added_lines (root : Path , args : argparse .Namespace ) -> List [DiffLine ]:
161+ def changed_lines (root : Path , args : argparse .Namespace ) -> Tuple [ List [DiffLine ], List [ DiffLine ] ]:
152162 if args .staged :
153163 output = git (["diff" , "--cached" , "--ignore-cr-at-eol" , "-U0" ], root ).stdout
154164 elif args .base and args .head :
155165 output = git (["diff" , "--ignore-cr-at-eol" , "-U0" , args .base , args .head ], root ).stdout
156166 else :
157167 output = ""
158- return parse_added_lines (output )
168+ return parse_changed_lines (output )
159169
160170
161171def read_changed_file_bytes (root : Path , path : str , args : argparse .Namespace ) -> bytes :
@@ -232,22 +242,57 @@ def check_line_endings(
232242 )
233243
234244
235- def check_global_dependencies (findings : List [Finding ], lines : Iterable [DiffLine ]) -> None :
236- pattern = re .compile (r"\b(GlobalV::|GlobalC::|PARAM(?:\.|->|::|\b))" )
245+ GLOBAL_DEPENDENCY_RE = re .compile (r"\b(GlobalV::|GlobalC::|PARAM(?:\.|->|::|\b))" )
246+
247+
248+ def is_global_dependency_check_path (path : str ) -> bool :
249+ if path .startswith ("tools/03_code_analysis/" ):
250+ return False
251+ return Path (path ).suffix .lower () in CODE_EXTENSIONS
252+
253+
254+ def global_dependency_hits (lines : Iterable [DiffLine ]) -> List [Tuple [DiffLine , int ]]:
255+ hits : List [Tuple [DiffLine , int ]] = []
237256 for line in lines :
238- if line .path . startswith ( "tools/03_code_analysis/" ):
257+ if not is_global_dependency_check_path ( line .path ):
239258 continue
240- if Path (line .path ).suffix .lower () not in CODE_EXTENSIONS :
241- continue
242- if pattern .search (line .content ):
243- add_finding (
259+ count = len (GLOBAL_DEPENDENCY_RE .findall (line .content ))
260+ if count :
261+ hits .append ((line , count ))
262+ return hits
263+
264+
265+ def check_global_dependencies (
266+ findings : List [Finding ],
267+ added_lines : Iterable [DiffLine ],
268+ removed_lines : Iterable [DiffLine ],
269+ ) -> None :
270+ added_hits = global_dependency_hits (added_lines )
271+ removed_hits = global_dependency_hits (removed_lines )
272+ added_count = sum (count for _ , count in added_hits )
273+ removed_count = sum (count for _ , count in removed_hits )
274+ delta = added_count - removed_count
275+ if added_count == 0 :
276+ return
277+
278+ severity = BLOCK if delta > 0 else WARN
279+ action = (
280+ "Reduce or explicitly pass dependencies so this PR does not increase global dependency usage."
281+ if delta > 0
282+ else "Confirm this is a migration-neutral move or partial cleanup, and explain the remaining global dependency rationale."
283+ )
284+ for line , count in added_hits :
285+ add_finding (
244286 findings ,
245- "No new cross-layer globals " ,
246- WARN ,
287+ "Global dependency budget " ,
288+ severity ,
247289 line .path ,
248290 line .line ,
249- "Added line introduces GlobalV, GlobalC, or PARAM as a dependency." ,
250- "Prefer explicit parameters or a narrow local interface. Document any required exception in the PR." ,
291+ (
292+ f"Added line introduces { count } GlobalV/GlobalC/PARAM reference(s); "
293+ f"PR total added={ added_count } , removed={ removed_count } , net_delta={ delta } ."
294+ ),
295+ action ,
251296 )
252297
253298
@@ -518,7 +563,7 @@ def check_pr_metadata(findings: List[Finding], body: Optional[str]) -> None:
518563 add_finding (
519564 findings ,
520565 "PR metadata completeness" ,
521- WARN ,
566+ BLOCK ,
522567 "pull_request.body" ,
523568 None ,
524569 "; " .join (reason_parts ),
@@ -621,12 +666,12 @@ def check_documentation_warning(findings: List[Finding], changed: Sequence[str],
621666def collect_findings (root : Path , args : argparse .Namespace ) -> List [Finding ]:
622667 findings : List [Finding ] = []
623668 statuses , changed = changed_paths (root , args )
624- lines = added_lines (root , args )
669+ lines , removed_lines = changed_lines (root , args )
625670 body = read_pr_body (args .event_path )
626671 body_text = body or ""
627672
628673 check_line_endings (findings , root , changed , statuses , args )
629- check_global_dependencies (findings , lines )
674+ check_global_dependencies (findings , lines , removed_lines )
630675 check_default_parameters (findings , lines )
631676 check_hpp_warnings (findings , statuses , lines )
632677 check_header_include_warnings (findings , lines )
0 commit comments