16
16
use Psalm \Internal \DataFlow \TaintSource ;
17
17
use Psalm \Plugin \EventHandler \Event \AddRemoveTaintsEvent ;
18
18
use Psalm \Type ;
19
- use Psalm \Type \Atomic \TLiteralFloat ;
20
- use Psalm \Type \Atomic \TLiteralInt ;
21
19
use Psalm \Type \Atomic \TLiteralString ;
22
20
use Psalm \Type \Atomic \TNonEmptyNonspecificLiteralString ;
23
21
use Psalm \Type \Atomic \TNonEmptyString ;
24
- use Psalm \Type \Atomic \TNonspecificLiteralInt ;
25
22
use Psalm \Type \Atomic \TNonspecificLiteralString ;
26
23
use Psalm \Type \Atomic \TString ;
27
24
use Psalm \Type \Union ;
@@ -40,24 +37,28 @@ public static function analyze(
40
37
Context $ context ,
41
38
): bool {
42
39
$ parent_nodes = [];
43
-
44
40
$ non_empty = false ;
45
-
46
41
$ all_literals = true ;
47
-
48
42
$ literal_string = "" ;
43
+ $ impossible = false ;
49
44
50
45
foreach ($ stmt ->parts as $ part ) {
51
46
if ($ part instanceof Expr) {
47
+ $ was_inside_general_use = $ context ->inside_general_use ;
48
+ $ context ->inside_general_use = true ;
52
49
if (ExpressionAnalyzer::analyze ($ statements_analyzer , $ part , $ context ) === false ) {
50
+ $ context ->inside_general_use = $ was_inside_general_use ;
53
51
return false ;
54
52
}
53
+
54
+ $ context ->inside_general_use = $ was_inside_general_use ;
55
55
}
56
56
57
57
if ($ part instanceof InterpolatedStringPart) {
58
58
if ($ literal_string !== null ) {
59
59
$ literal_string .= $ part ->value ;
60
60
}
61
+
61
62
$ non_empty = $ non_empty || $ part ->value !== "" ;
62
63
} elseif ($ part_type = $ statements_analyzer ->node_data ->getType ($ part )) {
63
64
$ casted_part_type = CastAnalyzer::castStringAttempt (
@@ -67,30 +68,65 @@ public static function analyze(
67
68
$ part ,
68
69
);
69
70
70
- if (!$ casted_part_type ->allLiterals ()) {
71
- $ all_literals = false ;
72
- } elseif (!$ non_empty ) {
73
- // Check if all literals are nonempty
74
- $ non_empty = true ;
75
- foreach ($ casted_part_type ->getAtomicTypes () as $ atomic_literal ) {
76
- if (!$ atomic_literal instanceof TLiteralInt
77
- && !$ atomic_literal instanceof TNonspecificLiteralInt
78
- && !$ atomic_literal instanceof TLiteralFloat
79
- && !$ atomic_literal instanceof TNonEmptyNonspecificLiteralString
80
- && !($ atomic_literal instanceof TLiteralString && $ atomic_literal ->value !== "" )
81
- ) {
82
- $ non_empty = false ;
83
- break ;
71
+ if ($ casted_part_type ->isNever ()) {
72
+ $ impossible = true ;
73
+
74
+ continue ;
75
+ }
76
+
77
+
78
+ $ is_non_empty_part = true ;
79
+ $ part_is_all_literals = true ;
80
+ $ part_literal_string = null ;
81
+ $ could_specify_literals = true ;
82
+ foreach ($ casted_part_type ->getAtomicTypes () as $ casted_part_atomic ) {
83
+ if (!$ casted_part_atomic instanceof TString) {
84
+ $ part_is_all_literals = false ;
85
+ $ could_specify_literals = false ;
86
+ continue ;
87
+ }
88
+
89
+ if (!$ casted_part_atomic instanceof TNonEmptyString
90
+ && !$ casted_part_atomic instanceof TNonEmptyNonspecificLiteralString
91
+ && !($ casted_part_atomic instanceof TLiteralString && $ casted_part_atomic ->value !== '' )
92
+ ) {
93
+ $ is_non_empty_part = false ;
94
+ }
95
+
96
+ if (!$ part_is_all_literals || !$ could_specify_literals ) {
97
+ continue ;
98
+ }
99
+
100
+ if ($ casted_part_atomic instanceof TLiteralString) {
101
+ if ($ part_literal_string === null ) {
102
+ $ part_literal_string = $ casted_part_atomic ->value ;
103
+ } elseif ($ part_literal_string !== $ casted_part_atomic ->value ) {
104
+ $ part_literal_string = null ;
105
+ $ could_specify_literals = false ;
84
106
}
107
+
108
+ continue ;
85
109
}
86
- }
87
110
88
- if ($ literal_string !== null ) {
89
- if ($ casted_part_type ->isSingleLiteral ()) {
90
- $ literal_string .= $ casted_part_type ->getSingleLiteral ()->value ;
91
- } else {
111
+ if ($ casted_part_atomic instanceof TNonspecificLiteralString) {
92
112
$ literal_string = null ;
113
+ $ part_literal_string = null ;
114
+ $ could_specify_literals = false ;
115
+
116
+ continue ;
93
117
}
118
+
119
+ $ part_is_all_literals = false ;
120
+ }
121
+
122
+ $ non_empty = $ non_empty || $ is_non_empty_part ;
123
+ $ all_literals = $ all_literals && $ part_is_all_literals ;
124
+ if (!$ part_is_all_literals || !$ could_specify_literals ) {
125
+ $ literal_string = null ;
126
+ } else if ($ part_literal_string !== null && $ literal_string !== null ) {
127
+ $ literal_string .= $ part_literal_string ;
128
+ } else {
129
+ $ literal_string = null ;
94
130
}
95
131
96
132
if ($ statements_analyzer ->data_flow_graph
@@ -134,36 +170,26 @@ public static function analyze(
134
170
}
135
171
}
136
172
137
- if ($ non_empty ) {
138
- if ($ literal_string !== null ) {
139
- $ stmt_type = new Union (
140
- [Type::getAtomicStringFromLiteral ($ literal_string )],
141
- ['parent_nodes ' => $ parent_nodes ],
142
- );
143
- } elseif ($ all_literals ) {
144
- $ stmt_type = new Union (
145
- [new TNonEmptyNonspecificLiteralString ()],
146
- ['parent_nodes ' => $ parent_nodes ],
147
- );
148
- } else {
149
- $ stmt_type = new Union (
150
- [new TNonEmptyString ()],
151
- ['parent_nodes ' => $ parent_nodes ],
152
- );
153
- }
173
+ if ($ impossible ) {
174
+ $ resulting_string = Type::getNever ();
175
+ } elseif ($ literal_string !== null ) {
176
+ $ resulting_string = Type::getAtomicStringFromLiteral ($ literal_string );
177
+ } elseif ($ non_empty && $ all_literals ) {
178
+ $ resulting_string = new TNonEmptyNonspecificLiteralString ();
179
+ } elseif ($ non_empty ) {
180
+ $ resulting_string = new TNonEmptyString ();
154
181
} elseif ($ all_literals ) {
155
- $ stmt_type = new Union (
156
- [new TNonspecificLiteralString ()],
157
- ['parent_nodes ' => $ parent_nodes ],
158
- );
182
+ $ resulting_string = new TNonspecificLiteralString ();
159
183
} else {
160
- $ stmt_type = new Union (
161
- [new TString ()],
162
- ['parent_nodes ' => $ parent_nodes ],
163
- );
184
+ $ resulting_string = new TString ();
164
185
}
165
186
166
- $ statements_analyzer ->node_data ->setType ($ stmt , $ stmt_type );
187
+ $ resulting_type = new Union (
188
+ [$ resulting_string ],
189
+ ['parent_nodes ' => $ parent_nodes ],
190
+ );
191
+
192
+ $ statements_analyzer ->node_data ->setType ($ stmt , $ resulting_type );
167
193
168
194
return true ;
169
195
}
0 commit comments