66use Psalm \Internal \Provider \FakeFileProvider ;
77use Psalm \Internal \Provider \Providers ;
88use Psalm \Internal \RuntimeCaches ;
9+ use Psalm \Internal \Type \Comparator \TypeComparisonResult ;
910use Psalm \Internal \Type \Comparator \UnionTypeComparator ;
1011use Psalm \Internal \Type \TypeTokenizer ;
1112use Psalm \Tests \Internal \Provider \FakeParserCacheProvider ;
@@ -129,6 +130,43 @@ public function testTypeDoesNotAcceptType(string $parent_type_string, string $ch
129130 );
130131 }
131132
133+ /** @dataProvider getCoercibleComparisons */
134+ public function testTypeIsCoercible (string $ parent_type_string , string $ child_type_string ): void
135+ {
136+ $ parent_type = Type::parseString ($ parent_type_string );
137+ $ child_type = Type::parseString ($ child_type_string );
138+
139+ $ result = new TypeComparisonResult ();
140+
141+ $ contained = UnionTypeComparator::isContainedBy (
142+ $ this ->project_analyzer ->getCodebase (),
143+ $ child_type ,
144+ $ parent_type ,
145+ false ,
146+ false ,
147+ $ result ,
148+ );
149+
150+ $ this ->assertFalse ($ contained , 'Type ' . $ parent_type_string . ' should not contain ' . $ child_type_string );
151+ $ this ->assertTrue (
152+ $ result ->type_coerced ,
153+ 'Type ' . $ parent_type_string . ' should be coercible into ' . $ child_type_string ,
154+ );
155+ }
156+
157+ /** @return iterable<string, list{string, string}> */
158+ public function getCoercibleComparisons (): iterable
159+ {
160+ yield 'callableStringIntoLowercaseString ' => [
161+ 'lowercase-string ' ,
162+ 'callable-string ' ,
163+ ];
164+ yield 'lowercaseStringIntoCallableString ' => [
165+ 'callable-string ' ,
166+ 'lowercase-string ' ,
167+ ];
168+ }
169+
132170 /**
133171 * @return array<array{string, string}>
134172 */
@@ -155,10 +193,6 @@ public function getSuccessfulComparisons(): array
155193 'array{foo?: string}&array<string, mixed> ' ,
156194 'array<never, never> ' ,
157195 ],
158- 'Lowercase-stringAndCallable-string ' => [
159- 'lowercase-string ' ,
160- 'callable-string ' ,
161- ],
162196 'callableUnionAcceptsCallableUnion ' => [
163197 '(callable(int,string[]): void)|(callable(int): void) ' ,
164198 '(callable(int): void)|(callable(int,string[]): void) ' ,
0 commit comments