| 1: | <?php declare(strict_types = 1); |
| 2: | |
| 3: | namespace PHPStan\Rules; |
| 4: | |
| 5: | use PHPStan\Reflection\ClassConstantReflection; |
| 6: | use PHPStan\Reflection\ExtendedMethodReflection; |
| 7: | use PHPStan\Reflection\ExtendedPropertyReflection; |
| 8: | use PHPStan\Reflection\FunctionReflection; |
| 9: | use function sprintf; |
| 10: | use function ucfirst; |
| 11: | |
| 12: | |
| 13: | |
| 14: | |
| 15: | final class ClassNameUsageLocation |
| 16: | { |
| 17: | |
| 18: | public const TRAIT_USE = 'traitUse'; |
| 19: | public const STATIC_PROPERTY_ACCESS = 'staticProperty'; |
| 20: | public const PHPDOC_TAG_ASSERT = 'assert'; |
| 21: | public const ATTRIBUTE = 'attribute'; |
| 22: | public const EXCEPTION_CATCH = 'catch'; |
| 23: | public const CLASS_CONSTANT_ACCESS = 'classConstant'; |
| 24: | public const CLASS_IMPLEMENTS = 'classImplements'; |
| 25: | public const ENUM_IMPLEMENTS = 'enumImplements'; |
| 26: | public const INTERFACE_EXTENDS = 'interfaceExtends'; |
| 27: | public const CLASS_EXTENDS = 'classExtends'; |
| 28: | public const INSTANCEOF = 'instanceof'; |
| 29: | public const PROPERTY_TYPE = 'property'; |
| 30: | public const PARAMETER_TYPE = 'parameter'; |
| 31: | public const RETURN_TYPE = 'return'; |
| 32: | public const PHPDOC_TAG_SELF_OUT = 'selfOut'; |
| 33: | public const PHPDOC_TAG_VAR = 'varTag'; |
| 34: | public const INSTANTIATION = 'new'; |
| 35: | public const TYPE_ALIAS = 'typeAlias'; |
| 36: | public const PHPDOC_TAG_METHOD = 'methodTag'; |
| 37: | public const PHPDOC_TAG_MIXIN = 'mixin'; |
| 38: | public const PHPDOC_TAG_PROPERTY = 'propertyTag'; |
| 39: | public const PHPDOC_TAG_REQUIRE_EXTENDS = 'requireExtends'; |
| 40: | public const PHPDOC_TAG_REQUIRE_IMPLEMENTS = 'requireImplements'; |
| 41: | public const PHPDOC_TAG_SEALED = 'sealed'; |
| 42: | public const STATIC_METHOD_CALL = 'staticMethod'; |
| 43: | public const PHPDOC_TAG_TEMPLATE_BOUND = 'templateBound'; |
| 44: | public const PHPDOC_TAG_TEMPLATE_DEFAULT = 'templateDefault'; |
| 45: | |
| 46: | |
| 47: | |
| 48: | |
| 49: | |
| 50: | private function __construct(public readonly string $value, public readonly array $data) |
| 51: | { |
| 52: | } |
| 53: | |
| 54: | |
| 55: | |
| 56: | |
| 57: | |
| 58: | public static function from(string $value, array $data = []): self |
| 59: | { |
| 60: | return new self($value, $data); |
| 61: | } |
| 62: | |
| 63: | public function getMethod(): ?ExtendedMethodReflection |
| 64: | { |
| 65: | return $this->data['method'] ?? null; |
| 66: | } |
| 67: | |
| 68: | public function getProperty(): ?ExtendedPropertyReflection |
| 69: | { |
| 70: | return $this->data['property'] ?? null; |
| 71: | } |
| 72: | |
| 73: | public function getFunction(): ?FunctionReflection |
| 74: | { |
| 75: | return $this->data['function'] ?? null; |
| 76: | } |
| 77: | |
| 78: | public function getPhpDocTagName(): ?string |
| 79: | { |
| 80: | return $this->data['phpDocTagName'] ?? null; |
| 81: | } |
| 82: | |
| 83: | public function getAssertedExprString(): ?string |
| 84: | { |
| 85: | return $this->data['assertedExprString'] ?? null; |
| 86: | } |
| 87: | |
| 88: | public function getClassConstant(): ?ClassConstantReflection |
| 89: | { |
| 90: | return $this->data['classConstant'] ?? null; |
| 91: | } |
| 92: | |
| 93: | public function getCurrentClassName(): ?string |
| 94: | { |
| 95: | return $this->data['currentClassName'] ?? null; |
| 96: | } |
| 97: | |
| 98: | public function getParameterName(): ?string |
| 99: | { |
| 100: | return $this->data['parameterName'] ?? null; |
| 101: | } |
| 102: | |
| 103: | public function getTypeAliasName(): ?string |
| 104: | { |
| 105: | return $this->data['typeAliasName'] ?? null; |
| 106: | } |
| 107: | |
| 108: | public function getMethodTagName(): ?string |
| 109: | { |
| 110: | return $this->data['methodTagName'] ?? null; |
| 111: | } |
| 112: | |
| 113: | public function getPropertyTagName(): ?string |
| 114: | { |
| 115: | return $this->data['propertyTagName'] ?? null; |
| 116: | } |
| 117: | |
| 118: | public function getTemplateTagName(): ?string |
| 119: | { |
| 120: | return $this->data['templateTagName'] ?? null; |
| 121: | } |
| 122: | |
| 123: | public function isInAnomyousFunction(): bool |
| 124: | { |
| 125: | return $this->data['isInAnonymousFunction'] ?? false; |
| 126: | } |
| 127: | |
| 128: | public function createMessage(string $part): string |
| 129: | { |
| 130: | switch ($this->value) { |
| 131: | case self::TRAIT_USE: |
| 132: | if ($this->getCurrentClassName() !== null) { |
| 133: | return sprintf('Usage of %s in class %s.', $part, $this->getCurrentClassName()); |
| 134: | } |
| 135: | return sprintf('Usage of %s.', $part); |
| 136: | case self::STATIC_PROPERTY_ACCESS: |
| 137: | $property = $this->getProperty(); |
| 138: | if ($property !== null) { |
| 139: | return sprintf('Access to static property $%s on %s.', $property->getName(), $part); |
| 140: | } |
| 141: | |
| 142: | return sprintf('Access to static property on %s.', $part); |
| 143: | case self::PHPDOC_TAG_ASSERT: |
| 144: | $phpDocTagName = $this->getPhpDocTagName(); |
| 145: | $assertExprString = $this->getAssertedExprString(); |
| 146: | if ($phpDocTagName !== null && $assertExprString !== null) { |
| 147: | return sprintf('PHPDoc tag %s for %s references %s.', $phpDocTagName, $assertExprString, $part); |
| 148: | } |
| 149: | |
| 150: | return sprintf('Assert tag references %s.', $part); |
| 151: | case self::ATTRIBUTE: |
| 152: | return sprintf('Attribute references %s.', $part); |
| 153: | case self::EXCEPTION_CATCH: |
| 154: | return sprintf('Catching %s.', $part); |
| 155: | case self::CLASS_CONSTANT_ACCESS: |
| 156: | if ($this->getClassConstant() !== null) { |
| 157: | return sprintf('Access to constant %s on %s.', $this->getClassConstant()->getName(), $part); |
| 158: | } |
| 159: | return sprintf('Access to constant on %s.', $part); |
| 160: | case self::CLASS_IMPLEMENTS: |
| 161: | if ($this->getCurrentClassName() !== null) { |
| 162: | return sprintf('Class %s implements %s.', $this->getCurrentClassName(), $part); |
| 163: | } |
| 164: | |
| 165: | return sprintf('Anonymous class implements %s.', $part); |
| 166: | case self::ENUM_IMPLEMENTS: |
| 167: | if ($this->getCurrentClassName() !== null) { |
| 168: | return sprintf('Enum %s implements %s.', $this->getCurrentClassName(), $part); |
| 169: | } |
| 170: | |
| 171: | return sprintf('Enum implements %s.', $part); |
| 172: | case self::INTERFACE_EXTENDS: |
| 173: | if ($this->getCurrentClassName() !== null) { |
| 174: | return sprintf('Interface %s extends %s.', $this->getCurrentClassName(), $part); |
| 175: | } |
| 176: | |
| 177: | return sprintf('Interface extends %s.', $part); |
| 178: | case self::CLASS_EXTENDS: |
| 179: | if ($this->getCurrentClassName() !== null) { |
| 180: | return sprintf('Class %s extends %s.', $this->getCurrentClassName(), $part); |
| 181: | } |
| 182: | |
| 183: | return sprintf('Anonymous class extends %s.', $part); |
| 184: | case self::INSTANCEOF: |
| 185: | return sprintf('Instanceof references %s.', $part); |
| 186: | case self::PROPERTY_TYPE: |
| 187: | $property = $this->getProperty(); |
| 188: | if ($property !== null) { |
| 189: | return sprintf('Property $%s references %s in its type.', $property->getName(), $part); |
| 190: | } |
| 191: | return sprintf('Property references %s in its type.', $part); |
| 192: | case self::PARAMETER_TYPE: |
| 193: | $parameterName = $this->getParameterName(); |
| 194: | if ($parameterName !== null) { |
| 195: | if ($this->isInAnomyousFunction()) { |
| 196: | return sprintf('Parameter $%s of anonymous function has typehint with %s.', $parameterName, $part); |
| 197: | } |
| 198: | if ($this->getMethod() !== null) { |
| 199: | if ($this->getCurrentClassName() !== null) { |
| 200: | return sprintf('Parameter $%s of method %s::%s() has typehint with %s.', $parameterName, $this->getCurrentClassName(), $this->getMethod()->getName(), $part); |
| 201: | } |
| 202: | |
| 203: | return sprintf('Parameter $%s of method %s() in anonymous class has typehint with %s.', $parameterName, $this->getMethod()->getName(), $part); |
| 204: | } |
| 205: | |
| 206: | if ($this->getFunction() !== null) { |
| 207: | return sprintf('Parameter $%s of function %s() has typehint with %s.', $parameterName, $this->getFunction()->getName(), $part); |
| 208: | } |
| 209: | |
| 210: | return sprintf('Parameter $%s has typehint with %s.', $parameterName, $part); |
| 211: | } |
| 212: | |
| 213: | return sprintf('Parameter has typehint with %s.', $part); |
| 214: | case self::RETURN_TYPE: |
| 215: | if ($this->isInAnomyousFunction()) { |
| 216: | return sprintf('Return type of anonymous function has typehint with %s.', $part); |
| 217: | } |
| 218: | if ($this->getMethod() !== null) { |
| 219: | if ($this->getCurrentClassName() !== null) { |
| 220: | return sprintf('Return type of method %s::%s() has typehint with %s.', $this->getCurrentClassName(), $this->getMethod()->getName(), $part); |
| 221: | } |
| 222: | |
| 223: | return sprintf('Return type of method %s() in anonymous class has typehint with %s.', $this->getMethod()->getName(), $part); |
| 224: | } |
| 225: | |
| 226: | if ($this->getFunction() !== null) { |
| 227: | return sprintf('Return type of function %s() has typehint with %s.', $this->getFunction()->getName(), $part); |
| 228: | } |
| 229: | |
| 230: | return sprintf('Return type has typehint with %s.', $part); |
| 231: | case self::PHPDOC_TAG_SELF_OUT: |
| 232: | return sprintf('PHPDoc tag @phpstan-self-out references %s.', $part); |
| 233: | case self::PHPDOC_TAG_VAR: |
| 234: | return sprintf('PHPDoc tag @var references %s.', $part); |
| 235: | case self::INSTANTIATION: |
| 236: | return sprintf('Instantiation of %s.', $part); |
| 237: | case self::TYPE_ALIAS: |
| 238: | if ($this->getTypeAliasName() !== null) { |
| 239: | return sprintf('Type alias %s references %s.', $this->getTypeAliasName(), $part); |
| 240: | } |
| 241: | |
| 242: | return sprintf('Type alias references %s.', $part); |
| 243: | case self::PHPDOC_TAG_METHOD: |
| 244: | if ($this->getMethodTagName() !== null) { |
| 245: | return sprintf('PHPDoc tag @method for %s() references %s.', $this->getMethodTagName(), $part); |
| 246: | } |
| 247: | return sprintf('PHPDoc tag @method references %s.', $part); |
| 248: | case self::PHPDOC_TAG_MIXIN: |
| 249: | return sprintf('PHPDoc tag @mixin references %s.', $part); |
| 250: | case self::PHPDOC_TAG_PROPERTY: |
| 251: | if ($this->getPropertyTagName() !== null) { |
| 252: | return sprintf('PHPDoc tag @property for $%s references %s.', $this->getPropertyTagName(), $part); |
| 253: | } |
| 254: | return sprintf('PHPDoc tag @property references %s.', $part); |
| 255: | case self::PHPDOC_TAG_REQUIRE_EXTENDS: |
| 256: | return sprintf('PHPDoc tag @phpstan-require-extends references %s.', $part); |
| 257: | case self::PHPDOC_TAG_REQUIRE_IMPLEMENTS: |
| 258: | return sprintf('PHPDoc tag @phpstan-require-implements references %s.', $part); |
| 259: | case self::PHPDOC_TAG_SEALED: |
| 260: | return sprintf('PHPDoc tag @phpstan-sealed references %s.', $part); |
| 261: | case self::STATIC_METHOD_CALL: |
| 262: | $method = $this->getMethod(); |
| 263: | if ($method !== null) { |
| 264: | return sprintf('Call to static method %s() on %s.', $method->getName(), $part); |
| 265: | } |
| 266: | |
| 267: | return sprintf('Call to static method on %s.', $part); |
| 268: | case self::PHPDOC_TAG_TEMPLATE_BOUND: |
| 269: | if ($this->getTemplateTagName() !== null) { |
| 270: | return sprintf('PHPDoc tag @template %s bound references %s.', $this->getTemplateTagName(), $part); |
| 271: | } |
| 272: | |
| 273: | return sprintf('PHPDoc tag @template bound references %s.', $part); |
| 274: | case self::PHPDOC_TAG_TEMPLATE_DEFAULT: |
| 275: | if ($this->getTemplateTagName() !== null) { |
| 276: | return sprintf('PHPDoc tag @template %s default references %s.', $this->getTemplateTagName(), $part); |
| 277: | } |
| 278: | |
| 279: | return sprintf('PHPDoc tag @template default references %s.', $part); |
| 280: | } |
| 281: | } |
| 282: | |
| 283: | public function createIdentifier(string $secondPart): string |
| 284: | { |
| 285: | if ($this->value === self::CLASS_IMPLEMENTS) { |
| 286: | return sprintf('class.implements%s', ucfirst($secondPart)); |
| 287: | } |
| 288: | if ($this->value === self::ENUM_IMPLEMENTS) { |
| 289: | return sprintf('enum.implements%s', ucfirst($secondPart)); |
| 290: | } |
| 291: | if ($this->value === self::INTERFACE_EXTENDS) { |
| 292: | return sprintf('interface.extends%s', ucfirst($secondPart)); |
| 293: | } |
| 294: | if ($this->value === self::CLASS_EXTENDS) { |
| 295: | return sprintf('class.extends%s', ucfirst($secondPart)); |
| 296: | } |
| 297: | if ($this->value === self::PHPDOC_TAG_TEMPLATE_BOUND) { |
| 298: | return sprintf('generics.%sBound', $secondPart); |
| 299: | } |
| 300: | if ($this->value === self::PHPDOC_TAG_TEMPLATE_DEFAULT) { |
| 301: | return sprintf('generics.%sDefault', $secondPart); |
| 302: | } |
| 303: | |
| 304: | return sprintf('%s.%s', $this->value, $secondPart); |
| 305: | } |
| 306: | |
| 307: | } |
| 308: | |