Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds type specifier for settype. #2920

Merged
merged 4 commits into from Feb 19, 2024
Merged

Conversation

ChrisBrenton
Copy link
Contributor

I'm fairly new to PHPStan's code base and I've been looking through some of the issues marked "Easy fixes" for a way to get started. I'm not certain if I am going about this the right way or if its completely mad - particularly the test.

I don't expect this to be accepted as is, but it'd be good to get some pointers to how I might otherwise go about it if/when you have time?

/**
* @param 'string'|'int'|'integer'|'float'|'double'|'bool'|'boolean'|'array'|'object'|'null' $castTo
*/
function setTypeSpecifying(string $value, string $castTo): void
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a 2nd test function which works with a non-constant $x would be helpful.

Like

function doFoo(int $i, string $s ...)

And assert similar to what you did in this method

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I caught the intention of your message correctly, this is just a mistake on my part when writing this test initially. The function was supposed to receive a mixed type for the first argument to match the signature of settype. I've changed this now, but if that wasn't the intention, please correct me.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a test-functions like

function doBoolean(int $i, string $s) {
	settype($i, 'boolean');
	assertType('bool', $x);

	settype($s, 'boolean');
	assertType('bool', $x);
}

since for this non-constant values, different types are returned.
there are lots of combinations and I think it doesn't make sense to test all combinations.. but at least a few most common ones should be tested

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated - I've added the functions as you described to the existing test cases.

@ChrisBrenton ChrisBrenton changed the title RFC: Adds type specifier for settype. Adds type specifier for settype. Feb 17, 2024
$valueType = $scope->getType($value);
$castType = $scope->getType($node->getArgs()[1]->value);

$constantStrings = $castType->getConstantStrings();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If the constant string is empty, we should return empty new SpecifiedTypes here.

}
}

return new SpecifiedTypes(['$value' => [$value, TypeCombinator::union(...$types)]], [], true);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$value is wrong here, it cannot work. It'd only work if the call looked like this: settype($value, 'int').

You need to call TypeSpecifier::create(), as all the other type-specifying extensions do. That will create the correct SpecifiedTypes object for the right expression.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've made the change as requested, but I am not confident that I have implemented it correctly. The tests pass as I'd expect but I am very unsure as to the purpose/behaviour of $context. I would appreciate feedback on this PR if possible, and if you have time and are willing, an explanation of $context - for me this is one of the biggest unknowns I have come across so far.

Copy link
Member

@ondrejmirtes ondrejmirtes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise it looks fine.

public function isFunctionSupported(FunctionReflection $functionReflection, FuncCall $node, TypeSpecifierContext $context): bool
{
return strtolower($functionReflection->getName()) === 'settype'
&& count($node->getArgs()) > 1;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs to check that $context->null().

switch ($constantString->getValue()) {
case 'bool':
case 'boolean':
var_dump('converting to bool');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot var_dump calls there

$value,
TypeCombinator::union(...$types),
TypeSpecifierContext::createTruthy(),
true,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No overwriting - this needs to be false.

@ondrejmirtes
Copy link
Member

Yeah, and a lot of tests are failing, please fix them :)

Also, I've got an idea for a future PR: There could be a rule that checks whether the performed settype makes sense - e.g. whether it doesn't lead to ErrorType.

@ondrejmirtes
Copy link
Member

Oh I'm sorry, $overwrite needs to be true in this case, it's a very rare case where this value makes sense :)

@ChrisBrenton
Copy link
Contributor Author

No worries. Fixed. My understanding is this is true in this scenario because it is effecting the passed in value by reference?

@ondrejmirtes
Copy link
Member

Let's say that the original type is non-empty-string|int. When the extension specified the type to StringType, then:

  • With overwrite=false the resulting type is non-empty-string
  • With overwrite=true the resulting type string

Most type-specifying functions are about type-checking and narrowing, not about casting (changing) the type. That's why overwrite=false is there in 99 % of cases.

@ondrejmirtes ondrejmirtes merged commit d2c30d1 into phpstan:1.10.x Feb 19, 2024
440 checks passed
@ondrejmirtes
Copy link
Member

Thank you.

@ondrejmirtes
Copy link
Member

FYI I thought you meant to fix phpstan/phpstan#3250 but I don't think it does that. Please try that with a regression test in a new PR.

@ChrisBrenton ChrisBrenton deleted the bug-3250 branch February 19, 2024 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
3 participants