MCPcopy Create free account
hub / github.com/PHPantom-dev/phpantom_lsp / merge_branch

Method merge_branch

src/completion/variable/forward_walk.rs:1900–1962  ·  view source on GitHub ↗

Merge another scope into `self`. For each variable: - Present in both: union the type sets (variable was assigned in both branches). - Present in only one: keep it with the existing types (variable was assigned in only one branch — it *might* have those types). After merging, subsumed entries are removed. When one entry's type is a subset of another (e.g. `string|null` ⊆ `int|string|null`, or `

(&mut self, other: &ScopeState)

Source from the content-addressed store, hash-verified

1898 /// narrowed types from non-exiting if-branches leak into the
1899 /// post-merge scope and pollute subsequent narrowing operations.
1900 pub fn merge_branch(&mut self, other: &ScopeState) {
1901 for (name, other_types) in &other.locals {
1902 let entry = self.locals.entry(*name).or_default();
1903
1904 // Merge other_types into entry. When an incoming entry
1905 // shares a class name with an existing entry but has a
1906 // broader type_string (e.g. `?A` vs `A`), widen the
1907 // existing entry's type_string instead of discarding
1908 // the incoming one. This prevents post-loop merges from
1909 // losing nullable information.
1910 for rt in other_types.iter() {
1911 let mut merged_into_existing = false;
1912 if let Some(ref rt_cls) = rt.class_info {
1913 for existing in entry.iter_mut() {
1914 if let Some(ref ex_cls) = existing.class_info
1915 && ex_cls.name == rt_cls.name
1916 {
1917 // Same class. If the incoming type is
1918 // broader, adopt it.
1919 if existing.type_string != rt.type_string
1920 && existing.type_string.is_subset_of(&rt.type_string)
1921 {
1922 existing.type_string = rt.type_string.clone();
1923 }
1924 merged_into_existing = true;
1925 break;
1926 }
1927 }
1928 }
1929 if !merged_into_existing {
1930 ResolvedType::push_unique(entry, rt.clone());
1931 }
1932 }
1933
1934 // Remove entries whose type is subsumed by a broader entry.
1935 // E.g. `string|null` ⊆ `int|string|null` → drop the former.
1936 if entry.len() > 1 {
1937 let types: Vec<crate::php_type::PhpType> =
1938 entry.iter().map(|rt| rt.type_string.clone()).collect();
1939 let mut keep = vec![true; types.len()];
1940 for i in 0..types.len() {
1941 if !keep[i] {
1942 continue;
1943 }
1944 for j in 0..types.len() {
1945 if i == j || !keep[j] {
1946 continue;
1947 }
1948 // If j is a strict subset of i, drop j.
1949 if types[j] != types[i] && types[j].is_subset_of(&types[i]) {
1950 keep[j] = false;
1951 }
1952 }
1953 }
1954 let mut idx = 0;
1955 entry.retain(|_| {
1956 let k = keep[idx];
1957 idx += 1;

Callers 9

process_if_colon_bodyFunction · 0.80
process_foreachFunction · 0.80
process_whileFunction · 0.80
process_forFunction · 0.80
process_do_whileFunction · 0.80
process_tryFunction · 0.80
process_switchFunction · 0.80

Calls 4

iterMethod · 0.80
is_subset_ofMethod · 0.80
cloneMethod · 0.80
mapMethod · 0.45

Tested by

no test coverage detected