Skip to content

Commit 5cd3df1

Browse files
authored
[console] nullable & bool default values (#92)
* [console] nullable & bool default values * no message
1 parent fcef6b8 commit 5cd3df1

File tree

3 files changed

+97
-42
lines changed

3 files changed

+97
-42
lines changed

src/Handler/ConsoleHandler.php

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?php
22

3+
declare(strict_types=1);
4+
35
namespace Psalm\SymfonyPsalmPlugin\Handler;
46

57
use PhpParser\Node\Arg;
@@ -136,12 +138,20 @@ private static function analyseArgument(array $args, StatementsSource $statement
136138

137139
if ($mode & InputArgument::IS_ARRAY) {
138140
$returnTypes = new Union([new TArray([new Union([new TInt()]), new Union([new TString()])])]);
139-
} elseif ($mode & InputArgument::REQUIRED || isset($args[3])) {
141+
} elseif ($mode & InputArgument::REQUIRED) {
140142
$returnTypes = new Union([new TString()]);
141143
} else {
142144
$returnTypes = new Union([new TString(), new TNull()]);
143145
}
144146

147+
if (isset($args[3])) {
148+
$defaultArg = $args[3];
149+
$returnTypes->removeType('null');
150+
if ($defaultArg->value instanceof Expr\ConstFetch && 'null' === $defaultArg->value->name->parts[0]) {
151+
$returnTypes->addType(new TNull());
152+
}
153+
}
154+
145155
self::$arguments[$identifier] = $returnTypes;
146156
}
147157

@@ -150,6 +160,11 @@ private static function analyseArgument(array $args, StatementsSource $statement
150160
*/
151161
private static function analyseOption(array $args, StatementsSource $statements_source): void
152162
{
163+
$identifier = self::getNodeIdentifier($args[0]->value);
164+
if (!$identifier) {
165+
return;
166+
}
167+
153168
if (isset($args[2])) {
154169
try {
155170
$mode = self::getModeValue($args[2]->value);
@@ -167,24 +182,34 @@ private static function analyseOption(array $args, StatementsSource $statements_
167182

168183
$returnTypes = new Union([new TString(), new TNull()]);
169184

185+
if (isset($args[4])) {
186+
$defaultArg = $args[4];
187+
$returnTypes->removeType('null');
188+
if ($defaultArg->value instanceof Expr\ConstFetch) {
189+
switch ($defaultArg->value->name->parts[0]) {
190+
case 'null':
191+
$returnTypes->addType(new TNull());
192+
break;
193+
case 'false':
194+
case 'true':
195+
$returnTypes->addType(new TBool());
196+
break;
197+
}
198+
}
199+
}
200+
170201
if ($mode & InputOption::VALUE_NONE) {
171202
$returnTypes = new Union([new TBool()]);
172203
}
173204

174-
if ($mode & InputOption::VALUE_REQUIRED &&
175-
($mode & InputOption::VALUE_IS_ARRAY || isset($args[4]))) {
205+
if ($mode & InputOption::VALUE_REQUIRED && $mode & InputOption::VALUE_IS_ARRAY) {
176206
$returnTypes->removeType('null');
177207
}
178208

179209
if ($mode & InputOption::VALUE_IS_ARRAY) {
180210
$returnTypes = new Union([new TArray([new Union([new TInt()]), $returnTypes])]);
181211
}
182212

183-
$identifier = self::getNodeIdentifier($args[0]->value);
184-
if (!$identifier) {
185-
return;
186-
}
187-
188213
self::$options[$identifier] = $returnTypes;
189214
}
190215

tests/acceptance/acceptance/ConsoleArgument.feature

Lines changed: 39 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,22 @@ Feature: ConsoleArgument
5555
5656
public function execute(InputInterface $input, OutputInterface $output): int
5757
{
58-
$string = $input->getArgument('required_string');
59-
$output->writeLn(sprintf('%s', $string));
58+
/** @psalm-trace $arg1 */
59+
$arg1 = $input->getArgument('required_string');
6060
61-
$array = $input->getArgument('required_array');
62-
shuffle($array);
61+
/** @psalm-trace $arg2 */
62+
$arg2 = $input->getArgument('required_array');
6363
6464
return 0;
6565
}
6666
}
6767
"""
6868
When I run Psalm
69-
Then I see no errors
69+
Then I see these errors
70+
| Type | Message |
71+
| Trace | $arg1: string |
72+
| Trace | $arg2: array<int, string> |
73+
And I see no other errors
7074

7175
Scenario: Asserting arguments return types have inferred (without error) using Definition array
7276
Given I have the following code
@@ -83,18 +87,22 @@ Feature: ConsoleArgument
8387
8488
public function execute(InputInterface $input, OutputInterface $output): int
8589
{
86-
$string = $input->getArgument('required_string');
87-
$output->writeLn(sprintf('%s', $string));
90+
/** @psalm-trace $arg1 */
91+
$arg1 = $input->getArgument('required_string');
8892
89-
$array = $input->getArgument('required_array');
90-
shuffle($array);
93+
/** @psalm-trace $arg2 */
94+
$arg2 = $input->getArgument('required_array');
9195
9296
return 0;
9397
}
9498
}
9599
"""
96100
When I run Psalm
97-
Then I see no errors
101+
Then I see these errors
102+
| Type | Message |
103+
| Trace | $arg1: string |
104+
| Trace | $arg2: array<int, string> |
105+
And I see no other errors
98106

99107
Scenario: Asserting arguments return types have inferred (without error) using Definition
100108
Given I have the following code
@@ -113,20 +121,24 @@ Feature: ConsoleArgument
113121
114122
public function execute(InputInterface $input, OutputInterface $output): int
115123
{
116-
$string = $input->getArgument('required_string');
117-
$output->writeLn(sprintf('%s', $string));
124+
/** @psalm-trace $arg1 */
125+
$arg1 = $input->getArgument('required_string');
118126
119-
$array = $input->getArgument('required_array');
120-
shuffle($array);
127+
/** @psalm-trace $arg2 */
128+
$arg2 = $input->getArgument('required_array');
121129
122130
return 0;
123131
}
124132
}
125133
"""
126134
When I run Psalm
127-
Then I see no errors
135+
Then I see these errors
136+
| Type | Message |
137+
| Trace | $arg1: string |
138+
| Trace | $arg2: array<int, string> |
139+
And I see no other errors
128140

129-
Scenario: Asserting arguments return types have inferred (without error) 2
141+
Scenario: Asserting arguments return types have inferred with const name
130142
Given I have the following code
131143
"""
132144
class MyCommand extends Command
@@ -140,15 +152,18 @@ Feature: ConsoleArgument
140152
141153
public function execute(InputInterface $input, OutputInterface $output): int
142154
{
143-
$string = $input->getArgument(self::FOO_ARGUMENT_NAME);
144-
$output->writeLn(sprintf('%s', $string));
155+
/** @psalm-trace $arg1 */
156+
$arg1 = $input->getArgument(self::FOO_ARGUMENT_NAME);
145157
146158
return 0;
147159
}
148160
}
149161
"""
150162
When I run Psalm
151-
Then I see no errors
163+
Then I see these errors
164+
| Type | Message |
165+
| Trace | $arg1: string |
166+
And I see no other errors
152167

153168
Scenario: Asserting string arguments return types have inferred
154169
Given I have the following code
@@ -164,6 +179,7 @@ Feature: ConsoleArgument
164179
->addArgument('arg4', InputArgument::OPTIONAL)
165180
->addArgument('arg5', InputArgument::OPTIONAL, '', 'default value')
166181
->addArgument('arg6', InputArgument::OPTIONAL)
182+
->addArgument('arg7', InputArgument::OPTIONAL, '', null)
167183
;
168184
}
169185
@@ -187,6 +203,9 @@ Feature: ConsoleArgument
187203
/** @psalm-trace $arg6 */
188204
$arg6 = $input->getArgument('arg6');
189205
206+
/** @psalm-trace $arg7 */
207+
$arg7 = $input->getArgument('arg7');
208+
190209
return 0;
191210
}
192211
}
@@ -200,6 +219,7 @@ Feature: ConsoleArgument
200219
| Trace | $arg4: null\|string |
201220
| Trace | $arg5: string |
202221
| Trace | $arg6: null\|string |
222+
| Trace | $arg7: null\|string |
203223
And I see no other errors
204224

205225
Scenario Outline: Asserting array arguments return types have inferred

tests/acceptance/acceptance/ConsoleOption.feature

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -84,33 +84,43 @@ Feature: ConsoleOption
8484
{
8585
public function configure(): void
8686
{
87-
$this->addOption('required_array', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, '', []);
88-
$this->addOption('required_string', null, InputOption::VALUE_REQUIRED, '', 'default');
89-
$this->addOption('boolean', null, InputOption::VALUE_NONE, '', true);
87+
$this->addOption('option1', null, InputOption::VALUE_REQUIRED | InputOption::VALUE_IS_ARRAY, '', []);
88+
$this->addOption('option2', null, InputOption::VALUE_REQUIRED, '', 'default');
89+
$this->addOption('option3', null, InputOption::VALUE_NONE, '', true);
90+
$this->addOption('option4', null, InputOption::VALUE_OPTIONAL, '', false);
91+
$this->addOption('option5', null, InputOption::VALUE_OPTIONAL, '', null);
9092
}
9193
9294
public function execute(InputInterface $input, OutputInterface $output): int
9395
{
94-
$string = $input->getOption('required_string');
95-
$output->writeLn(sprintf('%s', $string));
96+
/** @psalm-trace $option1 */
97+
$option1 = $input->getOption('option1');
9698
97-
$array = $input->getOption('required_array');
98-
foreach ($array as $value) {
99-
$output->writeLn(sprintf('%s', $value));
100-
};
99+
/** @psalm-trace $option2 */
100+
$option2 = $input->getOption('option2');
101101
102-
$this->boolean($input->getOption('boolean'));
102+
/** @psalm-trace $option3 */
103+
$option3 = $input->getOption('option3');
103104
104-
return 0;
105-
}
105+
/** @psalm-trace $option4 */
106+
$option4 = $input->getOption('option4');
106107
107-
private function boolean(bool $input): void
108-
{
108+
/** @psalm-trace $option5 */
109+
$option5 = $input->getOption('option5');
110+
111+
return 0;
109112
}
110113
}
111114
"""
112115
When I run Psalm
113-
Then I see no errors
116+
Then I see these errors
117+
| Type | Message |
118+
| Trace | $option1: array<int, string> |
119+
| Trace | $option2: string |
120+
| Trace | $option3: bool |
121+
| Trace | $option4: bool |
122+
| Trace | $option5: null\|string |
123+
And I see no other errors
114124

115125
Scenario: Asserting options return types have inferred (without error) using Definition
116126
Given I have the following code

0 commit comments

Comments
 (0)