Repository: spaze/phpstan-disallowed-calls (php)

Issue:
allowExceptParams fails on v2.11.3 when concatenation with object param
I had this PHPStan rule specified in my configuration:
```neon
parameters:
	disallowedFunctionCalls:
		-
			function: 'env()'
			allowIn:
				- 'src/AllowedIn.php'
			allowExceptParams:
				-
					position: 1
					name: 'key'
					value: 'SOME_ENV'
```
and it worked perfectly fine until upgrading to version v2.11.3. In the version v2.11.3 and v2.11.4 code snippet from below fails even though it shouldn't:

```php
<?php

class WithProperty {
	/** @var string */
	public string $property;
}

class Test {
	public function __construct() {
		$withProperty = new WithProperty();
		env($withProperty->property . '_OTHER');
	}
}

```

Pull request description:
Don't return plain false when the param is not of the type we know how to process
Close #166

Patch:
diff --git a/src/DisallowedHelper.php b/src/DisallowedHelper.php
index 363e262..ce06879 100644
--- a/src/DisallowedHelper.php
+++ b/src/DisallowedHelper.php
@@ -19,6 +19,7 @@ use PHPStan\Type\ObjectType;
 use PHPStan\Type\Type;
 use PHPStan\Type\TypeWithClassName;
 use PHPStan\Type\UnionType;
+use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
 use Spaze\PHPStan\Rules\Disallowed\Params\DisallowedCallParam;
 
 class DisallowedHelper
@@ -118,8 +119,12 @@ class DisallowedHelper
 				$types = [$type];
 			}
 			foreach ($types as $type) {
-				if (!$param->matches($type)) {
-					return false;
+				try {
+					if (!$param->matches($type)) {
+						return false;
+					}
+				} catch (UnsupportedParamTypeException $e) {
+					return !$paramsRequired;
 				}
 			}
 		}
diff --git a/src/Exceptions/UnsupportedParamTypeException.php b/src/Exceptions/UnsupportedParamTypeException.php
new file mode 100644
index 0000000..3afb446
--- /dev/null
+++ b/src/Exceptions/UnsupportedParamTypeException.php
@@ -0,0 +1,10 @@
+<?php
+declare(strict_types = 1);
+
+namespace Spaze\PHPStan\Rules\Disallowed\Exceptions;
+
+use Exception;
+
+class UnsupportedParamTypeException extends Exception
+{
+}
diff --git a/src/Params/DisallowedCallParamValueCaseInsensitiveExcept.php b/src/Params/DisallowedCallParamValueCaseInsensitiveExcept.php
index e913080..c52eee5 100644
--- a/src/Params/DisallowedCallParamValueCaseInsensitiveExcept.php
+++ b/src/Params/DisallowedCallParamValueCaseInsensitiveExcept.php
@@ -6,6 +6,7 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params;
 use PHPStan\Type\Constant\ConstantStringType;
 use PHPStan\Type\ConstantScalarType;
 use PHPStan\Type\Type;
+use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
 
 /**
  * @extends DisallowedCallParamValue<int|bool|string|null>
@@ -13,10 +14,13 @@ use PHPStan\Type\Type;
 class DisallowedCallParamValueCaseInsensitiveExcept extends DisallowedCallParamValue
 {
 
+	/**
+	 * @throws UnsupportedParamTypeException
+	 */
 	public function matches(Type $type): bool
 	{
 		if (!$type instanceof ConstantScalarType) {
-			return false;
+			throw new UnsupportedParamTypeException();
 		}
 		$a = is_string($this->getValue()) ? strtolower($this->getValue()) : $this->getValue();
 		$b = $type instanceof ConstantStringType ? strtolower($type->getValue()) : $type->getValue();
diff --git a/src/Params/DisallowedCallParamValueExcept.php b/src/Params/DisallowedCallParamValueExcept.php
index 2f5e70d..5a2814a 100644
--- a/src/Params/DisallowedCallParamValueExcept.php
+++ b/src/Params/DisallowedCallParamValueExcept.php
@@ -5,6 +5,7 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params;
 
 use PHPStan\Type\ConstantScalarType;
 use PHPStan\Type\Type;
+use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
 
 /**
  * @extends DisallowedCallParamValue<int|bool|string|null>
@@ -12,10 +13,13 @@ use PHPStan\Type\Type;
 class DisallowedCallParamValueExcept extends DisallowedCallParamValue
 {
 
+	/**
+	 * @throws UnsupportedParamTypeException
+	 */
 	public function matches(Type $type): bool
 	{
 		if (!$type instanceof ConstantScalarType) {
-			return false;
+			throw new UnsupportedParamTypeException();
 		}
 		return $this->getValue() !== $type->getValue();
 	}
diff --git a/src/Params/DisallowedCallParamValueFlagExcept.php b/src/Params/DisallowedCallParamValueFlagExcept.php
index 5548f8e..6da59ae 100644
--- a/src/Params/DisallowedCallParamValueFlagExcept.php
+++ b/src/Params/DisallowedCallParamValueFlagExcept.php
@@ -5,6 +5,7 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params;
 
 use PHPStan\Type\Constant\ConstantIntegerType;
 use PHPStan\Type\Type;
+use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
 
 /**
  * @extends DisallowedCallParamValue<int>
@@ -12,10 +13,13 @@ use PHPStan\Type\Type;
 class DisallowedCallParamValueFlagExcept extends DisallowedCallParamValue
 {
 
+	/**
+	 * @throws UnsupportedParamTypeException
+	 */
 	public function matches(Type $type): bool
 	{
 		if (!$type instanceof ConstantIntegerType) {
-			return false;
+			throw new UnsupportedParamTypeException();
 		}
 		return ($this->getValue() & $type->getValue()) === 0;
 	}
diff --git a/src/Params/DisallowedCallParamValueFlagSpecific.php b/src/Params/DisallowedCallParamValueFlagSpecific.php
index d515af4..9e1e823 100644
--- a/src/Params/DisallowedCallParamValueFlagSpecific.php
+++ b/src/Params/DisallowedCallParamValueFlagSpecific.php
@@ -5,6 +5,7 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params;
 
 use PHPStan\Type\Constant\ConstantIntegerType;
 use PHPStan\Type\Type;
+use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
 
 /**
  * @extends DisallowedCallParamValue<int>
@@ -12,10 +13,13 @@ use PHPStan\Type\Type;
 class DisallowedCallParamValueFlagSpecific extends DisallowedCallParamValue
 {
 
+	/**
+	 * @throws UnsupportedParamTypeException
+	 */
 	public function matches(Type $type): bool
 	{
 		if (!$type instanceof ConstantIntegerType) {
-			return false;
+			throw new UnsupportedParamTypeException();
 		}
 		return ($this->getValue() & $type->getValue()) !== 0;
 	}
diff --git a/src/Params/DisallowedCallParamValueSpecific.php b/src/Params/DisallowedCallParamValueSpecific.php
index c777138..b824ad8 100644
--- a/src/Params/DisallowedCallParamValueSpecific.php
+++ b/src/Params/DisallowedCallParamValueSpecific.php
@@ -5,6 +5,7 @@ namespace Spaze\PHPStan\Rules\Disallowed\Params;
 
 use PHPStan\Type\ConstantScalarType;
 use PHPStan\Type\Type;
+use Spaze\PHPStan\Rules\Disallowed\Exceptions\UnsupportedParamTypeException;
 
 /**
  * @extends DisallowedCallParamValue<int|bool|string|null>
@@ -12,10 +13,13 @@ use PHPStan\Type\Type;
 class DisallowedCallParamValueSpecific extends DisallowedCallParamValue
 {
 
+	/**
+	 * @throws UnsupportedParamTypeException
+	 */
 	public function matches(Type $type): bool
 	{
 		if (!$type instanceof ConstantScalarType) {
-			return false;
+			throw new UnsupportedParamTypeException();
 		}
 		return $this->getValue() === $type->getValue();
 	}


Test patch:
diff --git a/tests/src/disallowed-allow/functionCalls.php b/tests/src/disallowed-allow/functionCalls.php
index 569b3b4..5d5151b 100644
--- a/tests/src/disallowed-allow/functionCalls.php
+++ b/tests/src/disallowed-allow/functionCalls.php
@@ -81,3 +81,6 @@ array_filter(['1', '2'], function () {
 // allowed when not FQCN to Blade class
 mocky(\Fiction\Pulp\Royale::class);
 mocky(\Waldo\Quux\Blade::class);
+
+// not disallowed
+hash((new stdClass())->property . 'foo', 'NAH');
diff --git a/tests/src/disallowed/functionCalls.php b/tests/src/disallowed/functionCalls.php
index 0f0299a..56cba2d 100644
--- a/tests/src/disallowed/functionCalls.php
+++ b/tests/src/disallowed/functionCalls.php
@@ -81,3 +81,6 @@ array_filter(['1', '2'], function () {
 // allowed when not FQCN to Blade class
 mocky(\Fiction\Pulp\Royale::class);
 mocky(\Waldo\Quux\Blade::class);
+
+// not disallowed
+hash((new stdClass())->property . 'foo', 'NAH');