@@ -65,6 +65,7 @@ def __init__(self):
6565 self ._dart_directives : dict [str , list ] = {}
6666 self ._dart_visible : dict [str , set [str ]] = {}
6767 self ._dart_library_members : dict [str , set [str ]] = {}
68+ self ._dart_unscoped = False
6869 self .call_relationships : list [CallRelationship ] = []
6970 self ._python_project_modules : set = set ()
7071 self ._python_external_import_roots : set = set ()
@@ -91,6 +92,7 @@ def analyze_code_files(self, code_files: list[dict], base_dir: str) -> dict:
9192 self ._dart_directives = {}
9293 self ._dart_visible = {}
9394 self ._dart_library_members = {}
95+ self ._dart_unscoped = False
9496
9597 files_analyzed = 0
9698 files_failed = 0
@@ -607,14 +609,21 @@ def _build_dart_scopes(self, base_dir: str) -> None:
607609 files = set (self ._dart_directives )
608610 resolver = DartPackageResolver .from_files (base_dir , files )
609611 scopes = build_scopes (self ._dart_directives , resolver , files )
610- self ._dart_visible = scopes .visible
611612 self ._dart_library_members = scopes .library_members
612-
613- def _has_dart_scope (self , caller : Node ) -> bool :
614- return Path (caller .relative_path ).as_posix () in self ._dart_visible
613+ if resolver .packages :
614+ self ._dart_visible = scopes .visible
615+ return
616+ # Without a pubspec.yaml (e.g. only `lib/` was analyzed) `package:`
617+ # imports resolve to nothing, and scoping would drop nearly every
618+ # cross-file edge. Match names across all Dart files instead.
619+ self ._dart_unscoped = True
620+ logger .warning (
621+ "No pubspec.yaml found for the analyzed Dart files; `package:` imports "
622+ "cannot be resolved, so Dart calls are matched by name across Dart files."
623+ )
615624
616625 def _dart_scope_candidates (
617- self , func_ids : list [str ], scope : set [str ], caller_file : str | None = None
626+ self , func_ids : list [str ], scope : set [str ] | None , caller_file : str | None = None
618627 ) -> list [str ]:
619628 from codewiki .src .be .dependency_analyzer .analyzers .dart_imports import (
620629 is_private_dart_name ,
@@ -627,7 +636,7 @@ def _dart_scope_candidates(
627636 if func_id in candidates or func is None :
628637 continue
629638 func_file = Path (func .relative_path ).as_posix ()
630- if func_file not in scope :
639+ if scope is not None and func_file not in scope :
631640 continue
632641 # A `_`-prefixed segment in the target (`_Ticker.tick`) is private
633642 # to its library, whatever the callee's own spelling.
@@ -648,12 +657,15 @@ def _resolve_dart_callee(
648657 if not lang_indexes or "::" in callee :
649658 return None
650659 caller_file = Path (caller .relative_path ).as_posix ()
660+ scope : set [str ] | None
651661 if is_private_dart_name (callee ):
652662 scope = self ._dart_library_members .get (caller_file , {caller_file })
663+ elif self ._dart_unscoped :
664+ scope = None # any Dart file
653665 else :
654666 scope = self ._dart_visible .get (caller_file )
655- if not scope :
656- return None
667+ if not scope :
668+ return None
657669 keys = [callee ]
658670 if "." in callee :
659671 keys .append (callee .split ("." )[0 ])
@@ -730,11 +742,11 @@ def _resolve_call_relationships(self):
730742 ]
731743
732744 def _is_unresolvable_dart (self , relationship : CallRelationship ) -> bool :
733- """A scoped Dart caller's callee that import-aware resolution rejected
734- is external or invisible; drop it so ast_parser's name-only fallback
735- cannot bind it to an unrelated component."""
745+ """A Dart caller's callee that Dart resolution rejected is external or
746+ invisible; drop it so ast_parser's name-only fallback cannot bind it
747+ to an unrelated component."""
736748 caller = self .functions .get (relationship .caller )
737- return caller is not None and caller .language == "dart" and self . _has_dart_scope ( caller )
749+ return caller is not None and caller .language == "dart"
738750
739751 def _dotted_project_packages (self ) -> dict [str , set ]:
740752 """Project packages/namespaces, partitioned by language. Java and C#
@@ -871,17 +883,9 @@ def _resolve_callee(
871883
872884 lang_indexes = indexes ["by_lang" ].get (caller_language ) if caller_language else None
873885 if caller_language == "dart" and caller is not None :
874- from codewiki .src .be .dependency_analyzer .analyzers .dart_imports import (
875- is_private_dart_name ,
876- )
877-
878- match = self ._resolve_dart_callee (relationship .callee , caller , lang_indexes )
879- if match :
880- return match
881- # Dart resolution is import-driven: a caller with a scope never
882- # falls back to name guessing in other languages or libraries.
883- if is_private_dart_name (relationship .callee ) or self ._has_dart_scope (caller ):
884- return None
886+ # Dart resolution is authoritative: it never falls back to name
887+ # guessing in other languages or invisible libraries.
888+ return self ._resolve_dart_callee (relationship .callee , caller , lang_indexes )
885889 if lang_indexes :
886890 match = self ._resolve_callee_in (
887891 relationship , lang_indexes ["exact" ], lang_indexes ["simple" ]
0 commit comments