Skip to content
Permalink
Browse files

Split apart TypeCoercion issues, allowing more granular issue filtering

  • Loading branch information...
muglug committed Apr 25, 2019
1 parent f227af8 commit 0e4c8ce482a16fd4898aaa6b649ef70e1f6f6cd7
Showing with 417 additions and 134 deletions.
  1. +40 −16 config.xsd
  2. +89 −27 docs/issues.md
  3. +7 −5 examples/plugins/composer-based/echo-checker/EchoChecker.php
  4. +23 −2 src/Psalm/Config.php
  5. +22 −0 src/Psalm/Config/IssueHandler.php
  6. +2 −2 src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php
  7. +14 −10 src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/PropertyAssignmentAnalyzer.php
  8. +8 −6 src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php
  9. +40 −23 src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php
  10. +2 −2 src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php
  11. +7 −1 src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php
  12. +1 −1 src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php
  13. +2 −2 src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php
  14. +24 −0 src/Psalm/Issue/ArgumentIssue.php
  15. +6 −0 src/Psalm/Issue/ArgumentTypeCoercion.php
  16. +24 −0 src/Psalm/Issue/FunctionIssue.php
  17. +1 −1 src/Psalm/Issue/InvalidArgument.php
  18. +1 −1 src/Psalm/Issue/InvalidScalarArgument.php
  19. +1 −1 src/Psalm/Issue/MixedArgument.php
  20. +6 −0 src/Psalm/Issue/MixedArgumentTypeCoercion.php
  21. +6 −0 src/Psalm/Issue/MixedArrayTypeCoercion.php
  22. +6 −0 src/Psalm/Issue/MixedPropertyTypeCoercion.php
  23. +6 −0 src/Psalm/Issue/MixedReturnTypeCoercion.php
  24. +5 −1 src/Psalm/Issue/MixedTypeCoercion.php
  25. +1 −1 src/Psalm/Issue/NullArgument.php
  26. +1 −1 src/Psalm/Issue/PossiblyFalseArgument.php
  27. +1 −1 src/Psalm/Issue/PossiblyInvalidArgument.php
  28. +1 −1 src/Psalm/Issue/PossiblyNullArgument.php
  29. +6 −0 src/Psalm/Issue/PropertyTypeCoercion.php
  30. +1 −1 src/Psalm/Issue/TooFewArguments.php
  31. +1 −1 src/Psalm/Issue/TooManyArguments.php
  32. +5 −1 src/Psalm/Issue/TypeCoercion.php
  33. +1 −1 src/Psalm/Issue/UndefinedFunction.php
  34. +25 −0 src/Psalm/IssueBuffer.php
  35. +1 −1 tests/AnnotationTest.php
  36. +1 −1 tests/ArrayAccessTest.php
  37. +1 −1 tests/ArrayAssignmentTest.php
  38. +2 −2 tests/ClassStringTest.php
  39. +2 −0 tests/ConfigTest.php
  40. +4 −0 tests/DocumentationTest.php
  41. +4 −4 tests/FunctionCallTest.php
  42. +2 −2 tests/MagicPropertyTest.php
  43. +2 −2 tests/MethodSignatureTest.php
  44. +4 −4 tests/PropertyTypeTest.php
  45. +1 −1 tests/ReturnTypeTest.php
  46. +6 −6 tests/Template/TemplateTest.php
  47. +1 −1 tests/TypeTest.php
@@ -140,6 +140,7 @@
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="PluginIssue" type="PluginIssueHandlerType" minOccurs="0" />
<xs:element name="AbstractInstantiation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ArgumentTypeCoercion" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="AssignmentToVoid" type="IssueHandlerType" minOccurs="0" />
<xs:element name="CircularReference" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ContinueOutsideLoop" type="IssueHandlerType" minOccurs="0" />
@@ -155,7 +156,7 @@
<xs:element name="DuplicateParam" type="IssueHandlerType" minOccurs="0" />
<xs:element name="DuplicateFunction" type="IssueHandlerType" minOccurs="0" />
<xs:element name="DuplicateClass" type="IssueHandlerType" minOccurs="0" />
<xs:element name="DuplicateMethod" type="IssueHandlerType" minOccurs="0" />
<xs:element name="DuplicateMethod" type="MethodIssueHandlerType" minOccurs="0" />
<xs:element name="EmptyArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="FalseOperand" type="IssueHandlerType" minOccurs="0" />
<xs:element name="FalsableReturnStatement" type="IssueHandlerType" minOccurs="0" />
@@ -165,13 +166,13 @@
<xs:element name="ImplementedReturnTypeMismatch" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ImplicitToStringCast" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InaccessibleClassConstant" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InaccessibleMethod" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InaccessibleMethod" type="MethodIssueHandlerType" minOccurs="0" />
<xs:element name="InaccessibleProperty" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InterfaceInstantiation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InternalClass" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="InternalMethod" type="MethodIssueHandlerType" minOccurs="0" />
<xs:element name="InternalProperty" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="InvalidArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="InvalidArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidArrayOffset" type="IssueHandlerType" minOccurs="0" />
@@ -195,7 +196,7 @@
<xs:element name="InvalidPropertyFetch" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidReturnStatement" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidReturnType" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidScalarArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidScalarArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="InvalidScope" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidStaticInvocation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="InvalidStaticVariable" type="IssueHandlerType" minOccurs="0" />
@@ -223,10 +224,11 @@
<xs:element name="MissingDocblockType" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MissingTemplateParam" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MissingThrowsDocblock" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="MixedArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedArrayAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedArrayOffset" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedArrayTypeCoercion" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedFunctionCall" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedInferredReturnType" type="IssueHandlerType" minOccurs="0" />
@@ -237,30 +239,33 @@
<xs:element name="MixedReturnStatement" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedStringOffsetAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedTypeCoercion" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedArgumentTypeCoercion" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="MixedReturnTypeCoercion" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MixedPropertyTypeCoercion" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="MoreSpecificReturnType" type="IssueHandlerType" minOccurs="0" />
<xs:element name="MoreSpecificImplementedParamType" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NoInterfaceProperties" type="ClassIssueHandlerType" minOccurs="0" />
<xs:element name="NonStaticSelfCall" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullableReturnStatement" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="NullArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullArrayOffset" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullFunctionCall" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullIterator" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullOperand" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullPropertyAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullPropertyFetch" type="IssueHandlerType" minOccurs="0" />
<xs:element name="NullPropertyAssignment" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="NullPropertyFetch" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="NullReference" type="IssueHandlerType" minOccurs="0" />
<xs:element name="OverriddenMethodAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="OverriddenPropertyAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="OverriddenPropertyAccess" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="ParadoxicalCondition" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ParentNotFound" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalseArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalseArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalseIterator" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalseOperand" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalsePropertyAssignmentValue" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalsePropertyAssignmentValue" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyFalseReference" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArrayAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidArrayOffset" type="IssueHandlerType" minOccurs="0" />
@@ -271,8 +276,8 @@
<xs:element name="PossiblyInvalidOperand" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidPropertyAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidPropertyAssignmentValue" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidPropertyFetch" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArgument" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyInvalidPropertyFetch" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArgument" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArrayAccess" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArrayAssignment" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyNullArrayOffset" type="IssueHandlerType" minOccurs="0" />
@@ -291,14 +296,15 @@
<xs:element name="PossiblyUnusedParam" type="IssueHandlerType" minOccurs="0" />
<xs:element name="PossiblyUnusedProperty" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="PropertyNotSetInConstructor" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="PropertyTypeCoercion" type="PropertyIssueHandlerType" minOccurs="0" />
<xs:element name="RawObjectIteration" type="IssueHandlerType" minOccurs="0" />
<xs:element name="RedundantCondition" type="IssueHandlerType" minOccurs="0" />
<xs:element name="RedundantConditionGivenDocblockType" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ReferenceConstraintViolation" type="IssueHandlerType" minOccurs="0" />
<xs:element name="ReservedWord" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TraitMethodSignatureMismatch" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TooFewArguments" type="FunctionIssueHandlerType" minOccurs="0" />
<xs:element name="TooManyArguments" type="FunctionIssueHandlerType" minOccurs="0" />
<xs:element name="TooFewArguments" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="TooManyArguments" type="ArgumentIssueHandlerType" minOccurs="0" />
<xs:element name="TooManyTemplateParams" type="FunctionIssueHandlerType" minOccurs="0" />
<xs:element name="TypeCoercion" type="IssueHandlerType" minOccurs="0" />
<xs:element name="TypeDoesNotContainNull" type="IssueHandlerType" minOccurs="0" />
@@ -404,6 +410,24 @@
<xs:attribute name="errorLevel" type="xs:string" />
</xs:complexType>

<xs:complexType name="ArgumentIssueHandlerType">
<xs:sequence>
<xs:element name="errorLevel" minOccurs="0" maxOccurs="unbounded">
<xs:complexType>
<xs:choice maxOccurs="unbounded">
<xs:element name="directory" minOccurs="0" maxOccurs="unbounded" type="NameAttributeType" />
<xs:element name="file" minOccurs="0" maxOccurs="unbounded" type="NameAttributeType" />
<xs:element name="referencedFunction" minOccurs="0" maxOccurs="unbounded" type="NameAttributeType" />
</xs:choice>

<xs:attribute name="type" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>

<xs:attribute name="errorLevel" type="xs:string" />
</xs:complexType>

<xs:complexType name="ClassIssueHandlerType">
<xs:sequence>
<xs:element name="errorLevel" minOccurs="0" maxOccurs="unbounded">
@@ -9,6 +9,20 @@ abstract class A {}
new A();
```

### ArgumentTypeCoercion

Emitted when calling a function with an argument which has a less specific type than the function expects

```php
class A {}
class B extends A {}
function takesA(A $a) : void {
takesB($a);
}
function takesB(B $b) : void {}
```

### AssignmentToVoid

Emitted when assigning from a function that returns `void`:
@@ -1004,6 +1018,19 @@ function takesInt(int $i) : void {}
takesInt($_GET['foo']);
```

### MixedArgumentTypeCoercion

Emitted when Psalm cannot be sure that part of an array/iterabble argument's type constraints can be fulfilled

```php
function foo(array $a) : void {
takesStringArray($a);
}
/** @param string[] $a */
function takesStringArray(array $a) : void {}
```

### MixedArrayAccess

Emitted when trying to access an array offset on a value whose type Psalm cannot determine
@@ -1028,6 +1055,22 @@ Emitted when attempting to access an array offset where Psalm cannot determine t
echo [1, 2, 3][$_GET['foo']];
```

### MixedArrayTypeCoercion

Emitted when trying to access an array with a less specific offset than is expected

```php
/**
* @param array<array-key, int> $a
* @param array<int, string> $b
*/
function foo(array $a, array $b) : void {
foreach ($a as $j => $k) {
echo $b[$j];
}
}
```

### MixedAssignment

Emitted when assigning a variable to a value for which Psalm cannot infer a type
@@ -1097,6 +1140,21 @@ function foo($a) : void {
}
```

### MixedPropertyTypeCoercion

Emitted when Psalm cannot be sure that part of an array/iterabble argument's type constraints can be fulfilled

```php
class A {
/** @var string[] */
public $takesStringArray = [];
}
function foo(A $a, array $arr) : void {
$a->takesStringArray = $arr;
}
```

### MixedReturnStatement

Emitted when Psalm cannot determine the type of a given return statement
@@ -1107,25 +1165,25 @@ function foo() : int {
}
```

### MixedStringOffsetAssignment
### MixedReturnTypeCoercion

Emitted when assigning a value on a string using a value for which Psalm cannot infer a type
Emitted when Psalm cannot be sure that part of an array/iterabble return type's constraints can be fulfilled

```php
"hello"[0] = $_GET['foo'];
/**
* @return string[]
*/
function foo(array $a) : array {
return $a;
}
```

### MixedTypeCoercion
### MixedStringOffsetAssignment

Emitted when Psalm cannot be sure that part of an array/iterabble argument's type constraints can be fulfilled
Emitted when assigning a value on a string using a value for which Psalm cannot infer a type

```php
function foo(array $a) : void {
takesStringArray($a);
}
/** @param string[] $a */
function takesStringArray(array $a) : void {}
"hello"[0] = $_GET['foo'];
```

### MoreSpecificImplementedParamType
@@ -1392,7 +1450,7 @@ foreach ($arr as $a) {}

### PossiblyFalseOperand

Emitted when using a possibly `false` value as part of an operation (e.g. `+`, `.`, `^` etc.`)
Emitted when using a possibly `false` value as part of an operation (e.g. `+`, `.`, `^` etc).

```php
function foo(string $a) : void {
@@ -1524,7 +1582,7 @@ foo()->bar();

### PossiblyInvalidOperand

Emitted when using a possibly invalid value as part of an operation (e.g. `+`, `.`, `^` etc.`)
Emitted when using a possibly invalid value as part of an operation (e.g. `+`, `.`, `^` etc.

```php
function foo() : void {
@@ -1825,6 +1883,24 @@ class A {
}
```

### PropertyTypeCoercion

Emitted when setting a property with an value which has a less specific type than the property expects

```php
class A {}
class B extends A {}
function takesA(C $c, A $a) : void {
$c->b = $a;
}
class C {
/** @var ?B */
public $b;
}
```

### RawObjectIteration

Emitted when iterating over an object’s properties. This issue exists because it may be undesired behaviour (e.g. you may have meant to iterate over an array)
@@ -1941,20 +2017,6 @@ class SomeIterator implements IteratorAggregate
}
```

### TypeCoercion

Emitted when calling a function with an argument which has a less specific type than the function expects

```php
class A {}
class B extends A {}
function takesA(A $a) : void {
takesB($a);
}
function takesB(B $b) : void {}
```

### TypeDoesNotContainNull

Emitted when checking a non-nullable type for `null`
@@ -7,7 +7,7 @@
use Psalm\Context;
use Psalm\FileManipulation;
use Psalm\IssueBuffer;
use Psalm\Issue\TypeCoercion;
use Psalm\Issue\ArgumentTypeCoercion;
use Psalm\Plugin\Hook\AfterStatementAnalysisInterface;
use Psalm\StatementsSource;
@@ -31,9 +31,10 @@ public static function afterStatementAnalysis(
foreach ($stmt->exprs as $expr) {
if (!isset($expr->inferredType) || $expr->inferredType->hasMixed()) {
if (IssueBuffer::accepts(
new TypeCoercion(
new ArgumentTypeCoercion(
'Echo requires an unescaped string, ' . $expr->inferredType . ' provided',
new CodeLocation($statements_source, $expr)
new CodeLocation($statements_source, $expr),
'echo'
),
$statements_source->getSuppressedIssues()
)) {
@@ -51,9 +52,10 @@ public static function afterStatementAnalysis(
&& !$type instanceof \Psalm\Type\Atomic\THtmlEscapedString
) {
if (IssueBuffer::accepts(
new TypeCoercion(
new ArgumentTypeCoercion(
'Echo requires an unescaped string, ' . $expr->inferredType . ' provided',
new CodeLocation($statements_source, $expr)
new CodeLocation($statements_source, $expr),
'echo'
),
$statements_source->getSuppressedIssues()
)) {

0 comments on commit 0e4c8ce

Please sign in to comment.
You can’t perform that action at this time.