4646use Twig \Node \Expression \BlockReferenceExpression ;
4747use Twig \Node \Expression \ConstantExpression ;
4848use Twig \Node \Expression \GetAttrExpression ;
49+ use Twig \Node \Expression \NameExpression ;
4950use Twig \Node \Expression \ParentExpression ;
5051use Twig \Node \Expression \TestExpression ;
5152use Twig \Node \Expression \Unary \NegUnary ;
5455use Twig \Node \MacroNode ;
5556use Twig \Node \Node ;
5657use Twig \Node \SetNode ;
58+ use Twig \Node \WithNode ;
5759use Twig \TypeHint \ArrayType ;
5860use Twig \TypeHint \TypeFactory ;
5961use Twig \TypeHint \TypeInterface ;
@@ -72,14 +74,21 @@ final class TypeEvaluateNodeVisitor implements NodeVisitorInterface
7274{
7375 public function enterNode (Node $ node , Environment $ env ): Node
7476 {
77+ if ($ node instanceof WithNode) {
78+ if ($ node ->hasNode ('variables ' )) {
79+ // we need to store the parent, so we can assign notes to the parent, after the variables' sibling nodes are entered
80+ $ node ->getNode ('variables ' )->setAttribute ('setKeyValuesAsTypeHints ' , $ node ->getAttribute ('only ' ));
81+ }
82+ }
83+
7584 return $ node ;
7685 }
7786
7887 public function leaveNode (Node $ node , Environment $ env ): ?Node
7988 {
8089 $ possibleTypes = [];
8190
82- foreach ($ this ->getPossibleTypes ($ node ) as $ possibleType ) {
91+ foreach ($ this ->getPossibleTypes ($ node, $ env ) as $ possibleType ) {
8392 if (!$ possibleType instanceof TypeInterface) {
8493 $ possibleType = TypeFactory::createTypeFromText ((string ) $ possibleType );
8594 }
@@ -114,6 +123,10 @@ public function leaveNode(Node $node, Environment $env): ?Node
114123
115124 if ($ typedVariables !== []) {
116125 $ node ->setAttribute ('typeHint ' , new ArrayType ($ typedVariables ));
126+
127+ foreach ($ typedVariables as $ typedVariableName => $ typedVariableTypes ) {
128+ $ env ->getTypeHintStack ()->addVariableType ($ typedVariableName , $ typedVariableTypes );
129+ }
117130 }
118131 }
119132
@@ -138,6 +151,33 @@ public function leaveNode(Node $node, Environment $env): ?Node
138151 }
139152 }
140153
154+ // this is the variables child node from WithNode and therefore these are types to note down
155+ if ($ node ->hasAttribute ('setKeyValuesAsTypeHints ' )) {
156+ $ only = $ node ->getAttribute ('setKeyValuesAsTypeHints ' );
157+
158+ if ($ node instanceof ArrayExpression && $ node ->hasAttribute ('typeHint ' ) && $ node ->getAttribute ('typeHint ' ) instanceof ArrayType) {
159+ if ($ only ) {
160+ $ env ->getTypeHintStack ()->pushMajorStack ();
161+ } else {
162+ $ env ->getTypeHintStack ()->pushMinorStack ();
163+ }
164+
165+ foreach ($ node ->getAttribute ('typeHint ' )->getAttributes () as $ typedVariableName => $ typedVariableTypes ) {
166+ $ env ->getTypeHintStack ()->addVariableType ($ typedVariableName , $ typedVariableTypes );
167+ }
168+ }
169+
170+ $ node ->removeAttribute ('setKeyValuesAsTypeHints ' );
171+ }
172+
173+ if ($ node instanceof WithNode) {
174+ if ($ node ->getAttribute ('only ' )) {
175+ $ env ->getTypeHintStack ()->popMajorStack ();
176+ } else {
177+ $ env ->getTypeHintStack ()->popMinorStack ();
178+ }
179+ }
180+
141181 return $ node ;
142182 }
143183
@@ -146,7 +186,7 @@ public function getPriority(): int
146186 return 10 ;
147187 }
148188
149- private function getPossibleTypes (Node $ node ): iterable
189+ private function getPossibleTypes (Node $ node, Environment $ env ): iterable
150190 {
151191 if ($ node instanceof AutoEscapeNode) {
152192 yield 'string ' ;
@@ -163,6 +203,9 @@ private function getPossibleTypes(Node $node): iterable
163203
164204 if ($ node ->getNode ('node ' )->hasAttribute ('typeHint ' )) {
165205 $ typeHint = $ node ->getNode ('node ' )->getAttribute ('typeHint ' );
206+ } elseif ($ node ->getNode ('node ' ) instanceof NameExpression) {
207+ $ variableName = $ node ->getNode ('node ' )->getAttribute ('name ' );
208+ $ typeHint = $ env ->getTypeHintStack ()->getVariableType ($ variableName );
166209 }
167210
168211 if ($ typeHint instanceof TypeInterface) {
@@ -192,6 +235,10 @@ private function getPossibleTypes(Node $node): iterable
192235 yield 'string ' ;
193236 }
194237
238+ if ($ node instanceof NameExpression && !$ node instanceof AssignNameExpression) {
239+ yield $ env ->getTypeHintStack ()->getVariableType ($ node ->getAttribute ('name ' ));
240+ }
241+
195242 if ($ node instanceof TestExpression) {
196243 yield 'boolean ' ;
197244 }
0 commit comments