66use PHPStan \Analyser \Scope ;
77use PHPStan \Reflection \FunctionReflection ;
88use PHPStan \ShouldNotHappenException ;
9+ use PHPStan \TrinaryLogic ;
910use PHPStan \Type \Accessory \AccessoryArrayListType ;
1011use PHPStan \Type \Accessory \NonEmptyArrayType ;
1112use PHPStan \Type \ArrayType ;
@@ -39,7 +40,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
3940
4041 $ argTypes = [];
4142 $ optionalArgTypes = [];
42- $ allConstant = true ;
43+ $ allConstant = TrinaryLogic:: createYes () ;
4344 foreach ($ args as $ arg ) {
4445 $ argType = $ scope ->getType ($ arg ->value );
4546
@@ -52,10 +53,7 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
5253
5354 foreach ($ argTypesFound as $ argTypeFound ) {
5455 $ argTypes [] = $ argTypeFound ;
55- if ($ argTypeFound instanceof ConstantArrayType) {
56- continue ;
57- }
58- $ allConstant = false ;
56+ $ allConstant = $ allConstant ->and ($ argTypeFound ->isConstantArray ());
5957 }
6058
6159 if (!$ argType ->isIterableAtLeastOnce ()->yes ()) {
@@ -67,22 +65,21 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
6765 }
6866 } else {
6967 $ argTypes [] = $ argType ;
70- if (!$ argType instanceof ConstantArrayType) {
71- $ allConstant = false ;
72- }
68+ $ allConstant = $ allConstant ->and ($ argType ->isConstantArray ());
7369 }
7470 }
7571
76- if ($ allConstant ) {
72+ if ($ allConstant-> yes () ) {
7773 $ newArrayBuilder = ConstantArrayTypeBuilder::createEmpty ();
7874 foreach ($ argTypes as $ argType ) {
79- if (!$ argType instanceof ConstantArrayType) {
75+ $ constantArrayType = $ this ->untagConstantArrayUnion ($ argType );
76+ if (!$ constantArrayType instanceof ConstantArrayType) {
8077 throw new ShouldNotHappenException ();
8178 }
8279
83- $ keyTypes = $ argType ->getKeyTypes ();
84- $ valueTypes = $ argType ->getValueTypes ();
85- $ optionalKeys = $ argType ->getOptionalKeys ();
80+ $ keyTypes = $ constantArrayType ->getKeyTypes ();
81+ $ valueTypes = $ constantArrayType ->getValueTypes ();
82+ $ optionalKeys = $ constantArrayType ->getOptionalKeys ();
8683
8784 foreach ($ keyTypes as $ k => $ keyType ) {
8885 $ isOptional = in_array ($ k , $ optionalKeys , true );
@@ -138,4 +135,29 @@ public function getTypeFromFunctionCall(FunctionReflection $functionReflection,
138135 return $ arrayType ;
139136 }
140137
138+ /**
139+ * array{0: 17, foo: 'bar'}|array{0: 19, foo: 'bar', fofoo: 'barbar'}
140+ * ->
141+ * array{0: 17|19, foo: 'bar', foofo?: 'barbar'}
142+ */
143+ private function untagConstantArrayUnion (Type $ constantArrayType ): Type
144+ {
145+ $ constantArrayTypes = $ constantArrayType ->getConstantArrays ();
146+ if (count ($ constantArrayTypes ) === 1 ) {
147+ return $ constantArrayTypes [0 ];
148+ }
149+
150+ $ builder = ConstantArrayTypeBuilder::createEmpty ();
151+ $ keyTypes = $ constantArrayType ->getKeysArray ()->getIterableValueType ()->getFiniteTypes ();
152+ foreach ($ keyTypes as $ keyType ) {
153+ $ builder ->setOffsetValueType (
154+ $ keyType ,
155+ $ constantArrayType ->getOffsetValueType ($ keyType ),
156+ !$ constantArrayType ->hasOffsetValueType ($ keyType )->yes (),
157+ );
158+ }
159+
160+ return $ builder ->getArray ();
161+ }
162+
141163}
0 commit comments