Skip to content

Commit 04020f9

Browse files
committed
Add an ArrayAccessMode enum to better analyse offset accesses
1 parent 7b4c9af commit 04020f9

40 files changed

+113
-72
lines changed

src/Rules/Arrays/NonexistentOffsetInArrayDimFetchRule.php

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
use PHPStan\Rules\Rule;
1010
use PHPStan\Rules\RuleErrorBuilder;
1111
use PHPStan\Rules\RuleLevelHelper;
12+
use PHPStan\Type\AccessOffsetMode;
1213
use PHPStan\Type\ErrorType;
1314
use PHPStan\Type\Type;
1415
use PHPStan\Type\VerbosityLevel;
@@ -62,10 +63,20 @@ public function processNode(Node $node, Scope $scope): array
6263
$isOffsetAccessible = $isOffsetAccessibleType->isOffsetAccessible();
6364

6465
if ($scope->isInExpressionAssign($node) && $isOffsetAccessible->yes()) {
65-
return [];
66+
if ($isOffsetAccessibleType->isOffsetAccessLegal(AccessOffsetMode::Write)->yes()) {
67+
return [];
68+
} else {
69+
// TODO Improve
70+
return [
71+
RuleErrorBuilder::message(sprintf(
72+
'Cannot access an offset on %s.',
73+
$isOffsetAccessibleType->describe(VerbosityLevel::typeOnly()),
74+
))->identifier('offsetAccess.nonOffsetAccessible')->build(),
75+
];
76+
}
6677
}
6778

68-
if ($scope->isUndefinedExpressionAllowed($node) && $isOffsetAccessibleType->isOffsetAccessLegal()->yes()) {
79+
if ($scope->isUndefinedExpressionAllowed($node) && $isOffsetAccessibleType->isOffsetAccessLegal(AccessOffsetMode::Read)->yes()) {
6980
return [];
7081
}
7182

src/Type/AccessOffsetMode.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace PHPStan\Type;
4+
5+
enum AccessOffsetMode
6+
{
7+
case Exist;
8+
case Read;
9+
case Write;
10+
case ReadWrite;
11+
case Unset;
12+
case Append;
13+
case IncrementDecrement;
14+
case Fetch; // By reference fetch
15+
}

src/Type/Accessory/AccessoryArrayListType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BooleanType;
1112
use PHPStan\Type\CompoundType;
1213
use PHPStan\Type\Constant\ConstantFloatType;
@@ -130,7 +131,7 @@ public function isOffsetAccessible(): TrinaryLogic
130131
return TrinaryLogic::createYes();
131132
}
132133

133-
public function isOffsetAccessLegal(): TrinaryLogic
134+
public function isOffsetAccessLegal(AccessOffsetMode $mode): TrinaryLogic
134135
{
135136
return TrinaryLogic::createYes();
136137
}

src/Type/Accessory/AccessoryLiteralStringType.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BenevolentUnionType;
1112
use PHPStan\Type\BooleanType;
1213
use PHPStan\Type\CompoundType;
@@ -27,6 +28,7 @@
2728
use PHPStan\Type\Traits\NonIterableTypeTrait;
2829
use PHPStan\Type\Traits\NonObjectTypeTrait;
2930
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
31+
use PHPStan\Type\Traits\StringTypeTrait;
3032
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3133
use PHPStan\Type\Type;
3234
use PHPStan\Type\UnionType;
@@ -42,6 +44,7 @@ class AccessoryLiteralStringType implements CompoundType, AccessoryType
4244
use UndecidedComparisonCompoundTypeTrait;
4345
use NonGenericTypeTrait;
4446
use NonRemoveableTypeTrait;
47+
use StringTypeTrait;
4548

4649
/** @api */
4750
public function __construct()
@@ -123,11 +126,6 @@ public function isOffsetAccessible(): TrinaryLogic
123126
return TrinaryLogic::createYes();
124127
}
125128

126-
public function isOffsetAccessLegal(): TrinaryLogic
127-
{
128-
return TrinaryLogic::createYes();
129-
}
130-
131129
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
132130
{
133131
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());

src/Type/Accessory/AccessoryLowercaseStringType.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BenevolentUnionType;
1112
use PHPStan\Type\BooleanType;
1213
use PHPStan\Type\CompoundType;
@@ -26,6 +27,7 @@
2627
use PHPStan\Type\Traits\NonIterableTypeTrait;
2728
use PHPStan\Type\Traits\NonObjectTypeTrait;
2829
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
30+
use PHPStan\Type\Traits\StringTypeTrait;
2931
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3032
use PHPStan\Type\Type;
3133
use PHPStan\Type\UnionType;
@@ -41,6 +43,7 @@ class AccessoryLowercaseStringType implements CompoundType, AccessoryType
4143
use UndecidedComparisonCompoundTypeTrait;
4244
use NonGenericTypeTrait;
4345
use NonRemoveableTypeTrait;
46+
use StringTypeTrait;
4447

4548
/** @api */
4649
public function __construct()
@@ -119,11 +122,6 @@ public function isOffsetAccessible(): TrinaryLogic
119122
return TrinaryLogic::createYes();
120123
}
121124

122-
public function isOffsetAccessLegal(): TrinaryLogic
123-
{
124-
return TrinaryLogic::createYes();
125-
}
126-
127125
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
128126
{
129127
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());

src/Type/Accessory/AccessoryNonEmptyStringType.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BenevolentUnionType;
1112
use PHPStan\Type\BooleanType;
1213
use PHPStan\Type\CompoundType;
@@ -26,6 +27,7 @@
2627
use PHPStan\Type\Traits\NonGenericTypeTrait;
2728
use PHPStan\Type\Traits\NonIterableTypeTrait;
2829
use PHPStan\Type\Traits\NonObjectTypeTrait;
30+
use PHPStan\Type\Traits\StringTypeTrait;
2931
use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
3032
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3133
use PHPStan\Type\Type;
@@ -43,6 +45,7 @@ class AccessoryNonEmptyStringType implements CompoundType, AccessoryType
4345
use UndecidedComparisonCompoundTypeTrait;
4446
use NonGenericTypeTrait;
4547
use UndecidedBooleanTypeTrait;
48+
use StringTypeTrait;
4649

4750
/** @api */
4851
public function __construct()
@@ -125,11 +128,6 @@ public function isOffsetAccessible(): TrinaryLogic
125128
return TrinaryLogic::createYes();
126129
}
127130

128-
public function isOffsetAccessLegal(): TrinaryLogic
129-
{
130-
return TrinaryLogic::createYes();
131-
}
132-
133131
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
134132
{
135133
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());

src/Type/Accessory/AccessoryNonFalsyStringType.php

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BenevolentUnionType;
1112
use PHPStan\Type\BooleanType;
1213
use PHPStan\Type\CompoundType;
@@ -26,6 +27,7 @@
2627
use PHPStan\Type\Traits\NonIterableTypeTrait;
2728
use PHPStan\Type\Traits\NonObjectTypeTrait;
2829
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
30+
use PHPStan\Type\Traits\StringTypeTrait;
2931
use PHPStan\Type\Traits\TruthyBooleanTypeTrait;
3032
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3133
use PHPStan\Type\Type;
@@ -43,6 +45,7 @@ class AccessoryNonFalsyStringType implements CompoundType, AccessoryType
4345
use UndecidedComparisonCompoundTypeTrait;
4446
use NonGenericTypeTrait;
4547
use NonRemoveableTypeTrait;
48+
use StringTypeTrait;
4649

4750
/** @api */
4851
public function __construct()
@@ -124,12 +127,6 @@ public function isOffsetAccessible(): TrinaryLogic
124127
{
125128
return TrinaryLogic::createYes();
126129
}
127-
128-
public function isOffsetAccessLegal(): TrinaryLogic
129-
{
130-
return TrinaryLogic::createYes();
131-
}
132-
133130
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
134131
{
135132
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());

src/Type/Accessory/AccessoryNumericStringType.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BenevolentUnionType;
1112
use PHPStan\Type\BooleanType;
1213
use PHPStan\Type\CompoundType;
@@ -25,6 +26,7 @@
2526
use PHPStan\Type\Traits\NonGenericTypeTrait;
2627
use PHPStan\Type\Traits\NonIterableTypeTrait;
2728
use PHPStan\Type\Traits\NonObjectTypeTrait;
29+
use PHPStan\Type\Traits\StringTypeTrait;
2830
use PHPStan\Type\Traits\UndecidedBooleanTypeTrait;
2931
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3032
use PHPStan\Type\Type;
@@ -42,6 +44,7 @@ class AccessoryNumericStringType implements CompoundType, AccessoryType
4244
use UndecidedBooleanTypeTrait;
4345
use UndecidedComparisonCompoundTypeTrait;
4446
use NonGenericTypeTrait;
47+
use StringTypeTrait;
4548

4649
/** @api */
4750
public function __construct()
@@ -128,11 +131,6 @@ public function isOffsetAccessible(): TrinaryLogic
128131
return TrinaryLogic::createYes();
129132
}
130133

131-
public function isOffsetAccessLegal(): TrinaryLogic
132-
{
133-
return TrinaryLogic::createYes();
134-
}
135-
136134
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
137135
{
138136
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());

src/Type/Accessory/AccessoryUppercaseStringType.php

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
use PHPStan\PhpDocParser\Ast\Type\TypeNode;
88
use PHPStan\TrinaryLogic;
99
use PHPStan\Type\AcceptsResult;
10+
use PHPStan\Type\AccessOffsetMode;
1011
use PHPStan\Type\BenevolentUnionType;
1112
use PHPStan\Type\BooleanType;
1213
use PHPStan\Type\CompoundType;
@@ -26,6 +27,7 @@
2627
use PHPStan\Type\Traits\NonIterableTypeTrait;
2728
use PHPStan\Type\Traits\NonObjectTypeTrait;
2829
use PHPStan\Type\Traits\NonRemoveableTypeTrait;
30+
use PHPStan\Type\Traits\StringTypeTrait;
2931
use PHPStan\Type\Traits\UndecidedComparisonCompoundTypeTrait;
3032
use PHPStan\Type\Type;
3133
use PHPStan\Type\UnionType;
@@ -41,6 +43,7 @@ class AccessoryUppercaseStringType implements CompoundType, AccessoryType
4143
use UndecidedComparisonCompoundTypeTrait;
4244
use NonGenericTypeTrait;
4345
use NonRemoveableTypeTrait;
46+
use StringTypeTrait;
4447

4548
/** @api */
4649
public function __construct()
@@ -119,11 +122,6 @@ public function isOffsetAccessible(): TrinaryLogic
119122
return TrinaryLogic::createYes();
120123
}
121124

122-
public function isOffsetAccessLegal(): TrinaryLogic
123-
{
124-
return TrinaryLogic::createYes();
125-
}
126-
127125
public function hasOffsetValueType(Type $offsetType): TrinaryLogic
128126
{
129127
return $offsetType->isInteger()->and(TrinaryLogic::createMaybe());

src/Type/Accessory/HasMethodType.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use PHPStan\Reflection\Type\UnresolvedMethodPrototypeReflection;
1313
use PHPStan\TrinaryLogic;
1414
use PHPStan\Type\AcceptsResult;
15+
use PHPStan\Type\AccessOffsetMode;
1516
use PHPStan\Type\CompoundType;
1617
use PHPStan\Type\ErrorType;
1718
use PHPStan\Type\IntersectionType;
@@ -111,7 +112,7 @@ public function describe(VerbosityLevel $level): string
111112
return sprintf('hasMethod(%s)', $this->methodName);
112113
}
113114

114-
public function isOffsetAccessLegal(): TrinaryLogic
115+
public function isOffsetAccessLegal(AccessOffsetMode $mode): TrinaryLogic
115116
{
116117
return TrinaryLogic::createMaybe();
117118
}

0 commit comments

Comments
 (0)