Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nextcloud/updater.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/vimeo/psalm/src/Psalm/Internal')
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Algebra.php630
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Algebra/FormulaGenerator.php457
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AlgebraAnalyzer.php174
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AttributesAnalyzer.php394
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CanAlias.php162
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php2638
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php648
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php40
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php343
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CommentAnalyzer.php391
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/DataFlowNodeData.php90
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php682
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php124
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php978
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php384
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php2023
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php147
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/IssueData.php175
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodAnalyzer.php290
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodComparator.php1080
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php280
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php1527
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php529
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/SourceAnalyzer.php182
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php210
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php190
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php1149
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php377
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php242
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php439
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php532
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php509
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php735
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php234
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php769
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php516
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/WhileAnalyzer.php139
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php95
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php100
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php135
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php607
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php55
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php4122
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php1027
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php33
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php1540
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php342
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php1728
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php232
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php1373
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php89
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php432
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php134
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php411
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php530
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php124
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php46
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php1629
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php142
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php1620
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php957
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php282
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php1129
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php87
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php861
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php89
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php890
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php673
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php70
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php173
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php634
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php196
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php454
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php437
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php586
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php866
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php382
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php1136
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php642
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php1171
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php538
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php148
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php52
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php142
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php85
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php145
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php224
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php2060
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php1217
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php679
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php309
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php482
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php486
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php559
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php138
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php423
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php95
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php49
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php107
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php351
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php98
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php109
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php760
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php306
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php134
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php219
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php82
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php514
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php83
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php658
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php188
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php93
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php138
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php357
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php1076
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TraitAnalyzer.php76
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php118
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Clause.php295
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php323
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Cli/Plugin.php46
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php1176
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php578
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Cli/Refactor.php330
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php665
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php1644
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php61
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php2331
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php363
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/DataFlowGraph.php159
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Functions.php603
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/InternalCallMapHandler.php437
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php1255
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php1224
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Properties.php356
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/PropertyMap.php46
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php61
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Reflection.php548
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php811
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/TaintFlowGraph.php524
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Codebase/VariableUseGraph.php224
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Composer.php43
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/DataFlowNode.php137
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/Path.php33
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSink.php7
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSource.php7
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Diff/AstDiffer.php129
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Diff/ClassStatementsDiffer.php251
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Diff/DiffElem.php37
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileDiffer.php319
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileStatementsDiffer.php172
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php144
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/ErrorHandler.php90
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php621
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php321
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php142
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/SystemCommandExecutor.php42
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/ClassDocblockManipulator.php141
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/CodeMigration.php38
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php245
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php579
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php281
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkMessage.php7
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php20
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php17
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php20
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php492
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php68
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/IncludeCollector.php55
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Json/Json.php46
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php79
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php111
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterInterface.php71
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterTrait.php157
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/IdGenerator.php25
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php64
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php575
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php73
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolReader.php15
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php148
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php35
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolWriter.php18
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php410
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php78
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/MethodIdentifier.php70
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpTraverser/CustomTraverser.php174
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php117
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php80
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CloningVisitor.php40
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php39
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCleanerVisitor.php28
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCounterVisitor.php21
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php70
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php122
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php409
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php80
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php562
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php1861
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php450
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php332
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php697
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php1454
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php1151
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php111
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php650
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php35
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php252
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TraitFinder.php84
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php41
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/DisableCommand.php86
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/EnableCommand.php86
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/ShowCommand.php98
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ComposerLock.php111
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ConfigFile.php152
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginList.php120
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginListFactory.php76
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php115
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php182
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php138
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php75
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php163
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php1007
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceProvider.php1320
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageCacheProvider.php184
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageProvider.php131
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionExistenceProvider.php117
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionParamsProvider.php139
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php205
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodExistenceProvider.php126
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodParamsProvider.php148
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php170
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodVisibilityProvider.php133
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/NodeDataProvider.php133
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ParserCacheProvider.php399
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ProjectCacheProvider.php141
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyExistenceProvider.php138
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider.php140
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php33
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php138
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/Providers.php66
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php55
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php142
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php111
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php331
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php499
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php287
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php68
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php105
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php96
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php76
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php298
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php68
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php98
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php76
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php80
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php88
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php70
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php42
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php102
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php170
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php47
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php41
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php131
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php30
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php88
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php84
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php129
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php143
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php51
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php163
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php115
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php111
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php70
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php31
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php64
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php72
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php74
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php82
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsProvider.php538
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsVolatileCache.php108
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/ReferenceConstraint.php36
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/RuntimeCaches.php44
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php147
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/DocblockParser.php300
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FileScanner.php117
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FunctionDocblockComment.php228
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ParsedDocblock.php95
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php398
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php23
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php19
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php20
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php23
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php23
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php23
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php20
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php23
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php28
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php10
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scanner/VarDocblockComment.php86
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scope/CaseScope.php32
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scope/FinallyScope.php24
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfConditionalScope.php50
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfScope.php87
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scope/LoopScope.php74
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Scope/SwitchScope.php53
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php314
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php480
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ArrayType.php63
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/AssertionReconciler.php1816
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php293
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php712
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php524
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php92
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php207
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php180
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php166
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php311
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php640
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php43
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php499
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php425
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree.php43
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableParamTree.php21
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableTree.php27
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableWithReturnTypeTree.php12
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/ConditionalTree.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/EncapsulationTree.php16
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/GenericTree.php27
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IndexedAccessTree.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IntersectionTree.php12
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayPropertyTree.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayTree.php27
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodParamTree.php43
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodTree.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodWithReturnTypeTree.php12
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/NullableTree.php12
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Root.php12
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateAsTree.php28
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateIsTree.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/UnionTree.php12
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Value.php48
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php842
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php2554
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php1819
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateBound.php52
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php416
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateResult.php73
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php1249
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias.php7
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php22
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php24
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php35
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombination.php99
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php1528
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php909
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php1336
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php511
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php63
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php45
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php31
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php50
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeChecker.php410
-rw-r--r--vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeScanner.php89
371 files changed, 0 insertions, 124101 deletions
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Algebra.php b/vendor/vimeo/psalm/src/Psalm/Internal/Algebra.php
deleted file mode 100644
index 7b59a0a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Algebra.php
+++ /dev/null
@@ -1,630 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use Psalm\Exception\ComplicatedExpressionException;
-use UnexpectedValueException;
-
-use function array_diff_key;
-use function array_filter;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_pop;
-use function array_unique;
-use function array_values;
-use function count;
-use function in_array;
-use function mt_rand;
-use function substr;
-
-class Algebra
-{
- /**
- * @param array<string, non-empty-list<non-empty-list<string>>> $all_types
- *
- * @return array<string, non-empty-list<non-empty-list<string>>>
- *
- * @psalm-pure
- */
- public static function negateTypes(array $all_types): array
- {
- return array_filter(
- array_map(
- /**
- * @param non-empty-list<non-empty-list<string>> $anded_types
- *
- * @return list<non-empty-list<string>>
- */
- function (array $anded_types): array {
- if (count($anded_types) > 1) {
- $new_anded_types = [];
-
- foreach ($anded_types as $orred_types) {
- if (count($orred_types) > 1) {
- return [];
- }
-
- $new_anded_types[] = self::negateType($orred_types[0]);
- }
-
- return [$new_anded_types];
- }
-
- $new_orred_types = [];
-
- foreach ($anded_types[0] as $orred_type) {
- $new_orred_types[] = [self::negateType($orred_type)];
- }
-
- return $new_orred_types;
- },
- $all_types
- )
- );
- }
-
- /**
- * @psalm-pure
- */
- public static function negateType(string $type): string
- {
- if ($type === 'mixed') {
- return $type;
- }
-
- return $type[0] === '!' ? substr($type, 1) : '!' . $type;
- }
-
- /**
- * This is a very simple simplification heuristic
- * for CNF formulae.
- *
- * It simplifies formulae:
- * ($a) && ($a || $b) => $a
- * (!$a) && (!$b) && ($a || $b || $c) => $c
- *
- * @param list<Clause> $clauses
- *
- * @return list<Clause>
- *
- * @psalm-pure
- */
- public static function simplifyCNF(array $clauses): array
- {
- $clause_count = count($clauses);
-
- //65536 seems to be a significant threshold, when put at 65537, the code https://psalm.dev/r/216f362ea6 goes
- //from seconds in analysis to many minutes
- if ($clause_count > 65536) {
- return [];
- }
-
- if ($clause_count > 50) {
- $all_has_unknown = true;
-
- foreach ($clauses as $clause) {
- $clause_has_unknown = false;
- foreach ($clause->possibilities as $key => $_) {
- if ($key[0] === '*') {
- $clause_has_unknown = true;
- break;
- }
- }
-
- if (!$clause_has_unknown) {
- $all_has_unknown = false;
- break;
- }
- }
-
- if ($all_has_unknown) {
- return $clauses;
- }
- }
-
- $cloned_clauses = [];
-
- // avoid strict duplicates
- foreach ($clauses as $clause) {
- $unique_clause = $clause->makeUnique();
- $cloned_clauses[$unique_clause->hash] = $unique_clause;
- }
-
- // remove impossible types
- foreach ($cloned_clauses as $clause_a_hash => $clause_a) {
- if (!$clause_a->reconcilable || $clause_a->wedge) {
- continue;
- }
- $clause_a_keys = array_keys($clause_a->possibilities);
-
- if (count($clause_a->possibilities) !== 1 || count(array_values($clause_a->possibilities)[0]) !== 1) {
- foreach ($cloned_clauses as $clause_b) {
- if ($clause_a === $clause_b || !$clause_b->reconcilable || $clause_b->wedge) {
- continue;
- }
-
- if ($clause_a_keys === array_keys($clause_b->possibilities)) {
- $opposing_keys = [];
-
- foreach ($clause_a->possibilities as $key => $a_possibilities) {
- $b_possibilities = $clause_b->possibilities[$key];
-
- if ($a_possibilities === $b_possibilities) {
- continue;
- }
-
- if (count($a_possibilities) === 1 && count($b_possibilities) === 1) {
- if ($a_possibilities[0] === '!' . $b_possibilities[0]
- || $b_possibilities[0] === '!' . $a_possibilities[0]
- ) {
- $opposing_keys[] = $key;
- continue;
- }
- }
-
- continue 2;
- }
-
- if (count($opposing_keys) === 1) {
- unset($cloned_clauses[$clause_a_hash]);
-
- $clause_a = $clause_a->removePossibilities($opposing_keys[0]);
-
- if (!$clause_a) {
- continue 2;
- }
-
- $cloned_clauses[$clause_a->hash] = $clause_a;
- }
- }
- }
-
- continue;
- }
-
- $clause_var = array_keys($clause_a->possibilities)[0];
- $only_type = array_pop(array_values($clause_a->possibilities)[0]);
- $negated_clause_type = self::negateType($only_type);
-
- foreach ($cloned_clauses as $clause_b_hash => $clause_b) {
- if ($clause_a === $clause_b || !$clause_b->reconcilable || $clause_b->wedge) {
- continue;
- }
-
- if (isset($clause_b->possibilities[$clause_var]) &&
- in_array($negated_clause_type, $clause_b->possibilities[$clause_var], true)
- ) {
- $clause_var_possibilities = array_values(
- array_filter(
- $clause_b->possibilities[$clause_var],
- function (string $possible_type) use ($negated_clause_type): bool {
- return $possible_type !== $negated_clause_type;
- }
- )
- );
-
- unset($cloned_clauses[$clause_b_hash]);
-
- if (!$clause_var_possibilities) {
- $updated_clause = $clause_b->removePossibilities($clause_var);
-
- if ($updated_clause) {
- $cloned_clauses[$updated_clause->hash] = $updated_clause;
- }
- } else {
- $updated_clause = $clause_b->addPossibilities(
- $clause_var,
- $clause_var_possibilities
- );
-
- $cloned_clauses[$updated_clause->hash] = $updated_clause;
- }
- }
- }
- }
-
- $simplified_clauses = [];
-
- foreach ($cloned_clauses as $clause_a) {
- $is_redundant = false;
-
- foreach ($cloned_clauses as $clause_b) {
- if ($clause_a === $clause_b
- || !$clause_b->reconcilable
- || $clause_b->wedge
- || $clause_a->wedge
- ) {
- continue;
- }
-
- if ($clause_a->contains($clause_b)) {
- $is_redundant = true;
- break;
- }
- }
-
- if (!$is_redundant) {
- $simplified_clauses[] = $clause_a;
- }
- }
-
- return $simplified_clauses;
- }
-
- /**
- * Look for clauses with only one possible value
- *
- * @param list<Clause> $clauses
- * @param array<string, bool> $cond_referenced_var_ids
- * @param array<string, array<int, array<int, string>>> $active_truths
- *
- * @return array<string, list<array<int, string>>>
- */
- public static function getTruthsFromFormula(
- array $clauses,
- ?int $creating_conditional_id = null,
- array &$cond_referenced_var_ids = [],
- array &$active_truths = []
- ): array {
- $truths = [];
- $active_truths = [];
-
- if ($clauses === []) {
- return [];
- }
-
- foreach ($clauses as $clause) {
- if (!$clause->reconcilable || count($clause->possibilities) !== 1) {
- continue;
- }
-
- foreach ($clause->possibilities as $var => $possible_types) {
- if ($var[0] === '*') {
- continue;
- }
-
- // if there's only one possible type, return it
- if (count($possible_types) === 1) {
- $possible_type = array_pop($possible_types);
-
- if (isset($truths[$var]) && !isset($clause->redefined_vars[$var])) {
- $truths[$var][] = [$possible_type];
- } else {
- $truths[$var] = [[$possible_type]];
- }
-
- if ($creating_conditional_id && $creating_conditional_id === $clause->creating_conditional_id) {
- if (!isset($active_truths[$var])) {
- $active_truths[$var] = [];
- }
-
- $active_truths[$var][count($truths[$var]) - 1] = [$possible_type];
- }
- } else {
- // if there's only one active clause, return all the non-negation clause members ORed together
- $things_that_can_be_said = array_filter(
- $possible_types,
- function (string $possible_type): bool {
- return $possible_type[0] !== '!';
- }
- );
-
- if ($things_that_can_be_said && count($things_that_can_be_said) === count($possible_types)) {
- $things_that_can_be_said = array_unique($things_that_can_be_said);
-
- if ($clause->generated && count($possible_types) > 1) {
- unset($cond_referenced_var_ids[$var]);
- }
-
- /** @var array<int, string> $things_that_can_be_said */
- $truths[$var] = [$things_that_can_be_said];
-
- if ($creating_conditional_id && $creating_conditional_id === $clause->creating_conditional_id) {
- $active_truths[$var] = [$things_that_can_be_said];
- }
- }
- }
- }
- }
-
- return $truths;
- }
-
- /**
- * @param non-empty-list<Clause> $clauses
- *
- * @return list<Clause>
- *
- * @psalm-pure
- */
- public static function groupImpossibilities(array $clauses): array
- {
- $complexity = 1;
-
- $seed_clauses = [];
-
- $clause = array_pop($clauses);
-
- if (!$clause->wedge) {
- if ($clause->impossibilities === null) {
- throw new UnexpectedValueException('$clause->impossibilities should not be null');
- }
-
- foreach ($clause->impossibilities as $var => $impossible_types) {
- foreach ($impossible_types as $impossible_type) {
- $seed_clause = new Clause(
- [$var => [$impossible_type]],
- $clause->creating_conditional_id,
- $clause->creating_object_id
- );
-
- $seed_clauses[] = $seed_clause;
-
- ++$complexity;
- }
- }
- }
-
- if (!$clauses || !$seed_clauses) {
- return $seed_clauses;
- }
-
- while ($clauses) {
- $clause = array_pop($clauses);
-
- $new_clauses = [];
-
- foreach ($seed_clauses as $grouped_clause) {
- if ($clause->impossibilities === null) {
- throw new UnexpectedValueException('$clause->impossibilities should not be null');
- }
-
- foreach ($clause->impossibilities as $var => $impossible_types) {
- foreach ($impossible_types as $impossible_type) {
- $new_clause_possibilities = $grouped_clause->possibilities;
-
- if (isset($grouped_clause->possibilities[$var])) {
- $new_clause_possibilities[$var] = array_values(
- array_unique(
- array_merge([$impossible_type], $new_clause_possibilities[$var])
- )
- );
-
- $removed_indexes = [];
-
- for ($i = 0, $l = count($new_clause_possibilities[$var]); $i < $l; $i++) {
- for ($j = $i + 1; $j < $l; $j++) {
- $ith = $new_clause_possibilities[$var][$i];
- $jth = $new_clause_possibilities[$var][$j];
-
- if ($ith === '!' . $jth || $jth === '!' . $ith) {
- $removed_indexes[$i] = true;
- $removed_indexes[$j] = true;
- }
- }
- }
-
- if ($removed_indexes) {
- $new_possibilities = array_values(
- array_diff_key(
- $new_clause_possibilities[$var],
- $removed_indexes
- )
- );
-
- if (!$new_possibilities) {
- unset($new_clause_possibilities[$var]);
- } else {
- $new_clause_possibilities[$var] = $new_possibilities;
- }
- }
- } else {
- $new_clause_possibilities[$var] = [$impossible_type];
- }
-
- if (!$new_clause_possibilities) {
- continue;
- }
-
- $new_clause = new Clause(
- $new_clause_possibilities,
- $grouped_clause->creating_conditional_id,
- $clause->creating_object_id,
- false,
- true,
- true,
- []
- );
-
- $new_clauses[] = $new_clause;
-
- ++$complexity;
-
- if ($complexity > 20000) {
- throw new ComplicatedExpressionException();
- }
- }
- }
- }
-
- $seed_clauses = $new_clauses;
- }
-
- return $seed_clauses;
- }
-
- /**
- * @param list<Clause> $left_clauses
- * @param list<Clause> $right_clauses
- *
- * @return list<Clause>
- *
- * @psalm-pure
- */
- public static function combineOredClauses(
- array $left_clauses,
- array $right_clauses,
- int $conditional_object_id
- ): array {
- if (count($left_clauses) > 60000 || count($right_clauses) > 60000) {
- return [];
- }
-
- $clauses = [];
-
- $all_wedges = true;
- $has_wedge = false;
-
- foreach ($left_clauses as $left_clause) {
- foreach ($right_clauses as $right_clause) {
- $all_wedges = $all_wedges && ($left_clause->wedge && $right_clause->wedge);
- $has_wedge = $has_wedge || ($left_clause->wedge && $right_clause->wedge);
- }
- }
-
- if ($all_wedges) {
- return [new Clause([], $conditional_object_id, $conditional_object_id, true)];
- }
-
- foreach ($left_clauses as $left_clause) {
- foreach ($right_clauses as $right_clause) {
- if ($left_clause->wedge && $right_clause->wedge) {
- // handled below
- continue;
- }
-
- /** @var array<string, non-empty-list<string>> */
- $possibilities = [];
-
- $can_reconcile = true;
-
- if ($left_clause->wedge ||
- $right_clause->wedge ||
- !$left_clause->reconcilable ||
- !$right_clause->reconcilable
- ) {
- $can_reconcile = false;
- }
-
- foreach ($left_clause->possibilities as $var => $possible_types) {
- if (isset($right_clause->redefined_vars[$var])) {
- continue;
- }
-
- if (isset($possibilities[$var])) {
- $possibilities[$var] = array_merge($possibilities[$var], $possible_types);
- } else {
- $possibilities[$var] = $possible_types;
- }
- }
-
- foreach ($right_clause->possibilities as $var => $possible_types) {
- if (isset($possibilities[$var])) {
- $possibilities[$var] = array_merge($possibilities[$var], $possible_types);
- } else {
- $possibilities[$var] = $possible_types;
- }
- }
-
- if (count($left_clauses) > 1 || count($right_clauses) > 1) {
- foreach ($possibilities as $var => $p) {
- $possibilities[$var] = array_values(array_unique($p));
- }
- }
-
- foreach ($possibilities as $var_possibilities) {
- if (count($var_possibilities) === 2) {
- if ($var_possibilities[0] === '!' . $var_possibilities[1]
- || $var_possibilities[1] === '!' . $var_possibilities[0]
- ) {
- continue 2;
- }
- }
- }
-
- $creating_conditional_id =
- $right_clause->creating_conditional_id === $left_clause->creating_conditional_id
- ? $right_clause->creating_conditional_id
- : $conditional_object_id;
-
- $clauses[] = new Clause(
- $possibilities,
- $creating_conditional_id,
- $creating_conditional_id,
- false,
- $can_reconcile,
- $right_clause->generated
- || $left_clause->generated
- || count($left_clauses) > 1
- || count($right_clauses) > 1,
- []
- );
- }
- }
-
- if ($has_wedge) {
- $clauses[] = new Clause([], $conditional_object_id, $conditional_object_id, true);
- }
-
- return $clauses;
- }
-
- /**
- * Negates a set of clauses
- * negateClauses([$a || $b]) => !$a && !$b
- * negateClauses([$a, $b]) => !$a || !$b
- * negateClauses([$a, $b || $c]) =>
- * (!$a || !$b) &&
- * (!$a || !$c)
- * negateClauses([$a, $b || $c, $d || $e || $f]) =>
- * (!$a || !$b || !$d) &&
- * (!$a || !$b || !$e) &&
- * (!$a || !$b || !$f) &&
- * (!$a || !$c || !$d) &&
- * (!$a || !$c || !$e) &&
- * (!$a || !$c || !$f)
- *
- * @param list<Clause> $clauses
- *
- * @return non-empty-list<Clause>
- */
- public static function negateFormula(array $clauses): array
- {
- $clauses = array_filter(
- $clauses,
- function ($clause) {
- return $clause->reconcilable;
- }
- );
-
- if (!$clauses) {
- $cond_id = mt_rand(0, 100000000);
- return [new Clause([], $cond_id, $cond_id, true)];
- }
-
- $clauses_with_impossibilities = [];
-
- foreach ($clauses as $clause) {
- $clauses_with_impossibilities[] = $clause->calculateNegation();
- }
-
- unset($clauses);
-
- $impossible_clauses = self::groupImpossibilities($clauses_with_impossibilities);
-
- if (!$impossible_clauses) {
- $cond_id = mt_rand(0, 100000000);
- return [new Clause([], $cond_id, $cond_id, true)];
- }
-
- $negated = self::simplifyCNF($impossible_clauses);
-
- if (!$negated) {
- $cond_id = mt_rand(0, 100000000);
- return [new Clause([], $cond_id, $cond_id, true)];
- }
-
- return $negated;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Algebra/FormulaGenerator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Algebra/FormulaGenerator.php
deleted file mode 100644
index b6504af..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Algebra/FormulaGenerator.php
+++ /dev/null
@@ -1,457 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Algebra;
-
-use PhpParser;
-use Psalm\Codebase;
-use Psalm\FileSource;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Analyzer\Statements\Expression\AssertionFinder;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Node\Expr\BinaryOp\VirtualBooleanAnd;
-use Psalm\Node\Expr\BinaryOp\VirtualBooleanOr;
-use Psalm\Node\Expr\VirtualBooleanNot;
-
-use function array_merge;
-use function count;
-use function spl_object_id;
-use function strlen;
-use function substr;
-
-class FormulaGenerator
-{
- /**
- * @return list<Clause>
- */
- public static function getFormula(
- int $conditional_object_id,
- int $creating_object_id,
- PhpParser\Node\Expr $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase = null,
- bool $inside_negation = false,
- bool $cache = true
- ): array {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd ||
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\LogicalAnd
- ) {
- $left_assertions = self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->left),
- $conditional->left,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
-
- $right_assertions = self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->right),
- $conditional->right,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
-
- return array_merge(
- $left_assertions,
- $right_assertions
- );
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr ||
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
- ) {
- $left_clauses = self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->left),
- $conditional->left,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
-
- $right_clauses = self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->right),
- $conditional->right,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
-
- return Algebra::combineOredClauses($left_clauses, $right_clauses, $conditional_object_id);
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BooleanNot) {
- if ($conditional->expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr) {
- $and_expr = new VirtualBooleanAnd(
- new VirtualBooleanNot(
- $conditional->expr->left,
- $conditional->getAttributes()
- ),
- new VirtualBooleanNot(
- $conditional->expr->right,
- $conditional->getAttributes()
- ),
- $conditional->expr->getAttributes()
- );
-
- return self::getFormula(
- $conditional_object_id,
- $conditional_object_id,
- $and_expr,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- false
- );
- }
-
- if ($conditional->expr instanceof PhpParser\Node\Expr\Isset_
- && count($conditional->expr->vars) > 1
- ) {
- $anded_assertions = null;
-
- if ($cache && $source instanceof StatementsAnalyzer) {
- $anded_assertions = $source->node_data->getAssertions($conditional->expr);
- }
-
- if ($anded_assertions === null) {
- $anded_assertions = AssertionFinder::scrapeAssertions(
- $conditional->expr,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
-
- if ($cache && $source instanceof StatementsAnalyzer) {
- $source->node_data->setAssertions($conditional->expr, $anded_assertions);
- }
- }
-
- $clauses = [];
-
- foreach ($anded_assertions as $assertions) {
- foreach ($assertions as $var => $anded_types) {
- $redefined = false;
-
- if ($var[0] === '=') {
- /** @var string */
- $var = substr($var, 1);
- $redefined = true;
- }
-
- foreach ($anded_types as $orred_types) {
- $clauses[] = new Clause(
- [$var => $orred_types],
- $conditional_object_id,
- spl_object_id($conditional->expr),
- false,
- true,
- $orred_types[0][0] === '='
- || $orred_types[0][0] === '~'
- || (strlen($orred_types[0]) > 1
- && ($orred_types[0][1] === '='
- || $orred_types[0][1] === '~')),
- $redefined ? [$var => true] : []
- );
- }
- }
- }
-
- return Algebra::negateFormula($clauses);
- }
-
- if ($conditional->expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd) {
- $and_expr = new VirtualBooleanOr(
- new VirtualBooleanNot(
- $conditional->expr->left,
- $conditional->getAttributes()
- ),
- new VirtualBooleanNot(
- $conditional->expr->right,
- $conditional->getAttributes()
- ),
- $conditional->expr->getAttributes()
- );
-
- return self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->expr),
- $and_expr,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- false
- );
- }
-
- return Algebra::negateFormula(
- self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->expr),
- $conditional->expr,
- $this_class_name,
- $source,
- $codebase,
- !$inside_negation
- )
- );
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || $conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
- ) {
- $false_pos = AssertionFinder::hasFalseVariable($conditional);
- $true_pos = AssertionFinder::hasTrueVariable($conditional);
-
- if ($false_pos === AssertionFinder::ASSIGNMENT_TO_RIGHT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->left instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return Algebra::negateFormula(
- self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->left),
- $conditional->left,
- $this_class_name,
- $source,
- $codebase,
- !$inside_negation,
- $cache
- )
- );
- }
-
- if ($false_pos === AssertionFinder::ASSIGNMENT_TO_LEFT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->right instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return Algebra::negateFormula(
- self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->right),
- $conditional->right,
- $this_class_name,
- $source,
- $codebase,
- !$inside_negation,
- $cache
- )
- );
- }
-
- if ($true_pos === AssertionFinder::ASSIGNMENT_TO_RIGHT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->left instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->left),
- $conditional->left,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
- }
-
- if ($true_pos === AssertionFinder::ASSIGNMENT_TO_LEFT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->right instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->right),
- $conditional->right,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
- }
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
- || $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- ) {
- $false_pos = AssertionFinder::hasFalseVariable($conditional);
- $true_pos = AssertionFinder::hasTrueVariable($conditional);
-
- if ($true_pos === AssertionFinder::ASSIGNMENT_TO_RIGHT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->left instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return Algebra::negateFormula(
- self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->left),
- $conditional->left,
- $this_class_name,
- $source,
- $codebase,
- !$inside_negation,
- $cache
- )
- );
- }
-
- if ($true_pos === AssertionFinder::ASSIGNMENT_TO_LEFT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->right instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return Algebra::negateFormula(
- self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->right),
- $conditional->right,
- $this_class_name,
- $source,
- $codebase,
- !$inside_negation,
- $cache
- )
- );
- }
-
- if ($false_pos === AssertionFinder::ASSIGNMENT_TO_RIGHT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->left instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->left),
- $conditional->left,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
- }
-
- if ($false_pos === AssertionFinder::ASSIGNMENT_TO_LEFT
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $conditional->right instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $conditional->right instanceof PhpParser\Node\Expr\BooleanNot)
- ) {
- return self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->right),
- $conditional->right,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
- }
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\Cast\Bool_) {
- return self::getFormula(
- $conditional_object_id,
- spl_object_id($conditional->expr),
- $conditional->expr,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
- }
-
- $anded_assertions = null;
-
- if ($cache && $source instanceof StatementsAnalyzer) {
- $anded_assertions = $source->node_data->getAssertions($conditional);
- }
-
- if ($anded_assertions === null) {
- $anded_assertions = AssertionFinder::scrapeAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache
- );
-
- if ($cache && $source instanceof StatementsAnalyzer) {
- $source->node_data->setAssertions($conditional, $anded_assertions);
- }
- }
-
- $clauses = [];
-
- foreach ($anded_assertions as $assertions) {
- foreach ($assertions as $var => $anded_types) {
- $redefined = false;
-
- if ($var[0] === '=') {
- /** @var string */
- $var = substr($var, 1);
- $redefined = true;
- }
-
- foreach ($anded_types as $orred_types) {
- $clauses[] = new Clause(
- [$var => $orred_types],
- $conditional_object_id,
- $creating_object_id,
- false,
- true,
- $orred_types[0][0] === '='
- || $orred_types[0][0] === '~'
- || (strlen($orred_types[0]) > 1
- && ($orred_types[0][1] === '='
- || $orred_types[0][1] === '~')),
- $redefined ? [$var => true] : []
- );
- }
- }
- }
-
- if ($clauses) {
- return $clauses;
- }
-
- /** @psalm-suppress MixedOperand */
- $conditional_ref = '*' . $conditional->getAttribute('startFilePos')
- . ':' . $conditional->getAttribute('endFilePos');
-
- return [new Clause([$conditional_ref => ['!falsy']], $conditional_object_id, $creating_object_id)];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AlgebraAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AlgebraAnalyzer.php
deleted file mode 100644
index a7f2a54..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AlgebraAnalyzer.php
+++ /dev/null
@@ -1,174 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Clause;
-use Psalm\Issue\ParadoxicalCondition;
-use Psalm\Issue\RedundantCondition;
-use Psalm\IssueBuffer;
-
-use function array_intersect_key;
-use function array_unique;
-use function count;
-use function implode;
-use function preg_match;
-
-/**
- * @internal
- */
-class AlgebraAnalyzer
-{
- /**
- * This looks to see if there are any clauses in one formula that contradict
- * clauses in another formula, or clauses that duplicate previous clauses
- *
- * e.g.
- * if ($a) { }
- * elseif ($a) { }
- *
- * @param list<Clause> $formula_1
- * @param list<Clause> $formula_2
- * @param array<string, int> $new_assigned_var_ids
- */
- public static function checkForParadox(
- array $formula_1,
- array $formula_2,
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node $stmt,
- array $new_assigned_var_ids
- ): void {
- try {
- $negated_formula2 = Algebra::negateFormula($formula_2);
- } catch (ComplicatedExpressionException $e) {
- return;
- }
-
- $formula_1_hashes = [];
-
- foreach ($formula_1 as $formula_1_clause) {
- $formula_1_hashes[$formula_1_clause->hash] = true;
- }
-
- $formula_2_hashes = [];
-
- foreach ($formula_2 as $formula_2_clause) {
- $hash = $formula_2_clause->hash;
-
- if (!$formula_2_clause->generated
- && !$formula_2_clause->wedge
- && $formula_2_clause->reconcilable
- && (isset($formula_1_hashes[$hash]) || isset($formula_2_hashes[$hash]))
- && !array_intersect_key($new_assigned_var_ids, $formula_2_clause->possibilities)
- ) {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $formula_2_clause . ' has already been asserted',
- new CodeLocation($statements_analyzer, $stmt),
- null
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- foreach ($formula_2_clause->possibilities as $key => $values) {
- if (!$formula_2_clause->generated
- && count($values) > 1
- && !isset($new_assigned_var_ids[$key])
- && count(array_unique($values)) < count($values)
- ) {
- IssueBuffer::maybeAdd(
- new ParadoxicalCondition(
- 'Found a redundant condition when evaluating assertion (' . $formula_2_clause . ')',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- $formula_2_hashes[$hash] = true;
- }
-
- // remove impossible types
- foreach ($negated_formula2 as $negated_clause_2) {
- if (count($negated_formula2) === 1) {
- foreach ($negated_clause_2->possibilities as $key => $values) {
- if (count($values) > 1
- && !isset($new_assigned_var_ids[$key])
- && count(array_unique($values)) < count($values)
- ) {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- 'Found a redundant condition when evaluating ' . $key,
- new CodeLocation($statements_analyzer, $stmt),
- null
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if (!$negated_clause_2->reconcilable || $negated_clause_2->wedge) {
- continue;
- }
-
- foreach ($formula_1 as $clause_1) {
- if ($negated_clause_2 === $clause_1 || !$clause_1->reconcilable || $clause_1->wedge) {
- continue;
- }
-
- $negated_clause_2_contains_1_possibilities = true;
-
- foreach ($clause_1->possibilities as $key => $keyed_possibilities) {
- if (!isset($negated_clause_2->possibilities[$key])) {
- $negated_clause_2_contains_1_possibilities = false;
- break;
- }
-
- if ($negated_clause_2->possibilities[$key] != $keyed_possibilities) {
- $negated_clause_2_contains_1_possibilities = false;
- break;
- }
- foreach ($keyed_possibilities as $possibility) {
- if (preg_match('@^!?in-array-@', $possibility)) {
- $negated_clause_2_contains_1_possibilities = false;
- break;
- }
- }
- }
-
- if ($negated_clause_2_contains_1_possibilities) {
- $mini_formula_2 = Algebra::negateFormula([$negated_clause_2]);
-
- if (!$mini_formula_2[0]->wedge) {
- if (count($mini_formula_2) > 1) {
- $paradox_message = 'Condition ((' . implode(') && (', $mini_formula_2) . '))'
- . ' contradicts a previously-established condition (' . $clause_1 . ')';
- } else {
- $paradox_message = 'Condition (' . $mini_formula_2[0] . ')'
- . ' contradicts a previously-established condition (' . $clause_1 . ')';
- }
- } else {
- $paradox_message = 'Condition not(' . $negated_clause_2 . ')'
- . ' contradicts a previously-established condition (' . $clause_1 . ')';
- }
-
- IssueBuffer::maybeAdd(
- new ParadoxicalCondition(
- $paradox_message,
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AttributesAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AttributesAnalyzer.php
deleted file mode 100644
index 3ce5ca4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/AttributesAnalyzer.php
+++ /dev/null
@@ -1,394 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use Generator;
-use PhpParser\Node\Arg;
-use PhpParser\Node\Attribute;
-use PhpParser\Node\AttributeGroup;
-use PhpParser\Node\Expr\New_;
-use PhpParser\Node\Name\FullyQualified;
-use PhpParser\Node\Stmt\Expression;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\ConstantTypeResolver;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-use Psalm\Issue\InvalidAttribute;
-use Psalm\Issue\UndefinedClass;
-use Psalm\IssueBuffer;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\HasAttributesInterface;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Union;
-
-use function array_shift;
-use function array_values;
-use function assert;
-use function count;
-use function reset;
-use function strtolower;
-
-class AttributesAnalyzer
-{
- private const TARGET_DESCRIPTIONS = [
- 1 => 'class',
- 2 => 'function',
- 4 => 'method',
- 8 => 'property',
- 16 => 'class constant',
- 32 => 'function/method parameter',
- 40 => 'promoted property',
- ];
-
- // Copied from Attribute class since that class might not exist at runtime
- public const TARGET_CLASS = 1;
- public const TARGET_FUNCTION = 2;
- public const TARGET_METHOD = 4;
- public const TARGET_PROPERTY = 8;
- public const TARGET_CLASS_CONSTANT = 16;
- public const TARGET_PARAMETER = 32;
- public const TARGET_ALL = 63;
- public const IS_REPEATABLE = 64;
-
- /**
- * @param array<array-key, AttributeGroup> $attribute_groups
- * @param key-of<self::TARGET_DESCRIPTIONS> $target
- * @param array<array-key, string> $suppressed_issues
- */
- public static function analyze(
- SourceAnalyzer $source,
- Context $context,
- HasAttributesInterface $storage,
- array $attribute_groups,
- int $target,
- array $suppressed_issues
- ): void {
- $codebase = $source->getCodebase();
- $appearing_non_repeatable_attributes = [];
- foreach (self::iterateAttributeNodes($attribute_groups) as $attribute) {
- if ($attribute->name instanceof FullyQualified) {
- $fq_attribute_name = (string) $attribute->name;
- } else {
- $fq_attribute_name = ClassLikeAnalyzer::getFQCLNFromNameObject($attribute->name, $source->getAliases());
- }
-
- $attribute_name = (string) $attribute->name;
- $attribute_name_location = new CodeLocation($source, $attribute->name);
-
- $attribute_class_storage = $codebase->classlikes->classExists($fq_attribute_name)
- ? $codebase->classlike_storage_provider->get($fq_attribute_name)
- : null;
-
- $attribute_class_flags = self::getAttributeClassFlags(
- $source,
- $attribute_name,
- $fq_attribute_name,
- $attribute_name_location,
- $attribute_class_storage,
- $suppressed_issues
- );
-
- self::analyzeAttributeConstruction(
- $source,
- $context,
- $fq_attribute_name,
- $attribute,
- $suppressed_issues,
- $storage instanceof ClassLikeStorage ? $storage : null
- );
-
- if (($attribute_class_flags & self::IS_REPEATABLE) === 0) {
- // Not IS_REPEATABLE
- if (isset($appearing_non_repeatable_attributes[$fq_attribute_name])) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- "Attribute {$attribute_name} is not repeatable",
- $attribute_name_location
- ),
- $suppressed_issues
- );
- }
- $appearing_non_repeatable_attributes[$fq_attribute_name] = true;
- }
-
- if (($attribute_class_flags & $target) === 0) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- "Attribute {$attribute_name} cannot be used on a "
- . self::TARGET_DESCRIPTIONS[$target],
- $attribute_name_location
- ),
- $suppressed_issues
- );
- }
- }
- }
-
- /**
- * @param array<array-key, string> $suppressed_issues
- */
- private static function analyzeAttributeConstruction(
- SourceAnalyzer $source,
- Context $context,
- string $fq_attribute_name,
- Attribute $attribute,
- array $suppressed_issues,
- ?ClassLikeStorage $classlike_storage = null
- ): void {
- $attribute_name_location = new CodeLocation($source, $attribute->name);
-
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $source,
- $fq_attribute_name,
- $attribute_name_location,
- null,
- null,
- $suppressed_issues,
- new ClassLikeNameOptions(
- false,
- false,
- false,
- false,
- false,
- true
- )
- ) === false) {
- return;
- }
-
- if (strtolower($fq_attribute_name) === 'attribute' && $classlike_storage) {
- if ($classlike_storage->is_trait) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- 'Traits cannot act as attribute classes',
- $attribute_name_location
- ),
- $suppressed_issues
- );
- } elseif ($classlike_storage->is_interface) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- 'Interfaces cannot act as attribute classes',
- $attribute_name_location
- ),
- $suppressed_issues
- );
- } elseif ($classlike_storage->abstract) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- 'Abstract classes cannot act as attribute classes',
- $attribute_name_location
- ),
- $suppressed_issues
- );
- } elseif (isset($classlike_storage->methods['__construct'])
- && $classlike_storage->methods['__construct']->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC
- ) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- 'Classes with protected/private constructors cannot act as attribute classes',
- $attribute_name_location
- ),
- $suppressed_issues
- );
- } elseif ($classlike_storage->is_enum) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- 'Enums cannot act as attribute classes',
- $attribute_name_location
- ),
- $suppressed_issues
- );
- }
- }
-
- $statements_analyzer = new StatementsAnalyzer(
- $source,
- new NodeDataProvider()
- );
- $statements_analyzer->addSuppressedIssues(array_values($suppressed_issues));
-
- $had_returned = $context->has_returned;
- $context->has_returned = false;
-
- IssueBuffer::startRecording();
- $statements_analyzer->analyze(
- [new Expression(new New_($attribute->name, $attribute->args, $attribute->getAttributes()))],
- // Use a new Context for the Attribute attribute so that it can't access `self`
- strtolower($fq_attribute_name) === "attribute" ? new Context() : $context
- );
- $context->has_returned = $had_returned;
-
- $issues = IssueBuffer::clearRecordingLevel();
- IssueBuffer::stopRecording();
- foreach ($issues as $issue) {
- if ($issue instanceof UndefinedClass && $issue->fq_classlike_name === $fq_attribute_name) {
- // Remove UndefinedClass for the attribute, since we already added UndefinedAttribute
- continue;
- }
- IssueBuffer::bubbleUp($issue);
- }
- }
-
- /**
- * @param array<array-key, string> $suppressed_issues
- */
- private static function getAttributeClassFlags(
- SourceAnalyzer $source,
- string $attribute_name,
- string $fq_attribute_name,
- CodeLocation $attribute_name_location,
- ?ClassLikeStorage $attribute_class_storage,
- array $suppressed_issues
- ): int {
- if (strtolower($fq_attribute_name) === "attribute") {
- // We override this here because we still want to analyze attributes
- // for PHP 7.4 when the Attribute class doesn't yet exist.
- return self::TARGET_CLASS;
- }
-
- if ($attribute_class_storage === null) {
- return self::TARGET_ALL; // Defaults to TARGET_ALL
- }
-
- foreach ($attribute_class_storage->attributes as $attribute_attribute) {
- if ($attribute_attribute->fq_class_name === 'Attribute') {
- if (!$attribute_attribute->args) {
- return self::TARGET_ALL; // Defaults to TARGET_ALL
- }
-
- $first_arg = reset($attribute_attribute->args);
-
- $first_arg_type = $first_arg->type;
-
- if ($first_arg_type instanceof UnresolvedConstantComponent) {
- $first_arg_type = new Union([
- ConstantTypeResolver::resolve(
- $source->getCodebase()->classlikes,
- $first_arg_type,
- $source instanceof StatementsAnalyzer ? $source : null
- )
- ]);
- }
-
- if (!$first_arg_type->isSingleIntLiteral()) {
- return self::TARGET_ALL; // Fall back to default if it's invalid
- }
-
- return $first_arg_type->getSingleIntLiteral()->value;
- }
- }
-
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- "The class {$attribute_name} doesn't have the Attribute attribute",
- $attribute_name_location
- ),
- $suppressed_issues
- );
-
- return self::TARGET_ALL; // Fall back to default if it's invalid
- }
-
- /**
- * @param iterable<AttributeGroup> $attribute_groups
- *
- * @return Generator<int, Attribute>
- */
- private static function iterateAttributeNodes(iterable $attribute_groups): Generator
- {
- foreach ($attribute_groups as $attribute_group) {
- foreach ($attribute_group->attrs as $attribute) {
- yield $attribute;
- }
- }
- }
-
- /**
- * Analyze Reflection getAttributes method calls.
-
- * @param list<Arg> $args
- */
- public static function analyzeGetAttributes(
- StatementsAnalyzer $statements_analyzer,
- string $method_id,
- array $args
- ): void {
- if (count($args) !== 1) {
- // We skip this analysis if $flags is specified on getAttributes, since the only option
- // is ReflectionAttribute::IS_INSTANCEOF, which causes getAttributes to return children.
- // When returning children we don't want to limit this since a child could add a target.
- return;
- }
-
- switch ($method_id) {
- case "ReflectionClass::getattributes":
- $target = self::TARGET_CLASS;
- break;
- case "ReflectionFunction::getattributes":
- $target = self::TARGET_FUNCTION;
- break;
- case "ReflectionMethod::getattributes":
- $target = self::TARGET_METHOD;
- break;
- case "ReflectionProperty::getattributes":
- $target = self::TARGET_PROPERTY;
- break;
- case "ReflectionClassConstant::getattributes":
- $target = self::TARGET_CLASS_CONSTANT;
- break;
- case "ReflectionParameter::getattributes":
- $target = self::TARGET_PARAMETER;
- break;
- default:
- return;
- }
-
- $arg = $args[0];
- if ($arg->name !== null) {
- for (; !empty($args) && ($arg->name->name ?? null) !== "name"; $arg = array_shift($args));
- if ($arg->name->name ?? null !== "name") {
- // No named argument for "name" parameter
- return;
- }
- }
-
- $arg_type = $statements_analyzer->getNodeTypeProvider()->getType($arg->value);
- if ($arg_type === null || !$arg_type->isSingle() || !$arg_type->hasLiteralString()) {
- return;
- }
-
- $class_string = $arg_type->getSingleAtomic();
- assert($class_string instanceof TLiteralString);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$codebase->classExists($class_string->value)) {
- return;
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($class_string->value);
- $arg_location = new CodeLocation($statements_analyzer, $arg);
- $class_attribute_target = self::getAttributeClassFlags(
- $statements_analyzer,
- $class_string->value,
- $class_string->value,
- $arg_location,
- $class_storage,
- $statements_analyzer->getSuppressedIssues()
- );
-
- if (($class_attribute_target & $target) === 0) {
- IssueBuffer::maybeAdd(
- new InvalidAttribute(
- "Attribute {$class_string->value} cannot be used on a "
- . self::TARGET_DESCRIPTIONS[$target],
- $arg_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CanAlias.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CanAlias.php
deleted file mode 100644
index 8f2ca0e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CanAlias.php
+++ /dev/null
@@ -1,162 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\FileManipulation;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-
-use function implode;
-use function strtolower;
-
-trait CanAlias
-{
- /**
- * @var array<lowercase-string, string>
- */
- private $aliased_classes = [];
-
- /**
- * @var array<lowercase-string, CodeLocation>
- */
- private $aliased_class_locations = [];
-
- /**
- * @var array<lowercase-string, string>
- */
- private $aliased_classes_flipped = [];
-
- /**
- * @var array<lowercase-string, string>
- */
- private $aliased_classes_flipped_replaceable = [];
-
- /**
- * @var array<lowercase-string, non-empty-string>
- */
- private $aliased_functions = [];
-
- /**
- * @var array<string, string>
- */
- private $aliased_constants = [];
-
- public function visitUse(PhpParser\Node\Stmt\Use_ $stmt): void
- {
- $codebase = $this->getCodebase();
-
- foreach ($stmt->uses as $use) {
- $use_path = implode('\\', $use->name->parts);
- $use_path_lc = strtolower($use_path);
- $use_alias = $use->alias->name ?? $use->name->getLast();
- $use_alias_lc = strtolower($use_alias);
-
- switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $stmt->type) {
- case PhpParser\Node\Stmt\Use_::TYPE_FUNCTION:
- $this->aliased_functions[$use_alias_lc] = $use_path;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_CONSTANT:
- $this->aliased_constants[$use_alias] = $use_path;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_NORMAL:
- $codebase->analyzer->addOffsetReference(
- $this->getFilePath(),
- (int) $use->getAttribute('startFilePos'),
- (int) $use->getAttribute('endFilePos'),
- $use_path
- );
- if ($codebase->collect_locations) {
- // register the path
- $codebase->use_referencing_locations[$use_path_lc][] =
- new CodeLocation($this, $use);
- }
-
- if ($codebase->alter_code) {
- if (isset($codebase->class_transforms[$use_path_lc])) {
- $new_fq_class_name = $codebase->class_transforms[$use_path_lc];
-
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- (int) $use->getAttribute('startFilePos'),
- (int) $use->getAttribute('endFilePos') + 1,
- $new_fq_class_name . ($use->alias ? ' as ' . $use_alias : '')
- );
-
- FileManipulationBuffer::add($this->getFilePath(), $file_manipulations);
- }
-
- $this->aliased_classes_flipped_replaceable[$use_path_lc] = $use_alias;
- }
-
- $this->aliased_classes[$use_alias_lc] = $use_path;
- $this->aliased_class_locations[$use_alias_lc] = new CodeLocation($this, $stmt);
- $this->aliased_classes_flipped[$use_path_lc] = $use_alias;
- break;
- }
- }
- }
-
- public function visitGroupUse(PhpParser\Node\Stmt\GroupUse $stmt): void
- {
- $use_prefix = implode('\\', $stmt->prefix->parts);
-
- $codebase = $this->getCodebase();
-
- foreach ($stmt->uses as $use) {
- $use_path = $use_prefix . '\\' . implode('\\', $use->name->parts);
- $use_alias = $use->alias->name ?? $use->name->getLast();
-
- switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $stmt->type) {
- case PhpParser\Node\Stmt\Use_::TYPE_FUNCTION:
- $this->aliased_functions[strtolower($use_alias)] = $use_path;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_CONSTANT:
- $this->aliased_constants[$use_alias] = $use_path;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_NORMAL:
- if ($codebase->collect_locations) {
- // register the path
- $codebase->use_referencing_locations[strtolower($use_path)][] =
- new CodeLocation($this, $use);
- }
-
- $this->aliased_classes[strtolower($use_alias)] = $use_path;
- $this->aliased_classes_flipped[strtolower($use_path)] = $use_alias;
- break;
- }
- }
- }
-
- /**
- * @return array<lowercase-string, string>
- */
- public function getAliasedClassesFlipped(): array
- {
- return $this->aliased_classes_flipped;
- }
-
- /**
- * @return array<string, string>
- */
- public function getAliasedClassesFlippedReplaceable(): array
- {
- return $this->aliased_classes_flipped_replaceable;
- }
-
- public function getAliases(): Aliases
- {
- return new Aliases(
- $this->getNamespace(),
- $this->aliased_classes,
- $this->aliased_functions,
- $this->aliased_constants
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php
deleted file mode 100644
index cb4e634..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php
+++ /dev/null
@@ -1,2638 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use Exception;
-use InvalidArgumentException;
-use LogicException;
-use PhpParser;
-use PhpParser\Node\Stmt\Class_;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\DocComment;
-use Psalm\Exception\DocblockParseException;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\FunctionLike\ReturnTypeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\FileManipulation\PropertyDocblockManipulator;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\DeprecatedClass;
-use Psalm\Issue\DeprecatedInterface;
-use Psalm\Issue\DeprecatedTrait;
-use Psalm\Issue\DuplicateEnumCaseValue;
-use Psalm\Issue\ExtensionRequirementViolation;
-use Psalm\Issue\ImplementationRequirementViolation;
-use Psalm\Issue\InaccessibleMethod;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\InvalidEnumCaseValue;
-use Psalm\Issue\InvalidExtendClass;
-use Psalm\Issue\InvalidTemplateParam;
-use Psalm\Issue\InvalidTraversableImplementation;
-use Psalm\Issue\MethodSignatureMismatch;
-use Psalm\Issue\MismatchingDocblockPropertyType;
-use Psalm\Issue\MissingConstructor;
-use Psalm\Issue\MissingImmutableAnnotation;
-use Psalm\Issue\MissingPropertyType;
-use Psalm\Issue\MissingTemplateParam;
-use Psalm\Issue\MutableDependency;
-use Psalm\Issue\NoEnumProperties;
-use Psalm\Issue\NonInvariantDocblockPropertyType;
-use Psalm\Issue\NonInvariantPropertyType;
-use Psalm\Issue\OverriddenPropertyAccess;
-use Psalm\Issue\ParseError;
-use Psalm\Issue\PropertyNotSetInConstructor;
-use Psalm\Issue\ReservedWord;
-use Psalm\Issue\TooManyTemplateParams;
-use Psalm\Issue\UndefinedClass;
-use Psalm\Issue\UndefinedInterface;
-use Psalm\Issue\UndefinedTrait;
-use Psalm\Issue\UnimplementedAbstractMethod;
-use Psalm\Issue\UnimplementedInterfaceMethod;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualStaticCall;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\Stmt\VirtualClassMethod;
-use Psalm\Node\Stmt\VirtualExpression;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Plugin\EventHandler\Event\AfterClassLikeAnalysisEvent;
-use Psalm\StatementsSource;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_pop;
-use function array_search;
-use function array_values;
-use function assert;
-use function count;
-use function explode;
-use function implode;
-use function in_array;
-use function is_int;
-use function is_string;
-use function preg_match;
-use function preg_replace;
-use function reset;
-use function str_replace;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class ClassAnalyzer extends ClassLikeAnalyzer
-{
- /**
- * @var array<string, Union>
- */
- public $inferred_property_types = [];
-
- /**
- * @param PhpParser\Node\Stmt\Class_|PhpParser\Node\Stmt\Enum_ $class
- */
- public function __construct(PhpParser\Node\Stmt $class, SourceAnalyzer $source, ?string $fq_class_name)
- {
- if (!$fq_class_name) {
- if (!$class instanceof PhpParser\Node\Stmt\Class_) {
- throw new UnexpectedValueException('Anonymous enums are not allowed');
- }
-
- $fq_class_name = self::getAnonymousClassName($class, $source->getFilePath());
- }
-
- parent::__construct($class, $source, $fq_class_name);
-
- if ($this->class instanceof PhpParser\Node\Stmt\Class_ && $this->class->extends) {
- $this->parent_fq_class_name = self::getFQCLNFromNameObject(
- $this->class->extends,
- $this->source->getAliases()
- );
- }
- }
-
- /** @return non-empty-string */
- public static function getAnonymousClassName(PhpParser\Node\Stmt\Class_ $class, string $file_path): string
- {
- return preg_replace('/[^A-Za-z0-9]/', '_', $file_path)
- . '_' . $class->getLine() . '_' . (int)$class->getAttribute('startFilePos');
- }
-
- public function analyze(
- ?Context $class_context = null,
- ?Context $global_context = null
- ): void {
- $class = $this->class;
-
- if (!$class instanceof PhpParser\Node\Stmt\Class_ && !$class instanceof PhpParser\Node\Stmt\Enum_) {
- throw new LogicException('Something went badly wrong');
- }
-
- $fq_class_name = $class_context && $class_context->self ? $class_context->self : $this->fq_class_name;
-
- $storage = $this->storage;
-
- if ($storage->has_visitor_issues) {
- return;
- }
-
- if ($class->name
- && (preg_match(
- '/(^|\\\)(int|float|bool|string|void|null|false|true|object|mixed)$/i',
- $fq_class_name
- ) || strtolower($fq_class_name) === 'resource')
- ) {
- $class_name_parts = explode('\\', $fq_class_name);
- $class_name = array_pop($class_name_parts);
-
- IssueBuffer::maybeAdd(
- new ReservedWord(
- $class_name . ' is a reserved word',
- new CodeLocation(
- $this,
- $class->name,
- null,
- true
- ),
- $class_name
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- return;
- }
-
- $project_analyzer = $this->file_analyzer->project_analyzer;
- $codebase = $this->getCodebase();
-
- if ($codebase->alter_code && $class->name && $codebase->classes_to_move) {
- if (isset($codebase->classes_to_move[strtolower($this->fq_class_name)])) {
- $destination_class = $codebase->classes_to_move[strtolower($this->fq_class_name)];
-
- $source_class_parts = explode('\\', $this->fq_class_name);
- $destination_class_parts = explode('\\', $destination_class);
-
- array_pop($source_class_parts);
- array_pop($destination_class_parts);
-
- $source_ns = implode('\\', $source_class_parts);
- $destination_ns = implode('\\', $destination_class_parts);
-
- if (strtolower($source_ns) !== strtolower($destination_ns)) {
- if ($storage->namespace_name_location) {
- $bounds = $storage->namespace_name_location->getSelectionBounds();
-
- $file_manipulations = [
- new FileManipulation(
- $bounds[0],
- $bounds[1],
- $destination_ns
- )
- ];
-
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- } elseif (!$source_ns) {
- $first_statement_pos = $this->getFileAnalyzer()->getFirstStatementOffset();
-
- if ($first_statement_pos === -1) {
- $first_statement_pos = (int) $class->getAttribute('startFilePos');
- }
-
- $file_manipulations = [
- new FileManipulation(
- $first_statement_pos,
- $first_statement_pos,
- 'namespace ' . $destination_ns . ';' . "\n\n",
- true
- )
- ];
-
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
-
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $this,
- $class->name,
- $this->fq_class_name,
- null
- );
- }
-
- foreach ($storage->docblock_issues as $docblock_issue) {
- IssueBuffer::maybeAdd($docblock_issue);
- }
-
- $classlike_storage_provider = $codebase->classlike_storage_provider;
-
- $parent_fq_class_name = $this->parent_fq_class_name;
-
- if ($class instanceof PhpParser\Node\Stmt\Class_ && $class->extends && $parent_fq_class_name) {
- $this->checkParentClass(
- $class,
- $class->extends,
- $fq_class_name,
- $parent_fq_class_name,
- $storage,
- $codebase,
- $class_context
- );
- }
-
-
- if ($storage->template_types) {
- foreach ($storage->template_types as $param_name => $_) {
- $fq_classlike_name = Type::getFQCLNFromString(
- $param_name,
- $this->getAliases()
- );
-
- if ($codebase->classOrInterfaceExists($fq_classlike_name)) {
- IssueBuffer::maybeAdd(
- new ReservedWord(
- 'Cannot use ' . $param_name . ' as template name since the class already exists',
- new CodeLocation($this, $this->class),
- 'resource'
- ),
- $this->getSuppressedIssues()
- );
- }
- }
- }
-
- if (($storage->templatedMixins || $storage->namedMixins)
- && $storage->mixin_declaring_fqcln === $storage->name) {
- /** @var non-empty-array<int, TTemplateParam|TNamedObject> $mixins */
- $mixins = array_merge($storage->templatedMixins, $storage->namedMixins);
- $union = new Union($mixins);
-
- $static_self = new TNamedObject($storage->name);
- $static_self->was_static = true;
-
- $union = TypeExpander::expandUnion(
- $codebase,
- $union,
- $storage->name,
- $static_self,
- null
- );
-
- $union->check(
- $this,
- new CodeLocation(
- $this,
- $class->name ?: $class,
- null,
- true
- ),
- $this->getSuppressedIssues()
- );
- }
-
- if ($storage->template_extended_params) {
- foreach ($storage->template_extended_params as $type_map) {
- foreach ($type_map as $atomic_type) {
- $atomic_type->check(
- $this,
- new CodeLocation(
- $this,
- $class->name ?: $class,
- null,
- true
- ),
- $this->getSuppressedIssues()
- );
- }
- }
- }
-
- if (!$class_context) {
- $class_context = new Context($this->fq_class_name);
- $class_context->parent = $parent_fq_class_name;
- }
-
- if ($global_context) {
- $class_context->strict_types = $global_context->strict_types;
- }
-
- if ($this->checkImplementedInterfaces(
- $class_context,
- $class,
- $codebase,
- $fq_class_name,
- $storage
- ) === false) {
- return;
- }
-
- if ($storage->invalid_dependencies) {
- return;
- }
-
- if ($this->leftover_stmts) {
- (new StatementsAnalyzer(
- $this,
- new NodeDataProvider()
- ))->analyze(
- $this->leftover_stmts,
- $class_context
- );
- }
-
- if (!$storage->abstract) {
- foreach ($storage->declaring_method_ids as $declaring_method_id) {
- $method_storage = $codebase->methods->getStorage($declaring_method_id);
-
- $declaring_class_name = $declaring_method_id->fq_class_name;
- $method_name_lc = $declaring_method_id->method_name;
-
- if ($method_storage->abstract) {
- if (IssueBuffer::accepts(
- new UnimplementedAbstractMethod(
- 'Method ' . $method_name_lc . ' is not defined on class ' .
- $this->fq_class_name . ', defined abstract in ' . $declaring_class_name,
- new CodeLocation(
- $this,
- $class->name ?? $class,
- $class_context->include_location,
- true
- )
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- )) {
- return;
- }
- }
- }
- }
-
- AttributesAnalyzer::analyze(
- $this,
- $class_context,
- $storage,
- $class->attrGroups,
- AttributesAnalyzer::TARGET_CLASS,
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- self::addContextProperties(
- $this,
- $storage,
- $class_context,
- $this->fq_class_name,
- $this->parent_fq_class_name,
- $class->stmts
- );
-
- $constructor_analyzer = null;
- $member_stmts = [];
-
- foreach ($class->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
- $method_analyzer = $this->analyzeClassMethod(
- $stmt,
- $storage,
- $this,
- $class_context,
- $global_context
- );
-
- if ($stmt->name->name === '__construct') {
- $constructor_analyzer = $method_analyzer;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\TraitUse) {
- if ($this->analyzeTraitUse(
- $this->source->getAliases(),
- $stmt,
- $project_analyzer,
- $storage,
- $class_context,
- $global_context,
- $constructor_analyzer
- ) === false) {
- return;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Property) {
- foreach ($stmt->props as $prop) {
- if ($storage->is_enum) {
- if (IssueBuffer::accepts(new NoEnumProperties(
- 'Enums cannot have properties',
- new CodeLocation($this, $prop),
- $fq_class_name
- ))) {
- // fall through
- }
- continue;
- }
- if ($prop->default) {
- $member_stmts[] = $stmt;
- }
-
- if ($codebase->alter_code) {
- $property_id = strtolower($this->fq_class_name) . '::$' . $prop->name;
-
- $property_storage = $codebase->properties->getStorage($property_id);
-
- if ($property_storage->type
- && $property_storage->type_location
- && $property_storage->type_location !== $property_storage->signature_type_location
- ) {
- $replace_type = TypeExpander::expandUnion(
- $codebase,
- $property_storage->type,
- $this->getFQCLN(),
- $this->getFQCLN(),
- $this->getParentFQCLN()
- );
-
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $this,
- $replace_type,
- $property_storage->type_location,
- null
- );
- }
-
- foreach ($codebase->properties_to_rename as $original_property_id => $new_property_name) {
- if ($property_id === $original_property_id) {
- $file_manipulations = [
- new FileManipulation(
- (int) $prop->name->getAttribute('startFilePos'),
- (int) $prop->name->getAttribute('endFilePos') + 1,
- '$' . $new_property_name
- )
- ];
-
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
- $member_stmts[] = $stmt;
-
- foreach ($stmt->consts as $const) {
- $const_id = strtolower($this->fq_class_name) . '::' . $const->name;
-
- foreach ($codebase->class_constants_to_rename as $original_const_id => $new_const_name) {
- if ($const_id === $original_const_id) {
- $file_manipulations = [
- new FileManipulation(
- (int) $const->name->getAttribute('startFilePos'),
- (int) $const->name->getAttribute('endFilePos') + 1,
- $new_const_name
- )
- ];
-
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
- }
- }
-
- $statements_analyzer = new StatementsAnalyzer($this, new NodeDataProvider());
- $statements_analyzer->analyze($member_stmts, $class_context, $global_context, true);
-
- $config = Config::getInstance();
-
- if ($class instanceof PhpParser\Node\Stmt\Class_) {
- $this->checkPropertyInitialization(
- $codebase,
- $config,
- $storage,
- $class_context,
- $global_context,
- $constructor_analyzer
- );
- }
-
- if ($class instanceof PhpParser\Node\Stmt\Enum_) {
- $this->checkEnum();
- }
-
- foreach ($class->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Property) {
- $this->analyzeProperty($this, $stmt, $class_context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\TraitUse) {
- foreach ($stmt->traits as $trait) {
- $fq_trait_name = self::getFQCLNFromNameObject(
- $trait,
- $this->source->getAliases()
- );
-
- try {
- $trait_file_analyzer = $project_analyzer->getFileAnalyzerForClassLike($fq_trait_name);
- } catch (Exception $e) {
- continue;
- }
-
- $trait_storage = $codebase->classlike_storage_provider->get($fq_trait_name);
- $trait_node = $codebase->classlikes->getTraitNode($fq_trait_name);
- $trait_aliases = $trait_storage->aliases;
-
- if ($trait_aliases === null) {
- continue;
- }
-
- $trait_analyzer = new TraitAnalyzer(
- $trait_node,
- $trait_file_analyzer,
- $fq_trait_name,
- $trait_aliases
- );
-
- $fq_trait_name_lc = strtolower($fq_trait_name);
-
- if (isset($storage->template_type_uses_count[$fq_trait_name_lc])) {
- $this->checkTemplateParams(
- $codebase,
- $storage,
- $trait_storage,
- new CodeLocation(
- $this,
- $trait
- ),
- $storage->template_type_uses_count[$fq_trait_name_lc]
- );
- }
-
- foreach ($trait_node->stmts as $trait_stmt) {
- if ($trait_stmt instanceof PhpParser\Node\Stmt\Property) {
- $this->analyzeProperty($trait_analyzer, $trait_stmt, $class_context);
- }
- }
-
- $trait_file_analyzer->clearSourceBeforeDestruction();
- }
- }
- }
-
- $pseudo_methods = $storage->pseudo_methods + $storage->pseudo_static_methods;
-
- foreach ($pseudo_methods as $pseudo_method_name => $pseudo_method_storage) {
- $pseudo_method_id = new MethodIdentifier(
- $this->fq_class_name,
- $pseudo_method_name
- );
-
- $overridden_method_ids = $codebase->methods->getOverriddenMethodIds($pseudo_method_id);
-
- if ($overridden_method_ids
- && $pseudo_method_name !== '__construct'
- && $pseudo_method_storage->location
- ) {
- foreach ($overridden_method_ids as $overridden_method_id) {
- $parent_method_storage = $codebase->methods->getStorage($overridden_method_id);
-
- $overridden_fq_class_name = $overridden_method_id->fq_class_name;
-
- $parent_storage = $classlike_storage_provider->get($overridden_fq_class_name);
-
- MethodComparator::compare(
- $codebase,
- null,
- $storage,
- $parent_storage,
- $pseudo_method_storage,
- $parent_method_storage,
- $this->fq_class_name,
- $pseudo_method_storage->visibility ?: 0,
- $storage->location ?: $pseudo_method_storage->location,
- $storage->suppressed_issues,
- true,
- false
- );
- }
- }
- }
-
- $event = new AfterClassLikeAnalysisEvent(
- $class,
- $storage,
- $this,
- $codebase,
- []
- );
-
- if ($codebase->config->eventDispatcher->dispatchAfterClassLikeAnalysis($event) === false) {
- return;
- }
- $file_manipulations = $event->getFileReplacements();
- if ($file_manipulations) {
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- }
- }
-
- public static function addContextProperties(
- StatementsSource $statements_source,
- ClassLikeStorage $storage,
- Context $class_context,
- string $fq_class_name,
- ?string $parent_fq_class_name,
- array $stmts = []
- ): void {
- $codebase = $statements_source->getCodebase();
-
- foreach ($storage->appearing_property_ids as $property_name => $appearing_property_id) {
- $property_class_name = $codebase->properties->getDeclaringClassForProperty(
- $appearing_property_id,
- true
- );
-
- if ($property_class_name === null) {
- continue;
- }
-
- $property_class_storage = $codebase->classlike_storage_provider->get($property_class_name);
-
- $property_storage = $property_class_storage->properties[$property_name];
-
- if (isset($storage->overridden_property_ids[$property_name])) {
- foreach ($storage->overridden_property_ids[$property_name] as $overridden_property_id) {
- [$guide_class_name] = explode('::$', $overridden_property_id);
- $guide_class_storage = $codebase->classlike_storage_provider->get($guide_class_name);
- $guide_property_storage = $guide_class_storage->properties[$property_name];
-
- if ($property_storage->visibility > $guide_property_storage->visibility
- && $property_storage->location
- ) {
- IssueBuffer::maybeAdd(
- new OverriddenPropertyAccess(
- 'Property ' . $fq_class_name . '::$' . $property_name
- . ' has different access level than '
- . $storage->name . '::$' . $property_name,
- $property_storage->location
- )
- );
- }
-
- if ((($property_storage->signature_type && !$guide_property_storage->signature_type)
- || (!$property_storage->signature_type && $guide_property_storage->signature_type)
- || ($property_storage->signature_type
- && !$property_storage->signature_type->equals(
- $guide_property_storage->signature_type
- )))
- && $property_storage->location
- ) {
- IssueBuffer::maybeAdd(
- new NonInvariantPropertyType(
- 'Property ' . $fq_class_name . '::$' . $property_name
- . ' has type '
- . ($property_storage->signature_type
- ? $property_storage->signature_type->getId()
- : '<empty>'
- )
- . ", not invariant with " . $guide_class_name . '::$'
- . $property_name . ' of type '
- . ($guide_property_storage->signature_type
- ? $guide_property_storage->signature_type->getId()
- : '<empty>'
- ),
- $property_storage->location
- ),
- $property_storage->suppressed_issues
- );
- }
-
- if ($property_storage->type === null) {
- // Property type not set, no need to check for docblock invariance
- continue;
- }
-
- $property_type = clone $property_storage->type;
-
- $guide_property_type = $guide_property_storage->type === null
- ? Type::getMixed()
- : clone $guide_property_storage->type;
-
- // Set upper bounds for all templates
- $lower_bounds = [];
- $extended_templates = $storage->template_extended_params ?? [];
- foreach ($extended_templates as $et_name => $et_array) {
- foreach ($et_array as $et_class_name => $extended_template) {
- if (!isset($lower_bounds[$et_class_name][$et_name])) {
- $lower_bounds[$et_class_name][$et_name] = $extended_template;
- }
- }
- }
-
- // Get actual types used for templates (to support @template-covariant)
- $template_standins = new TemplateResult($lower_bounds, []);
- TemplateStandinTypeReplacer::replace(
- $guide_property_type,
- $template_standins,
- $codebase,
- null,
- $property_type
- );
-
- // Iterate over parent classes to find template-covariants, and replace the upper bound with the
- // standin. Since @template-covariant allows child classes, we want to use the standin type
- // instead of the template extended type.
- $parent_class = $storage->parent_class;
- while ($parent_class !== null) {
- $parent_storage = $codebase->classlike_storage_provider->get($parent_class);
- foreach ($parent_storage->template_covariants ?? [] as $pt_offset => $covariant) {
- if ($covariant) {
- // If template_covariants is set template_types should also be set
- assert($parent_storage->template_types !== null);
- $pt_name = array_keys($parent_storage->template_types)[$pt_offset];
- if (isset($template_standins->lower_bounds[$pt_name][$parent_class])) {
- $lower_bounds[$pt_name][$parent_class] =
- TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $template_standins->lower_bounds[$pt_name][$parent_class],
- $codebase
- );
- }
- }
- }
- $parent_class = $parent_storage->parent_class;
- }
-
- $template_result = new TemplateResult([], $lower_bounds);
-
- TemplateInferredTypeReplacer::replace(
- $guide_property_type,
- $template_result,
- $codebase
- );
- TemplateInferredTypeReplacer::replace(
- $property_type,
- $template_result,
- $codebase
- );
-
- if ($property_storage->location
- && !$property_type->equals($guide_property_type, false)
- && $guide_class_storage->user_defined
- ) {
- IssueBuffer::maybeAdd(
- new NonInvariantDocblockPropertyType(
- 'Property ' . $fq_class_name . '::$' . $property_name
- . ' has type ' . $property_type->getId()
- . ", not invariant with " . $guide_class_name . '::$'
- . $property_name . ' of type '
- . $guide_property_type->getId(),
- $property_storage->location
- ),
- $property_storage->suppressed_issues
- );
- }
- }
- }
-
- if ($property_storage->type) {
- $property_type = clone $property_storage->type;
-
- if (!$property_type->isMixed()
- && !$property_storage->is_promoted
- && !$property_storage->has_default
- && !($property_type->isNullable() && $property_type->from_docblock)
- ) {
- $property_type->initialized = false;
- $property_type->from_property = true;
- $property_type->from_static_property = $property_storage->is_static === true;
- }
- } else {
- $property_type = Type::getMixed();
-
- if (!$property_storage->has_default && !$property_storage->is_promoted) {
- $property_type->initialized = false;
- $property_type->from_property = true;
- $property_type->from_static_property = $property_storage->is_static === true;
- }
- }
-
- $property_type_location = $property_storage->type_location;
-
- $fleshed_out_type = !$property_type->isMixed()
- ? TypeExpander::expandUnion(
- $codebase,
- $property_type,
- $fq_class_name,
- $fq_class_name,
- $parent_fq_class_name,
- true,
- false,
- $storage->final
- )
- : $property_type;
-
- $class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $property_class_storage,
- $storage,
- null,
- new TNamedObject($fq_class_name),
- true
- );
-
- if ($class_template_params) {
- $this_object_type = self::getThisObjectType(
- $storage,
- $fq_class_name
- );
-
- if (!$this_object_type instanceof TGenericObject) {
- $type_params = [];
-
- foreach ($class_template_params as $type_map) {
- $type_params[] = clone array_values($type_map)[0];
- }
-
- $this_object_type = new TGenericObject($this_object_type->value, $type_params);
- }
-
- $fleshed_out_type = AtomicPropertyFetchAnalyzer::localizePropertyType(
- $codebase,
- $fleshed_out_type,
- $this_object_type,
- $storage,
- $property_class_storage
- );
- }
-
- if ($property_type_location && !$fleshed_out_type->isMixed()) {
- $stmt = array_filter(
- $stmts,
- function ($stmt) use ($property_name): bool {
- return $stmt instanceof PhpParser\Node\Stmt\Property
- && isset($stmt->props[0]->name->name)
- && $stmt->props[0]->name->name === $property_name;
- }
- );
-
- $suppressed = [];
- if (count($stmt) > 0) {
- $stmt = array_pop($stmt);
-
- $docComment = $stmt->getDocComment();
- if ($docComment) {
- try {
- $docBlock = DocComment::parsePreservingLength($docComment);
- $suppressed = $docBlock->tags['psalm-suppress'] ?? [];
- } catch (DocblockParseException $e) {
- // do nothing to keep original behavior
- }
- }
- }
-
- $fleshed_out_type->check(
- $statements_source,
- $property_type_location,
- $storage->suppressed_issues + $statements_source->getSuppressedIssues() + $suppressed,
- [],
- false
- );
-
- if ($property_storage->signature_type) {
- $union_comparison_result = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $fleshed_out_type,
- $property_storage->signature_type,
- false,
- false,
- $union_comparison_result
- ) && !$union_comparison_result->type_coerced_from_mixed
- ) {
- IssueBuffer::maybeAdd(
- new MismatchingDocblockPropertyType(
- 'Parameter '
- . $property_class_name . '::$' . $property_name
- . ' has wrong type \'' . $fleshed_out_type .
- '\', should be \'' . $property_storage->signature_type . '\'',
- $property_type_location
- )
- );
- }
- }
- }
-
- if ($property_storage->is_static) {
- $property_id = $fq_class_name . '::$' . $property_name;
-
- $class_context->vars_in_scope[$property_id] = $fleshed_out_type;
- } else {
- $class_context->vars_in_scope['$this->' . $property_name] = $fleshed_out_type;
- }
- }
-
- foreach ($storage->pseudo_property_get_types as $property_name => $property_type) {
- $property_name = substr($property_name, 1);
-
- if (isset($class_context->vars_in_scope['$this->' . $property_name])) {
- $fleshed_out_type = !$property_type->isMixed()
- ? TypeExpander::expandUnion(
- $codebase,
- $property_type,
- $fq_class_name,
- $fq_class_name,
- $parent_fq_class_name
- )
- : $property_type;
-
- $class_context->vars_in_scope['$this->' . $property_name] = $fleshed_out_type;
- }
- }
- }
-
- private function checkPropertyInitialization(
- Codebase $codebase,
- Config $config,
- ClassLikeStorage $storage,
- Context $class_context,
- ?Context $global_context = null,
- ?MethodAnalyzer $constructor_analyzer = null
- ): void {
- if (!$config->reportIssueInFile('PropertyNotSetInConstructor', $this->getFilePath())) {
- return;
- }
-
- if (!isset($storage->declaring_method_ids['__construct'])
- && !$config->reportIssueInFile('MissingConstructor', $this->getFilePath())
- ) {
- return;
- }
-
- $fq_class_name = $class_context->self ?: $this->fq_class_name;
- $fq_class_name_lc = strtolower($fq_class_name);
-
- $included_file_path = $this->getFilePath();
-
- $method_already_analyzed = $codebase->analyzer->isMethodAlreadyAnalyzed(
- $included_file_path,
- $fq_class_name_lc . '::__construct',
- true
- );
-
- if ($method_already_analyzed && !$codebase->diff_methods) {
- // this can happen when re-analysing a class that has been include()d inside another
- return;
- }
-
- /** @var PhpParser\Node\Stmt\Class_ */
- $class = $this->class;
- $classlike_storage_provider = $codebase->classlike_storage_provider;
- $class_storage = $classlike_storage_provider->get($fq_class_name_lc);
-
- $constructor_appearing_fqcln = $fq_class_name_lc;
-
- $uninitialized_variables = [];
- $uninitialized_properties = [];
- $uninitialized_typed_properties = [];
- $uninitialized_private_properties = false;
-
- foreach ($storage->appearing_property_ids as $property_name => $appearing_property_id) {
- $property_class_name = $codebase->properties->getDeclaringClassForProperty(
- $appearing_property_id,
- true
- );
-
- if ($property_class_name === null) {
- continue;
- }
-
- $property_class_storage = $classlike_storage_provider->get($property_class_name);
-
- $property = $property_class_storage->properties[$property_name];
-
- $property_is_initialized = isset($property_class_storage->initialized_properties[$property_name]);
-
- if ($property->is_static) {
- continue;
- }
-
- if ($property->has_default || $property_is_initialized) {
- continue;
- }
-
- if ($property->type && $property->type->from_docblock && $property->type->isNullable()) {
- continue;
- }
-
- if ($codebase->diff_methods && $method_already_analyzed && $property->location) {
- [$start, $end] = $property->location->getSelectionBounds();
-
- $existing_issues = $codebase->analyzer->getExistingIssuesForFile(
- $this->getFilePath(),
- $start,
- $end,
- 'PropertyNotSetInConstructor'
- );
-
- if ($existing_issues) {
- IssueBuffer::addIssues([$this->getFilePath() => $existing_issues]);
- continue;
- }
- }
-
- if ($property->location) {
- $codebase->analyzer->removeExistingDataForFile(
- $this->getFilePath(),
- $property->location->raw_file_start,
- $property->location->raw_file_end,
- 'PropertyNotSetInConstructor'
- );
- }
-
- $codebase->file_reference_provider->addMethodReferenceToMissingClassMember(
- $fq_class_name_lc . '::__construct',
- strtolower($property_class_name) . '::$' . $property_name
- );
-
- if ($property->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE) {
- $uninitialized_private_properties = true;
- }
-
- $uninitialized_variables[] = '$this->' . $property_name;
- $uninitialized_properties[$property_class_name . '::$' . $property_name] = $property;
-
- if ($property->type && !$property->type->isMixed()) {
- $uninitialized_typed_properties[$property_class_name . '::$' . $property_name] = $property;
- }
- }
-
- if (!$uninitialized_properties) {
- return;
- }
-
- if (!$storage->abstract
- && !$constructor_analyzer
- && isset($storage->declaring_method_ids['__construct'])
- && isset($storage->appearing_method_ids['__construct'])
- && $class->extends
- ) {
- $constructor_declaring_fqcln = $storage->declaring_method_ids['__construct']->fq_class_name;
- $constructor_appearing_fqcln = $storage->appearing_method_ids['__construct']->fq_class_name;
-
- $constructor_class_storage = $classlike_storage_provider->get($constructor_declaring_fqcln);
-
- // ignore oldstyle constructors and classes without any declared properties
- if ($constructor_class_storage->user_defined
- && !$constructor_class_storage->stubbed
- && isset($constructor_class_storage->methods['__construct'])
- ) {
- $constructor_storage = $constructor_class_storage->methods['__construct'];
-
- $fake_constructor_params = array_map(
- function (FunctionLikeParameter $param): PhpParser\Node\Param {
- $fake_param = (new PhpParser\Builder\Param($param->name));
- if ($param->signature_type) {
- $fake_param->setType((string)$param->signature_type);
- }
-
- $node = $fake_param->getNode();
-
- $attributes = $param->location
- ? [
- 'startFilePos' => $param->location->raw_file_start,
- 'endFilePos' => $param->location->raw_file_end,
- 'startLine' => $param->location->raw_line_number
- ]
- : [];
-
- $node->setAttributes($attributes);
-
- return $node;
- },
- $constructor_storage->params
- );
-
- $fake_constructor_stmt_args = array_map(
- function (FunctionLikeParameter $param): PhpParser\Node\Arg {
- $attributes = $param->location
- ? [
- 'startFilePos' => $param->location->raw_file_start,
- 'endFilePos' => $param->location->raw_file_end,
- 'startLine' => $param->location->raw_line_number
- ]
- : [];
-
- return new VirtualArg(
- new VirtualVariable($param->name, $attributes),
- false,
- $param->is_variadic,
- $attributes
- );
- },
- $constructor_storage->params
- );
-
- $fake_constructor_attributes = [
- 'startLine' => $class->extends->getLine(),
- 'startFilePos' => $class->extends->getAttribute('startFilePos'),
- 'endFilePos' => $class->extends->getAttribute('endFilePos'),
- ];
-
- $fake_call_attributes = $fake_constructor_attributes
- + [
- 'comments' => [new PhpParser\Comment\Doc(
- '/** @psalm-suppress InaccessibleMethod */',
- $class->extends->getLine(),
- (int) $class->extends->getAttribute('startFilePos')
- )],
- ];
-
- $fake_constructor_stmts = [
- new VirtualExpression(
- new VirtualStaticCall(
- new VirtualFullyQualified($constructor_declaring_fqcln),
- new VirtualIdentifier('__construct', $fake_constructor_attributes),
- $fake_constructor_stmt_args,
- $fake_call_attributes
- ),
- $fake_call_attributes
- ),
- ];
-
- $fake_stmt = new VirtualClassMethod(
- new VirtualIdentifier('__construct'),
- [
- 'type' => PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC,
- 'params' => $fake_constructor_params,
- 'stmts' => $fake_constructor_stmts,
- ],
- $fake_constructor_attributes
- );
-
- $codebase->analyzer->disableMixedCounts();
-
- $was_collecting_initializations = $class_context->collect_initializations;
-
- $class_context->collect_initializations = true;
- $class_context->collect_nonprivate_initializations = !$uninitialized_private_properties;
-
- $constructor_analyzer = $this->analyzeClassMethod(
- $fake_stmt,
- $storage,
- $this,
- $class_context,
- $global_context,
- true
- );
-
- $class_context->collect_initializations = $was_collecting_initializations;
-
- $codebase->analyzer->enableMixedCounts();
- }
- }
-
- if ($constructor_analyzer) {
- $method_context = clone $class_context;
- $method_context->collect_initializations = true;
- $method_context->collect_nonprivate_initializations = !$uninitialized_private_properties;
- $method_context->self = $fq_class_name;
-
- $this_atomic_object_type = new TNamedObject($fq_class_name);
- $this_atomic_object_type->was_static = !$storage->final;
-
- $method_context->vars_in_scope['$this'] = new Union([$this_atomic_object_type]);
- $method_context->vars_possibly_in_scope['$this'] = true;
- $method_context->calling_method_id = strtolower($fq_class_name) . '::__construct';
-
- $constructor_analyzer->analyze(
- $method_context,
- new NodeDataProvider(),
- $global_context,
- true
- );
-
- foreach ($uninitialized_properties as $property_id => $property_storage) {
- [, $property_name] = explode('::$', $property_id);
-
- if (!isset($method_context->vars_in_scope['$this->' . $property_name])) {
- $end_type = Type::getVoid();
- $end_type->initialized = false;
- } else {
- $end_type = $method_context->vars_in_scope['$this->' . $property_name];
- }
-
- $constructor_class_property_storage = $property_storage;
-
- $error_location = $property_storage->location;
-
- if ($storage->declaring_property_ids[$property_name] !== $fq_class_name) {
- $error_location = $storage->location ?: $storage->stmt_location;
- }
-
- if ($fq_class_name_lc !== $constructor_appearing_fqcln
- && $property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- $a_class_storage = $classlike_storage_provider->get(
- $end_type->initialized_class ?: $constructor_appearing_fqcln
- );
-
- if (!isset($a_class_storage->declaring_property_ids[$property_name])) {
- $constructor_class_property_storage = null;
- } else {
- $declaring_property_class = $a_class_storage->declaring_property_ids[$property_name];
- $constructor_class_property_storage = $classlike_storage_provider
- ->get($declaring_property_class)
- ->properties[$property_name];
- }
- }
-
- if ($property_storage->location
- && $error_location
- && (!$end_type->initialized || $property_storage !== $constructor_class_property_storage)
- ) {
- if ($property_storage->type) {
- $expected_visibility = $uninitialized_private_properties
- ? 'private or final '
- : '';
-
- IssueBuffer::maybeAdd(
- new PropertyNotSetInConstructor(
- 'Property ' . $class_storage->name . '::$' . $property_name
- . ' is not defined in constructor of '
- . $this->fq_class_name . ' or in any ' . $expected_visibility
- . 'methods called in the constructor',
- $error_location,
- $property_id
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- } elseif (!$property_storage->has_default) {
- if (isset($this->inferred_property_types[$property_name])) {
- $this->inferred_property_types[$property_name]->addType(new TNull());
- $this->inferred_property_types[$property_name]->setFromDocblock();
- }
- }
- }
- }
-
- $codebase->analyzer->setAnalyzedMethod(
- $included_file_path,
- $fq_class_name_lc . '::__construct',
- true
- );
-
- return;
- }
-
- if (!$storage->abstract && $uninitialized_typed_properties) {
- foreach ($uninitialized_typed_properties as $id => $uninitialized_property) {
- if ($uninitialized_property->location) {
- IssueBuffer::maybeAdd(
- new MissingConstructor(
- $class_storage->name . ' has an uninitialized property ' . $id .
- ', but no constructor',
- $uninitialized_property->location,
- $class_storage->name . '::' . $uninitialized_variables[0]
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- /**
- * @return false|null
- */
- private function analyzeTraitUse(
- Aliases $aliases,
- PhpParser\Node\Stmt\TraitUse $stmt,
- ProjectAnalyzer $project_analyzer,
- ClassLikeStorage $storage,
- Context $class_context,
- ?Context $global_context = null,
- ?MethodAnalyzer &$constructor_analyzer = null,
- ?TraitAnalyzer $previous_trait_analyzer = null
- ): ?bool {
- $codebase = $this->getCodebase();
-
- $previous_context_include_location = $class_context->include_location;
-
- foreach ($stmt->traits as $trait_name) {
- $trait_location = new CodeLocation($this, $trait_name, null, true);
- $class_context->include_location = new CodeLocation($this, $trait_name, null, true);
-
- $fq_trait_name = self::getFQCLNFromNameObject(
- $trait_name,
- $aliases
- );
-
- if (!$codebase->classlikes->hasFullyQualifiedTraitName($fq_trait_name, $trait_location)) {
- IssueBuffer::maybeAdd(
- new UndefinedTrait(
- 'Trait ' . $fq_trait_name . ' does not exist',
- new CodeLocation($previous_trait_analyzer ?? $this, $trait_name)
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- return false;
- }
-
- if (!$codebase->traitHasCorrectCase($fq_trait_name)) {
- if (IssueBuffer::accepts(
- new UndefinedTrait(
- 'Trait ' . $fq_trait_name . ' has wrong casing',
- new CodeLocation($previous_trait_analyzer ?? $this, $trait_name)
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- )) {
- return false;
- }
-
- continue;
- }
-
- $fq_trait_name_resolved = $codebase->classlikes->getUnAliasedName($fq_trait_name);
- $trait_storage = $codebase->classlike_storage_provider->get($fq_trait_name_resolved);
-
- if ($trait_storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedTrait(
- 'Trait ' . $fq_trait_name . ' is deprecated',
- new CodeLocation($previous_trait_analyzer ?? $this, $trait_name)
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($trait_storage->extension_requirement !== null) {
- $extension_requirement = $codebase->classlikes->getUnAliasedName(
- $trait_storage->extension_requirement
- );
- $extensionRequirementMet = in_array($extension_requirement, $storage->parent_classes);
-
- if (!$extensionRequirementMet) {
- IssueBuffer::maybeAdd(
- new ExtensionRequirementViolation(
- $fq_trait_name . ' requires using class to extend ' . $extension_requirement
- . ', but ' . $storage->name . ' does not',
- new CodeLocation($previous_trait_analyzer ?? $this, $trait_name)
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
-
- foreach ($trait_storage->implementation_requirements as $implementation_requirement) {
- $implementation_requirement = $codebase->classlikes->getUnAliasedName($implementation_requirement);
- $implementationRequirementMet = in_array($implementation_requirement, $storage->class_implements);
-
- if (!$implementationRequirementMet) {
- IssueBuffer::maybeAdd(
- new ImplementationRequirementViolation(
- $fq_trait_name . ' requires using class to implement '
- . $implementation_requirement . ', but ' . $storage->name . ' does not',
- new CodeLocation($previous_trait_analyzer ?? $this, $trait_name)
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
-
- if ($storage->mutation_free && !$trait_storage->mutation_free) {
- IssueBuffer::maybeAdd(
- new MutableDependency(
- $storage->name . ' is marked @psalm-immutable but ' . $fq_trait_name . ' is not',
- new CodeLocation($previous_trait_analyzer ?? $this, $trait_name)
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- $trait_file_analyzer = $project_analyzer->getFileAnalyzerForClassLike($fq_trait_name_resolved);
- $trait_node = $codebase->classlikes->getTraitNode($fq_trait_name_resolved);
- $trait_aliases = $trait_storage->aliases;
- if ($trait_aliases === null) {
- continue;
- }
-
- $trait_analyzer = new TraitAnalyzer(
- $trait_node,
- $trait_file_analyzer,
- $fq_trait_name_resolved,
- $trait_aliases
- );
-
- foreach ($trait_node->stmts as $trait_stmt) {
- if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
- $trait_method_analyzer = $this->analyzeClassMethod(
- $trait_stmt,
- $storage,
- $trait_analyzer,
- $class_context,
- $global_context
- );
-
- if ($trait_stmt->name->name === '__construct') {
- $constructor_analyzer = $trait_method_analyzer;
- }
- } elseif ($trait_stmt instanceof PhpParser\Node\Stmt\TraitUse) {
- if ($this->analyzeTraitUse(
- $trait_aliases,
- $trait_stmt,
- $project_analyzer,
- $storage,
- $class_context,
- $global_context,
- $constructor_analyzer,
- $trait_analyzer
- ) === false) {
- return false;
- }
- }
- }
-
- $trait_file_analyzer->clearSourceBeforeDestruction();
- }
-
- $class_context->include_location = $previous_context_include_location;
-
- return null;
- }
-
- private function analyzeProperty(
- SourceAnalyzer $source,
- PhpParser\Node\Stmt\Property $stmt,
- Context $context
- ): void {
- $fq_class_name = $source->getFQCLN();
- $property_name = $stmt->props[0]->name->name;
-
- $codebase = $this->getCodebase();
-
- $property_id = $fq_class_name . '::$' . $property_name;
-
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $property_id,
- true
- );
-
- if (!$declaring_property_class) {
- return;
- }
-
- $fq_class_name = $declaring_property_class;
-
- // gets inherited property type
- $class_property_type = $codebase->properties->getPropertyType($property_id, false, $source, $context);
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $property_storage = $class_storage->properties[$property_name];
-
- AttributesAnalyzer::analyze(
- $source,
- $context,
- $property_storage,
- $stmt->attrGroups,
- AttributesAnalyzer::TARGET_PROPERTY,
- $property_storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- if ($class_property_type && ($property_storage->type_location || !$codebase->alter_code)) {
- return;
- }
-
- $message = 'Property ' . $property_id . ' does not have a declared type';
-
- $suggested_type = $property_storage->suggested_type;
-
- if (isset($this->inferred_property_types[$property_name])) {
- $suggested_type = Type::combineUnionTypes(
- $suggested_type,
- $this->inferred_property_types[$property_name] ?? null,
- $codebase
- );
- }
-
- if ($suggested_type && !$property_storage->has_default && $property_storage->is_static) {
- $suggested_type->addType(new TNull());
- }
-
- if ($suggested_type && !$suggested_type->isNull()) {
- $message .= ' - consider ' . str_replace(
- ['<array-key, mixed>', '<empty, empty>'],
- '',
- (string)$suggested_type
- );
- }
-
- $project_analyzer = ProjectAnalyzer::getInstance();
-
- if ($codebase->alter_code
- && $source === $this
- && isset($project_analyzer->getIssuesToFix()['MissingPropertyType'])
- && !in_array('MissingPropertyType', $this->getSuppressedIssues())
- && $suggested_type
- ) {
- if ($suggested_type->hasMixed() || $suggested_type->isNull()) {
- return;
- }
-
- self::addOrUpdatePropertyType(
- $project_analyzer,
- $stmt,
- $suggested_type,
- $this,
- $suggested_type->from_docblock
- );
-
- return;
- }
-
- IssueBuffer::maybeAdd(
- new MissingPropertyType(
- $message,
- new CodeLocation($source, $stmt->props[0]->name),
- $property_id
- ),
- $this->source->getSuppressedIssues() + $property_storage->suppressed_issues
- );
- }
-
- private static function addOrUpdatePropertyType(
- ProjectAnalyzer $project_analyzer,
- PhpParser\Node\Stmt\Property $property,
- Union $inferred_type,
- StatementsSource $source,
- bool $docblock_only = false
- ): void {
- $manipulator = PropertyDocblockManipulator::getForProperty(
- $project_analyzer,
- $source->getFilePath(),
- $property
- );
-
- $codebase = $project_analyzer->getCodebase();
-
- $allow_native_type = !$docblock_only
- && $codebase->php_major_version >= 7
- && ($codebase->php_major_version > 7 || $codebase->php_minor_version >= 4)
- && $codebase->allow_backwards_incompatible_changes;
-
- $manipulator->setType(
- $allow_native_type
- ? (string) $inferred_type->toPhpString(
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- $source->getFQCLN(),
- $codebase->php_major_version,
- $codebase->php_minor_version
- ) : null,
- $inferred_type->toNamespacedString(
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- $source->getFQCLN(),
- false
- ),
- $inferred_type->toNamespacedString(
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- $source->getFQCLN(),
- true
- ),
- $inferred_type->canBeFullyExpressedInPhp($codebase->php_major_version, $codebase->php_minor_version)
- );
- }
-
- private function analyzeClassMethod(
- PhpParser\Node\Stmt\ClassMethod $stmt,
- ClassLikeStorage $class_storage,
- SourceAnalyzer $source,
- Context $class_context,
- ?Context $global_context = null,
- bool $is_fake = false
- ): ?MethodAnalyzer {
- $config = Config::getInstance();
-
- if ($stmt->stmts === null && !$stmt->isAbstract()) {
- IssueBuffer::maybeAdd(
- new ParseError(
- 'Non-abstract class method must have statements',
- new CodeLocation($this, $stmt)
- )
- );
-
- return null;
- }
-
- try {
- $method_analyzer = new MethodAnalyzer($stmt, $source);
- } catch (UnexpectedValueException $e) {
- IssueBuffer::maybeAdd(
- new ParseError(
- 'Problem loading method: ' . $e->getMessage(),
- new CodeLocation($this, $stmt)
- )
- );
-
- return null;
- }
-
- $actual_method_id = $method_analyzer->getMethodId();
-
- $project_analyzer = $source->getProjectAnalyzer();
- $codebase = $source->getCodebase();
-
- $analyzed_method_id = $actual_method_id;
-
- $included_file_path = $source->getFilePath();
-
- if ($class_context->self && strtolower($class_context->self) !== strtolower((string) $source->getFQCLN())) {
- $analyzed_method_id = $method_analyzer->getMethodId($class_context->self);
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($analyzed_method_id);
-
- if ((string) $actual_method_id !== (string) $declaring_method_id) {
- // the method is an abstract trait method
-
- $declaring_method_storage = $method_analyzer->getFunctionLikeStorage();
-
- if (!$declaring_method_storage instanceof MethodStorage) {
- throw new LogicException('This should never happen');
- }
-
- if ($declaring_method_id && $declaring_method_storage->abstract) {
- $implementer_method_storage = $codebase->methods->getStorage($declaring_method_id);
- $declaring_storage = $codebase->classlike_storage_provider->get(
- $actual_method_id->fq_class_name
- );
-
- MethodComparator::compare(
- $codebase,
- null,
- $class_storage,
- $declaring_storage,
- $implementer_method_storage,
- $declaring_method_storage,
- $this->fq_class_name,
- $implementer_method_storage->visibility,
- new CodeLocation($source, $stmt),
- $implementer_method_storage->suppressed_issues,
- false
- );
- }
-
- return null;
- }
- }
-
- $trait_safe_method_id = strtolower((string) $analyzed_method_id);
-
- $actual_method_id_str = strtolower((string) $actual_method_id);
-
- if ($actual_method_id_str !== $trait_safe_method_id) {
- $trait_safe_method_id .= '&' . $actual_method_id_str;
- }
-
- $method_already_analyzed = $codebase->analyzer->isMethodAlreadyAnalyzed(
- $included_file_path,
- $trait_safe_method_id
- );
-
- $start = (int)$stmt->getAttribute('startFilePos');
- $end = (int)$stmt->getAttribute('endFilePos');
-
- $comments = $stmt->getComments();
-
- if ($comments) {
- $start = $comments[0]->getStartFilePos();
- }
-
- if ($codebase->diff_methods
- && $method_already_analyzed
- && !$class_context->collect_initializations
- && !$class_context->collect_mutations
- && !$is_fake
- ) {
- $project_analyzer->progress->debug(
- 'Skipping analysis of pre-analyzed method ' . $analyzed_method_id . "\n"
- );
-
- $existing_issues = $codebase->analyzer->getExistingIssuesForFile(
- $source->getFilePath(),
- $start,
- $end
- );
-
- IssueBuffer::addIssues([$source->getFilePath() => $existing_issues]);
-
- return $method_analyzer;
- }
-
- $codebase->analyzer->removeExistingDataForFile(
- $source->getFilePath(),
- $start,
- $end
- );
-
- $method_context = clone $class_context;
-
- foreach ($method_context->vars_in_scope as $context_var_id => $context_type) {
- $method_context->vars_in_scope[$context_var_id] = clone $context_type;
-
- if ($context_type->from_property && $stmt->name->name !== '__construct') {
- $method_context->vars_in_scope[$context_var_id]->initialized = true;
- }
- }
-
- $method_context->collect_exceptions = $config->check_for_throws_docblock;
-
- $type_provider = new NodeDataProvider();
-
- $method_analyzer->analyze(
- $method_context,
- $type_provider,
- $global_context ? clone $global_context : null
- );
-
- if ($stmt->name->name !== '__construct'
- && $config->reportIssueInFile('InvalidReturnType', $source->getFilePath())
- && $class_context->self
- ) {
- self::analyzeClassMethodReturnType(
- $stmt,
- $method_analyzer,
- $source,
- $type_provider,
- $codebase,
- $class_storage,
- $class_context->self,
- $analyzed_method_id,
- $actual_method_id,
- $method_context->has_returned
- );
- }
-
- if (!$method_already_analyzed
- && !$class_context->collect_initializations
- && !$class_context->collect_mutations
- && !$is_fake
- ) {
- $codebase->analyzer->setAnalyzedMethod($included_file_path, $trait_safe_method_id);
- }
-
- return $method_analyzer;
- }
-
- private static function getThisObjectType(
- ClassLikeStorage $class_storage,
- string $original_fq_classlike_name
- ): TNamedObject {
- if ($class_storage->template_types) {
- $template_params = [];
-
- foreach ($class_storage->template_types as $param_name => $template_map) {
- $key = array_keys($template_map)[0];
-
- $template_params[] = new Union([
- new TTemplateParam(
- $param_name,
- reset($template_map),
- $key
- )
- ]);
- }
-
- return new TGenericObject(
- $original_fq_classlike_name,
- $template_params
- );
- }
-
- return new TNamedObject($original_fq_classlike_name);
- }
-
- public static function analyzeClassMethodReturnType(
- PhpParser\Node\Stmt\ClassMethod $stmt,
- MethodAnalyzer $method_analyzer,
- SourceAnalyzer $source,
- NodeDataProvider $type_provider,
- Codebase $codebase,
- ClassLikeStorage $class_storage,
- string $fq_classlike_name,
- MethodIdentifier $analyzed_method_id,
- MethodIdentifier $actual_method_id,
- bool $did_explicitly_return
- ): void {
- $secondary_return_type_location = null;
-
- $actual_method_storage = $codebase->methods->getStorage($actual_method_id);
-
- $return_type_location = $codebase->methods->getMethodReturnTypeLocation(
- $actual_method_id,
- $secondary_return_type_location
- );
-
- $original_fq_classlike_name = $fq_classlike_name;
-
- $return_type = $codebase->methods->getMethodReturnType(
- $analyzed_method_id,
- $fq_classlike_name,
- $method_analyzer
- );
-
- if ($return_type && $class_storage->template_extended_params) {
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($analyzed_method_id);
-
- if ($declaring_method_id) {
- $declaring_class_name = $declaring_method_id->fq_class_name;
-
- $class_storage = $codebase->classlike_storage_provider->get($declaring_class_name);
- }
-
- $this_object_type = self::getThisObjectType(
- $class_storage,
- $original_fq_classlike_name
- );
-
- $class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $class_storage,
- $codebase->classlike_storage_provider->get($original_fq_classlike_name),
- strtolower($stmt->name->name),
- $this_object_type
- ) ?: [];
-
- $template_result = new TemplateResult(
- $class_template_params ?: [],
- []
- );
-
- $return_type = TemplateStandinTypeReplacer::replace(
- $return_type,
- $template_result,
- $codebase,
- null,
- null,
- null,
- $original_fq_classlike_name
- );
- }
-
- $overridden_method_ids = $class_storage->overridden_method_ids[strtolower($stmt->name->name)] ?? [];
-
- if (!$return_type
- && !$class_storage->is_interface
- && $overridden_method_ids
- ) {
- foreach ($overridden_method_ids as $interface_method_id) {
- $interface_class = $interface_method_id->fq_class_name;
-
- if (!$codebase->classlikes->interfaceExists($interface_class)) {
- continue;
- }
-
- $interface_return_type = $codebase->methods->getMethodReturnType(
- $interface_method_id,
- $interface_class
- );
-
- $interface_return_type_location = $codebase->methods->getMethodReturnTypeLocation(
- $interface_method_id
- );
-
- ReturnTypeAnalyzer::verifyReturnType(
- $stmt,
- $stmt->getStmts() ?: [],
- $source,
- $type_provider,
- $method_analyzer,
- $interface_return_type,
- $interface_class,
- $original_fq_classlike_name,
- $interface_return_type_location,
- [$analyzed_method_id->__toString()],
- $did_explicitly_return
- );
- }
- }
-
- $overridden_method_ids = array_map(
- function ($method_id) {
- return $method_id->__toString();
- },
- $overridden_method_ids
- );
-
- if ($actual_method_storage->overridden_downstream) {
- $overridden_method_ids['overridden::downstream'] = 'overridden::downstream';
- }
-
-
- ReturnTypeAnalyzer::verifyReturnType(
- $stmt,
- $stmt->getStmts() ?: [],
- $source,
- $type_provider,
- $method_analyzer,
- $return_type,
- $fq_classlike_name,
- $original_fq_classlike_name,
- $return_type_location,
- $overridden_method_ids,
- $did_explicitly_return
- );
- }
-
- private function checkTemplateParams(
- Codebase $codebase,
- ClassLikeStorage $storage,
- ClassLikeStorage $parent_storage,
- CodeLocation $code_location,
- int $given_param_count
- ): void {
- $expected_param_count = $parent_storage->template_types === null
- ? 0
- : count($parent_storage->template_types);
-
- if ($expected_param_count > $given_param_count) {
- IssueBuffer::maybeAdd(
- new MissingTemplateParam(
- $storage->name . ' has missing template params when extending ' . $parent_storage->name
- . ' , expecting ' . $expected_param_count,
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- } elseif ($expected_param_count < $given_param_count) {
- IssueBuffer::maybeAdd(
- new TooManyTemplateParams(
- $storage->name . ' has too many template params when extending ' . $parent_storage->name
- . ' , expecting ' . $expected_param_count,
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- $storage_param_count = ($storage->template_types ? count($storage->template_types) : 0);
-
- if ($parent_storage->enforce_template_inheritance
- && $expected_param_count !== $storage_param_count
- ) {
- if ($expected_param_count > $storage_param_count) {
- IssueBuffer::maybeAdd(
- new MissingTemplateParam(
- $storage->name . ' requires the same number of template params as ' . $parent_storage->name
- . ' but saw ' . $storage_param_count,
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TooManyTemplateParams(
- $storage->name . ' requires the same number of template params as ' . $parent_storage->name
- . ' but saw ' . $storage_param_count,
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
-
- if ($parent_storage->template_types && $storage->template_extended_params) {
- $i = 0;
-
- $previous_extended = [];
-
- foreach ($parent_storage->template_types as $template_name => $type_map) {
- // declares the variables
- foreach ($type_map as $declaring_class => $template_type) {
- }
-
- if (isset($storage->template_extended_params[$parent_storage->name][$template_name])) {
- $extended_type = $storage->template_extended_params[$parent_storage->name][$template_name];
-
- if (isset($parent_storage->template_covariants[$i])
- && !$parent_storage->template_covariants[$i]
- ) {
- foreach ($extended_type->getAtomicTypes() as $t) {
- if ($t instanceof TTemplateParam
- && $storage->template_types
- && $storage->template_covariants
- && ($local_offset
- = array_search($t->param_name, array_keys($storage->template_types)))
- !== false
- && !empty($storage->template_covariants[$local_offset])
- ) {
- IssueBuffer::maybeAdd(
- new InvalidTemplateParam(
- 'Cannot extend an invariant template param ' . $template_name
- . ' into a covariant context',
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($parent_storage->enforce_template_inheritance) {
- foreach ($extended_type->getAtomicTypes() as $t) {
- if (!$t instanceof TTemplateParam
- || !isset($storage->template_types[$t->param_name])
- ) {
- IssueBuffer::maybeAdd(
- new InvalidTemplateParam(
- 'Cannot extend a strictly-enforced parent template param '
- . $template_name
- . ' with a non-template type',
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- } elseif ($storage->template_types[$t->param_name][$storage->name]->getId()
- !== $template_type->getId()
- ) {
- IssueBuffer::maybeAdd(
- new InvalidTemplateParam(
- 'Cannot extend a strictly-enforced parent template param '
- . $template_name
- . ' with constraint ' . $template_type->getId()
- . ' with a child template param ' . $t->param_name
- . ' with different constraint '
- . $storage->template_types[$t->param_name][$storage->name]->getId(),
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
- }
-
- if (!$template_type->isMixed()) {
- $template_type_copy = clone $template_type;
-
- $template_result = new TemplateResult(
- $previous_extended ?: [],
- []
- );
-
- $template_type_copy = TemplateStandinTypeReplacer::replace(
- $template_type_copy,
- $template_result,
- $codebase,
- null,
- $extended_type,
- null,
- null
- );
-
- if (!UnionTypeComparator::isContainedBy($codebase, $extended_type, $template_type_copy)) {
- IssueBuffer::maybeAdd(
- new InvalidTemplateParam(
- 'Extended template param ' . $template_name
- . ' expects type ' . $template_type_copy->getId()
- . ', type ' . $extended_type->getId() . ' given',
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- } else {
- $previous_extended[$template_name] = [
- $declaring_class => $extended_type
- ];
- }
- } else {
- $previous_extended[$template_name] = [
- $declaring_class => $extended_type
- ];
- }
- }
-
- $i++;
- }
- }
- }
-
- /**
- * @param PhpParser\Node\Stmt\Class_|PhpParser\Node\Stmt\Enum_ $class
- */
- private function checkImplementedInterfaces(
- Context $class_context,
- PhpParser\Node\Stmt $class,
- Codebase $codebase,
- string $fq_class_name,
- ClassLikeStorage $storage
- ): bool {
- $classlike_storage_provider = $codebase->classlike_storage_provider;
-
- foreach ($class->implements as $interface_name) {
- $fq_interface_name = self::getFQCLNFromNameObject(
- $interface_name,
- $this->source->getAliases()
- );
-
- $fq_interface_name_lc = strtolower($fq_interface_name);
-
- $codebase->analyzer->addNodeReference(
- $this->getFilePath(),
- $interface_name,
- $codebase->classlikes->interfaceExists($fq_interface_name)
- ? $fq_interface_name
- : '*'
- . ($interface_name instanceof PhpParser\Node\Name\FullyQualified
- ? '\\'
- : $this->getNamespace() . '-')
- . implode('\\', $interface_name->parts)
- );
-
- $interface_location = new CodeLocation($this, $interface_name);
-
- if (self::checkFullyQualifiedClassLikeName(
- $this,
- $fq_interface_name,
- $interface_location,
- null,
- null,
- $this->getSuppressedIssues()
- ) === false) {
- return false;
- }
-
- if ($codebase->store_node_types && $fq_class_name) {
- $bounds = $interface_location->getSelectionBounds();
-
- $codebase->analyzer->addOffsetReference(
- $this->getFilePath(),
- $bounds[0],
- $bounds[1],
- $fq_interface_name
- );
- }
-
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $this,
- $interface_name,
- $fq_interface_name,
- null
- );
-
- try {
- $interface_storage = $classlike_storage_provider->get($fq_interface_name);
- } catch (InvalidArgumentException $e) {
- return false;
- }
-
- $code_location = new CodeLocation(
- $this,
- $interface_name,
- $class_context->include_location,
- true
- );
-
- if (!$interface_storage->is_interface) {
- IssueBuffer::maybeAdd(
- new UndefinedInterface(
- $fq_interface_name . ' is not an interface',
- $code_location,
- $fq_interface_name
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if (isset($storage->template_type_implements_count[$fq_interface_name_lc])) {
- $this->checkTemplateParams(
- $codebase,
- $storage,
- $interface_storage,
- $code_location,
- $storage->template_type_implements_count[$fq_interface_name_lc]
- );
- }
- }
-
- foreach ($storage->class_implements as $fq_interface_name_lc => $fq_interface_name) {
- try {
- $interface_storage = $classlike_storage_provider->get($fq_interface_name_lc);
- } catch (InvalidArgumentException $e) {
- return false;
- }
-
- $code_location = new CodeLocation(
- $this,
- $class->name ?? $class,
- $class_context->include_location,
- true
- );
-
- if ($fq_interface_name_lc === 'traversable'
- && !$storage->abstract
- && !isset($storage->class_implements['iteratoraggregate'])
- && !isset($storage->class_implements['iterator'])
- && !isset($storage->parent_classes['pdostatement'])
- && !isset($storage->parent_classes['ds\collection'])
- && !isset($storage->parent_classes['domnodelist'])
- && !isset($storage->parent_classes['dateperiod'])
- ) {
- IssueBuffer::maybeAdd(
- new InvalidTraversableImplementation(
- 'Traversable should be implemented by implementing IteratorAggregate or Iterator',
- $code_location,
- $fq_class_name
- )
- );
- }
-
- if ($interface_storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedInterface(
- $fq_interface_name . ' is marked deprecated',
- $code_location,
- $fq_interface_name
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($interface_storage->external_mutation_free
- && !$storage->external_mutation_free
- ) {
- IssueBuffer::maybeAdd(
- new MissingImmutableAnnotation(
- $fq_interface_name . ' is marked @psalm-immutable, but '
- . $fq_class_name . ' is not marked @psalm-immutable',
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- foreach ($interface_storage->methods as $interface_method_name_lc => $interface_method_storage) {
- if ($interface_method_storage->visibility === self::VISIBILITY_PUBLIC) {
- $implementer_declaring_method_id = $codebase->methods->getDeclaringMethodId(
- new MethodIdentifier(
- $this->fq_class_name,
- $interface_method_name_lc
- )
- );
-
- $implementer_method_storage = null;
- $implementer_classlike_storage = null;
-
- if ($implementer_declaring_method_id) {
- $implementer_fq_class_name = $implementer_declaring_method_id->fq_class_name;
- $implementer_method_storage = $codebase->methods->getStorage(
- $implementer_declaring_method_id
- );
- $implementer_classlike_storage = $classlike_storage_provider->get(
- $implementer_fq_class_name
- );
- }
-
- if ($storage->is_enum) {
- if ($interface_method_name_lc === 'cases') {
- continue;
- }
- if ($storage->enum_type
- && in_array($interface_method_name_lc, ['from', 'tryfrom'], true)
- ) {
- continue;
- }
- }
-
- if (!$implementer_method_storage) {
- IssueBuffer::maybeAdd(
- new UnimplementedInterfaceMethod(
- 'Method ' . $interface_method_name_lc . ' is not defined on class ' .
- $storage->name,
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- return true;
- }
-
- $implementer_appearing_method_id = $codebase->methods->getAppearingMethodId(
- new MethodIdentifier(
- $this->fq_class_name,
- $interface_method_name_lc
- )
- );
-
- $implementer_visibility = $implementer_method_storage->visibility;
-
- if ($implementer_appearing_method_id
- && $implementer_appearing_method_id !== $implementer_declaring_method_id
- ) {
- $appearing_fq_class_name = $implementer_appearing_method_id->fq_class_name;
- $appearing_method_name = $implementer_appearing_method_id->method_name;
-
- $appearing_class_storage = $classlike_storage_provider->get(
- $appearing_fq_class_name
- );
-
- if (isset($appearing_class_storage->trait_visibility_map[$appearing_method_name])) {
- $implementer_visibility
- = $appearing_class_storage->trait_visibility_map[$appearing_method_name];
- }
- }
-
- if ($implementer_visibility !== self::VISIBILITY_PUBLIC) {
- IssueBuffer::maybeAdd(
- new InaccessibleMethod(
- 'Interface-defined method ' . $implementer_method_storage->cased_name
- . ' must be public in ' . $storage->name,
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- return true;
- }
-
- if ($interface_method_storage->is_static && !$implementer_method_storage->is_static) {
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Method ' . $implementer_method_storage->cased_name
- . ' should be static like '
- . $storage->name . '::' . $interface_method_storage->cased_name,
- $code_location
- ),
- $implementer_method_storage->suppressed_issues
- );
-
- return true;
- }
-
- if ($storage->abstract && $implementer_method_storage === $interface_method_storage) {
- continue;
- }
-
- MethodComparator::compare(
- $codebase,
- null,
- $implementer_classlike_storage ?? $storage,
- $interface_storage,
- $implementer_method_storage,
- $interface_method_storage,
- $this->fq_class_name,
- $implementer_visibility,
- $code_location,
- $implementer_method_storage->suppressed_issues,
- false
- );
- }
- }
- }
-
- return true;
- }
-
- private function checkParentClass(
- Class_ $class,
- PhpParser\Node\Name $extended_class,
- string $fq_class_name,
- string $parent_fq_class_name,
- ClassLikeStorage $storage,
- Codebase $codebase,
- ?Context $class_context
- ): void {
- $classlike_storage_provider = $codebase->classlike_storage_provider;
-
- if (!$parent_fq_class_name) {
- throw new UnexpectedValueException('Parent class should be filled in for ' . $fq_class_name);
- }
-
- $parent_reference_location = new CodeLocation($this, $extended_class);
-
- if (self::checkFullyQualifiedClassLikeName(
- $this->getSource(),
- $parent_fq_class_name,
- $parent_reference_location,
- null,
- null,
- $storage->suppressed_issues + $this->getSuppressedIssues()
- ) === false) {
- return;
- }
-
- if ($codebase->alter_code && $codebase->classes_to_move) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $this,
- $extended_class,
- $parent_fq_class_name,
- null
- );
- }
-
- try {
- $parent_class_storage = $classlike_storage_provider->get($parent_fq_class_name);
-
- $code_location = new CodeLocation(
- $this,
- $extended_class,
- $class_context->include_location ?? null,
- true
- );
-
- if ($parent_class_storage->is_trait || $parent_class_storage->is_interface) {
- IssueBuffer::maybeAdd(
- new UndefinedClass(
- $parent_fq_class_name . ' is not a class',
- $code_location,
- $parent_fq_class_name . ' as class'
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($parent_class_storage->final) {
- IssueBuffer::maybeAdd(
- new InvalidExtendClass(
- 'Class ' . $fq_class_name . ' may not inherit from final class ' . $parent_fq_class_name,
- $code_location,
- $fq_class_name
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($parent_class_storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- $parent_fq_class_name . ' is marked deprecated',
- $code_location,
- $parent_fq_class_name
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if (!NamespaceAnalyzer::isWithinAny($fq_class_name, $parent_class_storage->internal)) {
- IssueBuffer::maybeAdd(
- new InternalClass(
- $parent_fq_class_name . ' is internal to '
- . InternalClass::listToPhrase($parent_class_storage->internal)
- . ' but called from ' . $fq_class_name,
- $code_location,
- $parent_fq_class_name
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($parent_class_storage->external_mutation_free
- && !$storage->external_mutation_free
- ) {
- IssueBuffer::maybeAdd(
- new MissingImmutableAnnotation(
- $parent_fq_class_name . ' is marked @psalm-immutable, but '
- . $fq_class_name . ' is not marked @psalm-immutable',
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($storage->mutation_free
- && !$parent_class_storage->mutation_free
- ) {
- IssueBuffer::maybeAdd(
- new MutableDependency(
- $fq_class_name . ' is marked @psalm-immutable but ' . $parent_fq_class_name . ' is not',
- $code_location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
-
- if ($codebase->store_node_types) {
- $codebase->analyzer->addNodeReference(
- $this->getFilePath(),
- $extended_class,
- $codebase->classlikes->classExists($parent_fq_class_name)
- ? $parent_fq_class_name
- : '*'
- . ($extended_class instanceof PhpParser\Node\Name\FullyQualified
- ? '\\'
- : $this->getNamespace() . '-')
- . implode('\\', $extended_class->parts)
- );
- }
-
- if ($storage->template_extended_count !== null || $parent_class_storage->enforce_template_inheritance) {
- $code_location = new CodeLocation(
- $this,
- $class->name ?: $class,
- $class_context->include_location ?? null,
- true
- );
-
- $this->checkTemplateParams(
- $codebase,
- $storage,
- $parent_class_storage,
- $code_location,
- $storage->template_extended_count ?? 0
- );
- }
- } catch (InvalidArgumentException $e) {
- // do nothing
- }
- }
-
- private function checkEnum(): void
- {
- $storage = $this->storage;
-
- $seen_values = [];
- foreach ($storage->enum_cases as $case_storage) {
- if ($case_storage->value !== null && $storage->enum_type === null) {
- if (IssueBuffer::accepts(
- new InvalidEnumCaseValue(
- 'Case of a non-backed enum should not have a value',
- $case_storage->stmt_location,
- $storage->name
- )
- )) {
- }
- } elseif ($case_storage->value === null && $storage->enum_type !== null) {
- if (IssueBuffer::accepts(
- new InvalidEnumCaseValue(
- 'Case of a backed enum should have a value',
- $case_storage->stmt_location,
- $storage->name
- )
- )) {
- }
- } elseif ($case_storage->value !== null && $storage->enum_type !== null) {
- if ((is_int($case_storage->value) && $storage->enum_type === 'string')
- || (is_string($case_storage->value) && $storage->enum_type === 'int')
- ) {
- if (IssueBuffer::accepts(
- new InvalidEnumCaseValue(
- 'Enum case value type should be ' . $storage->enum_type,
- $case_storage->stmt_location,
- $storage->name
- )
- )) {
- }
- }
- }
-
- if ($case_storage->value !== null) {
- if (in_array($case_storage->value, $seen_values, true)) {
- if (IssueBuffer::accepts(
- new DuplicateEnumCaseValue(
- 'Enum case values should be unique',
- $case_storage->stmt_location,
- $storage->name
- )
- )) {
- }
- } else {
- $seen_values[] = $case_storage->value;
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php
deleted file mode 100644
index 0b4934e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php
+++ /dev/null
@@ -1,648 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Issue\InaccessibleProperty;
-use Psalm\Issue\InvalidClass;
-use Psalm\Issue\MissingDependency;
-use Psalm\Issue\ReservedWord;
-use Psalm\Issue\UndefinedAttributeClass;
-use Psalm\Issue\UndefinedClass;
-use Psalm\Issue\UndefinedDocblockClass;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AfterClassLikeExistenceCheckEvent;
-use Psalm\StatementsSource;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_pop;
-use function explode;
-use function gettype;
-use function implode;
-use function in_array;
-use function preg_match;
-use function preg_replace;
-use function strtolower;
-
-/**
- * @internal
- */
-abstract class ClassLikeAnalyzer extends SourceAnalyzer
-{
- public const VISIBILITY_PUBLIC = 1;
- public const VISIBILITY_PROTECTED = 2;
- public const VISIBILITY_PRIVATE = 3;
-
- public const SPECIAL_TYPES = [
- 'int' => 'int',
- 'string' => 'string',
- 'float' => 'float',
- 'bool' => 'bool',
- 'false' => 'false',
- 'object' => 'object',
- 'empty' => 'empty',
- 'callable' => 'callable',
- 'array' => 'array',
- 'iterable' => 'iterable',
- 'null' => 'null',
- 'mixed' => 'mixed',
- ];
-
- public const GETTYPE_TYPES = [
- 'boolean' => true,
- 'integer' => true,
- 'double' => true,
- 'string' => true,
- 'array' => true,
- 'object' => true,
- 'resource' => true,
- 'resource (closed)' => true,
- 'NULL' => true,
- 'unknown type' => true,
- ];
-
- /**
- * @var PhpParser\Node\Stmt\ClassLike
- */
- protected $class;
-
- /** @var FileAnalyzer */
- public $file_analyzer;
-
- /**
- * @var string
- */
- protected $fq_class_name;
-
- /**
- * The parent class
- *
- * @var string|null
- */
- protected $parent_fq_class_name;
-
- /**
- * @var PhpParser\Node\Stmt[]
- */
- protected $leftover_stmts = [];
-
- /** @var ClassLikeStorage */
- protected $storage;
-
- public function __construct(PhpParser\Node\Stmt\ClassLike $class, SourceAnalyzer $source, string $fq_class_name)
- {
- $this->class = $class;
- $this->source = $source;
- $this->file_analyzer = $source->getFileAnalyzer();
- $this->fq_class_name = $fq_class_name;
- $codebase = $source->getCodebase();
- $this->storage = $codebase->classlike_storage_provider->get($fq_class_name);
- }
-
- public function __destruct()
- {
- unset($this->source);
- unset($this->file_analyzer);
- }
-
- public function getMethodMutations(
- string $method_name,
- Context $context
- ): void {
- $project_analyzer = $this->getFileAnalyzer()->project_analyzer;
- $codebase = $project_analyzer->getCodebase();
-
- foreach ($this->class->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod &&
- strtolower($stmt->name->name) === strtolower($method_name)
- ) {
- $method_analyzer = new MethodAnalyzer($stmt, $this);
-
- $method_analyzer->analyze($context, new NodeDataProvider(), null, true);
-
- $context->clauses = [];
- } elseif ($stmt instanceof PhpParser\Node\Stmt\TraitUse) {
- foreach ($stmt->traits as $trait) {
- $fq_trait_name = self::getFQCLNFromNameObject(
- $trait,
- $this->source->getAliases()
- );
-
- $trait_file_analyzer = $project_analyzer->getFileAnalyzerForClassLike($fq_trait_name);
- $trait_node = $codebase->classlikes->getTraitNode($fq_trait_name);
- $trait_storage = $codebase->classlike_storage_provider->get($fq_trait_name);
- $trait_aliases = $trait_storage->aliases;
-
- if ($trait_aliases === null) {
- continue;
- }
-
- $trait_analyzer = new TraitAnalyzer(
- $trait_node,
- $trait_file_analyzer,
- $fq_trait_name,
- $trait_aliases
- );
-
- foreach ($trait_node->stmts as $trait_stmt) {
- if ($trait_stmt instanceof PhpParser\Node\Stmt\ClassMethod &&
- strtolower($trait_stmt->name->name) === strtolower($method_name)
- ) {
- $method_analyzer = new MethodAnalyzer($trait_stmt, $trait_analyzer);
-
- $actual_method_id = $method_analyzer->getMethodId();
-
- if ($context->self && $context->self !== $this->fq_class_name) {
- $analyzed_method_id = $method_analyzer->getMethodId($context->self);
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($analyzed_method_id);
-
- if ((string) $actual_method_id !== (string) $declaring_method_id) {
- break;
- }
- }
-
- $method_analyzer->analyze(
- $context,
- new NodeDataProvider(),
- null,
- true
- );
- }
- }
-
- $trait_file_analyzer->clearSourceBeforeDestruction();
- }
- }
- }
- }
-
- public function getFunctionLikeAnalyzer(string $method_name): ?MethodAnalyzer
- {
- foreach ($this->class->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod &&
- strtolower($stmt->name->name) === strtolower($method_name)
- ) {
- return new MethodAnalyzer($stmt, $this);
- }
- }
-
- return null;
- }
-
- /**
- * @param array<string> $suppressed_issues
- */
- public static function checkFullyQualifiedClassLikeName(
- StatementsSource $statements_source,
- string $fq_class_name,
- CodeLocation $code_location,
- ?string $calling_fq_class_name,
- ?string $calling_method_id,
- array $suppressed_issues,
- ?ClassLikeNameOptions $options = null
- ): ?bool {
- if ($options === null) {
- $options = new ClassLikeNameOptions();
- }
-
- $codebase = $statements_source->getCodebase();
- if ($fq_class_name === '') {
- if (IssueBuffer::accepts(
- new UndefinedClass(
- 'Class or interface <empty string> does not exist',
- $code_location,
- 'empty string'
- ),
- $suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
- $fq_class_name = preg_replace('/^\\\/', '', $fq_class_name);
-
- if (in_array($fq_class_name, ['callable', 'iterable', 'self', 'static', 'parent'], true)) {
- return true;
- }
-
- if (preg_match(
- '/(^|\\\)(int|float|bool|string|void|null|false|true|object|mixed)$/i',
- $fq_class_name
- ) || strtolower($fq_class_name) === 'resource'
- ) {
- $class_name_parts = explode('\\', $fq_class_name);
- $class_name = array_pop($class_name_parts);
-
- IssueBuffer::maybeAdd(
- new ReservedWord(
- $class_name . ' is a reserved word',
- $code_location,
- $class_name
- ),
- $suppressed_issues
- );
-
- return null;
- }
-
- $class_exists = $codebase->classlikes->classExists(
- $fq_class_name,
- !$options->inferred ? $code_location : null,
- $calling_fq_class_name,
- $calling_method_id
- );
-
- $interface_exists = $codebase->classlikes->interfaceExists(
- $fq_class_name,
- !$options->inferred ? $code_location : null,
- $calling_fq_class_name,
- $calling_method_id
- );
-
- $enum_exists = $codebase->classlikes->enumExists(
- $fq_class_name,
- !$options->inferred ? $code_location : null,
- $calling_fq_class_name,
- $calling_method_id
- );
-
- if (!$class_exists
- && !($interface_exists && $options->allow_interface)
- && !($enum_exists && $options->allow_enum)
- ) {
- if (!$options->allow_trait || !$codebase->classlikes->traitExists($fq_class_name, $code_location)) {
- if ($options->from_docblock) {
- if (IssueBuffer::accepts(
- new UndefinedDocblockClass(
- 'Docblock-defined class, interface or enum named ' . $fq_class_name . ' does not exist',
- $code_location,
- $fq_class_name
- ),
- $suppressed_issues
- )) {
- return false;
- }
- } elseif ($options->from_attribute) {
- if (IssueBuffer::accepts(
- new UndefinedAttributeClass(
- 'Attribute class ' . $fq_class_name . ' does not exist',
- $code_location,
- $fq_class_name
- ),
- $suppressed_issues
- )) {
- return false;
- }
- } else {
- if (IssueBuffer::accepts(
- new UndefinedClass(
- 'Class, interface or enum named ' . $fq_class_name . ' does not exist',
- $code_location,
- $fq_class_name
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
- }
-
- return null;
- }
-
- $aliased_name = $codebase->classlikes->getUnAliasedName(
- $fq_class_name
- );
-
- try {
- $class_storage = $codebase->classlike_storage_provider->get($aliased_name);
- } catch (InvalidArgumentException $e) {
- if (!$options->inferred) {
- throw $e;
- }
-
- return null;
- }
-
- foreach ($class_storage->invalid_dependencies as $dependency_class_name) {
- // if the implemented/extended class is stubbed, it may not yet have
- // been hydrated
- if ($codebase->classlike_storage_provider->has($dependency_class_name)) {
- continue;
- }
-
- if (IssueBuffer::accepts(
- new MissingDependency(
- $fq_class_name . ' depends on class or interface '
- . $dependency_class_name . ' that does not exist',
- $code_location,
- $fq_class_name
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
-
- if (!$options->inferred) {
- if (($class_exists && !$codebase->classHasCorrectCasing($fq_class_name))
- || ($interface_exists && !$codebase->interfaceHasCorrectCasing($fq_class_name))
- || ($enum_exists && !$codebase->classlikes->enumHasCorrectCasing($fq_class_name))
- ) {
- if ($codebase->classlikes->isUserDefined(strtolower($aliased_name))) {
- IssueBuffer::maybeAdd(
- new InvalidClass(
- 'Class, interface or enum ' . $fq_class_name . ' has wrong casing',
- $code_location,
- $fq_class_name
- ),
- $suppressed_issues
- );
- }
- }
- }
-
- if (!$options->inferred) {
- $event = new AfterClassLikeExistenceCheckEvent(
- $fq_class_name,
- $code_location,
- $statements_source,
- $codebase,
- []
- );
-
- $codebase->config->eventDispatcher->dispatchAfterClassLikeExistenceCheck($event);
-
- $file_manipulations = $event->getFileReplacements();
- if ($file_manipulations) {
- FileManipulationBuffer::add($code_location->file_path, $file_manipulations);
- }
- }
-
- return true;
- }
-
- /**
- * Gets the fully-qualified class name from a Name object
- *
- *
- */
- public static function getFQCLNFromNameObject(
- PhpParser\Node\Name $class_name,
- Aliases $aliases
- ): string {
- /** @var string|null */
- $resolved_name = $class_name->getAttribute('resolvedName');
-
- if ($resolved_name) {
- return $resolved_name;
- }
-
- if ($class_name instanceof PhpParser\Node\Name\FullyQualified) {
- return implode('\\', $class_name->parts);
- }
-
- if (in_array($class_name->parts[0], ['self', 'static', 'parent'], true)) {
- return $class_name->parts[0];
- }
-
- return Type::getFQCLNFromString(
- implode('\\', $class_name->parts),
- $aliases
- );
- }
-
- /**
- * @return array<lowercase-string, string>
- */
- public function getAliasedClassesFlipped(): array
- {
- if ($this->source instanceof NamespaceAnalyzer || $this->source instanceof FileAnalyzer) {
- return $this->source->getAliasedClassesFlipped();
- }
-
- return [];
- }
-
- /**
- * @return array<string, string>
- */
- public function getAliasedClassesFlippedReplaceable(): array
- {
- if ($this->source instanceof NamespaceAnalyzer || $this->source instanceof FileAnalyzer) {
- return $this->source->getAliasedClassesFlippedReplaceable();
- }
-
- return [];
- }
-
- public function getFQCLN(): string
- {
- return $this->fq_class_name;
- }
-
- public function getClassName(): ?string
- {
- return $this->class->name->name ?? null;
- }
-
- /**
- * @return array<string, array<string, Union>>|null
- */
- public function getTemplateTypeMap(): ?array
- {
- return $this->storage->template_types;
- }
-
- public function getParentFQCLN(): ?string
- {
- return $this->parent_fq_class_name;
- }
-
- public function isStatic(): bool
- {
- return false;
- }
-
- /**
- * Gets the Psalm type from a particular value
- *
- * @param mixed $value
- *
- */
- public static function getTypeFromValue($value): Union
- {
- switch (gettype($value)) {
- case 'boolean':
- if ($value) {
- return Type::getTrue();
- }
-
- return Type::getFalse();
-
- case 'integer':
- return Type::getInt(false, $value);
-
- case 'double':
- return Type::getFloat($value);
-
- case 'string':
- return Type::getString($value);
-
- case 'array':
- return Type::getArray();
-
- case 'NULL':
- return Type::getNull();
-
- default:
- return Type::getMixed();
- }
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- public static function checkPropertyVisibility(
- string $property_id,
- Context $context,
- SourceAnalyzer $source,
- CodeLocation $code_location,
- array $suppressed_issues,
- bool $emit_issues = true
- ): ?bool {
- [$fq_class_name, $property_name] = explode('::$', $property_id);
-
- $codebase = $source->getCodebase();
-
- if ($codebase->properties->property_visibility_provider->has($fq_class_name)) {
- $property_visible = $codebase->properties->property_visibility_provider->isPropertyVisible(
- $source,
- $fq_class_name,
- $property_name,
- true,
- $context,
- $code_location
- );
-
- if ($property_visible !== null) {
- return $property_visible;
- }
- }
-
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $property_id,
- true
- );
- $appearing_property_class = $codebase->properties->getAppearingClassForProperty(
- $property_id,
- true
- );
-
- if (!$declaring_property_class || !$appearing_property_class) {
- throw new UnexpectedValueException(
- 'Appearing/Declaring classes are not defined for ' . $property_id
- );
- }
-
- // if the calling class is the same, we know the property exists, so it must be visible
- if ($appearing_property_class === $context->self) {
- return $emit_issues ? null : true;
- }
-
- if ($source->getSource() instanceof TraitAnalyzer
- && strtolower($declaring_property_class) === strtolower((string) $source->getFQCLN())
- ) {
- return $emit_issues ? null : true;
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
-
- if (!isset($class_storage->properties[$property_name])) {
- throw new UnexpectedValueException('$storage should not be null for ' . $property_id);
- }
-
- $storage = $class_storage->properties[$property_name];
-
- switch ($storage->visibility) {
- case self::VISIBILITY_PUBLIC:
- return $emit_issues ? null : true;
-
- case self::VISIBILITY_PRIVATE:
- if ($emit_issues && IssueBuffer::accepts(
- new InaccessibleProperty(
- 'Cannot access private property ' . $property_id . ' from context ' . $context->self,
- $code_location
- ),
- $suppressed_issues
- )) {
- // fall through
- }
-
- return null;
- case self::VISIBILITY_PROTECTED:
- if (!$context->self) {
- if ($emit_issues && IssueBuffer::accepts(
- new InaccessibleProperty(
- 'Cannot access protected property ' . $property_id,
- $code_location
- ),
- $suppressed_issues
- )) {
- // fall through
- }
-
- return null;
- }
-
- if ($codebase->classExtends($appearing_property_class, $context->self)) {
- return $emit_issues ? null : true;
- }
-
- if (!$codebase->classExtends($context->self, $appearing_property_class)) {
- if ($emit_issues && IssueBuffer::accepts(
- new InaccessibleProperty(
- 'Cannot access protected property ' . $property_id . ' from context ' . $context->self,
- $code_location
- ),
- $suppressed_issues
- )) {
- // fall through
- }
-
- return null;
- }
- }
-
- return $emit_issues ? null : true;
- }
-
- /**
- * @return array<string, string>
- */
- public static function getClassesForFile(Codebase $codebase, string $file_path): array
- {
- try {
- return $codebase->file_storage_provider->get($file_path)->classlikes_in_file;
- } catch (InvalidArgumentException $e) {
- return [];
- }
- }
-
- public function getFileAnalyzer(): FileAnalyzer
- {
- return $this->file_analyzer;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php
deleted file mode 100644
index dff5056..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeNameOptions.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-class ClassLikeNameOptions
-{
- /** @var bool */
- public $inferred;
-
- /** @var bool */
- public $allow_trait;
-
- /** @var bool */
- public $allow_interface;
-
- /** @var bool */
- public $allow_enum;
-
- /** @var bool */
- public $from_docblock;
-
- /** @var bool */
- public $from_attribute;
-
- public function __construct(
- bool $inferred = false,
- bool $allow_trait = false,
- bool $allow_interface = true,
- bool $allow_enum = true,
- bool $from_docblock = false,
- bool $from_attribute = false
- ) {
- $this->inferred = $inferred;
- $this->allow_trait = $allow_trait;
- $this->allow_interface = $allow_interface;
- $this->allow_enum = $allow_enum;
- $this->from_docblock = $from_docblock;
- $this->from_attribute = $from_attribute;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php
deleted file mode 100644
index 1b36c9b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClosureAnalyzer.php
+++ /dev/null
@@ -1,343 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\PhpVisitor\ShortClosureVisitor;
-use Psalm\Issue\DuplicateParam;
-use Psalm\Issue\PossiblyUndefinedVariable;
-use Psalm\Issue\UndefinedVariable;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function array_map;
-use function in_array;
-use function is_string;
-use function preg_match;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- * @extends FunctionLikeAnalyzer<PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\ArrowFunction>
- */
-class ClosureAnalyzer extends FunctionLikeAnalyzer
-{
- /**
- * @param PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\ArrowFunction $function
- */
- public function __construct(PhpParser\Node\FunctionLike $function, SourceAnalyzer $source)
- {
- $codebase = $source->getCodebase();
-
- $function_id = strtolower($source->getFilePath())
- . ':' . $function->getLine()
- . ':' . (int)$function->getAttribute('startFilePos')
- . ':-:closure';
-
- $storage = $codebase->getClosureStorage($source->getFilePath(), $function_id);
-
- parent::__construct($function, $source, $storage);
- }
-
- public function getTemplateTypeMap(): ?array
- {
- return $this->source->getTemplateTypeMap();
- }
-
- /**
- * @return non-empty-lowercase-string
- */
- public function getClosureId(): string
- {
- return strtolower($this->getFilePath())
- . ':' . $this->function->getLine()
- . ':' . (int)$this->function->getAttribute('startFilePos')
- . ':-:closure';
- }
-
- /**
- * @param PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\ArrowFunction $stmt
- */
- public static function analyzeExpression(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\FunctionLike $stmt,
- Context $context
- ): bool {
- $closure_analyzer = new ClosureAnalyzer($stmt, $statements_analyzer);
-
- if ($stmt instanceof PhpParser\Node\Expr\Closure
- && self::analyzeClosureUses($statements_analyzer, $stmt, $context) === false
- ) {
- return false;
- }
-
- $use_context = new Context($context->self);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$statements_analyzer->isStatic()) {
- if ($context->collect_mutations &&
- $context->self &&
- $codebase->classExtends(
- $context->self,
- (string)$statements_analyzer->getFQCLN()
- )
- ) {
- /** @psalm-suppress PossiblyUndefinedStringArrayOffset */
- $use_context->vars_in_scope['$this'] = clone $context->vars_in_scope['$this'];
- } elseif ($context->self) {
- $this_atomic = new TNamedObject($context->self);
- $this_atomic->was_static = true;
-
- $use_context->vars_in_scope['$this'] = new Union([$this_atomic]);
- }
- }
-
- foreach ($context->vars_in_scope as $var => $type) {
- if (strpos($var, '$this->') === 0) {
- $use_context->vars_in_scope[$var] = clone $type;
- }
- }
-
- if ($context->self) {
- $self_class_storage = $codebase->classlike_storage_provider->get($context->self);
-
- ClassAnalyzer::addContextProperties(
- $statements_analyzer,
- $self_class_storage,
- $use_context,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
- }
-
- foreach ($context->vars_possibly_in_scope as $var => $_) {
- if (strpos($var, '$this->') === 0) {
- $use_context->vars_possibly_in_scope[$var] = true;
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Closure) {
- foreach ($stmt->uses as $use) {
- if (!is_string($use->var->name)) {
- continue;
- }
-
- $use_var_id = '$' . $use->var->name;
-
- // insert the ref into the current context if passed by ref, as whatever we're passing
- // the closure to could execute it straight away.
- if ($use->byRef && !$context->hasVariable($use_var_id)) {
- $context->vars_in_scope[$use_var_id] = Type::getMixed();
- }
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- && $context->hasVariable($use_var_id)
- ) {
- $parent_nodes = $context->vars_in_scope[$use_var_id]->parent_nodes;
-
- foreach ($parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('closure-use', 'closure use', null),
- 'closure-use'
- );
- }
- }
-
- $use_context->vars_in_scope[$use_var_id] =
- $context->hasVariable($use_var_id) && !$use->byRef
- ? clone $context->vars_in_scope[$use_var_id]
- : Type::getMixed();
-
- if ($use->byRef) {
- $use_context->vars_in_scope[$use_var_id]->by_ref = true;
- }
-
- $use_context->vars_possibly_in_scope[$use_var_id] = true;
-
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (preg_match('/^\$' . $use->var->name . '[\[\-]/', $var_id)) {
- $use_context->vars_in_scope[$var_id] = clone $type;
- $use_context->vars_possibly_in_scope[$var_id] = true;
- }
- }
- }
- } else {
- $traverser = new PhpParser\NodeTraverser;
-
- $short_closure_visitor = new ShortClosureVisitor();
-
- $traverser->addVisitor($short_closure_visitor);
- $traverser->traverse($stmt->getStmts());
-
- foreach ($short_closure_visitor->getUsedVariables() as $use_var_id => $_) {
- if ($context->hasVariable($use_var_id)) {
- $use_context->vars_in_scope[$use_var_id] = clone $context->vars_in_scope[$use_var_id];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- $parent_nodes = $context->vars_in_scope[$use_var_id]->parent_nodes;
-
- foreach ($parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('closure-use', 'closure use', null),
- 'closure-use'
- );
- }
- }
- }
-
- $use_context->vars_possibly_in_scope[$use_var_id] = true;
- }
- }
-
- $use_context->calling_method_id = $context->calling_method_id;
-
- $closure_analyzer->analyze($use_context, $statements_analyzer->node_data, $context, false);
-
- if ($closure_analyzer->inferred_impure
- && $statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
-
- if ($closure_analyzer->inferred_has_mutation
- && $statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- }
-
- if (!$statements_analyzer->node_data->getType($stmt)) {
- $statements_analyzer->node_data->setType($stmt, Type::getClosure());
- }
-
- return true;
- }
-
- /**
- * @return false|null
- */
- public static function analyzeClosureUses(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Closure $stmt,
- Context $context
- ): ?bool {
- $param_names = array_map(
- function (PhpParser\Node\Param $p): string {
- if (!$p->var instanceof PhpParser\Node\Expr\Variable
- || !is_string($p->var->name)
- ) {
- return '';
- }
- return $p->var->name;
- },
- $stmt->params
- );
-
- foreach ($stmt->uses as $use) {
- if (!is_string($use->var->name)) {
- continue;
- }
-
- $use_var_id = '$' . $use->var->name;
-
- if (in_array($use->var->name, $param_names)) {
- if (IssueBuffer::accepts(
- new DuplicateParam(
- 'Closure use duplicates param name ' . $use_var_id,
- new CodeLocation($statements_analyzer->getSource(), $use->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
-
- if (!$context->hasVariable($use_var_id)) {
- if ($use_var_id === '$argv' || $use_var_id === '$argc') {
- continue;
- }
-
- if ($use->byRef) {
- $context->vars_in_scope[$use_var_id] = Type::getMixed();
- $context->vars_possibly_in_scope[$use_var_id] = true;
-
- if (!$statements_analyzer->hasVariable($use_var_id)) {
- $statements_analyzer->registerVariable(
- $use_var_id,
- new CodeLocation($statements_analyzer, $use->var),
- null
- );
- }
-
- return null;
- }
-
- if (!isset($context->vars_possibly_in_scope[$use_var_id])) {
- if ($context->check_variables) {
- if (IssueBuffer::accepts(
- new UndefinedVariable(
- 'Cannot find referenced variable ' . $use_var_id,
- new CodeLocation($statements_analyzer->getSource(), $use->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return null;
- }
- }
-
- $first_appearance = $statements_analyzer->getFirstAppearance($use_var_id);
-
- if ($first_appearance) {
- if (IssueBuffer::accepts(
- new PossiblyUndefinedVariable(
- 'Possibly undefined variable ' . $use_var_id . ', first seen on line ' .
- $first_appearance->getLineNumber(),
- new CodeLocation($statements_analyzer->getSource(), $use->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- continue;
- }
-
- if ($context->check_variables) {
- if (IssueBuffer::accepts(
- new UndefinedVariable(
- 'Cannot find referenced variable ' . $use_var_id,
- new CodeLocation($statements_analyzer->getSource(), $use->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- continue;
- }
- } elseif ($use->byRef) {
- $new_type = Type::getMixed();
- $new_type->parent_nodes = $context->vars_in_scope[$use_var_id]->parent_nodes;
-
- $context->remove($use_var_id);
-
- $context->vars_in_scope[$use_var_id] = $new_type;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CommentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CommentAnalyzer.php
deleted file mode 100644
index 5af3a43..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/CommentAnalyzer.php
+++ /dev/null
@@ -1,391 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\DocComment;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\FileSource;
-use Psalm\Internal\Scanner\DocblockParser;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\Internal\Scanner\VarDocblockComment;
-use Psalm\Internal\Type\TypeAlias;
-use Psalm\Internal\Type\TypeParser;
-use Psalm\Internal\Type\TypeTokenizer;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function count;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function rtrim;
-use function str_replace;
-use function strlen;
-use function substr;
-use function substr_count;
-use function trim;
-
-/**
- * @internal
- */
-class CommentAnalyzer
-{
- public const TYPE_REGEX = '(\??\\\?[\(\)A-Za-z0-9_&\<\.=,\>\[\]\-\{\}:|?\\\\]*|\$[a-zA-Z_0-9_]+)';
-
- /**
- * @param array<string, array<string, Union>>|null $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- *
- * @throws DocblockParseException if there was a problem parsing the docblock
- *
- * @return list<VarDocblockComment>
- */
- public static function getTypeFromComment(
- PhpParser\Comment\Doc $comment,
- FileSource $source,
- Aliases $aliases,
- ?array $template_type_map = null,
- ?array $type_aliases = null
- ): array {
- $parsed_docblock = DocComment::parsePreservingLength($comment);
-
- return self::arrayToDocblocks(
- $comment,
- $parsed_docblock,
- $source,
- $aliases,
- $template_type_map,
- $type_aliases
- );
- }
-
- /**
- * @param array<string, array<string, Union>>|null $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- *
- * @return list<VarDocblockComment>
- *
- * @throws DocblockParseException if there was a problem parsing the docblock
- */
- public static function arrayToDocblocks(
- PhpParser\Comment\Doc $comment,
- ParsedDocblock $parsed_docblock,
- FileSource $source,
- Aliases $aliases,
- ?array $template_type_map = null,
- ?array $type_aliases = null
- ): array {
- $var_id = null;
-
- $var_type_tokens = null;
- $original_type = null;
-
- $var_comments = [];
-
- $comment_text = $comment->getText();
-
- $var_line_number = $comment->getStartLine();
-
- if (isset($parsed_docblock->combined_tags['var'])) {
- foreach ($parsed_docblock->combined_tags['var'] as $offset => $var_line) {
- $var_line = trim($var_line);
-
- if (!$var_line) {
- continue;
- }
-
- $type_start = null;
- $type_end = null;
-
- $line_parts = self::splitDocLine($var_line);
-
- $line_number = $comment->getStartLine() + substr_count(
- $comment_text,
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- );
- $description = $parsed_docblock->description;
-
- if ($line_parts[0]) {
- $type_start = $offset;
- $type_end = $type_start + strlen($line_parts[0]);
-
- $line_parts[0] = self::sanitizeDocblockType($line_parts[0]);
-
- if ($line_parts[0] === ''
- || ($line_parts[0][0] === '$'
- && !preg_match('/^\$this(\||$)/', $line_parts[0]))
- ) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- try {
- $var_type_tokens = TypeTokenizer::getFullyQualifiedTokens(
- $line_parts[0],
- $aliases,
- $template_type_map,
- $type_aliases
- );
- } catch (TypeParseTreeException $e) {
- throw new DocblockParseException($line_parts[0] . ' is not a valid type');
- }
-
- $original_type = $line_parts[0];
-
- $var_line_number = $line_number;
-
- if (count($line_parts) > 1) {
- if ($line_parts[1][0] === '$') {
- $var_id = $line_parts[1];
- $description = trim(substr($var_line, strlen($line_parts[0]) + strlen($line_parts[1]) + 2));
- } else {
- $description = trim(substr($var_line, strlen($line_parts[0]) + 1));
- }
- $description = preg_replace('/\\n \\*\\s+/um', ' ', $description);
- }
- }
-
- if (!$var_type_tokens || !$original_type) {
- continue;
- }
-
- try {
- $defined_type = TypeParser::parseTokens(
- $var_type_tokens,
- null,
- $template_type_map ?: [],
- $type_aliases ?: []
- );
- } catch (TypeParseTreeException $e) {
- throw new DocblockParseException(
- $line_parts[0] .
- ' is not a valid type' .
- ' (from ' .
- $source->getFilePath() .
- ':' .
- $comment->getStartLine() .
- ')'
- );
- }
-
- $defined_type->setFromDocblock();
-
- $var_comment = new VarDocblockComment();
- $var_comment->type = $defined_type;
- $var_comment->var_id = $var_id;
- $var_comment->line_number = $var_line_number;
- $var_comment->type_start = $type_start;
- $var_comment->type_end = $type_end;
- $var_comment->description = $description;
-
- self::decorateVarDocblockComment($var_comment, $parsed_docblock);
-
- $var_comments[] = $var_comment;
- }
- }
-
- if (!$var_comments
- && (isset($parsed_docblock->tags['deprecated'])
- || isset($parsed_docblock->tags['internal'])
- || isset($parsed_docblock->tags['readonly'])
- || isset($parsed_docblock->tags['psalm-readonly'])
- || isset($parsed_docblock->tags['psalm-readonly-allow-private-mutation'])
- || isset($parsed_docblock->tags['psalm-allow-private-mutation'])
- || isset($parsed_docblock->tags['psalm-taint-escape'])
- || isset($parsed_docblock->tags['psalm-internal'])
- || isset($parsed_docblock->tags['psalm-suppress'])
- || $parsed_docblock->description)
- ) {
- $var_comment = new VarDocblockComment();
-
- self::decorateVarDocblockComment($var_comment, $parsed_docblock);
-
- $var_comments[] = $var_comment;
- }
-
- return $var_comments;
- }
-
- private static function decorateVarDocblockComment(
- VarDocblockComment $var_comment,
- ParsedDocblock $parsed_docblock
- ): void {
- $var_comment->deprecated = isset($parsed_docblock->tags['deprecated']);
- $var_comment->internal = isset($parsed_docblock->tags['internal']);
- $var_comment->readonly = isset($parsed_docblock->tags['readonly'])
- || isset($parsed_docblock->tags['psalm-readonly'])
- || isset($parsed_docblock->tags['psalm-readonly-allow-private-mutation']);
-
- $var_comment->allow_private_mutation
- = isset($parsed_docblock->tags['psalm-allow-private-mutation'])
- || isset($parsed_docblock->tags['psalm-readonly-allow-private-mutation']);
-
- if (!$var_comment->description) {
- $var_comment->description = $parsed_docblock->description;
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-escape'])) {
- foreach ($parsed_docblock->tags['psalm-taint-escape'] as $param) {
- $param = trim($param);
- $var_comment->removed_taints[] = $param;
- }
- }
-
- if (count($var_comment->psalm_internal = DocblockParser::handlePsalmInternal($parsed_docblock)) !== 0) {
- $var_comment->internal = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-suppress'])) {
- foreach ($parsed_docblock->tags['psalm-suppress'] as $offset => $suppress_entry) {
- foreach (DocComment::parseSuppressList($suppress_entry) as $issue_offset => $suppressed_issue) {
- $var_comment->suppressed_issues[$issue_offset + $offset] = $suppressed_issue;
- }
- }
- }
- }
-
- /**
- * @psalm-pure
- */
- public static function sanitizeDocblockType(string $docblock_type): string
- {
- $docblock_type = preg_replace('@^[ \t]*\*@m', '', $docblock_type);
- $docblock_type = preg_replace('/,\n\s+\}/', '}', $docblock_type);
- return str_replace("\n", '', $docblock_type);
- }
-
- /**
- * @throws DocblockParseException if an invalid string is found
- *
- * @return non-empty-list<string>
- *
- * @psalm-pure
- */
- public static function splitDocLine(string $return_block): array
- {
- $brackets = '';
-
- $type = '';
-
- $expects_callable_return = false;
-
- $return_block = str_replace("\t", ' ', $return_block);
-
- $quote_char = null;
- $escaped = false;
-
- for ($i = 0, $l = strlen($return_block); $i < $l; ++$i) {
- $char = $return_block[$i];
- $next_char = $i < $l - 1 ? $return_block[$i + 1] : null;
- $last_char = $i > 0 ? $return_block[$i - 1] : null;
-
- if ($quote_char) {
- if ($char === $quote_char && !$escaped) {
- $quote_char = null;
-
- $type .= $char;
-
- continue;
- }
-
- if ($char === '\\' && !$escaped && ($next_char === $quote_char || $next_char === '\\')) {
- $escaped = true;
-
- $type .= $char;
-
- continue;
- }
-
- $escaped = false;
-
- $type .= $char;
-
- continue;
- }
-
- if ($char === '"' || $char === '\'') {
- $quote_char = $char;
-
- $type .= $char;
-
- continue;
- }
-
- if ($char === ':' && $last_char === ')') {
- $expects_callable_return = true;
-
- $type .= $char;
-
- continue;
- }
-
- if ($char === '[' || $char === '{' || $char === '(' || $char === '<') {
- $brackets .= $char;
- } elseif ($char === ']' || $char === '}' || $char === ')' || $char === '>') {
- $last_bracket = substr($brackets, -1);
- $brackets = substr($brackets, 0, -1);
-
- if (($char === ']' && $last_bracket !== '[')
- || ($char === '}' && $last_bracket !== '{')
- || ($char === ')' && $last_bracket !== '(')
- || ($char === '>' && $last_bracket !== '<')
- ) {
- throw new DocblockParseException('Invalid string ' . $return_block);
- }
- } elseif ($char === ' ') {
- if ($brackets) {
- $expects_callable_return = false;
- $type .= ' ';
- continue;
- }
-
- if ($next_char === '|' || $next_char === '&') {
- $nexter_char = $i < $l - 2 ? $return_block[$i + 2] : null;
-
- if ($nexter_char === ' ') {
- ++$i;
- $type .= $next_char . ' ';
- continue;
- }
- }
-
- if ($last_char === '|' || $last_char === '&') {
- $type .= ' ';
- continue;
- }
-
- if ($next_char === ':') {
- ++$i;
- $type .= ' :';
- $expects_callable_return = true;
- continue;
- }
-
- if ($expects_callable_return) {
- $type .= ' ';
- $expects_callable_return = false;
- continue;
- }
-
- $remaining = trim(preg_replace('@^[ \t]*\* *@m', ' ', substr($return_block, $i + 1)));
-
- if ($remaining) {
- return array_merge([rtrim($type)], preg_split('/[ \s]+/', $remaining));
- }
-
- return [$type];
- }
-
- $expects_callable_return = false;
-
- $type .= $char;
- }
-
- return [$type];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/DataFlowNodeData.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/DataFlowNodeData.php
deleted file mode 100644
index a33ac96..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/DataFlowNodeData.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-/**
- * @psalm-immutable
- */
-class DataFlowNodeData
-{
- /**
- * @var int
- */
- public $line_from;
-
- /**
- * @var int
- */
- public $line_to;
-
- /**
- * @var string
- */
- public $label;
-
- /**
- * @var string
- */
- public $file_name;
-
- /**
- * @var string
- */
- public $file_path;
-
- /**
- * @var string
- */
- public $snippet;
-
- /**
- * @var int
- */
- public $from;
-
- /**
- * @var int
- */
- public $to;
-
- /**
- * @var int
- */
- public $snippet_from;
-
- /**
- * @var int
- */
- public $column_from;
-
- /**
- * @var int
- */
- public $column_to;
-
- public function __construct(
- string $label,
- int $line_from,
- int $line_to,
- string $file_name,
- string $file_path,
- string $snippet,
- int $from,
- int $to,
- int $snippet_from,
- int $column_from,
- int $column_to
- ) {
- $this->label = $label;
- $this->line_from = $line_from;
- $this->line_to = $line_to;
- $this->file_name = $file_name;
- $this->file_path = $file_path;
- $this->snippet = $snippet;
- $this->from = $from;
- $this->to = $to;
- $this->snippet_from = $snippet_from;
- $this->column_from = $column_from;
- $this->column_to = $column_to;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php
deleted file mode 100644
index 0d57d4c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FileAnalyzer.php
+++ /dev/null
@@ -1,682 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\UnpreparedAnalysisException;
-use Psalm\Internal\Codebase\Functions;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Codebase\Reflection;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\TypeAlias\LinkableTypeAlias;
-use Psalm\Internal\Type\TypeTokenizer;
-use Psalm\Issue\InvalidTypeImport;
-use Psalm\Issue\UncaughtThrowInGlobalScope;
-use Psalm\IssueBuffer;
-use Psalm\NodeTypeProvider;
-use Psalm\Plugin\EventHandler\Event\AfterFileAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\BeforeFileAnalysisEvent;
-use Psalm\Type;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_combine;
-use function array_diff_key;
-use function array_keys;
-use function count;
-use function implode;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- * @psalm-consistent-constructor
- */
-class FileAnalyzer extends SourceAnalyzer
-{
- use CanAlias;
-
- /**
- * @var string
- */
- protected $file_name;
-
- /**
- * @var string
- */
- protected $file_path;
-
- /**
- * @var string|null
- */
- protected $root_file_path;
-
- /**
- * @var string|null
- */
- protected $root_file_name;
-
- /**
- * @var array<string, bool>
- */
- private $required_file_paths = [];
-
- /**
- * @var array<string, bool>
- */
- private $parent_file_paths = [];
-
- /**
- * @var array<string>
- */
- private $suppressed_issues = [];
-
- /**
- * @var array<string, array<string, string>>
- */
- private $namespace_aliased_classes = [];
-
- /**
- * @var array<string, array<lowercase-string, string>>
- */
- private $namespace_aliased_classes_flipped = [];
-
- /**
- * @var array<string, array<string, string>>
- */
- private $namespace_aliased_classes_flipped_replaceable = [];
-
- /**
- * @var array<lowercase-string, InterfaceAnalyzer>
- */
- public $interface_analyzers_to_analyze = [];
-
- /**
- * @var array<lowercase-string, ClassAnalyzer>
- */
- public $class_analyzers_to_analyze = [];
-
- /**
- * @var null|Context
- */
- public $context;
-
- /**
- * @var ProjectAnalyzer
- */
- public $project_analyzer;
-
- /**
- * @var Codebase
- */
- public $codebase;
-
- /**
- * @var int
- */
- private $first_statement_offset = -1;
-
- /** @var ?NodeDataProvider */
- private $node_data;
-
- /** @var ?Union */
- private $return_type;
-
- public function __construct(ProjectAnalyzer $project_analyzer, string $file_path, string $file_name)
- {
- $this->source = $this;
- $this->file_path = $file_path;
- $this->file_name = $file_name;
- $this->project_analyzer = $project_analyzer;
- $this->codebase = $project_analyzer->getCodebase();
- }
-
- public function analyze(
- ?Context $file_context = null,
- ?Context $global_context = null
- ): void {
- $codebase = $this->project_analyzer->getCodebase();
-
- $file_storage = $codebase->file_storage_provider->get($this->file_path);
-
- if (!$file_storage->deep_scan && !$codebase->server_mode) {
- throw new UnpreparedAnalysisException('File ' . $this->file_path . ' has not been properly scanned');
- }
-
- if ($file_storage->has_visitor_issues) {
- return;
- }
-
- if ($file_context) {
- $this->context = $file_context;
- }
-
- if (!$this->context) {
- $this->context = new Context();
- }
-
- if ($codebase->config->useStrictTypesForFile($this->file_path)) {
- $this->context->strict_types = true;
- }
-
- $this->context->is_global = true;
- $this->context->defineGlobals();
- $this->context->collect_exceptions = $codebase->config->check_for_throws_in_global_scope;
-
- try {
- $stmts = $codebase->getStatementsForFile($this->file_path);
- } catch (PhpParser\Error $e) {
- return;
- }
-
- $event = new BeforeFileAnalysisEvent($this, $this->context, $file_storage, $codebase);
-
- $codebase->config->eventDispatcher->dispatchBeforeFileAnalysis($event);
-
- if ($codebase->alter_code) {
- foreach ($stmts as $stmt) {
- if (!$stmt instanceof PhpParser\Node\Stmt\Declare_) {
- $this->first_statement_offset = (int) $stmt->getAttribute('startFilePos');
- break;
- }
- }
- }
-
- $leftover_stmts = $this->populateCheckers($stmts);
-
- $this->node_data = new NodeDataProvider();
- $statements_analyzer = new StatementsAnalyzer($this, $this->node_data);
-
- foreach ($file_storage->docblock_issues as $docblock_issue) {
- IssueBuffer::maybeAdd($docblock_issue);
- }
-
- // if there are any leftover statements, evaluate them,
- // in turn causing the classes/interfaces be evaluated
- if ($leftover_stmts) {
- $statements_analyzer->analyze($leftover_stmts, $this->context, $global_context, true);
-
- foreach ($leftover_stmts as $leftover_stmt) {
- if ($leftover_stmt instanceof PhpParser\Node\Stmt\Return_) {
- if ($leftover_stmt->expr) {
- $this->return_type =
- $statements_analyzer->node_data->getType($leftover_stmt->expr) ?? Type::getMixed();
- } else {
- $this->return_type = Type::getVoid();
- }
-
- break;
- }
- }
- }
-
- // check any leftover interfaces not already evaluated
- foreach ($this->interface_analyzers_to_analyze as $interface_analyzer) {
- $interface_analyzer->analyze();
- }
-
- // check any leftover classes not already evaluated
-
- foreach ($this->class_analyzers_to_analyze as $class_analyzer) {
- $class_analyzer->analyze(null, $this->context);
- }
-
- if ($codebase->config->check_for_throws_in_global_scope) {
- $uncaught_throws = $statements_analyzer->getUncaughtThrows($this->context);
- foreach ($uncaught_throws as $possibly_thrown_exception => $codelocations) {
- foreach ($codelocations as $codelocation) {
- // issues are suppressed in ThrowAnalyzer, CallAnalyzer, etc.
- IssueBuffer::maybeAdd(
- new UncaughtThrowInGlobalScope(
- $possibly_thrown_exception . ' is thrown but not caught in global scope',
- $codelocation
- )
- );
- }
- }
- }
-
- // validate type imports
- if ($file_storage->type_aliases) {
- foreach ($file_storage->type_aliases as $alias) {
- if ($alias instanceof LinkableTypeAlias) {
- $location = new DocblockTypeLocation(
- $this->getSource(),
- $alias->start_offset,
- $alias->end_offset,
- $alias->line_number
- );
- $fq_source_classlike = $alias->declaring_fq_classlike_name;
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $this->getSource(),
- $fq_source_classlike,
- $location,
- null,
- null,
- $this->suppressed_issues,
- new ClassLikeNameOptions(
- true,
- false,
- true,
- true,
- true
- )
- ) === false) {
- continue;
- }
-
- $referenced_class_storage = $codebase->classlike_storage_provider->get($fq_source_classlike);
- if (!isset($referenced_class_storage->type_aliases[$alias->alias_name])) {
- IssueBuffer::maybeAdd(
- new InvalidTypeImport(
- 'Type alias ' . $alias->alias_name
- . ' imported from ' . $fq_source_classlike
- . ' is not defined on the source class',
- $location
- )
- );
- }
- }
- }
- }
-
- $event = new AfterFileAnalysisEvent($this, $this->context, $file_storage, $codebase, $stmts);
- $codebase->config->eventDispatcher->dispatchAfterFileAnalysis($event);
-
- $this->class_analyzers_to_analyze = [];
- $this->interface_analyzers_to_analyze = [];
- }
-
- /**
- * @param array<int, PhpParser\Node\Stmt> $stmts
- *
- * @return list<PhpParser\Node\Stmt>
- */
- public function populateCheckers(array $stmts): array
- {
- $leftover_stmts = [];
-
- foreach ($stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Trait_) {
- $leftover_stmts[] = $stmt;
- } elseif ($stmt instanceof PhpParser\Node\Stmt\ClassLike) {
- $this->populateClassLikeAnalyzers($stmt);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Namespace_) {
- $namespace_name = $stmt->name ? implode('\\', $stmt->name->parts) : '';
-
- $namespace_analyzer = new NamespaceAnalyzer($stmt, $this);
- $namespace_analyzer->collectAnalyzableInformation();
-
- $this->namespace_aliased_classes[$namespace_name] = $namespace_analyzer->getAliases()->uses;
- $this->namespace_aliased_classes_flipped[$namespace_name] =
- $namespace_analyzer->getAliasedClassesFlipped();
- $this->namespace_aliased_classes_flipped_replaceable[$namespace_name] =
- $namespace_analyzer->getAliasedClassesFlippedReplaceable();
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Use_) {
- $this->visitUse($stmt);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\GroupUse) {
- $this->visitGroupUse($stmt);
- } else {
- if ($stmt instanceof PhpParser\Node\Stmt\If_) {
- foreach ($stmt->stmts as $if_stmt) {
- if ($if_stmt instanceof PhpParser\Node\Stmt\ClassLike) {
- $this->populateClassLikeAnalyzers($if_stmt);
- }
- }
- }
-
- $leftover_stmts[] = $stmt;
- }
- }
-
- return $leftover_stmts;
- }
-
- private function populateClassLikeAnalyzers(PhpParser\Node\Stmt\ClassLike $stmt): void
- {
- if ($stmt instanceof PhpParser\Node\Stmt\Class_ || $stmt instanceof PhpParser\Node\Stmt\Enum_) {
- if (!$stmt->name) {
- return;
- }
-
- // this can happen when stubbing
- if (!$this->codebase->classExists($stmt->name->name)
- && !$this->codebase->classlikes->enumExists($stmt->name->name)
- ) {
- return;
- }
-
-
- $class_analyzer = new ClassAnalyzer($stmt, $this, $stmt->name->name);
-
- $fq_class_name = $class_analyzer->getFQCLN();
-
- $this->class_analyzers_to_analyze[strtolower($fq_class_name)] = $class_analyzer;
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Interface_) {
- if (!$stmt->name) {
- return;
- }
-
- // this can happen when stubbing
- if (!$this->codebase->interfaceExists($stmt->name->name)) {
- return;
- }
-
- $class_analyzer = new InterfaceAnalyzer($stmt, $this, $stmt->name->name);
-
- $fq_class_name = $class_analyzer->getFQCLN();
-
- $this->interface_analyzers_to_analyze[strtolower($fq_class_name)] = $class_analyzer;
- }
- }
-
- public function addNamespacedClassAnalyzer(string $fq_class_name, ClassAnalyzer $class_analyzer): void
- {
- $this->class_analyzers_to_analyze[strtolower($fq_class_name)] = $class_analyzer;
- }
-
- public function addNamespacedInterfaceAnalyzer(string $fq_class_name, InterfaceAnalyzer $interface_analyzer): void
- {
- $this->interface_analyzers_to_analyze[strtolower($fq_class_name)] = $interface_analyzer;
- }
-
- public function getMethodMutations(
- MethodIdentifier $method_id,
- Context $this_context,
- bool $from_project_analyzer = false
- ): void {
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
- $fq_class_name_lc = strtolower($fq_class_name);
-
- if (isset($this->class_analyzers_to_analyze[$fq_class_name_lc])) {
- $class_analyzer_to_examine = $this->class_analyzers_to_analyze[$fq_class_name_lc];
- } else {
- if (!$from_project_analyzer) {
- $this->project_analyzer->getMethodMutations(
- $method_id,
- $this_context,
- $this->getRootFilePath(),
- $this->getRootFileName()
- );
- }
-
- return;
- }
-
- $call_context = new Context($this_context->self);
- $call_context->collect_mutations = $this_context->collect_mutations;
- $call_context->collect_initializations = $this_context->collect_initializations;
- $call_context->collect_nonprivate_initializations = $this_context->collect_nonprivate_initializations;
- $call_context->initialized_methods = $this_context->initialized_methods;
- $call_context->include_location = $this_context->include_location;
- $call_context->calling_method_id = $this_context->calling_method_id;
-
- foreach ($this_context->vars_possibly_in_scope as $var => $_) {
- if (strpos($var, '$this->') === 0) {
- $call_context->vars_possibly_in_scope[$var] = true;
- }
- }
-
- foreach ($this_context->vars_in_scope as $var => $type) {
- if (strpos($var, '$this->') === 0) {
- $call_context->vars_in_scope[$var] = $type;
- }
- }
-
- if (!isset($this_context->vars_in_scope['$this'])) {
- throw new UnexpectedValueException('Should exist');
- }
-
- $call_context->vars_in_scope['$this'] = $this_context->vars_in_scope['$this'];
-
- $class_analyzer_to_examine->getMethodMutations($method_name, $call_context);
-
- foreach ($call_context->vars_possibly_in_scope as $var => $_) {
- $this_context->vars_possibly_in_scope[$var] = true;
- }
-
- foreach ($call_context->vars_in_scope as $var => $type) {
- $this_context->vars_in_scope[$var] = $type;
- }
- }
-
- public function getFunctionLikeAnalyzer(MethodIdentifier $method_id): ?MethodAnalyzer
- {
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- $fq_class_name_lc = strtolower($fq_class_name);
-
- if (!isset($this->class_analyzers_to_analyze[$fq_class_name_lc])) {
- return null;
- }
-
- $class_analyzer_to_examine = $this->class_analyzers_to_analyze[$fq_class_name_lc];
-
- return $class_analyzer_to_examine->getFunctionLikeAnalyzer($method_name);
- }
-
- public function getNamespace(): ?string
- {
- return null;
- }
-
- /**
- * @return array<lowercase-string, string>
- */
- public function getAliasedClassesFlipped(?string $namespace_name = null): array
- {
- if ($namespace_name && isset($this->namespace_aliased_classes_flipped[$namespace_name])) {
- return $this->namespace_aliased_classes_flipped[$namespace_name];
- }
-
- return $this->aliased_classes_flipped;
- }
-
- /**
- * @return array<string, string>
- */
- public function getAliasedClassesFlippedReplaceable(?string $namespace_name = null): array
- {
- if ($namespace_name && isset($this->namespace_aliased_classes_flipped_replaceable[$namespace_name])) {
- return $this->namespace_aliased_classes_flipped_replaceable[$namespace_name];
- }
-
- return $this->aliased_classes_flipped_replaceable;
- }
-
- public static function clearCache(): void
- {
- TypeTokenizer::clearCache();
- Reflection::clearCache();
- Functions::clearCache();
- IssueBuffer::clearCache();
- FileManipulationBuffer::clearCache();
- FunctionLikeAnalyzer::clearCache();
- ClassLikeStorageProvider::deleteAll();
- FileStorageProvider::deleteAll();
- FileReferenceProvider::clearCache();
- InternalCallMapHandler::clearCache();
- }
-
- public function getFileName(): string
- {
- return $this->file_name;
- }
-
- public function getFilePath(): string
- {
- return $this->file_path;
- }
-
- public function getRootFileName(): string
- {
- return $this->root_file_name ?: $this->file_name;
- }
-
- public function getRootFilePath(): string
- {
- return $this->root_file_path ?: $this->file_path;
- }
-
- public function setRootFilePath(string $file_path, string $file_name): void
- {
- $this->root_file_name = $file_name;
- $this->root_file_path = $file_path;
- }
-
- public function addRequiredFilePath(string $file_path): void
- {
- $this->required_file_paths[$file_path] = true;
- }
-
- public function addParentFilePath(string $file_path): void
- {
- $this->parent_file_paths[$file_path] = true;
- }
-
- public function hasParentFilePath(string $file_path): bool
- {
- return $this->file_path === $file_path || isset($this->parent_file_paths[$file_path]);
- }
-
- public function hasAlreadyRequiredFilePath(string $file_path): bool
- {
- return isset($this->required_file_paths[$file_path]);
- }
-
- /**
- * @return list<string>
- */
- public function getRequiredFilePaths(): array
- {
- return array_keys($this->required_file_paths);
- }
-
- /**
- * @return list<string>
- */
- public function getParentFilePaths(): array
- {
- return array_keys($this->parent_file_paths);
- }
-
- public function getRequireNesting(): int
- {
- return count($this->parent_file_paths);
- }
-
- /**
- * @return array<string>
- */
- public function getSuppressedIssues(): array
- {
- return $this->suppressed_issues;
- }
-
- /**
- * @param array<int, string> $new_issues
- */
- public function addSuppressedIssues(array $new_issues): void
- {
- if (isset($new_issues[0])) {
- $new_issues = array_combine($new_issues, $new_issues);
- }
-
- $this->suppressed_issues = $new_issues + $this->suppressed_issues;
- }
-
- /**
- * @param array<int, string> $new_issues
- */
- public function removeSuppressedIssues(array $new_issues): void
- {
- if (isset($new_issues[0])) {
- $new_issues = array_combine($new_issues, $new_issues);
- }
-
- $this->suppressed_issues = array_diff_key($this->suppressed_issues, $new_issues);
- }
-
- public function getFQCLN(): ?string
- {
- return null;
- }
-
- public function getParentFQCLN(): ?string
- {
- return null;
- }
-
- public function getClassName(): ?string
- {
- return null;
- }
-
- /**
- * @return array<string, array<string, Union>>|null
- */
- public function getTemplateTypeMap(): ?array
- {
- return null;
- }
-
- public function isStatic(): bool
- {
- return false;
- }
-
- /**
- * @psalm-mutation-free
- */
- public function getFileAnalyzer(): FileAnalyzer
- {
- return $this;
- }
-
- /**
- * @psalm-mutation-free
- */
- public function getProjectAnalyzer(): ProjectAnalyzer
- {
- return $this->project_analyzer;
- }
-
- public function getCodebase(): Codebase
- {
- return $this->codebase;
- }
-
- public function getFirstStatementOffset(): int
- {
- return $this->first_statement_offset;
- }
-
- public function getNodeTypeProvider(): NodeTypeProvider
- {
- if (!$this->node_data) {
- throw new UnexpectedValueException('There should be a node type provider');
- }
-
- return $this->node_data;
- }
-
- public function getReturnType(): ?Union
- {
- return $this->return_type;
- }
-
- public function clearSourceBeforeDestruction(): void
- {
- unset($this->source);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php
deleted file mode 100644
index c11aa47..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionAnalyzer.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\Config;
-use Psalm\Context;
-use UnexpectedValueException;
-
-use function is_string;
-use function strtolower;
-
-/**
- * @internal
- * @extends FunctionLikeAnalyzer<PhpParser\Node\Stmt\Function_>
- */
-class FunctionAnalyzer extends FunctionLikeAnalyzer
-{
- public function __construct(PhpParser\Node\Stmt\Function_ $function, SourceAnalyzer $source)
- {
- $codebase = $source->getCodebase();
-
- $file_storage_provider = $codebase->file_storage_provider;
-
- $file_storage = $file_storage_provider->get($source->getFilePath());
-
- $namespace = $source->getNamespace();
-
- $function_id = ($namespace ? strtolower($namespace) . '\\' : '') . strtolower($function->name->name);
-
- if (!isset($file_storage->functions[$function_id])) {
- throw new UnexpectedValueException(
- 'Function ' . $function_id . ' should be defined in ' . $source->getFilePath()
- );
- }
-
- $storage = $file_storage->functions[$function_id];
-
- parent::__construct($function, $source, $storage);
- }
-
- /**
- * @return non-empty-lowercase-string
- * @throws UnexpectedValueException if function is closure or arrow function.
- */
- public function getFunctionId(): string
- {
- $namespace = $this->source->getNamespace();
-
- /** @var non-empty-lowercase-string */
- return ($namespace ? strtolower($namespace) . '\\' : '') . strtolower($this->function->name->name);
- }
-
- public static function analyzeStatement(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Function_ $stmt,
- Context $context
- ): void {
- foreach ($stmt->stmts as $function_stmt) {
- if ($function_stmt instanceof PhpParser\Node\Stmt\Global_) {
- foreach ($function_stmt->vars as $var) {
- if ($var instanceof PhpParser\Node\Expr\Variable) {
- if (is_string($var->name)) {
- $var_id = '$' . $var->name;
-
- // registers variable in global context
- $context->hasVariable($var_id);
- }
- }
- }
- } elseif (!$function_stmt instanceof PhpParser\Node\Stmt\Nop) {
- break;
- }
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$codebase->register_stub_files
- && !$codebase->register_autoload_files
- ) {
- $function_name = strtolower($stmt->name->name);
-
- if ($ns = $statements_analyzer->getNamespace()) {
- $fq_function_name = strtolower($ns) . '\\' . $function_name;
- } else {
- $fq_function_name = $function_name;
- }
-
- $function_context = new Context($context->self);
- $function_context->strict_types = $context->strict_types;
- $config = Config::getInstance();
- $function_context->collect_exceptions = $config->check_for_throws_docblock;
-
- if ($function_analyzer = $statements_analyzer->getFunctionAnalyzer($fq_function_name)) {
- $function_analyzer->analyze(
- $function_context,
- $statements_analyzer->node_data,
- $context
- );
-
- if ($config->reportIssueInFile('InvalidReturnType', $statements_analyzer->getFilePath())) {
- $method_id = $function_analyzer->getId();
-
- $function_storage = $codebase->functions->getStorage(
- $statements_analyzer,
- strtolower($method_id)
- );
-
- $return_type = $function_storage->return_type;
- $return_type_location = $function_storage->return_type_location;
-
- $function_analyzer->verifyReturnType(
- $stmt->getStmts(),
- $statements_analyzer,
- $return_type,
- $statements_analyzer->getFQCLN(),
- $return_type_location,
- $function_context->has_returned
- );
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php
deleted file mode 100644
index d6f1d7e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeAnalyzer.php
+++ /dev/null
@@ -1,978 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\FunctionLike;
-
-use PhpParser;
-use PhpParser\Node\Expr\ArrowFunction;
-use PhpParser\Node\Expr\Closure;
-use PhpParser\Node\FunctionLike;
-use PhpParser\Node\Stmt\ClassMethod;
-use PhpParser\Node\Stmt\Function_;
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\InterfaceAnalyzer;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\SourceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\FileManipulation\FunctionDocblockManipulator;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ImplicitToStringCast;
-use Psalm\Issue\InvalidFalsableReturnType;
-use Psalm\Issue\InvalidNullableReturnType;
-use Psalm\Issue\InvalidParent;
-use Psalm\Issue\InvalidReturnType;
-use Psalm\Issue\InvalidToString;
-use Psalm\Issue\LessSpecificReturnType;
-use Psalm\Issue\MismatchingDocblockReturnType;
-use Psalm\Issue\MissingClosureReturnType;
-use Psalm\Issue\MissingReturnType;
-use Psalm\Issue\MixedInferredReturnType;
-use Psalm\Issue\MixedReturnTypeCoercion;
-use Psalm\Issue\MoreSpecificReturnType;
-use Psalm\IssueBuffer;
-use Psalm\StatementsSource;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-use function array_diff;
-use function array_filter;
-use function array_values;
-use function count;
-use function in_array;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class ReturnTypeAnalyzer
-{
- /**
- * @param Closure|Function_|ClassMethod|ArrowFunction $function
- * @param PhpParser\Node\Stmt[] $function_stmts
- * @param string[] $compatible_method_ids
- *
- * @return false|null
- *
- * @psalm-suppress PossiblyUnusedReturnValue unused but seems important
- */
- public static function verifyReturnType(
- FunctionLike $function,
- array $function_stmts,
- SourceAnalyzer $source,
- NodeDataProvider $type_provider,
- FunctionLikeAnalyzer $function_like_analyzer,
- ?Union $return_type = null,
- ?string $fq_class_name = null,
- ?string $static_fq_class_name = null,
- ?CodeLocation $return_type_location = null,
- array $compatible_method_ids = [],
- bool $did_explicitly_return = false,
- bool $closure_inside_call = false
- ): ?bool {
- $suppressed_issues = $function_like_analyzer->getSuppressedIssues();
- $codebase = $source->getCodebase();
- $project_analyzer = $source->getProjectAnalyzer();
-
- $function_like_storage = null;
-
- if ($source instanceof StatementsAnalyzer) {
- $function_like_storage = $function_like_analyzer->getFunctionLikeStorage($source);
- } elseif ($source instanceof ClassAnalyzer
- || $source instanceof TraitAnalyzer
- ) {
- $function_like_storage = $function_like_analyzer->getFunctionLikeStorage();
- }
-
- $cased_method_id = $function_like_analyzer->getCorrectlyCasedMethodId();
-
- if (!$function->getStmts() &&
- (
- $function instanceof ClassMethod &&
- ($source instanceof InterfaceAnalyzer || $function->isAbstract())
- )
- ) {
- if (!$return_type) {
- IssueBuffer::maybeAdd(
- new MissingReturnType(
- 'Method ' . $cased_method_id . ' does not have a return type',
- new CodeLocation($function_like_analyzer, $function->name, null, true)
- ),
- $suppressed_issues
- );
- }
-
- return null;
- }
-
- $is_to_string = $function instanceof ClassMethod && strtolower($function->name->name) === '__tostring';
-
- if ($function instanceof ClassMethod
- && strpos($function->name->name, '__') === 0
- && !$is_to_string
- && !$return_type
- ) {
- // do not check __construct, __set, __get, __call etc.
- return null;
- }
-
- if (!$return_type_location) {
- $return_type_location = new CodeLocation(
- $function_like_analyzer,
- $function instanceof Closure || $function instanceof ArrowFunction ? $function : $function->name
- );
- }
-
- $inferred_yield_types = [];
-
- $inferred_return_type_parts = ReturnTypeCollector::getReturnTypes(
- $codebase,
- $type_provider,
- $function_stmts,
- $inferred_yield_types,
- true
- );
-
- if (!$inferred_return_type_parts) {
- $did_explicitly_return = true;
- }
-
- if ((!$return_type || $return_type->from_docblock)
- && ScopeAnalyzer::getControlActions(
- $function_stmts,
- $type_provider,
- $codebase->config->exit_functions,
- []
- ) !== [ScopeAnalyzer::ACTION_END]
- && !$inferred_yield_types
- && count($inferred_return_type_parts)
- && !$did_explicitly_return
- ) {
- // only add null if we have a return statement elsewhere and it wasn't void
- foreach ($inferred_return_type_parts as $inferred_return_type_part) {
- if (!$inferred_return_type_part->isVoid()) {
- $atomic_null = new TNull();
- $atomic_null->from_docblock = true;
- $inferred_return_type_parts[] = new Union([$atomic_null]);
- break;
- }
- }
- }
-
- $control_actions = ScopeAnalyzer::getControlActions(
- $function_stmts,
- $type_provider,
- $codebase->config->exit_functions,
- [],
- false
- );
-
- $function_always_exits = $control_actions === [ScopeAnalyzer::ACTION_END];
-
- $function_returns_implicitly = (bool)array_diff(
- $control_actions,
- [ScopeAnalyzer::ACTION_END, ScopeAnalyzer::ACTION_RETURN]
- );
-
- /** @psalm-suppress PossiblyUndefinedStringArrayOffset */
- if ($return_type
- && (!$return_type->from_docblock
- || ($return_type->isNullable()
- && !$return_type->hasTemplate()
- && !$return_type->getAtomicTypes()['null']->from_docblock
- )
- )
- && !$return_type->isVoid()
- && !$inferred_yield_types
- && (!$function_like_storage || !$function_like_storage->has_yield)
- && $function_returns_implicitly
- ) {
- if (IssueBuffer::accepts(
- new InvalidReturnType(
- 'Not all code paths of ' . $cased_method_id . ' end in a return statement, return type '
- . $return_type . ' expected',
- $return_type_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
-
- if ($return_type
- && $return_type->isNever()
- && !$inferred_yield_types
- && !$function_always_exits
- ) {
- if (IssueBuffer::accepts(
- new InvalidReturnType(
- $cased_method_id . ' is not expected to return any values but it does, '
- . 'either implicitly or explicitly',
- $return_type_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
- $number_of_types = count($inferred_return_type_parts);
- // we filter TNever and TEmpty that have no bearing on the return type
- if ($number_of_types > 1) {
- $inferred_return_type_parts = array_filter(
- $inferred_return_type_parts,
- static function (Union $union_type): bool {
- return !($union_type->isNever() || $union_type->isEmpty());
- }
- );
- }
-
- $inferred_return_type_parts = array_values($inferred_return_type_parts);
-
- $inferred_return_type = $inferred_return_type_parts
- ? Type::combineUnionTypeArray($inferred_return_type_parts, $codebase)
- : Type::getVoid();
-
- if ($function_always_exits) {
- $inferred_return_type = new Union([new TNever]);
- }
-
- $inferred_yield_type = $inferred_yield_types
- ? Type::combineUnionTypeArray($inferred_yield_types, $codebase)
- : null;
-
- if ($inferred_yield_type) {
- $inferred_return_type = $inferred_yield_type;
- }
-
- $unsafe_return_type = false;
-
- // prevent any return types that do not return a value from being used in PHP typehints
- if ($codebase->alter_code
- && $inferred_return_type->isNullable()
- && !$inferred_yield_types
- ) {
- foreach ($inferred_return_type_parts as $inferred_return_type_part) {
- if ($inferred_return_type_part->isVoid()) {
- $unsafe_return_type = true;
- break;
- }
- }
- }
-
- $inferred_return_type = TypeExpander::expandUnion(
- $codebase,
- $inferred_return_type,
- $source->getFQCLN(),
- $source->getFQCLN(),
- $source->getParentFQCLN()
- );
-
- // hack until we have proper yield type collection
- if ($function_like_storage
- && $function_like_storage->has_yield
- && !$inferred_yield_type
- && !$inferred_return_type->isVoid()
- ) {
- $inferred_return_type = new Union([new TNamedObject('Generator')]);
- }
-
- if ($is_to_string) {
- $union_comparison_results = new TypeComparisonResult();
- if (!$inferred_return_type->hasMixed() &&
- !UnionTypeComparator::isContainedBy(
- $codebase,
- $inferred_return_type,
- Type::getString(),
- $inferred_return_type->ignore_nullable_issues,
- $inferred_return_type->ignore_falsable_issues,
- $union_comparison_results
- )
- ) {
- if (IssueBuffer::accepts(
- new InvalidToString(
- '__toString methods must return a string, ' . $inferred_return_type . ' returned',
- $return_type_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
-
- if ($union_comparison_results->to_string_cast) {
- IssueBuffer::maybeAdd(
- new ImplicitToStringCast(
- 'The declared return type for ' . $cased_method_id . ' expects string, ' .
- '\'' . $inferred_return_type . '\' provided with a __toString method',
- $return_type_location
- ),
- $suppressed_issues
- );
- }
-
- return null;
- }
-
- if (!$return_type) {
- if ($function instanceof Closure || $function instanceof ArrowFunction) {
- if (!$closure_inside_call || $inferred_return_type->isMixed()) {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MissingClosureReturnType'])
- && !in_array('MissingClosureReturnType', $suppressed_issues)
- ) {
- if ($inferred_return_type->hasMixed() || $inferred_return_type->isNull()) {
- return null;
- }
-
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $inferred_return_type,
- $source,
- ($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock,
- $function_like_storage
- );
-
- return null;
- }
-
- IssueBuffer::maybeAdd(
- new MissingClosureReturnType(
- 'Closure does not have a return type, expecting ' . $inferred_return_type->getId(),
- new CodeLocation($function_like_analyzer, $function, null, true)
- ),
- $suppressed_issues,
- !$inferred_return_type->hasMixed() && !$inferred_return_type->isNull()
- );
- }
-
- return null;
- }
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MissingReturnType'])
- && !in_array('MissingReturnType', $suppressed_issues)
- ) {
- if ($inferred_return_type->hasMixed() || $inferred_return_type->isNull()) {
- return null;
- }
-
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $inferred_return_type,
- $source,
- $compatible_method_ids
- || !$did_explicitly_return
- || (($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock),
- $function_like_storage
- );
-
- return null;
- }
-
- IssueBuffer::maybeAdd(
- new MissingReturnType(
- 'Method ' . $cased_method_id . ' does not have a return type' .
- (!$inferred_return_type->hasMixed() ? ', expecting ' . $inferred_return_type->getId() : ''),
- new CodeLocation($function_like_analyzer, $function->name, null, true)
- ),
- $suppressed_issues,
- !$inferred_return_type->hasMixed() && !$inferred_return_type->isNull()
- );
-
- return null;
- }
-
- $self_fq_class_name = $fq_class_name ?: $source->getFQCLN();
-
- $parent_class = null;
-
- $classlike_storage = null;
-
- if ($self_fq_class_name) {
- $classlike_storage = $codebase->classlike_storage_provider->get($self_fq_class_name);
- $parent_class = $classlike_storage->parent_class;
- }
-
- // passing it through fleshOutTypes eradicates errant $ vars
- $declared_return_type = TypeExpander::expandUnion(
- $codebase,
- $return_type,
- $self_fq_class_name,
- $static_fq_class_name,
- $parent_class,
- true,
- true,
- ($function_like_storage instanceof MethodStorage && $function_like_storage->final)
- || ($classlike_storage && $classlike_storage->final)
- );
-
- if (!$inferred_return_type_parts
- && !$inferred_return_type->isNever()
- && !$inferred_yield_types
- && (!$function_like_storage || !$function_like_storage->has_yield)
- ) {
- if ($declared_return_type->isVoid() || $declared_return_type->isNever()) {
- return null;
- }
-
- if (ScopeAnalyzer::onlyThrowsOrExits($type_provider, $function_stmts)) {
- // if there's a single throw statement, it's presumably an exception saying this method is not to be
- // used
- return null;
- }
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['InvalidReturnType'])
- && !in_array('InvalidReturnType', $suppressed_issues)
- ) {
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- Type::getVoid(),
- $source,
- $compatible_method_ids
- || (($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock)
- );
-
- return null;
- }
-
- if (!$declared_return_type->from_docblock || !$declared_return_type->isNullable()) {
- if (IssueBuffer::accepts(
- new InvalidReturnType(
- 'No return statements were found for method ' . $cased_method_id .
- ' but return type \'' . $declared_return_type . '\' was expected',
- $return_type_location
- ),
- $suppressed_issues,
- true
- )) {
- return false;
- }
- }
-
- return null;
- }
-
- if (!$declared_return_type->hasMixed()) {
- if ($inferred_return_type->isVoid()
- && ($declared_return_type->isVoid() || ($function_like_storage && $function_like_storage->has_yield))
- ) {
- return null;
- }
-
- if ($inferred_return_type->hasMixed() || $inferred_return_type->isEmpty()) {
- if (IssueBuffer::accepts(
- new MixedInferredReturnType(
- 'Could not verify return type \'' . $declared_return_type . '\' for ' .
- $cased_method_id,
- $return_type_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $inferred_return_type,
- $declared_return_type,
- true,
- true,
- $union_comparison_results
- )) {
- // is the declared return type more specific than the inferred one?
- if ($union_comparison_results->type_coerced) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- if (!$union_comparison_results->type_coerced_from_as_mixed) {
- if (IssueBuffer::accepts(
- new MixedReturnTypeCoercion(
- 'The declared return type \'' . $declared_return_type->getId() . '\' for '
- . $cased_method_id . ' is more specific than the inferred return type '
- . '\'' . $inferred_return_type->getId() . '\'',
- $return_type_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
- } else {
- if (IssueBuffer::accepts(
- new MoreSpecificReturnType(
- 'The declared return type \'' . $declared_return_type->getId() . '\' for '
- . $cased_method_id . ' is more specific than the inferred return type '
- . '\'' . $inferred_return_type->getId() . '\'',
- $return_type_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
- } else {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['InvalidReturnType'])
- && !in_array('InvalidReturnType', $suppressed_issues)
- ) {
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $inferred_return_type,
- $source,
- ($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock,
- $function_like_storage
- );
-
- return null;
- }
-
- if (IssueBuffer::accepts(
- new InvalidReturnType(
- 'The declared return type \''
- . $declared_return_type->getId()
- . '\' for ' . $cased_method_id
- . ' is incorrect, got \''
- . $inferred_return_type->getId() . '\'',
- $return_type_location
- ),
- $suppressed_issues,
- true
- )) {
- return false;
- }
- }
- } elseif (!$inferred_return_type->hasMixed()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $declared_return_type,
- $inferred_return_type,
- false,
- false
- )
- ) {
- if ($codebase->alter_code) {
- if (isset($project_analyzer->getIssuesToFix()['LessSpecificReturnType'])
- && !in_array('LessSpecificReturnType', $suppressed_issues)
- && !($function_like_storage instanceof MethodStorage && $function_like_storage->inheritdoc)
- ) {
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $inferred_return_type,
- $source,
- $compatible_method_ids
- || (($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock),
- $function_like_storage
- );
- }
- } else {
- if ($function instanceof Function_
- || $function instanceof Closure
- || $function instanceof ArrowFunction
- || $function->isPrivate()
- ) {
- $check_for_less_specific_type = true;
- } elseif ($source instanceof StatementsAnalyzer) {
- if ($function_like_storage instanceof MethodStorage) {
- $check_for_less_specific_type = !$function_like_storage->overridden_somewhere;
- } else {
- $check_for_less_specific_type = false;
- }
- } else {
- $check_for_less_specific_type = false;
- }
-
- if ($check_for_less_specific_type
- && (Config::getInstance()->restrict_return_types
- || (!$inferred_return_type->isNullable() && $declared_return_type->isNullable())
- || (!$inferred_return_type->isFalsable() && $declared_return_type->isFalsable()))
- ) {
- if (IssueBuffer::accepts(
- new LessSpecificReturnType(
- 'The inferred return type \''
- . $inferred_return_type->getId()
- . '\' for ' . $cased_method_id
- . ' is more specific than the declared return type \''
- . $declared_return_type->getId() . '\'',
- $return_type_location
- ),
- $suppressed_issues,
- !($function_like_storage instanceof MethodStorage && $function_like_storage->inheritdoc)
- )) {
- return false;
- }
- }
- }
- }
-
- if ($union_comparison_results->to_string_cast) {
- IssueBuffer::maybeAdd(
- new ImplicitToStringCast(
- 'The declared return type for ' . $cased_method_id . ' expects \'' .
- $declared_return_type . '\', ' . '\'' . $inferred_return_type .
- '\' provided with a __toString method',
- $return_type_location
- ),
- $suppressed_issues
- );
- }
-
- if (!$inferred_return_type->ignore_nullable_issues
- && $inferred_return_type->isNullable()
- && !$declared_return_type->isNullable()
- && !$declared_return_type->hasTemplate()
- && !$declared_return_type->isVoid()
- ) {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['InvalidNullableReturnType'])
- && !in_array('InvalidNullableReturnType', $suppressed_issues)
- && !$inferred_return_type->isNull()
- ) {
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $inferred_return_type,
- $source,
- ($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock,
- $function_like_storage
- );
-
- return null;
- }
-
- if (IssueBuffer::accepts(
- new InvalidNullableReturnType(
- 'The declared return type \'' . $declared_return_type . '\' for ' . $cased_method_id .
- ' is not nullable, but \'' . $inferred_return_type . '\' contains null',
- $return_type_location
- ),
- $suppressed_issues,
- !$inferred_return_type->isNull()
- )) {
- return false;
- }
- }
-
- if (!$inferred_return_type->ignore_falsable_issues
- && $inferred_return_type->isFalsable()
- && !$declared_return_type->isFalsable()
- && !$declared_return_type->hasBool()
- && !$declared_return_type->hasScalar()
- ) {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['InvalidFalsableReturnType'])
- ) {
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $inferred_return_type,
- $source,
- ($project_analyzer->only_replace_php_types_with_non_docblock_types
- || $unsafe_return_type)
- && $inferred_return_type->from_docblock,
- $function_like_storage
- );
-
- return null;
- }
-
- if (IssueBuffer::accepts(
- new InvalidFalsableReturnType(
- 'The declared return type \'' . $declared_return_type . '\' for ' . $cased_method_id .
- ' does not allow false, but \'' . $inferred_return_type . '\' contains false',
- $return_type_location
- ),
- $suppressed_issues,
- true
- )) {
- return false;
- }
- }
- }
-
- return null;
- }
-
- /**
- * @param Closure|Function_|ClassMethod|ArrowFunction $function
- *
- * @return false|null
- */
- public static function checkReturnType(
- FunctionLike $function,
- ProjectAnalyzer $project_analyzer,
- FunctionLikeAnalyzer $function_like_analyzer,
- FunctionLikeStorage $storage,
- Context $context
- ): ?bool {
- $codebase = $project_analyzer->getCodebase();
-
- if (!$storage->return_type || !$storage->return_type_location) {
- return null;
- }
-
- $parent_class = null;
-
- $classlike_storage = null;
-
- if ($context->self) {
- $classlike_storage = $codebase->classlike_storage_provider->get($context->self);
- $parent_class = $classlike_storage->parent_class;
- }
-
- if (!$storage->signature_return_type || $storage->signature_return_type === $storage->return_type) {
- foreach ($storage->return_type->getAtomicTypes() as $type) {
- if ($type instanceof TNamedObject
- && 'parent' === $type->value
- && null === $parent_class
- ) {
- if (IssueBuffer::accepts(
- new InvalidParent(
- 'Cannot use parent as a return type when class has no parent',
- $storage->return_type_location
- ),
- $storage->suppressed_issues
- )) {
- return false;
- }
- return null;
- }
- }
-
- $fleshed_out_return_type = TypeExpander::expandUnion(
- $codebase,
- $storage->return_type,
- $classlike_storage->name ?? null,
- $classlike_storage->name ?? null,
- $parent_class
- );
-
- $fleshed_out_return_type->check(
- $function_like_analyzer,
- $storage->return_type_location,
- $storage->suppressed_issues,
- [],
- false,
- false,
- false,
- $context->calling_method_id
- );
-
- return null;
- }
-
- $fleshed_out_signature_type = TypeExpander::expandUnion(
- $codebase,
- $storage->signature_return_type,
- $classlike_storage->name ?? null,
- $classlike_storage->name ?? null,
- $parent_class
- );
-
- if ($fleshed_out_signature_type->check(
- $function_like_analyzer,
- $storage->signature_return_type_location ?: $storage->return_type_location,
- $storage->suppressed_issues,
- [],
- false
- ) === false) {
- return false;
- }
-
- if ($function instanceof Closure || $function instanceof ArrowFunction) {
- return null;
- }
-
- $fleshed_out_return_type = TypeExpander::expandUnion(
- $codebase,
- $storage->return_type,
- $classlike_storage->name ?? null,
- $classlike_storage->name ?? null,
- $parent_class,
- true,
- true
- );
-
- if ($fleshed_out_return_type->check(
- $function_like_analyzer,
- $storage->return_type_location,
- $storage->suppressed_issues,
- [],
- false,
- $storage instanceof MethodStorage && $storage->inherited_return_type
- ) === false) {
- return false;
- }
-
- if ($classlike_storage && $context->self) {
- $class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $classlike_storage,
- $codebase->classlike_storage_provider->get($context->self),
- strtolower($function->name->name),
- new TNamedObject($context->self),
- true
- );
-
- $class_template_params = $class_template_params ?: [];
-
- if ($class_template_params) {
- $template_result = new TemplateResult(
- $class_template_params,
- []
- );
-
- $fleshed_out_return_type = TemplateStandinTypeReplacer::replace(
- $fleshed_out_return_type,
- $template_result,
- $codebase,
- null,
- null,
- null
- );
- }
- }
-
- $union_comparison_result = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $fleshed_out_return_type,
- $fleshed_out_signature_type,
- false,
- false,
- $union_comparison_result
- ) && !$union_comparison_result->type_coerced_from_mixed
- ) {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MismatchingDocblockReturnType'])
- ) {
- self::addOrUpdateReturnType(
- $function,
- $project_analyzer,
- $storage->signature_return_type,
- $function_like_analyzer->getSource()
- );
-
- return null;
- }
-
- if (IssueBuffer::accepts(
- new MismatchingDocblockReturnType(
- 'Docblock has incorrect return type \'' . $storage->return_type->getId() .
- '\', should be \'' . $storage->signature_return_type->getId() . '\'',
- $storage->return_type_location
- ),
- $storage->suppressed_issues,
- true
- )) {
- return false;
- }
- }
-
- return null;
- }
-
- /**
- * @param Closure|Function_|ClassMethod|ArrowFunction $function
- *
- */
- private static function addOrUpdateReturnType(
- FunctionLike $function,
- ProjectAnalyzer $project_analyzer,
- Union $inferred_return_type,
- StatementsSource $source,
- bool $docblock_only = false,
- ?FunctionLikeStorage $function_like_storage = null
- ): void {
- $manipulator = FunctionDocblockManipulator::getForFunction(
- $project_analyzer,
- $source->getFilePath(),
- $function
- );
-
- $codebase = $project_analyzer->getCodebase();
- $is_final = true;
- $fqcln = $source->getFQCLN();
-
- if ($fqcln !== null && $function instanceof ClassMethod) {
- $class_storage = $codebase->classlike_storage_provider->get($fqcln);
- $is_final = $function->isFinal() || $class_storage->final;
- }
-
- $allow_native_type = !$docblock_only
- && $codebase->php_major_version >= 7
- && (
- $codebase->allow_backwards_incompatible_changes
- || $is_final
- || !$function instanceof PhpParser\Node\Stmt\ClassMethod
- );
-
- $manipulator->setReturnType(
- $allow_native_type
- ? (string) $inferred_return_type->toPhpString(
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- $source->getFQCLN(),
- $codebase->php_major_version,
- $codebase->php_minor_version
- ) : null,
- $inferred_return_type->toNamespacedString(
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- $source->getFQCLN(),
- false
- ),
- $inferred_return_type->toNamespacedString(
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- $source->getFQCLN(),
- true
- ),
- $inferred_return_type->canBeFullyExpressedInPhp($codebase->php_major_version, $codebase->php_minor_version),
- $function_like_storage->return_type_description ?? null
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php
deleted file mode 100644
index f0ab1bc..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLike/ReturnTypeCollector.php
+++ /dev/null
@@ -1,384 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\FunctionLike;
-
-use PhpParser;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function array_merge;
-
-/**
- * A class for analysing a given method call's effects in relation to $this/self and also looking at return types
- */
-class ReturnTypeCollector
-{
- /**
- * Gets the return types from a list of statements
- *
- * @param array<PhpParser\Node> $stmts
- * @param list<Union> $yield_types
- *
- * @return list<Union> a list of return types
- *
- * @psalm-suppress ComplexMethod to be refactored
- *
- * TODO: This would probably benefit from using the list of exit_functions
- */
- public static function getReturnTypes(
- Codebase $codebase,
- NodeDataProvider $nodes,
- array $stmts,
- array &$yield_types,
- bool $collapse_types = false
- ): array {
- $return_types = [];
-
- foreach ($stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Return_) {
- if (!$stmt->expr) {
- $return_types[] = Type::getVoid();
- } elseif ($stmt_type = $nodes->getType($stmt)) {
- $return_types[] = $stmt_type;
-
- $yield_types = array_merge($yield_types, self::getYieldTypeFromExpression($stmt->expr, $nodes));
- } elseif ($stmt->expr instanceof PhpParser\Node\Scalar\String_) {
- $return_types[] = Type::getString();
- } elseif ($stmt->expr instanceof PhpParser\Node\Scalar\LNumber) {
- $return_types[] = Type::getInt();
- } elseif ($stmt->expr instanceof PhpParser\Node\Expr\ConstFetch) {
- if ((string)$stmt->expr->name === 'true') {
- $return_types[] = Type::getTrue();
- } elseif ((string)$stmt->expr->name === 'false') {
- $return_types[] = Type::getFalse();
- } elseif ((string)$stmt->expr->name === 'null') {
- $return_types[] = Type::getNull();
- }
- } else {
- $return_types[] = Type::getMixed();
- }
-
- break;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Break_
- || $stmt instanceof PhpParser\Node\Stmt\Continue_
- ) {
- break;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Throw_) {
- if ($collapse_types) {
- $return_types[] = Type::getNever();
- }
-
- break;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Expression) {
- if ($stmt->expr instanceof PhpParser\Node\Expr\Exit_) {
- if ($collapse_types) {
- $return_types[] = Type::getNever();
- }
-
- break;
- }
-
- if ($stmt->expr instanceof PhpParser\Node\Expr\Assign) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- [$stmt->expr->expr],
- $yield_types
- )
- );
- }
-
- $yield_types = array_merge($yield_types, self::getYieldTypeFromExpression($stmt->expr, $nodes));
- } elseif ($stmt instanceof PhpParser\Node\Stmt\If_) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->stmts,
- $yield_types
- )
- );
-
- foreach ($stmt->elseifs as $elseif) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $elseif->stmts,
- $yield_types
- )
- );
- }
-
- if ($stmt->else) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->else->stmts,
- $yield_types
- )
- );
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->stmts,
- $yield_types
- )
- );
-
- foreach ($stmt->catches as $catch) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $catch->stmts,
- $yield_types
- )
- );
- }
-
- if ($stmt->finally) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->finally->stmts,
- $yield_types
- )
- );
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\For_) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->stmts,
- $yield_types
- )
- );
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Foreach_) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->stmts,
- $yield_types
- )
- );
- } elseif ($stmt instanceof PhpParser\Node\Stmt\While_) {
- $yield_types = array_merge($yield_types, self::getYieldTypeFromExpression($stmt->cond, $nodes));
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->stmts,
- $yield_types
- )
- );
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Do_) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $stmt->stmts,
- $yield_types
- )
- );
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
- foreach ($stmt->cases as $case) {
- $return_types = array_merge(
- $return_types,
- self::getReturnTypes(
- $codebase,
- $nodes,
- $case->stmts,
- $yield_types
- )
- );
- }
- }
- }
-
- // if we're at the top level and we're not ending in a return, make sure to add possible null
- if ($collapse_types) {
- // if it's a generator, boil everything down to a single generator return type
- if ($yield_types) {
- $yield_types = self::processYieldTypes($codebase, $return_types, $yield_types);
- }
- }
-
- return $return_types;
- }
-
- /**
- * @param list<Union> $return_types
- * @param non-empty-list<Union> $yield_types
- * @return non-empty-list<Union>
- */
- private static function processYieldTypes(
- Codebase $codebase,
- array $return_types,
- array $yield_types
- ): array {
- $key_type = null;
- $value_type = null;
-
- $yield_type = Type::combineUnionTypeArray($yield_types, null);
-
- foreach ($yield_type->getAtomicTypes() as $type) {
- if ($type instanceof TKeyedArray) {
- $type = $type->getGenericArrayType();
- }
-
- if ($type instanceof TList) {
- $type = new TArray([Type::getInt(), $type->type_param]);
- }
-
- if ($type instanceof TArray) {
- [$key_type_param, $value_type_param] = $type->type_params;
-
- $key_type = Type::combineUnionTypes(clone $key_type_param, $key_type);
- $value_type = Type::combineUnionTypes(clone $value_type_param, $value_type);
- } elseif ($type instanceof TIterable
- || $type instanceof TNamedObject
- ) {
- ForeachAnalyzer::getKeyValueParamsForTraversableObject(
- $type,
- $codebase,
- $key_type,
- $value_type
- );
- }
- }
-
- return [
- new Union([
- new TGenericObject(
- 'Generator',
- [
- $key_type ?? Type::getMixed(),
- $value_type ?? Type::getMixed(),
- Type::getMixed(),
- $return_types ? Type::combineUnionTypeArray($return_types, null) : Type::getVoid()
- ]
- ),
- ])
- ];
- }
-
- /**
- * @return list<Union>
- */
- protected static function getYieldTypeFromExpression(
- PhpParser\Node\Expr $stmt,
- NodeDataProvider $nodes
- ): array {
- if ($stmt instanceof PhpParser\Node\Expr\Yield_) {
- $key_type = null;
-
- if ($stmt->key && ($stmt_key_type = $nodes->getType($stmt->key))) {
- $key_type = $stmt_key_type;
- }
-
- if ($stmt->value
- && $value_type = $nodes->getType($stmt->value)
- ) {
- $generator_type = new TGenericObject(
- 'Generator',
- [
- $key_type ? clone $key_type : Type::getInt(),
- clone $value_type,
- Type::getMixed(),
- Type::getMixed()
- ]
- );
-
- return [new Union([$generator_type])];
- }
-
- return [Type::getMixed()];
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\YieldFrom) {
- if ($stmt_expr_type = $nodes->getType($stmt->expr)) {
- return [$stmt_expr_type];
- }
-
- return [Type::getMixed()];
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
- return array_merge(
- self::getYieldTypeFromExpression($stmt->left, $nodes),
- self::getYieldTypeFromExpression($stmt->right, $nodes)
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Assign) {
- return self::getYieldTypeFromExpression($stmt->expr, $nodes);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\MethodCall
- || $stmt instanceof PhpParser\Node\Expr\FuncCall
- || $stmt instanceof PhpParser\Node\Expr\StaticCall
- || $stmt instanceof PhpParser\Node\Expr\New_
- ) {
- if ($stmt->isFirstClassCallable()) {
- return [];
- }
-
- $yield_types = [];
-
- foreach ($stmt->getArgs() as $arg) {
- $yield_types = array_merge($yield_types, self::getYieldTypeFromExpression($arg->value, $nodes));
- }
-
- return $yield_types;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Array_) {
- $yield_types = [];
-
- foreach ($stmt->items as $item) {
- if ($item instanceof PhpParser\Node\Expr\ArrayItem) {
- $yield_types = array_merge($yield_types, self::getYieldTypeFromExpression($item->value, $nodes));
- }
- }
-
- return $yield_types;
- }
-
- return [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
deleted file mode 100644
index 385a954..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php
+++ /dev/null
@@ -1,2023 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use PhpParser\Node\Expr\ArrowFunction;
-use PhpParser\Node\Expr\Closure;
-use PhpParser\Node\Param;
-use PhpParser\Node\Stmt\ClassMethod;
-use PhpParser\Node\Stmt\Function_;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\FunctionLike\ReturnTypeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLike\ReturnTypeCollector;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\FileManipulation\FunctionDocblockManipulator;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\PhpVisitor\NodeCounterVisitor;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\InvalidDocblockParamName;
-use Psalm\Issue\InvalidParamDefault;
-use Psalm\Issue\InvalidThrow;
-use Psalm\Issue\MethodSignatureMismatch;
-use Psalm\Issue\MismatchingDocblockParamType;
-use Psalm\Issue\MissingClosureParamType;
-use Psalm\Issue\MissingParamType;
-use Psalm\Issue\MissingThrowsDocblock;
-use Psalm\Issue\ReferenceConstraintViolation;
-use Psalm\Issue\ReservedWord;
-use Psalm\Issue\UnusedClosureParam;
-use Psalm\Issue\UnusedParam;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AfterFunctionLikeAnalysisEvent;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Storage\FunctionStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_combine;
-use function array_diff_key;
-use function array_key_exists;
-use function array_keys;
-use function array_merge;
-use function array_search;
-use function array_values;
-use function count;
-use function end;
-use function in_array;
-use function is_string;
-use function mb_strpos;
-use function md5;
-use function microtime;
-use function reset;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- * @template-covariant TFunction as Closure|Function_|ClassMethod|ArrowFunction
- */
-abstract class FunctionLikeAnalyzer extends SourceAnalyzer
-{
- /**
- * @var TFunction
- */
- protected $function;
-
- /**
- * @var Codebase
- */
- protected $codebase;
-
- /**
- * @var array<string>
- */
- protected $suppressed_issues;
-
- /**
- * @var bool
- */
- protected $is_static = false;
-
- /**
- * @var ?array<string, Union>
- */
- protected $return_vars_in_scope = [];
-
- /**
- * @var ?array<string, bool>
- */
- protected $return_vars_possibly_in_scope = [];
-
- /**
- * @var Union|null
- */
- private $local_return_type;
-
- /**
- * @var array<string, bool>
- */
- protected static $no_effects_hashes = [];
-
- /**
- * @var bool
- */
- public $track_mutations = false;
-
- /**
- * @var bool
- */
- public $inferred_impure = false;
-
- /**
- * @var bool
- */
- public $inferred_has_mutation = false;
-
- /**
- * Holds param nodes for functions with func_get_args calls
- *
- * @var array<string, DataFlowNode>
- */
- public $param_nodes = [];
-
- /**
- * @var FunctionLikeStorage
- */
- protected $storage;
-
- /**
- * @param TFunction $function
- */
- public function __construct($function, SourceAnalyzer $source, FunctionLikeStorage $storage)
- {
- $this->function = $function;
- $this->source = $source;
- $this->suppressed_issues = $source->getSuppressedIssues();
- $this->codebase = $source->getCodebase();
- $this->storage = $storage;
- }
-
- /**
- * @param bool $add_mutations whether or not to add mutations to this method
- *
- * @return false|null
- *
- * @psalm-suppress PossiblyUnusedReturnValue unused but seems important
- */
- public function analyze(
- Context $context,
- NodeDataProvider $type_provider,
- ?Context $global_context = null,
- bool $add_mutations = false
- ): ?bool {
- $storage = $this->storage;
-
- $function_stmts = $this->function->getStmts() ?: [];
-
- if ($this->function instanceof ArrowFunction
- && isset($function_stmts[0])
- && $function_stmts[0] instanceof PhpParser\Node\Stmt\Return_
- && $function_stmts[0]->expr
- ) {
- $function_stmts[0]->setAttributes($function_stmts[0]->expr->getAttributes());
- }
-
- if ($global_context) {
- foreach ($global_context->constants as $const_name => $var_type) {
- if (!$context->hasVariable($const_name)) {
- $context->vars_in_scope[$const_name] = clone $var_type;
- }
- }
- }
-
- $codebase = $this->codebase;
- $project_analyzer = $this->getProjectAnalyzer();
-
- if ($codebase->track_unused_suppressions && !isset($storage->suppressed_issues[0])) {
- if (count($storage->suppressed_issues) === 1 // UnusedPsalmSuppress by itself should be marked as unused
- || !in_array("UnusedPsalmSuppress", $storage->suppressed_issues)
- ) {
- foreach ($storage->suppressed_issues as $offset => $issue_name) {
- IssueBuffer::addUnusedSuppression($this->getFilePath(), $offset, $issue_name);
- }
- }
- }
-
- foreach ($storage->docblock_issues as $docblock_issue) {
- IssueBuffer::maybeAdd($docblock_issue);
- }
-
- $function_information = $this->getFunctionInformation(
- $context,
- $codebase,
- $type_provider,
- $storage,
- $add_mutations
- );
-
- if ($function_information === null) {
- return null;
- }
-
- [
- $real_method_id,
- $method_id,
- $appearing_class_storage,
- $hash,
- $cased_method_id,
- $overridden_method_ids
- ] = $function_information;
-
- $this->suppressed_issues = $this->getSource()->getSuppressedIssues() + $storage->suppressed_issues;
- if ($appearing_class_storage) {
- $this->suppressed_issues += $appearing_class_storage->suppressed_issues;
- }
-
- if ($storage instanceof MethodStorage && $storage->is_static) {
- $this->is_static = true;
- }
-
- $statements_analyzer = new StatementsAnalyzer($this, $type_provider);
-
- if ($this instanceof ClosureAnalyzer && $this->function instanceof Closure) {
- $byref_uses = [];
-
- foreach ($this->function->uses as $use) {
- if (!is_string($use->var->name)) {
- continue;
- }
-
- $use_var_id = '$' . $use->var->name;
-
- $use_location = new CodeLocation($this, $use);
-
- $use_assignment = null;
-
- if ($statements_analyzer->data_flow_graph) {
- $use_assignment = DataFlowNode::getForAssignment(
- $use_var_id,
- $use_location
- );
-
- $statements_analyzer->data_flow_graph->addNode($use_assignment);
-
- $context->vars_in_scope[$use_var_id]->parent_nodes += [$use_assignment->id => $use_assignment];
- }
-
- if ($use->byRef) {
- $byref_uses[$use_var_id] = true;
-
- if ($statements_analyzer->data_flow_graph && $use_assignment) {
- $statements_analyzer->data_flow_graph->addPath(
- $use_assignment,
- new DataFlowNode('closure-use', 'closure use', null),
- 'closure-use'
- );
- }
- } else {
- $statements_analyzer->registerVariable($use_var_id, $use_location, null);
- }
- }
-
- $statements_analyzer->setByRefUses($byref_uses);
- }
-
- if ($storage->template_types) {
- foreach ($storage->template_types as $param_name => $_) {
- $fq_classlike_name = Type::getFQCLNFromString(
- $param_name,
- $this->getAliases()
- );
-
- if ($codebase->classOrInterfaceExists($fq_classlike_name)) {
- IssueBuffer::maybeAdd(
- new ReservedWord(
- 'Cannot use ' . $param_name . ' as template name since the class already exists',
- new CodeLocation($this, $this->function),
- 'resource'
- ),
- $this->getSuppressedIssues()
- );
- }
- }
- }
-
- $template_types = $storage->template_types;
-
- if ($appearing_class_storage && $appearing_class_storage->template_types) {
- $template_types = array_merge($template_types ?: [], $appearing_class_storage->template_types);
- }
-
- $params = $storage->params;
-
- if ($codebase->alter_code) {
- $this->alterParams($codebase, $storage, $params, $context);
- }
-
- foreach ($codebase->methods_to_rename as $original_method_id => $new_method_name) {
- if ($this instanceof MethodAnalyzer
- && strtolower((string) $this->getMethodId()) === $original_method_id
- ) {
- $file_manipulations = [
- new FileManipulation(
- (int) $this->function->name->getAttribute('startFilePos'),
- (int) $this->function->name->getAttribute('endFilePos') + 1,
- $new_method_name
- )
- ];
-
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- }
- }
-
- if ($storage instanceof MethodStorage
- && $method_id instanceof MethodIdentifier
- && $overridden_method_ids
- ) {
- $params = $codebase->methods->getMethodParams(
- $method_id,
- $this
- );
- }
-
- $check_stmts = $this->processParams(
- $statements_analyzer,
- $storage,
- $cased_method_id,
- $params,
- array_values($this->function->params),
- $context,
- (bool) $template_types
- );
-
- if ($storage->pure) {
- $context->pure = true;
- }
-
- if ($storage->mutation_free
- && $cased_method_id
- && !strpos($cased_method_id, '__construct')
- && !($storage instanceof MethodStorage && $storage->mutation_free_inferred)
- ) {
- $context->mutation_free = true;
- }
-
- if ($storage instanceof MethodStorage
- && $storage->external_mutation_free
- && !$storage->mutation_free_inferred
- ) {
- $context->external_mutation_free = true;
- }
-
- if ($storage->unused_docblock_params) {
- foreach ($storage->unused_docblock_params as $param_name => $param_location) {
- if (IssueBuffer::accepts(
- new InvalidDocblockParamName(
- 'Incorrect param name $' . $param_name . ' in docblock for ' . $cased_method_id,
- $param_location
- )
- )) {
- }
- }
- }
-
- if ($storage->signature_return_type && $storage->signature_return_type_location) {
- [$start, $end] = $storage->signature_return_type_location->getSelectionBounds();
-
- $codebase->analyzer->addOffsetReference(
- $this->getFilePath(),
- $start,
- $end,
- (string) $storage->signature_return_type
- );
- }
-
- if ($storage instanceof MethodStorage && $storage->location && !$storage->allow_named_arg_calls) {
- foreach ($overridden_method_ids as $overridden_method_id) {
- $overridden_storage = $codebase->methods->getStorage($overridden_method_id);
- if ($overridden_storage->allow_named_arg_calls) {
- IssueBuffer::maybeAdd(new MethodSignatureMismatch(
- 'Method ' . (string) $method_id . ' should accept named arguments '
- . ' as ' . (string) $overridden_method_id . ' does',
- $storage->location
- ));
- }
- }
- }
-
- if (ReturnTypeAnalyzer::checkReturnType(
- $this->function,
- $project_analyzer,
- $this,
- $storage,
- $context
- ) === false) {
- $check_stmts = false;
- }
-
- if (!$check_stmts) {
- return false;
- }
-
- if ($context->collect_initializations || $context->collect_mutations) {
- $statements_analyzer->addSuppressedIssues([
- 'DocblockTypeContradiction',
- 'InvalidReturnStatement',
- 'RedundantCondition',
- 'RedundantConditionGivenDocblockType',
- 'TypeDoesNotContainNull',
- 'TypeDoesNotContainType',
- 'LoopInvalidation',
- ]);
-
- if ($context->collect_initializations) {
- $statements_analyzer->addSuppressedIssues([
- 'UndefinedInterfaceMethod',
- 'UndefinedMethod',
- 'PossiblyUndefinedMethod',
- ]);
- }
- } elseif ($cased_method_id && strpos($cased_method_id, '__destruct')) {
- $statements_analyzer->addSuppressedIssues([
- 'InvalidPropertyAssignmentValue',
- 'PossiblyNullPropertyAssignmentValue',
- ]);
- }
-
- $time = microtime(true);
-
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- if ($codebase->alter_code
- && (isset($project_analyzer->getIssuesToFix()['MissingPureAnnotation'])
- || isset($project_analyzer->getIssuesToFix()['MissingImmutableAnnotation']))
- ) {
- $this->track_mutations = true;
- } elseif ($this->function instanceof Closure
- || $this->function instanceof ArrowFunction
- ) {
- $this->track_mutations = true;
- }
-
- if ($this->function instanceof ArrowFunction && $storage->return_type && $storage->return_type->isNever()) {
- // ArrowFunction perform a return implicitly so if the return type is never, we have to suppress the error
- // note: the never can only come from phpdoc. PHP will refuse short closures with never in signature
- $statements_analyzer->addSuppressedIssues(['NoValue']);
- }
-
- $statements_analyzer->analyze($function_stmts, $context, $global_context, true);
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MissingPureAnnotation'])
- && !$this->inferred_impure
- && ($this->function instanceof Function_
- || $this->function instanceof ClassMethod)
- && $storage->params
- && !$overridden_method_ids
- ) {
- $manipulator = FunctionDocblockManipulator::getForFunction(
- $project_analyzer,
- $this->source->getFilePath(),
- $this->function
- );
-
- $yield_types = [];
-
- $inferred_return_types = ReturnTypeCollector::getReturnTypes(
- $codebase,
- $type_provider,
- $function_stmts,
- $yield_types,
- true
- );
-
- $inferred_return_type = $inferred_return_types
- ? Type::combineUnionTypeArray(
- $inferred_return_types,
- $codebase
- )
- : Type::getVoid();
-
- if (!$inferred_return_type->isVoid()
- && !$inferred_return_type->isFalse()
- && !$inferred_return_type->isNull()
- && !$inferred_return_type->isSingleIntLiteral()
- && !$inferred_return_type->isSingleStringLiteral()
- && !$inferred_return_type->isTrue()
- && $inferred_return_type->getId() !== 'array<empty, empty>'
- ) {
- $manipulator->makePure();
- }
- }
-
- if ($this->inferred_has_mutation && $context->self) {
- $this->codebase->analyzer->addMutableClass($context->self);
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $project_analyzer->debug_performance
- && $cased_method_id
- ) {
- $traverser = new PhpParser\NodeTraverser;
-
- $node_counter = new NodeCounterVisitor();
- $traverser->addVisitor($node_counter);
- $traverser->traverse($function_stmts);
-
- if ($node_counter->count > 5) {
- $time_taken = microtime(true) - $time;
- $codebase->analyzer->addFunctionTiming($cased_method_id, $time_taken / $node_counter->count);
- }
- }
-
- $final_actions = ScopeAnalyzer::getControlActions(
- $this->function->getStmts() ?: [],
- null,
- $codebase->config->exit_functions,
- []
- );
-
- if ($final_actions !== [ScopeAnalyzer::ACTION_END]) {
- $this->examineParamTypes($statements_analyzer, $context, $codebase);
- }
-
- foreach ($params as $function_param) {
- // only complain if there's no type defined by a parent type
- if (!$function_param->type
- && $function_param->location
- ) {
- if ($this->function instanceof Closure
- || $this->function instanceof ArrowFunction
- ) {
- IssueBuffer::maybeAdd(
- new MissingClosureParamType(
- 'Parameter $' . $function_param->name . ' has no provided type',
- $function_param->location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new MissingParamType(
- 'Parameter $' . $function_param->name . ' has no provided type',
- $function_param->location
- ),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($this->function instanceof Closure
- || $this->function instanceof ArrowFunction
- ) {
- $this->verifyReturnType(
- $function_stmts,
- $statements_analyzer,
- $storage->return_type,
- $this->source->getFQCLN(),
- $storage->return_type_location,
- $context->has_returned,
- $global_context && ($global_context->inside_call || $global_context->inside_return)
- );
-
- $closure_yield_types = [];
-
- $closure_return_types = ReturnTypeCollector::getReturnTypes(
- $codebase,
- $type_provider,
- $function_stmts,
- $closure_yield_types,
- true
- );
-
- $closure_return_type = $closure_return_types
- ? Type::combineUnionTypeArray(
- $closure_return_types,
- $codebase
- )
- : Type::getVoid();
-
- $closure_yield_type = $closure_yield_types
- ? Type::combineUnionTypeArray(
- $closure_yield_types,
- $codebase
- )
- : null;
-
- if ($closure_yield_type) {
- $closure_return_type = $closure_yield_type;
- }
-
- if ($function_type = $statements_analyzer->node_data->getType($this->function)) {
- /**
- * @var TClosure
- */
- $closure_atomic = $function_type->getSingleAtomic();
-
- if (($storage->return_type === $storage->signature_return_type)
- && (!$storage->return_type
- || $storage->return_type->hasMixed()
- || UnionTypeComparator::isContainedBy(
- $codebase,
- $closure_return_type,
- $storage->return_type
- ))
- ) {
- $closure_atomic->return_type = $closure_return_type;
- }
-
- $closure_atomic->is_pure = !$this->inferred_impure;
- }
- }
-
- if ($codebase->collect_references
- && !$context->collect_initializations
- && !$context->collect_mutations
- && $codebase->find_unused_variables
- && $context->check_variables
- ) {
- $this->checkParamReferences(
- $statements_analyzer,
- $storage,
- $appearing_class_storage,
- $context
- );
- }
-
- foreach ($storage->throws as $expected_exception => $_) {
- if (($expected_exception === 'self'
- || $expected_exception === 'static')
- && $context->self
- ) {
- $expected_exception = $context->self;
- }
-
- if (isset($storage->throw_locations[$expected_exception])) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $expected_exception,
- $storage->throw_locations[$expected_exception],
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(
- false,
- false,
- true,
- true,
- true
- )
- )) {
- $input_type = new Union([new TNamedObject($expected_exception)]);
- $container_type = new Union([new TNamedObject('Exception'), new TNamedObject('Throwable')]);
-
- if (!UnionTypeComparator::isContainedBy($codebase, $input_type, $container_type)) {
- IssueBuffer::maybeAdd(
- new InvalidThrow(
- 'Class supplied for @throws ' . $expected_exception
- . ' does not implement Throwable',
- $storage->throw_locations[$expected_exception],
- $expected_exception
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($codebase->alter_code) {
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $this,
- $input_type,
- $storage->throw_locations[$expected_exception],
- $context->calling_method_id
- );
- }
- }
- }
- }
-
- $missingThrowsDocblockErrors = [];
- foreach ($statements_analyzer->getUncaughtThrows($context) as $possibly_thrown_exception => $codelocations) {
- $is_expected = false;
-
- foreach ($storage->throws as $expected_exception => $_) {
- if ($expected_exception === $possibly_thrown_exception
- || $codebase->classExtendsOrImplements($possibly_thrown_exception, $expected_exception)
- ) {
- $is_expected = true;
- break;
- }
- }
-
- if (!$is_expected) {
- $missingThrowsDocblockErrors[] = $possibly_thrown_exception;
- foreach ($codelocations as $codelocation) {
- // issues are suppressed in ThrowAnalyzer, CallAnalyzer, etc.
- IssueBuffer::maybeAdd(
- new MissingThrowsDocblock(
- $possibly_thrown_exception . ' is thrown but not caught - please either catch'
- . ' or add a @throws annotation',
- $codelocation
- )
- );
- }
- }
- }
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MissingThrowsDocblock'])
- ) {
- $manipulator = FunctionDocblockManipulator::getForFunction(
- $project_analyzer,
- $this->source->getFilePath(),
- $this->function
- );
- $manipulator->addThrowsDocblock($missingThrowsDocblockErrors);
- }
-
- if ($codebase->taint_flow_graph
- && $this->function instanceof ClassMethod
- && $cased_method_id
- && $storage->specialize_call
- && isset($context->vars_in_scope['$this'])
- && $context->vars_in_scope['$this']->parent_nodes
- ) {
- $method_source = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $storage->location
- );
-
- $codebase->taint_flow_graph->addNode($method_source);
-
- foreach ($context->vars_in_scope['$this']->parent_nodes as $parent_node) {
- $codebase->taint_flow_graph->addPath(
- $parent_node,
- $method_source,
- '$this'
- );
- }
- }
-
- if ($add_mutations) {
- if ($this->return_vars_in_scope !== null) {
- $context->vars_in_scope = TypeAnalyzer::combineKeyedTypes(
- $context->vars_in_scope,
- $this->return_vars_in_scope
- );
- }
-
- if ($this->return_vars_possibly_in_scope !== null) {
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $this->return_vars_possibly_in_scope
- );
- }
-
- foreach ($context->vars_in_scope as $var => $_) {
- if (strpos($var, '$this->') !== 0 && $var !== '$this') {
- unset($context->vars_in_scope[$var]);
- }
- }
-
- foreach ($context->vars_possibly_in_scope as $var => $_) {
- if (strpos($var, '$this->') !== 0 && $var !== '$this') {
- unset($context->vars_possibly_in_scope[$var]);
- }
- }
-
- if ($hash
- && $real_method_id
- && $this instanceof MethodAnalyzer
- && !$context->collect_initializations
- ) {
- $new_hash = md5($real_method_id . '::' . $context->getScopeSummary());
-
- if ($new_hash === $hash) {
- self::$no_effects_hashes[$hash] = true;
- }
- }
- }
-
- $event = new AfterFunctionLikeAnalysisEvent(
- $this->function,
- $storage,
- $this,
- $codebase,
- [],
- $type_provider,
- $context
- );
-
- if ($codebase->config->eventDispatcher->dispatchAfterFunctionLikeAnalysis($event) === false) {
- return false;
- }
-
- $file_manipulations = $event->getFileReplacements();
-
- if ($file_manipulations) {
- FileManipulationBuffer::add(
- $this->getFilePath(),
- $file_manipulations
- );
- }
-
- AttributesAnalyzer::analyze(
- $this,
- $context,
- $storage,
- $this->function->attrGroups,
- $storage instanceof MethodStorage ? AttributesAnalyzer::TARGET_METHOD : AttributesAnalyzer::TARGET_FUNCTION,
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- return null;
- }
-
- private function checkParamReferences(
- StatementsAnalyzer $statements_analyzer,
- FunctionLikeStorage $storage,
- ?ClassLikeStorage $class_storage,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- $unused_params = [];
-
- foreach ($statements_analyzer->getUnusedVarLocations() as [$var_name, $original_location]) {
- if (!array_key_exists(substr($var_name, 1), $storage->param_lookup)) {
- continue;
- }
-
- if (strpos($var_name, '$_') === 0 || (strpos($var_name, '$unused') === 0 && $var_name !== '$unused')) {
- continue;
- }
-
- $position = array_search(substr($var_name, 1), array_keys($storage->param_lookup), true);
-
- if ($position === false) {
- throw new UnexpectedValueException('$position should not be false here');
- }
-
- if ($storage->params[$position]->by_ref) {
- continue;
- }
-
- if ($storage->params[$position]->promoted_property) {
- continue;
- }
-
- $did_match_param = false;
-
- foreach ($this->function->params as $param) {
- if ($param->var->getAttribute('endFilePos') === $original_location->raw_file_end) {
- $did_match_param = true;
- break;
- }
- }
-
- if (!$did_match_param) {
- continue;
- }
-
- $assignment_node = DataFlowNode::getForAssignment($var_name, $original_location);
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- && $statements_analyzer->data_flow_graph->isVariableUsed($assignment_node)
- ) {
- continue;
- }
-
- if (!$storage instanceof MethodStorage
- || !$storage->cased_name
- || $storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- if ($this instanceof ClosureAnalyzer) {
- IssueBuffer::maybeAdd(
- new UnusedClosureParam(
- 'Param ' . $var_name . ' is never referenced in this method',
- $original_location
- ),
- $this->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UnusedParam(
- 'Param ' . $var_name . ' is never referenced in this method',
- $original_location
- ),
- $this->getSuppressedIssues()
- );
- }
- } else {
- $fq_class_name = (string)$context->self;
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $method_name_lc = strtolower($storage->cased_name);
-
- if ($storage->abstract) {
- continue;
- }
-
- if (isset($class_storage->overridden_method_ids[$method_name_lc])) {
- $parent_method_id = end($class_storage->overridden_method_ids[$method_name_lc]);
-
- if ($parent_method_id) {
- $parent_method_storage = $codebase->methods->getStorage($parent_method_id);
-
- // if the parent method has a param at that position and isn't abstract
- if (!$parent_method_storage->abstract
- && isset($parent_method_storage->params[$position])
- ) {
- continue;
- }
- }
- }
-
- $unused_params[$position] = $original_location;
- }
- }
-
- if ($storage instanceof MethodStorage
- && $this instanceof MethodAnalyzer
- && $class_storage
- && $storage->cased_name
- && $storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- $method_id_lc = strtolower((string) $this->getMethodId());
-
- foreach ($storage->params as $i => $_) {
- if (!isset($unused_params[$i])) {
- $codebase->file_reference_provider->addMethodParamUse(
- $method_id_lc,
- $i,
- $method_id_lc
- );
-
- $method_name_lc = strtolower($storage->cased_name);
-
- if (!isset($class_storage->overridden_method_ids[$method_name_lc])) {
- continue;
- }
-
- foreach ($class_storage->overridden_method_ids[$method_name_lc] as $parent_method_id) {
- $codebase->file_reference_provider->addMethodParamUse(
- strtolower((string) $parent_method_id),
- $i,
- $method_id_lc
- );
- }
- }
- }
- }
- }
-
- /**
- * @param list<FunctionLikeParameter> $params
- * @param list<Param> $param_stmts
- */
- private function processParams(
- StatementsAnalyzer $statements_analyzer,
- FunctionLikeStorage $storage,
- ?string $cased_method_id,
- array $params,
- array $param_stmts,
- Context $context,
- bool $has_template_types
- ): bool {
- $check_stmts = true;
- $codebase = $statements_analyzer->getCodebase();
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- foreach ($params as $offset => $function_param) {
- $signature_type = $function_param->signature_type;
- $signature_type_location = $function_param->signature_type_location;
-
- if ($signature_type && $signature_type_location && $signature_type->hasObjectType()) {
- $referenced_type = $signature_type;
- if ($referenced_type->isNullable()) {
- $referenced_type = clone $referenced_type;
- $referenced_type->removeType('null');
- }
- [$start, $end] = $signature_type_location->getSelectionBounds();
- $codebase->analyzer->addOffsetReference(
- $this->getFilePath(),
- $start,
- $end,
- (string) $referenced_type
- );
- }
-
- if ($signature_type) {
- $signature_type = TypeExpander::expandUnion(
- $codebase,
- $signature_type,
- $context->self,
- $context->self,
- $this->getParentFQCLN()
- );
- }
-
- if ($function_param->type) {
- $param_type = clone $function_param->type;
-
- $param_type = TypeExpander::expandUnion(
- $codebase,
- $param_type,
- $context->self,
- $context->self,
- $this->getParentFQCLN(),
- true,
- false,
- false,
- true
- );
-
- if ($function_param->type_location) {
- if ($param_type->check(
- $this,
- $function_param->type_location,
- $storage->suppressed_issues,
- [],
- false,
- false,
- $this->function instanceof ClassMethod
- && strtolower($this->function->name->name) !== '__construct',
- $context->calling_method_id
- ) === false) {
- $check_stmts = false;
- }
- }
- } else {
- $param_type = Type::getMixed();
- }
-
- $var_type = $param_type;
-
- if ($function_param->is_variadic) {
- if ($storage->allow_named_arg_calls) {
- $var_type = new Union([
- new TArray([Type::getArrayKey(), $param_type]),
- ]);
- } else {
- $var_type = new Union([
- new TList($param_type),
- ]);
- }
- }
-
- if ($statements_analyzer->data_flow_graph
- && $function_param->location
- ) {
- //don't add to taint flow graph if the type can't transmit taints
- if (!$statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- || $function_param->type === null
- || !$function_param->type->isSingle()
- || (!$function_param->type->isInt()
- && !$function_param->type->isFloat()
- && !$function_param->type->isBool())
- ) {
- $param_assignment = DataFlowNode::getForAssignment(
- '$' . $function_param->name,
- $function_param->location
- );
-
- $statements_analyzer->data_flow_graph->addNode($param_assignment);
-
- if ($cased_method_id) {
- $type_source = DataFlowNode::getForMethodArgument(
- $cased_method_id,
- $cased_method_id,
- $offset,
- $function_param->location,
- null
- );
-
- $statements_analyzer->data_flow_graph->addPath($type_source, $param_assignment, 'param');
- }
-
- if ($function_param->by_ref
- && $codebase->find_unused_variables
- ) {
- $statements_analyzer->data_flow_graph->addPath(
- $param_assignment,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
-
- if ($storage->variadic) {
- $this->param_nodes += [$param_assignment->id => $param_assignment];
- }
-
- $var_type->parent_nodes += [$param_assignment->id => $param_assignment];
- }
- }
-
- $context->vars_in_scope['$' . $function_param->name] = $var_type;
- $context->vars_possibly_in_scope['$' . $function_param->name] = true;
-
- if ($function_param->by_ref) {
- $context->vars_in_scope['$' . $function_param->name]->by_ref = true;
- }
-
- $parser_param = $this->function->getParams()[$offset] ?? null;
-
- if ($function_param->location) {
- $statements_analyzer->registerVariable(
- '$' . $function_param->name,
- $function_param->location,
- null
- );
- }
-
- if (!$function_param->type_location || !$function_param->location) {
- if ($parser_param && $parser_param->default) {
- ExpressionAnalyzer::analyze($statements_analyzer, $parser_param->default, $context);
- }
-
- continue;
- }
-
- if ($signature_type) {
- $union_comparison_result = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $param_type,
- $signature_type,
- false,
- false,
- $union_comparison_result
- ) && !$union_comparison_result->type_coerced_from_mixed
- ) {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MismatchingDocblockParamType'])
- ) {
- $this->addOrUpdateParamType($project_analyzer, $function_param->name, $signature_type, true);
-
- continue;
- }
-
- IssueBuffer::maybeAdd(
- new MismatchingDocblockParamType(
- 'Parameter $' . $function_param->name . ' has wrong type \'' . $param_type .
- '\', should be \'' . $signature_type . '\'',
- $function_param->type_location
- ),
- $storage->suppressed_issues,
- true
- );
-
- if ($signature_type->check(
- $this,
- $function_param->type_location,
- $storage->suppressed_issues,
- [],
- false
- ) === false) {
- $check_stmts = false;
- }
-
- continue;
- }
- }
-
- if ($parser_param && $parser_param->default) {
- ExpressionAnalyzer::analyze($statements_analyzer, $parser_param->default, $context);
-
- $default_type = $statements_analyzer->node_data->getType($parser_param->default);
-
- if ($default_type
- && !$default_type->hasMixed()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $default_type,
- $param_type,
- false,
- false,
- null,
- true
- )
- ) {
- IssueBuffer::maybeAdd(
- new InvalidParamDefault(
- 'Default value type ' . $default_type->getId() . ' for argument ' . ($offset + 1)
- . ' of method ' . $cased_method_id
- . ' does not match the given type ' . $param_type->getId(),
- $function_param->type_location
- )
- );
- }
- }
-
- if ($has_template_types) {
- $substituted_type = clone $param_type;
- if ($substituted_type->check(
- $this->source,
- $function_param->type_location,
- $this->suppressed_issues,
- [],
- false
- ) === false) {
- $check_stmts = false;
- }
- } else {
- if ($param_type->isVoid()) {
- IssueBuffer::maybeAdd(
- new ReservedWord(
- 'Parameter cannot be void',
- $function_param->type_location,
- 'void'
- ),
- $this->suppressed_issues
- );
- }
-
- if ($param_type->check(
- $this->source,
- $function_param->type_location,
- $this->suppressed_issues,
- [],
- false
- ) === false) {
- $check_stmts = false;
- }
- }
-
- if ($codebase->collect_locations) {
- if ($function_param->type_location !== $function_param->signature_type_location &&
- $function_param->signature_type_location &&
- $function_param->signature_type
- ) {
- if ($function_param->signature_type->check(
- $this->source,
- $function_param->signature_type_location,
- $this->suppressed_issues,
- [],
- false
- ) === false) {
- $check_stmts = false;
- }
- }
- }
-
- if ($function_param->by_ref) {
- // register by ref params as having been used, to avoid false positives
- // @todo change the assignment analysis *just* for byref params
- // so that we don't have to do this
- $context->hasVariable('$' . $function_param->name);
- }
-
- if (count($param_stmts) === count($params)) {
- AttributesAnalyzer::analyze(
- $this,
- $context,
- $function_param,
- $param_stmts[$offset]->attrGroups,
- AttributesAnalyzer::TARGET_PARAMETER
- | ($function_param->promoted_property ? AttributesAnalyzer::TARGET_PROPERTY : 0),
- $storage->suppressed_issues + $this->getSuppressedIssues()
- );
- }
- }
-
- return $check_stmts;
- }
-
- /**
- * @param FunctionLikeParameter[] $params
- */
- private function alterParams(
- Codebase $codebase,
- FunctionLikeStorage $storage,
- array $params,
- Context $context
- ): void {
- foreach ($this->function->params as $param) {
- $param_name_node = null;
-
- if ($param->type instanceof PhpParser\Node\Name) {
- $param_name_node = $param->type;
- } elseif ($param->type instanceof PhpParser\Node\NullableType
- && $param->type->type instanceof PhpParser\Node\Name
- ) {
- $param_name_node = $param->type->type;
- }
-
- if ($param_name_node) {
- $resolved_name = ClassLikeAnalyzer::getFQCLNFromNameObject($param_name_node, $this->getAliases());
-
- $parent_fqcln = $this->getParentFQCLN();
-
- if ($resolved_name === 'self' && $context->self) {
- $resolved_name = $context->self;
- } elseif ($resolved_name === 'parent' && $parent_fqcln) {
- $resolved_name = $parent_fqcln;
- }
-
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $this,
- $param_name_node,
- $resolved_name,
- $context->calling_method_id,
- false,
- true
- );
- }
- }
-
- if ($this->function->returnType) {
- $return_name_node = null;
-
- if ($this->function->returnType instanceof PhpParser\Node\Name) {
- $return_name_node = $this->function->returnType;
- } elseif ($this->function->returnType instanceof PhpParser\Node\NullableType
- && $this->function->returnType->type instanceof PhpParser\Node\Name
- ) {
- $return_name_node = $this->function->returnType->type;
- }
-
- if ($return_name_node) {
- $resolved_name = ClassLikeAnalyzer::getFQCLNFromNameObject($return_name_node, $this->getAliases());
-
- $parent_fqcln = $this->getParentFQCLN();
-
- if ($resolved_name === 'self' && $context->self) {
- $resolved_name = $context->self;
- } elseif ($resolved_name === 'parent' && $parent_fqcln) {
- $resolved_name = $parent_fqcln;
- }
-
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $this,
- $return_name_node,
- $resolved_name,
- $context->calling_method_id,
- false,
- true
- );
- }
- }
-
- if ($storage->return_type
- && $storage->return_type_location
- && $storage->return_type_location !== $storage->signature_return_type_location
- ) {
- $replace_type = TypeExpander::expandUnion(
- $codebase,
- $storage->return_type,
- $context->self,
- 'static',
- $this->getParentFQCLN(),
- false
- );
-
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $this,
- $replace_type,
- $storage->return_type_location,
- $context->calling_method_id
- );
- }
-
- foreach ($params as $function_param) {
- if ($function_param->type
- && $function_param->type_location
- && $function_param->type_location !== $function_param->signature_type_location
- && $function_param->type_location->file_path === $this->getFilePath()
- ) {
- $replace_type = TypeExpander::expandUnion(
- $codebase,
- $function_param->type,
- $context->self,
- 'static',
- $this->getParentFQCLN(),
- false
- );
-
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $this,
- $replace_type,
- $function_param->type_location,
- $context->calling_method_id
- );
- }
- }
- }
-
- /**
- * @param array<PhpParser\Node\Stmt> $function_stmts
- */
- public function verifyReturnType(
- array $function_stmts,
- StatementsAnalyzer $statements_analyzer,
- ?Union $return_type = null,
- ?string $fq_class_name = null,
- ?CodeLocation $return_type_location = null,
- bool $did_explicitly_return = false,
- bool $closure_inside_call = false
- ): void {
- ReturnTypeAnalyzer::verifyReturnType(
- $this->function,
- $function_stmts,
- $statements_analyzer,
- $statements_analyzer->node_data,
- $this,
- $return_type,
- $fq_class_name,
- $fq_class_name,
- $return_type_location,
- [],
- $did_explicitly_return,
- $closure_inside_call
- );
- }
-
- public function addOrUpdateParamType(
- ProjectAnalyzer $project_analyzer,
- string $param_name,
- Union $inferred_return_type,
- bool $docblock_only = false
- ): void {
- $manipulator = FunctionDocblockManipulator::getForFunction(
- $project_analyzer,
- $this->source->getFilePath(),
- $this->function
- );
-
- $codebase = $project_analyzer->getCodebase();
- $is_final = true;
- $fqcln = $this->source->getFQCLN();
-
- if ($fqcln !== null && $this instanceof MethodAnalyzer) {
- $class_storage = $codebase->classlike_storage_provider->get($fqcln);
- $is_final = $this->function->isFinal() || $class_storage->final;
- }
-
- $allow_native_type = !$docblock_only
- && $codebase->php_major_version >= 7
- && (
- $codebase->allow_backwards_incompatible_changes
- || $is_final
- || !$this instanceof MethodAnalyzer
- );
-
- $manipulator->setParamType(
- $param_name,
- $allow_native_type
- ? $inferred_return_type->toPhpString(
- $this->source->getNamespace(),
- $this->source->getAliasedClassesFlipped(),
- $this->source->getFQCLN(),
- $project_analyzer->getCodebase()->php_major_version,
- $project_analyzer->getCodebase()->php_minor_version
- ) : null,
- $inferred_return_type->toNamespacedString(
- $this->source->getNamespace(),
- $this->source->getAliasedClassesFlipped(),
- $this->source->getFQCLN(),
- false
- ),
- $inferred_return_type->toNamespacedString(
- $this->source->getNamespace(),
- $this->source->getAliasedClassesFlipped(),
- $this->source->getFQCLN(),
- true
- )
- );
- }
-
- /**
- * Adds return types for the given function
- */
- public function addReturnTypes(Context $context): void
- {
- if ($this->return_vars_in_scope !== null) {
- $this->return_vars_in_scope = TypeAnalyzer::combineKeyedTypes(
- $context->vars_in_scope,
- $this->return_vars_in_scope
- );
- } else {
- $this->return_vars_in_scope = $context->vars_in_scope;
- }
-
- if ($this->return_vars_possibly_in_scope !== null) {
- $this->return_vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $this->return_vars_possibly_in_scope
- );
- } else {
- $this->return_vars_possibly_in_scope = $context->vars_possibly_in_scope;
- }
- }
-
- public function examineParamTypes(
- StatementsAnalyzer $statements_analyzer,
- Context $context,
- Codebase $codebase,
- PhpParser\Node $stmt = null
- ): void {
- $storage = $this->getFunctionLikeStorage($statements_analyzer);
-
- foreach ($storage->params as $param) {
- if ($param->by_ref && isset($context->vars_in_scope['$' . $param->name]) && !$param->is_variadic) {
- $actual_type = $context->vars_in_scope['$' . $param->name];
- $param_out_type = $param->out_type ?: $param->type;
-
- if ($param_out_type && !$actual_type->hasMixed() && $param->location) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $actual_type,
- $param_out_type,
- $actual_type->ignore_nullable_issues,
- $actual_type->ignore_falsable_issues
- )
- ) {
- IssueBuffer::maybeAdd(
- new ReferenceConstraintViolation(
- 'Variable ' . '$' . $param->name . ' is limited to values of type '
- . $param_out_type->getId()
- . ' because it is passed by reference, '
- . $actual_type->getId() . ' type found. Use @param-out to specify '
- . 'a different output type',
- $stmt
- ? new CodeLocation($this, $stmt)
- : $param->location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
- }
-
- public function getMethodName(): ?string
- {
- if ($this->function instanceof ClassMethod) {
- return (string)$this->function->name;
- }
-
- return null;
- }
-
- public function getCorrectlyCasedMethodId(?string $context_self = null): string
- {
- if ($this->function instanceof ClassMethod) {
- $function_name = (string)$this->function->name;
-
- return ($context_self ?: $this->source->getFQCLN()) . '::' . $function_name;
- }
-
- if ($this->function instanceof Function_) {
- $namespace = $this->source->getNamespace();
-
- return ($namespace ? $namespace . '\\' : '') . $this->function->name;
- }
-
- if (!$this instanceof ClosureAnalyzer) {
- throw new UnexpectedValueException('This is weird');
- }
-
- return $this->getClosureId();
- }
-
- public function getFunctionLikeStorage(?StatementsAnalyzer $statements_analyzer = null): FunctionLikeStorage
- {
- $codebase = $this->codebase;
-
- if ($this->function instanceof ClassMethod && $this instanceof MethodAnalyzer) {
- $method_id = $this->getMethodId();
- $codebase_methods = $codebase->methods;
-
- try {
- return $codebase_methods->getStorage($method_id);
- } catch (UnexpectedValueException $e) {
- $declaring_method_id = $codebase_methods->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id === null) {
- throw new UnexpectedValueException('Cannot get storage for function that doesn‘t exist');
- }
-
- // happens for fake constructors
- return $codebase_methods->getStorage($declaring_method_id);
- }
- }
-
- if ($this instanceof FunctionAnalyzer) {
- $function_id = $this->getFunctionId();
- } elseif ($this instanceof ClosureAnalyzer) {
- $function_id = $this->getClosureId();
- } else {
- throw new UnexpectedValueException('This is weird');
- }
-
- return $codebase->functions->getStorage($statements_analyzer, $function_id);
- }
-
- /** @return non-empty-string */
- public function getId(): string
- {
- if ($this instanceof MethodAnalyzer) {
- return (string) $this->getMethodId();
- }
-
- if ($this instanceof FunctionAnalyzer) {
- return $this->getFunctionId();
- }
-
- if ($this instanceof ClosureAnalyzer) {
- return $this->getClosureId();
- }
-
- throw new UnexpectedValueException('This is weird');
- }
-
- /**
- * @return array<lowercase-string, string>
- */
- public function getAliasedClassesFlipped(): array
- {
- if ($this->source instanceof NamespaceAnalyzer ||
- $this->source instanceof FileAnalyzer ||
- $this->source instanceof ClassLikeAnalyzer
- ) {
- return $this->source->getAliasedClassesFlipped();
- }
-
- return [];
- }
-
- /**
- * @return array<string, string>
- */
- public function getAliasedClassesFlippedReplaceable(): array
- {
- if ($this->source instanceof NamespaceAnalyzer ||
- $this->source instanceof FileAnalyzer ||
- $this->source instanceof ClassLikeAnalyzer
- ) {
- return $this->source->getAliasedClassesFlippedReplaceable();
- }
-
- return [];
- }
-
- /**
- * @return array<string, array<string, Union>>|null
- */
- public function getTemplateTypeMap(): ?array
- {
- if ($this->source instanceof ClassLikeAnalyzer) {
- return ($this->source->getTemplateTypeMap() ?: [])
- + ($this->storage->template_types ?: []);
- }
-
- return $this->storage->template_types;
- }
-
- public function isStatic(): bool
- {
- return $this->is_static;
- }
-
- public function getCodebase(): Codebase
- {
- return $this->codebase;
- }
-
- /**
- * Get a list of suppressed issues
- *
- * @return array<string>
- */
- public function getSuppressedIssues(): array
- {
- return $this->suppressed_issues;
- }
-
- /**
- * @param array<int, string> $new_issues
- */
- public function addSuppressedIssues(array $new_issues): void
- {
- if (isset($new_issues[0])) {
- $new_issues = array_combine($new_issues, $new_issues);
- }
-
- $this->suppressed_issues = $new_issues + $this->suppressed_issues;
- }
-
- /**
- * @param array<int, string> $new_issues
- */
- public function removeSuppressedIssues(array $new_issues): void
- {
- if (isset($new_issues[0])) {
- $new_issues = array_combine($new_issues, $new_issues);
- }
-
- $this->suppressed_issues = array_diff_key($this->suppressed_issues, $new_issues);
- }
-
- /**
- * Adds a suppressed issue, useful when creating a method checker from scratch
- *
- */
- public function addSuppressedIssue(string $issue_name): void
- {
- $this->suppressed_issues[] = $issue_name;
- }
-
- public static function clearCache(): void
- {
- self::$no_effects_hashes = [];
- }
-
- public function getLocalReturnType(Union $storage_return_type, bool $final = false): Union
- {
- if ($this->local_return_type) {
- return $this->local_return_type;
- }
-
- $this->local_return_type = TypeExpander::expandUnion(
- $this->codebase,
- $storage_return_type,
- $this->getFQCLN(),
- $this->getFQCLN(),
- $this->getParentFQCLN(),
- true,
- true,
- $final
- );
-
- return $this->local_return_type;
- }
-
- /**
- * @return array{
- * MethodIdentifier|null,
- * MethodIdentifier|null,
- * ClassLikeStorage|null,
- * ?string,
- * ?string,
- * array<string, MethodIdentifier>
- * }|null
- */
- private function getFunctionInformation(
- Context $context,
- Codebase $codebase,
- NodeDataProvider $type_provider,
- FunctionLikeStorage $storage,
- bool $add_mutations
- ): ?array {
- $classlike_storage_provider = $codebase->classlike_storage_provider;
- $real_method_id = null;
- $method_id = null;
-
- $cased_method_id = null;
- $hash = null;
- $appearing_class_storage = null;
- $overridden_method_ids = [];
-
- if ($this instanceof MethodAnalyzer) {
- if (!$storage instanceof MethodStorage) {
- throw new UnexpectedValueException('$storage must be MethodStorage');
- }
-
- $real_method_id = $this->getMethodId();
-
- $method_id = $this->getMethodId($context->self);
-
- $fq_class_name = (string)$context->self;
- $appearing_class_storage = $classlike_storage_provider->get($fq_class_name);
-
- if ($add_mutations) {
- if (!$context->collect_initializations) {
- $hash = md5($real_method_id . '::' . $context->getScopeSummary());
-
- // if we know that the function has no effects on vars, we don't bother rechecking
- if (isset(self::$no_effects_hashes[$hash])) {
- return null;
- }
- }
- } elseif ($context->self) {
- if ($appearing_class_storage->template_types) {
- $template_params = [];
-
- foreach ($appearing_class_storage->template_types as $param_name => $template_map) {
- $key = array_keys($template_map)[0];
-
- $template_params[] = new Union([
- new TTemplateParam(
- $param_name,
- reset($template_map),
- $key
- )
- ]);
- }
-
- $this_object_type = new TGenericObject(
- $context->self,
- $template_params
- );
- } else {
- $this_object_type = new TNamedObject($context->self);
- }
-
- $this_object_type->was_static = !$storage->final;
-
- if ($this->storage instanceof MethodStorage && $this->storage->if_this_is_type) {
- $template_result = new TemplateResult($this->getTemplateTypeMap() ?? [], []);
-
- TemplateStandinTypeReplacer::replace(
- new Union([$this_object_type]),
- $template_result,
- $codebase,
- null,
- $this->storage->if_this_is_type
- );
-
- foreach ($context->vars_in_scope as $var_name => $var_type) {
- if (0 === mb_strpos($var_name, '$this->')) {
- TemplateInferredTypeReplacer::replace($var_type, $template_result, $codebase);
- }
- }
-
- $context->vars_in_scope['$this'] = $this->storage->if_this_is_type;
- } else {
- $context->vars_in_scope['$this'] = new Union([$this_object_type]);
- }
-
- if ($codebase->taint_flow_graph
- && $storage->specialize_call
- && $storage->location
- ) {
- $new_parent_node = DataFlowNode::getForAssignment('$this in ' . $method_id, $storage->location);
-
- $codebase->taint_flow_graph->addNode($new_parent_node);
- $context->vars_in_scope['$this']->parent_nodes += [$new_parent_node->id => $new_parent_node];
- }
-
- if ($storage->external_mutation_free
- && !$storage->mutation_free_inferred
- ) {
- $context->vars_in_scope['$this']->reference_free = true;
-
- if ($this->function->name->name !== '__construct') {
- $context->vars_in_scope['$this']->allow_mutations = false;
- }
- }
-
- $context->vars_possibly_in_scope['$this'] = true;
- }
-
- if ($appearing_class_storage->has_visitor_issues) {
- return null;
- }
-
- $cased_method_id = $fq_class_name . '::' . $storage->cased_name;
-
- $overridden_method_ids = $codebase->methods->getOverriddenMethodIds($method_id);
-
- $codeLocation = new CodeLocation(
- $this,
- $this->function,
- null,
- true
- );
-
- if ($overridden_method_ids
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- foreach ($overridden_method_ids as $overridden_method_id) {
- $parent_method_storage = $codebase->methods->getStorage($overridden_method_id);
-
- $overridden_fq_class_name = $overridden_method_id->fq_class_name;
-
- $parent_storage = $classlike_storage_provider->get($overridden_fq_class_name);
-
- if ($this->function->name->name === '__construct'
- && !$parent_storage->preserve_constructor_signature
- ) {
- continue;
- }
-
- $implementer_visibility = $storage->visibility;
-
- $implementer_appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
- $implementer_declaring_method_id = $real_method_id;
-
- $declaring_class_storage = $appearing_class_storage;
-
- if ($implementer_appearing_method_id
- && $implementer_appearing_method_id !== $implementer_declaring_method_id
- ) {
- $appearing_fq_class_name = $implementer_appearing_method_id->fq_class_name;
- $appearing_method_name = $implementer_appearing_method_id->method_name;
-
- $declaring_fq_class_name = $implementer_declaring_method_id->fq_class_name;
-
- $appearing_class_storage = $classlike_storage_provider->get(
- $appearing_fq_class_name
- );
-
- $declaring_class_storage = $classlike_storage_provider->get(
- $declaring_fq_class_name
- );
-
- if (isset($appearing_class_storage->trait_visibility_map[$appearing_method_name])) {
- $implementer_visibility
- = $appearing_class_storage->trait_visibility_map[$appearing_method_name];
- }
- }
-
- // we've already checked this in the class checker
- if (!isset($appearing_class_storage->class_implements[strtolower($overridden_fq_class_name)])) {
- MethodComparator::compare(
- $codebase,
- count($overridden_method_ids) === 1 ? $this->function : null,
- $declaring_class_storage,
- $parent_storage,
- $storage,
- $parent_method_storage,
- $fq_class_name,
- $implementer_visibility,
- $codeLocation,
- $storage->suppressed_issues
- );
- }
- }
- }
-
- MethodAnalyzer::checkMethodSignatureMustOmitReturnType($storage, $codeLocation);
-
- if (!$context->calling_method_id || !$context->collect_initializations) {
- $context->calling_method_id = strtolower((string)$method_id);
- }
- } elseif ($this instanceof FunctionAnalyzer) {
- $function_name = $this->function->name->name;
- $namespace_prefix = $this->getNamespace();
- $cased_method_id = ($namespace_prefix !== null ? $namespace_prefix . '\\' : '') . $function_name;
- $context->calling_function_id = strtolower($cased_method_id);
- } elseif ($this instanceof ClosureAnalyzer) {
- if ($storage->return_type) {
- $closure_return_type = TypeExpander::expandUnion(
- $codebase,
- $storage->return_type,
- $context->self,
- $context->self,
- $this->getParentFQCLN()
- );
- } else {
- $closure_return_type = Type::getMixed();
- }
-
- $closure_type = new TClosure(
- 'Closure',
- $storage->params,
- $closure_return_type
- );
-
- if ($storage instanceof FunctionStorage) {
- $closure_type->byref_uses = $storage->byref_uses;
- $closure_type->is_pure = $storage->pure;
- }
-
- $type_provider->setType(
- $this->function,
- new Union([
- $closure_type,
- ])
- );
- } else {
- throw new UnexpectedValueException('Impossible');
- }
-
- return [
- $real_method_id,
- $method_id,
- $appearing_class_storage,
- $hash,
- $cased_method_id,
- $overridden_method_ids
- ];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php
deleted file mode 100644
index 9c68e2e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/InterfaceAnalyzer.php
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use InvalidArgumentException;
-use LogicException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Issue\ParseError;
-use Psalm\Issue\UndefinedInterface;
-use Psalm\IssueBuffer;
-use UnexpectedValueException;
-
-/**
- * @internal
- */
-class InterfaceAnalyzer extends ClassLikeAnalyzer
-{
- public function __construct(
- PhpParser\Node\Stmt\Interface_ $interface,
- SourceAnalyzer $source,
- string $fq_interface_name
- ) {
- parent::__construct($interface, $source, $fq_interface_name);
- }
-
- public function analyze(): void
- {
- if (!$this->class instanceof PhpParser\Node\Stmt\Interface_) {
- throw new LogicException('Something went badly wrong');
- }
-
- $project_analyzer = $this->file_analyzer->project_analyzer;
- $codebase = $project_analyzer->getCodebase();
- $config = $project_analyzer->getConfig();
-
- if ($this->class->extends) {
- foreach ($this->class->extends as $extended_interface) {
- $extended_interface_name = self::getFQCLNFromNameObject(
- $extended_interface,
- $this->getAliases()
- );
-
- $parent_reference_location = new CodeLocation($this, $extended_interface);
-
- if (!$codebase->classOrInterfaceExists(
- $extended_interface_name,
- $parent_reference_location
- )) {
- // we should not normally get here
- return;
- }
-
- try {
- $extended_interface_storage = $codebase->classlike_storage_provider->get($extended_interface_name);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if (!$extended_interface_storage->is_interface) {
- $code_location = new CodeLocation(
- $this,
- $extended_interface
- );
-
- IssueBuffer::maybeAdd(
- new UndefinedInterface(
- $extended_interface_name . ' is not an interface',
- $code_location,
- $extended_interface_name
- ),
- $this->getSuppressedIssues()
- );
- }
-
- if ($codebase->store_node_types && $extended_interface_name) {
- $bounds = $parent_reference_location->getSelectionBounds();
-
- $codebase->analyzer->addOffsetReference(
- $this->getFilePath(),
- $bounds[0],
- $bounds[1],
- $extended_interface_name
- );
- }
- }
- }
-
- $fq_interface_name = $this->getFQCLN();
-
- if (!$fq_interface_name) {
- throw new UnexpectedValueException('bad');
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_interface_name);
- $interface_context = new Context($this->getFQCLN());
-
- AttributesAnalyzer::analyze(
- $this,
- $interface_context,
- $class_storage,
- $this->class->attrGroups,
- AttributesAnalyzer::TARGET_CLASS,
- $class_storage->suppressed_issues + $this->getSuppressedIssues()
- );
-
- foreach ($this->class->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
- $method_analyzer = new MethodAnalyzer($stmt, $this);
-
- $type_provider = new NodeDataProvider();
-
- $method_analyzer->analyze($interface_context, $type_provider);
-
- $actual_method_id = $method_analyzer->getMethodId();
-
- if ($stmt->name->name !== '__construct'
- && $config->reportIssueInFile('InvalidReturnType', $this->getFilePath())
- ) {
- ClassAnalyzer::analyzeClassMethodReturnType(
- $stmt,
- $method_analyzer,
- $this,
- $type_provider,
- $codebase,
- $class_storage,
- $fq_interface_name,
- $actual_method_id,
- $actual_method_id,
- false
- );
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Property) {
- IssueBuffer::maybeAdd(
- new ParseError(
- 'Interfaces cannot have properties',
- new CodeLocation($this, $stmt)
- )
- );
-
- return;
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/IssueData.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/IssueData.php
deleted file mode 100644
index 4ab16f2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/IssueData.php
+++ /dev/null
@@ -1,175 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use function str_pad;
-
-use const STR_PAD_LEFT;
-
-class IssueData
-{
- /**
- * @var string
- */
- public $severity;
-
- /**
- * @var int
- */
- public $line_from;
-
- /**
- * @var int
- */
- public $line_to;
-
- /**
- * @var string
- * @readonly
- */
- public $type;
-
- /**
- * @var string
- * @readonly
- */
- public $message;
-
- /**
- * @var string
- * @readonly
- */
- public $file_name;
-
- /**
- * @var string
- * @readonly
- */
- public $file_path;
-
- /**
- * @var string
- * @readonly
- */
- public $snippet;
-
- /**
- * @var string
- * @readonly
- */
- public $selected_text;
-
- /**
- * @var int
- */
- public $from;
-
- /**
- * @var int
- */
- public $to;
-
- /**
- * @var int
- */
- public $snippet_from;
-
- /**
- * @var int
- */
- public $snippet_to;
-
- /**
- * @var int
- * @readonly
- */
- public $column_from;
-
- /**
- * @var int
- * @readonly
- */
- public $column_to;
-
- /**
- * @var int
- */
- public $error_level;
-
- /**
- * @var int
- * @readonly
- */
- public $shortcode;
-
- /**
- * @var string
- * @readonly
- */
- public $link;
-
- /**
- * @var ?list<DataFlowNodeData|array{label: string, entry_path_type: string}>
- */
- public $taint_trace;
-
- /**
- * @var ?list<DataFlowNodeData>
- */
- public $other_references;
-
- /**
- * @var ?string
- * @readonly
- */
- public $dupe_key;
-
- /**
- * @param ?list<DataFlowNodeData|array{label: string, entry_path_type: string}> $taint_trace
- * @param ?list<DataFlowNodeData> $other_references
- */
- public function __construct(
- string $severity,
- int $line_from,
- int $line_to,
- string $type,
- string $message,
- string $file_name,
- string $file_path,
- string $snippet,
- string $selected_text,
- int $from,
- int $to,
- int $snippet_from,
- int $snippet_to,
- int $column_from,
- int $column_to,
- int $shortcode = 0,
- int $error_level = -1,
- ?array $taint_trace = null,
- array $other_references = null,
- ?string $dupe_key = null
- ) {
- $this->severity = $severity;
- $this->line_from = $line_from;
- $this->line_to = $line_to;
- $this->type = $type;
- $this->message = $message;
- $this->file_name = $file_name;
- $this->file_path = $file_path;
- $this->snippet = $snippet;
- $this->selected_text = $selected_text;
- $this->from = $from;
- $this->to = $to;
- $this->snippet_from = $snippet_from;
- $this->snippet_to = $snippet_to;
- $this->column_from = $column_from;
- $this->column_to = $column_to;
- $this->shortcode = $shortcode;
- $this->error_level = $error_level;
- $this->link = $shortcode ? 'https://psalm.dev/' . str_pad((string) $shortcode, 3, "0", STR_PAD_LEFT) : '';
- $this->taint_trace = $taint_trace;
- $this->other_references = $other_references;
- $this->dupe_key = $dupe_key;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodAnalyzer.php
deleted file mode 100644
index a3b02c1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodAnalyzer.php
+++ /dev/null
@@ -1,290 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use LogicException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Issue\InvalidStaticInvocation;
-use Psalm\Issue\MethodSignatureMustOmitReturnType;
-use Psalm\Issue\NonStaticSelfCall;
-use Psalm\Issue\UndefinedMethod;
-use Psalm\IssueBuffer;
-use Psalm\StatementsSource;
-use Psalm\Storage\MethodStorage;
-use UnexpectedValueException;
-
-use function in_array;
-use function strtolower;
-
-/**
- * @internal
- * @extends FunctionLikeAnalyzer<PhpParser\Node\Stmt\ClassMethod>
- */
-class MethodAnalyzer extends FunctionLikeAnalyzer
-{
- public function __construct(
- PhpParser\Node\Stmt\ClassMethod $function,
- SourceAnalyzer $source,
- ?MethodStorage $storage = null
- ) {
- $codebase = $source->getCodebase();
-
- $method_name_lc = strtolower((string) $function->name);
-
- $source_fqcln = (string) $source->getFQCLN();
-
- $source_fqcln_lc = strtolower($source_fqcln);
-
- $method_id = new MethodIdentifier($source_fqcln, $method_name_lc);
-
- if (!$storage) {
- try {
- $storage = $codebase->methods->getStorage($method_id);
- } catch (UnexpectedValueException $e) {
- $class_storage = $codebase->classlike_storage_provider->get($source_fqcln_lc);
-
- if (!$class_storage->parent_classes) {
- throw $e;
- }
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if (!$declaring_method_id) {
- throw $e;
- }
-
- // happens for fake constructors
- $storage = $codebase->methods->getStorage($declaring_method_id);
- }
- }
-
- parent::__construct($function, $source, $storage);
- }
-
- /**
- * Determines whether a given method is static or not
- * @param array<string> $suppressed_issues
- */
- public static function checkStatic(
- MethodIdentifier $method_id,
- bool $self_call,
- bool $is_context_dynamic,
- Codebase $codebase,
- CodeLocation $code_location,
- array $suppressed_issues,
- ?bool &$is_dynamic_this_method = false
- ): bool {
- $codebase_methods = $codebase->methods;
-
- if ($method_id->fq_class_name === 'Closure'
- && $method_id->method_name === 'fromcallable'
- ) {
- return true;
- }
-
- $original_method_id = $method_id;
-
- $method_id = $codebase_methods->getDeclaringMethodId($method_id);
-
- if (!$method_id) {
- if (InternalCallMapHandler::inCallMap((string) $original_method_id)) {
- return true;
- }
-
- throw new LogicException('Declaring method for ' . $original_method_id . ' should not be null');
- }
-
- $storage = $codebase_methods->getStorage($method_id);
-
- if (!$storage->is_static) {
- if ($self_call) {
- if (!$is_context_dynamic) {
- if (IssueBuffer::accepts(
- new NonStaticSelfCall(
- 'Method ' . $codebase_methods->getCasedMethodId($method_id) .
- ' is not static, but is called ' .
- 'using self::',
- $code_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- } else {
- $is_dynamic_this_method = true;
- }
- } else {
- if (IssueBuffer::accepts(
- new InvalidStaticInvocation(
- 'Method ' . $codebase_methods->getCasedMethodId($method_id) .
- ' is not static, but is called ' .
- 'statically',
- $code_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
- }
-
- return true;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param lowercase-string|null $calling_method_id
- *
- */
- public static function checkMethodExists(
- Codebase $codebase,
- MethodIdentifier $method_id,
- CodeLocation $code_location,
- array $suppressed_issues,
- ?string $calling_method_id = null
- ): ?bool {
- if ($codebase->methods->methodExists(
- $method_id,
- $calling_method_id,
- !$calling_method_id
- || $calling_method_id !== strtolower((string) $method_id)
- ? $code_location
- : null,
- null,
- $code_location->file_path
- )) {
- return true;
- }
-
- if (IssueBuffer::accepts(
- new UndefinedMethod('Method ' . $method_id . ' does not exist', $code_location, (string) $method_id),
- $suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
- public static function isMethodVisible(
- MethodIdentifier $method_id,
- Context $context,
- StatementsSource $source
- ): bool {
- $codebase = $source->getCodebase();
-
- $fq_classlike_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- if ($codebase->methods->visibility_provider->has($fq_classlike_name)) {
- $method_visible = $codebase->methods->visibility_provider->isMethodVisible(
- $source,
- $fq_classlike_name,
- $method_name,
- $context,
- null
- );
-
- if ($method_visible !== null) {
- return $method_visible;
- }
- }
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if (!$declaring_method_id) {
- // this can happen for methods in the callmap that were not reflected
- return true;
- }
-
- $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
-
- $appearing_method_class = null;
-
- if ($appearing_method_id) {
- $appearing_method_class = $appearing_method_id->fq_class_name;
-
- // if the calling class is the same, we know the method exists, so it must be visible
- if ($appearing_method_class === $context->self) {
- return true;
- }
- }
-
- $declaring_method_class = $declaring_method_id->fq_class_name;
-
- if ($source->getSource() instanceof TraitAnalyzer
- && strtolower($declaring_method_class) === strtolower((string) $source->getFQCLN())
- ) {
- return true;
- }
-
- $storage = $codebase->methods->getStorage($declaring_method_id);
-
- switch ($storage->visibility) {
- case ClassLikeAnalyzer::VISIBILITY_PUBLIC:
- return true;
-
- case ClassLikeAnalyzer::VISIBILITY_PRIVATE:
- return $context->self && $appearing_method_class === $context->self;
-
- case ClassLikeAnalyzer::VISIBILITY_PROTECTED:
- if (!$context->self) {
- return false;
- }
-
- if ($appearing_method_class
- && $codebase->classExtends($appearing_method_class, $context->self)
- ) {
- return true;
- }
-
- if ($appearing_method_class
- && !$codebase->classExtends($context->self, $appearing_method_class)
- ) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Check that __clone, __construct, and __destruct do not have a return type
- * hint in their signature.
- */
- public static function checkMethodSignatureMustOmitReturnType(
- MethodStorage $method_storage,
- CodeLocation $code_location
- ): void {
- if ($method_storage->signature_return_type === null) {
- return;
- }
-
- $cased_method_name = $method_storage->cased_name;
- $methodsOfInterest = ['__clone', '__construct', '__destruct'];
-
- if (in_array($cased_method_name, $methodsOfInterest)) {
- IssueBuffer::maybeAdd(
- new MethodSignatureMustOmitReturnType(
- 'Method ' . $cased_method_name . ' must not declare a return type',
- $code_location
- )
- );
- }
- }
-
- public function getMethodId(?string $context_self = null): MethodIdentifier
- {
- $function_name = (string)$this->function->name;
-
- return new MethodIdentifier(
- $context_self ?: (string) $this->source->getFQCLN(),
- strtolower($function_name)
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodComparator.php
deleted file mode 100644
index 16ff2d9..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/MethodComparator.php
+++ /dev/null
@@ -1,1080 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser\Node\Stmt\ClassMethod;
-use PhpParser\NodeTraverser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\PhpVisitor\ParamReplacementVisitor;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ConstructorSignatureMismatch;
-use Psalm\Issue\ImplementedParamTypeMismatch;
-use Psalm\Issue\ImplementedReturnTypeMismatch;
-use Psalm\Issue\LessSpecificImplementedReturnType;
-use Psalm\Issue\MethodSignatureMismatch;
-use Psalm\Issue\MissingImmutableAnnotation;
-use Psalm\Issue\MoreSpecificImplementedParamType;
-use Psalm\Issue\OverriddenMethodAccess;
-use Psalm\Issue\ParamNameMismatch;
-use Psalm\Issue\TraitMethodSignatureMismatch;
-use Psalm\IssueBuffer;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function in_array;
-use function strpos;
-use function strtolower;
-
-class MethodComparator
-{
- /**
- * @param string[] $suppressed_issues
- *
- * @return false|null
- *
- * @psalm-suppress PossiblyUnusedReturnValue unused but seems important
- */
- public static function compare(
- Codebase $codebase,
- ?ClassMethod $stmt,
- ClassLikeStorage $implementer_classlike_storage,
- ClassLikeStorage $guide_classlike_storage,
- MethodStorage $implementer_method_storage,
- MethodStorage $guide_method_storage,
- string $implementer_called_class_name,
- int $implementer_visibility,
- CodeLocation $code_location,
- array $suppressed_issues,
- bool $prevent_abstract_override = true,
- bool $prevent_method_signature_mismatch = true
- ): ?bool {
- $implementer_method_id = new MethodIdentifier(
- $implementer_classlike_storage->name,
- strtolower($guide_method_storage->cased_name ?: '')
- );
-
- $implementer_declaring_method_id = $codebase->methods->getDeclaringMethodId(
- $implementer_method_id
- );
-
- $cased_implementer_method_id = $implementer_classlike_storage->name . '::'
- . $implementer_method_storage->cased_name;
-
- $cased_guide_method_id = $guide_classlike_storage->name . '::' . $guide_method_storage->cased_name;
-
- $codebase->methods->file_reference_provider->addMethodDependencyToClassMember(
- strtolower((string)($implementer_declaring_method_id ?? $implementer_method_id)),
- strtolower($guide_classlike_storage->name . '::' . $guide_method_storage->cased_name)
- );
-
- self::checkForObviousMethodMismatches(
- $guide_classlike_storage,
- $implementer_classlike_storage,
- $guide_method_storage,
- $implementer_method_storage,
- $guide_method_storage->visibility,
- $implementer_visibility,
- $cased_guide_method_id,
- $cased_implementer_method_id,
- $prevent_method_signature_mismatch,
- $prevent_abstract_override,
- $codebase->php_major_version >= 8,
- $code_location,
- $suppressed_issues
- );
-
- if ($guide_method_storage->signature_return_type && $prevent_method_signature_mismatch) {
- self::compareMethodSignatureReturnTypes(
- $codebase,
- $guide_classlike_storage,
- $implementer_classlike_storage,
- $guide_method_storage,
- $implementer_method_storage,
- $guide_method_storage->signature_return_type,
- $cased_guide_method_id,
- $implementer_called_class_name,
- $cased_implementer_method_id,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($guide_method_storage->return_type
- && $implementer_method_storage->return_type
- && !$implementer_method_storage->inherited_return_type
- && ($guide_method_storage->signature_return_type !== $guide_method_storage->return_type
- || $implementer_method_storage->signature_return_type !== $implementer_method_storage->return_type)
- && $implementer_classlike_storage->user_defined
- && (!$guide_classlike_storage->stubbed || $guide_classlike_storage->template_types)
- ) {
- self::compareMethodDocblockReturnTypes(
- $codebase,
- $guide_classlike_storage,
- $implementer_classlike_storage,
- $implementer_method_storage,
- $guide_method_storage->return_type,
- $implementer_method_storage->return_type,
- $cased_guide_method_id,
- $implementer_called_class_name,
- $implementer_declaring_method_id,
- $code_location,
- $suppressed_issues
- );
- }
-
- foreach ($guide_method_storage->params as $i => $guide_param) {
- if (!isset($implementer_method_storage->params[$i])) {
- if (!$prevent_abstract_override && $i >= $guide_method_storage->required_param_count) {
- continue;
- }
-
- if (IssueBuffer::accepts(
- new MethodSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' has fewer parameters than parent method ' .
- $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
- self::compareMethodParams(
- $codebase,
- $stmt,
- $implementer_classlike_storage,
- $guide_classlike_storage,
- $implementer_called_class_name,
- $guide_method_storage,
- $implementer_method_storage,
- $guide_param,
- $implementer_method_storage->params[$i],
- $i,
- $cased_guide_method_id,
- $cased_implementer_method_id,
- $prevent_method_signature_mismatch,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($guide_classlike_storage->user_defined
- && ($guide_classlike_storage->is_interface
- || $guide_classlike_storage->preserve_constructor_signature
- || $implementer_method_storage->cased_name !== '__construct')
- && $implementer_method_storage->required_param_count > $guide_method_storage->required_param_count
- ) {
- if ($implementer_method_storage->cased_name !== '__construct') {
- if (IssueBuffer::accepts(
- new MethodSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' has more required parameters than parent method ' .
- $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- )) {
- return false;
- }
- } else {
- if (IssueBuffer::accepts(
- new ConstructorSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' has more required parameters than parent method ' .
- $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- )) {
- return false;
- }
- }
-
-
- return null;
- }
-
- return null;
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function checkForObviousMethodMismatches(
- ClassLikeStorage $guide_classlike_storage,
- ClassLikeStorage $implementer_classlike_storage,
- MethodStorage $guide_method_storage,
- MethodStorage $implementer_method_storage,
- int $guide_visibility,
- int $implementer_visibility,
- string $cased_guide_method_id,
- string $cased_implementer_method_id,
- bool $prevent_method_signature_mismatch,
- bool $prevent_abstract_override,
- bool $trait_mismatches_are_fatal,
- CodeLocation $code_location,
- array $suppressed_issues
- ): void {
- if ($implementer_visibility > $guide_visibility) {
- if ($trait_mismatches_are_fatal
- || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait
- || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits)
- || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name
- || (!$implementer_method_storage->abstract
- && !$guide_method_storage->abstract)
- ) {
- IssueBuffer::maybeAdd(
- new OverriddenMethodAccess(
- 'Method ' . $cased_implementer_method_id . ' has different access level than '
- . $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- } elseif (IssueBuffer::accepts(
- new TraitMethodSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' has different access level than '
- . $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- )) {
- // fall through
- }
- }
-
- if ($guide_method_storage->final
- && $prevent_method_signature_mismatch
- && $prevent_abstract_override
- ) {
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Method ' . $cased_guide_method_id . ' is declared final and cannot be overridden',
- $code_location
- ),
- $guide_method_storage->final_from_docblock ?
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues :
- []
- );
- }
-
- if ($prevent_abstract_override
- && !$guide_method_storage->abstract
- && $implementer_method_storage->abstract
- && !$guide_classlike_storage->abstract
- && !$guide_classlike_storage->is_interface
- ) {
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' cannot be abstract when inherited method '
- . $cased_guide_method_id . ' is non-abstract',
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
-
- if ($guide_method_storage->external_mutation_free
- && !$implementer_method_storage->external_mutation_free
- && !$guide_method_storage->mutation_free_inferred
- && $prevent_method_signature_mismatch
- ) {
- IssueBuffer::maybeAdd(
- new MissingImmutableAnnotation(
- $cased_guide_method_id . ' is marked @psalm-immutable, but '
- . $implementer_classlike_storage->name . '::'
- . ($guide_method_storage->cased_name ?: '')
- . ' is not marked @psalm-immutable',
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function compareMethodParams(
- Codebase $codebase,
- ?ClassMethod $stmt,
- ClassLikeStorage $implementer_classlike_storage,
- ClassLikeStorage $guide_classlike_storage,
- string $implementer_called_class_name,
- MethodStorage $guide_method_storage,
- MethodStorage $implementer_method_storage,
- FunctionLikeParameter $guide_param,
- FunctionLikeParameter $implementer_param,
- int $i,
- string $cased_guide_method_id,
- string $cased_implementer_method_id,
- bool $prevent_method_signature_mismatch,
- CodeLocation $code_location,
- array $suppressed_issues
- ): void {
- if ($prevent_method_signature_mismatch) {
- if (!$guide_classlike_storage->user_defined
- && $guide_param->type
- ) {
- $implementer_param_type = $implementer_param->signature_type;
-
- $guide_param_signature_type = $guide_param->type;
-
- $or_null_guide_param_signature_type = $guide_param->signature_type
- ? clone $guide_param->signature_type
- : null;
-
- if ($or_null_guide_param_signature_type) {
- $or_null_guide_param_signature_type->addType(new TNull);
- }
-
- if ($cased_guide_method_id === 'Serializable::unserialize') {
- $guide_param_signature_type = null;
- $or_null_guide_param_signature_type = null;
- }
-
- if (!$guide_param->type->hasMixed()
- && !$guide_param->type->from_docblock
- && ($implementer_param_type || $guide_param_signature_type)
- ) {
- $config = Config::getInstance();
-
- if ($implementer_param_type
- && (!$guide_param_signature_type
- || strtolower($implementer_param_type->getId())
- !== strtolower($guide_param_signature_type->getId()))
- && (!$or_null_guide_param_signature_type
- || strtolower($implementer_param_type->getId())
- !== strtolower($or_null_guide_param_signature_type->getId()))
- ) {
- if ($implementer_method_storage->cased_name === '__construct') {
- IssueBuffer::maybeAdd(
- new ConstructorSignatureMismatch(
- 'Argument ' . ($i + 1) . ' of '
- . $cased_implementer_method_id . ' has wrong type \''
- . $implementer_param_type . '\', expecting \''
- . $guide_param_signature_type . '\' as defined by '
- . $cased_guide_method_id,
- $implementer_param->location
- && $config->isInProjectDirs(
- $implementer_param->location->file_path
- )
- ? $implementer_param->location
- : $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Argument ' . ($i + 1) . ' of '
- . $cased_implementer_method_id . ' has wrong type \''
- . $implementer_param_type . '\', expecting \''
- . $guide_param_signature_type . '\' as defined by '
- . $cased_guide_method_id,
- $implementer_param->location
- && $config->isInProjectDirs(
- $implementer_param->location->file_path
- )
- ? $implementer_param->location
- : $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
-
-
- return;
- }
- }
- }
-
- $config = Config::getInstance();
-
- if ($guide_param->name !== $implementer_param->name
- && $guide_method_storage->allow_named_arg_calls
- && $guide_classlike_storage->user_defined
- && $implementer_classlike_storage->user_defined
- && $implementer_param->location
- && $guide_method_storage->cased_name
- && strpos($guide_method_storage->cased_name, '__') !== 0
- && $config->isInProjectDirs(
- $implementer_param->location->file_path
- )
- ) {
- if ($config->allow_named_arg_calls
- || ($guide_classlike_storage->location
- && !$config->isInProjectDirs($guide_classlike_storage->location->file_path)
- )
- ) {
- if ($codebase->alter_code) {
- $project_analyzer = ProjectAnalyzer::getInstance();
-
- if ($stmt && isset($project_analyzer->getIssuesToFix()['ParamNameMismatch'])) {
- $param_replacer = new ParamReplacementVisitor(
- $implementer_param->name,
- $guide_param->name
- );
-
- $traverser = new NodeTraverser();
- $traverser->addVisitor($param_replacer);
- $traverser->traverse([$stmt]);
-
- if ($replacements = $param_replacer->getReplacements()) {
- FileManipulationBuffer::add(
- $implementer_param->location->file_path,
- $replacements
- );
- }
- }
- } else {
- IssueBuffer::maybeAdd(
- new ParamNameMismatch(
- 'Argument ' . ($i + 1) . ' of ' . $cased_implementer_method_id . ' has wrong name $'
- . $implementer_param->name . ', expecting $'
- . $guide_param->name . ' as defined by '
- . $cased_guide_method_id,
- $implementer_param->location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- }
- }
-
- if ($guide_classlike_storage->user_defined
- && $implementer_param->signature_type
- ) {
- self::compareMethodSignatureParams(
- $codebase,
- $i,
- $guide_classlike_storage,
- $implementer_classlike_storage,
- $guide_method_storage,
- $implementer_method_storage,
- $guide_param,
- $implementer_param->signature_type,
- $cased_guide_method_id,
- $cased_implementer_method_id,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($implementer_param->type
- && $guide_param->type
- && $implementer_param->type->getId() !== $guide_param->type->getId()
- ) {
- self::compareMethodDocblockParams(
- $codebase,
- $i,
- $guide_classlike_storage,
- $implementer_classlike_storage,
- $implementer_called_class_name,
- $guide_method_storage,
- $implementer_method_storage,
- $cased_guide_method_id,
- $cased_implementer_method_id,
- $guide_param->type,
- $implementer_param->type,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($guide_classlike_storage->user_defined && $implementer_param->by_ref !== $guide_param->by_ref) {
- $config = Config::getInstance();
-
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Argument ' . ($i + 1) . ' of ' . $cased_implementer_method_id . ' is' .
- ($implementer_param->by_ref ? '' : ' not') . ' passed by reference, but argument ' .
- ($i + 1) . ' of ' . $cased_guide_method_id . ' is' . ($guide_param->by_ref ? '' : ' not'),
- $implementer_param->location
- && $config->isInProjectDirs(
- $implementer_param->location->file_path
- )
- ? $implementer_param->location
- : $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function compareMethodSignatureParams(
- Codebase $codebase,
- int $i,
- ClassLikeStorage $guide_classlike_storage,
- ClassLikeStorage $implementer_classlike_storage,
- MethodStorage $guide_method_storage,
- MethodStorage $implementer_method_storage,
- FunctionLikeParameter $guide_param,
- Union $implementer_param_signature_type,
- string $cased_guide_method_id,
- string $cased_implementer_method_id,
- CodeLocation $code_location,
- array $suppressed_issues
- ): void {
- $guide_param_signature_type = $guide_param->signature_type
- ? TypeExpander::expandUnion(
- $codebase,
- $guide_param->signature_type,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->parent_class
- : $guide_classlike_storage->parent_class
- )
- : null;
-
- $implementer_param_signature_type = TypeExpander::expandUnion(
- $codebase,
- $implementer_param_signature_type,
- $implementer_classlike_storage->name,
- $implementer_classlike_storage->name,
- $implementer_classlike_storage->parent_class
- );
-
- $is_contained_by = (($codebase->php_major_version === 7
- && $codebase->php_minor_version === 4)
- || $codebase->php_major_version >= 8)
- && $guide_param_signature_type
- ? UnionTypeComparator::isContainedBy(
- $codebase,
- $guide_param_signature_type,
- $implementer_param_signature_type
- )
- : UnionTypeComparator::isContainedByInPhp(
- $guide_param_signature_type,
- $implementer_param_signature_type
- );
- if (!$is_contained_by) {
- $config = Config::getInstance();
-
- if ($codebase->php_major_version >= 8
- || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait
- || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits)
- || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name
- || (!$implementer_method_storage->abstract
- && !$guide_method_storage->abstract)
- ) {
- if ($implementer_method_storage->cased_name === '__construct') {
- IssueBuffer::maybeAdd(
- new ConstructorSignatureMismatch(
- 'Argument ' . ($i + 1) . ' of '
- . $cased_implementer_method_id
- . ' has wrong type \''
- . $implementer_param_signature_type . '\', expecting \''
- . $guide_param_signature_type . '\' as defined by '
- . $cased_guide_method_id,
- $implementer_method_storage->params[$i]->location
- && $config->isInProjectDirs(
- $implementer_method_storage->params[$i]->location->file_path
- )
- ? $implementer_method_storage->params[$i]->location
- : $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Argument ' . ($i + 1) . ' of '
- . $cased_implementer_method_id
- . ' has wrong type \''
- . $implementer_param_signature_type . '\', expecting \''
- . $guide_param_signature_type . '\' as defined by '
- . $cased_guide_method_id,
- $implementer_method_storage->params[$i]->location
- && $config->isInProjectDirs(
- $implementer_method_storage->params[$i]->location->file_path
- )
- ? $implementer_method_storage->params[$i]->location
- : $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- } else {
- IssueBuffer::maybeAdd(
- new TraitMethodSignatureMismatch(
- 'Argument ' . ($i + 1) . ' of ' . $cased_implementer_method_id . ' has wrong type \'' .
- $implementer_param_signature_type . '\', expecting \'' .
- $guide_param_signature_type . '\' as defined by ' .
- $cased_guide_method_id,
- $implementer_method_storage->params[$i]->location
- && $config->isInProjectDirs(
- $implementer_method_storage->params[$i]->location->file_path
- )
- ? $implementer_method_storage->params[$i]->location
- : $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- }
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function compareMethodDocblockParams(
- Codebase $codebase,
- int $i,
- ClassLikeStorage $guide_classlike_storage,
- ClassLikeStorage $implementer_classlike_storage,
- string $implementer_called_class_name,
- MethodStorage $guide_method_storage,
- MethodStorage $implementer_method_storage,
- string $cased_guide_method_id,
- string $cased_implementer_method_id,
- Union $guide_param_type,
- Union $implementer_param_type,
- CodeLocation $code_location,
- array $suppressed_issues
- ): void {
- $implementer_method_storage_param_type = TypeExpander::expandUnion(
- $codebase,
- $implementer_param_type,
- $implementer_classlike_storage->name,
- $implementer_called_class_name,
- $implementer_classlike_storage->parent_class
- );
-
- $guide_method_storage_param_type = TypeExpander::expandUnion(
- $codebase,
- $guide_param_type,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->parent_class
- : $guide_classlike_storage->parent_class
- );
-
- $guide_class_name = $guide_classlike_storage->name;
-
- if ($implementer_classlike_storage->is_trait) {
- $implementer_called_class_storage = $codebase->classlike_storage_provider->get(
- $implementer_called_class_name
- );
-
- if (isset(
- $implementer_called_class_storage->template_extended_params[$implementer_classlike_storage->name]
- )) {
- self::transformTemplates(
- $implementer_called_class_storage->template_extended_params,
- $implementer_classlike_storage->name,
- $implementer_method_storage_param_type,
- $codebase
- );
-
- self::transformTemplates(
- $implementer_called_class_storage->template_extended_params,
- $guide_class_name,
- $guide_method_storage_param_type,
- $codebase
- );
- }
- }
-
- foreach ($implementer_method_storage_param_type->getAtomicTypes() as $k => $t) {
- if ($t instanceof TTemplateParam
- && strpos($t->defining_class, 'fn-') === 0
- ) {
- $implementer_method_storage_param_type->removeType($k);
-
- foreach ($t->as->getAtomicTypes() as $as_t) {
- $implementer_method_storage_param_type->addType($as_t);
- }
- }
- }
-
- foreach ($guide_method_storage_param_type->getAtomicTypes() as $k => $t) {
- if ($t instanceof TTemplateParam
- && strpos($t->defining_class, 'fn-') === 0
- ) {
- $guide_method_storage_param_type->removeType($k);
-
- foreach ($t->as->getAtomicTypes() as $as_t) {
- $guide_method_storage_param_type->addType($as_t);
- }
- }
- }
-
- if ($implementer_classlike_storage->template_extended_params) {
- self::transformTemplates(
- $implementer_classlike_storage->template_extended_params,
- $guide_class_name,
- $guide_method_storage_param_type,
- $codebase
- );
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $guide_method_storage_param_type,
- $implementer_method_storage_param_type,
- !$guide_classlike_storage->user_defined,
- !$guide_classlike_storage->user_defined,
- $union_comparison_results
- )) {
- // is the declared return type more specific than the inferred one?
- if ($union_comparison_results->type_coerced) {
- if ($guide_classlike_storage->user_defined) {
- IssueBuffer::maybeAdd(
- new MoreSpecificImplementedParamType(
- 'Argument ' . ($i + 1) . ' of ' . $cased_implementer_method_id
- . ' has the more specific type \'' .
- $implementer_method_storage_param_type->getId() . '\', expecting \'' .
- $guide_method_storage_param_type->getId() . '\' as defined by ' .
- $cased_guide_method_id,
- $implementer_method_storage->params[$i]->location
- ?: $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- } else {
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $implementer_method_storage_param_type,
- $guide_method_storage_param_type,
- !$guide_classlike_storage->user_defined,
- !$guide_classlike_storage->user_defined
- )) {
- if (IssueBuffer::accepts(
- new MoreSpecificImplementedParamType(
- 'Argument ' . ($i + 1) . ' of ' . $cased_implementer_method_id
- . ' has the more specific type \'' .
- $implementer_method_storage_param_type->getId() . '\', expecting \'' .
- $guide_method_storage_param_type->getId() . '\' as defined by ' .
- $cased_guide_method_id,
- $implementer_method_storage->params[$i]->location
- ?: $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- )) {
- // fall through
- }
- } else {
- if (IssueBuffer::accepts(
- new ImplementedParamTypeMismatch(
- 'Argument ' . ($i + 1) . ' of ' . $cased_implementer_method_id
- . ' has wrong type \'' .
- $implementer_method_storage_param_type->getId() . '\', expecting \'' .
- $guide_method_storage_param_type->getId() . '\' as defined by ' .
- $cased_guide_method_id,
- $implementer_method_storage->params[$i]->location
- ?: $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- )) {
- // fall through
- }
- }
- }
- }
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function compareMethodSignatureReturnTypes(
- Codebase $codebase,
- ClassLikeStorage $guide_classlike_storage,
- ClassLikeStorage $implementer_classlike_storage,
- MethodStorage $guide_method_storage,
- MethodStorage $implementer_method_storage,
- Union $guide_signature_return_type,
- string $cased_guide_method_id,
- string $implementer_called_class_name,
- string $cased_implementer_method_id,
- CodeLocation $code_location,
- array $suppressed_issues
- ): void {
- $guide_signature_return_type = TypeExpander::expandUnion(
- $codebase,
- $guide_signature_return_type,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- ($guide_classlike_storage->is_trait && $guide_method_storage->abstract)
- || $guide_classlike_storage->final
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->is_trait && $guide_method_storage->abstract
- ? $implementer_classlike_storage->parent_class
- : $guide_classlike_storage->parent_class,
- true,
- true,
- $implementer_method_storage->final
- );
-
- $implementer_signature_return_type = $implementer_method_storage->signature_return_type
- ? TypeExpander::expandUnion(
- $codebase,
- $implementer_method_storage->signature_return_type,
- $implementer_classlike_storage->is_trait
- ? $implementer_called_class_name
- : $implementer_classlike_storage->name,
- $implementer_classlike_storage->is_trait
- ? $implementer_called_class_name
- : $implementer_classlike_storage->name,
- $implementer_classlike_storage->parent_class
- ) : null;
-
- $is_contained_by = (($codebase->php_major_version === 7
- && $codebase->php_minor_version === 4)
- || $codebase->php_major_version >= 8)
- && $implementer_signature_return_type
- ? UnionTypeComparator::isContainedBy(
- $codebase,
- $implementer_signature_return_type,
- $guide_signature_return_type
- )
- : UnionTypeComparator::isContainedByInPhp($implementer_signature_return_type, $guide_signature_return_type);
-
- if (!$is_contained_by) {
- if ($codebase->php_major_version >= 8
- || $guide_classlike_storage->is_trait === $implementer_classlike_storage->is_trait
- || !in_array($guide_classlike_storage->name, $implementer_classlike_storage->used_traits)
- || $implementer_method_storage->defining_fqcln !== $implementer_classlike_storage->name
- || (!$implementer_method_storage->abstract
- && !$guide_method_storage->abstract)
- ) {
- IssueBuffer::maybeAdd(
- new MethodSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' with return type \''
- . $implementer_signature_return_type . '\' is different to return type \''
- . $guide_signature_return_type . '\' of inherited method ' . $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new TraitMethodSignatureMismatch(
- 'Method ' . $cased_implementer_method_id . ' with return type \''
- . $implementer_signature_return_type . '\' is different to return type \''
- . $guide_signature_return_type . '\' of inherited method ' . $cased_guide_method_id,
- $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- }
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function compareMethodDocblockReturnTypes(
- Codebase $codebase,
- ClassLikeStorage $guide_classlike_storage,
- ClassLikeStorage $implementer_classlike_storage,
- MethodStorage $implementer_method_storage,
- Union $guide_return_type,
- Union $implementer_return_type,
- string $cased_guide_method_id,
- string $implementer_called_class_name,
- ?MethodIdentifier $implementer_declaring_method_id,
- CodeLocation $code_location,
- array $suppressed_issues
- ): void {
- $implementer_method_storage_return_type = TypeExpander::expandUnion(
- $codebase,
- $implementer_return_type,
- $implementer_classlike_storage->is_trait
- ? $implementer_called_class_name
- : $implementer_classlike_storage->name,
- $implementer_called_class_name,
- $implementer_classlike_storage->parent_class
- );
-
- $guide_method_storage_return_type = TypeExpander::expandUnion(
- $codebase,
- $guide_return_type,
- $guide_classlike_storage->is_trait
- ? $implementer_classlike_storage->name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->is_trait
- || $implementer_method_storage->final
- ? $implementer_called_class_name
- : $guide_classlike_storage->name,
- $guide_classlike_storage->parent_class,
- true,
- true,
- $implementer_method_storage->final
- );
-
- $guide_class_name = $guide_classlike_storage->name;
-
- if ($implementer_classlike_storage->template_extended_params) {
- self::transformTemplates(
- $implementer_classlike_storage->template_extended_params,
- $guide_class_name,
- $guide_method_storage_return_type,
- $codebase
- );
-
- if ($implementer_method_storage->defining_fqcln) {
- self::transformTemplates(
- $implementer_classlike_storage->template_extended_params,
- $implementer_method_storage->defining_fqcln,
- $implementer_method_storage_return_type,
- $codebase
- );
- }
- }
-
- if ($implementer_classlike_storage->is_trait) {
- $implementer_called_class_storage = $codebase->classlike_storage_provider->get(
- $implementer_called_class_name
- );
-
- if ($implementer_called_class_storage->template_extended_params) {
- self::transformTemplates(
- $implementer_called_class_storage->template_extended_params,
- $implementer_classlike_storage->name,
- $implementer_method_storage_return_type,
- $codebase
- );
-
- self::transformTemplates(
- $implementer_called_class_storage->template_extended_params,
- $guide_class_name,
- $guide_method_storage_return_type,
- $codebase
- );
- }
- }
-
- // treat void as null when comparing against docblock implementer
- if ($implementer_method_storage_return_type->isVoid()) {
- $implementer_method_storage_return_type = Type::getNull();
- }
-
- if ($guide_method_storage_return_type->isVoid()) {
- $guide_method_storage_return_type = Type::getNull();
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $implementer_method_storage_return_type,
- $guide_method_storage_return_type,
- false,
- false,
- $union_comparison_results
- )) {
- // is the declared return type more specific than the inferred one?
- if ($union_comparison_results->type_coerced) {
- IssueBuffer::maybeAdd(
- new LessSpecificImplementedReturnType(
- 'The inherited return type \'' . $guide_method_storage_return_type->getId()
- . '\' for ' . $cased_guide_method_id . ' is more specific than the implemented '
- . 'return type for ' . $implementer_declaring_method_id . ' \''
- . $implementer_method_storage_return_type->getId() . '\'',
- $implementer_method_storage->return_type_location
- ?: $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new ImplementedReturnTypeMismatch(
- 'The inherited return type \'' . $guide_method_storage_return_type->getId()
- . '\' for ' . $cased_guide_method_id . ' is different to the implemented '
- . 'return type for ' . $implementer_declaring_method_id . ' \''
- . $implementer_method_storage_return_type->getId() . '\'',
- $implementer_method_storage->return_type_location
- ?: $code_location
- ),
- $suppressed_issues + $implementer_classlike_storage->suppressed_issues
- );
- }
- }
- }
-
- /**
- * @param array<string, array<string, Union>> $template_extended_params
- */
- private static function transformTemplates(
- array $template_extended_params,
- string $base_class_name,
- Union $templated_type,
- Codebase $codebase
- ): void {
- if (isset($template_extended_params[$base_class_name])) {
- $map = $template_extended_params[$base_class_name];
-
- $template_types = [];
-
- foreach ($map as $key => $mapped_type) {
- $new_bases = [];
-
- foreach ($mapped_type->getTemplateTypes() as $mapped_atomic_type) {
- if ($mapped_atomic_type->defining_class === $base_class_name) {
- continue;
- }
-
- $new_bases[] = $mapped_atomic_type->defining_class;
- }
-
- if ($new_bases) {
- $mapped_type = clone $mapped_type;
-
- foreach ($new_bases as $new_base_class_name) {
- self::transformTemplates(
- $template_extended_params,
- $new_base_class_name,
- $mapped_type,
- $codebase
- );
- }
- }
-
- $template_types[$key][$base_class_name] = $mapped_type;
- }
-
- $template_result = new TemplateResult([], $template_types);
-
- TemplateInferredTypeReplacer::replace(
- $templated_type,
- $template_result,
- $codebase
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php
deleted file mode 100644
index 06413e6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php
+++ /dev/null
@@ -1,280 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use InvalidArgumentException;
-use PhpParser;
-use PhpParser\Node\Stmt\Namespace_;
-use Psalm\Context;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Type;
-use Psalm\Type\Union;
-use ReflectionProperty;
-use UnexpectedValueException;
-
-use function assert;
-use function count;
-use function implode;
-use function preg_replace;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class NamespaceAnalyzer extends SourceAnalyzer
-{
- use CanAlias;
-
- /**
- * @var FileAnalyzer
- * @psalm-suppress NonInvariantDocblockPropertyType
- */
- protected $source;
-
- /**
- * @var Namespace_
- */
- private $namespace;
-
- /**
- * @var string
- */
- private $namespace_name;
-
- /**
- * A lookup table for public namespace constants
- *
- * @var array<string, array<string, Union>>
- */
- protected static $public_namespace_constants = [];
-
- public function __construct(Namespace_ $namespace, FileAnalyzer $source)
- {
- $this->source = $source;
- $this->namespace = $namespace;
- $this->namespace_name = $this->namespace->name ? implode('\\', $this->namespace->name->parts) : '';
- }
-
- public function collectAnalyzableInformation(): void
- {
- $leftover_stmts = [];
-
- if (!isset(self::$public_namespace_constants[$this->namespace_name])) {
- self::$public_namespace_constants[$this->namespace_name] = [];
- }
-
- $codebase = $this->getCodebase();
-
- foreach ($this->namespace->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassLike) {
- $this->collectAnalyzableClassLike($stmt);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Use_) {
- $this->visitUse($stmt);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\GroupUse) {
- $this->visitGroupUse($stmt);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Const_) {
- foreach ($stmt->consts as $const) {
- self::$public_namespace_constants[$this->namespace_name][$const->name->name] = Type::getMixed();
- }
-
- $leftover_stmts[] = $stmt;
- } else {
- $leftover_stmts[] = $stmt;
- }
- }
-
- if ($leftover_stmts) {
- $statements_analyzer = new StatementsAnalyzer($this, new NodeDataProvider());
- $context = new Context();
- $context->is_global = true;
- $context->defineGlobals();
- $context->collect_exceptions = $codebase->config->check_for_throws_in_global_scope;
- $statements_analyzer->analyze($leftover_stmts, $context, null, true);
-
- $file_context = $this->source->context;
- if ($file_context) {
- $file_context->mergeExceptions($context);
- }
- }
- }
-
- public function collectAnalyzableClassLike(PhpParser\Node\Stmt\ClassLike $stmt): void
- {
- if (!$stmt->name) {
- throw new UnexpectedValueException('Did not expect anonymous class here');
- }
-
- $fq_class_name = Type::getFQCLNFromString($stmt->name->name, $this->getAliases());
-
- if ($stmt instanceof PhpParser\Node\Stmt\Class_ || $stmt instanceof PhpParser\Node\Stmt\Enum_) {
- $this->source->addNamespacedClassAnalyzer(
- $fq_class_name,
- new ClassAnalyzer($stmt, $this, $fq_class_name)
- );
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Interface_) {
- $this->source->addNamespacedInterfaceAnalyzer(
- $fq_class_name,
- new InterfaceAnalyzer($stmt, $this, $fq_class_name)
- );
- }
- }
-
- public function getNamespace(): string
- {
- return $this->namespace_name;
- }
-
- public function setConstType(string $const_name, Union $const_type): void
- {
- self::$public_namespace_constants[$this->namespace_name][$const_name] = $const_type;
- }
-
- /**
- * @return array<string, Union>
- */
- public static function getConstantsForNamespace(string $namespace_name, int $visibility): array
- {
- // @todo this does not allow for loading in namespace constants not already defined in the current sweep
- if (!isset(self::$public_namespace_constants[$namespace_name])) {
- self::$public_namespace_constants[$namespace_name] = [];
- }
-
- if ($visibility === ReflectionProperty::IS_PUBLIC) {
- return self::$public_namespace_constants[$namespace_name];
- }
-
- throw new InvalidArgumentException('Given $visibility not supported');
- }
-
- public function getFileAnalyzer(): FileAnalyzer
- {
- return $this->source;
- }
-
- /**
- * Returns true if $calling_identifier is the same as, or is within with $identifier, in a
- * case-insensitive comparison. Identifiers can be namespaces, classlikes, functions, or methods.
- *
- * @psalm-pure
- *
- * @throws InvalidArgumentException if $identifier is not a valid identifier
- */
- public static function isWithin(string $calling_identifier, string $identifier): bool
- {
- $normalized_calling_ident = self::normalizeIdentifier($calling_identifier);
- $normalized_ident = self::normalizeIdentifier($identifier);
-
- if ($normalized_calling_ident === $normalized_ident) {
- return true;
- }
-
- $normalized_calling_ident_parts = self::getIdentifierParts($normalized_calling_ident);
- $normalized_ident_parts = self::getIdentifierParts($normalized_ident);
-
- if (count($normalized_calling_ident_parts) < count($normalized_ident_parts)) {
- return false;
- }
-
- for ($i = 0; $i < count($normalized_ident_parts); ++$i) {
- if ($normalized_ident_parts[$i] !== $normalized_calling_ident_parts[$i]) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Returns true if $calling_identifier is the same as or is within any identifier
- * in $identifiers in a case-insensitive comparison, or if $identifiers is empty.
- * Identifiers can be namespaces, classlikes, functions, or methods.
- *
- * @psalm-pure
- *
- * @psalm-assert-if-false !empty $identifiers
- *
- * @param list<string> $identifiers
- */
- public static function isWithinAny(string $calling_identifier, array $identifiers): bool
- {
- if (count($identifiers) === 0) {
- return true;
- }
-
- foreach ($identifiers as $identifier) {
- if (self::isWithin($calling_identifier, $identifier)) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * @param non-empty-string $fullyQualifiedClassName, e.g. '\Psalm\Internal\Analyzer\NamespaceAnalyzer'
- *
- * @return non-empty-string , e.g. 'Psalm'
- *
- * @psalm-pure
- */
- public static function getNameSpaceRoot(string $fullyQualifiedClassName): string
- {
- $root_namespace = preg_replace('/^([^\\\]+).*/', '$1', $fullyQualifiedClassName);
- if ($root_namespace === "") {
- throw new InvalidArgumentException("Invalid classname \"$fullyQualifiedClassName\"");
- }
- return $root_namespace;
- }
-
- /**
- * @return ($lowercase is true ? lowercase-string : string)
- *
- * @psalm-pure
- */
- public static function normalizeIdentifier(string $identifier, bool $lowercase = true): string
- {
- if ($identifier === "") {
- return "";
- }
-
- $identifier = $identifier[0] === "\\" ? substr($identifier, 1) : $identifier;
- return $lowercase ? strtolower($identifier) : $identifier;
- }
-
- /**
- * Splits an identifier into parts, eg `Foo\Bar::baz` becomes ["Foo", "\\", "Bar", "::", "baz"].
- *
- * @return list<non-empty-string>
- *
- * @psalm-pure
- */
- public static function getIdentifierParts(string $identifier): array
- {
- $parts = [];
- while (($pos = strpos($identifier, "\\")) !== false) {
- if ($pos > 0) {
- $part = substr($identifier, 0, $pos);
- assert($part !== "");
- $parts[] = $part;
- }
- $parts[] = "\\";
- $identifier = substr($identifier, $pos + 1);
- }
- if (($pos = strpos($identifier, "::")) !== false) {
- if ($pos > 0) {
- $part = substr($identifier, 0, $pos);
- assert($part !== "");
- $parts[] = $part;
- }
- $parts[] = "::";
- $identifier = substr($identifier, $pos + 2);
- }
- if ($identifier !== "") {
- $parts[] = $identifier;
- }
-
- return $parts;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php
deleted file mode 100644
index 40b3316..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php
+++ /dev/null
@@ -1,1527 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use Amp\Loop;
-use InvalidArgumentException;
-use LogicException;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Exception\RefactorException;
-use Psalm\Exception\UnsupportedIssueToFixException;
-use Psalm\FileManipulation;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\LanguageServer\LanguageServer;
-use Psalm\Internal\LanguageServer\ProtocolStreamReader;
-use Psalm\Internal\LanguageServer\ProtocolStreamWriter;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\ParserCacheProvider;
-use Psalm\Internal\Provider\ProjectCacheProvider;
-use Psalm\Internal\Provider\Providers;
-use Psalm\Internal\Provider\StatementsProvider;
-use Psalm\Issue\CodeIssue;
-use Psalm\Issue\InvalidFalsableReturnType;
-use Psalm\Issue\InvalidNullableReturnType;
-use Psalm\Issue\InvalidReturnType;
-use Psalm\Issue\LessSpecificReturnType;
-use Psalm\Issue\MismatchingDocblockParamType;
-use Psalm\Issue\MismatchingDocblockReturnType;
-use Psalm\Issue\MissingClosureReturnType;
-use Psalm\Issue\MissingParamType;
-use Psalm\Issue\MissingPropertyType;
-use Psalm\Issue\MissingReturnType;
-use Psalm\Issue\ParamNameMismatch;
-use Psalm\Issue\PossiblyUndefinedGlobalVariable;
-use Psalm\Issue\PossiblyUndefinedVariable;
-use Psalm\Issue\PossiblyUnusedMethod;
-use Psalm\Issue\PossiblyUnusedProperty;
-use Psalm\Issue\RedundantCast;
-use Psalm\Issue\RedundantCastGivenDocblockType;
-use Psalm\Issue\UnnecessaryVarAnnotation;
-use Psalm\Issue\UnusedMethod;
-use Psalm\Issue\UnusedProperty;
-use Psalm\Issue\UnusedVariable;
-use Psalm\Plugin\EventHandler\Event\AfterCodebasePopulatedEvent;
-use Psalm\Progress\Progress;
-use Psalm\Progress\VoidProgress;
-use Psalm\Report;
-use Psalm\Report\ReportOptions;
-use Psalm\Type;
-use ReflectionProperty;
-use RuntimeException;
-use UnexpectedValueException;
-
-use function array_combine;
-use function array_diff;
-use function array_fill_keys;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_shift;
-use function cli_set_process_title;
-use function count;
-use function defined;
-use function dirname;
-use function end;
-use function explode;
-use function extension_loaded;
-use function file_exists;
-use function file_get_contents;
-use function filter_var;
-use function function_exists;
-use function fwrite;
-use function implode;
-use function in_array;
-use function ini_get;
-use function is_dir;
-use function is_file;
-use function is_int;
-use function is_readable;
-use function is_string;
-use function microtime;
-use function mkdir;
-use function number_format;
-use function pcntl_fork;
-use function preg_match;
-use function rename;
-use function shell_exec;
-use function stream_set_blocking;
-use function stream_socket_accept;
-use function stream_socket_client;
-use function stream_socket_server;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-use function substr_count;
-use function trim;
-use function usort;
-use function version_compare;
-
-use const FILTER_VALIDATE_INT;
-use const PHP_EOL;
-use const PHP_OS;
-use const PHP_VERSION;
-use const PSALM_VERSION;
-use const STDERR;
-use const STDIN;
-use const STDOUT;
-
-/**
- * @internal
- */
-class ProjectAnalyzer
-{
- /**
- * Cached config
- *
- * @var Config
- */
- private $config;
-
- /**
- * @var self
- */
- public static $instance;
-
- /**
- * An object representing everything we know about the code
- *
- * @var Codebase
- */
- private $codebase;
-
- /** @var FileProvider */
- private $file_provider;
-
- /** @var ClassLikeStorageProvider */
- private $classlike_storage_provider;
-
- /** @var ?ParserCacheProvider */
- private $parser_cache_provider;
-
- /** @var ?ProjectCacheProvider */
- public $project_cache_provider;
-
- /** @var FileReferenceProvider */
- private $file_reference_provider;
-
- /**
- * @var Progress
- */
- public $progress;
-
- /**
- * @var bool
- */
- public $debug_lines = false;
-
- /**
- * @var bool
- */
- public $debug_performance = false;
-
- /**
- * @var bool
- */
- public $show_issues = true;
-
- /** @var int */
- public $threads;
-
- /**
- * @var array<string, bool>
- */
- private $issues_to_fix = [];
-
- /**
- * @var bool
- */
- public $dry_run = false;
-
- /**
- * @var bool
- */
- public $full_run = false;
-
- /**
- * @var bool
- */
- public $only_replace_php_types_with_non_docblock_types = false;
-
- /**
- * @var ?int
- */
- public $onchange_line_limit;
-
- /**
- * @var bool
- */
- public $provide_completion = false;
-
- /**
- * @var array<string,string>
- */
- private $project_files = [];
-
- /**
- * @var array<string,string>
- */
- private $extra_files = [];
-
- /**
- * @var array<string, string>
- */
- private $to_refactor = [];
-
- /**
- * @var ?ReportOptions
- */
- public $stdout_report_options;
-
- /**
- * @var array<ReportOptions>
- */
- public $generated_report_options;
-
- /**
- * @var array<int, class-string<CodeIssue>>
- */
- private const SUPPORTED_ISSUES_TO_FIX = [
- InvalidFalsableReturnType::class,
- InvalidNullableReturnType::class,
- InvalidReturnType::class,
- LessSpecificReturnType::class,
- MismatchingDocblockParamType::class,
- MismatchingDocblockReturnType::class,
- MissingClosureReturnType::class,
- MissingParamType::class,
- MissingPropertyType::class,
- MissingReturnType::class,
- ParamNameMismatch::class,
- PossiblyUndefinedGlobalVariable::class,
- PossiblyUndefinedVariable::class,
- PossiblyUnusedMethod::class,
- PossiblyUnusedProperty::class,
- RedundantCast::class,
- RedundantCastGivenDocblockType::class,
- UnusedMethod::class,
- UnusedProperty::class,
- UnusedVariable::class,
- UnnecessaryVarAnnotation::class,
- ];
-
- /**
- * When this is true, the language server will send the diagnostic code with a help link.
- *
- * @var bool
- */
- public $language_server_use_extended_diagnostic_codes = false;
-
- /**
- * If this is true then the language server will send log messages to the client with additional information.
- *
- * @var bool
- */
- public $language_server_verbose = false;
-
- /**
- * @param array<ReportOptions> $generated_report_options
- */
- public function __construct(
- Config $config,
- Providers $providers,
- ?ReportOptions $stdout_report_options = null,
- array $generated_report_options = [],
- int $threads = 1,
- ?Progress $progress = null
- ) {
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- $this->parser_cache_provider = $providers->parser_cache_provider;
- $this->project_cache_provider = $providers->project_cache_provider;
- $this->file_provider = $providers->file_provider;
- $this->classlike_storage_provider = $providers->classlike_storage_provider;
- $this->file_reference_provider = $providers->file_reference_provider;
-
- $this->progress = $progress;
- $this->threads = $threads;
- $this->config = $config;
-
- $this->clearCacheDirectoryIfConfigOrComposerLockfileChanged();
-
- $this->codebase = new Codebase(
- $config,
- $providers,
- $progress
- );
-
- $this->stdout_report_options = $stdout_report_options;
- $this->generated_report_options = $generated_report_options;
-
- $file_extensions = $this->config->getFileExtensions();
-
- foreach ($this->config->getProjectDirectories() as $dir_name) {
- $file_paths = $this->file_provider->getFilesInDir(
- $dir_name,
- $file_extensions,
- [$this->config, 'isInProjectDirs']
- );
-
- foreach ($file_paths as $file_path) {
- $this->addProjectFile($file_path);
- }
- }
-
- foreach ($this->config->getExtraDirectories() as $dir_name) {
- $file_paths = $this->file_provider->getFilesInDir(
- $dir_name,
- $file_extensions,
- [$this->config, 'isInExtraDirs']
- );
-
- foreach ($file_paths as $file_path) {
- $this->addExtraFile($file_path);
- }
- }
-
- foreach ($this->config->getProjectFiles() as $file_path) {
- $this->addProjectFile($file_path);
- }
-
- self::$instance = $this;
- }
-
- private function clearCacheDirectoryIfConfigOrComposerLockfileChanged(): void
- {
- if ($this->project_cache_provider
- && $this->project_cache_provider->hasLockfileChanged()
- ) {
- $this->progress->debug(
- 'Composer lockfile change detected, clearing cache' . "\n"
- );
-
- $cache_directory = $this->config->getCacheDirectory();
- if ($cache_directory !== null) {
- Config::removeCacheDirectory($cache_directory);
- }
-
- if ($this->file_reference_provider->cache) {
- $this->file_reference_provider->cache->hasConfigChanged();
- }
-
- $this->project_cache_provider->updateComposerLockHash();
- } elseif ($this->file_reference_provider->cache
- && $this->file_reference_provider->cache->hasConfigChanged()
- ) {
- $this->progress->debug(
- 'Config change detected, clearing cache' . "\n"
- );
-
- $cache_directory = $this->config->getCacheDirectory();
- if ($cache_directory !== null) {
- Config::removeCacheDirectory($cache_directory);
- }
-
- if ($this->project_cache_provider) {
- $this->project_cache_provider->hasLockfileChanged();
- }
- }
- }
-
- /**
- * @param array<string> $report_file_paths
- * @return list<ReportOptions>
- */
- public static function getFileReportOptions(array $report_file_paths, bool $show_info = true): array
- {
- $report_options = [];
-
- $mapping = [
- 'checkstyle.xml' => Report::TYPE_CHECKSTYLE,
- 'sonarqube.json' => Report::TYPE_SONARQUBE,
- 'codeclimate.json' => Report::TYPE_CODECLIMATE,
- 'summary.json' => Report::TYPE_JSON_SUMMARY,
- 'junit.xml' => Report::TYPE_JUNIT,
- '.xml' => Report::TYPE_XML,
- '.json' => Report::TYPE_JSON,
- '.txt' => Report::TYPE_TEXT,
- '.emacs' => Report::TYPE_EMACS,
- '.pylint' => Report::TYPE_PYLINT,
- '.console' => Report::TYPE_CONSOLE,
- '.sarif' => Report::TYPE_SARIF,
- 'count.txt' => Report::TYPE_COUNT,
- ];
-
- foreach ($report_file_paths as $report_file_path) {
- foreach ($mapping as $extension => $type) {
- if (substr($report_file_path, -strlen($extension)) === $extension) {
- $o = new ReportOptions();
-
- $o->format = $type;
- $o->show_info = $show_info;
- $o->output_path = $report_file_path;
- $o->use_color = false;
- $report_options[] = $o;
- continue 2;
- }
- }
-
- throw new UnexpectedValueException('Unknown report format ' . $report_file_path);
- }
-
- return $report_options;
- }
-
- private function visitAutoloadFiles(): void
- {
- $start_time = microtime(true);
-
- $this->config->visitComposerAutoloadFiles($this, $this->progress);
-
- $now_time = microtime(true);
-
- $this->progress->debug(
- 'Visiting autoload files took ' . number_format($now_time - $start_time, 3) . 's' . "\n"
- );
- }
-
- public function server(?string $address = '127.0.0.1:12345', bool $socket_server_mode = false): void
- {
- $this->visitAutoloadFiles();
- $this->codebase->diff_methods = true;
- $this->file_reference_provider->loadReferenceCache();
- $this->codebase->enterServerMode();
-
- if (ini_get('pcre.jit') === '1'
- && PHP_OS === 'Darwin'
- && version_compare(PHP_VERSION, '7.3.0') >= 0
- && version_compare(PHP_VERSION, '7.4.0') < 0
- ) {
- // do nothing
- } else {
- $cpu_count = self::getCpuCount();
-
- // let's not go crazy
- $usable_cpus = $cpu_count - 2;
-
- if ($usable_cpus > 1) {
- $this->threads = $usable_cpus;
- }
- }
-
- $this->config->initializePlugins($this);
-
- foreach ($this->config->getProjectDirectories() as $dir_name) {
- $this->checkDirWithConfig($dir_name, $this->config);
- }
-
- @cli_set_process_title('Psalm ' . PSALM_VERSION . ' - PHP Language Server');
-
- if (!$socket_server_mode && $address) {
- // Connect to a TCP server
- $socket = stream_socket_client('tcp://' . $address, $errno, $errstr);
- if ($socket === false) {
- fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr");
- exit(1);
- }
- stream_set_blocking($socket, false);
- new LanguageServer(
- new ProtocolStreamReader($socket),
- new ProtocolStreamWriter($socket),
- $this
- );
- Loop::run();
- } elseif ($socket_server_mode && $address) {
- // Run a TCP Server
- $tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr);
- if ($tcpServer === false) {
- fwrite(STDERR, "Could not listen on $address. Error $errno\n$errstr");
- exit(1);
- }
- fwrite(STDOUT, "Server listening on $address\n");
-
- $fork_available = true;
- if (!extension_loaded('pcntl')) {
- fwrite(STDERR, "PCNTL is not available. Only a single connection will be accepted\n");
- $fork_available = false;
- }
-
- $disabled_functions = array_map('trim', explode(',', ini_get('disable_functions')));
- if (in_array('pcntl_fork', $disabled_functions)) {
- fwrite(
- STDERR,
- "pcntl_fork() is disabled by php configuration (disable_functions directive)."
- . " Only a single connection will be accepted\n"
- );
- $fork_available = false;
- }
-
- while ($socket = stream_socket_accept($tcpServer, -1)) {
- fwrite(STDOUT, "Connection accepted\n");
- stream_set_blocking($socket, false);
- if ($fork_available) {
- // If PCNTL is available, fork a child process for the connection
- // An exit notification will only terminate the child process
- $pid = pcntl_fork();
- if ($pid === -1) {
- fwrite(STDERR, "Could not fork\n");
- exit(1);
- }
-
- if ($pid === 0) {
- // Child process
- $reader = new ProtocolStreamReader($socket);
- $reader->on(
- 'close',
- function (): void {
- fwrite(STDOUT, "Connection closed\n");
- }
- );
- new LanguageServer(
- $reader,
- new ProtocolStreamWriter($socket),
- $this
- );
- // Just for safety
- exit(0);
- }
- } else {
- // If PCNTL is not available, we only accept one connection.
- // An exit notification will terminate the server
- new LanguageServer(
- new ProtocolStreamReader($socket),
- new ProtocolStreamWriter($socket),
- $this
- );
- Loop::run();
- }
- }
- } else {
- // Use STDIO
- stream_set_blocking(STDIN, false);
- new LanguageServer(
- new ProtocolStreamReader(STDIN),
- new ProtocolStreamWriter(STDOUT),
- $this
- );
- Loop::run();
- }
- }
-
- public static function getInstance(): ProjectAnalyzer
- {
- return self::$instance;
- }
-
- public function canReportIssues(string $file_path): bool
- {
- return isset($this->project_files[$file_path]);
- }
-
- private function generatePHPVersionMessage(): string
- {
- $codebase = $this->codebase;
-
- $version = $codebase->php_major_version . '.' . $codebase->php_minor_version;
-
- switch ($codebase->php_version_source) {
- case 'cli':
- $source = '(set by CLI argument)';
- break;
- case 'config':
- $source = '(set by config file)';
- break;
- case 'composer':
- $source = '(inferred from composer.json)';
- break;
- case 'tests':
- $source = '(set by tests)';
- break;
- case 'runtime':
- $source = '(inferred from current PHP version)';
- break;
- }
-
- return "Target PHP version: $version $source\n";
- }
-
- public function check(string $base_dir, bool $is_diff = false): void
- {
- $start_checks = (int)microtime(true);
-
- if (!$base_dir) {
- throw new InvalidArgumentException('Cannot work with empty base_dir');
- }
-
- $diff_files = null;
- $deleted_files = null;
-
- $this->full_run = true;
-
- $reference_cache = $this->file_reference_provider->loadReferenceCache(true);
-
- $this->codebase->diff_methods = $is_diff;
-
- if ($is_diff
- && $reference_cache
- && $this->project_cache_provider
- && $this->project_cache_provider->canDiffFiles()
- ) {
- $deleted_files = $this->file_reference_provider->getDeletedReferencedFiles();
- $diff_files = array_merge($deleted_files, $this->getDiffFiles());
- }
-
- $this->progress->write($this->generatePHPVersionMessage());
- $this->progress->startScanningFiles();
-
- $diff_no_files = false;
-
- if ($diff_files === null
- || $deleted_files === null
- || count($diff_files) > 200
- ) {
- $this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
- $this->visitAutoloadFiles();
-
- $this->codebase->scanner->addFilesToShallowScan($this->extra_files);
- $this->codebase->scanner->addFilesToDeepScan($this->project_files);
- $this->codebase->analyzer->addFilesToAnalyze($this->project_files);
-
- $this->config->initializePlugins($this);
-
- $this->codebase->scanFiles($this->threads);
-
- $this->codebase->infer_types_from_usage = true;
- } else {
- $this->progress->debug(count($diff_files) . ' changed files: ' . "\n");
- $this->progress->debug(' ' . implode("\n ", $diff_files) . "\n");
-
- $this->codebase->analyzer->addFilesToShowResults($this->project_files);
-
- if ($diff_files) {
- $file_list = $this->getReferencedFilesFromDiff($diff_files);
-
- // strip out deleted files
- $file_list = array_diff($file_list, $deleted_files);
-
- if ($file_list) {
- $this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
- $this->visitAutoloadFiles();
-
- $this->checkDiffFilesWithConfig($this->config, $file_list);
-
- $this->config->initializePlugins($this);
-
- $this->codebase->scanFiles($this->threads);
- } else {
- $diff_no_files = true;
- }
- } else {
- $diff_no_files = true;
- }
- }
-
- if (!$diff_no_files) {
- $this->config->visitStubFiles($this->codebase, $this->progress);
-
- $event = new AfterCodebasePopulatedEvent($this->codebase);
-
- $this->config->eventDispatcher->dispatchAfterCodebasePopulated($event);
- }
-
- $this->progress->startAnalyzingFiles();
-
- $this->codebase->analyzer->analyzeFiles(
- $this,
- $this->threads,
- $this->codebase->alter_code,
- true
- );
-
- if ($this->parser_cache_provider && !$is_diff) {
- $removed_parser_files = $this->parser_cache_provider->deleteOldParserCaches($start_checks);
-
- if ($removed_parser_files) {
- $this->progress->debug('Removed ' . $removed_parser_files . ' old parser caches' . "\n");
- }
- }
- }
-
- public function consolidateAnalyzedData(): void
- {
- $this->codebase->classlikes->consolidateAnalyzedData(
- $this->codebase->methods,
- $this->progress,
- (bool)$this->codebase->find_unused_code
- );
- }
-
- public function trackTaintedInputs(): void
- {
- $this->codebase->taint_flow_graph = new TaintFlowGraph();
- }
-
- public function trackUnusedSuppressions(): void
- {
- $this->codebase->track_unused_suppressions = true;
- }
-
- public function interpretRefactors(): void
- {
- if (!$this->codebase->alter_code) {
- throw new UnexpectedValueException('Should not be checking references');
- }
-
- // interpret wildcards
- foreach ($this->to_refactor as $source => $destination) {
- if (($source_pos = strpos($source, '*'))
- && ($destination_pos = strpos($destination, '*'))
- && $source_pos === (strlen($source) - 1)
- && $destination_pos === (strlen($destination) - 1)
- ) {
- foreach ($this->codebase->classlike_storage_provider->getAll() as $class_storage) {
- if (strpos($source, substr($class_storage->name, 0, $source_pos)) === 0) {
- $this->to_refactor[$class_storage->name]
- = substr($destination, 0, -1) . substr($class_storage->name, $source_pos);
- }
- }
-
- unset($this->to_refactor[$source]);
- }
- }
-
- foreach ($this->to_refactor as $source => $destination) {
- $source_parts = explode('::', $source);
- $destination_parts = explode('::', $destination);
-
- if (!$this->codebase->classlikes->hasFullyQualifiedClassName($source_parts[0])) {
- throw new RefactorException(
- 'Source class ' . $source_parts[0] . ' doesn’t exist'
- );
- }
-
- if (count($source_parts) === 1 && count($destination_parts) === 1) {
- if ($this->codebase->classlikes->hasFullyQualifiedClassName($destination_parts[0])) {
- throw new RefactorException(
- 'Destination class ' . $destination_parts[0] . ' already exists'
- );
- }
-
- $source_class_storage = $this->codebase->classlike_storage_provider->get($source_parts[0]);
-
- $destination_parts = explode('\\', $destination, -1);
- $destination_ns = implode('\\', $destination_parts);
-
- $this->codebase->classes_to_move[strtolower($source)] = $destination;
-
- $destination_class_storage = $this->codebase->classlike_storage_provider->create($destination);
-
- $destination_class_storage->name = $destination;
-
- if ($source_class_storage->aliases) {
- $destination_class_storage->aliases = clone $source_class_storage->aliases;
- $destination_class_storage->aliases->namespace = $destination_ns;
- }
-
- $destination_class_storage->location = $source_class_storage->location;
- $destination_class_storage->stmt_location = $source_class_storage->stmt_location;
- $destination_class_storage->populated = true;
-
- $this->codebase->class_transforms[strtolower($source)] = $destination;
-
- continue;
- }
-
- $source_method_id = new MethodIdentifier(
- $source_parts[0],
- strtolower($source_parts[1])
- );
-
- if ($this->codebase->methods->methodExists($source_method_id)) {
- if ($this->codebase->methods->methodExists(
- new MethodIdentifier(
- $destination_parts[0],
- strtolower($destination_parts[1])
- )
- )) {
- throw new RefactorException(
- 'Destination method ' . $destination . ' already exists'
- );
- }
-
- if (!$this->codebase->classlikes->classExists($destination_parts[0])) {
- throw new RefactorException(
- 'Destination class ' . $destination_parts[0] . ' doesn’t exist'
- );
- }
-
- $source_lc = strtolower($source);
- if (strtolower($source_parts[0]) !== strtolower($destination_parts[0])) {
- $source_method_storage = $this->codebase->methods->getStorage($source_method_id);
- $destination_class_storage
- = $this->codebase->classlike_storage_provider->get($destination_parts[0]);
-
- if (!$source_method_storage->is_static
- && !isset(
- $destination_class_storage->parent_classes[strtolower($source_method_id->fq_class_name)]
- )
- ) {
- throw new RefactorException(
- 'Cannot move non-static method ' . $source
- . ' into unrelated class ' . $destination_parts[0]
- );
- }
-
- $this->codebase->methods_to_move[$source_lc]= $destination;
- } else {
- $this->codebase->methods_to_rename[$source_lc] = $destination_parts[1];
- }
-
- $this->codebase->call_transforms[$source_lc . '\((.*\))'] = $destination . '($1)';
- continue;
- }
-
- if ($source_parts[1][0] === '$') {
- if ($destination_parts[1][0] !== '$') {
- throw new RefactorException(
- 'Destination property must be of the form Foo::$bar'
- );
- }
-
- if (!$this->codebase->properties->propertyExists($source, true)) {
- throw new RefactorException(
- 'Property ' . $source . ' does not exist'
- );
- }
-
- if ($this->codebase->properties->propertyExists($destination, true)) {
- throw new RefactorException(
- 'Destination property ' . $destination . ' already exists'
- );
- }
-
- if (!$this->codebase->classlikes->classExists($destination_parts[0])) {
- throw new RefactorException(
- 'Destination class ' . $destination_parts[0] . ' doesn’t exist'
- );
- }
-
- $source_id = strtolower($source_parts[0]) . '::' . $source_parts[1];
-
- if (strtolower($source_parts[0]) !== strtolower($destination_parts[0])) {
- $source_storage = $this->codebase->properties->getStorage($source);
-
- if (!$source_storage->is_static) {
- throw new RefactorException(
- 'Cannot move non-static property ' . $source
- );
- }
-
- $this->codebase->properties_to_move[$source_id] = $destination;
- } else {
- $this->codebase->properties_to_rename[$source_id] = substr($destination_parts[1], 1);
- }
-
- $this->codebase->property_transforms[$source_id] = $destination;
- continue;
- }
-
- $source_class_constants = $this->codebase->classlikes->getConstantsForClass(
- $source_parts[0],
- ReflectionProperty::IS_PRIVATE
- );
-
- if (isset($source_class_constants[$source_parts[1]])) {
- if (!$this->codebase->classlikes->hasFullyQualifiedClassName($destination_parts[0])) {
- throw new RefactorException(
- 'Destination class ' . $destination_parts[0] . ' doesn’t exist'
- );
- }
-
- $destination_class_constants = $this->codebase->classlikes->getConstantsForClass(
- $destination_parts[0],
- ReflectionProperty::IS_PRIVATE
- );
-
- if (isset($destination_class_constants[$destination_parts[1]])) {
- throw new RefactorException(
- 'Destination constant ' . $destination . ' already exists'
- );
- }
-
- $source_id = strtolower($source_parts[0]) . '::' . $source_parts[1];
-
- if (strtolower($source_parts[0]) !== strtolower($destination_parts[0])) {
- $this->codebase->class_constants_to_move[$source_id] = $destination;
- } else {
- $this->codebase->class_constants_to_rename[$source_id] = $destination_parts[1];
- }
-
- $this->codebase->class_constant_transforms[$source_id] = $destination;
- continue;
- }
-
- throw new RefactorException(
- 'Psalm cannot locate ' . $source
- );
- }
- }
-
- public function prepareMigration(): void
- {
- if (!$this->codebase->alter_code) {
- throw new UnexpectedValueException('Should not be checking references');
- }
-
- $this->codebase->classlikes->moveMethods(
- $this->codebase->methods,
- $this->progress
- );
-
- $this->codebase->classlikes->moveProperties(
- $this->codebase->properties,
- $this->progress
- );
-
- $this->codebase->classlikes->moveClassConstants(
- $this->progress
- );
- }
-
- public function migrateCode(): void
- {
- if (!$this->codebase->alter_code) {
- throw new UnexpectedValueException('Should not be checking references');
- }
-
- $migration_manipulations = FileManipulationBuffer::getMigrationManipulations(
- $this->codebase->file_provider
- );
-
- if ($migration_manipulations) {
- foreach ($migration_manipulations as $file_path => $file_manipulations) {
- usort(
- $file_manipulations,
- function (FileManipulation $a, FileManipulation $b): int {
- if ($a->start === $b->start) {
- if ($b->end === $a->end) {
- return $b->insertion_text > $a->insertion_text ? 1 : -1;
- }
-
- return $b->end > $a->end ? 1 : -1;
- }
-
- return $b->start > $a->start ? 1 : -1;
- }
- );
-
- $existing_contents = $this->codebase->file_provider->getContents($file_path);
-
- foreach ($file_manipulations as $manipulation) {
- $existing_contents = $manipulation->transform($existing_contents);
- }
-
- $this->codebase->file_provider->setContents($file_path, $existing_contents);
- }
- }
-
- if ($this->codebase->classes_to_move) {
- foreach ($this->codebase->classes_to_move as $source => $destination) {
- $source_class_storage = $this->codebase->classlike_storage_provider->get($source);
-
- if (!$source_class_storage->location) {
- continue;
- }
-
- $potential_file_path = $this->config->getPotentialComposerFilePathForClassLike($destination);
-
- if ($potential_file_path && !file_exists($potential_file_path)) {
- $containing_dir = dirname($potential_file_path);
-
- if (!file_exists($containing_dir)) {
- mkdir($containing_dir, 0777, true);
- }
-
- rename($source_class_storage->location->file_path, $potential_file_path);
- }
- }
- }
- }
-
- public function findReferencesTo(string $symbol): void
- {
- if (!$this->stdout_report_options) {
- throw new UnexpectedValueException('Not expecting to emit output');
- }
-
- $locations = $this->codebase->findReferencesToSymbol($symbol);
-
- foreach ($locations as $location) {
- $snippet = $location->getSnippet();
-
- $snippet_bounds = $location->getSnippetBounds();
- $selection_bounds = $location->getSelectionBounds();
-
- $selection_start = $selection_bounds[0] - $snippet_bounds[0];
- $selection_length = $selection_bounds[1] - $selection_bounds[0];
-
- echo $location->file_name . ':' . $location->getLineNumber() . "\n" .
- (
- $this->stdout_report_options->use_color
- ? substr($snippet, 0, $selection_start) .
- "\e[97;42m" . substr($snippet, $selection_start, $selection_length) .
- "\e[0m" . substr($snippet, $selection_length + $selection_start)
- : $snippet
- ) . "\n" . "\n";
- }
- }
-
- public function checkDir(string $dir_name): void
- {
- $this->file_reference_provider->loadReferenceCache();
-
- $this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
-
- $this->checkDirWithConfig($dir_name, $this->config, true);
-
- $this->progress->write($this->generatePHPVersionMessage());
- $this->progress->startScanningFiles();
-
- $this->config->initializePlugins($this);
-
- $this->codebase->scanFiles($this->threads);
-
- $this->config->visitStubFiles($this->codebase, $this->progress);
-
- $this->progress->startAnalyzingFiles();
-
- $this->codebase->analyzer->analyzeFiles(
- $this,
- $this->threads,
- $this->codebase->alter_code,
- $this->codebase->find_unused_code === 'always'
- );
- }
-
- private function checkDirWithConfig(string $dir_name, Config $config, bool $allow_non_project_files = false): void
- {
- $file_extensions = $config->getFileExtensions();
- $filter = $allow_non_project_files ? null : [$this->config, 'isInProjectDirs'];
-
- $file_paths = $this->file_provider->getFilesInDir(
- $dir_name,
- $file_extensions,
- $filter
- );
-
- $files_to_scan = [];
-
- foreach ($file_paths as $file_path) {
- $files_to_scan[$file_path] = $file_path;
- }
-
- $this->codebase->addFilesToAnalyze($files_to_scan);
- }
-
- public function addProjectFile(string $file_path): void
- {
- $this->project_files[$file_path] = $file_path;
- }
-
- public function addExtraFile(string $file_path): void
- {
- $this->extra_files[$file_path] = $file_path;
- }
-
- /**
- * @return list<string>
- */
- protected function getDiffFiles(): array
- {
- if (!$this->parser_cache_provider || !$this->project_cache_provider) {
- throw new UnexpectedValueException('Parser cache provider cannot be null here');
- }
-
- $diff_files = [];
-
- $last_run = $this->project_cache_provider->getLastRun(PSALM_VERSION);
-
- foreach ($this->project_files as $file_path) {
- if ($this->file_provider->getModifiedTime($file_path) >= $last_run
- && $this->parser_cache_provider->loadExistingFileContentsFromCache($file_path)
- !== $this->file_provider->getContents($file_path)
- ) {
- $diff_files[] = $file_path;
- }
- }
-
- return $diff_files;
- }
-
- /**
- * @param array<string> $file_list
- *
- */
- private function checkDiffFilesWithConfig(Config $config, array $file_list = []): void
- {
- $files_to_scan = [];
-
- foreach ($file_list as $file_path) {
- if (!$this->file_provider->fileExists($file_path)) {
- continue;
- }
-
- if (!$config->isInProjectDirs($file_path)) {
- $this->progress->debug('skipping ' . $file_path . "\n");
-
- continue;
- }
-
- $files_to_scan[$file_path] = $file_path;
- }
-
- $this->codebase->addFilesToAnalyze($files_to_scan);
- }
-
- public function checkFile(string $file_path): void
- {
- $this->progress->debug('Checking ' . $file_path . "\n");
-
- $this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
-
- $this->config->hide_external_errors = $this->config->isInProjectDirs($file_path);
-
- $this->codebase->addFilesToAnalyze([$file_path => $file_path]);
-
- $this->file_reference_provider->loadReferenceCache();
-
- $this->progress->write($this->generatePHPVersionMessage());
- $this->progress->startScanningFiles();
-
- $this->config->initializePlugins($this);
-
- $this->codebase->scanFiles($this->threads);
-
- $this->config->visitStubFiles($this->codebase, $this->progress);
-
- $this->progress->startAnalyzingFiles();
-
- $this->codebase->analyzer->analyzeFiles(
- $this,
- $this->threads,
- $this->codebase->alter_code,
- $this->codebase->find_unused_code === 'always'
- );
- }
-
- /**
- * @param string[] $paths_to_check
- */
- public function checkPaths(array $paths_to_check): void
- {
- $this->config->visitPreloadedStubFiles($this->codebase, $this->progress);
- $this->visitAutoloadFiles();
-
- $this->codebase->scanner->addFilesToShallowScan($this->extra_files);
-
- foreach ($paths_to_check as $path) {
- $this->progress->debug('Checking ' . $path . "\n");
-
- if (is_dir($path)) {
- $this->checkDirWithConfig($path, $this->config, true);
- } elseif (is_file($path)) {
- $this->codebase->addFilesToAnalyze([$path => $path]);
- $this->config->hide_external_errors = $this->config->isInProjectDirs($path);
- }
- }
-
- $this->file_reference_provider->loadReferenceCache();
-
- $this->progress->write($this->generatePHPVersionMessage());
- $this->progress->startScanningFiles();
-
- $this->config->initializePlugins($this);
-
-
- $this->codebase->scanFiles($this->threads);
-
- $this->config->visitStubFiles($this->codebase, $this->progress);
-
- $this->progress->startAnalyzingFiles();
-
- $this->codebase->analyzer->analyzeFiles(
- $this,
- $this->threads,
- $this->codebase->alter_code,
- $this->codebase->find_unused_code === 'always'
- );
-
- if ($this->stdout_report_options
- && in_array(
- $this->stdout_report_options->format,
- [Report::TYPE_CONSOLE, Report::TYPE_PHP_STORM]
- )
- && $this->codebase->collect_references
- ) {
- fwrite(
- STDERR,
- PHP_EOL . 'To whom it may concern: Psalm cannot detect unused classes, methods and properties'
- . PHP_EOL . 'when analyzing individual files and folders. Run on the full project to enable'
- . PHP_EOL . 'complete unused code detection.' . PHP_EOL
- );
- }
- }
-
- public function getConfig(): Config
- {
- return $this->config;
- }
-
- /**
- * @param array<string> $diff_files
- *
- * @return array<string, string>
- */
- public function getReferencedFilesFromDiff(array $diff_files, bool $include_referencing_files = true): array
- {
- $all_inherited_files_to_check = $diff_files;
-
- while ($diff_files) {
- $diff_file = array_shift($diff_files);
-
- $dependent_files = $this->file_reference_provider->getFilesInheritingFromFile($diff_file);
-
- $new_dependent_files = array_diff($dependent_files, $all_inherited_files_to_check);
-
- $all_inherited_files_to_check = array_merge($all_inherited_files_to_check, $new_dependent_files);
- $diff_files = array_merge($diff_files, $new_dependent_files);
- }
-
- $all_files_to_check = $all_inherited_files_to_check;
-
- if ($include_referencing_files) {
- foreach ($all_inherited_files_to_check as $file_name) {
- $dependent_files = $this->file_reference_provider->getFilesReferencingFile($file_name);
- $all_files_to_check = array_merge($dependent_files, $all_files_to_check);
- }
- }
-
- return array_combine($all_files_to_check, $all_files_to_check);
- }
-
- public function fileExists(string $file_path): bool
- {
- return $this->file_provider->fileExists($file_path);
- }
-
- public function alterCodeAfterCompletion(
- bool $dry_run = false,
- bool $safe_types = false
- ): void {
- $this->codebase->alter_code = true;
- $this->codebase->infer_types_from_usage = true;
- $this->show_issues = false;
- $this->dry_run = $dry_run;
- $this->only_replace_php_types_with_non_docblock_types = $safe_types;
- }
-
- /**
- * @param array<string, string> $to_refactor
- *
- */
- public function refactorCodeAfterCompletion(array $to_refactor): void
- {
- $this->to_refactor = $to_refactor;
- $this->codebase->alter_code = true;
- $this->show_issues = false;
- }
-
- /**
- * @param 'cli'|'config'|'composer'|'tests' $source
- */
- public function setPhpVersion(string $version, string $source): void
- {
- if (!preg_match('/^(5\.[456]|7\.[01234]|8\.[01])(\..*)?$/', $version)) {
- throw new UnexpectedValueException('Expecting a version number in the format x.y');
- }
-
- [$php_major_version, $php_minor_version] = explode('.', $version);
-
- $php_major_version = (int) $php_major_version;
- $php_minor_version = (int) $php_minor_version;
-
- if ($this->codebase->php_major_version !== $php_major_version
- || $this->codebase->php_minor_version !== $php_minor_version
- ) {
- // reset lexer and parser when php version changes
- StatementsProvider::clearLexer();
- StatementsProvider::clearParser();
- }
-
- $this->codebase->php_major_version = $php_major_version;
- $this->codebase->php_minor_version = $php_minor_version;
- $this->codebase->analysis_php_version_id = $php_major_version * 10000 + $php_minor_version * 100;
- $this->codebase->php_version_source = $source;
- }
-
- /**
- * @param array<string, bool> $issues
- * @throws UnsupportedIssueToFixException
- *
- */
- public function setIssuesToFix(array $issues): void
- {
- $supported_issues_to_fix = static::getSupportedIssuesToFix();
-
- $supported_issues_to_fix[] = 'MissingImmutableAnnotation';
- $supported_issues_to_fix[] = 'MissingPureAnnotation';
- $supported_issues_to_fix[] = 'MissingThrowsDocblock';
-
- $unsupportedIssues = array_diff(array_keys($issues), $supported_issues_to_fix);
-
- if (! empty($unsupportedIssues)) {
- throw new UnsupportedIssueToFixException(
- 'Psalm doesn\'t know how to fix issue(s): ' . implode(', ', $unsupportedIssues) . PHP_EOL
- . 'Supported issues to fix are: ' . implode(',', $supported_issues_to_fix)
- );
- }
-
- $this->issues_to_fix = $issues;
- }
-
- public function setAllIssuesToFix(): void
- {
- $keyed_issues = array_fill_keys(static::getSupportedIssuesToFix(), true);
-
- $this->setIssuesToFix($keyed_issues);
- }
-
- /**
- * @return array<string, bool>
- */
- public function getIssuesToFix(): array
- {
- return $this->issues_to_fix;
- }
-
- public function getCodebase(): Codebase
- {
- return $this->codebase;
- }
-
- public function getFileAnalyzerForClassLike(string $fq_class_name): FileAnalyzer
- {
- $fq_class_name_lc = strtolower($fq_class_name);
-
- $file_path = $this->codebase->scanner->getClassLikeFilePath($fq_class_name_lc);
-
- return new FileAnalyzer(
- $this,
- $file_path,
- $this->config->shortenFileName($file_path)
- );
- }
-
- public function getMethodMutations(
- MethodIdentifier $original_method_id,
- Context $this_context,
- string $root_file_path,
- string $root_file_name
- ): void {
- $fq_class_name = $original_method_id->fq_class_name;
-
- $appearing_method_id = $this->codebase->methods->getAppearingMethodId($original_method_id);
-
- if (!$appearing_method_id) {
- // this can happen for some abstract classes implementing (but not fully) interfaces
- return;
- }
-
- $appearing_fq_class_name = $appearing_method_id->fq_class_name;
-
- $appearing_class_storage = $this->classlike_storage_provider->get($appearing_fq_class_name);
-
- if (!$appearing_class_storage->user_defined) {
- return;
- }
-
- $file_analyzer = $this->getFileAnalyzerForClassLike($fq_class_name);
-
- $file_analyzer->setRootFilePath($root_file_path, $root_file_name);
-
- if ($appearing_fq_class_name !== $fq_class_name) {
- $file_analyzer = $this->getFileAnalyzerForClassLike($appearing_fq_class_name);
- }
-
- $stmts = $this->codebase->getStatementsForFile(
- $file_analyzer->getFilePath()
- );
-
- $file_analyzer->populateCheckers($stmts);
-
- if (!$this_context->self) {
- $this_context->self = $fq_class_name;
- $this_context->vars_in_scope['$this'] = Type::parseString($fq_class_name);
- }
-
- $file_analyzer->getMethodMutations($appearing_method_id, $this_context, true);
-
- $file_analyzer->class_analyzers_to_analyze = [];
- $file_analyzer->interface_analyzers_to_analyze = [];
- $file_analyzer->clearSourceBeforeDestruction();
- }
-
- public function getFunctionLikeAnalyzer(
- MethodIdentifier $method_id,
- string $file_path
- ): ?FunctionLikeAnalyzer {
- $file_analyzer = new FileAnalyzer(
- $this,
- $file_path,
- $this->config->shortenFileName($file_path)
- );
-
- $stmts = $this->codebase->getStatementsForFile(
- $file_analyzer->getFilePath()
- );
-
- $file_analyzer->populateCheckers($stmts);
-
- $function_analyzer = $file_analyzer->getFunctionLikeAnalyzer($method_id);
-
- $file_analyzer->class_analyzers_to_analyze = [];
- $file_analyzer->interface_analyzers_to_analyze = [];
-
- return $function_analyzer;
- }
-
- /**
- * Adapted from https://gist.github.com/divinity76/01ef9ca99c111565a72d3a8a6e42f7fb
- * returns number of cpu cores
- * Copyleft 2018, license: WTFPL
- * @throws RuntimeException
- * @throws LogicException
- * @psalm-suppress ForbiddenCode
- */
- public static function getCpuCount(): int
- {
- if (defined('PHP_WINDOWS_VERSION_MAJOR')) {
- /*
- $str = trim((string) shell_exec('wmic cpu get NumberOfCores 2>&1'));
- if (!preg_match('/(\d+)/', $str, $matches)) {
- throw new RuntimeException('wmic failed to get number of cpu cores on windows!');
- }
- return ((int) $matches [1]);
- */
- return 1;
- }
-
- if (ini_get('pcre.jit') === '1'
- && PHP_OS === 'Darwin'
- && version_compare(PHP_VERSION, '7.3.0') >= 0
- && version_compare(PHP_VERSION, '7.4.0') < 0
- ) {
- return 1;
- }
-
- if (!extension_loaded('pcntl') || !function_exists('shell_exec')) {
- return 1;
- }
-
- $has_nproc = trim((string) @shell_exec('command -v nproc'));
- if ($has_nproc) {
- $ret = @shell_exec('nproc');
- if (is_string($ret)) {
- $ret = trim($ret);
- $tmp = filter_var($ret, FILTER_VALIDATE_INT);
- if (is_int($tmp)) {
- return $tmp;
- }
- }
- }
-
- $ret = @shell_exec('sysctl -n hw.ncpu');
- if (is_string($ret)) {
- $ret = trim($ret);
- $tmp = filter_var($ret, FILTER_VALIDATE_INT);
- if (is_int($tmp)) {
- return $tmp;
- }
- }
-
- if (is_readable('/proc/cpuinfo')) {
- $cpuinfo = file_get_contents('/proc/cpuinfo');
- $count = substr_count($cpuinfo, 'processor');
- if ($count > 0) {
- return $count;
- }
- }
-
- throw new LogicException('failed to detect number of CPUs!');
- }
-
- /**
- * @return array<int, string>
- *
- * @psalm-pure
- */
- public static function getSupportedIssuesToFix(): array
- {
- return array_map(
- /** @param class-string $issue_class */
- function (string $issue_class): string {
- $parts = explode('\\', $issue_class);
- return end($parts);
- },
- self::SUPPORTED_ISSUES_TO_FIX
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php
deleted file mode 100644
index 4bbbd9b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ScopeAnalyzer.php
+++ /dev/null
@@ -1,529 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\NodeTypeProvider;
-
-use function array_diff;
-use function array_filter;
-use function array_intersect;
-use function array_merge;
-use function array_unique;
-use function array_values;
-use function count;
-use function in_array;
-use function strtolower;
-
-/**
- * @internal
- */
-class ScopeAnalyzer
-{
- public const ACTION_END = 'END';
- public const ACTION_BREAK = 'BREAK';
- public const ACTION_CONTINUE = 'CONTINUE';
- public const ACTION_LEAVE_SWITCH = 'LEAVE_SWITCH';
- public const ACTION_NONE = 'NONE';
- public const ACTION_RETURN = 'RETURN';
-
- /**
- * @param array<PhpParser\Node\Stmt> $stmts
- *
- */
- public static function doesEverBreak(array $stmts): bool
- {
- if (empty($stmts)) {
- return false;
- }
-
- for ($i = count($stmts) - 1; $i >= 0; --$i) {
- $stmt = $stmts[$i];
-
- if ($stmt instanceof PhpParser\Node\Stmt\Break_) {
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\If_) {
- if (self::doesEverBreak($stmt->stmts)) {
- return true;
- }
-
- if ($stmt->else && self::doesEverBreak($stmt->else->stmts)) {
- return true;
- }
-
- foreach ($stmt->elseifs as $elseif) {
- if (self::doesEverBreak($elseif->stmts)) {
- return true;
- }
- }
- }
- }
-
- return false;
- }
-
- /**
- * @param array<PhpParser\Node> $stmts
- * @param array<lowercase-string, bool> $exit_functions
- * @param list<'loop'|'switch'> $break_types
- * @param bool $return_is_exit Exit and Throw statements are treated differently from return if this is false
- *
- * @return list<self::ACTION_*>
- *
- * @psalm-suppress ComplexMethod nothing much we can do
- */
- public static function getControlActions(
- array $stmts,
- ?NodeDataProvider $nodes,
- array $exit_functions,
- array $break_types,
- bool $return_is_exit = true
- ): array {
- if (empty($stmts)) {
- return [self::ACTION_NONE];
- }
-
- $control_actions = [];
-
- foreach ($stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Return_ ||
- $stmt instanceof PhpParser\Node\Stmt\Throw_ ||
- ($stmt instanceof PhpParser\Node\Stmt\Expression && $stmt->expr instanceof PhpParser\Node\Expr\Exit_)
- ) {
- if (!$return_is_exit && $stmt instanceof PhpParser\Node\Stmt\Return_) {
- $stmt_return_type = null;
- if ($nodes && $stmt->expr) {
- $stmt_return_type = $nodes->getType($stmt->expr);
- }
-
- // don't consider a return if the expression never returns (e.g. a throw inside a short closure)
- if ($stmt_return_type && ($stmt_return_type->isNever() || $stmt_return_type->isEmpty())) {
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_END])));
- }
-
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_RETURN])));
- }
-
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_END])));
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Expression) {
- // This allows calls to functions that always exit to act as exit statements themselves
- if ($nodes
- && ($stmt_expr_type = $nodes->getType($stmt->expr))
- && $stmt_expr_type->isNever()
- ) {
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_END])));
- }
-
- if ($exit_functions) {
- if ($stmt->expr instanceof PhpParser\Node\Expr\FuncCall
- || $stmt->expr instanceof PhpParser\Node\Expr\StaticCall
- ) {
- if ($stmt->expr instanceof PhpParser\Node\Expr\FuncCall) {
- /** @var string|null */
- $resolved_name = $stmt->expr->name->getAttribute('resolvedName');
-
- if ($resolved_name && isset($exit_functions[strtolower($resolved_name)])) {
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_END])));
- }
- } elseif ($stmt->expr->class instanceof PhpParser\Node\Name
- && $stmt->expr->name instanceof PhpParser\Node\Identifier
- ) {
- /** @var string|null */
- $resolved_class_name = $stmt->expr->class->getAttribute('resolvedName');
-
- if ($resolved_class_name
- && isset($exit_functions[strtolower($resolved_class_name . '::' . $stmt->expr->name)])
- ) {
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_END])));
- }
- }
- }
- }
-
- continue;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Continue_) {
- $count = !$stmt->num
- ? 1
- : ($stmt->num instanceof PhpParser\Node\Scalar\LNumber ? $stmt->num->value : null);
-
- if ($break_types && $count !== null && count($break_types) >= $count) {
- if ($break_types[count($break_types) - $count] === 'switch') {
- return array_merge($control_actions, [self::ACTION_LEAVE_SWITCH]);
- }
-
- return array_values($control_actions);
- }
-
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_CONTINUE])));
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Break_) {
- $count = !$stmt->num
- ? 1
- : ($stmt->num instanceof PhpParser\Node\Scalar\LNumber ? $stmt->num->value : null);
-
- if ($break_types && $count !== null && count($break_types) >= $count) {
- if ($break_types[count($break_types) - $count] === 'switch') {
- return array_merge($control_actions, [self::ACTION_LEAVE_SWITCH]);
- }
-
- return array_values($control_actions);
- }
-
- return array_values(array_unique(array_merge($control_actions, [self::ACTION_BREAK])));
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\If_) {
- $if_statement_actions = self::getControlActions(
- $stmt->stmts,
- $nodes,
- $exit_functions,
- $break_types,
- $return_is_exit
- );
-
- $all_leave = !array_filter(
- $if_statement_actions,
- function ($action) {
- return $action === self::ACTION_NONE;
- }
- );
-
- $else_statement_actions = $stmt->else
- ? self::getControlActions(
- $stmt->else->stmts,
- $nodes,
- $exit_functions,
- $break_types,
- $return_is_exit
- ) : [];
-
- $all_leave = $all_leave
- && $else_statement_actions
- && !array_filter(
- $else_statement_actions,
- function ($action) {
- return $action === self::ACTION_NONE;
- }
- );
-
- $all_elseif_actions = [];
-
- if ($stmt->elseifs) {
- foreach ($stmt->elseifs as $elseif) {
- $elseif_control_actions = self::getControlActions(
- $elseif->stmts,
- $nodes,
- $exit_functions,
- $break_types,
- $return_is_exit
- );
-
- $all_leave = $all_leave
- && !array_filter(
- $elseif_control_actions,
- function ($action) {
- return $action === self::ACTION_NONE;
- }
- );
-
- $all_elseif_actions = array_merge($elseif_control_actions, $all_elseif_actions);
- }
- }
-
- if ($all_leave) {
- return array_values(
- array_unique(
- array_merge(
- $control_actions,
- $if_statement_actions,
- $else_statement_actions,
- $all_elseif_actions
- )
- )
- );
- }
-
- $control_actions = array_filter(
- array_merge(
- $control_actions,
- $if_statement_actions,
- $else_statement_actions,
- $all_elseif_actions
- ),
- function ($action) {
- return $action !== self::ACTION_NONE;
- }
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
- $has_ended = false;
- $has_non_breaking_default = false;
- $has_default_terminator = false;
-
- $all_case_actions = [];
-
- // iterate backwards in a case statement
- for ($d = count($stmt->cases) - 1; $d >= 0; --$d) {
- $case = $stmt->cases[$d];
-
- $case_actions = self::getControlActions(
- $case->stmts,
- $nodes,
- $exit_functions,
- array_merge($break_types, ['switch']),
- $return_is_exit
- );
-
- if (array_intersect([
- self::ACTION_LEAVE_SWITCH,
- self::ACTION_BREAK,
- self::ACTION_CONTINUE
- ], $case_actions)
- ) {
- continue 2;
- }
-
- if (!$case->cond) {
- $has_non_breaking_default = true;
- }
-
- $case_does_end = !array_diff(
- $control_actions,
- [self::ACTION_END, self::ACTION_RETURN]
- );
-
- if ($case_does_end) {
- $has_ended = true;
- }
-
- $all_case_actions = array_merge(
- $all_case_actions,
- $case_actions
- );
-
- if (!$case_does_end && !$has_ended) {
- continue 2;
- }
-
- if ($has_non_breaking_default && $case_does_end) {
- $has_default_terminator = true;
- }
- }
-
- $all_case_actions = array_filter(
- $all_case_actions,
- function ($action) {
- return $action !== self::ACTION_NONE;
- }
- );
-
- if ($has_default_terminator || $stmt->getAttribute('allMatched', false)) {
- return array_values(array_unique(array_merge($control_actions, $all_case_actions)));
- }
-
- $control_actions = array_merge(
- $control_actions,
- $all_case_actions
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Do_
- || $stmt instanceof PhpParser\Node\Stmt\While_
- || $stmt instanceof PhpParser\Node\Stmt\Foreach_
- || $stmt instanceof PhpParser\Node\Stmt\For_
- ) {
- $loop_actions = self::getControlActions(
- $stmt->stmts,
- $nodes,
- $exit_functions,
- array_merge($break_types, ['loop']),
- $return_is_exit
- );
-
- $control_actions = array_filter(
- array_merge($control_actions, $loop_actions),
- function ($action) {
- return $action !== self::ACTION_NONE;
- }
- );
-
- if ($stmt instanceof PhpParser\Node\Stmt\While_
- && $nodes
- && ($stmt_expr_type = $nodes->getType($stmt->cond))
- && $stmt_expr_type->isAlwaysTruthy()
- ) {
- //infinite while loop that only return don't have an exit path
- $have_exit_path = (bool)array_diff(
- $control_actions,
- [self::ACTION_END, self::ACTION_RETURN]
- );
-
- if (!$have_exit_path) {
- return array_values(array_unique($control_actions));
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\For_
- && $nodes
- ) {
- $is_infinite_loop = true;
- if ($stmt->cond) {
- foreach ($stmt->cond as $cond) {
- $stmt_expr_type = $nodes->getType($cond);
- if (!$stmt_expr_type || !$stmt_expr_type->isAlwaysTruthy()) {
- $is_infinite_loop = false;
- }
- }
- }
-
- if ($is_infinite_loop) {
- //infinite while loop that only return don't have an exit path
- $have_exit_path = (bool)array_diff(
- $control_actions,
- [self::ACTION_END, self::ACTION_RETURN]
- );
-
- if (!$have_exit_path) {
- return array_values(array_unique($control_actions));
- }
- }
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
- $try_statement_actions = self::getControlActions(
- $stmt->stmts,
- $nodes,
- $exit_functions,
- $break_types,
- $return_is_exit
- );
-
- $try_leaves = !array_filter(
- $try_statement_actions,
- function ($action) {
- return $action === self::ACTION_NONE;
- }
- );
-
- $all_catch_actions = [];
-
- if ($stmt->catches) {
- $all_leave = $try_leaves;
-
- foreach ($stmt->catches as $catch) {
- $catch_actions = self::getControlActions(
- $catch->stmts,
- $nodes,
- $exit_functions,
- $break_types,
- $return_is_exit
- );
-
- $all_leave = $all_leave
- && !array_filter(
- $catch_actions,
- function ($action) {
- return $action === self::ACTION_NONE;
- }
- );
-
- if (!$all_leave) {
- $control_actions = array_merge($control_actions, $catch_actions);
- } else {
- $all_catch_actions = array_merge($all_catch_actions, $catch_actions);
- }
- }
-
- if ($all_leave && $try_statement_actions !== [self::ACTION_NONE]) {
- return array_values(
- array_unique(
- array_merge(
- $control_actions,
- $try_statement_actions,
- $all_catch_actions
- )
- )
- );
- }
- } elseif ($try_leaves) {
- return array_values(array_unique(array_merge($control_actions, $try_statement_actions)));
- }
-
- if ($stmt->finally && $stmt->finally->stmts) {
- $finally_statement_actions = self::getControlActions(
- $stmt->finally->stmts,
- $nodes,
- $exit_functions,
- $break_types,
- $return_is_exit
- );
-
- if (!in_array(self::ACTION_NONE, $finally_statement_actions, true)) {
- return array_merge(
- array_filter(
- $control_actions,
- function ($action) {
- return $action !== self::ACTION_NONE;
- }
- ),
- $finally_statement_actions
- );
- }
- }
-
- $control_actions = array_filter(
- array_merge($control_actions, $try_statement_actions),
- function ($action) {
- return $action !== self::ACTION_NONE;
- }
- );
- }
- }
-
- $control_actions[] = self::ACTION_NONE;
-
- return array_values(array_unique($control_actions));
- }
-
- /**
- * @param array<PhpParser\Node> $stmts
- *
- */
- public static function onlyThrowsOrExits(NodeTypeProvider $type_provider, array $stmts): bool
- {
- if (empty($stmts)) {
- return false;
- }
-
- for ($i = count($stmts) - 1; $i >= 0; --$i) {
- $stmt = $stmts[$i];
-
- if ($stmt instanceof PhpParser\Node\Stmt\Throw_
- || ($stmt instanceof PhpParser\Node\Stmt\Expression
- && $stmt->expr instanceof PhpParser\Node\Expr\Exit_)
- ) {
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\Expression) {
- $stmt_type = $type_provider->getType($stmt->expr);
-
- if ($stmt_type && $stmt_type->isNever()) {
- return true;
- }
- }
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/SourceAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/SourceAnalyzer.php
deleted file mode 100644
index 8be2d28..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/SourceAnalyzer.php
+++ /dev/null
@@ -1,182 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use Psalm\Aliases;
-use Psalm\Codebase;
-use Psalm\NodeTypeProvider;
-use Psalm\StatementsSource;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-abstract class SourceAnalyzer implements StatementsSource
-{
- /**
- * @var SourceAnalyzer
- */
- protected $source;
-
- public function __destruct()
- {
- unset($this->source);
- }
-
- public function getAliases(): Aliases
- {
- return $this->source->getAliases();
- }
-
- /**
- * @return array<lowercase-string, string>
- */
- public function getAliasedClassesFlipped(): array
- {
- return $this->source->getAliasedClassesFlipped();
- }
-
- /**
- * @return array<string, string>
- */
- public function getAliasedClassesFlippedReplaceable(): array
- {
- return $this->source->getAliasedClassesFlippedReplaceable();
- }
-
- public function getFQCLN(): ?string
- {
- return $this->source->getFQCLN();
- }
-
- public function getClassName(): ?string
- {
- return $this->source->getClassName();
- }
-
- public function getParentFQCLN(): ?string
- {
- return $this->source->getParentFQCLN();
- }
-
- public function getFileName(): string
- {
- return $this->source->getFileName();
- }
-
- public function getFilePath(): string
- {
- return $this->source->getFilePath();
- }
-
- public function getRootFileName(): string
- {
- return $this->source->getRootFileName();
- }
-
- public function getRootFilePath(): string
- {
- return $this->source->getRootFilePath();
- }
-
- public function setRootFilePath(string $file_path, string $file_name): void
- {
- $this->source->setRootFilePath($file_path, $file_name);
- }
-
- public function hasParentFilePath(string $file_path): bool
- {
- return $this->source->hasParentFilePath($file_path);
- }
-
- public function hasAlreadyRequiredFilePath(string $file_path): bool
- {
- return $this->source->hasAlreadyRequiredFilePath($file_path);
- }
-
- public function getRequireNesting(): int
- {
- return $this->source->getRequireNesting();
- }
-
- /**
- * @psalm-mutation-free
- */
- public function getSource(): StatementsSource
- {
- return $this->source;
- }
-
- /**
- * Get a list of suppressed issues
- *
- * @return array<string>
- */
- public function getSuppressedIssues(): array
- {
- return $this->source->getSuppressedIssues();
- }
-
- /**
- * @param array<int, string> $new_issues
- */
- public function addSuppressedIssues(array $new_issues): void
- {
- $this->source->addSuppressedIssues($new_issues);
- }
-
- /**
- * @param array<int, string> $new_issues
- */
- public function removeSuppressedIssues(array $new_issues): void
- {
- $this->source->removeSuppressedIssues($new_issues);
- }
-
- public function getNamespace(): ?string
- {
- return $this->source->getNamespace();
- }
-
- public function isStatic(): bool
- {
- return $this->source->isStatic();
- }
-
- /**
- * @psalm-mutation-free
- */
- public function getCodebase(): Codebase
- {
- return $this->source->getCodebase();
- }
-
- /**
- * @psalm-mutation-free
- */
- public function getProjectAnalyzer(): ProjectAnalyzer
- {
- return $this->source->getProjectAnalyzer();
- }
-
- /**
- * @psalm-mutation-free
- */
- public function getFileAnalyzer(): FileAnalyzer
- {
- return $this->source->getFileAnalyzer();
- }
-
- /**
- * @return array<string, array<string, Union>>|null
- */
- public function getTemplateTypeMap(): ?array
- {
- return $this->source->getTemplateTypeMap();
- }
-
- public function getNodeTypeProvider(): NodeTypeProvider
- {
- return $this->source->getNodeTypeProvider();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php
deleted file mode 100644
index aeeb964..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/DoAnalyzer.php
+++ /dev/null
@@ -1,210 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\Scope\LoopScope;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-use UnexpectedValueException;
-
-use function array_diff;
-use function array_filter;
-use function array_intersect_key;
-use function array_keys;
-use function array_merge;
-use function array_values;
-use function in_array;
-use function preg_match;
-use function preg_quote;
-use function spl_object_id;
-
-/**
- * @internal
- */
-class DoAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Do_ $stmt,
- Context $context
- ): ?bool {
- $do_context = clone $context;
- $do_context->break_types[] = 'loop';
- $do_context->inside_loop = true;
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($codebase->alter_code) {
- $do_context->branch_point = $do_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
-
- $loop_scope = new LoopScope($do_context, $context);
- $loop_scope->protected_var_ids = $context->protected_var_ids;
-
- self::analyzeDoNaively($statements_analyzer, $stmt, $do_context, $loop_scope);
-
- $mixed_var_ids = [];
-
- foreach ($do_context->vars_in_scope as $var_id => $type) {
- if ($type->hasMixed()) {
- $mixed_var_ids[] = $var_id;
- }
- }
-
- $cond_id = spl_object_id($stmt->cond);
-
- $while_clauses = FormulaGenerator::getFormula(
- $cond_id,
- $cond_id,
- $stmt->cond,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- $while_clauses = array_values(
- array_filter(
- $while_clauses,
- function (Clause $c) use ($mixed_var_ids): bool {
- $keys = array_keys($c->possibilities);
-
- $mixed_var_ids = array_diff($mixed_var_ids, $keys);
-
- foreach ($keys as $key) {
- foreach ($mixed_var_ids as $mixed_var_id) {
- if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
- return false;
- }
- }
- }
-
- return true;
- }
- )
- );
-
- if (!$while_clauses) {
- $while_clauses = [new Clause([], $cond_id, $cond_id, true)];
- }
-
- if (LoopAnalyzer::analyze(
- $statements_analyzer,
- $stmt->stmts,
- WhileAnalyzer::getAndExpressions($stmt->cond),
- [],
- $loop_scope,
- $inner_loop_context,
- true,
- true
- ) === false) {
- return false;
- }
-
- // because it's a do {} while, inner loop vars belong to the main context
- if (!$inner_loop_context) {
- throw new UnexpectedValueException('There should be an inner loop context');
- }
-
- $negated_while_clauses = Algebra::negateFormula($while_clauses);
-
- $negated_while_types = Algebra::getTruthsFromFormula(
- Algebra::simplifyCNF(
- array_merge($context->clauses, $negated_while_clauses)
- )
- );
-
- if ($negated_while_types) {
- $changed_var_ids = [];
-
- $inner_loop_context->vars_in_scope =
- Reconciler::reconcileKeyedTypes(
- $negated_while_types,
- [],
- $inner_loop_context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- [],
- true,
- new CodeLocation($statements_analyzer->getSource(), $stmt->cond)
- );
- }
-
- foreach ($inner_loop_context->vars_in_scope as $var_id => $type) {
- // if there are break statements in the loop it's not certain
- // that the loop has finished executing, so the assertions at the end
- // the loop in the while conditional may not hold
- if (in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true)) {
- if (isset($loop_scope->possibly_defined_loop_parent_vars[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_defined_loop_parent_vars[$var_id]
- );
- }
- } else {
- $context->vars_in_scope[$var_id] = $type;
- }
- }
-
- $do_context->loop_scope = null;
-
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $do_context->vars_possibly_in_scope
- );
-
- $context->referenced_var_ids = array_intersect_key(
- $do_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- if ($context->collect_exceptions) {
- $context->mergeExceptions($inner_loop_context);
- }
-
- return null;
- }
-
- private static function analyzeDoNaively(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Do_ $stmt,
- Context $context,
- LoopScope $loop_scope
- ): void {
- $do_context = clone $context;
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantCondition']);
- }
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
- if (!in_array('TypeDoesNotContainType', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['TypeDoesNotContainType']);
- }
-
- $do_context->loop_scope = $loop_scope;
-
- $statements_analyzer->analyze($stmt->stmts, $do_context);
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantCondition']);
- }
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
- if (!in_array('TypeDoesNotContainType', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['TypeDoesNotContainType']);
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php
deleted file mode 100644
index c45ca9b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForAnalyzer.php
+++ /dev/null
@@ -1,190 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Scope\LoopScope;
-use Psalm\Type;
-use UnexpectedValueException;
-
-use function array_intersect_key;
-use function array_merge;
-use function count;
-use function in_array;
-use function is_string;
-
-/**
- * @internal
- */
-class ForAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\For_ $stmt,
- Context $context
- ): ?bool {
- $pre_assigned_var_ids = $context->assigned_var_ids;
- $context->assigned_var_ids = [];
-
- $init_var_types = [];
-
- foreach ($stmt->init as $init) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $init, $context) === false) {
- return false;
- }
-
- if ($init instanceof PhpParser\Node\Expr\Assign
- && $init->var instanceof PhpParser\Node\Expr\Variable
- && is_string($init->var->name)
- && ($init_var_type = $statements_analyzer->node_data->getType($init->expr))
- ) {
- $init_var_types[$init->var->name] = $init_var_type;
- }
- }
-
- $assigned_var_ids = $context->assigned_var_ids;
-
- $context->assigned_var_ids = array_merge(
- $pre_assigned_var_ids,
- $assigned_var_ids
- );
-
- $while_true = !$stmt->cond && !$stmt->init && !$stmt->loop;
-
- $pre_context = null;
-
- if ($while_true) {
- $pre_context = clone $context;
- }
-
- $for_context = clone $context;
-
- $for_context->inside_loop = true;
- $for_context->break_types[] = 'loop';
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($codebase->alter_code) {
- $for_context->branch_point = $for_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
-
- $loop_scope = new LoopScope($for_context, $context);
-
- $loop_scope->protected_var_ids = array_merge(
- $assigned_var_ids,
- $context->protected_var_ids
- );
-
- if (LoopAnalyzer::analyze(
- $statements_analyzer,
- $stmt->stmts,
- $stmt->cond,
- $stmt->loop,
- $loop_scope,
- $inner_loop_context
- ) === false) {
- return false;
- }
-
- if (!$inner_loop_context) {
- throw new UnexpectedValueException('There should be an inner loop context');
- }
-
- $always_enters_loop = false;
-
- foreach ($stmt->cond as $cond) {
- if ($cond_type = $statements_analyzer->node_data->getType($cond)) {
- $always_enters_loop = $cond_type->isAlwaysTruthy();
- }
-
- if (count($stmt->init) === 1
- && count($stmt->cond) === 1
- && $cond instanceof PhpParser\Node\Expr\BinaryOp
- && $cond->right instanceof PhpParser\Node\Scalar\LNumber
- && $cond->left instanceof PhpParser\Node\Expr\Variable
- && is_string($cond->left->name)
- && isset($init_var_types[$cond->left->name])
- && $init_var_types[$cond->left->name]->isSingleIntLiteral()
- ) {
- $init_value = $init_var_types[$cond->left->name]->getSingleIntLiteral()->value;
- $cond_value = $cond->right->value;
-
- if ($cond instanceof PhpParser\Node\Expr\BinaryOp\Smaller && $init_value < $cond_value) {
- $always_enters_loop = true;
- break;
- }
-
- if ($cond instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual && $init_value <= $cond_value) {
- $always_enters_loop = true;
- break;
- }
-
- if ($cond instanceof PhpParser\Node\Expr\BinaryOp\Greater && $init_value > $cond_value) {
- $always_enters_loop = true;
- break;
- }
-
- if ($cond instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual && $init_value >= $cond_value) {
- $always_enters_loop = true;
- break;
- }
- }
- }
-
- if ($while_true) {
- $always_enters_loop = true;
- }
-
- $can_leave_loop = !$while_true
- || in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true);
-
- if ($always_enters_loop && $can_leave_loop) {
- foreach ($inner_loop_context->vars_in_scope as $var_id => $type) {
- // if there are break statements in the loop it's not certain
- // that the loop has finished executing, so the assertions at the end
- // the loop in the while conditional may not hold
- if (in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true)
- || in_array(ScopeAnalyzer::ACTION_CONTINUE, $loop_scope->final_actions, true)
- ) {
- if (isset($loop_scope->possibly_defined_loop_parent_vars[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_defined_loop_parent_vars[$var_id]
- );
- }
- } else {
- $context->vars_in_scope[$var_id] = $type;
- }
- }
- }
-
- $for_context->loop_scope = null;
-
- if ($can_leave_loop) {
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $for_context->vars_possibly_in_scope
- );
- } elseif ($pre_context) {
- $context->vars_possibly_in_scope = $pre_context->vars_possibly_in_scope;
- }
-
- $context->referenced_var_ids = array_intersect_key(
- $for_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- if ($context->collect_exceptions) {
- $context->mergeExceptions($for_context);
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php
deleted file mode 100644
index bdcf834..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php
+++ /dev/null
@@ -1,1149 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Scope\LoopScope;
-use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\InvalidIterator;
-use Psalm\Issue\NullIterator;
-use Psalm\Issue\PossibleRawObjectIteration;
-use Psalm\Issue\PossiblyFalseIterator;
-use Psalm\Issue\PossiblyInvalidIterator;
-use Psalm\Issue\PossiblyNullIterator;
-use Psalm\Issue\RawObjectIteration;
-use Psalm\Issue\UnnecessaryVarAnnotation;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TDependentListKey;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TVoid;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_intersect_key;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_search;
-use function array_values;
-use function in_array;
-use function is_string;
-use function reset;
-use function strtolower;
-
-/**
- * @internal
- */
-class ForeachAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Foreach_ $stmt,
- Context $context
- ): ?bool {
- $var_comments = [];
-
- $doc_comment = $stmt->getDocComment();
-
- $codebase = $statements_analyzer->getCodebase();
- $file_path = $statements_analyzer->getRootFilePath();
- $type_aliases = $codebase->file_storage_provider->get($file_path)->type_aliases;
-
- if ($doc_comment) {
- try {
- $var_comments = CommentAnalyzer::getTypeFromComment(
- $doc_comment,
- $statements_analyzer->getSource(),
- $statements_analyzer->getSource()->getAliases(),
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $type_aliases
- );
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer, $stmt)
- )
- );
- }
- }
-
- $safe_var_ids = [];
-
- if ($stmt->keyVar instanceof PhpParser\Node\Expr\Variable && is_string($stmt->keyVar->name)) {
- $safe_var_ids['$' . $stmt->keyVar->name] = true;
- }
-
- if ($stmt->valueVar instanceof PhpParser\Node\Expr\Variable && is_string($stmt->valueVar->name)) {
- $safe_var_ids['$' . $stmt->valueVar->name] = true;
- $statements_analyzer->foreach_var_locations['$' . $stmt->valueVar->name][] = new CodeLocation(
- $statements_analyzer,
- $stmt->valueVar
- );
- } elseif ($stmt->valueVar instanceof PhpParser\Node\Expr\List_) {
- foreach ($stmt->valueVar->items as $list_item) {
- if (!$list_item) {
- continue;
- }
-
- $list_item_key = $list_item->key;
- $list_item_value = $list_item->value;
-
- if ($list_item_value instanceof PhpParser\Node\Expr\Variable && is_string($list_item_value->name)) {
- $safe_var_ids['$' . $list_item_value->name] = true;
- }
-
- if ($list_item_key instanceof PhpParser\Node\Expr\Variable && is_string($list_item_key->name)) {
- $safe_var_ids['$' . $list_item_key->name] = true;
- }
- }
- }
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->var_id || !$var_comment->type) {
- continue;
- }
-
- if (isset($safe_var_ids[$var_comment->var_id])) {
- continue;
- }
-
- $comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
-
- $type_location = null;
-
- if ($var_comment->type_start
- && $var_comment->type_end
- && $var_comment->line_number
- ) {
- $type_location = new DocblockTypeLocation(
- $statements_analyzer,
- $var_comment->type_start,
- $var_comment->type_end,
- $var_comment->line_number
- );
-
- if ($codebase->alter_code) {
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $statements_analyzer,
- $comment_type,
- $type_location,
- $context->calling_method_id
- );
- }
- }
-
- if (isset($context->vars_in_scope[$var_comment->var_id])
- || VariableFetchAnalyzer::isSuperGlobal($var_comment->var_id)
- ) {
- if ($codebase->find_unused_variables
- && $doc_comment
- && $type_location
- && isset($context->vars_in_scope[$var_comment->var_id])
- && $context->vars_in_scope[$var_comment->var_id]->getId() === $comment_type->getId()
- && !$comment_type->isMixed()
- ) {
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['UnnecessaryVarAnnotation'])
- ) {
- FileManipulationBuffer::addVarAnnotationToRemove($type_location);
- } elseif (IssueBuffer::accepts(
- new UnnecessaryVarAnnotation(
- 'The @var ' . $comment_type . ' annotation for '
- . $var_comment->var_id . ' is unnecessary',
- $type_location
- ),
- $statements_analyzer->getSuppressedIssues(),
- true
- )) {
- // fall through
- }
- }
-
- if (isset($context->vars_in_scope[$var_comment->var_id])) {
- $comment_type->parent_nodes = $context->vars_in_scope[$var_comment->var_id]->parent_nodes;
- }
-
- $context->vars_in_scope[$var_comment->var_id] = $comment_type;
- }
- }
-
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return false;
- }
- $context->inside_general_use = $was_inside_general_use;
-
- $key_type = null;
- $value_type = null;
- $always_non_empty_array = true;
-
- $var_id = ExpressionIdentifier::getVarId(
- $stmt->expr,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
- $iterator_type = $stmt_expr_type;
- } elseif ($var_id && $context->hasVariable($var_id)) {
- $iterator_type = $context->vars_in_scope[$var_id];
- } else {
- $iterator_type = null;
- }
-
- if ($iterator_type) {
- if (self::checkIteratorType(
- $statements_analyzer,
- $stmt,
- $stmt->expr,
- $iterator_type,
- $codebase,
- $context,
- $key_type,
- $value_type,
- $always_non_empty_array
- ) === false
- ) {
- return false;
- }
- }
-
- $foreach_context = clone $context;
-
- foreach ($foreach_context->vars_in_scope as $context_var_id => $context_type) {
- $foreach_context->vars_in_scope[$context_var_id] = clone $context_type;
- }
-
- $foreach_context->inside_loop = true;
- $foreach_context->break_types[] = 'loop';
-
- if ($codebase->alter_code) {
- $foreach_context->branch_point =
- $foreach_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
-
- if ($stmt->keyVar instanceof PhpParser\Node\Expr\Variable && is_string($stmt->keyVar->name)) {
- $key_type = $key_type ?? Type::getMixed();
-
- AssignmentAnalyzer::analyze(
- $statements_analyzer,
- $stmt->keyVar,
- $stmt->expr,
- $key_type,
- $foreach_context,
- $doc_comment,
- ['$' . $stmt->keyVar->name => true]
- );
- }
-
- $value_type = $value_type ?? Type::getMixed();
-
- if ($stmt->byRef) {
- $value_type->by_ref = true;
- }
-
- AssignmentAnalyzer::analyze(
- $statements_analyzer,
- $stmt->valueVar,
- $stmt->expr,
- $value_type,
- $foreach_context,
- $doc_comment,
- $stmt->valueVar instanceof PhpParser\Node\Expr\Variable
- && is_string($stmt->valueVar->name)
- ? ['$' . $stmt->valueVar->name => true]
- : []
- );
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->var_id || !$var_comment->type) {
- continue;
- }
-
- $comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
-
- if (isset($foreach_context->vars_in_scope[$var_comment->var_id])) {
- $existing_var_type = $foreach_context->vars_in_scope[$var_comment->var_id];
- $comment_type->parent_nodes = $existing_var_type->parent_nodes;
- $comment_type->by_ref = $existing_var_type->by_ref;
- }
-
- $foreach_context->vars_in_scope[$var_comment->var_id] = $comment_type;
- }
-
- $loop_scope = new LoopScope($foreach_context, $context);
-
- $loop_scope->protected_var_ids = $context->protected_var_ids;
-
- if (LoopAnalyzer::analyze(
- $statements_analyzer,
- $stmt->stmts,
- [],
- [],
- $loop_scope,
- $inner_loop_context,
- false,
- $always_non_empty_array
- ) === false) {
- return false;
- }
-
- if (!$inner_loop_context) {
- throw new UnexpectedValueException('There should be an inner loop context');
- }
-
- $foreach_context->loop_scope = null;
-
- $context->vars_possibly_in_scope = array_merge(
- $foreach_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- );
-
- $context->referenced_var_ids = array_intersect_key(
- $foreach_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- if ($context->collect_exceptions) {
- $context->mergeExceptions($foreach_context);
- }
-
- return null;
- }
-
- /**
- * @param PhpParser\Node\Stmt\Foreach_|PhpParser\Node\Expr\YieldFrom $stmt
- * @return false|null
- */
- public static function checkIteratorType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\NodeAbstract $stmt,
- PhpParser\Node\Expr $expr,
- Union $iterator_type,
- Codebase $codebase,
- Context $context,
- ?Union &$key_type,
- ?Union &$value_type,
- bool &$always_non_empty_array
- ): ?bool {
- if ($iterator_type->isNull()) {
- if (IssueBuffer::accepts(
- new NullIterator(
- 'Cannot iterate over null',
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
-
- return false;
- }
-
- if ($iterator_type->isNullable() && !$iterator_type->ignore_nullable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyNullIterator(
- 'Cannot iterate over nullable var ' . $iterator_type,
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
-
- if ($iterator_type->isFalsable() && !$iterator_type->ignore_falsable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyFalseIterator(
- 'Cannot iterate over falsable var ' . $iterator_type,
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
-
- $has_valid_iterator = false;
- $invalid_iterator_types = [];
- $raw_object_types = [];
-
- foreach ($iterator_type->getAtomicTypes() as $iterator_atomic_type) {
- if ($iterator_atomic_type instanceof TTemplateParam) {
- $iterator_atomic_type = $iterator_atomic_type->as->getSingleAtomic();
- }
-
- // if it's an empty array, we cannot iterate over it
- if ($iterator_atomic_type instanceof TArray
- && $iterator_atomic_type->type_params[1]->isEmpty()
- ) {
- $always_non_empty_array = false;
- $has_valid_iterator = true;
- continue;
- }
-
- if ($iterator_atomic_type instanceof TNull
- || $iterator_atomic_type instanceof TFalse
- ) {
- $always_non_empty_array = false;
- continue;
- }
-
- if ($iterator_atomic_type instanceof TArray
- || $iterator_atomic_type instanceof TKeyedArray
- || $iterator_atomic_type instanceof TList
- ) {
- if ($iterator_atomic_type instanceof TKeyedArray) {
- if (!$iterator_atomic_type->sealed) {
- $always_non_empty_array = false;
- }
- $iterator_atomic_type = $iterator_atomic_type->getGenericArrayType();
- } elseif ($iterator_atomic_type instanceof TList) {
- $list_var_id = ExpressionIdentifier::getArrayVarId(
- $expr,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if (!$iterator_atomic_type instanceof TNonEmptyList) {
- $always_non_empty_array = false;
- }
-
- $iterator_atomic_type = new TArray([
- $list_var_id
- ? new Union([
- new TDependentListKey($list_var_id)
- ])
- : new Union([new TIntRange(0, null)]),
- $iterator_atomic_type->type_param
- ]);
- } elseif (!$iterator_atomic_type instanceof TNonEmptyArray) {
- $always_non_empty_array = false;
- }
-
- $value_type = Type::combineUnionTypes($value_type, clone $iterator_atomic_type->type_params[1]);
-
- $key_type_part = $iterator_atomic_type->type_params[0];
-
- $key_type = Type::combineUnionTypes($key_type, $key_type_part);
-
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $expr,
- null,
- $value_type,
- $key_type
- );
-
- $has_valid_iterator = true;
- continue;
- }
-
- $always_non_empty_array = false;
-
- if ($iterator_atomic_type instanceof Scalar || $iterator_atomic_type instanceof TVoid) {
- $invalid_iterator_types[] = $iterator_atomic_type->getKey();
-
- $value_type = Type::getMixed();
- } elseif ($iterator_atomic_type instanceof TObject || $iterator_atomic_type instanceof TMixed) {
- $has_valid_iterator = true;
- $value_type = Type::getMixed();
- $key_type = Type::getMixed();
-
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $expr,
- null,
- $value_type,
- $key_type
- );
-
- if (!$context->pure) {
- if ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- } else {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating iterator from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($iterator_atomic_type instanceof TIterable) {
- if ($iterator_atomic_type->extra_types) {
- $iterator_atomic_type_copy = clone $iterator_atomic_type;
- $iterator_atomic_type_copy->extra_types = [];
- $iterator_atomic_types = [$iterator_atomic_type_copy];
- $iterator_atomic_types = array_merge(
- $iterator_atomic_types,
- $iterator_atomic_type->extra_types
- );
- } else {
- $iterator_atomic_types = [$iterator_atomic_type];
- }
-
- $intersection_value_type = null;
- $intersection_key_type = null;
-
- foreach ($iterator_atomic_types as $iat) {
- if (!$iat instanceof TIterable) {
- continue;
- }
-
- [$key_type_part, $value_type_part] = $iat->type_params;
-
- if (!$intersection_value_type) {
- $intersection_value_type = $value_type_part;
- } else {
- $intersection_value_type = Type::intersectUnionTypes(
- $intersection_value_type,
- $value_type_part,
- $codebase
- ) ?? Type::getMixed();
- }
-
- if (!$intersection_key_type) {
- $intersection_key_type = $key_type_part;
- } else {
- $intersection_key_type = Type::intersectUnionTypes(
- $intersection_key_type,
- $key_type_part,
- $codebase
- ) ?? Type::getMixed();
- }
- }
-
- if (!$intersection_value_type || !$intersection_key_type) {
- throw new UnexpectedValueException('Should not happen');
- }
-
- $value_type = Type::combineUnionTypes($value_type, $intersection_value_type);
- $key_type = Type::combineUnionTypes($key_type, $intersection_key_type);
-
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $expr,
- null,
- $value_type,
- $key_type
- );
-
- $has_valid_iterator = true;
-
- if (!$context->pure) {
- if ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- } else {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating Traversable::getIterator from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($iterator_atomic_type instanceof TNamedObject) {
- if ($iterator_atomic_type->value !== 'Traversable' &&
- $iterator_atomic_type->value !== $statements_analyzer->getClassName()
- ) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $iterator_atomic_type->value,
- new CodeLocation($statements_analyzer->getSource(), $expr),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false) {
- return false;
- }
- }
-
- if (AtomicTypeComparator::isContainedBy(
- $codebase,
- $iterator_atomic_type,
- new TIterable([Type::getMixed(), Type::getMixed()])
- )) {
- self::handleIterable(
- $statements_analyzer,
- $iterator_atomic_type,
- $expr,
- $codebase,
- $context,
- $key_type,
- $value_type,
- $has_valid_iterator
- );
- } else {
- $raw_object_types[] = $iterator_atomic_type->value;
- }
-
- if (!$context->pure) {
- if ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- } else {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating iterator from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($raw_object_types) {
- if ($has_valid_iterator) {
- IssueBuffer::maybeAdd(
- new PossibleRawObjectIteration(
- 'Possibly undesired iteration over regular object ' . reset($raw_object_types),
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RawObjectIteration(
- 'Possibly undesired iteration over regular object ' . reset($raw_object_types),
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($invalid_iterator_types) {
- if ($has_valid_iterator) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidIterator(
- 'Cannot iterate over ' . $invalid_iterator_types[0],
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidIterator(
- 'Cannot iterate over ' . $invalid_iterator_types[0],
- new CodeLocation($statements_analyzer->getSource(), $expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return null;
- }
-
- public static function handleIterable(
- StatementsAnalyzer $statements_analyzer,
- TNamedObject $iterator_atomic_type,
- PhpParser\Node\Expr $foreach_expr,
- Codebase $codebase,
- Context $context,
- ?Union &$key_type,
- ?Union &$value_type,
- bool &$has_valid_iterator
- ): void {
- if ($iterator_atomic_type->extra_types) {
- $iterator_atomic_type_copy = clone $iterator_atomic_type;
- $iterator_atomic_type_copy->extra_types = [];
- $iterator_atomic_types = [$iterator_atomic_type_copy];
- $iterator_atomic_types = array_merge($iterator_atomic_types, $iterator_atomic_type->extra_types);
- } else {
- $iterator_atomic_types = [$iterator_atomic_type];
- }
-
- foreach ($iterator_atomic_types as $iterator_atomic_type) {
- if ($iterator_atomic_type instanceof TTemplateParam
- || $iterator_atomic_type instanceof TObjectWithProperties
- ) {
- throw new UnexpectedValueException('Shouldn’t get a generic param here');
- }
-
-
- $has_valid_iterator = true;
-
- if ($iterator_atomic_type instanceof TNamedObject
- && strtolower($iterator_atomic_type->value) === 'simplexmlelement'
- ) {
- $value_type = Type::combineUnionTypes(
- $value_type,
- new Union([clone $iterator_atomic_type])
- );
-
- $key_type = Type::combineUnionTypes(
- $key_type,
- Type::getString()
- );
- }
-
- if ($iterator_atomic_type instanceof TIterable
- || (strtolower($iterator_atomic_type->value) === 'traversable'
- || $codebase->classImplements(
- $iterator_atomic_type->value,
- 'Traversable'
- ) ||
- (
- $codebase->interfaceExists($iterator_atomic_type->value)
- && $codebase->interfaceExtends(
- $iterator_atomic_type->value,
- 'Traversable'
- )
- ))
- ) {
- if (strtolower($iterator_atomic_type->value) === 'iteratoraggregate'
- || $codebase->classImplements(
- $iterator_atomic_type->value,
- 'IteratorAggregate'
- )
- || ($codebase->interfaceExists($iterator_atomic_type->value)
- && $codebase->interfaceExtends(
- $iterator_atomic_type->value,
- 'IteratorAggregate'
- )
- )
- ) {
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_method_call = new VirtualMethodCall(
- $foreach_expr,
- new VirtualIdentifier('getIterator', $foreach_expr->getAttributes())
- );
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('PossiblyUndefinedMethod', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyUndefinedMethod']);
- }
-
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call,
- $context
- );
-
- $context->inside_call = $was_inside_call;
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('PossiblyUndefinedMethod', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyUndefinedMethod']);
- }
-
- $iterator_class_type = $statements_analyzer->node_data->getType($fake_method_call) ?? null;
-
- $statements_analyzer->node_data = $old_data_provider;
-
- if ($iterator_class_type) {
- foreach ($iterator_class_type->getAtomicTypes() as $array_atomic_type) {
- $key_type_part = null;
- $value_type_part = null;
-
- if ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- ) {
- if ($array_atomic_type instanceof TKeyedArray) {
- $array_atomic_type = $array_atomic_type->getGenericArrayType();
- }
-
- [$key_type_part, $value_type_part] = $array_atomic_type->type_params;
- } else {
- if ($array_atomic_type instanceof TNamedObject
- && $codebase->classExists($array_atomic_type->value)
- && $codebase->classImplements(
- $array_atomic_type->value,
- 'Traversable'
- )
- ) {
- $generic_storage = $codebase->classlike_storage_provider->get(
- $array_atomic_type->value
- );
-
- // The collection might be an iterator, in which case
- // we want to call the iterator function
- /** @psalm-suppress PossiblyUndefinedStringArrayOffset */
- if (!isset($generic_storage->template_extended_params['Traversable'])
- || ($generic_storage
- ->template_extended_params['Traversable']['TKey']->isMixed()
- && $generic_storage
- ->template_extended_params['Traversable']['TValue']->isMixed())
- ) {
- self::handleIterable(
- $statements_analyzer,
- $array_atomic_type,
- $fake_method_call,
- $codebase,
- $context,
- $key_type,
- $value_type,
- $has_valid_iterator
- );
-
- continue;
- }
- }
-
- if ($array_atomic_type instanceof TIterable
- || ($array_atomic_type instanceof TNamedObject
- && ($array_atomic_type->value === 'Traversable'
- || ($codebase->classOrInterfaceExists($array_atomic_type->value)
- && $codebase->classImplements(
- $array_atomic_type->value,
- 'Traversable'
- ))))
- ) {
- self::getKeyValueParamsForTraversableObject(
- $array_atomic_type,
- $codebase,
- $key_type_part,
- $value_type_part
- );
- }
- }
-
- if (!$key_type_part || !$value_type_part) {
- break;
- }
-
- $key_type = Type::combineUnionTypes($key_type, $key_type_part);
- $value_type = Type::combineUnionTypes($value_type, $value_type_part);
- }
- }
- } elseif ($codebase->classImplements(
- $iterator_atomic_type->value,
- 'Iterator'
- ) ||
- (
- $codebase->interfaceExists($iterator_atomic_type->value)
- && $codebase->interfaceExtends(
- $iterator_atomic_type->value,
- 'Iterator'
- )
- )
- ) {
- $iterator_value_type = self::getFakeMethodCallType(
- $statements_analyzer,
- $foreach_expr,
- $context,
- 'current'
- );
-
- $iterator_key_type = self::getFakeMethodCallType(
- $statements_analyzer,
- $foreach_expr,
- $context,
- 'key'
- );
-
- if ($iterator_value_type && !$iterator_value_type->isMixed()) {
- $value_type = Type::combineUnionTypes($value_type, $iterator_value_type);
- }
-
- if ($iterator_key_type && !$iterator_key_type->isMixed()) {
- $key_type = Type::combineUnionTypes($key_type, $iterator_key_type);
- }
- }
-
- if (!$key_type && !$value_type) {
- self::getKeyValueParamsForTraversableObject(
- $iterator_atomic_type,
- $codebase,
- $key_type,
- $value_type
- );
- }
-
- return;
- }
-
- if (!$codebase->classlikes->classOrInterfaceExists($iterator_atomic_type->value)) {
- return;
- }
- }
- }
-
- public static function getKeyValueParamsForTraversableObject(
- Atomic $iterator_atomic_type,
- Codebase $codebase,
- ?Union &$key_type,
- ?Union &$value_type
- ): void {
- if ($iterator_atomic_type instanceof TIterable
- || ($iterator_atomic_type instanceof TGenericObject
- && strtolower($iterator_atomic_type->value) === 'traversable')
- ) {
- $value_type = Type::combineUnionTypes($value_type, $iterator_atomic_type->type_params[1]);
- $key_type = Type::combineUnionTypes($key_type, $iterator_atomic_type->type_params[0]);
-
- return;
- }
-
- if ($iterator_atomic_type instanceof TNamedObject
- && (
- $codebase->classImplements(
- $iterator_atomic_type->value,
- 'Traversable'
- )
- || $codebase->interfaceExtends(
- $iterator_atomic_type->value,
- 'Traversable'
- )
- )
- ) {
- $generic_storage = $codebase->classlike_storage_provider->get(
- $iterator_atomic_type->value
- );
-
- if (!isset($generic_storage->template_extended_params['Traversable'])) {
- return;
- }
-
- if ($generic_storage->template_types
- || $iterator_atomic_type instanceof TGenericObject
- ) {
- // if we're just being passed the non-generic class itself, assume
- // that it's inside the calling class
- $passed_type_params = $iterator_atomic_type instanceof TGenericObject
- ? $iterator_atomic_type->type_params
- : array_values(
- array_map(
- /** @param array<string, Union> $arr */
- function (array $arr) use ($iterator_atomic_type): Union {
- return $arr[$iterator_atomic_type->value] ?? Type::getMixed();
- },
- $generic_storage->template_types
- )
- );
- } else {
- $passed_type_params = null;
- }
-
- $key_type = self::getExtendedType(
- 'TKey',
- 'Traversable',
- $generic_storage->name,
- $generic_storage->template_extended_params,
- $generic_storage->template_types,
- $passed_type_params
- );
-
- $value_type = self::getExtendedType(
- 'TValue',
- 'Traversable',
- $generic_storage->name,
- $generic_storage->template_extended_params,
- $generic_storage->template_types,
- $passed_type_params
- );
-
- return;
- }
- }
-
- private static function getFakeMethodCallType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $foreach_expr,
- Context $context,
- string $method_name
- ): ?Union {
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_method_call = new VirtualMethodCall(
- $foreach_expr,
- new VirtualIdentifier($method_name, $foreach_expr->getAttributes())
- );
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('PossiblyUndefinedMethod', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyUndefinedMethod']);
- }
-
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call,
- $context
- );
-
- $context->inside_call = $was_inside_call;
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('PossiblyUndefinedMethod', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyUndefinedMethod']);
- }
-
- $iterator_class_type = $statements_analyzer->node_data->getType($fake_method_call) ?? null;
-
- $statements_analyzer->node_data = $old_data_provider;
-
- return $iterator_class_type;
- }
-
- /**
- * @param array<string, array<string, Union>> $template_extended_params
- * @param array<string, array<string, Union>> $class_template_types
- * @param array<int, Union> $calling_type_params
- */
- private static function getExtendedType(
- string $template_name,
- string $template_class,
- string $calling_class,
- array $template_extended_params,
- ?array $class_template_types = null,
- ?array $calling_type_params = null
- ): ?Union {
- if ($calling_class === $template_class) {
- if (isset($class_template_types[$template_name]) && $calling_type_params) {
- $offset = array_search($template_name, array_keys($class_template_types));
-
- if ($offset !== false && isset($calling_type_params[$offset])) {
- return $calling_type_params[$offset];
- }
- }
-
- return null;
- }
-
- if (isset($template_extended_params[$template_class][$template_name])) {
- $extended_type = $template_extended_params[$template_class][$template_name];
-
- $return_type = null;
-
- foreach ($extended_type->getAtomicTypes() as $extended_atomic_type) {
- if (!$extended_atomic_type instanceof TTemplateParam) {
- $return_type = Type::combineUnionTypes(
- $return_type,
- $extended_type
- );
-
- continue;
- }
-
- $candidate_type = self::getExtendedType(
- $extended_atomic_type->param_name,
- $extended_atomic_type->defining_class,
- $calling_class,
- $template_extended_params,
- $class_template_types,
- $calling_type_params
- );
-
- if ($candidate_type) {
- $return_type = Type::combineUnionTypes(
- $return_type,
- $candidate_type
- );
- }
- }
-
- if ($return_type) {
- return $return_type;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php
deleted file mode 100644
index cd4002e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfConditionalAnalyzer.php
+++ /dev/null
@@ -1,377 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\ScopeAnalysisException;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\Scope\IfConditionalScope;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Issue\DocblockTypeContradiction;
-use Psalm\Issue\RedundantCondition;
-use Psalm\Issue\RedundantConditionGivenDocblockType;
-use Psalm\Issue\TypeDoesNotContainType;
-use Psalm\IssueBuffer;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function array_diff_key;
-use function array_filter;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_values;
-use function count;
-
-class IfConditionalAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $cond,
- Context $outer_context,
- Codebase $codebase,
- IfScope $if_scope,
- int $branch_point
- ): IfConditionalScope {
- $entry_clauses = [];
-
- // used when evaluating elseifs
- if ($if_scope->negated_clauses) {
- $entry_clauses = array_merge($outer_context->clauses, $if_scope->negated_clauses);
-
- $changed_var_ids = [];
-
- if ($if_scope->negated_types) {
- $vars_reconciled = Reconciler::reconcileKeyedTypes(
- $if_scope->negated_types,
- [],
- $outer_context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- [],
- $outer_context->inside_loop,
- new CodeLocation(
- $statements_analyzer->getSource(),
- $cond instanceof PhpParser\Node\Expr\BooleanNot
- ? $cond->expr
- : $cond,
- $outer_context->include_location,
- false
- )
- );
-
- if ($changed_var_ids) {
- $outer_context = clone $outer_context;
- $outer_context->vars_in_scope = $vars_reconciled;
-
- $entry_clauses = array_values(
- array_filter(
- $entry_clauses,
- function (Clause $c) use ($changed_var_ids): bool {
- return count($c->possibilities) > 1
- || $c->wedge
- || !isset($changed_var_ids[array_keys($c->possibilities)[0]]);
- }
- )
- );
- }
- }
- }
-
- // get the first expression in the if, which should be evaluated on its own
- // this allows us to update the context of $matches in
- // if (!preg_match('/a/', 'aa', $matches)) {
- // exit
- // }
- // echo $matches[0];
- $externally_applied_if_cond_expr = self::getDefinitelyEvaluatedExpressionAfterIf($cond);
-
- $internally_applied_if_cond_expr = self::getDefinitelyEvaluatedExpressionInsideIf($cond);
-
- $pre_condition_vars_in_scope = $outer_context->vars_in_scope;
-
- $referenced_var_ids = $outer_context->referenced_var_ids;
- $outer_context->referenced_var_ids = [];
-
- $pre_assigned_var_ids = $outer_context->assigned_var_ids;
- $outer_context->assigned_var_ids = [];
-
- $if_context = null;
-
- if ($internally_applied_if_cond_expr !== $externally_applied_if_cond_expr) {
- $if_context = clone $outer_context;
- }
-
- $was_inside_conditional = $outer_context->inside_conditional;
-
- $outer_context->inside_conditional = true;
-
- if ($externally_applied_if_cond_expr) {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $externally_applied_if_cond_expr,
- $outer_context
- ) === false) {
- throw new ScopeAnalysisException();
- }
- }
-
- $first_cond_assigned_var_ids = $outer_context->assigned_var_ids;
- $outer_context->assigned_var_ids = array_merge(
- $pre_assigned_var_ids,
- $first_cond_assigned_var_ids
- );
-
- $first_cond_referenced_var_ids = $outer_context->referenced_var_ids;
- $outer_context->referenced_var_ids = array_merge(
- $referenced_var_ids,
- $first_cond_referenced_var_ids
- );
-
- $outer_context->inside_conditional = $was_inside_conditional;
-
- if (!$if_context) {
- $if_context = clone $outer_context;
- }
-
- $if_conditional_context = clone $if_context;
- $if_conditional_context->if_context = $if_context;
- $if_conditional_context->if_scope = $if_scope;
-
- if ($codebase->alter_code) {
- $if_context->branch_point = $branch_point;
- }
-
- // we need to clone the current context so our ongoing updates
- // to $outer_context don't mess with elseif/else blocks
- $post_if_context = clone $outer_context;
-
- if ($internally_applied_if_cond_expr !== $cond
- || $externally_applied_if_cond_expr !== $cond
- ) {
- $assigned_var_ids = $first_cond_assigned_var_ids;
- $if_conditional_context->assigned_var_ids = [];
-
- $referenced_var_ids = $first_cond_referenced_var_ids;
- $if_conditional_context->referenced_var_ids = [];
-
- $was_inside_conditional = $if_conditional_context->inside_conditional;
-
- $if_conditional_context->inside_conditional = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $cond, $if_conditional_context) === false) {
- throw new ScopeAnalysisException();
- }
-
- $if_conditional_context->inside_conditional = $was_inside_conditional;
-
- /** @var array<string, bool> */
- $more_cond_referenced_var_ids = $if_conditional_context->referenced_var_ids;
- $if_conditional_context->referenced_var_ids = array_merge(
- $more_cond_referenced_var_ids,
- $referenced_var_ids
- );
-
- $cond_referenced_var_ids = array_merge(
- $first_cond_referenced_var_ids,
- $more_cond_referenced_var_ids
- );
-
- /** @var array<string, int> */
- $more_cond_assigned_var_ids = $if_conditional_context->assigned_var_ids;
- $if_conditional_context->assigned_var_ids = array_merge(
- $more_cond_assigned_var_ids,
- $assigned_var_ids
- );
-
- $assigned_in_conditional_var_ids = array_merge(
- $first_cond_assigned_var_ids,
- $more_cond_assigned_var_ids
- );
- } else {
- $cond_referenced_var_ids = $first_cond_referenced_var_ids;
-
- $assigned_in_conditional_var_ids = $first_cond_assigned_var_ids;
- }
-
- $newish_var_ids = array_map(
- /**
- * @param Union $_
- *
- * @return true
- */
- function (Union $_): bool {
- return true;
- },
- array_diff_key(
- $if_conditional_context->vars_in_scope,
- $pre_condition_vars_in_scope,
- $cond_referenced_var_ids,
- $assigned_in_conditional_var_ids
- )
- );
-
- self::handleParadoxicalCondition($statements_analyzer, $cond, true);
-
- // get all the var ids that were referenced in the conditional, but not assigned in it
- $cond_referenced_var_ids = array_diff_key($cond_referenced_var_ids, $assigned_in_conditional_var_ids);
-
- $cond_referenced_var_ids = array_merge($newish_var_ids, $cond_referenced_var_ids);
-
- return new IfConditionalScope(
- $if_context,
- $post_if_context,
- $cond_referenced_var_ids,
- $assigned_in_conditional_var_ids,
- $entry_clauses
- );
- }
-
- /**
- * Returns statements that are definitely evaluated before any statements after the end of the
- * if/elseif/else blocks
- */
- private static function getDefinitelyEvaluatedExpressionAfterIf(PhpParser\Node\Expr $stmt): ?PhpParser\Node\Expr
- {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- if ($stmt->left instanceof PhpParser\Node\Expr\ConstFetch
- && $stmt->left->name->parts === ['true']
- ) {
- return self::getDefinitelyEvaluatedExpressionAfterIf($stmt->right);
- }
-
- if ($stmt->right instanceof PhpParser\Node\Expr\ConstFetch
- && $stmt->right->name->parts === ['true']
- ) {
- return self::getDefinitelyEvaluatedExpressionAfterIf($stmt->left);
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalAnd
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalXor
- ) {
- return self::getDefinitelyEvaluatedExpressionAfterIf($stmt->left);
- }
-
- return $stmt;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BooleanNot) {
- $inner_stmt = self::getDefinitelyEvaluatedExpressionInsideIf($stmt->expr);
-
- if ($inner_stmt !== $stmt->expr) {
- return $inner_stmt;
- }
- }
-
- return $stmt;
- }
-
- /**
- * Returns statements that are definitely evaluated before any statements inside
- * the if block
- */
- private static function getDefinitelyEvaluatedExpressionInsideIf(PhpParser\Node\Expr $stmt): ?PhpParser\Node\Expr
- {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- if ($stmt->left instanceof PhpParser\Node\Expr\ConstFetch
- && $stmt->left->name->parts === ['true']
- ) {
- return self::getDefinitelyEvaluatedExpressionInsideIf($stmt->right);
- }
-
- if ($stmt->right instanceof PhpParser\Node\Expr\ConstFetch
- && $stmt->right->name->parts === ['true']
- ) {
- return self::getDefinitelyEvaluatedExpressionInsideIf($stmt->left);
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalXor
- ) {
- return self::getDefinitelyEvaluatedExpressionInsideIf($stmt->left);
- }
-
- return $stmt;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BooleanNot) {
- $inner_stmt = self::getDefinitelyEvaluatedExpressionAfterIf($stmt->expr);
-
- if ($inner_stmt !== $stmt->expr) {
- return $inner_stmt;
- }
- }
-
- return $stmt;
- }
-
- public static function handleParadoxicalCondition(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- bool $emit_redundant_with_assignation = false
- ): void {
- $type = $statements_analyzer->node_data->getType($stmt);
-
- if ($type !== null) {
- if ($type->isAlwaysFalsy()) {
- if ($type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- 'Operand of type ' . $type->getId() . ' is always false',
- new CodeLocation($statements_analyzer, $stmt),
- 'false falsy'
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- 'Operand of type ' . $type->getId() . ' is always false',
- new CodeLocation($statements_analyzer, $stmt),
- 'false falsy'
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($type->isAlwaysTruthy() &&
- (!$stmt instanceof PhpParser\Node\Expr\Assign || $emit_redundant_with_assignation)
- ) {
- if ($type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- 'Operand of type ' . $type->getId() . ' is always true',
- new CodeLocation($statements_analyzer, $stmt),
- 'true falsy'
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- 'Operand of type ' . $type->getId() . ' is always true',
- new CodeLocation($statements_analyzer, $stmt),
- 'true falsy'
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php
deleted file mode 100644
index f2071b3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php
+++ /dev/null
@@ -1,242 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block\IfElse;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\ConflictingReferenceConstraint;
-use Psalm\IssueBuffer;
-use Psalm\Type\Reconciler;
-
-use function array_diff_key;
-use function array_key_exists;
-use function array_keys;
-use function array_merge;
-use function count;
-use function in_array;
-use function preg_match;
-use function preg_quote;
-
-class ElseAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- ?PhpParser\Node\Stmt\Else_ $else,
- IfScope $if_scope,
- Context $else_context,
- Context $outer_context
- ): ?bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$else && !$if_scope->negated_clauses && !$else_context->clauses) {
- $if_scope->final_actions = array_merge([ScopeAnalyzer::ACTION_NONE], $if_scope->final_actions);
- $if_scope->assigned_var_ids = [];
- $if_scope->new_vars = [];
- $if_scope->redefined_vars = [];
- $if_scope->reasonable_clauses = [];
-
- return null;
- }
-
- $else_context->clauses = Algebra::simplifyCNF(
- array_merge(
- $else_context->clauses,
- $if_scope->negated_clauses
- )
- );
-
- $else_types = Algebra::getTruthsFromFormula($else_context->clauses);
-
- if (!$else && !$else_types) {
- $if_scope->final_actions = array_merge([ScopeAnalyzer::ACTION_NONE], $if_scope->final_actions);
- $if_scope->assigned_var_ids = [];
- $if_scope->new_vars = [];
- $if_scope->redefined_vars = [];
- $if_scope->reasonable_clauses = [];
-
- return null;
- }
-
- $original_context = clone $else_context;
-
- if ($else_types) {
- $changed_var_ids = [];
-
- $else_vars_reconciled = Reconciler::reconcileKeyedTypes(
- $else_types,
- [],
- $else_context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $else_context->inside_loop,
- $else
- ? new CodeLocation($statements_analyzer->getSource(), $else, $outer_context->include_location)
- : null
- );
-
- $else_context->vars_in_scope = $else_vars_reconciled;
-
- $else_context->clauses = Context::removeReconciledClauses($else_context->clauses, $changed_var_ids)[0];
-
- foreach ($changed_var_ids as $changed_var_id => $_) {
- foreach ($else_context->vars_in_scope as $var_id => $_) {
- if (preg_match('/' . preg_quote($changed_var_id, '/') . '[\]\[\-]/', $var_id)
- && !array_key_exists($var_id, $changed_var_ids)
- ) {
- unset($else_context->vars_in_scope[$var_id]);
- }
- }
- }
- }
-
- $old_else_context = clone $else_context;
-
- $pre_stmts_assigned_var_ids = $else_context->assigned_var_ids;
- $else_context->assigned_var_ids = [];
-
- $pre_possibly_assigned_var_ids = $else_context->possibly_assigned_var_ids;
- $else_context->possibly_assigned_var_ids = [];
-
- if ($else) {
- if ($statements_analyzer->analyze(
- $else->stmts,
- $else_context
- ) === false
- ) {
- return false;
- }
- }
-
- /** @var array<string, int> */
- $new_assigned_var_ids = $else_context->assigned_var_ids;
- $else_context->assigned_var_ids = $pre_stmts_assigned_var_ids;
-
- /** @var array<string, bool> */
- $new_possibly_assigned_var_ids = $else_context->possibly_assigned_var_ids;
- $else_context->possibly_assigned_var_ids = $pre_possibly_assigned_var_ids + $new_possibly_assigned_var_ids;
-
- if ($else) {
- foreach ($else_context->byref_constraints as $var_id => $byref_constraint) {
- if (isset($outer_context->byref_constraints[$var_id])
- && ($outer_constraint_type = $outer_context->byref_constraints[$var_id]->type)
- && $byref_constraint->type
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $byref_constraint->type,
- $outer_constraint_type
- )
- ) {
- IssueBuffer::maybeAdd(
- new ConflictingReferenceConstraint(
- 'There is more than one pass-by-reference constraint on ' . $var_id,
- new CodeLocation($statements_analyzer, $else, $outer_context->include_location, true)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $outer_context->byref_constraints[$var_id] = $byref_constraint;
- }
- }
- }
-
- $final_actions = $else
- ? ScopeAnalyzer::getControlActions(
- $else->stmts,
- $statements_analyzer->node_data,
- $codebase->config->exit_functions,
- []
- )
- : [ScopeAnalyzer::ACTION_NONE];
- // has a return/throw at end
- $has_ending_statements = $final_actions === [ScopeAnalyzer::ACTION_END];
- $has_leaving_statements = $has_ending_statements
- || (count($final_actions) && !in_array(ScopeAnalyzer::ACTION_NONE, $final_actions, true));
-
- $has_break_statement = $final_actions === [ScopeAnalyzer::ACTION_BREAK];
- $has_continue_statement = $final_actions === [ScopeAnalyzer::ACTION_CONTINUE];
-
- $if_scope->final_actions = array_merge($final_actions, $if_scope->final_actions);
-
- // if it doesn't end in a return
- if (!$has_leaving_statements) {
- IfAnalyzer::updateIfScope(
- $codebase,
- $if_scope,
- $else_context,
- $original_context,
- $new_assigned_var_ids,
- $new_possibly_assigned_var_ids,
- [],
- (bool) $else
- );
-
- $if_scope->reasonable_clauses = [];
- }
-
- // update the parent context as necessary
- if ($if_scope->negatable_if_types) {
- $outer_context->update(
- $old_else_context,
- $else_context,
- $has_leaving_statements,
- array_keys($if_scope->negatable_if_types),
- $if_scope->updated_vars
- );
- }
-
- if (!$has_ending_statements) {
- $vars_possibly_in_scope = array_diff_key(
- $else_context->vars_possibly_in_scope,
- $outer_context->vars_possibly_in_scope
- );
-
- $possibly_assigned_var_ids = $new_possibly_assigned_var_ids;
-
- if ($has_leaving_statements && $else_context->loop_scope) {
- if (!$has_continue_statement && !$has_break_statement) {
- $if_scope->new_vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $if_scope->new_vars_possibly_in_scope
- );
-
- $if_scope->possibly_assigned_var_ids = array_merge(
- $possibly_assigned_var_ids,
- $if_scope->possibly_assigned_var_ids
- );
- }
-
- $else_context->loop_scope->vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $else_context->loop_scope->vars_possibly_in_scope
- );
- } elseif (!$has_leaving_statements) {
- $if_scope->new_vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $if_scope->new_vars_possibly_in_scope
- );
-
- $if_scope->possibly_assigned_var_ids = array_merge(
- $possibly_assigned_var_ids,
- $if_scope->possibly_assigned_var_ids
- );
- }
- }
-
- if ($outer_context->collect_exceptions) {
- $outer_context->mergeExceptions($else_context);
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php
deleted file mode 100644
index 105bf20..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseIfAnalyzer.php
+++ /dev/null
@@ -1,439 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block\IfElse;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Exception\ScopeAnalysisException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\AlgebraAnalyzer;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfConditionalAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\ConflictingReferenceConstraint;
-use Psalm\IssueBuffer;
-use Psalm\Type\Reconciler;
-
-use function array_combine;
-use function array_diff;
-use function array_diff_key;
-use function array_filter;
-use function array_key_exists;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_reduce;
-use function array_unique;
-use function array_values;
-use function count;
-use function in_array;
-use function preg_match;
-use function preg_quote;
-use function spl_object_id;
-
-class ElseIfAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\ElseIf_ $elseif,
- IfScope $if_scope,
- Context $else_context,
- Context $outer_context,
- Codebase $codebase,
- int $branch_point
- ): ?bool {
- $pre_conditional_context = clone $else_context;
-
- try {
- $if_conditional_scope = IfConditionalAnalyzer::analyze(
- $statements_analyzer,
- $elseif->cond,
- $else_context,
- $codebase,
- $if_scope,
- $branch_point
- );
-
- $elseif_context = $if_conditional_scope->if_context;
- $cond_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
- $assigned_in_conditional_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
- $entry_clauses = $if_conditional_scope->entry_clauses;
- } catch (ScopeAnalysisException $e) {
- return false;
- }
-
- $mixed_var_ids = [];
-
- foreach ($elseif_context->vars_in_scope as $var_id => $type) {
- if ($type->hasMixed()) {
- $mixed_var_ids[] = $var_id;
- }
- }
-
- $elseif_cond_id = spl_object_id($elseif->cond);
-
- $elseif_clauses = FormulaGenerator::getFormula(
- $elseif_cond_id,
- $elseif_cond_id,
- $elseif->cond,
- $else_context->self,
- $statements_analyzer,
- $codebase
- );
-
- $elseif_clauses = array_map(
- /**
- * @return Clause
- */
- function (Clause $c) use ($mixed_var_ids, $elseif_cond_id): Clause {
- $keys = array_keys($c->possibilities);
-
- $mixed_var_ids = array_diff($mixed_var_ids, $keys);
-
- foreach ($keys as $key) {
- foreach ($mixed_var_ids as $mixed_var_id) {
- if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
- return new Clause([], $elseif_cond_id, $elseif_cond_id, true);
- }
- }
- }
-
- return $c;
- },
- $elseif_clauses
- );
-
- $entry_clauses = array_map(
- /**
- * @return Clause
- */
- function (Clause $c) use ($assigned_in_conditional_var_ids, $elseif_cond_id): Clause {
- $keys = array_keys($c->possibilities);
-
- foreach ($keys as $key) {
- foreach ($assigned_in_conditional_var_ids as $conditional_assigned_var_id => $_) {
- if (preg_match('/^' . preg_quote($conditional_assigned_var_id, '/') . '(\[|-|$)/', $key)) {
- return new Clause([], $elseif_cond_id, $elseif_cond_id, true);
- }
- }
- }
-
- return $c;
- },
- $entry_clauses
- );
-
- // this will see whether any of the clauses in set A conflict with the clauses in set B
- AlgebraAnalyzer::checkForParadox(
- $entry_clauses,
- $elseif_clauses,
- $statements_analyzer,
- $elseif->cond,
- $assigned_in_conditional_var_ids
- );
-
- $elseif_context_clauses = array_merge($entry_clauses, $elseif_clauses);
-
- if ($elseif_context->reconciled_expression_clauses) {
- $reconciled_expression_clauses = $elseif_context->reconciled_expression_clauses;
-
- $elseif_context_clauses = array_values(
- array_filter(
- $elseif_context_clauses,
- function ($c) use ($reconciled_expression_clauses): bool {
- return !in_array($c->hash, $reconciled_expression_clauses);
- }
- )
- );
- }
-
- $elseif_context->clauses = Algebra::simplifyCNF($elseif_context_clauses);
-
- $active_elseif_types = [];
-
- try {
- if (array_filter(
- $entry_clauses,
- function ($clause): bool {
- return (bool)$clause->possibilities;
- }
- )) {
- $omit_keys = array_reduce(
- $entry_clauses,
- /**
- * @param array<string> $carry
- * @return array<string>
- */
- function (array $carry, Clause $clause): array {
- return array_merge($carry, array_keys($clause->possibilities));
- },
- []
- );
-
- $omit_keys = array_combine($omit_keys, $omit_keys);
- $omit_keys = array_diff_key($omit_keys, Algebra::getTruthsFromFormula($entry_clauses));
-
- $cond_referenced_var_ids = array_diff_key(
- $cond_referenced_var_ids,
- $omit_keys
- );
- }
- $reconcilable_elseif_types = Algebra::getTruthsFromFormula(
- $elseif_context->clauses,
- spl_object_id($elseif->cond),
- $cond_referenced_var_ids,
- $active_elseif_types
- );
- $negated_elseif_types = Algebra::getTruthsFromFormula(
- Algebra::negateFormula($elseif_clauses)
- );
- } catch (ComplicatedExpressionException $e) {
- $reconcilable_elseif_types = [];
- $negated_elseif_types = [];
- }
-
- $all_negated_vars = array_unique(
- array_merge(
- array_keys($negated_elseif_types),
- array_keys($if_scope->negated_types)
- )
- );
-
- foreach ($all_negated_vars as $var_id) {
- if (isset($negated_elseif_types[$var_id])) {
- if (isset($if_scope->negated_types[$var_id])) {
- $if_scope->negated_types[$var_id] = array_merge(
- $if_scope->negated_types[$var_id],
- $negated_elseif_types[$var_id]
- );
- } else {
- $if_scope->negated_types[$var_id] = $negated_elseif_types[$var_id];
- }
- }
- }
-
- $newly_reconciled_var_ids = [];
-
- // if the elseif has an || in the conditional, we cannot easily reason about it
- if ($reconcilable_elseif_types) {
- $elseif_vars_reconciled = Reconciler::reconcileKeyedTypes(
- $reconcilable_elseif_types,
- $active_elseif_types,
- $elseif_context->vars_in_scope,
- $newly_reconciled_var_ids,
- $cond_referenced_var_ids,
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $elseif_context->inside_loop,
- new CodeLocation(
- $statements_analyzer->getSource(),
- $elseif->cond instanceof PhpParser\Node\Expr\BooleanNot
- ? $elseif->cond->expr
- : $elseif->cond,
- $outer_context->include_location
- )
- );
-
- $elseif_context->vars_in_scope = $elseif_vars_reconciled;
-
- if ($newly_reconciled_var_ids) {
- $elseif_context->clauses = Context::removeReconciledClauses(
- $elseif_context->clauses,
- $newly_reconciled_var_ids
- )[0];
-
- foreach ($newly_reconciled_var_ids as $changed_var_id => $_) {
- foreach ($elseif_context->vars_in_scope as $var_id => $_) {
- if (preg_match('/' . preg_quote($changed_var_id, '/') . '[\]\[\-]/', $var_id)
- && !array_key_exists($var_id, $newly_reconciled_var_ids)
- && !array_key_exists($var_id, $cond_referenced_var_ids)
- ) {
- unset($elseif_context->vars_in_scope[$var_id]);
- }
- }
- }
- }
- }
-
- $pre_stmts_assigned_var_ids = $elseif_context->assigned_var_ids;
- $elseif_context->assigned_var_ids = [];
- $pre_stmts_possibly_assigned_var_ids = $elseif_context->possibly_assigned_var_ids;
- $elseif_context->possibly_assigned_var_ids = [];
-
- if ($statements_analyzer->analyze(
- $elseif->stmts,
- $elseif_context
- ) === false
- ) {
- return false;
- }
-
- /** @var array<string, int> */
- $new_stmts_assigned_var_ids = $elseif_context->assigned_var_ids;
- $elseif_context->assigned_var_ids = $pre_stmts_assigned_var_ids + $new_stmts_assigned_var_ids;
-
- /** @var array<string, bool> */
- $new_stmts_possibly_assigned_var_ids = $elseif_context->possibly_assigned_var_ids;
- $elseif_context->possibly_assigned_var_ids =
- $pre_stmts_possibly_assigned_var_ids + $new_stmts_possibly_assigned_var_ids;
-
- foreach ($elseif_context->byref_constraints as $var_id => $byref_constraint) {
- if (isset($outer_context->byref_constraints[$var_id])
- && ($outer_constraint_type = $outer_context->byref_constraints[$var_id]->type)
- && $byref_constraint->type
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $byref_constraint->type,
- $outer_constraint_type
- )
- ) {
- IssueBuffer::maybeAdd(
- new ConflictingReferenceConstraint(
- 'There is more than one pass-by-reference constraint on ' . $var_id,
- new CodeLocation($statements_analyzer, $elseif, $outer_context->include_location, true)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $outer_context->byref_constraints[$var_id] = $byref_constraint;
- }
- }
-
- $final_actions = ScopeAnalyzer::getControlActions(
- $elseif->stmts,
- $statements_analyzer->node_data,
- $codebase->config->exit_functions,
- []
- );
- // has a return/throw at end
- $has_ending_statements = $final_actions === [ScopeAnalyzer::ACTION_END];
- $has_leaving_statements = $has_ending_statements
- || (count($final_actions) && !in_array(ScopeAnalyzer::ACTION_NONE, $final_actions, true));
-
- $has_break_statement = $final_actions === [ScopeAnalyzer::ACTION_BREAK];
- $has_continue_statement = $final_actions === [ScopeAnalyzer::ACTION_CONTINUE];
-
- $if_scope->final_actions = array_merge($final_actions, $if_scope->final_actions);
-
- // update the parent context as necessary
- if (!$has_leaving_statements) {
- IfAnalyzer::updateIfScope(
- $codebase,
- $if_scope,
- $elseif_context,
- $outer_context,
- array_merge($new_stmts_assigned_var_ids, $assigned_in_conditional_var_ids),
- $new_stmts_possibly_assigned_var_ids,
- $newly_reconciled_var_ids
- );
-
- $reasonable_clause_count = count($if_scope->reasonable_clauses);
-
- if ($reasonable_clause_count && $reasonable_clause_count < 20000 && $elseif_clauses) {
- $if_scope->reasonable_clauses = Algebra::combineOredClauses(
- $if_scope->reasonable_clauses,
- $elseif_clauses,
- $elseif_cond_id
- );
- } else {
- $if_scope->reasonable_clauses = [];
- }
- } else {
- $if_scope->reasonable_clauses = [];
- }
-
- if ($negated_elseif_types) {
- if ($has_leaving_statements) {
- $newly_reconciled_var_ids = [];
-
- $leaving_vars_reconciled = Reconciler::reconcileKeyedTypes(
- $negated_elseif_types,
- [],
- $pre_conditional_context->vars_in_scope,
- $newly_reconciled_var_ids,
- [],
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $elseif_context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $elseif, $outer_context->include_location)
- );
-
- $implied_outer_context = clone $elseif_context;
- $implied_outer_context->vars_in_scope = $leaving_vars_reconciled;
-
- $updated_vars = [];
-
- $outer_context->update(
- $elseif_context,
- $implied_outer_context,
- false,
- array_keys($negated_elseif_types),
- $updated_vars
- );
- }
- }
-
- if (!$has_ending_statements) {
- $vars_possibly_in_scope = array_diff_key(
- $elseif_context->vars_possibly_in_scope,
- $outer_context->vars_possibly_in_scope
- );
-
- $possibly_assigned_var_ids = $new_stmts_possibly_assigned_var_ids;
-
- if ($has_leaving_statements && $elseif_context->loop_scope) {
- if (!$has_continue_statement && !$has_break_statement) {
- $if_scope->new_vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $if_scope->new_vars_possibly_in_scope
- );
- $if_scope->possibly_assigned_var_ids = array_merge(
- $possibly_assigned_var_ids,
- $if_scope->possibly_assigned_var_ids
- );
- }
-
- $elseif_context->loop_scope->vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $elseif_context->loop_scope->vars_possibly_in_scope
- );
- } elseif (!$has_leaving_statements) {
- $if_scope->new_vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $if_scope->new_vars_possibly_in_scope
- );
- $if_scope->possibly_assigned_var_ids = array_merge(
- $possibly_assigned_var_ids,
- $if_scope->possibly_assigned_var_ids
- );
- }
- }
-
- if ($outer_context->collect_exceptions) {
- $outer_context->mergeExceptions($elseif_context);
- }
-
- try {
- $if_scope->negated_clauses = Algebra::simplifyCNF(
- array_merge(
- $if_scope->negated_clauses,
- Algebra::negateFormula($elseif_clauses)
- )
- );
- } catch (ComplicatedExpressionException $e) {
- $if_scope->negated_clauses = [];
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php
deleted file mode 100644
index 005a272..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/IfAnalyzer.php
+++ /dev/null
@@ -1,532 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block\IfElse;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Scope\IfConditionalScope;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\ConflictingReferenceConstraint;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\BinaryOp\VirtualBooleanOr;
-use Psalm\Node\Expr\VirtualBooleanNot;
-use Psalm\Node\Expr\VirtualFuncCall;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\VirtualArg;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function array_diff_key;
-use function array_filter;
-use function array_intersect;
-use function array_intersect_key;
-use function array_keys;
-use function array_merge;
-use function array_unique;
-use function count;
-use function in_array;
-use function strpos;
-use function substr;
-
-class IfAnalyzer
-{
- /**
- * @param array<string, Union> $pre_assignment_else_redefined_vars
- *
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\If_ $stmt,
- IfScope $if_scope,
- IfConditionalScope $if_conditional_scope,
- Context $if_context,
- Context $old_if_context,
- Context $outer_context,
- array $pre_assignment_else_redefined_vars
- ): ?bool {
- $codebase = $statements_analyzer->getCodebase();
-
- $if_context->parent_context = $outer_context;
-
- $assigned_var_ids = $if_context->assigned_var_ids;
- $possibly_assigned_var_ids = $if_context->possibly_assigned_var_ids;
- $if_context->assigned_var_ids = [];
- $if_context->possibly_assigned_var_ids = [];
-
- if ($statements_analyzer->analyze(
- $stmt->stmts,
- $if_context
- ) === false
- ) {
- return false;
- }
-
- $final_actions = ScopeAnalyzer::getControlActions(
- $stmt->stmts,
- $statements_analyzer->node_data,
- $codebase->config->exit_functions,
- []
- );
-
- $has_ending_statements = $final_actions === [ScopeAnalyzer::ACTION_END];
-
- $has_leaving_statements = $has_ending_statements
- || (count($final_actions) && !in_array(ScopeAnalyzer::ACTION_NONE, $final_actions, true));
-
- $has_break_statement = $final_actions === [ScopeAnalyzer::ACTION_BREAK];
- $has_continue_statement = $final_actions === [ScopeAnalyzer::ACTION_CONTINUE];
-
- $if_scope->final_actions = $final_actions;
-
- /** @var array<string, int> */
- $new_assigned_var_ids = $if_context->assigned_var_ids;
- /** @var array<string, bool> */
- $new_possibly_assigned_var_ids = $if_context->possibly_assigned_var_ids;
-
- $if_context->assigned_var_ids = array_merge($assigned_var_ids, $new_assigned_var_ids);
- $if_context->possibly_assigned_var_ids = array_merge(
- $possibly_assigned_var_ids,
- $new_possibly_assigned_var_ids
- );
-
- foreach ($if_context->byref_constraints as $var_id => $byref_constraint) {
- if (isset($outer_context->byref_constraints[$var_id])
- && $byref_constraint->type
- && ($outer_constraint_type = $outer_context->byref_constraints[$var_id]->type)
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $byref_constraint->type,
- $outer_constraint_type
- )
- ) {
- IssueBuffer::maybeAdd(
- new ConflictingReferenceConstraint(
- 'There is more than one pass-by-reference constraint on ' . $var_id
- . ' between ' . $byref_constraint->type->getId()
- . ' and ' . $outer_constraint_type->getId(),
- new CodeLocation($statements_analyzer, $stmt, $outer_context->include_location, true)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $outer_context->byref_constraints[$var_id] = $byref_constraint;
- }
- }
-
- $mic_drop = false;
-
- if (!$has_leaving_statements) {
- self::updateIfScope(
- $codebase,
- $if_scope,
- $if_context,
- $outer_context,
- $new_assigned_var_ids,
- $new_possibly_assigned_var_ids,
- $if_scope->if_cond_changed_var_ids
- );
-
- if ($if_scope->reasonable_clauses) {
- // remove all reasonable clauses that would be negated by the if stmts
- foreach ($new_assigned_var_ids as $var_id => $_) {
- $if_scope->reasonable_clauses = Context::filterClauses(
- $var_id,
- $if_scope->reasonable_clauses,
- $if_context->vars_in_scope[$var_id] ?? null,
- $statements_analyzer
- );
- }
- }
- } else {
- if (!$has_break_statement) {
- $if_scope->reasonable_clauses = [];
-
- // If we're assigning inside
- if ($if_conditional_scope->assigned_in_conditional_var_ids
- && $if_scope->post_leaving_if_context
- ) {
- self::addConditionallyAssignedVarsToContext(
- $statements_analyzer,
- $stmt->cond,
- $if_scope->post_leaving_if_context,
- $outer_context,
- $if_conditional_scope->assigned_in_conditional_var_ids
- );
- }
-
- if (!$stmt->else && !$stmt->elseifs) {
- $mic_drop = self::handleMicDrop(
- $statements_analyzer,
- $stmt->cond,
- $if_scope,
- $outer_context,
- $new_assigned_var_ids
- );
-
- $outer_context->clauses = Algebra::simplifyCNF(
- array_merge($outer_context->clauses, $if_scope->negated_clauses)
- );
- }
- }
- }
-
- // update the parent context as necessary, but only if we can safely reason about type negation.
- // We only update vars that changed both at the start of the if block and then again by an assignment
- // in the if statement.
- if ($if_scope->negated_types && !$mic_drop) {
- $vars_to_update = array_intersect(
- array_keys($pre_assignment_else_redefined_vars),
- array_keys($if_scope->negated_types)
- );
-
- $extra_vars_to_update = [];
-
- // if there's an object-like array in there, we also need to update the root array variable
- foreach ($vars_to_update as $var_id) {
- $bracked_pos = strpos($var_id, '[');
- if ($bracked_pos !== false) {
- $extra_vars_to_update[] = substr($var_id, 0, $bracked_pos);
- }
- }
-
- if ($extra_vars_to_update) {
- $vars_to_update = array_unique(array_merge($extra_vars_to_update, $vars_to_update));
- }
-
- //update $if_context vars to include the pre-assignment else vars
- if (!$stmt->else && !$has_leaving_statements) {
- foreach ($pre_assignment_else_redefined_vars as $var_id => $type) {
- if (isset($if_context->vars_in_scope[$var_id])) {
- $if_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $if_context->vars_in_scope[$var_id],
- $type,
- $codebase
- );
- }
- }
- }
-
- $outer_context->update(
- $old_if_context,
- $if_context,
- $has_leaving_statements,
- $vars_to_update,
- $if_scope->updated_vars
- );
- }
-
- if (!$has_ending_statements) {
- $vars_possibly_in_scope = array_diff_key(
- $if_context->vars_possibly_in_scope,
- $outer_context->vars_possibly_in_scope
- );
-
- if ($if_context->loop_scope) {
- if (!$has_continue_statement && !$has_break_statement) {
- $if_scope->new_vars_possibly_in_scope = $vars_possibly_in_scope;
- }
-
- $if_context->loop_scope->vars_possibly_in_scope = array_merge(
- $vars_possibly_in_scope,
- $if_context->loop_scope->vars_possibly_in_scope
- );
- } elseif (!$has_leaving_statements) {
- $if_scope->new_vars_possibly_in_scope = $vars_possibly_in_scope;
- }
- }
-
- if ($outer_context->collect_exceptions) {
- $outer_context->mergeExceptions($if_context);
- }
-
- return null;
- }
-
- /**
- * This handles the situation when returning inside an
- * if block with no else or elseifs
- *
- * @param array<string, int> $new_assigned_var_ids
- */
- private static function handleMicDrop(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $cond,
- IfScope $if_scope,
- Context $post_if_context,
- array $new_assigned_var_ids
- ): bool {
- if (!$if_scope->negated_types) {
- return false;
- }
-
- $newly_reconciled_var_ids = [];
-
- $post_if_context_vars_reconciled = Reconciler::reconcileKeyedTypes(
- $if_scope->negated_types,
- [],
- $post_if_context->vars_in_scope,
- $newly_reconciled_var_ids,
- [],
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $post_if_context->inside_loop,
- new CodeLocation(
- $statements_analyzer->getSource(),
- $cond instanceof PhpParser\Node\Expr\BooleanNot
- ? $cond->expr
- : $cond,
- $post_if_context->include_location,
- false
- )
- );
-
- foreach ($newly_reconciled_var_ids as $changed_var_id => $_) {
- $post_if_context->removeVarFromConflictingClauses($changed_var_id);
- }
-
- $newly_reconciled_var_ids += $new_assigned_var_ids;
-
- foreach ($newly_reconciled_var_ids as $var_id => $_) {
- $if_scope->negated_clauses = Context::filterClauses(
- $var_id,
- $if_scope->negated_clauses
- );
- }
-
- foreach ($newly_reconciled_var_ids as $var_id => $_) {
- $first_appearance = $statements_analyzer->getFirstAppearance($var_id);
-
- if ($first_appearance
- && isset($post_if_context->vars_in_scope[$var_id])
- && isset($post_if_context_vars_reconciled[$var_id])
- && $post_if_context->vars_in_scope[$var_id]->hasMixed()
- && !$post_if_context_vars_reconciled[$var_id]->hasMixed()
- ) {
- if (!$post_if_context->collect_initializations
- && !$post_if_context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- ) {
- $parent_source = $statements_analyzer->getSource();
-
- $functionlike_storage = $parent_source instanceof FunctionLikeAnalyzer
- ? $parent_source->getFunctionLikeStorage($statements_analyzer)
- : null;
-
- if (!$functionlike_storage
- || (!$parent_source->getSource() instanceof TraitAnalyzer
- && !isset($functionlike_storage->param_lookup[substr($var_id, 1)]))
- ) {
- $codebase = $statements_analyzer->getCodebase();
- $codebase->analyzer->decrementMixedCount($statements_analyzer->getFilePath());
- }
- }
-
- IssueBuffer::remove(
- $statements_analyzer->getFilePath(),
- 'MixedAssignment',
- $first_appearance->raw_file_start
- );
- }
- }
-
- $post_if_context->vars_in_scope = $post_if_context_vars_reconciled;
-
- return true;
- }
-
- /**
- * @param array<string, int> $assigned_in_conditional_var_ids
- */
- public static function addConditionallyAssignedVarsToContext(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $cond,
- Context $post_leaving_if_context,
- Context $post_if_context,
- array $assigned_in_conditional_var_ids
- ): void {
- // this filters out coercions to expected types in ArgumentAnalyzer
- $assigned_in_conditional_var_ids = array_filter($assigned_in_conditional_var_ids);
-
- if (!$assigned_in_conditional_var_ids) {
- return;
- }
-
- $exprs = self::getDefinitelyEvaluatedOredExpressions($cond);
-
- // if there was no assignment in the first expression it's safe to proceed
- $old_node_data = $statements_analyzer->node_data;
- $statements_analyzer->node_data = clone $old_node_data;
-
- IssueBuffer::startRecording();
-
- foreach ($exprs as $expr) {
- if ($expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd) {
- $fake_not = new VirtualBooleanOr(
- self::negateExpr($expr->left),
- self::negateExpr($expr->right),
- $expr->getAttributes()
- );
- } else {
- $fake_not = self::negateExpr($expr);
- }
-
- $fake_negated_expr = new VirtualFuncCall(
- new VirtualFullyQualified('assert'),
- [new VirtualArg(
- $fake_not,
- false,
- false,
- $expr->getAttributes()
- )],
- $expr->getAttributes()
- );
-
- $post_leaving_if_context->inside_negation = !$post_leaving_if_context->inside_negation;
-
- ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $fake_negated_expr,
- $post_leaving_if_context
- );
-
- $post_leaving_if_context->inside_negation = !$post_leaving_if_context->inside_negation;
- }
-
- IssueBuffer::clearRecordingLevel();
- IssueBuffer::stopRecording();
-
- $statements_analyzer->node_data = $old_node_data;
-
- foreach ($assigned_in_conditional_var_ids as $var_id => $_) {
- if (isset($post_leaving_if_context->vars_in_scope[$var_id])) {
- $post_if_context->vars_in_scope[$var_id] = clone $post_leaving_if_context->vars_in_scope[$var_id];
- }
- }
- }
-
- /**
- * Returns all expressions inside an ored expression
- * @return non-empty-list<PhpParser\Node\Expr>
- */
- private static function getDefinitelyEvaluatedOredExpressions(PhpParser\Node\Expr $stmt): array
- {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalXor
- ) {
- return array_merge(
- self::getDefinitelyEvaluatedOredExpressions($stmt->left),
- self::getDefinitelyEvaluatedOredExpressions($stmt->right)
- );
- }
-
- return [$stmt];
- }
-
- private static function negateExpr(PhpParser\Node\Expr $expr): PhpParser\Node\Expr
- {
- if ($expr instanceof PhpParser\Node\Expr\BooleanNot) {
- return $expr->expr;
- }
-
- return new VirtualBooleanNot($expr, $expr->getAttributes());
- }
-
- /**
- * @param array<string, int> $assigned_var_ids
- * @param array<string, bool> $possibly_assigned_var_ids
- * @param array<string, bool> $newly_reconciled_var_ids
- */
- public static function updateIfScope(
- Codebase $codebase,
- IfScope $if_scope,
- Context $if_context,
- Context $outer_context,
- array $assigned_var_ids,
- array $possibly_assigned_var_ids,
- array $newly_reconciled_var_ids,
- bool $update_new_vars = true
- ): void {
- $redefined_vars = $if_context->getRedefinedVars($outer_context->vars_in_scope);
-
- if ($if_scope->new_vars === null) {
- if ($update_new_vars) {
- $if_scope->new_vars = array_diff_key($if_context->vars_in_scope, $outer_context->vars_in_scope);
- }
- } else {
- foreach ($if_scope->new_vars as $new_var => $type) {
- if (!$if_context->hasVariable($new_var)) {
- unset($if_scope->new_vars[$new_var]);
- } else {
- $if_scope->new_vars[$new_var] = Type::combineUnionTypes(
- $type,
- $if_context->vars_in_scope[$new_var],
- $codebase
- );
- }
- }
- }
-
- $possibly_redefined_vars = $redefined_vars;
-
- foreach ($possibly_redefined_vars as $var_id => $_) {
- if (!isset($possibly_assigned_var_ids[$var_id])
- && isset($newly_reconciled_var_ids[$var_id])
- ) {
- unset($possibly_redefined_vars[$var_id]);
- }
- }
-
- if ($if_scope->assigned_var_ids === null) {
- $if_scope->assigned_var_ids = $assigned_var_ids;
- } else {
- $if_scope->assigned_var_ids = array_intersect_key($assigned_var_ids, $if_scope->assigned_var_ids);
- }
-
- $if_scope->possibly_assigned_var_ids += $possibly_assigned_var_ids;
-
- if ($if_scope->redefined_vars === null) {
- $if_scope->redefined_vars = $redefined_vars;
- $if_scope->possibly_redefined_vars = $possibly_redefined_vars;
- } else {
- foreach ($if_scope->redefined_vars as $redefined_var => $type) {
- if (!isset($redefined_vars[$redefined_var])) {
- unset($if_scope->redefined_vars[$redefined_var]);
- } else {
- $if_scope->redefined_vars[$redefined_var] = Type::combineUnionTypes(
- $redefined_vars[$redefined_var],
- $type,
- $codebase
- );
-
- if (isset($outer_context->vars_in_scope[$redefined_var])
- && $if_scope->redefined_vars[$redefined_var]->equals(
- $outer_context->vars_in_scope[$redefined_var]
- )
- ) {
- unset($if_scope->redefined_vars[$redefined_var]);
- }
- }
- }
-
- foreach ($possibly_redefined_vars as $var => $type) {
- $if_scope->possibly_redefined_vars[$var] = Type::combineUnionTypes(
- $type,
- $if_scope->possibly_redefined_vars[$var] ?? null,
- $codebase
- );
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php
deleted file mode 100644
index 1f9dcc3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElseAnalyzer.php
+++ /dev/null
@@ -1,509 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Exception\ScopeAnalysisException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\AlgebraAnalyzer;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElse\ElseAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElse\ElseIfAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElse\IfAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Node\Expr\VirtualBooleanNot;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-
-use function array_combine;
-use function array_diff;
-use function array_diff_key;
-use function array_filter;
-use function array_intersect_key;
-use function array_key_exists;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_reduce;
-use function array_unique;
-use function array_values;
-use function count;
-use function in_array;
-use function preg_match;
-use function preg_quote;
-use function spl_object_id;
-
-/**
- * @internal
- */
-class IfElseAnalyzer
-{
- /**
- * System of type substitution and deletion
- *
- * for example
- *
- * x: A|null
- *
- * if (x)
- * (x: A)
- * x = B -- effects: remove A from the type of x, add B
- * else
- * (x: null)
- * x = C -- effects: remove null from the type of x, add C
- *
- *
- * x: A|null
- *
- * if (!x)
- * (x: null)
- * throw new Exception -- effects: remove null from the type of x
- *
- *
- * @return null|false
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\If_ $stmt,
- Context $context
- ): ?bool {
- $codebase = $statements_analyzer->getCodebase();
-
- $if_scope = new IfScope();
-
- // We need to clone the original context for later use if we're exiting in this if conditional
- if ($stmt->cond instanceof PhpParser\Node\Expr\BinaryOp
- || ($stmt->cond instanceof PhpParser\Node\Expr\BooleanNot
- && $stmt->cond->expr instanceof PhpParser\Node\Expr\BinaryOp)
- ) {
- $final_actions = ScopeAnalyzer::getControlActions(
- $stmt->stmts,
- null,
- $codebase->config->exit_functions,
- []
- );
-
- $has_leaving_statements = $final_actions === [ScopeAnalyzer::ACTION_END]
- || (count($final_actions) && !in_array(ScopeAnalyzer::ACTION_NONE, $final_actions, true));
-
- if ($has_leaving_statements) {
- $if_scope->post_leaving_if_context = clone $context;
- }
- }
-
- try {
- $if_conditional_scope = IfConditionalAnalyzer::analyze(
- $statements_analyzer,
- $stmt->cond,
- $context,
- $codebase,
- $if_scope,
- $context->branch_point ?: (int) $stmt->getAttribute('startFilePos')
- );
-
- $if_context = $if_conditional_scope->if_context;
-
- $post_if_context = $if_conditional_scope->post_if_context;
- $cond_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
- $assigned_in_conditional_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
- } catch (ScopeAnalysisException $e) {
- return false;
- }
-
- $mixed_var_ids = [];
-
- foreach ($if_context->vars_in_scope as $var_id => $type) {
- if ($type->isMixed() && isset($context->vars_in_scope[$var_id])) {
- $mixed_var_ids[] = $var_id;
- }
- }
-
- $cond_object_id = spl_object_id($stmt->cond);
-
- $if_clauses = FormulaGenerator::getFormula(
- $cond_object_id,
- $cond_object_id,
- $stmt->cond,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- if (count($if_clauses) > 200) {
- $if_clauses = [];
- }
-
- $if_clauses = array_map(
- /**
- * @return Clause
- */
- function (Clause $c) use ($mixed_var_ids, $cond_object_id): Clause {
- $keys = array_keys($c->possibilities);
-
- $mixed_var_ids = array_diff($mixed_var_ids, $keys);
-
- foreach ($keys as $key) {
- foreach ($mixed_var_ids as $mixed_var_id) {
- if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
- return new Clause([], $cond_object_id, $cond_object_id, true);
- }
- }
- }
-
- return $c;
- },
- $if_clauses
- );
-
- $entry_clauses = $context->clauses;
-
- // this will see whether any of the clauses in set A conflict with the clauses in set B
- AlgebraAnalyzer::checkForParadox(
- $context->clauses,
- $if_clauses,
- $statements_analyzer,
- $stmt->cond,
- $assigned_in_conditional_var_ids
- );
-
- // if we have assignments in the if, we may have duplicate clauses
- if ($assigned_in_conditional_var_ids) {
- $if_clauses = Algebra::simplifyCNF($if_clauses);
- }
-
- $if_context_clauses = array_merge($entry_clauses, $if_clauses);
-
- $if_context->clauses = Algebra::simplifyCNF($if_context_clauses);
-
- if ($if_context->reconciled_expression_clauses) {
- $reconciled_expression_clauses = $if_context->reconciled_expression_clauses;
-
- $if_context->clauses = array_values(
- array_filter(
- $if_context->clauses,
- function ($c) use ($reconciled_expression_clauses): bool {
- return !in_array($c->hash, $reconciled_expression_clauses);
- }
- )
- );
-
- if (count($if_context->clauses) === 1
- && $if_context->clauses[0]->wedge
- && !$if_context->clauses[0]->possibilities
- ) {
- $if_context->clauses = [];
- $if_context->reconciled_expression_clauses = [];
- }
- }
-
- // define this before we alter local clauses after reconciliation
- $if_scope->reasonable_clauses = $if_context->clauses;
-
- try {
- $if_scope->negated_clauses = Algebra::negateFormula($if_clauses);
- } catch (ComplicatedExpressionException $e) {
- try {
- $if_scope->negated_clauses = FormulaGenerator::getFormula(
- $cond_object_id,
- $cond_object_id,
- new VirtualBooleanNot($stmt->cond),
- $context->self,
- $statements_analyzer,
- $codebase,
- false
- );
- } catch (ComplicatedExpressionException $e) {
- $if_scope->negated_clauses = [];
- }
- }
-
- $if_scope->negated_types = Algebra::getTruthsFromFormula(
- Algebra::simplifyCNF(
- array_merge($context->clauses, $if_scope->negated_clauses)
- )
- );
-
- $active_if_types = [];
-
- $reconcilable_if_types = Algebra::getTruthsFromFormula(
- $if_context->clauses,
- spl_object_id($stmt->cond),
- $cond_referenced_var_ids,
- $active_if_types
- );
-
- if (array_filter(
- $context->clauses,
- function ($clause): bool {
- return (bool)$clause->possibilities;
- }
- )) {
- $omit_keys = array_reduce(
- $context->clauses,
- /**
- * @param array<string> $carry
- * @return array<string>
- */
- function (array $carry, Clause $clause): array {
- return array_merge($carry, array_keys($clause->possibilities));
- },
- []
- );
-
- $omit_keys = array_combine($omit_keys, $omit_keys);
- $omit_keys = array_diff_key($omit_keys, Algebra::getTruthsFromFormula($context->clauses));
-
- $cond_referenced_var_ids = array_diff_key(
- $cond_referenced_var_ids,
- $omit_keys
- );
- }
-
- // if the if has an || in the conditional, we cannot easily reason about it
- if ($reconcilable_if_types) {
- $changed_var_ids = [];
-
- $if_vars_in_scope_reconciled =
- Reconciler::reconcileKeyedTypes(
- $reconcilable_if_types,
- $active_if_types,
- $if_context->vars_in_scope,
- $changed_var_ids,
- $cond_referenced_var_ids,
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $if_context->inside_loop,
- $context->check_variables
- ? new CodeLocation(
- $statements_analyzer->getSource(),
- $stmt->cond instanceof PhpParser\Node\Expr\BooleanNot
- ? $stmt->cond->expr
- : $stmt->cond,
- $context->include_location
- ) : null
- );
-
- $if_context->vars_in_scope = $if_vars_in_scope_reconciled;
-
- foreach ($reconcilable_if_types as $var_id => $_) {
- $if_context->vars_possibly_in_scope[$var_id] = true;
- }
-
- if ($changed_var_ids) {
- $if_context->clauses = Context::removeReconciledClauses($if_context->clauses, $changed_var_ids)[0];
-
- foreach ($changed_var_ids as $changed_var_id => $_) {
- foreach ($if_context->vars_in_scope as $var_id => $_) {
- if (preg_match('/' . preg_quote($changed_var_id, '/') . '[\]\[\-]/', $var_id)
- && !array_key_exists($var_id, $changed_var_ids)
- && !array_key_exists($var_id, $cond_referenced_var_ids)
- ) {
- unset($if_context->vars_in_scope[$var_id]);
- }
- }
- }
- }
-
- $if_scope->if_cond_changed_var_ids = $changed_var_ids;
- }
-
- $if_context->reconciled_expression_clauses = [];
-
- $old_if_context = clone $if_context;
- $context->vars_possibly_in_scope = array_merge(
- $if_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- );
-
- $context->referenced_var_ids = array_merge(
- $if_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- $temp_else_context = clone $post_if_context;
-
- $changed_var_ids = [];
-
- if ($if_scope->negated_types) {
- $else_vars_reconciled = Reconciler::reconcileKeyedTypes(
- $if_scope->negated_types,
- [],
- $temp_else_context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $context->inside_loop,
- $context->check_variables
- ? new CodeLocation(
- $statements_analyzer->getSource(),
- $stmt->cond instanceof PhpParser\Node\Expr\BooleanNot
- ? $stmt->cond->expr
- : $stmt->cond,
- $context->include_location
- ) : null
- );
-
- $temp_else_context->vars_in_scope = $else_vars_reconciled;
- }
-
- // we calculate the vars redefined in a hypothetical else statement to determine
- // which vars of the if we can safely change
- $pre_assignment_else_redefined_vars = array_intersect_key(
- $temp_else_context->getRedefinedVars($context->vars_in_scope, true),
- $changed_var_ids
- );
-
- // check the if
- if (IfAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $if_scope,
- $if_conditional_scope,
- $if_context,
- $old_if_context,
- $context,
- $pre_assignment_else_redefined_vars
- ) === false) {
- return false;
- }
-
- // this has to go on a separate line because the phar compactor messes with precedence
- $scope_to_clone = $if_scope->post_leaving_if_context ?? $post_if_context;
- $else_context = clone $scope_to_clone;
-
- // check the elseifs
- foreach ($stmt->elseifs as $elseif) {
- if (ElseIfAnalyzer::analyze(
- $statements_analyzer,
- $elseif,
- $if_scope,
- $else_context,
- $context,
- $codebase,
- $else_context->branch_point ?: (int) $stmt->getAttribute('startFilePos')
- ) === false) {
- return false;
- }
- }
-
- if ($stmt->else) {
- if ($codebase->alter_code) {
- $else_context->branch_point =
- $else_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
- }
-
- if (ElseAnalyzer::analyze(
- $statements_analyzer,
- $stmt->else,
- $if_scope,
- $else_context,
- $context
- ) === false) {
- return false;
- }
-
- if ($context->loop_scope) {
- $context->loop_scope->final_actions = array_unique(
- array_merge(
- $context->loop_scope->final_actions,
- $if_scope->final_actions
- )
- );
- }
-
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $if_scope->new_vars_possibly_in_scope
- );
-
- $context->possibly_assigned_var_ids = array_merge(
- $context->possibly_assigned_var_ids,
- $if_scope->possibly_assigned_var_ids ?: []
- );
-
- // vars can only be defined/redefined if there was an else (defined in every block)
- $context->assigned_var_ids = array_merge(
- $context->assigned_var_ids,
- $if_scope->assigned_var_ids ?: []
- );
-
- if ($if_scope->new_vars) {
- foreach ($if_scope->new_vars as $var_id => $type) {
- if (isset($context->vars_possibly_in_scope[$var_id])
- && $statements_analyzer->data_flow_graph
- ) {
- $type->parent_nodes += $statements_analyzer->getParentNodesForPossiblyUndefinedVariable($var_id);
- }
-
- $context->vars_in_scope[$var_id] = $type;
- }
- }
-
- if ($if_scope->redefined_vars) {
- foreach ($if_scope->redefined_vars as $var_id => $type) {
- $context->vars_in_scope[$var_id] = $type;
- $if_scope->updated_vars[$var_id] = true;
-
- if ($if_scope->reasonable_clauses) {
- $if_scope->reasonable_clauses = Context::filterClauses(
- $var_id,
- $if_scope->reasonable_clauses,
- $context->vars_in_scope[$var_id] ?? null,
- $statements_analyzer
- );
- }
- }
- }
-
- if ($if_scope->reasonable_clauses
- && (count($if_scope->reasonable_clauses) > 1 || !$if_scope->reasonable_clauses[0]->wedge)
- ) {
- $context->clauses = Algebra::simplifyCNF(
- array_merge(
- $if_scope->reasonable_clauses,
- $context->clauses
- )
- );
- }
-
- if ($if_scope->possibly_redefined_vars) {
- foreach ($if_scope->possibly_redefined_vars as $var_id => $type) {
- if (isset($context->vars_in_scope[$var_id])) {
- if (!$type->failed_reconciliation
- && !isset($if_scope->updated_vars[$var_id])
- ) {
- $combined_type = Type::combineUnionTypes(
- $context->vars_in_scope[$var_id],
- $type,
- $codebase
- );
-
- if (!$combined_type->equals($context->vars_in_scope[$var_id])) {
- $context->removeDescendents($var_id, $combined_type);
- }
-
- $context->vars_in_scope[$var_id] = $combined_type;
- } else {
- $context->vars_in_scope[$var_id]->parent_nodes += $type->parent_nodes;
- }
- }
- }
- }
-
- $context->possibly_assigned_var_ids += $if_scope->possibly_assigned_var_ids;
-
- if (!in_array(ScopeAnalyzer::ACTION_NONE, $if_scope->final_actions, true)) {
- $context->has_returned = true;
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php
deleted file mode 100644
index 1cfd402..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php
+++ /dev/null
@@ -1,735 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\PhpVisitor\AssignmentMapVisitor;
-use Psalm\Internal\PhpVisitor\NodeCleanerVisitor;
-use Psalm\Internal\Scope\LoopScope;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-
-use function array_intersect_key;
-use function array_keys;
-use function array_merge;
-use function array_unique;
-use function in_array;
-use function spl_object_id;
-use function strpos;
-
-/**
- * @internal
- */
-class LoopAnalyzer
-{
- /**
- * Checks an array of statements in a loop
- *
- * @param array<PhpParser\Node\Stmt> $stmts
- * @param PhpParser\Node\Expr[] $pre_conditions
- * @param PhpParser\Node\Expr[] $post_expressions
- *
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- array $stmts,
- array $pre_conditions,
- array $post_expressions,
- LoopScope $loop_scope,
- Context &$inner_context = null,
- bool $is_do = false,
- bool $always_enters_loop = false
- ): ?bool {
- $traverser = new PhpParser\NodeTraverser;
-
- $assignment_mapper = new AssignmentMapVisitor($loop_scope->loop_context->self);
- $traverser->addVisitor($assignment_mapper);
-
- $traverser->traverse(array_merge($pre_conditions, $stmts, $post_expressions));
-
- $assignment_map = $assignment_mapper->getAssignmentMap();
-
- $assignment_depth = 0;
-
- $always_assigned_before_loop_body_vars = [];
-
- $pre_condition_clauses = [];
-
- $original_protected_var_ids = $loop_scope->loop_parent_context->protected_var_ids;
-
- $codebase = $statements_analyzer->getCodebase();
-
- $inner_do_context = null;
-
- if ($pre_conditions) {
- foreach ($pre_conditions as $i => $pre_condition) {
- $pre_condition_id = spl_object_id($pre_condition);
-
- $pre_condition_clauses[$i] = FormulaGenerator::getFormula(
- $pre_condition_id,
- $pre_condition_id,
- $pre_condition,
- $loop_scope->loop_context->self,
- $statements_analyzer,
- $codebase
- );
- }
- } else {
- $always_assigned_before_loop_body_vars = Context::getNewOrUpdatedVarIds(
- $loop_scope->loop_parent_context,
- $loop_scope->loop_context
- );
- }
-
- $final_actions = ScopeAnalyzer::getControlActions(
- $stmts,
- $statements_analyzer->node_data,
- Config::getInstance()->exit_functions,
- []
- );
-
- $does_always_break = $final_actions === [ScopeAnalyzer::ACTION_BREAK];
-
- $has_continue = in_array(ScopeAnalyzer::ACTION_CONTINUE, $final_actions, true);
-
- if ($assignment_map) {
- $first_var_id = array_keys($assignment_map)[0];
-
- $assignment_depth = self::getAssignmentMapDepth($first_var_id, $assignment_map);
- }
-
- if ($has_continue) {
- // this intuitively feels right to me – if there's a continue statement,
- // maybe more assignment intrigue is possible
- $assignment_depth++;
- }
-
- $loop_scope->loop_context->parent_context = $loop_scope->loop_parent_context;
-
- $pre_outer_context = $loop_scope->loop_parent_context;
-
- if ($assignment_depth === 0 || $does_always_break) {
- $inner_context = clone $loop_scope->loop_context;
-
- foreach ($inner_context->vars_in_scope as $context_var_id => $context_type) {
- $inner_context->vars_in_scope[$context_var_id] = clone $context_type;
- }
-
- $inner_context->loop_scope = $loop_scope;
-
- $inner_context->parent_context = $loop_scope->loop_context;
- $old_referenced_var_ids = $inner_context->referenced_var_ids;
- $inner_context->referenced_var_ids = [];
-
- foreach ($pre_conditions as $condition_offset => $pre_condition) {
- self::applyPreConditionToLoopContext(
- $statements_analyzer,
- $pre_condition,
- $pre_condition_clauses[$condition_offset],
- $inner_context,
- $loop_scope->loop_parent_context,
- $is_do
- );
- }
-
- $inner_context->protected_var_ids = $loop_scope->protected_var_ids;
-
- $statements_analyzer->analyze($stmts, $inner_context);
- self::updateLoopScopeContexts($loop_scope, $loop_scope->loop_parent_context);
-
- foreach ($post_expressions as $post_expression) {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $post_expression,
- $loop_scope->loop_context
- ) === false
- ) {
- return false;
- }
- }
-
- $inner_context->referenced_var_ids = $old_referenced_var_ids + $inner_context->referenced_var_ids;
-
- $loop_scope->loop_parent_context->vars_possibly_in_scope = array_merge(
- $inner_context->vars_possibly_in_scope,
- $loop_scope->loop_parent_context->vars_possibly_in_scope
- );
- } else {
- $pre_outer_context = clone $loop_scope->loop_parent_context;
-
- $analyzer = $statements_analyzer->getCodebase()->analyzer;
-
- $original_mixed_counts = $analyzer->getMixedCountsForFile($statements_analyzer->getFilePath());
-
- $pre_condition_vars_in_scope = $loop_scope->loop_context->vars_in_scope;
-
- IssueBuffer::startRecording();
-
- if (!$is_do) {
- foreach ($pre_conditions as $condition_offset => $pre_condition) {
- self::applyPreConditionToLoopContext(
- $statements_analyzer,
- $pre_condition,
- $pre_condition_clauses[$condition_offset],
- $loop_scope->loop_context,
- $loop_scope->loop_parent_context,
- $is_do
- );
- }
- }
-
- // record all the vars that existed before we did the first pass through the loop
- $pre_loop_context = clone $loop_scope->loop_context;
-
- $inner_context = clone $loop_scope->loop_context;
-
- foreach ($inner_context->vars_in_scope as $context_var_id => $context_type) {
- $inner_context->vars_in_scope[$context_var_id] = clone $context_type;
- }
-
- $inner_context->parent_context = $loop_scope->loop_context;
- $inner_context->loop_scope = $loop_scope;
-
- $old_referenced_var_ids = $inner_context->referenced_var_ids;
- $inner_context->referenced_var_ids = [];
-
- $inner_context->protected_var_ids = $loop_scope->protected_var_ids;
-
- $statements_analyzer->analyze($stmts, $inner_context);
-
- self::updateLoopScopeContexts($loop_scope, $pre_outer_context);
-
- $inner_context->protected_var_ids = $original_protected_var_ids;
-
- if ($is_do) {
- $inner_do_context = clone $inner_context;
-
- foreach ($pre_conditions as $condition_offset => $pre_condition) {
- $always_assigned_before_loop_body_vars = array_merge(
- self::applyPreConditionToLoopContext(
- $statements_analyzer,
- $pre_condition,
- $pre_condition_clauses[$condition_offset],
- $inner_context,
- $loop_scope->loop_parent_context,
- $is_do
- ),
- $always_assigned_before_loop_body_vars
- );
- }
- }
-
- $always_assigned_before_loop_body_vars = array_unique($always_assigned_before_loop_body_vars);
-
- foreach ($post_expressions as $post_expression) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $post_expression, $inner_context) === false) {
- return false;
- }
- }
-
- $inner_context->referenced_var_ids = array_intersect_key(
- $old_referenced_var_ids,
- $inner_context->referenced_var_ids
- );
-
- $recorded_issues = IssueBuffer::clearRecordingLevel();
- IssueBuffer::stopRecording();
-
- for ($i = 0; $i < $assignment_depth; ++$i) {
- $vars_to_remove = [];
-
- $loop_scope->iteration_count++;
-
- $has_changes = false;
-
- // reset the $inner_context to what it was before we started the analysis,
- // but union the types with what's in the loop scope
-
- foreach ($inner_context->vars_in_scope as $var_id => $type) {
- if (in_array($var_id, $always_assigned_before_loop_body_vars, true)) {
- // set the vars to whatever the while/foreach loop expects them to be
- if (!isset($pre_loop_context->vars_in_scope[$var_id])
- || !$type->equals($pre_loop_context->vars_in_scope[$var_id])
- ) {
- $has_changes = true;
- }
- } elseif (isset($pre_outer_context->vars_in_scope[$var_id])) {
- if (!$type->equals($pre_outer_context->vars_in_scope[$var_id])) {
- $has_changes = true;
-
- // widen the foreach context type with the initial context type
- $inner_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $inner_context->vars_in_scope[$var_id],
- $pre_outer_context->vars_in_scope[$var_id]
- );
-
- // if there's a change, invalidate related clauses
- $pre_loop_context->removeVarFromConflictingClauses($var_id);
-
- $loop_scope->loop_parent_context->possibly_assigned_var_ids[$var_id] = true;
- }
-
- if (isset($loop_scope->loop_context->vars_in_scope[$var_id])
- && !$type->equals($loop_scope->loop_context->vars_in_scope[$var_id])
- ) {
- $has_changes = true;
-
- // widen the foreach context type with the initial context type
- $inner_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $inner_context->vars_in_scope[$var_id],
- $loop_scope->loop_context->vars_in_scope[$var_id]
- );
-
- // if there's a change, invalidate related clauses
- $pre_loop_context->removeVarFromConflictingClauses($var_id);
- }
- } else {
- // give an opportunity to redeemed UndefinedVariable issues
- if ($recorded_issues) {
- $has_changes = true;
- }
-
- // if we're in a do block we don't want to remove vars before evaluating
- // the where conditional
- if (!$is_do) {
- $vars_to_remove[] = $var_id;
- }
- }
- }
-
- $inner_context->has_returned = false;
-
- $loop_scope->loop_parent_context->vars_possibly_in_scope = array_merge(
- $inner_context->vars_possibly_in_scope,
- $loop_scope->loop_parent_context->vars_possibly_in_scope
- );
-
- // if there are no changes to the types, no need to re-examine
- if (!$has_changes) {
- break;
- }
-
- // remove vars that were defined in the foreach
- foreach ($vars_to_remove as $var_id) {
- unset($inner_context->vars_in_scope[$var_id]);
- }
-
- $inner_context->clauses = $pre_loop_context->clauses;
- $inner_context->byref_constraints = $pre_loop_context->byref_constraints;
-
- $analyzer->setMixedCountsForFile($statements_analyzer->getFilePath(), $original_mixed_counts);
- IssueBuffer::startRecording();
-
- foreach ($pre_loop_context->vars_in_scope as $var_id => $_) {
- if (!isset($pre_condition_vars_in_scope[$var_id])
- && isset($inner_context->vars_in_scope[$var_id])
- && strpos($var_id, '->') === false
- && strpos($var_id, '[') === false
- ) {
- $inner_context->vars_in_scope[$var_id]->possibly_undefined = true;
- }
- }
-
- if (!$is_do) {
- foreach ($pre_conditions as $condition_offset => $pre_condition) {
- self::applyPreConditionToLoopContext(
- $statements_analyzer,
- $pre_condition,
- $pre_condition_clauses[$condition_offset],
- $inner_context,
- $loop_scope->loop_parent_context,
- false
- );
- }
- }
-
- foreach ($always_assigned_before_loop_body_vars as $var_id) {
- if ((!isset($inner_context->vars_in_scope[$var_id])
- || $inner_context->vars_in_scope[$var_id]->getId()
- !== $pre_loop_context->vars_in_scope[$var_id]->getId()
- || $inner_context->vars_in_scope[$var_id]->from_docblock
- !== $pre_loop_context->vars_in_scope[$var_id]->from_docblock
- )
- ) {
- if (isset($pre_loop_context->vars_in_scope[$var_id])) {
- $inner_context->vars_in_scope[$var_id] = clone $pre_loop_context->vars_in_scope[$var_id];
- } else {
- unset($inner_context->vars_in_scope[$var_id]);
- }
- }
- }
-
- $inner_context->clauses = $pre_loop_context->clauses;
-
- $inner_context->protected_var_ids = $loop_scope->protected_var_ids;
-
- $traverser = new PhpParser\NodeTraverser;
-
- $traverser->addVisitor(
- new NodeCleanerVisitor(
- $statements_analyzer->node_data
- )
- );
- $traverser->traverse($stmts);
-
- $statements_analyzer->analyze($stmts, $inner_context);
-
- self::updateLoopScopeContexts($loop_scope, $pre_outer_context);
-
- $inner_context->protected_var_ids = $original_protected_var_ids;
-
- if ($is_do) {
- $inner_do_context = clone $inner_context;
-
- foreach ($pre_conditions as $condition_offset => $pre_condition) {
- self::applyPreConditionToLoopContext(
- $statements_analyzer,
- $pre_condition,
- $pre_condition_clauses[$condition_offset],
- $inner_context,
- $loop_scope->loop_parent_context,
- $is_do
- );
- }
- }
-
- foreach ($post_expressions as $post_expression) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $post_expression, $inner_context) === false) {
- return false;
- }
- }
-
- $recorded_issues = IssueBuffer::clearRecordingLevel();
-
- IssueBuffer::stopRecording();
- }
-
- if ($recorded_issues) {
- foreach ($recorded_issues as $recorded_issue) {
- // if we're not in any loops then this will just result in the issue being emitted
- IssueBuffer::bubbleUp($recorded_issue);
- }
- }
- }
-
- $does_sometimes_break = in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true);
- $does_always_break = $loop_scope->final_actions === [ScopeAnalyzer::ACTION_BREAK];
-
- if ($does_sometimes_break) {
- if ($loop_scope->possibly_redefined_loop_parent_vars !== null) {
- foreach ($loop_scope->possibly_redefined_loop_parent_vars as $var => $type) {
- $loop_scope->loop_parent_context->vars_in_scope[$var] = Type::combineUnionTypes(
- $type,
- $loop_scope->loop_parent_context->vars_in_scope[$var]
- );
-
- $loop_scope->loop_parent_context->possibly_assigned_var_ids[$var] = true;
- }
- }
- }
-
- foreach ($loop_scope->loop_parent_context->vars_in_scope as $var_id => $type) {
- if (!isset($loop_scope->loop_context->vars_in_scope[$var_id])) {
- continue;
- }
-
- if ($loop_scope->loop_context->vars_in_scope[$var_id]->getId() !== $type->getId()) {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $loop_scope->loop_parent_context->vars_in_scope[$var_id],
- $loop_scope->loop_context->vars_in_scope[$var_id]
- );
-
- $loop_scope->loop_parent_context->removeVarFromConflictingClauses($var_id);
- } else {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id]->parent_nodes
- += $loop_scope->loop_context->vars_in_scope[$var_id]->parent_nodes;
- }
- }
-
- if (!$does_always_break) {
- foreach ($loop_scope->loop_parent_context->vars_in_scope as $var_id => $type) {
- if (!isset($inner_context->vars_in_scope[$var_id])) {
- unset($loop_scope->loop_parent_context->vars_in_scope[$var_id]);
- continue;
- }
-
- if ($inner_context->vars_in_scope[$var_id]->hasMixed()) {
- $inner_context->vars_in_scope[$var_id]->parent_nodes
- += $loop_scope->loop_parent_context->vars_in_scope[$var_id]->parent_nodes;
-
- $loop_scope->loop_parent_context->vars_in_scope[$var_id] =
- $inner_context->vars_in_scope[$var_id];
- $loop_scope->loop_parent_context->removeVarFromConflictingClauses($var_id);
-
- continue;
- }
-
- if ($inner_context->vars_in_scope[$var_id]->getId() !== $type->getId()) {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $loop_scope->loop_parent_context->vars_in_scope[$var_id],
- $inner_context->vars_in_scope[$var_id]
- );
-
- $loop_scope->loop_parent_context->removeVarFromConflictingClauses($var_id);
- } else {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id]->parent_nodes = array_merge(
- $loop_scope->loop_parent_context->vars_in_scope[$var_id]->parent_nodes,
- $inner_context->vars_in_scope[$var_id]->parent_nodes
- );
- }
- }
- }
-
- if ($pre_conditions && $pre_condition_clauses && !ScopeAnalyzer::doesEverBreak($stmts)) {
- // if the loop contains an assertion and there are no break statements, we can negate that assertion
- // and apply it to the current context
-
- try {
- $negated_pre_condition_clauses = Algebra::negateFormula(array_merge(...$pre_condition_clauses));
- } catch (ComplicatedExpressionException $e) {
- $negated_pre_condition_clauses = [];
- }
-
- $negated_pre_condition_types = Algebra::getTruthsFromFormula($negated_pre_condition_clauses);
-
- if ($negated_pre_condition_types) {
- $changed_var_ids = [];
-
- $vars_in_scope_reconciled = Reconciler::reconcileKeyedTypes(
- $negated_pre_condition_types,
- [],
- $inner_context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- [],
- true,
- new CodeLocation($statements_analyzer->getSource(), $pre_conditions[0])
- );
-
- foreach ($changed_var_ids as $var_id => $_) {
- if (isset($vars_in_scope_reconciled[$var_id])
- && isset($loop_scope->loop_parent_context->vars_in_scope[$var_id])
- ) {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id] = $vars_in_scope_reconciled[$var_id];
- }
-
- $loop_scope->loop_parent_context->removeVarFromConflictingClauses($var_id);
- }
- }
- }
-
- $loop_scope->loop_context->referenced_var_ids = array_merge(
- array_intersect_key(
- $inner_context->referenced_var_ids,
- $pre_outer_context->vars_in_scope
- ),
- $loop_scope->loop_context->referenced_var_ids
- );
-
- if ($always_enters_loop) {
- foreach ($inner_context->vars_in_scope as $var_id => $type) {
- // if there are break statements in the loop it's not certain
- // that the loop has finished executing, so the assertions at the end
- // the loop in the while conditional may not hold
- if (in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true)
- || in_array(ScopeAnalyzer::ACTION_CONTINUE, $loop_scope->final_actions, true)
- ) {
- if (isset($loop_scope->possibly_defined_loop_parent_vars[$var_id])) {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_defined_loop_parent_vars[$var_id]
- );
- }
- } else {
- $loop_scope->loop_parent_context->vars_in_scope[$var_id] = $type;
- }
- }
- }
-
- if ($inner_do_context) {
- $inner_context = $inner_do_context;
- }
-
- return null;
- }
-
- private static function updateLoopScopeContexts(
- LoopScope $loop_scope,
- Context $pre_outer_context
- ): void {
- $updated_loop_vars = [];
-
- if (!in_array(ScopeAnalyzer::ACTION_CONTINUE, $loop_scope->final_actions, true)) {
- $loop_scope->loop_context->vars_in_scope = $pre_outer_context->vars_in_scope;
- } else {
- if ($loop_scope->redefined_loop_vars !== null) {
- foreach ($loop_scope->redefined_loop_vars as $var => $type) {
- $loop_scope->loop_context->vars_in_scope[$var] = $type;
- $updated_loop_vars[$var] = true;
- }
- }
-
- if ($loop_scope->possibly_redefined_loop_vars) {
- foreach ($loop_scope->possibly_redefined_loop_vars as $var => $type) {
- if ($loop_scope->loop_context->hasVariable($var)) {
- if (!isset($updated_loop_vars[$var])) {
- $loop_scope->loop_context->vars_in_scope[$var] = Type::combineUnionTypes(
- $loop_scope->loop_context->vars_in_scope[$var],
- $type
- );
- } else {
- $loop_scope->loop_context->vars_in_scope[$var]->parent_nodes
- += $type->parent_nodes;
- }
- }
- }
- }
- }
-
- // merge vars possibly in scope at the end of each loop
- $loop_scope->loop_context->vars_possibly_in_scope = array_merge(
- $loop_scope->loop_context->vars_possibly_in_scope,
- $loop_scope->vars_possibly_in_scope
- );
- }
-
- /**
- * @param list<Clause> $pre_condition_clauses
- *
- * @return list<string>
- */
- private static function applyPreConditionToLoopContext(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $pre_condition,
- array $pre_condition_clauses,
- Context $loop_context,
- Context $outer_context,
- bool $is_do
- ): array {
- $pre_referenced_var_ids = $loop_context->referenced_var_ids;
- $loop_context->referenced_var_ids = [];
-
- $was_inside_conditional = $loop_context->inside_conditional;
-
- $loop_context->inside_conditional = true;
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantCondition']);
- }
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
- if (!in_array('TypeDoesNotContainType', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['TypeDoesNotContainType']);
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $pre_condition, $loop_context) === false) {
- $loop_context->inside_conditional = $was_inside_conditional;
-
- return [];
- }
-
- $loop_context->inside_conditional = $was_inside_conditional;
-
- $new_referenced_var_ids = $loop_context->referenced_var_ids;
- $loop_context->referenced_var_ids = array_merge($pre_referenced_var_ids, $new_referenced_var_ids);
-
- $always_assigned_before_loop_body_vars = Context::getNewOrUpdatedVarIds($outer_context, $loop_context);
-
- $loop_context->clauses = Algebra::simplifyCNF(
- array_merge($outer_context->clauses, $pre_condition_clauses)
- );
-
- $active_while_types = [];
-
- $reconcilable_while_types = Algebra::getTruthsFromFormula(
- $loop_context->clauses,
- spl_object_id($pre_condition),
- $new_referenced_var_ids
- );
-
- $changed_var_ids = [];
-
- if ($reconcilable_while_types) {
- $pre_condition_vars_in_scope_reconciled = Reconciler::reconcileKeyedTypes(
- $reconcilable_while_types,
- $active_while_types,
- $loop_context->vars_in_scope,
- $changed_var_ids,
- $new_referenced_var_ids,
- $statements_analyzer,
- [],
- true,
- new CodeLocation($statements_analyzer->getSource(), $pre_condition)
- );
-
- $loop_context->vars_in_scope = $pre_condition_vars_in_scope_reconciled;
- }
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantCondition']);
- }
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
- if (!in_array('TypeDoesNotContainType', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['TypeDoesNotContainType']);
- }
-
- if ($is_do) {
- return [];
- }
-
- foreach ($always_assigned_before_loop_body_vars as $var_id) {
- $loop_context->clauses = Context::filterClauses(
- $var_id,
- $loop_context->clauses,
- null,
- $statements_analyzer
- );
- }
-
- return $always_assigned_before_loop_body_vars;
- }
-
- /**
- * @param array<string, array<string, bool>> $assignment_map
- *
- */
- private static function getAssignmentMapDepth(string $first_var_id, array $assignment_map): int
- {
- $max_depth = 0;
-
- $assignment_var_ids = $assignment_map[$first_var_id];
- unset($assignment_map[$first_var_id]);
-
- foreach ($assignment_var_ids as $assignment_var_id => $_) {
- $depth = 1;
-
- if (isset($assignment_map[$assignment_var_id])) {
- $depth = 1 + self::getAssignmentMapDepth($assignment_var_id, $assignment_map);
- }
-
- if ($depth > $max_depth) {
- $max_depth = $depth;
- }
- }
-
- return $max_depth;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php
deleted file mode 100644
index fd492eb..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchAnalyzer.php
+++ /dev/null
@@ -1,234 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Scope\SwitchScope;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-use SplFixedArray;
-
-use function array_merge;
-use function count;
-use function in_array;
-
-/**
- * @internal
- */
-class SwitchAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Switch_ $stmt,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- $was_inside_conditional = $context->inside_conditional;
-
- $context->inside_conditional = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->cond, $context) === false) {
- $context->inside_conditional = $was_inside_conditional;
-
- return;
- }
-
- $context->inside_conditional = $was_inside_conditional;
-
- $switch_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->cond,
- null,
- $statements_analyzer
- );
-
- if (!$switch_var_id
- && ($stmt->cond instanceof PhpParser\Node\Expr\FuncCall
- || $stmt->cond instanceof PhpParser\Node\Expr\MethodCall
- || $stmt->cond instanceof PhpParser\Node\Expr\StaticCall
- )
- ) {
- $switch_var_id = '$__tmp_switch__' . (int) $stmt->cond->getAttribute('startFilePos');
-
- $condition_type = $statements_analyzer->node_data->getType($stmt->cond) ?? Type::getMixed();
-
- $context->vars_in_scope[$switch_var_id] = $condition_type;
- }
-
- $original_context = clone $context;
-
- // the last statement always breaks, by default
- $last_case_exit_type = 'break';
-
- $case_exit_types = new SplFixedArray(count($stmt->cases));
-
- $has_default = false;
-
- $case_action_map = [];
-
- $config = Config::getInstance();
-
- // create a map of case statement -> ultimate exit type
- for ($i = count($stmt->cases) - 1; $i >= 0; --$i) {
- $case = $stmt->cases[$i];
-
- $case_actions = $case_action_map[$i] = ScopeAnalyzer::getControlActions(
- $case->stmts,
- $statements_analyzer->node_data,
- $config->exit_functions,
- ['switch']
- );
-
- if (!in_array(ScopeAnalyzer::ACTION_NONE, $case_actions, true)) {
- if ($case_actions === [ScopeAnalyzer::ACTION_END]) {
- $last_case_exit_type = 'return_throw';
- } elseif ($case_actions === [ScopeAnalyzer::ACTION_CONTINUE]) {
- $last_case_exit_type = 'continue';
- } elseif (in_array(ScopeAnalyzer::ACTION_LEAVE_SWITCH, $case_actions, true)) {
- $last_case_exit_type = 'break';
- }
- } elseif (count($case_actions) !== 1) {
- $last_case_exit_type = 'hybrid';
- }
-
- $case_exit_types[$i] = $last_case_exit_type;
- }
-
- $switch_scope = new SwitchScope();
-
- $was_caching_assertions = $statements_analyzer->node_data->cache_assertions;
-
- $statements_analyzer->node_data->cache_assertions = false;
-
- $all_options_returned = true;
-
- for ($i = 0, $l = count($stmt->cases); $i < $l; $i++) {
- $case = $stmt->cases[$i];
-
- /** @var string */
- $case_exit_type = $case_exit_types[$i];
- if ($case_exit_type !== 'return_throw') {
- $all_options_returned = false;
- }
-
- $case_actions = $case_action_map[$i];
-
- if (!$case->cond) {
- $has_default = true;
- }
-
- if (SwitchCaseAnalyzer::analyze(
- $statements_analyzer,
- $codebase,
- $stmt,
- $switch_var_id,
- $case,
- $context,
- $original_context,
- $case_exit_type,
- $case_actions,
- $i === $l - 1,
- $switch_scope
- ) === false
- ) {
- return;
- }
- }
-
- $all_options_matched = $has_default;
-
- if (!$has_default && $switch_scope->negated_clauses && $switch_var_id) {
- $entry_clauses = Algebra::simplifyCNF(
- array_merge(
- $original_context->clauses,
- $switch_scope->negated_clauses
- )
- );
-
- $reconcilable_if_types = Algebra::getTruthsFromFormula($entry_clauses);
-
- // if the if has an || in the conditional, we cannot easily reason about it
- if ($reconcilable_if_types && isset($reconcilable_if_types[$switch_var_id])) {
- $changed_var_ids = [];
-
- $case_vars_in_scope_reconciled =
- Reconciler::reconcileKeyedTypes(
- $reconcilable_if_types,
- [],
- $original_context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- [],
- $original_context->inside_loop
- );
-
- if (isset($case_vars_in_scope_reconciled[$switch_var_id])
- && $case_vars_in_scope_reconciled[$switch_var_id]->isEmpty()
- ) {
- $all_options_matched = true;
- }
- }
- }
-
- if ($was_caching_assertions) {
- $statements_analyzer->node_data->cache_assertions = true;
- }
-
- // only update vars if there is a default or all possible cases accounted for
- // if the default has a throw/return/continue, that should be handled above
- if ($all_options_matched) {
- if ($switch_scope->new_vars_in_scope) {
- $context->vars_in_scope = array_merge($context->vars_in_scope, $switch_scope->new_vars_in_scope);
- }
-
- if ($switch_scope->redefined_vars) {
- $context->vars_in_scope = array_merge($context->vars_in_scope, $switch_scope->redefined_vars);
- }
-
- if ($switch_scope->possibly_redefined_vars) {
- foreach ($switch_scope->possibly_redefined_vars as $var_id => $type) {
- if (!isset($switch_scope->redefined_vars[$var_id])
- && !isset($switch_scope->new_vars_in_scope[$var_id])
- && isset($context->vars_in_scope[$var_id])
- ) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $context->vars_in_scope[$var_id]
- );
- }
- }
- }
-
- $stmt->setAttribute('allMatched', true);
- } elseif ($switch_scope->possibly_redefined_vars) {
- foreach ($switch_scope->possibly_redefined_vars as $var_id => $type) {
- if (isset($context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $context->vars_in_scope[$var_id]
- );
- }
- }
- }
-
- if ($switch_scope->new_assigned_var_ids) {
- $context->assigned_var_ids += $switch_scope->new_assigned_var_ids;
- }
-
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $switch_scope->new_vars_possibly_in_scope
- );
-
- //a switch can't return in all options without a default
- $context->has_returned = $all_options_returned && $has_default;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php
deleted file mode 100644
index d8802a1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/SwitchCaseAnalyzer.php
+++ /dev/null
@@ -1,769 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\AlgebraAnalyzer;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\PhpVisitor\ConditionCloningVisitor;
-use Psalm\Internal\PhpVisitor\TypeMappingVisitor;
-use Psalm\Internal\Scope\CaseScope;
-use Psalm\Internal\Scope\SwitchScope;
-use Psalm\Issue\ContinueOutsideLoop;
-use Psalm\Issue\ParadoxicalCondition;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\BinaryOp\VirtualBooleanOr;
-use Psalm\Node\Expr\BinaryOp\VirtualEqual;
-use Psalm\Node\Expr\BinaryOp\VirtualIdentical;
-use Psalm\Node\Expr\VirtualArray;
-use Psalm\Node\Expr\VirtualArrayItem;
-use Psalm\Node\Expr\VirtualBooleanNot;
-use Psalm\Node\Expr\VirtualConstFetch;
-use Psalm\Node\Expr\VirtualFuncCall;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\Scalar\VirtualLNumber;
-use Psalm\Node\Stmt\VirtualIf;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualName;
-use Psalm\Type;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TDependentGetDebugType;
-use Psalm\Type\Atomic\TDependentGetType;
-use Psalm\Type\Reconciler;
-
-use function array_diff_key;
-use function array_intersect_key;
-use function array_merge;
-use function count;
-use function in_array;
-use function is_string;
-use function spl_object_id;
-use function strpos;
-use function substr;
-
-/**
- * @internal
- */
-class SwitchCaseAnalyzer
-{
- /**
- * @return null|false
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Stmt\Switch_ $stmt,
- ?string $switch_var_id,
- PhpParser\Node\Stmt\Case_ $case,
- Context $context,
- Context $original_context,
- string $case_exit_type,
- array $case_actions,
- bool $is_last,
- SwitchScope $switch_scope
- ): ?bool {
- // has a return/throw at end
- $has_ending_statements = $case_actions === [ScopeAnalyzer::ACTION_END];
- $has_leaving_statements = $has_ending_statements
- || (count($case_actions) && !in_array(ScopeAnalyzer::ACTION_NONE, $case_actions, true));
-
- $case_context = clone $original_context;
-
- if ($codebase->alter_code) {
- $case_context->branch_point = $case_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
-
- $case_context->parent_context = $context;
- $case_scope = $case_context->case_scope = new CaseScope($case_context);
-
- $case_equality_expr = null;
-
- $old_node_data = $statements_analyzer->node_data;
-
- $fake_switch_condition = false;
-
- if ($switch_var_id && strpos($switch_var_id, '$__tmp_switch__') === 0) {
- $switch_condition = new VirtualVariable(
- substr($switch_var_id, 1),
- $stmt->cond->getAttributes()
- );
-
- $fake_switch_condition = true;
- } else {
- $switch_condition = $stmt->cond;
- }
-
- if ($case->cond) {
- $was_inside_conditional = $case_context->inside_conditional;
- $case_context->inside_conditional = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $case->cond, $case_context) === false) {
- unset($case_scope->parent_context);
- unset($case_context->case_scope);
- unset($case_context->parent_context);
-
- return false;
- }
-
- $case_context->inside_conditional = $was_inside_conditional;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $traverser = new PhpParser\NodeTraverser;
- $traverser->addVisitor(
- new ConditionCloningVisitor(
- $statements_analyzer->node_data
- )
- );
-
- /** @var PhpParser\Node\Expr */
- $switch_condition = $traverser->traverse([$switch_condition])[0];
-
- if ($fake_switch_condition) {
- $statements_analyzer->node_data->setType(
- $switch_condition,
- $case_context->vars_in_scope[$switch_var_id] ?? Type::getMixed()
- );
- }
-
- if ($switch_condition instanceof PhpParser\Node\Expr\Variable
- && is_string($switch_condition->name)
- && isset($context->vars_in_scope['$' . $switch_condition->name])
- ) {
- $switch_var_type = $context->vars_in_scope['$' . $switch_condition->name];
-
- $type_statements = [];
-
- foreach ($switch_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TDependentGetClass) {
- $type_statements[] = new VirtualFuncCall(
- new VirtualName(['get_class']),
- [
- new VirtualArg(
- new VirtualVariable(
- substr($type->typeof, 1),
- $stmt->cond->getAttributes()
- ),
- false,
- false,
- $stmt->cond->getAttributes()
- ),
- ],
- $stmt->cond->getAttributes()
- );
- } elseif ($type instanceof TDependentGetType) {
- $type_statements[] = new VirtualFuncCall(
- new VirtualName(['gettype']),
- [
- new VirtualArg(
- new VirtualVariable(
- substr($type->typeof, 1),
- $stmt->cond->getAttributes()
- ),
- false,
- false,
- $stmt->cond->getAttributes()
- ),
- ],
- $stmt->cond->getAttributes()
- );
- } elseif ($type instanceof TDependentGetDebugType) {
- $type_statements[] = new VirtualFuncCall(
- new VirtualName(['get_debug_type']),
- [
- new VirtualArg(
- new VirtualVariable(
- substr($type->typeof, 1),
- $stmt->cond->getAttributes()
- ),
- false,
- false,
- $stmt->cond->getAttributes()
- ),
- ],
- $stmt->cond->getAttributes()
- );
- } else {
- $type_statements = null;
- break;
- }
- }
-
- if ($type_statements && count($type_statements) === 1) {
- $switch_condition = $type_statements[0];
-
- if ($fake_switch_condition) {
- $statements_analyzer->node_data->setType(
- $switch_condition,
- $case_context->vars_in_scope[$switch_var_id] ?? Type::getMixed()
- );
- }
- }
- }
-
- if ($switch_condition instanceof PhpParser\Node\Expr\ConstFetch
- && $switch_condition->name->parts === ['true']
- ) {
- $case_equality_expr = $case->cond;
- } elseif (($switch_condition_type = $statements_analyzer->node_data->getType($switch_condition))
- && ($case_cond_type = $statements_analyzer->node_data->getType($case->cond))
- && (($switch_condition_type->isString() && $case_cond_type->isString())
- || ($switch_condition_type->isInt() && $case_cond_type->isInt())
- || ($switch_condition_type->isFloat() && $case_cond_type->isFloat())
- )
- ) {
- $case_equality_expr = new VirtualIdentical(
- $switch_condition,
- $case->cond,
- $case->cond->getAttributes()
- );
- } else {
- $case_equality_expr = new VirtualEqual(
- $switch_condition,
- $case->cond,
- $case->cond->getAttributes()
- );
- }
- }
-
- $continue_case_equality_expr = false;
-
- if ($case->stmts) {
- $case_stmts = array_merge($switch_scope->leftover_statements, $case->stmts);
- } else {
- $continue_case_equality_expr = count($switch_scope->leftover_statements) === 1;
- $case_stmts = $switch_scope->leftover_statements;
- }
-
- if (!$has_leaving_statements && !$is_last) {
- if (!$case_equality_expr) {
- $case_equality_expr = new VirtualFuncCall(
- new VirtualFullyQualified(['rand']),
- [
- new VirtualArg(new VirtualLNumber(0)),
- new VirtualArg(new VirtualLNumber(1)),
- ],
- $case->getAttributes()
- );
- }
-
- $switch_scope->leftover_case_equality_expr = $switch_scope->leftover_case_equality_expr
- ? new VirtualBooleanOr(
- $switch_scope->leftover_case_equality_expr,
- $case_equality_expr,
- $case->cond ? $case->cond->getAttributes() : $case->getAttributes()
- )
- : $case_equality_expr;
-
- if ($continue_case_equality_expr
- && $switch_scope->leftover_statements[0] instanceof PhpParser\Node\Stmt\If_
- ) {
- $case_if_stmt = $switch_scope->leftover_statements[0];
- $case_if_stmt->cond = $switch_scope->leftover_case_equality_expr;
- } else {
- $case_if_stmt = new VirtualIf(
- $switch_scope->leftover_case_equality_expr,
- ['stmts' => $case_stmts],
- $case->getAttributes()
- );
-
- $switch_scope->leftover_statements = [$case_if_stmt];
- }
-
- unset($case_scope->parent_context);
- unset($case_context->case_scope);
- unset($case_context->parent_context);
-
- $statements_analyzer->node_data = $old_node_data;
-
- return null;
- }
-
- if ($switch_scope->leftover_case_equality_expr) {
- $case_or_default_equality_expr = $case_equality_expr;
-
- if (!$case_or_default_equality_expr) {
- $case_or_default_equality_expr = new VirtualFuncCall(
- new VirtualFullyQualified(['rand']),
- [
- new VirtualArg(new VirtualLNumber(0)),
- new VirtualArg(new VirtualLNumber(1)),
- ],
- $case->getAttributes()
- );
- }
-
- $case_equality_expr = new VirtualBooleanOr(
- $switch_scope->leftover_case_equality_expr,
- $case_or_default_equality_expr,
- $case_or_default_equality_expr->getAttributes()
- );
- }
-
- if ($case_equality_expr
- && $switch_condition instanceof PhpParser\Node\Expr\Variable
- && is_string($switch_condition->name)
- && isset($context->vars_in_scope['$' . $switch_condition->name])
- ) {
- $new_case_equality_expr = self::simplifyCaseEqualityExpression(
- $case_equality_expr,
- $switch_condition
- );
-
- if ($new_case_equality_expr) {
- $was_inside_conditional = $case_context->inside_conditional;
-
- $case_context->inside_conditional = true;
-
- ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $new_case_equality_expr->getArgs()[1]->value,
- $case_context
- );
-
- $case_context->inside_conditional = $was_inside_conditional;
-
- $case_equality_expr = $new_case_equality_expr;
- }
- }
-
- $case_context->break_types[] = 'switch';
-
- $switch_scope->leftover_statements = [];
- $switch_scope->leftover_case_equality_expr = null;
-
- $case_clauses = [];
-
- if ($case_equality_expr) {
- $case_equality_expr_id = spl_object_id($case_equality_expr);
- $case_clauses = FormulaGenerator::getFormula(
- $case_equality_expr_id,
- $case_equality_expr_id,
- $case_equality_expr,
- $context->self,
- $statements_analyzer,
- $codebase,
- false,
- false
- );
- }
-
- if ($switch_scope->negated_clauses && count($switch_scope->negated_clauses) < 50) {
- $entry_clauses = Algebra::simplifyCNF(
- array_merge(
- $original_context->clauses,
- $switch_scope->negated_clauses
- )
- );
- } else {
- $entry_clauses = $original_context->clauses;
- }
-
- if ($case_clauses && $case->cond) {
- // this will see whether any of the clauses in set A conflict with the clauses in set B
- AlgebraAnalyzer::checkForParadox(
- $entry_clauses,
- $case_clauses,
- $statements_analyzer,
- $case->cond,
- []
- );
-
- if (count($entry_clauses) + count($case_clauses) < 50) {
- $case_context->clauses = Algebra::simplifyCNF(array_merge($entry_clauses, $case_clauses));
- } else {
- $case_context->clauses = array_merge($entry_clauses, $case_clauses);
- }
- } else {
- $case_context->clauses = $entry_clauses;
- }
-
- $reconcilable_if_types = Algebra::getTruthsFromFormula($case_context->clauses);
-
- // if the if has an || in the conditional, we cannot easily reason about it
- if ($reconcilable_if_types) {
- $changed_var_ids = [];
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantCondition']);
- }
-
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
-
- $case_vars_in_scope_reconciled =
- Reconciler::reconcileKeyedTypes(
- $reconcilable_if_types,
- [],
- $case_context->vars_in_scope,
- $changed_var_ids,
- $case->cond && $switch_var_id ? [$switch_var_id => true] : [],
- $statements_analyzer,
- [],
- $case_context->inside_loop,
- new CodeLocation(
- $statements_analyzer->getSource(),
- $case->cond ?? $case,
- $context->include_location
- )
- );
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantCondition']);
- }
-
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
-
- $case_context->vars_in_scope = $case_vars_in_scope_reconciled;
- foreach ($reconcilable_if_types as $var_id => $_) {
- $case_context->vars_possibly_in_scope[$var_id] = true;
- }
-
- if ($changed_var_ids) {
- $case_context->clauses = Context::removeReconciledClauses($case_context->clauses, $changed_var_ids)[0];
- }
- }
-
- if ($case_clauses && $case_equality_expr) {
- try {
- $negated_case_clauses = Algebra::negateFormula($case_clauses);
- } catch (ComplicatedExpressionException $e) {
- $case_equality_expr_id = spl_object_id($case_equality_expr);
-
- try {
- $negated_case_clauses = FormulaGenerator::getFormula(
- $case_equality_expr_id,
- $case_equality_expr_id,
- new VirtualBooleanNot($case_equality_expr),
- $context->self,
- $statements_analyzer,
- $codebase,
- false,
- false
- );
- } catch (ComplicatedExpressionException $e) {
- $negated_case_clauses = [];
- }
- }
-
- $switch_scope->negated_clauses = array_merge(
- $switch_scope->negated_clauses,
- $negated_case_clauses
- );
- }
-
- $pre_possibly_assigned_var_ids = $case_context->possibly_assigned_var_ids;
- $case_context->possibly_assigned_var_ids = [];
-
- $pre_assigned_var_ids = $case_context->assigned_var_ids;
- $case_context->assigned_var_ids = [];
-
- $statements_analyzer->analyze($case_stmts, $case_context);
-
- $traverser = new PhpParser\NodeTraverser;
- $traverser->addVisitor(
- new TypeMappingVisitor(
- $statements_analyzer->node_data,
- $old_node_data
- )
- );
-
- $traverser->traverse([$case]);
-
- $statements_analyzer->node_data = $old_node_data;
-
- /** @var array<string, int> */
- $new_case_assigned_var_ids = $case_context->assigned_var_ids;
- $case_context->assigned_var_ids = $pre_assigned_var_ids + $new_case_assigned_var_ids;
-
- /** @var array<string, bool> */
- $new_case_possibly_assigned_var_ids = $case_context->possibly_assigned_var_ids;
- $case_context->possibly_assigned_var_ids =
- $pre_possibly_assigned_var_ids + $new_case_possibly_assigned_var_ids;
-
- $context->referenced_var_ids = array_merge(
- $context->referenced_var_ids,
- $case_context->referenced_var_ids
- );
-
- if ($case_exit_type !== 'return_throw') {
- if (self::handleNonReturningCase(
- $statements_analyzer,
- $switch_var_id,
- $case,
- $context,
- $case_context,
- $original_context,
- $case_exit_type,
- $switch_scope
- ) === false) {
- unset($case_scope->parent_context);
- unset($case_context->case_scope);
- unset($case_context->parent_context);
-
- return false;
- }
- }
-
- // augment the information with data from break statements
- if ($case_scope->break_vars !== null) {
- if ($switch_scope->possibly_redefined_vars === null) {
- $switch_scope->possibly_redefined_vars = array_intersect_key(
- $case_scope->break_vars,
- $context->vars_in_scope
- );
- } else {
- foreach ($case_scope->break_vars as $var_id => $type) {
- if (isset($context->vars_in_scope[$var_id])) {
- $switch_scope->possibly_redefined_vars[$var_id] = Type::combineUnionTypes(
- clone $type,
- $switch_scope->possibly_redefined_vars[$var_id] ?? null
- );
- }
- }
- }
-
- if ($switch_scope->new_vars_in_scope !== null) {
- foreach ($switch_scope->new_vars_in_scope as $var_id => $type) {
- if (isset($case_scope->break_vars[$var_id])) {
- if (!isset($case_context->vars_in_scope[$var_id])) {
- unset($switch_scope->new_vars_in_scope[$var_id]);
- } else {
- $switch_scope->new_vars_in_scope[$var_id] = Type::combineUnionTypes(
- clone $case_scope->break_vars[$var_id],
- $type
- );
- }
- } else {
- unset($switch_scope->new_vars_in_scope[$var_id]);
- }
- }
- }
-
- if ($switch_scope->redefined_vars !== null) {
- foreach ($switch_scope->redefined_vars as $var_id => $type) {
- if (isset($case_scope->break_vars[$var_id])) {
- $switch_scope->redefined_vars[$var_id] = Type::combineUnionTypes(
- clone $case_scope->break_vars[$var_id],
- $type
- );
- } else {
- unset($switch_scope->redefined_vars[$var_id]);
- }
- }
- }
- }
-
- unset($case_scope->parent_context);
- unset($case_context->case_scope);
- unset($case_context->parent_context);
-
- return null;
- }
-
- /**
- * @return null|false
- */
- private static function handleNonReturningCase(
- StatementsAnalyzer $statements_analyzer,
- ?string $switch_var_id,
- PhpParser\Node\Stmt\Case_ $case,
- Context $context,
- Context $case_context,
- Context $original_context,
- string $case_exit_type,
- SwitchScope $switch_scope
- ): ?bool {
- if (!$case->cond
- && $switch_var_id
- && isset($case_context->vars_in_scope[$switch_var_id])
- && $case_context->vars_in_scope[$switch_var_id]->isEmpty()
- ) {
- if (IssueBuffer::accepts(
- new ParadoxicalCondition(
- 'All possible case statements have been met, default is impossible here',
- new CodeLocation($statements_analyzer->getSource(), $case)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
-
- // if we're leaving this block, add vars to outer for loop scope
- if ($case_exit_type === 'continue') {
- if (!$context->loop_scope) {
- if (IssueBuffer::accepts(
- new ContinueOutsideLoop(
- 'Continue called when not in loop',
- new CodeLocation($statements_analyzer->getSource(), $case)
- )
- )) {
- return false;
- }
- }
- } else {
- $case_redefined_vars = $case_context->getRedefinedVars($original_context->vars_in_scope);
-
- if ($switch_scope->possibly_redefined_vars === null) {
- $switch_scope->possibly_redefined_vars = $case_redefined_vars;
- } else {
- foreach ($case_redefined_vars as $var_id => $type) {
- $switch_scope->possibly_redefined_vars[$var_id] = Type::combineUnionTypes(
- clone $type,
- $switch_scope->possibly_redefined_vars[$var_id] ?? null
- );
- }
- }
-
- if ($switch_scope->redefined_vars === null) {
- $switch_scope->redefined_vars = $case_redefined_vars;
- } else {
- foreach ($switch_scope->redefined_vars as $var_id => $type) {
- if (!isset($case_redefined_vars[$var_id])) {
- unset($switch_scope->redefined_vars[$var_id]);
- } else {
- $switch_scope->redefined_vars[$var_id] = Type::combineUnionTypes(
- $type,
- clone $case_redefined_vars[$var_id]
- );
- }
- }
- }
-
- $context_new_vars = array_diff_key($case_context->vars_in_scope, $context->vars_in_scope);
-
- if ($switch_scope->new_vars_in_scope === null) {
- $switch_scope->new_vars_in_scope = $context_new_vars;
- $switch_scope->new_vars_possibly_in_scope = array_diff_key(
- $case_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- );
- } else {
- foreach ($switch_scope->new_vars_in_scope as $new_var => $type) {
- if (!$case_context->hasVariable($new_var)) {
- unset($switch_scope->new_vars_in_scope[$new_var]);
- } else {
- $switch_scope->new_vars_in_scope[$new_var] =
- Type::combineUnionTypes(clone $case_context->vars_in_scope[$new_var], $type);
- }
- }
-
- $switch_scope->new_vars_possibly_in_scope = array_merge(
- array_diff_key(
- $case_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- ),
- $switch_scope->new_vars_possibly_in_scope
- );
- }
- }
-
- if ($context->collect_exceptions) {
- $context->mergeExceptions($case_context);
- }
-
- return null;
- }
-
- private static function simplifyCaseEqualityExpression(
- PhpParser\Node\Expr $case_equality_expr,
- PhpParser\Node\Expr\Variable $var
- ): ?PhpParser\Node\Expr\FuncCall {
- if ($case_equality_expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr) {
- $nested_or_options = self::getOptionsFromNestedOr($case_equality_expr, $var);
-
- if ($nested_or_options) {
- return new VirtualFuncCall(
- new VirtualFullyQualified(['in_array']),
- [
- new VirtualArg(
- $var,
- false,
- false,
- $var->getAttributes()
- ),
- new VirtualArg(
- new VirtualArray(
- $nested_or_options,
- $case_equality_expr->getAttributes()
- ),
- false,
- false,
- $case_equality_expr->getAttributes()
- ),
- new VirtualArg(
- new VirtualConstFetch(
- new VirtualFullyQualified(['true'])
- )
- ),
- ]
- );
- }
- }
-
- return null;
- }
-
- /**
- * @param array<PhpParser\Node\Expr\ArrayItem> $in_array_values
- * @return ?array<PhpParser\Node\Expr\ArrayItem>
- */
- private static function getOptionsFromNestedOr(
- PhpParser\Node\Expr $case_equality_expr,
- PhpParser\Node\Expr\Variable $var,
- array $in_array_values = []
- ): ?array {
- if ($case_equality_expr instanceof PhpParser\Node\Expr\BinaryOp\Identical
- && $case_equality_expr->left instanceof PhpParser\Node\Expr\Variable
- && $case_equality_expr->left->name === $var->name
- ) {
- $in_array_values[] = new VirtualArrayItem(
- $case_equality_expr->right,
- null,
- false,
- $case_equality_expr->right->getAttributes()
- );
-
- return $in_array_values;
- }
-
- if (!$case_equality_expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr) {
- return null;
- }
-
- if (!$case_equality_expr->right instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || !$case_equality_expr->right->left instanceof PhpParser\Node\Expr\Variable
- || $case_equality_expr->right->left->name !== $var->name
- ) {
- return null;
- }
-
- $in_array_values[] = new VirtualArrayItem(
- $case_equality_expr->right->right,
- null,
- false,
- $case_equality_expr->right->right->getAttributes()
- );
-
- return self::getOptionsFromNestedOr(
- $case_equality_expr->left,
- $var,
- $in_array_values
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php
deleted file mode 100644
index 5074bd6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/TryAnalyzer.php
+++ /dev/null
@@ -1,516 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\Scope\FinallyScope;
-use Psalm\Issue\InvalidCatch;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_intersect_key;
-use function array_map;
-use function array_merge;
-use function in_array;
-use function is_string;
-use function strtolower;
-use function version_compare;
-
-use const PHP_VERSION;
-
-/**
- * @internal
- */
-class TryAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\TryCatch $stmt,
- Context $context
- ): ?bool {
- $catch_actions = [];
- $all_catches_leave = true;
-
- $codebase = $statements_analyzer->getCodebase();
-
- /** @var int $i */
- foreach ($stmt->catches as $i => $catch) {
- $catch_actions[$i] = ScopeAnalyzer::getControlActions(
- $catch->stmts,
- $statements_analyzer->node_data,
- $codebase->config->exit_functions,
- []
- );
- $all_catches_leave = $all_catches_leave && !in_array(ScopeAnalyzer::ACTION_NONE, $catch_actions[$i], true);
- }
-
- $existing_thrown_exceptions = $context->possibly_thrown_exceptions;
-
- /**
- * @var array<string, array<array-key, CodeLocation>> $context->possibly_thrown_exceptions
- */
- $context->possibly_thrown_exceptions = [];
-
- $old_context = clone $context;
-
- if ($all_catches_leave && !$stmt->finally) {
- $try_context = $context;
- } else {
- $try_context = clone $context;
-
- if ($codebase->alter_code) {
- $try_context->branch_point = $try_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
-
- if ($stmt->finally) {
- $try_context->finally_scope = new FinallyScope($try_context->vars_in_scope);
- }
- }
-
- $assigned_var_ids = $try_context->assigned_var_ids;
- $context->assigned_var_ids = [];
-
- $old_referenced_var_ids = $try_context->referenced_var_ids;
-
- $was_inside_try = $context->inside_try;
- $context->inside_try = true;
- if ($statements_analyzer->analyze($stmt->stmts, $context) === false) {
- return false;
- }
- $context->inside_try = $was_inside_try;
-
- if ($try_context->finally_scope) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- $try_context->finally_scope->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $try_context->finally_scope->vars_in_scope[$var_id] ?? null,
- $type,
- $statements_analyzer->getCodebase()
- );
- }
- }
-
- $context->has_returned = false;
-
- $try_block_control_actions = ScopeAnalyzer::getControlActions(
- $stmt->stmts,
- $statements_analyzer->node_data,
- $codebase->config->exit_functions,
- []
- );
-
- /** @var array<string, int> */
- $newly_assigned_var_ids = $context->assigned_var_ids;
-
- $context->assigned_var_ids = array_merge(
- $assigned_var_ids,
- $newly_assigned_var_ids
- );
-
- $possibly_referenced_var_ids = array_merge(
- $context->referenced_var_ids,
- $old_referenced_var_ids
- );
-
- if ($try_context !== $context) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (!isset($try_context->vars_in_scope[$var_id])) {
- $try_context->vars_in_scope[$var_id] = clone $type;
-
- $context->vars_in_scope[$var_id]->possibly_undefined = true;
- $context->vars_in_scope[$var_id]->possibly_undefined_from_try = true;
- } else {
- $try_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $try_context->vars_in_scope[$var_id],
- $type
- );
- }
- }
-
- $try_context->vars_possibly_in_scope = $context->vars_possibly_in_scope;
- $try_context->possibly_thrown_exceptions = $context->possibly_thrown_exceptions;
-
- $context->referenced_var_ids = array_intersect_key(
- $try_context->referenced_var_ids,
- $context->referenced_var_ids
- );
- }
-
- $try_leaves_loop = $context->loop_scope
- && $context->loop_scope->final_actions
- && !in_array(ScopeAnalyzer::ACTION_NONE, $context->loop_scope->final_actions, true);
-
- if (!$all_catches_leave) {
- foreach ($newly_assigned_var_ids as $assigned_var_id => $_) {
- $context->removeVarFromConflictingClauses($assigned_var_id);
- }
- } else {
- foreach ($newly_assigned_var_ids as $assigned_var_id => $_) {
- $try_context->removeVarFromConflictingClauses($assigned_var_id);
- }
- }
-
- // at this point we have two contexts – $context, in which it is assumed that everything was fine,
- // and $try_context - which allows all variables to have the union of the values before and after
- // the try was applied
- $original_context = clone $try_context;
-
- $issues_to_suppress = [
- 'RedundantCondition',
- 'RedundantConditionGivenDocblockType',
- 'TypeDoesNotContainNull',
- 'TypeDoesNotContainType',
- ];
-
- $definitely_newly_assigned_var_ids = $newly_assigned_var_ids;
-
- /** @var int $i */
- foreach ($stmt->catches as $i => $catch) {
- $catch_context = clone $original_context;
- $catch_context->has_returned = false;
-
- foreach ($catch_context->vars_in_scope as $var_id => $type) {
- if (!isset($old_context->vars_in_scope[$var_id])) {
- $type = clone $type;
- $type->possibly_undefined_from_try = true;
- $catch_context->vars_in_scope[$var_id] = $type;
- } else {
- $catch_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $old_context->vars_in_scope[$var_id]
- );
- }
- }
-
- $fq_catch_classes = [];
-
- if (!$catch->types) {
- throw new UnexpectedValueException('Very bad');
- }
-
- foreach ($catch->types as $catch_type) {
- $fq_catch_class = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $catch_type,
- $statements_analyzer->getAliases()
- );
-
- $fq_catch_class = $codebase->classlikes->getUnAliasedName($fq_catch_class);
-
- if ($codebase->alter_code && $fq_catch_class) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $catch_type,
- $fq_catch_class,
- $context->calling_method_id
- );
- }
-
- if ($original_context->check_classes) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_catch_class,
- new CodeLocation($statements_analyzer->getSource(), $catch_type, $context->include_location),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false) {
- // fall through
- }
- }
-
- if (($codebase->classExists($fq_catch_class)
- && strtolower($fq_catch_class) !== 'exception'
- && !($codebase->classExtends($fq_catch_class, 'Exception')
- || $codebase->classImplements($fq_catch_class, 'Throwable')))
- || ($codebase->interfaceExists($fq_catch_class)
- && strtolower($fq_catch_class) !== 'throwable'
- && !$codebase->interfaceExtends($fq_catch_class, 'Throwable'))
- ) {
- IssueBuffer::maybeAdd(
- new InvalidCatch(
- 'Class/interface ' . $fq_catch_class . ' cannot be caught',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_catch_class
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $fq_catch_classes[] = $fq_catch_class;
- }
-
- if ($catch_context->collect_exceptions) {
- foreach ($fq_catch_classes as $fq_catch_class) {
- $fq_catch_class_lower = strtolower($fq_catch_class);
-
- foreach ($catch_context->possibly_thrown_exceptions as $exception_fqcln => $_) {
- $exception_fqcln_lower = strtolower($exception_fqcln);
-
- if ($exception_fqcln_lower === $fq_catch_class_lower
- || ($codebase->classExists($exception_fqcln)
- && $codebase->classExtendsOrImplements($exception_fqcln, $fq_catch_class))
- || ($codebase->interfaceExists($exception_fqcln)
- && $codebase->interfaceExtends($exception_fqcln, $fq_catch_class))
- ) {
- unset($original_context->possibly_thrown_exceptions[$exception_fqcln]);
- unset($context->possibly_thrown_exceptions[$exception_fqcln]);
- unset($catch_context->possibly_thrown_exceptions[$exception_fqcln]);
- }
- }
- }
-
- $catch_context->possibly_thrown_exceptions = [];
- }
-
- // discard all clauses because crazy stuff may have happened in try block
- $catch_context->clauses = [];
-
- if ($catch->var && is_string($catch->var->name)) {
- $catch_var_id = '$' . $catch->var->name;
-
- $catch_context->vars_in_scope[$catch_var_id] = new Union(
- array_map(
- function (string $fq_catch_class) use ($codebase): TNamedObject {
- $catch_class_type = new TNamedObject($fq_catch_class);
-
- if (version_compare(PHP_VERSION, '7.0.0dev', '>=')
- && strtolower($fq_catch_class) !== 'throwable'
- && $codebase->interfaceExists($fq_catch_class)
- && !$codebase->interfaceExtends($fq_catch_class, 'Throwable')
- ) {
- $catch_class_type->addIntersectionType(new TNamedObject('Throwable'));
- }
-
- return $catch_class_type;
- },
- $fq_catch_classes
- )
- );
-
- // removes dependent vars from $context
- $catch_context->removeDescendents(
- $catch_var_id,
- null,
- $catch_context->vars_in_scope[$catch_var_id],
- $statements_analyzer
- );
-
- $catch_context->vars_possibly_in_scope[$catch_var_id] = true;
-
- $location = new CodeLocation($statements_analyzer->getSource(), $catch->var);
-
- if (!$statements_analyzer->hasVariable($catch_var_id)) {
- $statements_analyzer->registerVariable(
- $catch_var_id,
- $location,
- $catch_context->branch_point
- );
- } else {
- $statements_analyzer->registerVariableAssignment(
- $catch_var_id,
- $location
- );
- }
-
- if ($statements_analyzer->data_flow_graph) {
- $catch_var_node = DataFlowNode::getForAssignment($catch_var_id, $location);
-
- $catch_context->vars_in_scope[$catch_var_id]->parent_nodes = [
- $catch_var_node->id => $catch_var_node
- ];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- $statements_analyzer->data_flow_graph->addPath(
- $catch_var_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
- }
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- foreach ($issues_to_suppress as $issue_to_suppress) {
- if (!in_array($issue_to_suppress, $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues([$issue_to_suppress]);
- }
- }
-
- $old_catch_assigned_var_ids = $catch_context->referenced_var_ids;
-
- $catch_context->assigned_var_ids = [];
-
- $statements_analyzer->analyze($catch->stmts, $catch_context);
-
- // recalculate in case there's a no-return clause
- $catch_actions[$i] = ScopeAnalyzer::getControlActions(
- $catch->stmts,
- $statements_analyzer->node_data,
- $codebase->config->exit_functions,
- []
- );
-
- foreach ($issues_to_suppress as $issue_to_suppress) {
- if (!in_array($issue_to_suppress, $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues([$issue_to_suppress]);
- }
- }
-
- /** @var array<string, bool> */
- $new_catch_assigned_var_ids = $catch_context->assigned_var_ids;
-
- $catch_context->assigned_var_ids += $old_catch_assigned_var_ids;
-
- $context->referenced_var_ids = array_intersect_key(
- $catch_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- $possibly_referenced_var_ids = array_merge(
- $catch_context->referenced_var_ids,
- $possibly_referenced_var_ids
- );
-
- if ($catch_context->collect_exceptions) {
- $context->mergeExceptions($catch_context);
- }
-
- $catch_doesnt_leave_parent_scope = $catch_actions[$i] !== [ScopeAnalyzer::ACTION_END]
- && $catch_actions[$i] !== [ScopeAnalyzer::ACTION_CONTINUE]
- && $catch_actions[$i] !== [ScopeAnalyzer::ACTION_BREAK];
-
- if ($catch_doesnt_leave_parent_scope) {
- $definitely_newly_assigned_var_ids = array_intersect_key(
- $new_catch_assigned_var_ids,
- $definitely_newly_assigned_var_ids
- );
-
- foreach ($catch_context->vars_in_scope as $var_id => $type) {
- if ($try_block_control_actions === [ScopeAnalyzer::ACTION_END]) {
- $context->vars_in_scope[$var_id] = $type;
- } elseif (isset($context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->vars_in_scope[$var_id],
- $type
- );
- }
- }
-
- $context->vars_possibly_in_scope = array_merge(
- $catch_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- );
- } else {
- if ($stmt->finally) {
- $context->vars_possibly_in_scope = array_merge(
- $catch_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- );
- }
- }
-
- if ($try_context->finally_scope) {
- foreach ($catch_context->vars_in_scope as $var_id => $type) {
- if (isset($try_context->finally_scope->vars_in_scope[$var_id])) {
- if ($try_context->finally_scope->vars_in_scope[$var_id] !== $type) {
- $try_context->finally_scope->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $try_context->finally_scope->vars_in_scope[$var_id],
- $type,
- $statements_analyzer->getCodebase()
- );
- }
- } else {
- $try_context->finally_scope->vars_in_scope[$var_id] = $type;
- $type->possibly_undefined = true;
- $type->possibly_undefined_from_try = true;
- }
- }
- }
- }
-
- if ($context->loop_scope
- && !$try_leaves_loop
- && !in_array(ScopeAnalyzer::ACTION_NONE, $context->loop_scope->final_actions, true)
- ) {
- $context->loop_scope->final_actions[] = ScopeAnalyzer::ACTION_NONE;
- }
-
- $finally_has_returned = false;
- if ($stmt->finally) {
- if ($try_context->finally_scope) {
- $finally_context = clone $context;
-
- $finally_context->assigned_var_ids = [];
- $finally_context->possibly_assigned_var_ids = [];
-
- $finally_context->vars_in_scope = $try_context->finally_scope->vars_in_scope;
-
- $statements_analyzer->analyze($stmt->finally->stmts, $finally_context);
-
- $finally_has_returned = $finally_context->has_returned;
-
- /** @var string $var_id */
- foreach ($finally_context->assigned_var_ids as $var_id => $_) {
- if (isset($context->vars_in_scope[$var_id])
- && isset($finally_context->vars_in_scope[$var_id])
- ) {
- if ($context->vars_in_scope[$var_id]->possibly_undefined
- && $context->vars_in_scope[$var_id]->possibly_undefined_from_try
- ) {
- $context->vars_in_scope[$var_id]->possibly_undefined = false;
- $context->vars_in_scope[$var_id]->possibly_undefined_from_try = false;
- }
-
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->vars_in_scope[$var_id],
- $finally_context->vars_in_scope[$var_id],
- $codebase
- );
- } elseif (isset($finally_context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = clone $finally_context->vars_in_scope[$var_id];
- }
- }
- }
- }
-
- foreach ($definitely_newly_assigned_var_ids as $var_id => $_) {
- if (isset($context->vars_in_scope[$var_id])) {
- $new_type = clone $context->vars_in_scope[$var_id];
-
- if ($new_type->possibly_undefined_from_try) {
- $new_type->possibly_undefined = false;
- $new_type->possibly_undefined_from_try = false;
- }
-
- $context->vars_in_scope[$var_id] = $new_type;
- }
- }
-
- foreach ($existing_thrown_exceptions as $possibly_thrown_exception => $codelocations) {
- foreach ($codelocations as $hash => $codelocation) {
- $context->possibly_thrown_exceptions[$possibly_thrown_exception][$hash] = $codelocation;
- }
- }
-
- $body_has_returned = !in_array(ScopeAnalyzer::ACTION_NONE, $try_block_control_actions, true);
- $context->has_returned = ($body_has_returned && $all_catches_leave) || $finally_has_returned;
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/WhileAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/WhileAnalyzer.php
deleted file mode 100644
index a6ef17d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/WhileAnalyzer.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Block;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Scope\LoopScope;
-use Psalm\Type;
-use UnexpectedValueException;
-
-use function array_intersect_key;
-use function array_merge;
-use function in_array;
-
-/**
- * @internal
- */
-class WhileAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\While_ $stmt,
- Context $context
- ): ?bool {
- $while_true = ($stmt->cond instanceof PhpParser\Node\Expr\ConstFetch && $stmt->cond->name->parts === ['true'])
- || ($stmt->cond instanceof PhpParser\Node\Scalar\LNumber && $stmt->cond->value > 0);
-
- $pre_context = null;
-
- if ($while_true) {
- $pre_context = clone $context;
- }
-
- $while_context = clone $context;
-
- $while_context->inside_loop = true;
- $while_context->break_types[] = 'loop';
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($codebase->alter_code) {
- $while_context->branch_point = $while_context->branch_point ?: (int) $stmt->getAttribute('startFilePos');
- }
-
- $loop_scope = new LoopScope($while_context, $context);
- $loop_scope->protected_var_ids = $context->protected_var_ids;
-
- if (LoopAnalyzer::analyze(
- $statements_analyzer,
- $stmt->stmts,
- self::getAndExpressions($stmt->cond),
- [],
- $loop_scope,
- $inner_loop_context
- ) === false) {
- return false;
- }
-
- if (!$inner_loop_context) {
- throw new UnexpectedValueException('There should be an inner loop context');
- }
-
- $always_enters_loop = false;
-
- if ($stmt_cond_type = $statements_analyzer->node_data->getType($stmt->cond)) {
- $always_enters_loop = $stmt_cond_type->isAlwaysTruthy();
- }
-
- if ($while_true) {
- $always_enters_loop = true;
- }
-
- $can_leave_loop = !$while_true
- || in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true);
-
- if ($always_enters_loop && $can_leave_loop) {
- foreach ($inner_loop_context->vars_in_scope as $var_id => $type) {
- // if there are break statements in the loop it's not certain
- // that the loop has finished executing, so the assertions at the end
- // the loop in the while conditional may not hold
- if (in_array(ScopeAnalyzer::ACTION_BREAK, $loop_scope->final_actions, true)
- || in_array(ScopeAnalyzer::ACTION_CONTINUE, $loop_scope->final_actions, true)
- ) {
- if (isset($loop_scope->possibly_defined_loop_parent_vars[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_defined_loop_parent_vars[$var_id]
- );
- }
- } else {
- $context->vars_in_scope[$var_id] = $type;
- }
- }
- }
-
- $while_context->loop_scope = null;
-
- if ($can_leave_loop) {
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $while_context->vars_possibly_in_scope
- );
- } elseif ($pre_context) {
- $context->vars_possibly_in_scope = $pre_context->vars_possibly_in_scope;
- }
-
- $context->referenced_var_ids = array_intersect_key(
- $while_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- if ($context->collect_exceptions) {
- $context->mergeExceptions($while_context);
- }
-
- return null;
- }
-
- /**
- * @return list<PhpParser\Node\Expr>
- */
- public static function getAndExpressions(
- PhpParser\Node\Expr $expr
- ): array {
- if ($expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd) {
- return array_merge(
- self::getAndExpressions($expr->left),
- self::getAndExpressions($expr->right)
- );
- }
-
- return [$expr];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php
deleted file mode 100644
index 2a98856..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/BreakAnalyzer.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-
-use function end;
-
-class BreakAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Break_ $stmt,
- Context $context
- ): void {
- $loop_scope = $context->loop_scope;
-
- $leaving_switch = true;
-
- if ($loop_scope) {
- if ($context->break_types
- && end($context->break_types) === 'switch'
- && (!$stmt->num instanceof PhpParser\Node\Scalar\LNumber || $stmt->num->value < 2)
- ) {
- $loop_scope->final_actions[] = ScopeAnalyzer::ACTION_LEAVE_SWITCH;
- } else {
- $leaving_switch = false;
-
- $loop_scope->final_actions[] = ScopeAnalyzer::ACTION_BREAK;
- }
-
- $redefined_vars = $context->getRedefinedVars($loop_scope->loop_parent_context->vars_in_scope);
-
- if ($loop_scope->possibly_redefined_loop_parent_vars === null) {
- $loop_scope->possibly_redefined_loop_parent_vars = $redefined_vars;
- } else {
- foreach ($redefined_vars as $var => $type) {
- $loop_scope->possibly_redefined_loop_parent_vars[$var] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_redefined_loop_parent_vars[$var] ?? null
- );
- }
- }
-
- if ($loop_scope->iteration_count === 0) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (!isset($loop_scope->loop_parent_context->vars_in_scope[$var_id])) {
- $loop_scope->possibly_defined_loop_parent_vars[$var_id] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_defined_loop_parent_vars[$var_id] ?? null
- );
- }
- }
- }
-
- if ($context->finally_scope) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (isset($context->finally_scope->vars_in_scope[$var_id])) {
- $context->finally_scope->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->finally_scope->vars_in_scope[$var_id],
- $type,
- $statements_analyzer->getCodebase()
- );
- } else {
- $context->finally_scope->vars_in_scope[$var_id] = $type;
- $type->possibly_undefined = true;
- $type->possibly_undefined_from_try = true;
- }
- }
- }
- }
-
- $case_scope = $context->case_scope;
- if ($case_scope && $leaving_switch) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if ($case_scope->parent_context !== $context) {
- if ($case_scope->break_vars === null) {
- $case_scope->break_vars = [];
- }
-
- $case_scope->break_vars[$var_id] = Type::combineUnionTypes(
- $type,
- $case_scope->break_vars[$var_id] ?? null
- );
- }
- }
- }
-
- $context->has_returned = true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php
deleted file mode 100644
index 17826f1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ContinueAnalyzer.php
+++ /dev/null
@@ -1,100 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Issue\ContinueOutsideLoop;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-
-use function end;
-
-class ContinueAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Continue_ $stmt,
- Context $context
- ): void {
- $count = $stmt->num instanceof PhpParser\Node\Scalar\LNumber? $stmt->num->value : 1;
-
- $loop_scope = $context->loop_scope;
-
- if ($count === 2 && isset($loop_scope->loop_parent_context->loop_scope)) {
- $loop_scope = $loop_scope->loop_parent_context->loop_scope;
- }
-
- if ($count === 3 && isset($loop_scope->loop_parent_context->loop_scope)) {
- $loop_scope = $loop_scope->loop_parent_context->loop_scope;
- }
-
- if ($loop_scope === null) {
- if (!$context->break_types) {
- if (IssueBuffer::accepts(
- new ContinueOutsideLoop(
- 'Continue call outside loop context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSource()->getSuppressedIssues()
- )) {
- return;
- }
- }
- } else {
- if ($context->break_types
- && end($context->break_types) === 'switch'
- && $count < 2
- ) {
- $loop_scope->final_actions[] = ScopeAnalyzer::ACTION_LEAVE_SWITCH;
- } else {
- $loop_scope->final_actions[] = ScopeAnalyzer::ACTION_CONTINUE;
- }
-
- $redefined_vars = $context->getRedefinedVars($loop_scope->loop_parent_context->vars_in_scope);
-
- if ($loop_scope->redefined_loop_vars === null) {
- $loop_scope->redefined_loop_vars = $redefined_vars;
- } else {
- foreach ($loop_scope->redefined_loop_vars as $redefined_var => $type) {
- if (!isset($redefined_vars[$redefined_var])) {
- unset($loop_scope->redefined_loop_vars[$redefined_var]);
- } else {
- $loop_scope->redefined_loop_vars[$redefined_var] = Type::combineUnionTypes(
- $redefined_vars[$redefined_var],
- $type
- );
- }
- }
- }
-
- foreach ($redefined_vars as $var => $type) {
- $loop_scope->possibly_redefined_loop_vars[$var] = Type::combineUnionTypes(
- $type,
- $loop_scope->possibly_redefined_loop_vars[$var] ?? null
- );
- }
-
- if ($context->finally_scope) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (isset($context->finally_scope->vars_in_scope[$var_id])) {
- $context->finally_scope->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->finally_scope->vars_in_scope[$var_id],
- $type,
- $statements_analyzer->getCodebase()
- );
- } else {
- $context->finally_scope->vars_in_scope[$var_id] = $type;
- $type->possibly_undefined = true;
- $type->possibly_undefined_from_try = true;
- }
- }
- }
- }
-
- $context->has_returned = true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php
deleted file mode 100644
index 8b9be56..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/EchoAnalyzer.php
+++ /dev/null
@@ -1,135 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CastAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\Issue\ForbiddenEcho;
-use Psalm\Issue\ImpureFunctionCall;
-use Psalm\IssueBuffer;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\TaintKind;
-
-class EchoAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Echo_ $stmt,
- Context $context
- ): bool {
- $echo_param = new FunctionLikeParameter(
- 'var',
- false
- );
-
- $codebase = $statements_analyzer->getCodebase();
-
- foreach ($stmt->exprs as $i => $expr) {
- $context->inside_call = true;
- ExpressionAnalyzer::analyze($statements_analyzer, $expr, $context);
- $context->inside_call = false;
-
- $expr_type = $statements_analyzer->node_data->getType($expr);
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- if ($expr_type && $expr_type->hasObjectType()) {
- $expr_type = CastAnalyzer::castStringAttempt(
- $statements_analyzer,
- $context,
- $expr_type,
- $expr,
- false
- );
- }
-
- $call_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $echo_param_sink = TaintSink::getForMethodArgument(
- 'echo',
- 'echo',
- (int) $i,
- null,
- $call_location
- );
-
- $echo_param_sink->taints = [
- TaintKind::INPUT_HTML,
- TaintKind::INPUT_HAS_QUOTES,
- TaintKind::USER_SECRET,
- TaintKind::SYSTEM_SECRET
- ];
-
- $statements_analyzer->data_flow_graph->addSink($echo_param_sink);
- }
-
- if (ArgumentAnalyzer::verifyType(
- $statements_analyzer,
- $expr_type ?? Type::getMixed(),
- Type::getString(),
- null,
- 'echo',
- null,
- (int)$i,
- new CodeLocation($statements_analyzer->getSource(), $expr),
- $expr,
- $context,
- $echo_param,
- false,
- null,
- true,
- true,
- new CodeLocation($statements_analyzer, $stmt)
- ) === false) {
- return false;
- }
- }
-
- if ($codebase->config->forbid_echo) {
- if (IssueBuffer::accepts(
- new ForbiddenEcho(
- 'Use of echo',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSource()->getSuppressedIssues()
- )) {
- return false;
- }
- } elseif (isset($codebase->config->forbidden_functions['echo'])) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'Use of echo',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSource()->getSuppressedIssues()
- );
- }
-
- if (!$context->collect_initializations && !$context->collect_mutations) {
- if ($context->mutation_free || $context->external_mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureFunctionCall(
- 'Cannot call echo from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php
deleted file mode 100644
index 0a92b1a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php
+++ /dev/null
@@ -1,607 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Issue\DuplicateArrayKey;
-use Psalm\Issue\InvalidArrayOffset;
-use Psalm\Issue\MixedArrayOffset;
-use Psalm\Issue\ParseError;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function array_values;
-use function count;
-use function in_array;
-use function is_string;
-use function preg_match;
-
-use const PHP_INT_MAX;
-
-/**
- * @internal
- */
-class ArrayAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Array_ $stmt,
- Context $context
- ): bool {
- // if the array is empty, this special type allows us to match any other array type against it
- if (empty($stmt->items)) {
- $statements_analyzer->node_data->setType($stmt, Type::getEmptyArray());
-
- return true;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $array_creation_info = new ArrayCreationInfo();
-
- foreach ($stmt->items as $item) {
- if ($item === null) {
- IssueBuffer::maybeAdd(
- new ParseError(
- 'Array element cannot be empty',
- new CodeLocation($statements_analyzer, $stmt)
- )
- );
-
- return false;
- }
-
- self::analyzeArrayItem(
- $statements_analyzer,
- $context,
- $array_creation_info,
- $item,
- $codebase
- );
- }
-
- if ($array_creation_info->item_key_atomic_types) {
- $item_key_type = TypeCombiner::combine(
- $array_creation_info->item_key_atomic_types,
- $codebase,
- false,
- true,
- 30
- );
- } else {
- $item_key_type = null;
- }
-
- if ($array_creation_info->item_value_atomic_types) {
- $item_value_type = TypeCombiner::combine(
- $array_creation_info->item_value_atomic_types,
- $codebase,
- false,
- true,
- 30
- );
- } else {
- $item_value_type = null;
- }
-
- // if this array looks like an object-like array, let's return that instead
- if ($item_value_type
- && $item_key_type
- && ($item_key_type->hasString() || $item_key_type->hasInt())
- && $array_creation_info->can_create_objectlike
- && $array_creation_info->property_types
- ) {
- $object_like = new TKeyedArray(
- $array_creation_info->property_types,
- $array_creation_info->class_strings
- );
- $object_like->sealed = true;
- $object_like->is_list = $array_creation_info->all_list;
-
- $stmt_type = new Union([$object_like]);
-
- if ($array_creation_info->parent_taint_nodes) {
- $stmt_type->parent_nodes = $array_creation_info->parent_taint_nodes;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return true;
- }
-
- if ($array_creation_info->all_list) {
- if (empty($array_creation_info->item_key_atomic_types)) {
- $array_type = new TList($item_value_type ?? Type::getMixed());
- } else {
- $array_type = new TNonEmptyList($item_value_type ?? Type::getMixed());
- $array_type->count = count($array_creation_info->property_types);
- }
-
- $stmt_type = new Union([
- $array_type,
- ]);
-
- if ($array_creation_info->parent_taint_nodes) {
- $stmt_type->parent_nodes = $array_creation_info->parent_taint_nodes;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return true;
- }
-
- if ($item_key_type) {
- $bad_types = [];
- $good_types = [];
-
- foreach ($item_key_type->getAtomicTypes() as $atomic_key_type) {
- if ($atomic_key_type instanceof TMixed) {
- IssueBuffer::maybeAdd(
- new MixedArrayOffset(
- 'Cannot create mixed offset – expecting array-key',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $bad_types[] = $atomic_key_type;
-
- $good_types[] = new TArrayKey;
-
-
- continue;
- }
-
- if (!$atomic_key_type instanceof TString
- && !$atomic_key_type instanceof TInt
- && !$atomic_key_type instanceof TArrayKey
- && !$atomic_key_type instanceof TTemplateParam
- && !(
- $atomic_key_type instanceof TObjectWithProperties
- && isset($atomic_key_type->methods['__toString'])
- )
- ) {
- IssueBuffer::maybeAdd(
- new InvalidArrayOffset(
- 'Cannot create offset of type ' . $item_key_type->getKey() . ', expecting array-key',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $bad_types[] = $atomic_key_type;
-
- if ($atomic_key_type instanceof TFalse) {
- $good_types[] = new TLiteralInt(0);
- } elseif ($atomic_key_type instanceof TTrue) {
- $good_types[] = new TLiteralInt(1);
- } elseif ($atomic_key_type instanceof TBool) {
- $good_types[] = new TLiteralInt(0);
- $good_types[] = new TLiteralInt(1);
- } elseif ($atomic_key_type instanceof TLiteralFloat) {
- $good_types[] = new TLiteralInt((int) $atomic_key_type->value);
- } elseif ($atomic_key_type instanceof TFloat) {
- $good_types[] = new TInt;
- } else {
- $good_types[] = new TArrayKey;
- }
- }
- }
-
- if ($bad_types && $good_types) {
- $item_key_type->substitute(
- TypeCombiner::combine($bad_types, $codebase),
- TypeCombiner::combine($good_types, $codebase)
- );
- }
- }
-
- $array_type = new TNonEmptyArray([
- $item_key_type && !$item_key_type->hasMixed() ? $item_key_type : Type::getArrayKey(),
- $item_value_type ?? Type::getMixed(),
- ]);
-
- $array_type->count = count($array_creation_info->property_types);
-
- $stmt_type = new Union([
- $array_type,
- ]);
-
- if ($array_creation_info->parent_taint_nodes) {
- $stmt_type->parent_nodes = $array_creation_info->parent_taint_nodes;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return true;
- }
-
- private static function analyzeArrayItem(
- StatementsAnalyzer $statements_analyzer,
- Context $context,
- ArrayCreationInfo $array_creation_info,
- PhpParser\Node\Expr\ArrayItem $item,
- Codebase $codebase
- ): void {
- if ($item->unpack) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $item->value, $context) === false) {
- return;
- }
-
- $unpacked_array_type = $statements_analyzer->node_data->getType($item->value);
-
- if (!$unpacked_array_type) {
- return;
- }
-
- self::handleUnpackedArray(
- $statements_analyzer,
- $array_creation_info,
- $item,
- $unpacked_array_type,
- $codebase
- );
-
- if (($data_flow_graph = $statements_analyzer->data_flow_graph)
- && $data_flow_graph instanceof VariableUseGraph
- && $unpacked_array_type->parent_nodes
- ) {
- $var_location = new CodeLocation($statements_analyzer->getSource(), $item->value);
-
- $new_parent_node = DataFlowNode::getForAssignment(
- 'array',
- $var_location
- );
-
- $data_flow_graph->addNode($new_parent_node);
-
- foreach ($unpacked_array_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'arrayvalue-assignment'
- );
- }
-
- $array_creation_info->parent_taint_nodes += [$new_parent_node->id => $new_parent_node];
- }
-
- return;
- }
-
- $item_key_value = null;
- $item_key_type = null;
- $item_is_list_item = false;
-
- if ($item->key) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
- if (ExpressionAnalyzer::analyze($statements_analyzer, $item->key, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return;
- }
- $context->inside_general_use = $was_inside_general_use;
-
- if ($item_key_type = $statements_analyzer->node_data->getType($item->key)) {
- $key_type = $item_key_type;
-
- if ($key_type->isNull()) {
- $key_type = Type::getString('');
- }
-
- if ($item->key instanceof PhpParser\Node\Scalar\String_
- && preg_match('/^(0|[1-9][0-9]*)$/', $item->key->value)
- && (
- (int) $item->key->value < PHP_INT_MAX ||
- $item->key->value === (string) PHP_INT_MAX
- )
- ) {
- $key_type = Type::getInt(false, (int) $item->key->value);
- }
-
- $array_creation_info->item_key_atomic_types = array_merge(
- $array_creation_info->item_key_atomic_types,
- array_values($key_type->getAtomicTypes())
- );
-
- if ($key_type->isSingleStringLiteral()) {
- $item_key_literal_type = $key_type->getSingleStringLiteral();
- $item_key_value = $item_key_literal_type->value;
-
- if ($item_key_literal_type instanceof TLiteralClassString) {
- $array_creation_info->class_strings[$item_key_value] = true;
- }
- } elseif ($key_type->isSingleIntLiteral()) {
- $item_key_value = $key_type->getSingleIntLiteral()->value;
-
- if ($item_key_value >= $array_creation_info->int_offset) {
- if ($item_key_value === $array_creation_info->int_offset) {
- $item_is_list_item = true;
- }
- $array_creation_info->int_offset = $item_key_value + 1;
- }
- }
- }
- } else {
- $item_is_list_item = true;
- $item_key_value = $array_creation_info->int_offset++;
- $array_creation_info->item_key_atomic_types[] = new TLiteralInt($item_key_value);
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $item->value, $context) === false) {
- return;
- }
-
- $array_creation_info->all_list = $array_creation_info->all_list && $item_is_list_item;
-
- if ($item_key_value !== null) {
- if (isset($array_creation_info->array_keys[$item_key_value])) {
- IssueBuffer::maybeAdd(
- new DuplicateArrayKey(
- 'Key \'' . $item_key_value . '\' already exists on array',
- new CodeLocation($statements_analyzer->getSource(), $item)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $array_creation_info->array_keys[$item_key_value] = true;
- }
-
-
- if (($data_flow_graph = $statements_analyzer->data_flow_graph)
- && ($data_flow_graph instanceof VariableUseGraph
- || !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues()))
- ) {
- if ($item_value_type = $statements_analyzer->node_data->getType($item->value)) {
- if ($item_value_type->parent_nodes
- && !($item_value_type->isSingle()
- && $item_value_type->hasLiteralValue()
- && $data_flow_graph instanceof TaintFlowGraph)
- ) {
- $var_location = new CodeLocation($statements_analyzer->getSource(), $item);
-
- $new_parent_node = DataFlowNode::getForAssignment(
- 'array'
- . ($item_key_value !== null ? '[\'' . $item_key_value . '\']' : ''),
- $var_location
- );
-
- $data_flow_graph->addNode($new_parent_node);
-
- $event = new AddRemoveTaintsEvent($item, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- foreach ($item_value_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'arrayvalue-assignment'
- . ($item_key_value !== null ? '-\'' . $item_key_value . '\'' : ''),
- $added_taints,
- $removed_taints
- );
- }
-
- $array_creation_info->parent_taint_nodes += [$new_parent_node->id => $new_parent_node];
- }
-
- if ($item_key_type
- && $item_key_type->parent_nodes
- && $item_key_value === null
- && !($item_key_type->isSingle()
- && $item_key_type->hasLiteralValue()
- && $data_flow_graph instanceof TaintFlowGraph)
- ) {
- $var_location = new CodeLocation($statements_analyzer->getSource(), $item);
-
- $new_parent_node = DataFlowNode::getForAssignment(
- 'array',
- $var_location
- );
-
- $data_flow_graph->addNode($new_parent_node);
-
- $event = new AddRemoveTaintsEvent($item, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- foreach ($item_key_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'arraykey-assignment',
- $added_taints,
- $removed_taints
- );
- }
-
- $array_creation_info->parent_taint_nodes += [$new_parent_node->id => $new_parent_node];
- }
- }
- }
-
- if ($item->byRef) {
- $var_id = ExpressionIdentifier::getArrayVarId(
- $item->value,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id) {
- $context->removeDescendents(
- $var_id,
- $context->vars_in_scope[$var_id] ?? null,
- null,
- $statements_analyzer
- );
-
- $context->vars_in_scope[$var_id] = Type::getMixed();
- }
- }
-
- $config = $codebase->config;
-
- if ($item_value_type = $statements_analyzer->node_data->getType($item->value)) {
- if ($item_key_value !== null
- && count($array_creation_info->property_types) <= $config->max_shaped_array_size
- ) {
- $array_creation_info->property_types[$item_key_value] = $item_value_type;
- } else {
- $array_creation_info->can_create_objectlike = false;
- }
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values($item_value_type->getAtomicTypes())
- );
- } else {
- $array_creation_info->item_value_atomic_types[] = new TMixed();
-
- if ($item_key_value !== null
- && count($array_creation_info->property_types) <= $config->max_shaped_array_size
- ) {
- $array_creation_info->property_types[$item_key_value] = Type::getMixed();
- } else {
- $array_creation_info->can_create_objectlike = false;
- }
- }
- }
-
- private static function handleUnpackedArray(
- StatementsAnalyzer $statements_analyzer,
- ArrayCreationInfo $array_creation_info,
- PhpParser\Node\Expr\ArrayItem $item,
- Union $unpacked_array_type,
- Codebase $codebase
- ): void {
- foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) {
- if ($unpacked_atomic_type instanceof TKeyedArray) {
- foreach ($unpacked_atomic_type->properties as $key => $property_value) {
- if (is_string($key)) {
- if ($codebase->php_major_version < 8 ||
- ($codebase->php_major_version === 8 && $codebase->php_minor_version < 1)
- ) {
- IssueBuffer::maybeAdd(
- new DuplicateArrayKey(
- 'String keys are not supported in unpacked arrays',
- new CodeLocation($statements_analyzer->getSource(), $item->value)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
- $new_offset = $key;
- $array_creation_info->item_key_atomic_types[] = new TLiteralString($new_offset);
- } else {
- $new_offset = $array_creation_info->int_offset++;
- $array_creation_info->item_key_atomic_types[] = new TLiteralInt($new_offset);
- }
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values($property_value->getAtomicTypes())
- );
-
- $array_creation_info->array_keys[$new_offset] = true;
- $array_creation_info->property_types[$new_offset] = $property_value;
- }
- } else {
- $codebase = $statements_analyzer->getCodebase();
-
- if ($unpacked_atomic_type instanceof TArray
- || $unpacked_atomic_type instanceof TIterable
- || (
- $unpacked_atomic_type instanceof TGenericObject
- && $unpacked_atomic_type->hasTraversableInterface($codebase)
- && count($unpacked_atomic_type->type_params) === 2
- )) {
- /** @psalm-suppress PossiblyUndefinedArrayOffset provably true, but Psalm can’t see it */
- if ($unpacked_atomic_type->type_params[1]->isEmpty()) {
- continue;
- }
- $array_creation_info->can_create_objectlike = false;
-
- if ($unpacked_atomic_type->type_params[0]->hasString()) {
- if ($codebase->php_major_version < 8 ||
- ($codebase->php_major_version === 8 && $codebase->php_minor_version < 1)
- ) {
- IssueBuffer::maybeAdd(
- new DuplicateArrayKey(
- 'String keys are not supported in unpacked arrays',
- new CodeLocation($statements_analyzer->getSource(), $item->value)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- $array_creation_info->item_key_atomic_types[] = new TString();
- } elseif ($unpacked_atomic_type->type_params[0]->hasInt()) {
- $array_creation_info->item_key_atomic_types[] = new TInt();
- }
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values(
- isset($unpacked_atomic_type->type_params[1])
- ? $unpacked_atomic_type->type_params[1]->getAtomicTypes()
- : [new TMixed()]
- )
- );
- } elseif ($unpacked_atomic_type instanceof TList) {
- if ($unpacked_atomic_type->type_param->isEmpty()) {
- continue;
- }
- $array_creation_info->can_create_objectlike = false;
-
- $array_creation_info->item_key_atomic_types[] = new TInt();
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values($unpacked_atomic_type->type_param->getAtomicTypes())
- );
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php
deleted file mode 100644
index ca0e761..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Type\Atomic;
-use Psalm\Type\Union;
-
-class ArrayCreationInfo
-{
- /**
- * @var list<Atomic>
- */
- public $item_key_atomic_types = [];
-
- /**
- * @var list<Atomic>
- */
- public $item_value_atomic_types = [];
-
- /**
- * @var array<int|string, Union>
- */
- public $property_types = [];
-
- /**
- * @var array<string, true>
- */
- public $class_strings = [];
-
- /**
- * @var bool
- */
- public $can_create_objectlike = true;
-
- /**
- * @var array<int|string, true>
- */
- public $array_keys = [];
-
- /**
- * @var int
- */
- public $int_offset = 0;
-
- /**
- * @var bool
- */
- public $all_list = true;
-
- /**
- * @var array<string, DataFlowNode>
- */
- public $parent_taint_nodes = [];
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php
deleted file mode 100644
index 0fa53ea..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssertionFinder.php
+++ /dev/null
@@ -1,4122 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use PhpParser\Node\Expr\BinaryOp;
-use PhpParser\Node\Expr\BinaryOp\Equal;
-use PhpParser\Node\Expr\BinaryOp\Greater;
-use PhpParser\Node\Expr\BinaryOp\GreaterOrEqual;
-use PhpParser\Node\Expr\BinaryOp\Identical;
-use PhpParser\Node\Expr\BinaryOp\NotEqual;
-use PhpParser\Node\Expr\BinaryOp\NotIdentical;
-use PhpParser\Node\Expr\BinaryOp\Smaller;
-use PhpParser\Node\Expr\BinaryOp\SmallerOrEqual;
-use PhpParser\Node\Expr\UnaryMinus;
-use PhpParser\Node\Expr\UnaryPlus;
-use PhpParser\Node\Scalar\LNumber;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\FileSource;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\DocblockTypeContradiction;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\RedundantCondition;
-use Psalm\Issue\RedundantConditionGivenDocblockType;
-use Psalm\Issue\RedundantIdentityWithTrue;
-use Psalm\Issue\TypeDoesNotContainNull;
-use Psalm\Issue\TypeDoesNotContainType;
-use Psalm\Issue\UnevaluatedCode;
-use Psalm\IssueBuffer;
-use Psalm\Storage\PropertyStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_key_exists;
-use function assert;
-use function count;
-use function explode;
-use function in_array;
-use function is_callable;
-use function is_int;
-use function is_numeric;
-use function is_string;
-use function json_encode;
-use function sprintf;
-use function str_replace;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- * This class transform conditions in code into "assertions" that will be reconciled with the type already known of a
- * given variable to narrow the type or find paradox.
- * For example if $a is an int, if($a > 0) will be turned into an assertion to make psalm understand that in the
- * if block, $a is a positive-int
- */
-class AssertionFinder
-{
- public const ASSIGNMENT_TO_RIGHT = 1;
- public const ASSIGNMENT_TO_LEFT = -1;
-
- public const IS_TYPE_CHECKS = [
- 'is_string' => ['string', [Type::class, 'getString']],
- 'is_int' => ['int', [Type::class, 'getInt']],
- 'is_integer' => ['int', [Type::class, 'getInt']],
- 'is_long' => ['int', [Type::class, 'getInt']],
- 'is_bool' => ['bool', [Type::class, 'getBool']],
- 'is_resource' => ['resource', [Type::class, 'getResource']],
- 'is_object' => ['object', [Type::class, 'getObject']],
- 'array_is_list' => ['list', [Type::class, 'getList']],
- 'is_array' => ['array', [Type::class, 'getArray']],
- 'is_numeric' => ['numeric', [Type::class, 'getNumeric']],
- 'is_null' => ['null', [Type::class, 'getNull']],
- 'is_float' => ['float', [Type::class, 'getFloat']],
- 'is_real' => ['float', [Type::class, 'getFloat']],
- 'is_double' => ['float', [Type::class, 'getFloat']],
- 'is_scalar' => ['scalar', [Type::class, 'getScalar']],
- 'is_iterable' => ['iterable'],
- 'is_countable' => ['countable'],
- ];
-
- /**
- * Gets all the type assertions in a conditional
- *
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- public static function scrapeAssertions(
- PhpParser\Node\Expr $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase = null,
- bool $inside_negation = false,
- bool $cache = true,
- bool $inside_conditional = true
- ): array {
- $if_types = [];
-
- if ($conditional instanceof PhpParser\Node\Expr\Instanceof_) {
- return self::getInstanceofAssertions(
- $conditional,
- $codebase,
- $source,
- $this_class_name,
- $inside_negation
- );
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\Assign) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->var,
- $this_class_name,
- $source
- );
-
- $candidate_if_types = $inside_conditional
- ? self::scrapeAssertions(
- $conditional->expr,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation,
- $cache,
- $inside_conditional
- )
- : [];
-
- if ($var_name) {
- if ($candidate_if_types) {
- $if_types[$var_name] = [['@' . json_encode($candidate_if_types[0])]];
- } else {
- $if_types[$var_name] = [['!falsy']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- $if_types[$var_name] = [['!falsy']];
-
- if (!$conditional instanceof PhpParser\Node\Expr\MethodCall
- && !$conditional instanceof PhpParser\Node\Expr\StaticCall
- ) {
- return [$if_types];
- }
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BooleanNot) {
- return [];
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical ||
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\Equal
- ) {
- return self::scrapeEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $cache,
- $inside_conditional
- );
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical ||
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- ) {
- return self::scrapeInequalityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $cache,
- $inside_conditional
- );
- }
-
- //A nullsafe method call basically adds an assertion !null for the checked variable
- if ($conditional instanceof PhpParser\Node\Expr\NullsafeMethodCall) {
- $if_types = [];
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->var,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- $if_types[$var_name] = [['!null']];
- }
-
- //we may throw a RedundantNullsafeMethodCall here in the future if $var_name is never null
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater
- || $conditional instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
- ) {
- return self::getGreaterAssertions(
- $conditional,
- $source,
- $this_class_name
- );
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller
- || $conditional instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
- ) {
- return self::getSmallerAssertions(
- $conditional,
- $source,
- $this_class_name
- );
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\FuncCall && !$conditional->isFirstClassCallable()) {
- return self::processFunctionCall(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $inside_negation
- );
- }
-
- if (($conditional instanceof PhpParser\Node\Expr\MethodCall
- || $conditional instanceof PhpParser\Node\Expr\StaticCall)
- && !$conditional->isFirstClassCallable()
- ) {
- $custom_assertions = self::processCustomAssertion($conditional, $this_class_name, $source);
-
- if ($custom_assertions) {
- return $custom_assertions;
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\Empty_) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->expr,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional->expr instanceof PhpParser\Node\Expr\Variable
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($conditional->expr))
- && !$var_type->isMixed()
- && !$var_type->possibly_undefined
- ) {
- $if_types[$var_name] = [['falsy']];
- } else {
- $if_types[$var_name] = [['empty']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\Isset_) {
- foreach ($conditional->vars as $isset_var) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $isset_var,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($isset_var instanceof PhpParser\Node\Expr\Variable
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($isset_var))
- && !$var_type->isMixed()
- && !$var_type->possibly_undefined
- && !$var_type->possibly_undefined_from_try
- ) {
- $if_types[$var_name] = [['!null']];
- } else {
- $if_types[$var_name] = [['isset']];
- }
- } else {
- // look for any variables we *can* use for an isset assertion
- $array_root = $isset_var;
-
- while ($array_root instanceof PhpParser\Node\Expr\ArrayDimFetch && !$var_name) {
- $array_root = $array_root->var;
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $array_root,
- $this_class_name,
- $source
- );
- }
-
- if ($var_name) {
- $if_types[$var_name] = [['=isset']];
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- return [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function scrapeEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase = null,
- bool $cache = true,
- bool $inside_conditional = true
- ): array {
- $null_position = self::hasNullVariable($conditional, $source);
-
- if ($null_position !== null) {
- return self::getNullEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $null_position
- );
- }
-
- $true_position = self::hasTrueVariable($conditional);
-
- if ($true_position) {
- return self::getTrueEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $cache,
- $true_position
- );
- }
-
- $false_position = self::hasFalseVariable($conditional);
-
- if ($false_position) {
- return self::getFalseEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $cache,
- $inside_conditional,
- $false_position
- );
- }
-
- $empty_array_position = self::hasEmptyArrayVariable($conditional);
-
- if ($empty_array_position !== null) {
- return self::getEmptyArrayEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $empty_array_position
- );
- }
-
- $gettype_position = self::hasGetTypeCheck($conditional);
-
- if ($gettype_position) {
- return self::getGettypeEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $gettype_position
- );
- }
-
- $get_debug_type_position = self::hasGetDebugTypeCheck($conditional);
-
- if ($get_debug_type_position) {
- return self::getGetdebugtypeEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $get_debug_type_position
- );
- }
-
- if (!$source instanceof StatementsAnalyzer) {
- return [];
- }
-
- $count = null;
- $count_equality_position = self::hasCountEqualityCheck($conditional, $count);
-
- if ($count_equality_position) {
- $if_types = [];
-
- if ($count_equality_position === self::ASSIGNMENT_TO_RIGHT) {
- $count_expr = $conditional->left;
- } elseif ($count_equality_position === self::ASSIGNMENT_TO_LEFT) {
- $count_expr = $conditional->right;
- } else {
- throw new UnexpectedValueException('$count_equality_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $count_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $count_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- $var_type = $source->node_data->getType($conditional->left);
- $other_type = $source->node_data->getType($conditional->right);
-
- if ($codebase
- && $other_type
- && $var_type
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- self::handleParadoxicalAssertions(
- $source,
- $var_type,
- $this_class_name,
- $other_type,
- $codebase,
- $conditional
- );
- }
-
- if ($var_name) {
- if ($count !== 0) {
- $if_types[$var_name] = [['=has-exactly-' . $count]];
- } else {
- $if_types[$var_name] = [['!non-empty-countable']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- $getclass_position = self::hasGetClassCheck($conditional, $source);
-
- if ($getclass_position) {
- return self::getGetclassEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $getclass_position
- );
- }
-
- $typed_value_position = self::hasTypedValueComparison($conditional, $source);
-
- if ($typed_value_position) {
- return self::getTypedValueEqualityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $typed_value_position
- );
- }
-
- $var_type = $source->node_data->getType($conditional->left);
- $other_type = $source->node_data->getType($conditional->right);
-
- if ($codebase
- && $var_type
- && $other_type
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- if (!UnionTypeComparator::canExpressionTypesBeIdentical($codebase, $var_type, $other_type)) {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- $var_type->getId() . ' cannot be identical to ' . $other_type->getId(),
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' ' . $other_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- } else {
- // both side of the Identical can be asserted to the intersection of both
- $intersection_type = Type::intersectUnionTypes($var_type, $other_type, $codebase);
-
- if ($intersection_type !== null && $intersection_type->isSingle()) {
- try {
- $assertion = $intersection_type->getAssertionString();
- } catch (UnexpectedValueException $e) {
- // getAssertionString can't work if the Union has more than one type
- return [];
- }
- $if_types = [];
-
- $var_name_left = ExpressionIdentifier::getArrayVarId(
- $conditional->left,
- $this_class_name,
- $source
- );
-
- try {
- $var_assertion_different = $var_type->getAssertionString() !== $assertion;
- } catch (UnexpectedValueException $e) {
- // if getAssertionString threw, it's different
- $var_assertion_different = true;
- }
-
- if ($var_name_left && $var_assertion_different) {
- $if_types[$var_name_left] = [['='.$assertion]];
- }
-
- $var_name_right = ExpressionIdentifier::getArrayVarId(
- $conditional->right,
- $this_class_name,
- $source
- );
-
-
- try {
- $other_assertion_different = $other_type->getAssertionString() !== $assertion;
- } catch (UnexpectedValueException $e) {
- // if getAssertionString threw, it's different
- $other_assertion_different = true;
- }
-
- if ($var_name_right && $other_assertion_different) {
- $if_types[$var_name_right] = [['='.$assertion]];
- }
-
- return $if_types ? [$if_types] : [];
- }
- }
- }
-
- return [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function scrapeInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase = null,
- bool $cache = true,
- bool $inside_conditional = true
- ): array {
- $null_position = self::hasNullVariable($conditional, $source);
-
- if ($null_position !== null) {
- return self::getNullInequalityAssertions(
- $conditional,
- $source,
- $this_class_name,
- $codebase,
- $null_position
- );
- }
-
- $false_position = self::hasFalseVariable($conditional);
-
- if ($false_position) {
- return self::getFalseInequalityAssertions(
- $conditional,
- $cache,
- $this_class_name,
- $source,
- $inside_conditional,
- $codebase,
- $false_position
- );
- }
-
- $true_position = self::hasTrueVariable($conditional);
-
- if ($true_position) {
- return self::getTrueInequalityAssertions(
- $true_position,
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $cache,
- $inside_conditional
- );
- }
-
- $count = null;
- $count_inequality_position = self::hasCountEqualityCheck($conditional, $count);
-
- if ($count_inequality_position) {
- $if_types = [];
-
- if ($count_inequality_position === self::ASSIGNMENT_TO_RIGHT) {
- $count_expr = $conditional->left;
- } elseif ($count_inequality_position === self::ASSIGNMENT_TO_LEFT) {
- $count_expr = $conditional->right;
- } else {
- throw new UnexpectedValueException('$count_equality_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $count_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $count_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($count) {
- $if_types[$var_name] = [['!has-exactly-' . $count]];
- } else {
- $if_types[$var_name] = [['non-empty-countable']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- $empty_array_position = self::hasEmptyArrayVariable($conditional);
-
- if ($empty_array_position !== null) {
- return self::getEmptyInequalityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $empty_array_position
- );
- }
-
- $gettype_position = self::hasGetTypeCheck($conditional);
-
- if ($gettype_position) {
- return self::getGettypeInequalityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $gettype_position
- );
- }
-
- $get_debug_type_position = self::hasGetDebugTypeCheck($conditional);
-
- if ($get_debug_type_position) {
- return self::getGetdebugTypeInequalityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $get_debug_type_position
- );
- }
-
- if (!$source instanceof StatementsAnalyzer) {
- return [];
- }
-
- $getclass_position = self::hasGetClassCheck($conditional, $source);
-
- if ($getclass_position) {
- return self::getGetclassInequalityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $getclass_position
- );
- }
-
- $typed_value_position = self::hasTypedValueComparison($conditional, $source);
-
- if ($typed_value_position) {
- return self::getTypedValueInequalityAssertions(
- $conditional,
- $this_class_name,
- $source,
- $codebase,
- $typed_value_position
- );
- }
-
- return [];
- }
-
- /**
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- public static function processFunctionCall(
- PhpParser\Node\Expr\FuncCall $expr,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase = null,
- bool $negate = false
- ): array {
- $first_var_name = isset($expr->getArgs()[0]->value)
- ? ExpressionIdentifier::getArrayVarId(
- $expr->getArgs()[0]->value,
- $this_class_name,
- $source
- )
- : null;
-
- $if_types = [];
-
- $first_var_type = isset($expr->getArgs()[0]->value)
- && $source instanceof StatementsAnalyzer
- ? $source->node_data->getType($expr->getArgs()[0]->value)
- : null;
-
- if ($tmp_if_types = self::handleIsTypeCheck(
- $codebase,
- $source,
- $expr,
- $first_var_name,
- $first_var_type,
- $expr,
- $negate
- )) {
- $if_types = $tmp_if_types;
- } elseif ($source instanceof StatementsAnalyzer && self::hasIsACheck($expr, $source)) {
- return self::getIsaAssertions($expr, $source, $this_class_name, $first_var_name);
- } elseif (self::hasCallableCheck($expr)) {
- if ($first_var_name) {
- $if_types[$first_var_name] = [['callable']];
- } elseif ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\Array_
- && isset($expr->getArgs()[0]->value->items[0], $expr->getArgs()[0]->value->items[1])
- && $expr->getArgs()[0]->value->items[1]->value instanceof PhpParser\Node\Scalar\String_
- ) {
- $first_var_name_in_array_argument = ExpressionIdentifier::getArrayVarId(
- $expr->getArgs()[0]->value->items[0]->value,
- $this_class_name,
- $source
- );
- if ($first_var_name_in_array_argument) {
- $if_types[$first_var_name_in_array_argument] = [
- ['hasmethod-' . $expr->getArgs()[0]->value->items[1]->value->value]
- ];
- }
- }
- } elseif ($class_exists_check_type = self::hasClassExistsCheck($expr)) {
- if ($first_var_name) {
- $class_string_type = ($class_exists_check_type === 1 ? 'loaded-' : '') . 'class-string';
- $if_types[$first_var_name] = [[$class_string_type]];
- }
- } elseif ($class_exists_check_type = self::hasTraitExistsCheck($expr)) {
- if ($first_var_name) {
- if ($class_exists_check_type === 2) {
- $if_types[$first_var_name] = [['trait-string']];
- } else {
- $if_types[$first_var_name] = [['=trait-string']];
- }
- }
- } elseif (self::hasInterfaceExistsCheck($expr)) {
- if ($first_var_name) {
- $if_types[$first_var_name] = [['interface-string']];
- }
- } elseif (self::hasFunctionExistsCheck($expr)) {
- if ($first_var_name) {
- $if_types[$first_var_name] = [['callable-string']];
- }
- } elseif ($expr->name instanceof PhpParser\Node\Name
- && strtolower($expr->name->parts[0]) === 'method_exists'
- && isset($expr->getArgs()[1])
- && $expr->getArgs()[1]->value instanceof PhpParser\Node\Scalar\String_
- ) {
- if ($first_var_name) {
- $if_types[$first_var_name] = [['hasmethod-' . $expr->getArgs()[1]->value->value]];
- }
- } elseif (self::hasInArrayCheck($expr) && $source instanceof StatementsAnalyzer) {
- return self::getInarrayAssertions($expr, $source, $first_var_name);
- } elseif (self::hasArrayKeyExistsCheck($expr)) {
- return self::getArrayKeyExistsAssertions(
- $expr,
- $first_var_type,
- $first_var_name,
- $source,
- $this_class_name
- );
- } elseif (self::hasNonEmptyCountCheck($expr)) {
- if ($first_var_name) {
- $if_types[$first_var_name] = [['non-empty-countable']];
- }
- } else {
- return self::processCustomAssertion($expr, $this_class_name, $source);
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- private static function processIrreconcilableFunctionCall(
- Union $first_var_type,
- Union $expected_type,
- PhpParser\Node\Expr $expr,
- StatementsAnalyzer $source,
- Codebase $codebase,
- bool $negate
- ): void {
- if ($first_var_type->hasMixed()) {
- return;
- }
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $first_var_type,
- $expected_type
- )) {
- return;
- }
-
- if (!$negate) {
- if ($first_var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- 'Docblock type ' . $first_var_type . ' always contains ' . $expected_type,
- new CodeLocation($source, $expr),
- $first_var_type . ' ' . $expected_type
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $first_var_type . ' always contains ' . $expected_type,
- new CodeLocation($source, $expr),
- $first_var_type . ' ' . $expected_type
- ),
- $source->getSuppressedIssues()
- );
- }
- } else {
- if ($first_var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- 'Docblock type !' . $first_var_type . ' does not contain ' . $expected_type,
- new CodeLocation($source, $expr),
- $first_var_type . ' ' . $expected_type
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- '!' . $first_var_type . ' does not contain ' . $expected_type,
- new CodeLocation($source, $expr),
- $first_var_type . ' ' . $expected_type
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
-
- /**
- * @param PhpParser\Node\Expr\FuncCall|PhpParser\Node\Expr\MethodCall|PhpParser\Node\Expr\StaticCall $expr
- *
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- protected static function processCustomAssertion(
- PhpParser\Node\Expr $expr,
- ?string $this_class_name,
- FileSource $source
- ): array {
- if (!$source instanceof StatementsAnalyzer) {
- return [];
- }
-
- $if_true_assertions = $source->node_data->getIfTrueAssertions($expr);
- $if_false_assertions = $source->node_data->getIfFalseAssertions($expr);
-
- if ($if_true_assertions === null && $if_false_assertions === null) {
- return [];
- }
-
- $first_var_name = isset($expr->getArgs()[0]->value)
- ? ExpressionIdentifier::getArrayVarId(
- $expr->getArgs()[0]->value,
- $this_class_name,
- $source
- )
- : null;
-
- $anded_types = [];
-
- if ($if_true_assertions) {
- foreach ($if_true_assertions as $assertion) {
- $if_types = [];
-
- $assertion = clone $assertion;
-
- foreach ($assertion->rule as $i => $and_rules) {
- foreach ($and_rules as $j => $rule) {
- if (strpos($rule, 'class-constant(') === 0) {
- $codebase = $source->getCodebase();
- try {
- $assertion->rule[$i][$j] = TypeExpander::expandUnion(
- $codebase,
- Type::parseString(substr($rule, 15, -1)),
- null,
- null,
- null
- )->getAssertionString();
- } catch (UnexpectedValueException $e) {
- continue;
- }
- }
- }
- }
-
- if (is_int($assertion->var_id) && isset($expr->getArgs()[$assertion->var_id])) {
- if ($assertion->var_id === 0) {
- $var_name = $first_var_name;
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $expr->getArgs()[$assertion->var_id]->value,
- $this_class_name,
- $source
- );
- }
-
- if ($var_name) {
- $if_types[$var_name] = [[$assertion->rule[0][0]]];
- }
- } elseif ($assertion->var_id === '$this') {
- if (!$expr instanceof PhpParser\Node\Expr\MethodCall) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Assertion of $this can be done only on method of a class',
- new CodeLocation($source, $expr)
- )
- );
- continue;
- }
-
- $var_id = ExpressionIdentifier::getArrayVarId(
- $expr->var,
- $this_class_name,
- $source
- );
-
- if ($var_id) {
- $if_types[$var_id] = [[$assertion->rule[0][0]]];
- }
- } elseif (is_string($assertion->var_id)) {
- $is_function = substr($assertion->var_id, -2) === '()';
- $exploded_id = explode('->', $assertion->var_id);
- $var_id = $exploded_id[0] ?? null;
- $property = $exploded_id[1] ?? null;
-
- if (is_numeric($var_id) && null !== $property && !$is_function) {
- $args = $expr->getArgs();
-
- if (!array_key_exists($var_id, $args)) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Variable '.$var_id.' is not an argument so cannot be asserted',
- new CodeLocation($source, $expr)
- )
- );
- continue;
- }
-
- $arg_value = $args[$var_id]->value;
- assert($arg_value instanceof PhpParser\Node\Expr\Variable);
-
- $arg_var_id = ExpressionIdentifier::getArrayVarId($arg_value, null, $source);
-
- if (null === $arg_var_id) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Variable being asserted as argument ' . ($var_id+1) . ' cannot be found
- in local scope',
- new CodeLocation($source, $expr)
- )
- );
- continue;
- }
-
- if (count($exploded_id) === 2) {
- $failedMessage = self::isPropertyImmutableOnArgument(
- $property,
- $source->getNodeTypeProvider(),
- $source->getCodebase()->classlike_storage_provider,
- $arg_value
- );
-
- if (null !== $failedMessage) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock($failedMessage, new CodeLocation($source, $expr))
- );
- continue;
- }
- }
-
- $assertion_var_id = str_replace($var_id, $arg_var_id, $assertion->var_id);
- } elseif (!$expr instanceof PhpParser\Node\Expr\FuncCall) {
- $assertion_var_id = $assertion->var_id;
-
- if (strpos($assertion_var_id, 'self::') === 0) {
- $assertion_var_id = $this_class_name.'::'.substr($assertion_var_id, 6);
- }
- } else {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- sprintf('Assertion of variable "%s" cannot be recognized', $assertion->var_id),
- new CodeLocation($source, $expr)
- )
- );
- continue;
- }
- $if_types[$assertion_var_id] = [[$assertion->rule[0][0]]];
- }
-
- if ($if_types) {
- $anded_types[] = $if_types;
- }
- }
- }
-
- if ($if_false_assertions) {
- foreach ($if_false_assertions as $assertion) {
- $if_types = [];
-
- $assertion = clone $assertion;
-
- foreach ($assertion->rule as $i => $and_rules) {
- foreach ($and_rules as $j => $rule) {
- if (strpos($rule, 'class-constant(') === 0) {
- $codebase = $source->getCodebase();
-
- try {
- $assertion->rule[$i][$j] = TypeExpander::expandUnion(
- $codebase,
- Type::parseString(substr($rule, 15, -1)),
- null,
- null,
- null
- )->getAssertionString();
- } catch (UnexpectedValueException $e) {
- continue;
- }
- }
- }
- }
-
- if (is_int($assertion->var_id) && isset($expr->getArgs()[$assertion->var_id])) {
- if ($assertion->var_id === 0) {
- $var_name = $first_var_name;
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $expr->getArgs()[$assertion->var_id]->value,
- $this_class_name,
- $source
- );
- }
-
- if ($var_name) {
- if ('!' === $assertion->rule[0][0][0]) {
- $if_types[$var_name] = [[substr($assertion->rule[0][0], 1)]];
- } else {
- $if_types[$var_name] = [['!' . $assertion->rule[0][0]]];
- }
- }
- } elseif ($assertion->var_id === '$this' && $expr instanceof PhpParser\Node\Expr\MethodCall) {
- $var_id = ExpressionIdentifier::getArrayVarId(
- $expr->var,
- $this_class_name,
- $source
- );
-
- if ($var_id) {
- if ('!' === $assertion->rule[0][0][0]) {
- $if_types[$var_id] = [[substr($assertion->rule[0][0], 1)]];
- } else {
- $if_types[$var_id] = [['!' . $assertion->rule[0][0]]];
- }
- }
- } elseif (is_string($assertion->var_id)) {
- $is_function = substr($assertion->var_id, -2) === '()';
- $exploded_id = explode('->', $assertion->var_id);
- $var_id = $exploded_id[0] ?? null;
- $property = $exploded_id[1] ?? null;
-
- if (is_numeric($var_id) && null !== $property && !$is_function) {
- $args = $expr->getArgs();
-
- if (!array_key_exists($var_id, $args)) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Variable '.$var_id.' is not an argument so cannot be asserted',
- new CodeLocation($source, $expr)
- )
- );
- continue;
- }
- /** @var PhpParser\Node\Expr\Variable $arg_value */
- $arg_value = $args[$var_id]->value;
-
- $arg_var_id = ExpressionIdentifier::getArrayVarId($arg_value, null, $source);
-
- if (null === $arg_var_id) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Variable being asserted as argument ' . ($var_id+1) . ' cannot be found
- in local scope',
- new CodeLocation($source, $expr)
- )
- );
- continue;
- }
-
- if (count($exploded_id) === 2) {
- $failedMessage = self::isPropertyImmutableOnArgument(
- $property,
- $source->getNodeTypeProvider(),
- $source->getCodebase()->classlike_storage_provider,
- $arg_value
- );
-
- if (null !== $failedMessage) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock($failedMessage, new CodeLocation($source, $expr))
- );
- continue;
- }
- }
-
- if ('!' === $assertion->rule[0][0][0]) {
- $rule = substr($assertion->rule[0][0], 1);
- } else {
- $rule = '!' . $assertion->rule[0][0];
- }
- $assertion_var_id = str_replace($var_id, $arg_var_id, $assertion->var_id);
-
- $if_types[$assertion_var_id] = [[$rule]];
- } elseif (!$expr instanceof PhpParser\Node\Expr\FuncCall) {
- $var_id = $assertion->var_id;
- if (strpos($var_id, 'self::') === 0) {
- $var_id = $this_class_name.'::'.substr($var_id, 6);
- }
- $if_types[$var_id] = [['!'.$assertion->rule[0][0]]];
- } else {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- sprintf('Assertion of variable "%s" cannot be recognized', $assertion->var_id),
- new CodeLocation($source, $expr)
- )
- );
- }
- }
-
- if ($if_types) {
- $anded_types[] = $if_types;
- }
- }
- }
-
- return $anded_types;
- }
-
- /**
- * @return list<string>
- */
- protected static function getInstanceOfTypes(
- PhpParser\Node\Expr\Instanceof_ $stmt,
- ?string $this_class_name,
- FileSource $source
- ): array {
- if ($stmt->class instanceof PhpParser\Node\Name) {
- if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
- $instanceof_class = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $source->getAliases()
- );
-
- if ($source instanceof StatementsAnalyzer) {
- $codebase = $source->getCodebase();
- $instanceof_class = $codebase->classlikes->getUnAliasedName($instanceof_class);
- }
-
- return [$instanceof_class];
- }
-
- if ($this_class_name
- && (in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true))) {
- if ($stmt->class->parts[0] === 'static') {
- return ['=' . $this_class_name . '&static'];
- }
-
- return [$this_class_name];
- }
- } elseif ($source instanceof StatementsAnalyzer) {
- $stmt_class_type = $source->node_data->getType($stmt->class);
-
- if ($stmt_class_type) {
- $literal_class_strings = [];
-
- foreach ($stmt_class_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TLiteralClassString) {
- $literal_class_strings[] = $atomic_type->value;
- } elseif ($atomic_type instanceof TTemplateParamClass) {
- $literal_class_strings[] = $atomic_type->param_name;
- } elseif ($atomic_type instanceof TClassString && $atomic_type->as !== 'object') {
- $literal_class_strings[] = $atomic_type->as;
- }
- }
-
- return $literal_class_strings;
- }
- }
-
- return [];
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- */
- protected static function hasNullVariable(
- PhpParser\Node\Expr\BinaryOp $conditional,
- FileSource $source
- ): ?int {
- if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($conditional->right->name->parts[0]) === 'null'
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($conditional->left->name->parts[0]) === 'null'
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- if ($source instanceof StatementsAnalyzer
- && ($right_type = $source->node_data->getType($conditional->right))
- && $right_type->isNull()
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- return null;
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- */
- public static function hasFalseVariable(
- PhpParser\Node\Expr\BinaryOp $conditional
- ): ?int {
- if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($conditional->right->name->parts[0]) === 'false'
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($conditional->left->name->parts[0]) === 'false'
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return null;
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- */
- public static function hasTrueVariable(
- PhpParser\Node\Expr\BinaryOp $conditional
- ): ?int {
- if ($conditional->right instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($conditional->right->name->parts[0]) === 'true'
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if ($conditional->left instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($conditional->left->name->parts[0]) === 'true'
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return null;
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- */
- protected static function hasEmptyArrayVariable(
- PhpParser\Node\Expr\BinaryOp $conditional
- ): ?int {
- if ($conditional->right instanceof PhpParser\Node\Expr\Array_
- && !$conditional->right->items
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if ($conditional->left instanceof PhpParser\Node\Expr\Array_
- && !$conditional->left->items
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return null;
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- * @return false|int
- */
- protected static function hasGetTypeCheck(
- PhpParser\Node\Expr\BinaryOp $conditional
- ) {
- if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->right->name instanceof PhpParser\Node\Name
- && strtolower($conditional->right->name->parts[0]) === 'gettype'
- && $conditional->right->getArgs()
- && $conditional->left instanceof PhpParser\Node\Scalar\String_
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'gettype'
- && $conditional->left->getArgs()
- && $conditional->right instanceof PhpParser\Node\Scalar\String_
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- * @return false|int
- */
- protected static function hasGetDebugTypeCheck(
- PhpParser\Node\Expr\BinaryOp $conditional
- ) {
- if ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->right->name instanceof PhpParser\Node\Name
- && strtolower($conditional->right->name->parts[0]) === 'get_debug_type'
- && $conditional->right->getArgs()
- && ($conditional->left instanceof PhpParser\Node\Scalar\String_
- || $conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch)
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'get_debug_type'
- && $conditional->left->getArgs()
- && ($conditional->right instanceof PhpParser\Node\Scalar\String_
- || $conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch)
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param Identical|Equal|NotIdentical|NotEqual $conditional
- * @return false|int
- */
- protected static function hasGetClassCheck(
- PhpParser\Node\Expr\BinaryOp $conditional,
- FileSource $source
- ) {
- if (!$source instanceof StatementsAnalyzer) {
- return false;
- }
-
- $right_get_class = $conditional->right instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->right->name instanceof PhpParser\Node\Name
- && strtolower($conditional->right->name->parts[0]) === 'get_class';
-
- $right_static_class = $conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch
- && $conditional->right->class instanceof PhpParser\Node\Name
- && $conditional->right->class->parts === ['static']
- && $conditional->right->name instanceof PhpParser\Node\Identifier
- && strtolower($conditional->right->name->name) === 'class';
-
- $right_variable_class_const = $conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch
- && $conditional->right->class instanceof PhpParser\Node\Expr\Variable
- && $conditional->right->name instanceof PhpParser\Node\Identifier
- && strtolower($conditional->right->name->name) === 'class';
-
- $left_class_string = $conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch
- && $conditional->left->class instanceof PhpParser\Node\Name
- && $conditional->left->name instanceof PhpParser\Node\Identifier
- && strtolower($conditional->left->name->name) === 'class';
-
- $left_type = $source->node_data->getType($conditional->left);
-
- $left_class_string_t = false;
-
- if ($left_type && $left_type->isSingle()) {
- foreach ($left_type->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TClassString) {
- $left_class_string_t = true;
- break;
- }
- }
- }
-
- if (($right_get_class || $right_static_class || $right_variable_class_const)
- && ($left_class_string || $left_class_string_t)
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- $left_get_class = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'get_class';
-
- $left_static_class = $conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch
- && $conditional->left->class instanceof PhpParser\Node\Name
- && $conditional->left->class->parts === ['static']
- && $conditional->left->name instanceof PhpParser\Node\Identifier
- && strtolower($conditional->left->name->name) === 'class';
-
- $left_variable_class_const = $conditional->left instanceof PhpParser\Node\Expr\ClassConstFetch
- && $conditional->left->class instanceof PhpParser\Node\Expr\Variable
- && $conditional->left->name instanceof PhpParser\Node\Identifier
- && strtolower($conditional->left->name->name) === 'class';
-
- $right_class_string = $conditional->right instanceof PhpParser\Node\Expr\ClassConstFetch
- && $conditional->right->class instanceof PhpParser\Node\Name
- && $conditional->right->name instanceof PhpParser\Node\Identifier
- && strtolower($conditional->right->name->name) === 'class';
-
- $right_type = $source->node_data->getType($conditional->right);
-
- $right_class_string_t = false;
-
- if ($right_type && $right_type->isSingle()) {
- foreach ($right_type->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TClassString) {
- $right_class_string_t = true;
- break;
- }
- }
- }
-
- if (($left_get_class || $left_static_class || $left_variable_class_const)
- && ($right_class_string || $right_class_string_t)
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param Greater|GreaterOrEqual|Smaller|SmallerOrEqual $conditional
- * @return false|int
- */
- protected static function hasNonEmptyCountEqualityCheck(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?int &$min_count
- ) {
- if ($conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'count'
- && $conditional->left->getArgs()
- && ($conditional instanceof BinaryOp\Greater || $conditional instanceof BinaryOp\GreaterOrEqual)
- ) {
- $assignment_to = self::ASSIGNMENT_TO_RIGHT;
- $compare_to = $conditional->right;
- $comparison_adjustment = $conditional instanceof BinaryOp\Greater ? 1 : 0;
- } elseif ($conditional->right instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->right->name instanceof PhpParser\Node\Name
- && strtolower($conditional->right->name->parts[0]) === 'count'
- && $conditional->right->getArgs()
- && ($conditional instanceof BinaryOp\Smaller || $conditional instanceof BinaryOp\SmallerOrEqual)
- ) {
- $assignment_to = self::ASSIGNMENT_TO_LEFT;
- $compare_to = $conditional->left;
- $comparison_adjustment = $conditional instanceof BinaryOp\Smaller ? 1 : 0;
- } else {
- return false;
- }
-
- // TODO get node type provider here somehow and check literal ints and int ranges
- if ($compare_to instanceof PhpParser\Node\Scalar\LNumber
- && $compare_to->value > (-1 * $comparison_adjustment)
- ) {
- $min_count = $compare_to->value + $comparison_adjustment;
-
- return $assignment_to;
- }
-
- return false;
- }
-
- /**
- * @param Greater|GreaterOrEqual|Smaller|SmallerOrEqual $conditional
- * @return false|int
- */
- protected static function hasLessThanCountEqualityCheck(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?int &$max_count
- ) {
- $left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'count'
- && $conditional->left->getArgs();
-
- $operator_less_than_or_equal =
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
- || $conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller;
-
- if ($left_count
- && $operator_less_than_or_equal
- && $conditional->right instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $max_count = $conditional->right->value -
- ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 1 : 0);
-
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- $right_count = $conditional->right instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->right->name instanceof PhpParser\Node\Name
- && strtolower($conditional->right->name->parts[0]) === 'count'
- && $conditional->right->getArgs();
-
- $operator_greater_than_or_equal =
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
- || $conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater;
-
- if ($right_count
- && $operator_greater_than_or_equal
- && $conditional->left instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $max_count = $conditional->left->value -
- ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 1 : 0);
-
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param Equal|Identical|NotEqual|NotIdentical $conditional
- * @return false|int
- */
- protected static function hasCountEqualityCheck(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?int &$count
- ) {
- $left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'count'
- && $conditional->left->getArgs();
-
- if ($left_count && $conditional->right instanceof PhpParser\Node\Scalar\LNumber) {
- $count = $conditional->right->value;
-
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- $right_count = $conditional->right instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->right->name instanceof PhpParser\Node\Name
- && strtolower($conditional->right->name->parts[0]) === 'count'
- && $conditional->right->getArgs();
-
- if ($right_count && $conditional->left instanceof PhpParser\Node\Scalar\LNumber) {
- $count = $conditional->left->value;
-
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Greater|PhpParser\Node\Expr\BinaryOp\GreaterOrEqual $conditional
- * @return false|int
- */
- protected static function hasSuperiorNumberCheck(
- FileSource $source,
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?int &$literal_value_comparison,
- bool &$isset_assert
- ) {
- $right_assignment = false;
- $value_right = null;
- if ($source instanceof StatementsAnalyzer
- && ($type = $source->node_data->getType($conditional->right))
- && $type->isSingleIntLiteral()
- ) {
- $right_assignment = true;
- $value_right = $type->getSingleIntLiteral()->value;
- } elseif ($conditional->right instanceof LNumber) {
- $right_assignment = true;
- $value_right = $conditional->right->value;
- } elseif ($conditional->right instanceof UnaryMinus && $conditional->right->expr instanceof LNumber) {
- $right_assignment = true;
- $value_right = -$conditional->right->expr->value;
- } elseif ($conditional->right instanceof UnaryPlus && $conditional->right->expr instanceof LNumber) {
- $right_assignment = true;
- $value_right = $conditional->right->expr->value;
- }
- if ($right_assignment === true && $value_right !== null) {
- $isset_assert = $value_right === 0 && $conditional instanceof Greater;
-
- $literal_value_comparison = $value_right +
- ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 1 : 0);
-
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- $left_assignment = false;
- $value_left = null;
- if ($source instanceof StatementsAnalyzer
- && ($type = $source->node_data->getType($conditional->left))
- && $type->isSingleIntLiteral()
- ) {
- $left_assignment = true;
- $value_left = $type->getSingleIntLiteral()->value;
- } elseif ($conditional->left instanceof LNumber) {
- $left_assignment = true;
- $value_left = $conditional->left->value;
- } elseif ($conditional->left instanceof UnaryMinus && $conditional->left->expr instanceof LNumber) {
- $left_assignment = true;
- $value_left = -$conditional->left->expr->value;
- } elseif ($conditional->left instanceof UnaryPlus && $conditional->left->expr instanceof LNumber) {
- $left_assignment = true;
- $value_left = $conditional->left->expr->value;
- }
- if ($left_assignment === true && $value_left !== null) {
- $isset_assert = $value_left === 0 && $conditional instanceof Greater;
-
- $literal_value_comparison = $value_left +
- ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? -1 : 0);
-
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Smaller|PhpParser\Node\Expr\BinaryOp\SmallerOrEqual $conditional
- * @return false|int
- */
- protected static function hasInferiorNumberCheck(
- FileSource $source,
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?int &$literal_value_comparison,
- bool &$isset_assert
- ) {
- $right_assignment = false;
- $value_right = null;
- if ($source instanceof StatementsAnalyzer
- && ($type = $source->node_data->getType($conditional->right))
- && $type->isSingleIntLiteral()
- ) {
- $right_assignment = true;
- $value_right = $type->getSingleIntLiteral()->value;
- } elseif ($conditional->right instanceof LNumber) {
- $right_assignment = true;
- $value_right = $conditional->right->value;
- } elseif ($conditional->right instanceof UnaryMinus && $conditional->right->expr instanceof LNumber) {
- $right_assignment = true;
- $value_right = -$conditional->right->expr->value;
- } elseif ($conditional->right instanceof UnaryPlus && $conditional->right->expr instanceof LNumber) {
- $right_assignment = true;
- $value_right = $conditional->right->expr->value;
- }
- if ($right_assignment === true && $value_right !== null) {
- $isset_assert = $value_right === 0 && $conditional instanceof Smaller;
-
- $literal_value_comparison = $value_right +
- ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? -1 : 0);
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- $left_assignment = false;
- $value_left = null;
- if ($source instanceof StatementsAnalyzer
- && ($type = $source->node_data->getType($conditional->left))
- && $type->isSingleIntLiteral()
- ) {
- $left_assignment = true;
- $value_left = $type->getSingleIntLiteral()->value;
- } elseif ($conditional->left instanceof LNumber) {
- $left_assignment = true;
- $value_left = $conditional->left->value;
- } elseif ($conditional->left instanceof UnaryMinus && $conditional->left->expr instanceof LNumber) {
- $left_assignment = true;
- $value_left = -$conditional->left->expr->value;
- } elseif ($conditional->left instanceof UnaryPlus && $conditional->left->expr instanceof LNumber) {
- $left_assignment = true;
- $value_left = $conditional->left->expr->value;
- }
- if ($left_assignment === true && $value_left !== null) {
- $isset_assert = $value_left === 0 && $conditional instanceof Smaller;
-
- $literal_value_comparison = $value_left +
- ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Smaller ? 1 : 0);
-
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Greater|PhpParser\Node\Expr\BinaryOp\GreaterOrEqual $conditional
- * @return false|int
- */
- protected static function hasReconcilableNonEmptyCountEqualityCheck(
- PhpParser\Node\Expr\BinaryOp $conditional
- ) {
- $left_count = $conditional->left instanceof PhpParser\Node\Expr\FuncCall
- && $conditional->left->name instanceof PhpParser\Node\Name
- && strtolower($conditional->left->name->parts[0]) === 'count';
-
- $right_number = $conditional->right instanceof PhpParser\Node\Scalar\LNumber
- && $conditional->right->value === (
- $conditional instanceof PhpParser\Node\Expr\BinaryOp\Greater ? 0 : 1);
-
- if ($left_count && $right_number) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- return false;
- }
-
- /**
- * @param Identical|Equal|Smaller|SmallerOrEqual|NotIdentical|NotEqual $conditional
- * @return false|int
- */
- protected static function hasTypedValueComparison(
- PhpParser\Node\Expr\BinaryOp $conditional,
- FileSource $source
- ) {
- if (!$source instanceof StatementsAnalyzer) {
- return false;
- }
-
- if (($right_type = $source->node_data->getType($conditional->right))
- && ((!$conditional->right instanceof PhpParser\Node\Expr\Variable
- && !$conditional->right instanceof PhpParser\Node\Expr\PropertyFetch
- && !$conditional->right instanceof PhpParser\Node\Expr\StaticPropertyFetch)
- || $conditional->left instanceof PhpParser\Node\Expr\Variable
- || $conditional->left instanceof PhpParser\Node\Expr\PropertyFetch
- || $conditional->left instanceof PhpParser\Node\Expr\StaticPropertyFetch)
- && count($right_type->getAtomicTypes()) === 1
- && !$right_type->hasMixed()
- ) {
- return self::ASSIGNMENT_TO_RIGHT;
- }
-
- if (($left_type = $source->node_data->getType($conditional->left))
- && !$conditional->left instanceof PhpParser\Node\Expr\Variable
- && !$conditional->left instanceof PhpParser\Node\Expr\PropertyFetch
- && !$conditional->left instanceof PhpParser\Node\Expr\StaticPropertyFetch
- && count($left_type->getAtomicTypes()) === 1
- && !$left_type->hasMixed()
- ) {
- return self::ASSIGNMENT_TO_LEFT;
- }
-
- return false;
- }
-
- protected static function hasIsACheck(
- PhpParser\Node\Expr\FuncCall $stmt,
- StatementsAnalyzer $source
- ): bool {
- if ($stmt->name instanceof PhpParser\Node\Name
- && (strtolower($stmt->name->parts[0]) === 'is_a'
- || strtolower($stmt->name->parts[0]) === 'is_subclass_of')
- && isset($stmt->getArgs()[1])
- ) {
- $second_arg = $stmt->getArgs()[1]->value;
-
- if ($second_arg instanceof PhpParser\Node\Scalar\String_
- || (
- $second_arg instanceof PhpParser\Node\Expr\ClassConstFetch
- && $second_arg->class instanceof PhpParser\Node\Name
- && $second_arg->name instanceof PhpParser\Node\Identifier
- && strtolower($second_arg->name->name) === 'class'
- )
- || (($second_arg_type = $source->node_data->getType($second_arg))
- && $second_arg_type->hasString())
- ) {
- return true;
- }
- }
-
- return false;
- }
-
- /**
- * @return array<string, non-empty-list<non-empty-list<string>>>
- */
- private static function handleIsTypeCheck(
- ?Codebase $codebase,
- FileSource $source,
- PhpParser\Node\Expr\FuncCall $stmt,
- ?string $first_var_name,
- ?Union $first_var_type,
- PhpParser\Node\Expr\FuncCall $expr,
- bool $negate
- ): array {
- $if_types = [];
- if ($stmt->name instanceof PhpParser\Node\Name
- && ($function_name = strtolower($stmt->name->parts[0]))
- && isset(self::IS_TYPE_CHECKS[$function_name])
- && $source instanceof StatementsAnalyzer
- && ($source->getNamespace() === null //either the namespace is null
- || $stmt->name instanceof PhpParser\Node\Name\FullyQualified //or we have a FQ to base function
- || isset($source->getAliases()->functions[$function_name]) //or it is imported
- || ($codebase && !$codebase->functions->functionExists(
- $source,
- strtolower($source->getNamespace()."\\".$function_name)
- )) //or this function name does not exist in current namespace
- )
- ) {
- if ($first_var_name) {
- $if_types[$first_var_name] = [[self::IS_TYPE_CHECKS[$function_name][0]]];
- } elseif ($first_var_type
- && $codebase
- ) {
- if (isset(self::IS_TYPE_CHECKS[$function_name][1])) {
- $callable = self::IS_TYPE_CHECKS[$function_name][1];
- assert(is_callable($callable));
- $type = $callable();
- assert($type instanceof Union);
- self::processIrreconcilableFunctionCall(
- $first_var_type,
- $type,
- $expr,
- $source,
- $codebase,
- $negate
- );
- }
- }
- }
-
- return $if_types;
- }
-
- protected static function hasCallableCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
- {
- return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'is_callable';
- }
-
- /**
- * @return Reconciler::RECONCILIATION_*
- */
- protected static function hasClassExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): int
- {
- if ($stmt->name instanceof PhpParser\Node\Name
- && strtolower($stmt->name->parts[0]) === 'class_exists'
- ) {
- if (!isset($stmt->getArgs()[1])) {
- return 2;
- }
-
- $second_arg = $stmt->getArgs()[1]->value;
-
- if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($second_arg->name->parts[0]) === 'true'
- ) {
- return 2;
- }
-
- return 1;
- }
-
- return 0;
- }
-
- /**
- * @return 0|1|2
- */
- protected static function hasTraitExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): int
- {
- if ($stmt->name instanceof PhpParser\Node\Name
- && strtolower($stmt->name->parts[0]) === 'trait_exists'
- ) {
- if (!isset($stmt->getArgs()[1])) {
- return 2;
- }
-
- $second_arg = $stmt->getArgs()[1]->value;
-
- if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($second_arg->name->parts[0]) === 'true'
- ) {
- return 2;
- }
-
- return 1;
- }
-
- return 0;
- }
-
- protected static function hasInterfaceExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
- {
- return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'interface_exists';
- }
-
- protected static function hasFunctionExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
- {
- return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'function_exists';
- }
-
- protected static function hasInArrayCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
- {
- if ($stmt->name instanceof PhpParser\Node\Name
- && strtolower($stmt->name->parts[0]) === 'in_array'
- && isset($stmt->getArgs()[2])
- ) {
- $second_arg = $stmt->getArgs()[2]->value;
-
- if ($second_arg instanceof PhpParser\Node\Expr\ConstFetch
- && strtolower($second_arg->name->parts[0]) === 'true'
- ) {
- return true;
- }
- }
-
- return false;
- }
-
- protected static function hasNonEmptyCountCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
- {
- return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'count';
- }
-
- protected static function hasArrayKeyExistsCheck(PhpParser\Node\Expr\FuncCall $stmt): bool
- {
- return $stmt->name instanceof PhpParser\Node\Name && strtolower($stmt->name->parts[0]) === 'array_key_exists';
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getNullInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- FileSource $source,
- ?string $this_class_name,
- ?Codebase $codebase,
- int $null_position
- ): array {
- $if_types = [];
-
- if ($null_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($null_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('Bad null variable position');
- }
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($base_conditional instanceof PhpParser\Node\Expr\Assign) {
- $var_name = '=' . $var_name;
- }
-
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $if_types[$var_name] = [['!null']];
- } else {
- $if_types[$var_name] = [['!falsy']];
- }
- }
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- ) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $null_type = Type::getNull();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $var_type,
- $null_type
- ) && !UnionTypeComparator::isContainedBy(
- $codebase,
- $null_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- 'Docblock-defined type ' . $var_type . ' can never contain null',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' null'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $var_type . ' can never contain null',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' null'
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getFalseInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- bool $cache,
- ?string $this_class_name,
- FileSource $source,
- bool $inside_conditional,
- ?Codebase $codebase,
- int $false_position
- ): array {
- $if_types = [];
-
- if ($false_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($false_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('Bad false variable position');
- }
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $if_types[$var_name] = [['!false']];
- } else {
- $if_types[$var_name] = [['!falsy']];
- }
-
- $if_types = [$if_types];
- } else {
- $if_types = null;
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $if_types = $source->node_data->getAssertions($base_conditional);
- }
-
- if ($if_types === null) {
- $if_types = self::scrapeAssertions(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- false,
- $cache,
- $inside_conditional
- );
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $source->node_data->setAssertions($base_conditional, $if_types);
- }
- }
- }
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
- ) {
- $config = $source->getCodebase()->config;
-
- if ($config->strict_binary_operands
- && $var_type->isSingle()
- && $var_type->hasBool()
- && !$var_type->from_docblock
- ) {
- IssueBuffer::maybeAdd(
- new RedundantIdentityWithTrue(
- 'The "!== false" part of this comparison is redundant',
- new CodeLocation($source, $conditional)
- ),
- $source->getSuppressedIssues()
- );
- }
-
- $false_type = Type::getFalse();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $var_type,
- $false_type
- ) && !UnionTypeComparator::isContainedBy(
- $codebase,
- $false_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- 'Docblock-defined type ' . $var_type . ' can never contain false',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' false'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $var_type . ' can never contain false',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' false'
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
-
- return $if_types;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getTrueInequalityAssertions(
- int $true_position,
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase,
- bool $cache,
- bool $inside_conditional
- ): array {
- $if_types = [];
-
- if ($true_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($true_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('Bad null variable position');
- }
-
- if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
- $notif_types = self::processFunctionCall(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- true
- );
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $if_types[$var_name] = [['!true']];
- } else {
- $if_types[$var_name] = [['falsy']];
- }
-
- $notif_types = [];
- } else {
- $notif_types = null;
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $notif_types = $source->node_data->getAssertions($base_conditional);
- }
-
- if ($notif_types === null) {
- $notif_types = self::scrapeAssertions(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- false,
- $cache,
- $inside_conditional
- );
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $source->node_data->setAssertions($base_conditional, $notif_types);
- }
- }
- }
- }
-
- if (count($notif_types) === 1) {
- $notif_types = $notif_types[0];
-
- if (count($notif_types) === 1) {
- $if_types = Algebra::negateTypes($notif_types);
- }
- }
-
- $if_types = $if_types ? [$if_types] : [];
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- ) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $true_type = Type::getTrue();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $var_type,
- $true_type
- ) && !UnionTypeComparator::isContainedBy(
- $codebase,
- $true_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- 'Docblock-defined type ' . $var_type . ' can never contain true',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' true'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $var_type . ' can never contain ' . $true_type,
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' true'
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- return $if_types;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getEmptyInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase,
- int $empty_array_position
- ): array {
- $if_types = [];
-
- if ($empty_array_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($empty_array_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('Bad empty array variable position');
- }
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $if_types[$var_name] = [['non-empty-countable']];
- } else {
- $if_types[$var_name] = [['!falsy']];
- }
- }
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- ) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical) {
- $empty_array_type = Type::getEmptyArray();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $var_type,
- $empty_array_type
- ) && !UnionTypeComparator::isContainedBy(
- $codebase,
- $empty_array_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- 'Docblock-defined type ' . $var_type->getId() . ' can never contain null',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' null'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $var_type->getId() . ' can never contain null',
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' null'
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGettypeInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- int $gettype_position
- ): array {
- $if_types = [];
-
- if ($gettype_position === self::ASSIGNMENT_TO_RIGHT) {
- $whichclass_expr = $conditional->left;
- $gettype_expr = $conditional->right;
- } elseif ($gettype_position === self::ASSIGNMENT_TO_LEFT) {
- $whichclass_expr = $conditional->right;
- $gettype_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$gettype_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $gettype_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $gettype_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
- $var_type = $whichclass_expr->value;
- } elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $whichclass_expr->class instanceof PhpParser\Node\Name
- ) {
- $var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $whichclass_expr->class,
- $source->getAliases()
- );
- } else {
- throw new UnexpectedValueException('Shouldn’t get here');
- }
-
- if (!isset(ClassLikeAnalyzer::GETTYPE_TYPES[$var_type])) {
- IssueBuffer::maybeAdd(
- new UnevaluatedCode(
- 'gettype cannot return this value',
- new CodeLocation($source, $whichclass_expr)
- )
- );
- } else {
- if ($var_name && $var_type) {
- if ($var_type === 'class@anonymous') {
- $if_types[$var_name] = [['!=object']];
- } elseif ($var_type === 'resource (closed)') {
- $if_types[$var_name] = [['!closed-resource']];
- } elseif (strpos($var_type, 'resource (') === 0) {
- $if_types[$var_name] = [['!=resource']];
- } else {
- $if_types[$var_name] = [['!' . $var_type]];
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGetdebugTypeInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- int $get_debug_type_position
- ): array {
- $if_types = [];
-
- if ($get_debug_type_position === self::ASSIGNMENT_TO_RIGHT) {
- $whichclass_expr = $conditional->left;
- $get_debug_type_expr = $conditional->right;
- } elseif ($get_debug_type_position === self::ASSIGNMENT_TO_LEFT) {
- $whichclass_expr = $conditional->right;
- $get_debug_type_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$gettype_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $get_debug_type_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $get_debug_type_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
- $var_type = $whichclass_expr->value;
- } elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $whichclass_expr->class instanceof PhpParser\Node\Name
- ) {
- $var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $whichclass_expr->class,
- $source->getAliases()
- );
- } else {
- throw new UnexpectedValueException('Shouldn’t get here');
- }
-
- if ($var_name && $var_type) {
- if ($var_type === 'class@anonymous') {
- $if_types[$var_name] = [['!=object']];
- } elseif ($var_type === 'resource (closed)') {
- $if_types[$var_name] = [['!closed-resource']];
- } elseif (strpos($var_type, 'resource (') === 0) {
- $if_types[$var_name] = [['!=resource']];
- } else {
- $if_types[$var_name] = [['!' . $var_type]];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGetclassInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- StatementsAnalyzer $source,
- int $getclass_position
- ): array {
- $if_types = [];
-
- if ($getclass_position === self::ASSIGNMENT_TO_RIGHT) {
- $whichclass_expr = $conditional->left;
- $getclass_expr = $conditional->right;
- } elseif ($getclass_position === self::ASSIGNMENT_TO_LEFT) {
- $whichclass_expr = $conditional->right;
- $getclass_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$getclass_position value');
- }
-
- if ($getclass_expr instanceof PhpParser\Node\Expr\FuncCall) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $getclass_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
- } elseif ($getclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $getclass_expr->class instanceof PhpParser\Node\Expr
- ) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $getclass_expr->class,
- $this_class_name,
- $source
- );
- } else {
- $var_name = '$this';
- }
-
- if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
- $var_type = $whichclass_expr->value;
- } elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $whichclass_expr->class instanceof PhpParser\Node\Name
- ) {
- $var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $whichclass_expr->class,
- $source->getAliases()
- );
-
- if ($var_type === 'self' || $var_type === 'static') {
- $var_type = $this_class_name;
- } elseif ($var_type === 'parent') {
- $var_type = null;
- }
- } else {
- $type = $source->node_data->getType($whichclass_expr);
-
- if ($type && $var_name) {
- foreach ($type->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TTemplateParamClass) {
- $if_types[$var_name] = [['!=' . $type_part->param_name]];
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($var_type
- && ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $source,
- $var_type,
- new CodeLocation($source, $whichclass_expr),
- null,
- null,
- $source->getSuppressedIssues()
- ) === false
- ) {
- // fall through
- } else {
- if ($var_name && $var_type) {
- $if_types[$var_name] = [['!=getclass-' . $var_type]];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\NotIdentical|PhpParser\Node\Expr\BinaryOp\NotEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getTypedValueInequalityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- StatementsAnalyzer $source,
- ?Codebase $codebase,
- int $typed_value_position
- ): array {
- $if_types = [];
-
- if ($typed_value_position === self::ASSIGNMENT_TO_RIGHT) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->left,
- $this_class_name,
- $source
- );
-
- $other_type = $source->node_data->getType($conditional->left);
- $var_type = $source->node_data->getType($conditional->right);
- } elseif ($typed_value_position === self::ASSIGNMENT_TO_LEFT) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->right,
- $this_class_name,
- $source
- );
-
- $var_type = $source->node_data->getType($conditional->left);
- $other_type = $source->node_data->getType($conditional->right);
- } else {
- throw new UnexpectedValueException('$typed_value_position value');
- }
-
- if ($var_type) {
- if ($var_name) {
- $not_identical = $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
- || ($other_type
- && (($var_type->isString() && $other_type->isString())
- || ($var_type->isInt() && $other_type->isInt())
- || ($var_type->isFloat() && $other_type->isFloat())
- )
- );
-
- try {
- $assertion = $var_type->getAssertionString();
- } catch (UnexpectedValueException $e) {
- $assertion = null;
- }
-
- if ($not_identical) {
- if ($assertion) {
- $if_types[$var_name] = [['!=' . $assertion]];
- }
- } else {
- if ($assertion) {
- $if_types[$var_name] = [['!~' . $assertion]];
- }
- }
- }
-
- if ($codebase
- && $other_type
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
- ) {
- self::handleParadoxicalAssertions(
- $source,
- $var_type,
- $this_class_name,
- $other_type,
- $codebase,
- $conditional
- );
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getNullEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase,
- int $null_position
- ): array {
- $if_types = [];
-
- if ($null_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($null_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('$null_position value');
- }
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name && $base_conditional instanceof PhpParser\Node\Expr\Assign) {
- $var_name = '=' . $var_name;
- }
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
- $if_types[$var_name] = [['null']];
- } else {
- $if_types[$var_name] = [['falsy']];
- }
- }
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- $null_type = Type::getNull();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $var_type,
- $null_type
- ) && !UnionTypeComparator::isContainedBy(
- $codebase,
- $null_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- $var_type . ' does not contain null',
- new CodeLocation($source, $conditional),
- $var_type . ' null'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainNull(
- $var_type . ' does not contain null',
- new CodeLocation($source, $conditional),
- $var_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getTrueEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase,
- bool $cache,
- int $true_position
- ): array {
- $if_types = [];
-
- if ($true_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($true_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('Unrecognised position');
- }
-
- if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
- $if_types = self::processFunctionCall(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- false
- );
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
- $if_types[$var_name] = [['true']];
- } else {
- $if_types[$var_name] = [['!falsy']];
- }
-
- $if_types = [$if_types];
- } else {
- $base_assertions = null;
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $base_assertions = $source->node_data->getAssertions($base_conditional);
- }
-
- if ($base_assertions === null) {
- $base_assertions = self::scrapeAssertions(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- false,
- $cache
- );
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $source->node_data->setAssertions($base_conditional, $base_assertions);
- }
- }
-
- $if_types = $base_assertions;
- }
- }
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- $config = $source->getCodebase()->config;
-
- if ($config->strict_binary_operands
- && $var_type->isSingle()
- && $var_type->hasBool()
- && !$var_type->from_docblock
- ) {
- IssueBuffer::maybeAdd(
- new RedundantIdentityWithTrue(
- 'The "=== true" part of this comparison is redundant',
- new CodeLocation($source, $conditional)
- ),
- $source->getSuppressedIssues()
- );
- }
-
- $true_type = Type::getTrue();
-
- if (!UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $true_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- $var_type . ' does not contain true',
- new CodeLocation($source, $conditional),
- $var_type . ' true'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- $var_type . ' does not contain true',
- new CodeLocation($source, $conditional),
- $var_type . ' true'
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
-
- return $if_types;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getFalseEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase,
- bool $cache,
- bool $inside_conditional,
- int $false_position
- ): array {
- $if_types = [];
-
- if ($false_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($false_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('$false_position value');
- }
-
- if ($base_conditional instanceof PhpParser\Node\Expr\FuncCall) {
- $notif_types = self::processFunctionCall(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- true
- );
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
- $if_types[$var_name] = [['false']];
- } else {
- $if_types[$var_name] = [['falsy']];
- }
-
- $notif_types = [];
- } else {
- $notif_types = null;
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $notif_types = $source->node_data->getAssertions($base_conditional);
- }
-
- if ($notif_types === null) {
- $notif_types = self::scrapeAssertions(
- $base_conditional,
- $this_class_name,
- $source,
- $codebase,
- false,
- $cache,
- $inside_conditional
- );
-
- if ($source instanceof StatementsAnalyzer && $cache) {
- $source->node_data->setAssertions($base_conditional, $notif_types);
- }
- }
- }
- }
-
- if (count($notif_types) === 1) {
- $notif_types = $notif_types[0];
-
- if (count($notif_types) === 1) {
- $if_types = Algebra::negateTypes($notif_types);
- }
- }
-
- $if_types = $if_types ? [$if_types] : [];
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- ) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
- $false_type = Type::getFalse();
-
- if (!UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $false_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- $var_type . ' does not contain false',
- new CodeLocation($source, $conditional),
- $var_type . ' false'
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- $var_type . ' does not contain false',
- new CodeLocation($source, $conditional),
- $var_type . ' false'
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- return $if_types;
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getEmptyArrayEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- ?Codebase $codebase,
- int $empty_array_position
- ): array {
- $if_types = [];
-
- if ($empty_array_position === self::ASSIGNMENT_TO_RIGHT) {
- $base_conditional = $conditional->left;
- } elseif ($empty_array_position === self::ASSIGNMENT_TO_LEFT) {
- $base_conditional = $conditional->right;
- } else {
- throw new UnexpectedValueException('$empty_array_position value');
- }
-
- $var_name = ExpressionIdentifier::getArrayVarId(
- $base_conditional,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical) {
- $if_types[$var_name] = [['!non-empty-countable']];
- } else {
- $if_types[$var_name] = [['falsy']];
- }
- }
-
- if ($codebase
- && $source instanceof StatementsAnalyzer
- && ($var_type = $source->node_data->getType($base_conditional))
- && $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- $empty_array_type = Type::getEmptyArray();
-
- if (!UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $empty_array_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- $var_type . ' does not contain an empty array',
- new CodeLocation($source, $conditional),
- null
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- $var_type . ' does not contain empty array',
- new CodeLocation($source, $conditional),
- null
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGettypeEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- int $gettype_position
- ): array {
- $if_types = [];
-
- if ($gettype_position === self::ASSIGNMENT_TO_RIGHT) {
- $string_expr = $conditional->left;
- $gettype_expr = $conditional->right;
- } elseif ($gettype_position === self::ASSIGNMENT_TO_LEFT) {
- $string_expr = $conditional->right;
- $gettype_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$gettype_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $gettype_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $gettype_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- /** @var PhpParser\Node\Scalar\String_ $string_expr */
- $var_type = $string_expr->value;
-
- if (!isset(ClassLikeAnalyzer::GETTYPE_TYPES[$var_type])) {
- IssueBuffer::maybeAdd(
- new UnevaluatedCode(
- 'gettype cannot return this value',
- new CodeLocation($source, $string_expr)
- )
- );
- } else {
- if ($var_name && $var_type) {
- if ($var_type === 'class@anonymous') {
- $if_types[$var_name] = [['=object']];
- } elseif ($var_type === 'resource (closed)') {
- $if_types[$var_name] = [['closed-resource']];
- } elseif (strpos($var_type, 'resource (') === 0) {
- $if_types[$var_name] = [['=resource']];
- } else {
- $if_types[$var_name] = [[$var_type]];
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGetdebugtypeEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- FileSource $source,
- int $get_debug_type_position
- ): array {
- $if_types = [];
-
- if ($get_debug_type_position === self::ASSIGNMENT_TO_RIGHT) {
- $whichclass_expr = $conditional->left;
- $get_debug_type_expr = $conditional->right;
- } elseif ($get_debug_type_position === self::ASSIGNMENT_TO_LEFT) {
- $whichclass_expr = $conditional->right;
- $get_debug_type_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$gettype_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $get_debug_type_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $get_debug_type_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($whichclass_expr instanceof PhpParser\Node\Scalar\String_) {
- $var_type = $whichclass_expr->value;
- } elseif ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $whichclass_expr->class instanceof PhpParser\Node\Name
- ) {
- $var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $whichclass_expr->class,
- $source->getAliases()
- );
- } else {
- throw new UnexpectedValueException('Shouldn’t get here');
- }
-
- if ($var_name && $var_type) {
- if ($var_type === 'class@anonymous') {
- $if_types[$var_name] = [['=object']];
- } elseif ($var_type === 'resource (closed)') {
- $if_types[$var_name] = [['closed-resource']];
- } elseif (strpos($var_type, 'resource (') === 0) {
- $if_types[$var_name] = [['=resource']];
- } else {
- $if_types[$var_name] = [[$var_type]];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGetclassEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- StatementsAnalyzer $source,
- int $getclass_position
- ): array {
- $if_types = [];
-
- if ($getclass_position === self::ASSIGNMENT_TO_RIGHT) {
- $whichclass_expr = $conditional->left;
- $getclass_expr = $conditional->right;
- } elseif ($getclass_position === self::ASSIGNMENT_TO_LEFT) {
- $whichclass_expr = $conditional->right;
- $getclass_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$getclass_position value');
- }
-
- if ($getclass_expr instanceof PhpParser\Node\Expr\FuncCall && isset($getclass_expr->getArgs()[0])) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $getclass_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
- } elseif ($getclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $getclass_expr->class instanceof PhpParser\Node\Expr
- ) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $getclass_expr->class,
- $this_class_name,
- $source
- );
- } else {
- $var_name = '$this';
- }
-
- if ($whichclass_expr instanceof PhpParser\Node\Expr\ClassConstFetch
- && $whichclass_expr->class instanceof PhpParser\Node\Name
- ) {
- $var_type = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $whichclass_expr->class,
- $source->getAliases()
- );
-
- if ($var_type === 'self' || $var_type === 'static') {
- $var_type = $this_class_name;
- } elseif ($var_type === 'parent') {
- $var_type = null;
- }
-
- if ($var_type) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $source,
- $var_type,
- new CodeLocation($source, $whichclass_expr),
- null,
- null,
- $source->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false
- ) {
- return [];
- }
- }
-
- if ($var_name && $var_type) {
- $if_types[$var_name] = [['=getclass-' . $var_type]];
- }
- } else {
- $type = $source->node_data->getType($whichclass_expr);
-
- if ($type && $var_name) {
- foreach ($type->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TTemplateParamClass) {
- $if_types[$var_name] = [['=' . $type_part->param_name]];
- }
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Identical|PhpParser\Node\Expr\BinaryOp\Equal $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getTypedValueEqualityAssertions(
- PhpParser\Node\Expr\BinaryOp $conditional,
- ?string $this_class_name,
- StatementsAnalyzer $source,
- ?Codebase $codebase,
- int $typed_value_position
- ): array {
- $if_types = [];
-
- if ($typed_value_position === self::ASSIGNMENT_TO_RIGHT) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->left,
- $this_class_name,
- $source
- );
-
- $other_var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->right,
- $this_class_name,
- $source
- );
-
- $other_type = $source->node_data->getType($conditional->left);
- $var_type = $source->node_data->getType($conditional->right);
- } elseif ($typed_value_position === self::ASSIGNMENT_TO_LEFT) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->right,
- $this_class_name,
- $source
- );
-
- $other_var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->left,
- $this_class_name,
- $source
- );
-
- $var_type = $source->node_data->getType($conditional->left);
- $other_type = $source->node_data->getType($conditional->right);
- } else {
- throw new UnexpectedValueException('$typed_value_position value');
- }
-
- if ($var_name && $var_type) {
- $identical = $conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || ($other_type
- && (($var_type->isString(true) && $other_type->isString(true))
- || ($var_type->isInt(true) && $other_type->isInt(true))
- || ($var_type->isFloat() && $other_type->isFloat())
- )
- );
-
- if ($identical) {
- try {
- $assertion = $var_type->getAssertionString(true);
- } catch (UnexpectedValueException $e) {
- $assertion = null;
- }
-
- if ($assertion) {
- $if_types[$var_name] = [['=' . $assertion]];
- }
- } else {
- try {
- $assertion = $var_type->getAssertionString();
- } catch (UnexpectedValueException $e) {
- $assertion = null;
- }
-
- if ($assertion) {
- $if_types[$var_name] = [['~' . $assertion]];
- }
- }
-
-
- if ($other_var_name && $other_type && !$other_type->isMixed()) {
- if ($identical) {
- try {
- $assertion = $other_type->getAssertionString(true);
- } catch (UnexpectedValueException $e) {
- $assertion = null;
- }
-
- if ($assertion) {
- $if_types[$other_var_name] = [['=' . $assertion]];
- }
- } else {
- try {
- $assertion = $other_type->getAssertionString();
- } catch (UnexpectedValueException $e) {
- $assertion = null;
- }
-
- if ($assertion) {
- $if_types[$other_var_name] = [['~' . $assertion]];
- }
- }
- }
- }
-
- if ($codebase
- && $other_type
- && $var_type
- && ($conditional instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || ($other_type->isString()
- && $var_type->isString())
- )
- ) {
- self::handleParadoxicalAssertions(
- $source,
- $var_type,
- $this_class_name,
- $other_type,
- $codebase,
- $conditional
- );
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\FuncCall $expr
- * @param StatementsAnalyzer $source
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getIsaAssertions(
- PhpParser\Node\Expr\FuncCall $expr,
- StatementsAnalyzer $source,
- ?string $this_class_name,
- ?string $first_var_name
- ): array {
- $if_types = [];
-
- if ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $expr->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
- && strtolower($expr->getArgs()[0]->value->name->name) === 'class'
- && $expr->getArgs()[0]->value->class instanceof PhpParser\Node\Name
- && count($expr->getArgs()[0]->value->class->parts) === 1
- && strtolower($expr->getArgs()[0]->value->class->parts[0]) === 'static'
- ) {
- $first_var_name = '$this';
- }
-
- if ($first_var_name) {
- $first_arg = $expr->getArgs()[0]->value;
- $second_arg = $expr->getArgs()[1]->value;
- $third_arg = $expr->getArgs()[2]->value ?? null;
-
- if ($third_arg instanceof PhpParser\Node\Expr\ConstFetch) {
- if (!in_array(strtolower($third_arg->name->parts[0]), ['true', 'false'])) {
- return [];
- }
-
- $third_arg_value = strtolower($third_arg->name->parts[0]);
- } else {
- $third_arg_value = $expr->name instanceof PhpParser\Node\Name
- && strtolower($expr->name->parts[0]) === 'is_subclass_of'
- ? 'true'
- : 'false';
- }
-
- $is_a_prefix = $third_arg_value === 'true' ? 'isa-string-' : 'isa-';
-
- if (($first_arg_type = $source->node_data->getType($first_arg))
- && $first_arg_type->isSingleStringLiteral()
- && $source->getSource()->getSource() instanceof TraitAnalyzer
- && $first_arg_type->getSingleStringLiteral()->value === $this_class_name
- ) {
- // do nothing
- } else {
- if ($second_arg instanceof PhpParser\Node\Scalar\String_) {
- $fq_class_name = $second_arg->value;
- if ($fq_class_name[0] === '\\') {
- $fq_class_name = substr($fq_class_name, 1);
- }
-
- $if_types[$first_var_name] = [[$is_a_prefix . $fq_class_name]];
- } elseif ($second_arg instanceof PhpParser\Node\Expr\ClassConstFetch
- && $second_arg->class instanceof PhpParser\Node\Name
- && $second_arg->name instanceof PhpParser\Node\Identifier
- && strtolower($second_arg->name->name) === 'class'
- ) {
- $class_node = $second_arg->class;
-
- if ($class_node->parts === ['static']) {
- if ($this_class_name) {
- $if_types[$first_var_name] = [[$is_a_prefix . $this_class_name . '&static']];
- }
- } elseif ($class_node->parts === ['self']) {
- if ($this_class_name) {
- $if_types[$first_var_name] = [[$is_a_prefix . $this_class_name]];
- }
- } elseif ($class_node->parts === ['parent']) {
- // do nothing
- } else {
- $if_types[$first_var_name] = [[
- $is_a_prefix
- . ClassLikeAnalyzer::getFQCLNFromNameObject(
- $class_node,
- $source->getAliases()
- )
- ]];
- }
- } elseif (($second_arg_type = $source->node_data->getType($second_arg))
- && $second_arg_type->hasString()
- ) {
- $vals = [];
-
- foreach ($second_arg_type->getAtomicTypes() as $second_arg_atomic_type) {
- if ($second_arg_atomic_type instanceof TTemplateParamClass) {
- $vals[] = [$is_a_prefix . $second_arg_atomic_type->param_name];
- }
- }
-
- if ($vals) {
- $if_types[$first_var_name] = $vals;
- }
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\FuncCall $expr
- * @param StatementsAnalyzer $source
- * @param string|null $first_var_name
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getInarrayAssertions(
- PhpParser\Node\Expr\FuncCall $expr,
- StatementsAnalyzer $source,
- ?string $first_var_name
- ): array {
- $if_types = [];
-
- if ($first_var_name
- && ($second_arg_type = $source->node_data->getType($expr->getArgs()[1]->value))
- && isset($expr->getArgs()[0]->value)
- && !$expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
- ) {
- foreach ($second_arg_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TArray
- || $atomic_type instanceof TKeyedArray
- || $atomic_type instanceof TList
- ) {
- $is_sealed = false;
- if ($atomic_type instanceof TList) {
- $value_type = $atomic_type->type_param;
- } elseif ($atomic_type instanceof TKeyedArray) {
- $value_type = $atomic_type->getGenericValueType();
- $is_sealed = $atomic_type->sealed;
- } else {
- $value_type = $atomic_type->type_params[1];
- }
-
- $assertions = [];
-
- if (!$is_sealed) {
- // `in-array-*` has special handling in the detection of paradoxical
- // conditions and the fact the negation doesn't imply anything.
- //
- // In the vast majority of cases, the negation of `in-array-*`
- // (`Algebra::negateType`) doesn't imply anything because:
- // - The array can be empty, or
- // - The array may have one of the types but not the others.
- //
- // NOTE: the negation of the negation is the original assertion.
- if ($value_type->getId() !== '' && !$value_type->isMixed() && !$value_type->hasTemplate()) {
- $assertions[] = 'in-array-' . $value_type->getId();
- }
- } else {
- foreach ($value_type->getAtomicTypes() as $atomic_value_type) {
- if ($atomic_value_type instanceof TLiteralInt
- || $atomic_value_type instanceof TLiteralString
- || $atomic_value_type instanceof TLiteralFloat
- || $atomic_value_type instanceof TEnumCase
- ) {
- $assertions[] = '=' . $atomic_value_type->getAssertionString();
- } elseif ($atomic_value_type instanceof TFalse
- || $atomic_value_type instanceof TTrue
- || $atomic_value_type instanceof TNull
- ) {
- $assertions[] = $atomic_value_type->getAssertionString();
- } elseif (!$atomic_value_type instanceof TMixed) {
- // mixed doesn't tell us anything and can be omitted.
- //
- // For the meaning of in-array, see the above comment.
- $assertions[] = 'in-array-' . $value_type->getId();
- }
- }
- }
-
- if ($assertions !== []) {
- $if_types[$first_var_name] = [$assertions];
- }
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\FuncCall $expr
- * @param Union|null $first_var_type
- * @param string|null $first_var_name
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getArrayKeyExistsAssertions(
- PhpParser\Node\Expr\FuncCall $expr,
- ?Union $first_var_type,
- ?string $first_var_name,
- FileSource $source,
- ?string $this_class_name
- ): array {
- $if_types = [];
-
- $literal_assertions = [];
-
- if (isset($expr->getArgs()[0])
- && isset($expr->getArgs()[1])
- && $first_var_type
- && $first_var_name
- && !$expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $source instanceof StatementsAnalyzer
- && ($second_var_type = $source->node_data->getType($expr->getArgs()[1]->value))
- ) {
- foreach ($second_var_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TArray
- || $atomic_type instanceof TKeyedArray
- ) {
- if ($atomic_type instanceof TKeyedArray) {
- $key_possibly_undefined = false;
-
- foreach ($atomic_type->properties as $property_type) {
- if ($property_type->possibly_undefined) {
- $key_possibly_undefined = true;
- break;
- }
- }
-
- $key_type = $atomic_type->getGenericKeyType();
-
- if ($key_possibly_undefined) {
- $key_type->possibly_undefined = true;
- }
- } else {
- $key_type = $atomic_type->type_params[0];
- }
-
- if ($key_type->allStringLiterals() && !$key_type->possibly_undefined) {
- foreach ($key_type->getLiteralStrings() as $array_literal_type) {
- $literal_assertions[] = '=' . $array_literal_type->getAssertionString();
- }
- } elseif ($key_type->allIntLiterals() && !$key_type->possibly_undefined) {
- foreach ($key_type->getLiteralInts() as $array_literal_type) {
- $literal_assertions[] = '~' . $array_literal_type->getAssertionString();
- }
- }
- }
- }
- }
-
- if ($literal_assertions && $first_var_name) {
- $if_types[$first_var_name] = [$literal_assertions];
- } else {
- $array_root = isset($expr->getArgs()[1]->value)
- ? ExpressionIdentifier::getArrayVarId(
- $expr->getArgs()[1]->value,
- $this_class_name,
- $source
- )
- : null;
-
- if ($array_root) {
- if ($first_var_name === null && isset($expr->getArgs()[0])) {
- $first_arg = $expr->getArgs()[0];
-
- if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) {
- $first_var_name = '\'' . $first_arg->value->value . '\'';
- } elseif ($first_arg->value instanceof PhpParser\Node\Scalar\LNumber) {
- $first_var_name = (string)$first_arg->value->value;
- }
- }
-
- if ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $expr->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
- && $expr->getArgs()[0]->value->name->name !== 'class'
- ) {
- $const_type = null;
-
- if ($source instanceof StatementsAnalyzer) {
- $const_type = $source->node_data->getType($expr->getArgs()[0]->value);
- }
-
- if ($const_type) {
- if ($const_type->isSingleStringLiteral()) {
- $first_var_name = $const_type->getSingleStringLiteral()->value;
- } elseif ($const_type->isSingleIntLiteral()) {
- $first_var_name = (string)$const_type->getSingleIntLiteral()->value;
- } else {
- $first_var_name = null;
- }
- } else {
- $first_var_name = null;
- }
- } elseif ($expr->getArgs()[0]->value instanceof PhpParser\Node\Expr\Variable
- && $source instanceof StatementsAnalyzer
- && ($first_var_type = $source->node_data->getType($expr->getArgs()[0]->value))
- ) {
- foreach ($first_var_type->getLiteralStrings() as $array_literal_type) {
- $if_types[$array_root . "['" . $array_literal_type->value . "']"] = [['array-key-exists']];
- }
- foreach ($first_var_type->getLiteralInts() as $array_literal_type) {
- $if_types[$array_root . "[" . $array_literal_type->value . "]"] = [['array-key-exists']];
- }
- }
-
- if ($first_var_name !== null
- && !strpos($first_var_name, '->')
- && !strpos($first_var_name, '[')
- ) {
- $if_types[$array_root . '[' . $first_var_name . ']'] = [['array-key-exists']];
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Greater|PhpParser\Node\Expr\BinaryOp\GreaterOrEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getGreaterAssertions(
- PhpParser\Node\Expr $conditional,
- FileSource $source,
- ?string $this_class_name
- ): array {
- $if_types = [];
-
- $min_count = null;
- $count_equality_position = self::hasNonEmptyCountEqualityCheck($conditional, $min_count);
- $max_count = null;
- $count_inequality_position = self::hasLessThanCountEqualityCheck($conditional, $max_count);
- $isset_assert = false;
- $superior_value_comparison = null;
- $superior_value_position = self::hasSuperiorNumberCheck(
- $source,
- $conditional,
- $superior_value_comparison,
- $isset_assert
- );
-
- if ($count_equality_position) {
- if ($count_equality_position === self::ASSIGNMENT_TO_RIGHT) {
- $counted_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$count_equality_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $counted_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $counted_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if (self::hasReconcilableNonEmptyCountEqualityCheck($conditional)) {
- $if_types[$var_name] = [['non-empty-countable']];
- } else {
- if ($min_count) {
- $if_types[$var_name] = [['=has-at-least-' . $min_count]];
- } else {
- $if_types[$var_name] = [['=non-empty-countable']];
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($count_inequality_position) {
- if ($count_inequality_position === self::ASSIGNMENT_TO_LEFT) {
- $count_expr = $conditional->right;
- } else {
- throw new UnexpectedValueException('$count_inequality_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $count_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $count_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($max_count) {
- $if_types[$var_name] = [['!has-at-least-' . ($max_count + 1)]];
- } else {
- $if_types[$var_name] = [['!non-empty-countable']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($superior_value_position) {
- if ($superior_value_position === self::ASSIGNMENT_TO_RIGHT) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->left,
- $this_class_name,
- $source
- );
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->right,
- $this_class_name,
- $source
- );
- }
-
- if ($var_name !== null) {
- if ($superior_value_position === self::ASSIGNMENT_TO_RIGHT) {
- if ($superior_value_comparison === 0) {
- $if_types[$var_name] = [['=positive-numeric', '=int(0)']];
- } elseif ($superior_value_comparison === 1) {
- $if_types[$var_name] = [['positive-numeric']];
- } else {
- $if_types[$var_name] = [['>' . $superior_value_comparison]];
- }
- } else {
- $if_types[$var_name] = [['<' . $superior_value_comparison]];
- }
-
- if ($isset_assert) {
- $if_types[$var_name][] = ['=isset'];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- return [];
- }
-
- /**
- * @param PhpParser\Node\Expr\BinaryOp\Smaller|PhpParser\Node\Expr\BinaryOp\SmallerOrEqual $conditional
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getSmallerAssertions(
- PhpParser\Node\Expr $conditional,
- FileSource $source,
- ?string $this_class_name
- ): array {
- $if_types = [];
- $min_count = null;
- $count_equality_position = self::hasNonEmptyCountEqualityCheck($conditional, $min_count);
- $max_count = null;
- $count_inequality_position = self::hasLessThanCountEqualityCheck($conditional, $max_count);
- $isset_assert = false;
- $inferior_value_comparison = null;
- $inferior_value_position = self::hasInferiorNumberCheck(
- $source,
- $conditional,
- $inferior_value_comparison,
- $isset_assert
- );
-
- if ($count_equality_position) {
- if ($count_equality_position === self::ASSIGNMENT_TO_LEFT) {
- $count_expr = $conditional->right;
- } else {
- throw new UnexpectedValueException('$count_equality_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $count_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $count_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($min_count) {
- $if_types[$var_name] = [['=has-at-least-' . $min_count]];
- } else {
- $if_types[$var_name] = [['=non-empty-countable']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($count_inequality_position) {
- if ($count_inequality_position === self::ASSIGNMENT_TO_RIGHT) {
- $count_expr = $conditional->left;
- } else {
- throw new UnexpectedValueException('$count_inequality_position value');
- }
-
- /** @var PhpParser\Node\Expr\FuncCall $count_expr */
- $var_name = ExpressionIdentifier::getArrayVarId(
- $count_expr->getArgs()[0]->value,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- if ($max_count) {
- $if_types[$var_name] = [['!has-at-least-' . ($max_count + 1)]];
- } else {
- $if_types[$var_name] = [['!non-empty-countable']];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- if ($inferior_value_position) {
- if ($inferior_value_position === self::ASSIGNMENT_TO_RIGHT) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->left,
- $this_class_name,
- $source
- );
- } else {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->right,
- $this_class_name,
- $source
- );
- }
-
-
- if ($var_name !== null) {
- if ($inferior_value_position === self::ASSIGNMENT_TO_RIGHT) {
- $if_types[$var_name] = [['<' . $inferior_value_comparison]];
- } else {
- if ($inferior_value_comparison === 0) {
- $if_types[$var_name] = [['=positive-numeric', '=int(0)']];
- } elseif ($inferior_value_comparison === 1) {
- $if_types[$var_name] = [['positive-numeric']];
- } else {
- $if_types[$var_name] = [['>' . $inferior_value_comparison]];
- }
- }
-
- if ($isset_assert) {
- $if_types[$var_name][] = ['=isset'];
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- return [];
- }
-
- /**
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>
- */
- private static function getInstanceofAssertions(
- PhpParser\Node\Expr\Instanceof_ $conditional,
- ?Codebase $codebase,
- FileSource $source,
- ?string $this_class_name,
- bool $inside_negation
- ): array {
- $if_types = [];
-
- $instanceof_types = self::getInstanceOfTypes($conditional, $this_class_name, $source);
-
- if ($instanceof_types) {
- $var_name = ExpressionIdentifier::getArrayVarId(
- $conditional->expr,
- $this_class_name,
- $source
- );
-
- if ($var_name) {
- $if_types[$var_name] = [$instanceof_types];
-
- $var_type = $source instanceof StatementsAnalyzer
- ? $source->node_data->getType($conditional->expr)
- : null;
-
- foreach ($instanceof_types as $instanceof_type) {
- if ($instanceof_type[0] === '=') {
- $instanceof_type = substr($instanceof_type, 1);
- }
-
- if ($codebase
- && $var_type
- && $inside_negation
- && $source instanceof StatementsAnalyzer
- ) {
- if ($codebase->interfaceExists($instanceof_type)) {
- continue;
- }
-
- $instanceof_type = Type::parseString(
- $instanceof_type,
- null,
- $source->getTemplateTypeMap() ?: []
- );
-
- if (!UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $instanceof_type,
- $var_type
- )) {
- if ($var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- $var_type->getId() . ' does not contain '
- . $instanceof_type->getId(),
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' ' . $instanceof_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $var_type->getId() . ' cannot be identical to '
- . $instanceof_type->getId(),
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' ' . $instanceof_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
- }
- }
- }
-
- return $if_types ? [$if_types] : [];
- }
-
- /**
- * @param NotIdentical|NotEqual|Identical|Equal $conditional
- */
- private static function handleParadoxicalAssertions(
- StatementsAnalyzer $source,
- Union $var_type,
- ?string $this_class_name,
- Union $other_type,
- Codebase $codebase,
- PhpParser\Node\Expr\BinaryOp $conditional
- ): void {
- $parent_source = $source->getSource();
-
- if ($parent_source->getSource() instanceof TraitAnalyzer
- && (($var_type->isSingleStringLiteral()
- && $var_type->getSingleStringLiteral()->value === $this_class_name)
- || ($other_type->isSingleStringLiteral()
- && $other_type->getSingleStringLiteral()->value === $this_class_name))
- ) {
- // do nothing
- } elseif (!UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $other_type,
- $var_type
- )) {
- if ($var_type->from_docblock || $other_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- $var_type->getId() . ' does not contain ' . $other_type->getId(),
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' ' . $other_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- } else {
- if ($conditional instanceof NotEqual || $conditional instanceof NotIdentical) {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $var_type->getId() . ' can never contain ' . $other_type->getId(),
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' ' . $other_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- $var_type->getId() . ' cannot be identical to ' . $other_type->getId(),
- new CodeLocation($source, $conditional),
- $var_type->getId() . ' ' . $other_type->getId()
- ),
- $source->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- public static function isPropertyImmutableOnArgument(
- string $property,
- NodeDataProvider $node_provider,
- ClassLikeStorageProvider $class_provider,
- PhpParser\Node\Expr\Variable $arg_expr
- ): ?string {
- $type = $node_provider->getType($arg_expr);
- /** @var string $name */
- $name = $arg_expr->name;
-
- if (null === $type) {
- return 'Cannot resolve a type of variable ' . $name;
- }
-
- foreach ($type->getAtomicTypes() as $type) {
- if (!$type instanceof TNamedObject) {
- return 'Variable ' . $name . ' is not an object so the assertion cannot be applied';
- }
-
- $class_definition = $class_provider->get($type->value);
- $property_definition = $class_definition->properties[$property] ?? null;
-
- if (!$property_definition instanceof PropertyStorage) {
- $magic_type = $class_definition->pseudo_property_get_types['$' . $property] ?? null;
- if ($magic_type === null) {
- return sprintf(
- 'Property %s is not defined on variable %s so the assertion cannot be applied',
- $property,
- $name
- );
- }
-
- $magic_getter = $class_definition->methods['__get'] ?? null;
- if ($magic_getter === null || !$magic_getter->mutation_free) {
- return "{$class_definition->name}::__get is not mutation-free, so the assertion cannot be applied";
- }
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php
deleted file mode 100644
index a2eb2c6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/ArrayAssignmentAnalyzer.php
+++ /dev/null
@@ -1,1027 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Assignment;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Issue\InvalidArrayAssignment;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TDependentListKey;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateIndexedAccess;
-use Psalm\Type\Atomic\TTemplateKeyOf;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-
-use function array_pop;
-use function array_reverse;
-use function array_shift;
-use function array_slice;
-use function array_unshift;
-use function count;
-use function implode;
-use function in_array;
-use function is_string;
-use function preg_match;
-use function strlen;
-
-/**
- * @internal
- */
-class ArrayAssignmentAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Context $context,
- ?PhpParser\Node\Expr $assign_value,
- Union $assignment_value_type
- ): void {
- $nesting = 0;
- $var_id = ExpressionIdentifier::getVarId(
- $stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer,
- $nesting
- );
-
- self::updateArrayType(
- $statements_analyzer,
- $stmt,
- $assign_value,
- $assignment_value_type,
- $context
- );
-
- if (!$statements_analyzer->node_data->getType($stmt->var) && $var_id) {
- $context->vars_in_scope[$var_id] = Type::getMixed();
- }
- }
-
- /**
- * @return false|null
- * @psalm-suppress PossiblyUnusedReturnValue not used but seems important
- */
- public static function updateArrayType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- ?PhpParser\Node\Expr $assign_value,
- Union $assignment_type,
- Context $context
- ): ?bool {
- $root_array_expr = $stmt;
-
- $child_stmts = [];
-
- while ($root_array_expr->var instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- $child_stmts[] = $root_array_expr;
- $root_array_expr = $root_array_expr->var;
- }
-
- $child_stmts[] = $root_array_expr;
- $root_array_expr = $root_array_expr->var;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $root_array_expr,
- $context,
- true
- ) === false) {
- // fall through
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $root_type = $statements_analyzer->node_data->getType($root_array_expr) ?? Type::getMixed();
-
- if ($root_type->hasMixed()) {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $stmt->var,
- $context,
- true
- ) === false) {
- // fall through
- }
-
- if ($stmt->dim) {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $stmt->dim,
- $context
- ) === false) {
- // fall through
- }
- }
- }
-
- $current_type = $root_type;
-
- $current_dim = $stmt->dim;
-
- // gets a variable id that *may* contain array keys
- $root_var_id = ExpressionIdentifier::getArrayVarId(
- $root_array_expr,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $parent_var_id = null;
-
- $offset_already_existed = false;
-
- $child_stmt = null;
-
- self::analyzeNestedArrayAssignment(
- $statements_analyzer,
- $codebase,
- $context,
- $assign_value,
- $assignment_type,
- array_reverse($child_stmts),
- $root_var_id,
- $parent_var_id,
- $child_stmt,
- $root_type,
- $current_type,
- $current_dim,
- $offset_already_existed
- );
-
- $root_is_string = $root_type->isString();
- $key_values = [];
-
- if ($current_dim instanceof PhpParser\Node\Scalar\String_) {
- $key_values[] = new TLiteralString($current_dim->value);
- } elseif ($current_dim instanceof PhpParser\Node\Scalar\LNumber && !$root_is_string) {
- $key_values[] = new TLiteralInt($current_dim->value);
- } elseif ($current_dim
- && ($key_type = $statements_analyzer->node_data->getType($current_dim))
- && !$root_is_string
- ) {
- $string_literals = $key_type->getLiteralStrings();
- $int_literals = $key_type->getLiteralInts();
-
- $all_atomic_types = $key_type->getAtomicTypes();
-
- if (count($string_literals) + count($int_literals) === count($all_atomic_types)) {
- foreach ($string_literals as $string_literal) {
- $key_values[] = clone $string_literal;
- }
-
- foreach ($int_literals as $int_literal) {
- $key_values[] = clone $int_literal;
- }
- }
- }
-
- if ($key_values) {
- $new_child_type = self::updateTypeWithKeyValues(
- $codebase,
- $root_type,
- $current_type,
- $key_values
- );
- } elseif (!$root_is_string) {
- $new_child_type = self::updateArrayAssignmentChildType(
- $statements_analyzer,
- $codebase,
- $current_dim,
- $context,
- $current_type,
- $root_type,
- $offset_already_existed,
- $child_stmt,
- $parent_var_id
- );
- } else {
- $new_child_type = $root_type;
- }
-
- $new_child_type->removeType('null');
-
- if (!$root_type->hasObjectType()) {
- $root_type = $new_child_type;
- }
-
- $statements_analyzer->node_data->setType($root_array_expr, $root_type);
-
- if ($root_array_expr instanceof PhpParser\Node\Expr\PropertyFetch) {
- if ($root_array_expr->name instanceof PhpParser\Node\Identifier) {
- InstancePropertyAssignmentAnalyzer::analyze(
- $statements_analyzer,
- $root_array_expr,
- $root_array_expr->name->name,
- null,
- $root_type,
- $context,
- false
- );
- } else {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $root_array_expr->name, $context) === false) {
- return false;
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $root_array_expr->var, $context) === false) {
- return false;
- }
- }
- } elseif ($root_array_expr instanceof PhpParser\Node\Expr\StaticPropertyFetch
- && $root_array_expr->name instanceof PhpParser\Node\Identifier
- ) {
- if (StaticPropertyAssignmentAnalyzer::analyze(
- $statements_analyzer,
- $root_array_expr,
- null,
- $root_type,
- $context
- ) === false) {
- return false;
- }
- } elseif ($root_var_id) {
- $context->vars_in_scope[$root_var_id] = $root_type;
- }
-
- if ($root_array_expr instanceof PhpParser\Node\Expr\MethodCall
- || $root_array_expr instanceof PhpParser\Node\Expr\StaticCall
- || $root_array_expr instanceof PhpParser\Node\Expr\FuncCall
- ) {
- if ($root_type->hasArray()) {
- if (IssueBuffer::accepts(
- new InvalidArrayAssignment(
- 'Assigning to the output of a function has no effect',
- new CodeLocation($statements_analyzer->getSource(), $root_array_expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // do nothing
- }
- }
- }
-
- return null;
- }
-
- /**
- * @param non-empty-list<TLiteralInt|TLiteralString> $key_values
- */
- private static function updateTypeWithKeyValues(
- Codebase $codebase,
- Union $child_stmt_type,
- Union $current_type,
- array $key_values
- ): Union {
- $has_matching_objectlike_property = false;
- $has_matching_string = false;
-
- $child_stmt_type = clone $child_stmt_type;
-
- foreach ($child_stmt_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- $type->as = self::updateTypeWithKeyValues(
- $codebase,
- $type->as,
- $current_type,
- $key_values
- );
-
- $has_matching_objectlike_property = true;
-
- $child_stmt_type->substitute(new Union([$type]), $type->as);
-
- continue;
- }
-
- foreach ($key_values as $key_value) {
- if ($type instanceof TKeyedArray) {
- if (isset($type->properties[$key_value->value])) {
- $has_matching_objectlike_property = true;
-
- $type->properties[$key_value->value] = clone $current_type;
- }
- } elseif ($type instanceof TString
- && $key_value instanceof TLiteralInt
- ) {
- $has_matching_string = true;
-
- if ($type instanceof TLiteralString
- && $current_type->isSingleStringLiteral()
- ) {
- $new_char = $current_type->getSingleStringLiteral()->value;
-
- if (strlen($new_char) === 1) {
- $type->value[0] = $new_char;
- }
- }
- } elseif ($type instanceof TNonEmptyList
- && $key_value instanceof TLiteralInt
- && count($key_values) === 1
- ) {
- $has_matching_objectlike_property = true;
-
- $type->type_param = Type::combineUnionTypes(
- clone $current_type,
- $type->type_param,
- $codebase,
- true,
- false
- );
- }
- }
- }
-
- $child_stmt_type->bustCache();
-
- if (!$has_matching_objectlike_property && !$has_matching_string) {
- if (count($key_values) === 1) {
- $key_value = $key_values[0];
-
- $object_like = new TKeyedArray(
- [$key_value->value => clone $current_type],
- $key_value instanceof TLiteralClassString
- ? [$key_value->value => true]
- : null
- );
-
- $object_like->sealed = true;
-
- $array_assignment_type = new Union([
- $object_like,
- ]);
- } else {
- $array_assignment_literals = $key_values;
-
- $array_assignment_type = new Union([
- new TNonEmptyArray([
- new Union($array_assignment_literals),
- clone $current_type
- ])
- ]);
- }
-
- return Type::combineUnionTypes(
- $child_stmt_type,
- $array_assignment_type,
- $codebase,
- true,
- false
- );
- }
-
- return $child_stmt_type;
- }
-
- /**
- * @param list<TLiteralInt|TLiteralString> $key_values $key_values
- */
- private static function taintArrayAssignment(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $expr,
- Union $stmt_type,
- Union $child_stmt_type,
- ?string $var_var_id,
- array $key_values
- ): void {
- if ($statements_analyzer->data_flow_graph
- && ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- || !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues()))
- ) {
- $var_location = new CodeLocation($statements_analyzer->getSource(), $expr->var);
-
- $parent_node = DataFlowNode::getForAssignment(
- $var_var_id ?: 'assignment',
- $var_location
- );
-
- $statements_analyzer->data_flow_graph->addNode($parent_node);
-
- $old_parent_nodes = $stmt_type->parent_nodes;
-
- $stmt_type->parent_nodes = [$parent_node->id => $parent_node];
-
- foreach ($old_parent_nodes as $old_parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $old_parent_node,
- $parent_node,
- '='
- );
-
- if ($stmt_type->by_ref) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $old_parent_node,
- '='
- );
- }
- }
-
- foreach ($stmt_type->parent_nodes as $parent_node) {
- foreach ($child_stmt_type->parent_nodes as $child_parent_node) {
- if ($key_values) {
- foreach ($key_values as $key_value) {
- $statements_analyzer->data_flow_graph->addPath(
- $child_parent_node,
- $parent_node,
- 'arrayvalue-assignment-\'' . $key_value->value . '\''
- );
- }
- } else {
- $statements_analyzer->data_flow_graph->addPath(
- $child_parent_node,
- $parent_node,
- 'arrayvalue-assignment'
- );
- }
- }
- }
- }
- }
-
- private static function updateArrayAssignmentChildType(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- ?PhpParser\Node\Expr $current_dim,
- Context $context,
- Union $value_type,
- Union $root_type,
- bool $offset_already_existed,
- ?PhpParser\Node\Expr $child_stmt,
- ?string $parent_var_id
- ): Union {
- $templated_assignment = false;
-
- if ($current_dim) {
- $key_type = $statements_analyzer->node_data->getType($current_dim);
-
- if ($key_type) {
- if ($key_type->hasMixed()) {
- $key_type = Type::getArrayKey();
- }
-
- if ($key_type->isSingle()) {
- $key_type_type = $key_type->getSingleAtomic();
-
- if ($key_type_type instanceof TDependentListKey
- && $key_type_type->getVarId() === $parent_var_id
- ) {
- $offset_already_existed = true;
- }
-
- if ($key_type_type instanceof TTemplateParam
- && $key_type_type->as->isSingle()
- && $root_type->isSingle()
- && $value_type->isSingle()
- ) {
- $key_type_as_type = $key_type_type->as->getSingleAtomic();
- $value_atomic_type = $value_type->getSingleAtomic();
- $root_atomic_type = $root_type->getSingleAtomic();
-
- if ($key_type_as_type instanceof TTemplateKeyOf
- && $root_atomic_type instanceof TTemplateParam
- && $value_atomic_type instanceof TTemplateIndexedAccess
- && $key_type_as_type->param_name === $root_atomic_type->param_name
- && $key_type_as_type->defining_class === $root_atomic_type->defining_class
- && $value_atomic_type->array_param_name === $root_atomic_type->param_name
- && $value_atomic_type->offset_param_name === $key_type_type->param_name
- && $value_atomic_type->defining_class === $root_atomic_type->defining_class
- ) {
- $templated_assignment = true;
- $offset_already_existed = true;
- }
- }
- }
-
- $array_atomic_key_type = ArrayFetchAnalyzer::replaceOffsetTypeWithInts(
- $key_type
- );
- } else {
- $array_atomic_key_type = Type::getArrayKey();
- }
-
- if ($offset_already_existed
- && $child_stmt
- && $parent_var_id
- && ($parent_type = $context->vars_in_scope[$parent_var_id] ?? null)
- ) {
- if ($parent_type->hasList()) {
- $array_atomic_type = new TNonEmptyList(
- $value_type
- );
- } elseif ($parent_type->hasClassStringMap()
- && $key_type
- && $key_type->isTemplatedClassString()
- ) {
- /**
- * @var TClassStringMap
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- */
- $class_string_map = $parent_type->getAtomicTypes()['array'];
- /**
- * @var TTemplateParamClass
- */
- $offset_type_part = $key_type->getSingleAtomic();
-
- $template_result = new TemplateResult(
- [],
- [
- $offset_type_part->param_name => [
- $offset_type_part->defining_class => new Union([
- new TTemplateParam(
- $class_string_map->param_name,
- $offset_type_part->as_type
- ? new Union([$offset_type_part->as_type])
- : Type::getObject(),
- 'class-string-map'
- )
- ])
- ]
- ]
- );
-
- TemplateInferredTypeReplacer::replace(
- $value_type,
- $template_result,
- $codebase
- );
-
- $array_atomic_type = new TClassStringMap(
- $class_string_map->param_name,
- $class_string_map->as_type,
- $value_type
- );
- } else {
- $array_atomic_type = new TNonEmptyArray([
- $array_atomic_key_type,
- $value_type,
- ]);
- }
- } else {
- $array_atomic_type = new TNonEmptyArray([
- $array_atomic_key_type,
- $value_type,
- ]);
- }
- } else {
- $array_atomic_type = new TNonEmptyList($value_type);
- }
-
- $from_countable_object_like = false;
-
- $new_child_type = null;
-
- if (!$current_dim && !$context->inside_loop) {
- $atomic_root_types = $root_type->getAtomicTypes();
-
- if (isset($atomic_root_types['array'])) {
- if ($array_atomic_type instanceof TClassStringMap) {
- $array_atomic_type = new TNonEmptyArray([
- $array_atomic_type->getStandinKeyParam(),
- $array_atomic_type->value_param
- ]);
- } elseif ($atomic_root_types['array'] instanceof TNonEmptyArray
- || $atomic_root_types['array'] instanceof TNonEmptyList
- ) {
- $array_atomic_type->count = $atomic_root_types['array']->count;
- } elseif ($atomic_root_types['array'] instanceof TKeyedArray
- && $atomic_root_types['array']->sealed
- ) {
- $array_atomic_type->count = count($atomic_root_types['array']->properties);
- $from_countable_object_like = true;
-
- if ($atomic_root_types['array']->is_list
- && $array_atomic_type instanceof TList
- ) {
- $array_atomic_type = clone $atomic_root_types['array'];
-
- $new_child_type = new Union([$array_atomic_type]);
-
- $new_child_type->parent_nodes = $root_type->parent_nodes;
- }
- } elseif ($array_atomic_type instanceof TList) {
- $array_atomic_type = new TNonEmptyList(
- $array_atomic_type->type_param
- );
- } else {
- $array_atomic_type = new TNonEmptyArray(
- $array_atomic_type->type_params
- );
- }
- }
- }
-
- $array_assignment_type = new Union([
- $array_atomic_type,
- ]);
-
- if (!$new_child_type) {
- if ($templated_assignment) {
- $new_child_type = $root_type;
- } else {
- $new_child_type = Type::combineUnionTypes(
- $root_type,
- $array_assignment_type,
- $codebase,
- true,
- true
- );
- }
- }
-
- if ($from_countable_object_like) {
- $atomic_root_types = $new_child_type->getAtomicTypes();
-
- if (isset($atomic_root_types['array'])
- && ($atomic_root_types['array'] instanceof TNonEmptyArray
- || $atomic_root_types['array'] instanceof TNonEmptyList)
- && $atomic_root_types['array']->count !== null
- ) {
- $atomic_root_types['array']->count++;
- }
- }
-
- return $new_child_type;
- }
-
- /**
- * @param array<PhpParser\Node\Expr\ArrayDimFetch> $child_stmts
- */
- private static function analyzeNestedArrayAssignment(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- Context $context,
- ?PhpParser\Node\Expr $assign_value,
- Union $assignment_type,
- array $child_stmts,
- ?string $root_var_id,
- ?string &$parent_var_id,
- ?PhpParser\Node\Expr &$child_stmt,
- Union &$root_type,
- Union &$current_type,
- ?PhpParser\Node\Expr &$current_dim,
- bool &$offset_already_existed
- ): void {
- $reversed_child_stmts = [];
- $var_id_additions = [];
- $full_var_id = true;
-
- $child_stmt = null;
-
- // First go from the root element up, and go as far as we can to figure out what
- // array types there are
- while ($child_stmts) {
- $child_stmt = array_shift($child_stmts);
-
- if (count($child_stmts)) {
- array_unshift($reversed_child_stmts, $child_stmt);
- }
-
- $child_stmt_dim_type = null;
-
- $offset_type = null;
-
- if ($child_stmt->dim) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $child_stmt->dim,
- $context
- ) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return;
- }
-
- $context->inside_general_use = $was_inside_general_use;
-
- if (!($child_stmt_dim_type = $statements_analyzer->node_data->getType($child_stmt->dim))) {
- return;
- }
-
- [$offset_type, $var_id_addition, $full_var_id] = self::getArrayAssignmentOffsetType(
- $statements_analyzer,
- $child_stmt,
- $child_stmt_dim_type
- );
-
- $var_id_additions[] = $var_id_addition;
- } else {
- $var_id_additions[] = '';
- $full_var_id = false;
- }
-
- if (!($child_stmt_var_type = $statements_analyzer->node_data->getType($child_stmt->var))) {
- return;
- }
-
- if ($child_stmt_var_type->isEmpty()) {
- $child_stmt_var_type = Type::getEmptyArray();
- $statements_analyzer->node_data->setType($child_stmt->var, $child_stmt_var_type);
- }
-
- $array_var_id = $root_var_id . implode('', $var_id_additions);
-
- if ($parent_var_id && isset($context->vars_in_scope[$parent_var_id])) {
- $child_stmt_var_type = clone $context->vars_in_scope[$parent_var_id];
- $statements_analyzer->node_data->setType($child_stmt->var, $child_stmt_var_type);
- }
-
- $array_type = clone $child_stmt_var_type;
-
- $child_stmt_type = ArrayFetchAnalyzer::getArrayAccessTypeGivenOffset(
- $statements_analyzer,
- $child_stmt,
- $array_type,
- $child_stmt_dim_type ?? Type::getInt(),
- true,
- $array_var_id,
- $context,
- $assign_value,
- $child_stmts ? null : $assignment_type
- );
-
- $statements_analyzer->node_data->setType(
- $child_stmt,
- $child_stmt_type
- );
-
- $statements_analyzer->node_data->setType($child_stmt->var, $array_type);
-
- if ($root_var_id) {
- if (!$parent_var_id) {
- $rooted_parent_id = $root_var_id;
- $root_type = $array_type;
- } else {
- $rooted_parent_id = $parent_var_id;
- }
-
- $context->vars_in_scope[$rooted_parent_id] = $array_type;
- $context->possibly_assigned_var_ids[$rooted_parent_id] = true;
- }
-
- if (!$child_stmts) {
- // we need this slight hack as the type we're putting it has to be
- // different from the type we're getting out
- if ($array_type->isSingle() && $array_type->hasClassStringMap()) {
- $assignment_type = $child_stmt_type;
- }
-
- $child_stmt_type = $assignment_type;
- $statements_analyzer->node_data->setType($child_stmt, $assignment_type);
-
- if ($statements_analyzer->data_flow_graph) {
- self::taintArrayAssignment(
- $statements_analyzer,
- $child_stmt,
- $array_type,
- $assignment_type,
- ExpressionIdentifier::getArrayVarId(
- $child_stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- ),
- $offset_type !== null ? [$offset_type] : []
- );
- }
- }
-
- $current_type = $child_stmt_type;
- $current_dim = $child_stmt->dim;
-
- $parent_var_id = $array_var_id;
- }
-
- if ($root_var_id
- && $full_var_id
- && $child_stmt
- && ($child_stmt_var_type = $statements_analyzer->node_data->getType($child_stmt->var))
- && !$child_stmt_var_type->hasObjectType()
- ) {
- $array_var_id = $root_var_id . implode('', $var_id_additions);
- $parent_var_id = $root_var_id . implode('', array_slice($var_id_additions, 0, -1));
-
- if (isset($context->vars_in_scope[$array_var_id])
- && !$context->vars_in_scope[$array_var_id]->possibly_undefined
- ) {
- $offset_already_existed = true;
- }
-
- $context->vars_in_scope[$array_var_id] = clone $assignment_type;
- $context->possibly_assigned_var_ids[$array_var_id] = true;
- }
-
- // only update as many child stmts are we were able to process above
- foreach ($reversed_child_stmts as $child_stmt) {
- $child_stmt_type = $statements_analyzer->node_data->getType($child_stmt);
-
- if (!$child_stmt_type) {
- throw new InvalidArgumentException('Should never get here');
- }
-
- $key_values = [];
-
- if ($current_dim instanceof PhpParser\Node\Scalar\String_) {
- $key_values[] = new TLiteralString($current_dim->value);
- } elseif ($current_dim instanceof PhpParser\Node\Scalar\LNumber) {
- $key_values[] = new TLiteralInt($current_dim->value);
- } elseif ($current_dim
- && ($key_type = $statements_analyzer->node_data->getType($current_dim))
- ) {
- $string_literals = $key_type->getLiteralStrings();
- $int_literals = $key_type->getLiteralInts();
-
- $all_atomic_types = $key_type->getAtomicTypes();
-
- if (count($string_literals) + count($int_literals) === count($all_atomic_types)) {
- foreach ($string_literals as $string_literal) {
- $key_values[] = clone $string_literal;
- }
-
- foreach ($int_literals as $int_literal) {
- $key_values[] = clone $int_literal;
- }
- }
- }
-
- if ($key_values) {
- $new_child_type = self::updateTypeWithKeyValues(
- $codebase,
- $child_stmt_type,
- $current_type,
- $key_values
- );
- } else {
- if (!$current_dim) {
- $array_assignment_type = new Union([
- new TList($current_type),
- ]);
- } else {
- $key_type = $statements_analyzer->node_data->getType($current_dim);
-
- $array_assignment_type = new Union([
- new TArray([
- $key_type && !$key_type->hasMixed()
- ? $key_type
- : Type::getArrayKey(),
- $current_type,
- ]),
- ]);
- }
-
- $new_child_type = Type::combineUnionTypes(
- $child_stmt_type,
- $array_assignment_type,
- $codebase,
- true,
- true
- );
- }
-
- $new_child_type->removeType('null');
- $new_child_type->possibly_undefined = false;
-
- if (!$child_stmt_type->hasObjectType()) {
- $child_stmt_type = $new_child_type;
- $statements_analyzer->node_data->setType($child_stmt, $new_child_type);
- }
-
- $current_type = $child_stmt_type;
- $current_dim = $child_stmt->dim;
-
- array_pop($var_id_additions);
-
- $parent_array_var_id = null;
-
- if ($root_var_id) {
- $array_var_id = $root_var_id . implode('', $var_id_additions);
- $parent_array_var_id = $root_var_id . implode('', array_slice($var_id_additions, 0, -1));
- $context->vars_in_scope[$array_var_id] = clone $child_stmt_type;
- $context->possibly_assigned_var_ids[$array_var_id] = true;
- }
-
- if ($statements_analyzer->data_flow_graph) {
- self::taintArrayAssignment(
- $statements_analyzer,
- $child_stmt,
- $statements_analyzer->node_data->getType($child_stmt->var) ?? Type::getMixed(),
- $new_child_type,
- $parent_array_var_id,
- $key_values
- );
- }
- }
- }
-
- /**
- * @return array{TLiteralInt|TLiteralString|null, string, bool}
- */
- private static function getArrayAssignmentOffsetType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $child_stmt,
- Union $child_stmt_dim_type
- ): array {
- if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_
- || (($child_stmt->dim instanceof PhpParser\Node\Expr\ConstFetch
- || $child_stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch)
- && $child_stmt_dim_type->isSingleStringLiteral())
- ) {
- if ($child_stmt->dim instanceof PhpParser\Node\Scalar\String_) {
- $offset_type = new TLiteralString($child_stmt->dim->value);
- } else {
- $offset_type = $child_stmt_dim_type->getSingleStringLiteral();
- }
-
- if (preg_match('/^(0|[1-9][0-9]*)$/', $offset_type->value)) {
- $var_id_addition = '[' . $offset_type->value . ']';
- } else {
- $var_id_addition = '[\'' . $offset_type->value . '\']';
- }
-
- return [$offset_type, $var_id_addition, true];
- }
-
- if ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber
- || (($child_stmt->dim instanceof PhpParser\Node\Expr\ConstFetch
- || $child_stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch)
- && $child_stmt_dim_type->isSingleIntLiteral())
- ) {
- if ($child_stmt->dim instanceof PhpParser\Node\Scalar\LNumber) {
- $offset_type = new TLiteralInt($child_stmt->dim->value);
- } else {
- $offset_type = $child_stmt_dim_type->getSingleIntLiteral();
- }
-
- $var_id_addition = '[' . $offset_type->value . ']';
-
- return [$offset_type, $var_id_addition, true];
- }
-
- if ($child_stmt->dim instanceof PhpParser\Node\Expr\Variable
- && is_string($child_stmt->dim->name)
- ) {
- $var_id_addition = '[$' . $child_stmt->dim->name . ']';
-
- return [null, $var_id_addition, true];
- }
-
- if ($child_stmt->dim instanceof PhpParser\Node\Expr\PropertyFetch
- && $child_stmt->dim->name instanceof PhpParser\Node\Identifier
- ) {
- $object_id = ExpressionIdentifier::getArrayVarId(
- $child_stmt->dim->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($object_id) {
- $var_id_addition = '[' . $object_id . '->' . $child_stmt->dim->name->name . ']';
- } else {
- $var_id_addition = '[' . $child_stmt_dim_type . ']';
- }
-
- return [null, $var_id_addition, true];
- }
-
- if ($child_stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch
- && $child_stmt->dim->name instanceof PhpParser\Node\Identifier
- && $child_stmt->dim->class instanceof PhpParser\Node\Name
- ) {
- $object_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $child_stmt->dim->class,
- $statements_analyzer->getAliases()
- );
- $var_id_addition = '[' . $object_name . '::' . $child_stmt->dim->name->name . ']';
-
- return [null, $var_id_addition, true];
- }
-
- $var_id_addition = '[' . $child_stmt_dim_type . ']';
-
- return [null, $var_id_addition, false];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php
deleted file mode 100644
index 9756e4f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/AssignedProperty.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Assignment;
-
-use Psalm\Type\Union;
-
-class AssignedProperty
-{
- /**
- * @var Union
- */
- public $property_type;
-
- /**
- * @var string
- */
- public $id;
-
- /**
- * @var Union
- */
- public $assignment_type;
-
- public function __construct(
- Union $property_type,
- string $id,
- Union $assignment_type
- ) {
- $this->property_type = $property_type;
- $this->id = $id;
- $this->assignment_type = $assignment_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php
deleted file mode 100644
index f5ebf74..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php
+++ /dev/null
@@ -1,1540 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Assignment;
-
-use PhpParser;
-use PhpParser\Node\Expr;
-use PhpParser\Node\Expr\PropertyFetch;
-use PhpParser\Node\Stmt\PropertyProperty;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ClassAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\Methods;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\DeprecatedProperty;
-use Psalm\Issue\ImplicitToStringCast;
-use Psalm\Issue\ImpurePropertyAssignment;
-use Psalm\Issue\InaccessibleProperty;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\InternalProperty;
-use Psalm\Issue\InvalidPropertyAssignment;
-use Psalm\Issue\InvalidPropertyAssignmentValue;
-use Psalm\Issue\LoopInvalidation;
-use Psalm\Issue\MixedAssignment;
-use Psalm\Issue\MixedPropertyAssignment;
-use Psalm\Issue\MixedPropertyTypeCoercion;
-use Psalm\Issue\NoInterfaceProperties;
-use Psalm\Issue\NullPropertyAssignment;
-use Psalm\Issue\PossiblyFalsePropertyAssignmentValue;
-use Psalm\Issue\PossiblyInvalidPropertyAssignment;
-use Psalm\Issue\PossiblyInvalidPropertyAssignmentValue;
-use Psalm\Issue\PossiblyNullPropertyAssignment;
-use Psalm\Issue\PossiblyNullPropertyAssignmentValue;
-use Psalm\Issue\PropertyTypeCoercion;
-use Psalm\Issue\UndefinedClass;
-use Psalm\Issue\UndefinedMagicPropertyAssignment;
-use Psalm\Issue\UndefinedPropertyAssignment;
-use Psalm\Issue\UndefinedThisPropertyAssignment;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\Scalar\VirtualString;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\PropertyStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_pop;
-use function count;
-use function in_array;
-use function reset;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class InstancePropertyAssignmentAnalyzer
-{
- /**
- * @param PropertyFetch|PropertyProperty $stmt
- * @param bool $direct_assignment whether the variable is assigned explicitly
- *
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\NodeAbstract $stmt,
- string $prop_name,
- ?PhpParser\Node\Expr $assignment_value,
- Union $assignment_value_type,
- Context $context,
- bool $direct_assignment = true
- ): ?bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if ($stmt instanceof PropertyProperty) {
- if (!$context->self || !$stmt->default) {
- return null;
- }
-
- $property_id = $context->self . '::$' . $prop_name;
-
- $class_property_type = null;
-
- try {
- $class_property_type = $codebase->properties->getPropertyType(
- $property_id,
- true,
- $statements_analyzer,
- $context
- );
- } catch (UnexpectedValueException $e) {
- // do nothing
- }
-
- if ($class_property_type) {
- $class_storage = $codebase->classlike_storage_provider->get($context->self);
-
- $class_property_type = self::getExpandedPropertyType(
- $codebase,
- $context->self,
- $prop_name,
- $class_storage
- );
- }
-
- $var_id = '$this->' . $prop_name;
-
- $assigned_properties = [
- new AssignedProperty(
- $class_property_type ?? Type::getMixed(),
- $property_id,
- $assignment_value_type
- )
- ];
- } else {
- $assigned_properties = self::analyzeRegularAssignment(
- $statements_analyzer,
- $stmt,
- $assignment_value,
- $context,
- $direct_assignment,
- $codebase,
- $assignment_value_type,
- $prop_name,
- $var_id
- );
- }
-
- if (!$assigned_properties) {
- return null;
- }
-
- if ($assignment_value_type->hasMixed()) {
- return null;
- }
-
- $invalid_assignment_value_types = [];
-
- $has_valid_assignment_value_type = false;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- && count($assigned_properties) === 1
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $assigned_properties[0]->property_type->getId()
- );
- }
-
- foreach ($assigned_properties as $assigned_property) {
- $class_property_type = $assigned_property->property_type;
- $assignment_type = $assigned_property->assignment_type;
-
- if ($class_property_type->hasMixed()) {
- continue;
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- $type_match_found = UnionTypeComparator::isContainedBy(
- $codebase,
- $assignment_type,
- $class_property_type,
- true,
- true,
- $union_comparison_results
- );
-
- if ($type_match_found && $union_comparison_results->replacement_union_type) {
- if ($var_id) {
- $context->vars_in_scope[$var_id] = $union_comparison_results->replacement_union_type;
- }
- }
-
- if ($union_comparison_results->type_coerced) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- IssueBuffer::maybeAdd(
- new MixedPropertyTypeCoercion(
- $var_id . ' expects \'' . $class_property_type->getId() . '\', '
- . ' parent type `' . $assignment_type->getId() . '` provided',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $assigned_property->id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new PropertyTypeCoercion(
- $var_id . ' expects \'' . $class_property_type->getId() . '\', '
- . ' parent type \'' . $assignment_type->getId() . '\' provided',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $assigned_property->id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($union_comparison_results->to_string_cast) {
- IssueBuffer::maybeAdd(
- new ImplicitToStringCast(
- $var_id . ' expects \'' . $class_property_type . '\', '
- . '\'' . $assignment_type . '\' provided with a __toString method',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- )
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$type_match_found && !$union_comparison_results->type_coerced) {
- if (UnionTypeComparator::canBeContainedBy(
- $codebase,
- $assignment_type,
- $class_property_type,
- true,
- true
- )) {
- $has_valid_assignment_value_type = true;
- }
-
- $invalid_assignment_value_types[$assigned_property->id] = $class_property_type->getId();
- } else {
- $has_valid_assignment_value_type = true;
- }
-
- if ($type_match_found) {
- if (!$assignment_type->ignore_nullable_issues
- && $assignment_type->isNullable()
- && !$class_property_type->isNullable()
- ) {
- if (IssueBuffer::accepts(
- new PossiblyNullPropertyAssignmentValue(
- $var_id . ' with non-nullable declared type \'' . $class_property_type .
- '\' cannot be assigned nullable type \'' . $assignment_type . '\'',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $assigned_property->id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
-
- if (!$assignment_type->ignore_falsable_issues
- && $assignment_type->isFalsable()
- && !$class_property_type->hasBool()
- && !$class_property_type->hasScalar()
- ) {
- if (IssueBuffer::accepts(
- new PossiblyFalsePropertyAssignmentValue(
- $var_id . ' with non-falsable declared type \'' . $class_property_type .
- '\' cannot be assigned possibly false type \'' . $assignment_type . '\'',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $assigned_property->id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
- }
- }
-
- foreach ($invalid_assignment_value_types as $property_id => $invalid_class_property_type) {
- if (!$has_valid_assignment_value_type) {
- if (IssueBuffer::accepts(
- new InvalidPropertyAssignmentValue(
- $var_id . ' with declared type \'' . $invalid_class_property_type .
- '\' cannot be assigned type \'' . $assignment_value_type->getId() . '\'',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- } else {
- if (IssueBuffer::accepts(
- new PossiblyInvalidPropertyAssignmentValue(
- $var_id . ' with declared type \'' . $invalid_class_property_type .
- '\' cannot be assigned possibly different type \'' .
- $assignment_value_type->getId() . '\'',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
- }
-
- return null;
- }
-
- public static function trackPropertyImpurity(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- string $property_id,
- PropertyStorage $property_storage,
- ClassLikeStorage $declaring_class_storage,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- $stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
-
- $property_var_pure_compatible = $stmt_var_type
- && $stmt_var_type->reference_free
- && $stmt_var_type->allow_mutations;
-
- $appearing_property_class = $codebase->properties->getAppearingClassForProperty(
- $property_id,
- true
- );
-
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- if ($appearing_property_class && ($property_storage->readonly || $codebase->alter_code)) {
- $can_set_readonly_property = $context->self
- && $context->calling_method_id
- && ($appearing_property_class === $context->self
- || $codebase->classExtends($context->self, $appearing_property_class))
- && (strpos($context->calling_method_id, '::__construct')
- || strpos($context->calling_method_id, '::unserialize')
- || strpos($context->calling_method_id, '::__unserialize')
- || strpos($context->calling_method_id, '::__clone')
- || $property_storage->allow_private_mutation
- || $property_var_pure_compatible);
-
- if (!$can_set_readonly_property) {
- if ($property_storage->readonly) {
- IssueBuffer::maybeAdd(
- new InaccessibleProperty(
- $property_id . ' is marked readonly',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif (!$declaring_class_storage->mutation_free
- && isset($project_analyzer->getIssuesToFix()['MissingImmutableAnnotation'])
- && $statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- ) {
- $codebase->analyzer->addMutableClass($declaring_class_storage->name);
- }
- }
- }
- }
-
- public static function analyzeStatement(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Property $stmt,
- Context $context
- ): void {
- foreach ($stmt->props as $prop) {
- if ($prop->default) {
- if ($stmt->isReadonly()) {
- IssueBuffer::maybeAdd(
- new InvalidPropertyAssignment(
- 'Readonly property ' . $context->self . '::$' . $prop->name->name
- . ' cannot have a default',
- new CodeLocation($statements_analyzer->getSource(), $prop->default)
- )
- );
- }
-
- ExpressionAnalyzer::analyze($statements_analyzer, $prop->default, $context);
-
- if ($prop_default_type = $statements_analyzer->node_data->getType($prop->default)) {
- if (self::analyze(
- $statements_analyzer,
- $prop,
- $prop->name->name,
- $prop->default,
- $prop_default_type,
- $context
- ) === false) {
- // fall through
- }
- }
- }
- }
- }
-
- private static function taintProperty(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- string $property_id,
- ClassLikeStorage $class_storage,
- Union $assignment_value_type,
- Context $context
- ): void {
- if (!$statements_analyzer->data_flow_graph) {
- return;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $data_flow_graph = $statements_analyzer->data_flow_graph;
-
- $var_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var);
- $property_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- if ($class_storage->specialize_instance) {
- $var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->var,
- null,
- $statements_analyzer
- );
-
- $var_property_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- null,
- $statements_analyzer
- );
-
- if ($var_id) {
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $context->vars_in_scope[$var_id]->parent_nodes = [];
- return;
- }
-
- $var_node = DataFlowNode::getForAssignment(
- $var_id,
- $var_location
- );
-
- $data_flow_graph->addNode($var_node);
-
- $property_node = DataFlowNode::getForAssignment(
- $var_property_id ?: $var_id . '->$property',
- $property_location
- );
-
- $data_flow_graph->addNode($property_node);
-
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- $data_flow_graph->addPath(
- $property_node,
- $var_node,
- 'property-assignment'
- . ($stmt->name instanceof PhpParser\Node\Identifier ? '-' . $stmt->name : ''),
- $added_taints,
- $removed_taints
- );
-
- if ($assignment_value_type->parent_nodes) {
- foreach ($assignment_value_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath($parent_node, $property_node, '=', $added_taints, $removed_taints);
- }
- }
-
- $stmt_var_type = clone $context->vars_in_scope[$var_id];
-
- if ($context->vars_in_scope[$var_id]->parent_nodes) {
- foreach ($context->vars_in_scope[$var_id]->parent_nodes as $parent_node) {
- $data_flow_graph->addPath($parent_node, $var_node, '=', $added_taints, $removed_taints);
- }
- }
-
- $stmt_var_type->parent_nodes = [$var_node->id => $var_node];
-
- $context->vars_in_scope[$var_id] = $stmt_var_type;
- }
- } else {
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $assignment_value_type->parent_nodes = [];
- return;
- }
-
- $var_property_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- null,
- $statements_analyzer
- );
-
- $localized_property_node = DataFlowNode::getForAssignment(
- $var_property_id
- ?: $property_id . '-' . $property_location->file_name . ':' . $property_location->raw_file_start,
- $property_location
- );
-
- $data_flow_graph->addNode($localized_property_node);
-
- $property_node = new DataFlowNode(
- $property_id,
- $property_id,
- null,
- null
- );
-
- $data_flow_graph->addNode($property_node);
-
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- $data_flow_graph->addPath(
- $localized_property_node,
- $property_node,
- 'property-assignment',
- $added_taints,
- $removed_taints
- );
-
- if ($assignment_value_type->parent_nodes) {
- foreach ($assignment_value_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath(
- $parent_node,
- $localized_property_node,
- '=',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $property_id,
- false,
- $statements_analyzer
- );
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $declaring_property_class
- && $declaring_property_class !== $class_storage->name
- && $stmt->name instanceof PhpParser\Node\Identifier
- ) {
- $declaring_property_node = new DataFlowNode(
- $declaring_property_class . '::$' . $stmt->name,
- $declaring_property_class . '::$' . $stmt->name,
- null,
- null
- );
-
- $data_flow_graph->addNode($declaring_property_node);
-
- $data_flow_graph->addPath(
- $property_node,
- $declaring_property_node,
- 'property-assignment',
- $added_taints,
- $removed_taints
- );
- }
- }
- }
-
- /**
- * @return list<AssignedProperty>
- */
- private static function analyzeRegularAssignment(
- StatementsAnalyzer $statements_analyzer,
- PropertyFetch $stmt,
- ?PhpParser\Node\Expr $assignment_value,
- Context $context,
- bool $direct_assignment,
- Codebase $codebase,
- Union $assignment_value_type,
- string $prop_name,
- ?string &$var_id
- ): array {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context);
-
- $context->inside_general_use = $was_inside_general_use;
-
- $lhs_type = $statements_analyzer->node_data->getType($stmt->var);
-
- if ($lhs_type === null) {
- return [];
- }
-
- $lhs_var_id = ExpressionIdentifier::getVarId(
- $stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $var_id = ExpressionIdentifier::getVarId(
- $stmt,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id) {
- $context->assigned_var_ids[$var_id] = (int)$stmt->var->getAttribute('startFilePos');
-
- if ($direct_assignment && isset($context->protected_var_ids[$var_id])) {
- if (IssueBuffer::accepts(
- new LoopInvalidation(
- 'Variable ' . $var_id . ' has already been assigned in a for/foreach loop',
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
- }
- }
-
- if ($lhs_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- $codebase->analyzer->addMixedMemberName(
- '$' . $stmt->name->name,
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- if (IssueBuffer::accepts(
- new MixedPropertyAssignment(
- $lhs_var_id . ' of type mixed cannot be assigned to',
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
-
- return [];
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($lhs_type->isNull()) {
- if (IssueBuffer::accepts(
- new NullPropertyAssignment(
- $lhs_var_id . ' of type null cannot be assigned to',
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
-
- return [];
- }
-
- if ($lhs_type->isNullable() && !$lhs_type->ignore_nullable_issues) {
- if (IssueBuffer::accepts(
- new PossiblyNullPropertyAssignment(
- $lhs_var_id . ' with possibly null type \'' . $lhs_type . '\' cannot be assigned to',
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
- }
-
- $has_regular_setter = false;
-
- $invalid_assignment_types = [];
-
- $has_valid_assignment_type = false;
-
- $lhs_atomic_types = $lhs_type->getAtomicTypes();
-
- $assigned_properties = [];
-
- $context_type = null;
-
- while ($lhs_atomic_types) {
- $lhs_type_part = array_pop($lhs_atomic_types);
-
- if ($lhs_type_part instanceof TTemplateParam) {
- $lhs_atomic_types = array_merge(
- $lhs_atomic_types,
- $lhs_type_part->as->getAtomicTypes()
- );
-
- continue;
- }
-
- $assigned_property = self::analyzeAtomicAssignment(
- $statements_analyzer,
- $codebase,
- $stmt,
- $assignment_value,
- $prop_name,
- $context,
- $lhs_type,
- $lhs_type_part,
- $invalid_assignment_types,
- $var_id,
- $assignment_value_type,
- $lhs_var_id,
- $has_valid_assignment_type,
- $has_regular_setter
- );
-
- if ($assigned_property) {
- $assigned_properties[] = $assigned_property;
-
- if ($context_type) {
- $context_type = Type::combineUnionTypes(
- $context_type,
- $assigned_property->assignment_type,
- $codebase
- );
- } else {
- $context_type = $assigned_property->assignment_type;
- }
- }
- }
-
- if ($invalid_assignment_types) {
- $invalid_assignment_type = $invalid_assignment_types[0];
-
- if (!$has_valid_assignment_type) {
- if (IssueBuffer::accepts(
- new InvalidPropertyAssignment(
- $lhs_var_id . ' with non-object type \'' . $invalid_assignment_type .
- '\' cannot treated as an object',
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
- } else {
- if (IssueBuffer::accepts(
- new PossiblyInvalidPropertyAssignment(
- $lhs_var_id . ' with possible non-object type \'' . $invalid_assignment_type .
- '\' cannot treated as an object',
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
- }
- }
-
- if (!$has_regular_setter) {
- return [];
- }
-
- $context_type = $context_type ?: $assignment_value_type;
-
- if ($var_id) {
- if ($context->collect_initializations
- && $lhs_var_id === '$this'
- ) {
- $context_type->initialized_class = $context->self;
- }
-
- // because we don't want to be assigning for property declarations
- $context->vars_in_scope[$var_id] = $context_type;
- }
-
- return $assigned_properties;
- }
-
- /**
- * @param list<string> $invalid_assignment_types
- */
- private static function analyzeAtomicAssignment(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PropertyFetch $stmt,
- ?PhpParser\Node\Expr $assignment_value,
- string $prop_name,
- Context $context,
- Union $lhs_type,
- Atomic $lhs_type_part,
- array &$invalid_assignment_types,
- ?string $var_id,
- Union $assignment_value_type,
- ?string $lhs_var_id,
- bool &$has_valid_assignment_type,
- bool &$has_regular_setter
- ): ?AssignedProperty {
- if ($lhs_type_part instanceof TNull) {
- return null;
- }
-
- if ($lhs_type_part instanceof TFalse
- && $lhs_type->ignore_falsable_issues
- && count($lhs_type->getAtomicTypes()) > 1
- ) {
- return null;
- }
-
- if (!$lhs_type_part instanceof TObject && !$lhs_type_part instanceof TNamedObject) {
- $invalid_assignment_types[] = (string)$lhs_type_part;
-
- return null;
- }
-
- $has_valid_assignment_type = true;
-
- // stdClass and SimpleXMLElement are special cases where we cannot infer the return types
- // but we don't want to throw an error
- // Hack has a similar issue: https://github.com/facebook/hhvm/issues/5164
- if ($lhs_type_part instanceof TObject ||
- (
- in_array(
- strtolower($lhs_type_part->value),
- Config::getInstance()->getUniversalObjectCrates() + [
- 'dateinterval',
- 'domdocument',
- 'domnode'
- ],
- true
- )
- )
- ) {
- if ($var_id) {
- if ($lhs_type_part instanceof TNamedObject &&
- strtolower($lhs_type_part->value) === 'stdclass'
- ) {
- $context->vars_in_scope[$var_id] = $assignment_value_type;
- } else {
- $context->vars_in_scope[$var_id] = Type::getMixed();
- }
- }
-
- return null;
- }
-
- if (ExpressionAnalyzer::isMock($lhs_type_part->value)) {
- if ($var_id) {
- $context->vars_in_scope[$var_id] = Type::getMixed();
- }
-
- return null;
- }
-
- $intersection_types = $lhs_type_part->getIntersectionTypes() ?: [];
-
- $fq_class_name = $lhs_type_part->value;
-
- $override_property_visibility = false;
-
- $class_exists = false;
- $interface_exists = false;
-
- if (!$codebase->classExists($lhs_type_part->value)) {
- if ($codebase->interfaceExists($lhs_type_part->value)) {
- $interface_exists = true;
- $interface_storage = $codebase->classlike_storage_provider->get(
- strtolower($lhs_type_part->value)
- );
-
- $override_property_visibility = $interface_storage->override_property_visibility;
-
- foreach ($intersection_types as $intersection_type) {
- if ($intersection_type instanceof TNamedObject
- && $codebase->classExists($intersection_type->value)
- ) {
- $fq_class_name = $intersection_type->value;
- $class_exists = true;
- break;
- }
- }
-
- if (!$class_exists) {
- if (IssueBuffer::accepts(
- new NoInterfaceProperties(
- 'Interfaces cannot have properties',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $lhs_type_part->value
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return null;
- }
-
- if (!$codebase->methods->methodExists(
- new MethodIdentifier(
- $fq_class_name,
- '__set'
- )
- )) {
- return null;
- }
- }
- }
-
- if (!$class_exists && !$interface_exists) {
- IssueBuffer::maybeAdd(
- new UndefinedClass(
- 'Cannot set properties of undefined class ' . $lhs_type_part->value,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $lhs_type_part->value
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
- } else {
- $class_exists = true;
- }
-
- $property_id = $fq_class_name . '::$' . $prop_name;
-
- $has_magic_setter = false;
-
- $set_method_id = new MethodIdentifier($fq_class_name, '__set');
-
- if ((!$codebase->properties->propertyExists($property_id, false, $statements_analyzer, $context)
- || ($lhs_var_id !== '$this'
- && $fq_class_name !== $context->self
- && ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues(),
- false
- ) !== true)
- )
- && $codebase->methods->methodExists(
- $set_method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath()
- )
- ) {
- $has_magic_setter = true;
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- if ($var_id) {
- if (isset($class_storage->pseudo_property_set_types['$' . $prop_name])) {
- $class_property_type = TypeExpander::expandUnion(
- $codebase,
- clone $class_storage->pseudo_property_set_types['$' . $prop_name],
- $fq_class_name,
- $fq_class_name,
- $class_storage->parent_class
- );
-
- $has_regular_setter = true;
-
- if (!$context->collect_initializations && !$context->collect_mutations) {
- self::taintProperty(
- $statements_analyzer,
- $stmt,
- $property_id,
- $class_storage,
- $assignment_value_type,
- $context
- );
- }
-
- return new AssignedProperty(
- $class_property_type,
- $property_id,
- $assignment_value_type
- );
- }
- }
-
- if ($assignment_value) {
- self::analyzeSetCall(
- $var_id,
- $context,
- $statements_analyzer,
- $stmt,
- $prop_name,
- $assignment_value
- );
- }
-
- /*
- * If we have an explicit list of all allowed magic properties on the class, and we're
- * not in that list, fall through
- */
- if (!$var_id || !$class_storage->sealed_properties) {
- if (!$context->collect_initializations && !$context->collect_mutations) {
- self::taintProperty(
- $statements_analyzer,
- $stmt,
- $property_id,
- $class_storage,
- $assignment_value_type,
- $context
- );
- }
-
- return null;
- }
-
- if (!$class_exists) {
- IssueBuffer::maybeAdd(
- new UndefinedMagicPropertyAssignment(
- 'Magic instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (!$class_exists) {
- return null;
- }
-
- $has_regular_setter = true;
-
- if ($stmt->var instanceof PhpParser\Node\Expr\Variable
- && $stmt->var->name === 'this'
- && $context->self
- ) {
- $self_property_id = $context->self . '::$' . $prop_name;
-
- if ($self_property_id !== $property_id
- && $codebase->properties->propertyExists(
- $self_property_id,
- false,
- $statements_analyzer,
- $context
- )
- ) {
- $property_id = $self_property_id;
- }
- }
-
- if ($statements_analyzer->data_flow_graph
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- self::taintProperty(
- $statements_analyzer,
- $stmt,
- $property_id,
- $class_storage,
- $assignment_value_type,
- $context
- );
- }
-
- if (!$codebase->properties->propertyExists(
- $property_id,
- false,
- $statements_analyzer,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- )
- // when property existence is asserted by a plugin it doesn't necessarily has storage
- || ($codebase->properties->hasStorage($property_id)
- && $codebase->properties->getStorage($property_id)->is_static
- )
- ) {
- if ($stmt->var instanceof PhpParser\Node\Expr\Variable && $stmt->var->name === 'this') {
- // if this is a proper error, we'll see it on the first pass
- if ($context->collect_mutations) {
- return null;
- }
-
- IssueBuffer::maybeAdd(
- new UndefinedThisPropertyAssignment(
- 'Instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- if ($has_magic_setter) {
- IssueBuffer::maybeAdd(
- new UndefinedMagicPropertyAssignment(
- 'Magic instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedPropertyAssignment(
- 'Instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return null;
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $property_id
- );
- }
-
- if (!$override_property_visibility) {
- if (!$context->collect_mutations) {
- if (ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return null;
- }
- } else {
- if (ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues(),
- false
- ) !== true) {
- return null;
- }
- }
- }
-
- $declaring_property_class = (string)$codebase->properties->getDeclaringClassForProperty(
- $property_id,
- false
- );
-
- if ($codebase->properties_to_rename) {
- $declaring_property_id = strtolower($declaring_property_class) . '::$' . $prop_name;
-
- foreach ($codebase->properties_to_rename as $original_property_id => $new_property_name) {
- if ($declaring_property_id === $original_property_id) {
- $file_manipulations = [
- new FileManipulation(
- (int)$stmt->name->getAttribute('startFilePos'),
- (int)$stmt->name->getAttribute('endFilePos') + 1,
- $new_property_name
- )
- ];
-
- FileManipulationBuffer::add(
- $statements_analyzer->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
-
- $declaring_class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
-
- if (isset($declaring_class_storage->properties[$prop_name])) {
- $property_storage = $declaring_class_storage->properties[$prop_name];
-
- if ($property_storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedProperty(
- $property_id . ' is marked deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($context->self && !NamespaceAnalyzer::isWithinAny($context->self, $property_storage->internal)) {
- IssueBuffer::maybeAdd(
- new InternalProperty(
- $property_id . ' is internal to ' . InternalClass::listToPhrase($property_storage->internal)
- . ' but called from ' . $context->self,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- self::trackPropertyImpurity(
- $statements_analyzer,
- $stmt,
- $property_id,
- $property_storage,
- $declaring_class_storage,
- $context
- );
-
- if (!$property_storage->readonly
- && !$context->collect_mutations
- && !$context->collect_initializations
- && isset($context->vars_in_scope[$lhs_var_id])
- && !$context->vars_in_scope[$lhs_var_id]->allow_mutations
- ) {
- if ($context->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpurePropertyAssignment(
- 'Cannot assign to a property from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- if ($property_storage->getter_method) {
- $getter_id = $lhs_var_id . '->' . $property_storage->getter_method . '()';
-
- unset($context->vars_in_scope[$getter_id]);
- }
- }
-
- $class_property_type = $codebase->properties->getPropertyType(
- $property_id,
- true,
- $statements_analyzer,
- $context
- );
-
- if (!$class_property_type
- || (isset($declaring_class_storage->properties[$prop_name])
- && !$declaring_class_storage->properties[$prop_name]->type_location)
- ) {
- if (!$class_property_type) {
- $class_property_type = Type::getMixed();
- }
-
- $source_analyzer = $statements_analyzer->getSource()->getSource();
-
- if ($lhs_var_id === '$this'
- && $source_analyzer instanceof ClassAnalyzer
- ) {
- $source_analyzer->inferred_property_types[$prop_name] = Type::combineUnionTypes(
- $assignment_value_type,
- $source_analyzer->inferred_property_types[$prop_name] ?? null
- );
- }
- }
-
- if (!$class_property_type->isMixed()) {
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $class_property_type = TypeExpander::expandUnion(
- $codebase,
- clone $class_property_type,
- $fq_class_name,
- $lhs_type_part,
- $declaring_class_storage->parent_class,
- true,
- false,
- $class_storage->final
- );
-
- $class_property_type = Methods::localizeType(
- $codebase,
- $class_property_type,
- $fq_class_name,
- $declaring_property_class
- );
-
- if ($lhs_type_part instanceof TGenericObject) {
- $class_property_type = AtomicPropertyFetchAnalyzer::localizePropertyType(
- $codebase,
- $class_property_type,
- $lhs_type_part,
- $class_storage,
- $declaring_class_storage
- );
- }
-
- $assignment_value_type = Methods::localizeType(
- $codebase,
- $assignment_value_type,
- $fq_class_name,
- $declaring_property_class
- );
-
- if (!$class_property_type->hasMixed() && $assignment_value_type->hasMixed()) {
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($assignment_value_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- $message = $var_id
- ? 'Unable to determine the type that ' . $var_id . ' is being assigned to'
- : 'Unable to determine the type of this assignment';
-
- if ($origin_location && $origin_location->getLineNumber() === $stmt->getLine()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedAssignment(
- $message,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return new AssignedProperty(
- $class_property_type,
- $property_id,
- $assignment_value_type
- );
- }
-
- public static function getExpandedPropertyType(
- Codebase $codebase,
- string $fq_class_name,
- string $property_name,
- ClassLikeStorage $storage
- ): ?Union {
- $property_class_name = $codebase->properties->getDeclaringClassForProperty(
- $fq_class_name . '::$' . $property_name,
- true
- );
-
- if ($property_class_name === null) {
- return null;
- }
-
- $property_class_storage = $codebase->classlike_storage_provider->get($property_class_name);
-
- $property_storage = $property_class_storage->properties[$property_name];
-
- if (!$property_storage->type) {
- return null;
- }
-
- $property_type = clone $property_storage->type;
-
- $fleshed_out_type = !$property_type->isMixed()
- ? TypeExpander::expandUnion(
- $codebase,
- $property_type,
- $fq_class_name,
- $fq_class_name,
- $storage->parent_class,
- true,
- false,
- $storage->final
- )
- : $property_type;
-
- $class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $property_class_storage,
- $storage,
- null,
- new TNamedObject($fq_class_name),
- true
- );
-
- $template_result = new TemplateResult(
- $class_template_params ?: [],
- []
- );
-
- if ($class_template_params) {
- $fleshed_out_type = TemplateStandinTypeReplacer::replace(
- $fleshed_out_type,
- $template_result,
- $codebase,
- null,
- null,
- null
- );
- }
-
- return $fleshed_out_type;
- }
-
- private static function analyzeSetCall(
- ?string $var_id,
- Context $context,
- StatementsAnalyzer $statements_analyzer,
- PropertyFetch $stmt,
- string $prop_name,
- Expr $assignment_value
- ): void {
- if ($var_id) {
- $context->removeVarFromConflictingClauses(
- $var_id,
- Type::getMixed(),
- $statements_analyzer
- );
-
- unset($context->vars_in_scope[$var_id]);
- }
-
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_method_call = new VirtualMethodCall(
- $stmt->var,
- new VirtualIdentifier('__set', $stmt->name->getAttributes()),
- [
- new VirtualArg(
- new VirtualString(
- $prop_name,
- $stmt->name->getAttributes()
- )
- ),
- new VirtualArg(
- $assignment_value
- )
- ]
- );
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('PossiblyNullReference', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyNullReference']);
- }
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call,
- $context,
- false
- );
-
- if (!in_array('PossiblyNullReference', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyNullReference']);
- }
-
- $statements_analyzer->node_data = $old_data_provider;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php
deleted file mode 100644
index ebe454a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/StaticPropertyAssignmentAnalyzer.php
+++ /dev/null
@@ -1,342 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Assignment;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ClassAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ImplicitToStringCast;
-use Psalm\Issue\InvalidPropertyAssignmentValue;
-use Psalm\Issue\MixedPropertyTypeCoercion;
-use Psalm\Issue\PossiblyInvalidPropertyAssignmentValue;
-use Psalm\Issue\PropertyTypeCoercion;
-use Psalm\Issue\UndefinedPropertyAssignment;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function explode;
-use function strtolower;
-
-/**
- * @internal
- */
-class StaticPropertyAssignmentAnalyzer
-{
- /**
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticPropertyFetch $stmt,
- ?PhpParser\Node\Expr $assignment_value,
- Union $assignment_value_type,
- Context $context
- ): ?bool {
- $var_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- $context->self,
- $statements_analyzer
- );
-
- $lhs_type = $statements_analyzer->node_data->getType($stmt->class);
-
- if (!$lhs_type) {
- return null;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $prop_name = $stmt->name;
-
- foreach ($lhs_type->getAtomicTypes() as $lhs_atomic_type) {
- if ($lhs_atomic_type instanceof TClassString) {
- if (!$lhs_atomic_type->as_type) {
- continue;
- }
-
- $lhs_atomic_type = $lhs_atomic_type->as_type;
- }
-
- if (!$lhs_atomic_type instanceof TNamedObject) {
- continue;
- }
-
- $fq_class_name = $lhs_atomic_type->value;
-
- if (!$prop_name instanceof PhpParser\Node\Identifier) {
- $was_inside_general_use = $context->inside_general_use;
-
- $context->inside_general_use = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $prop_name, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return false;
- }
-
- $context->inside_general_use = $was_inside_general_use;
-
- if (!$context->ignore_variable_property) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($fq_class_name) . '::$',
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- return null;
- }
-
- $property_id = $fq_class_name . '::$' . $prop_name;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $fq_class_name
- );
-
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $property_id
- );
- }
-
- if (!$codebase->properties->propertyExists($property_id, false, $statements_analyzer, $context)) {
- IssueBuffer::maybeAdd(
- new UndefinedPropertyAssignment(
- 'Static property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
-
- if (ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return false;
- }
-
- $declaring_property_class = (string) $codebase->properties->getDeclaringClassForProperty(
- $fq_class_name . '::$' . $prop_name->name,
- false
- );
-
- $declaring_property_id = strtolower($declaring_property_class) . '::$' . $prop_name;
-
- if ($codebase->alter_code && $stmt->class instanceof PhpParser\Node\Name) {
- $moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id
- );
-
- if (!$moved_class) {
- foreach ($codebase->property_transforms as $original_pattern => $transformation) {
- if ($declaring_property_id === $original_pattern) {
- [$old_declaring_fq_class_name] = explode('::$', $declaring_property_id);
- [$new_fq_class_name, $new_property_name] = explode('::$', $transformation);
-
- $file_manipulations = [];
-
- if (strtolower($new_fq_class_name) !== $old_declaring_fq_class_name) {
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->class->getAttribute('startFilePos'),
- (int) $stmt->class->getAttribute('endFilePos') + 1,
- Type::getStringFromFQCLN(
- $new_fq_class_name,
- $statements_analyzer->getNamespace(),
- $statements_analyzer->getAliasedClassesFlipped(),
- null
- )
- );
- }
-
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->name->getAttribute('startFilePos'),
- (int) $stmt->name->getAttribute('endFilePos') + 1,
- '$' . $new_property_name
- );
-
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
- }
- }
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
-
- if ($var_id) {
- $context->vars_in_scope[$var_id] = $assignment_value_type;
- }
-
- $class_property_type = $codebase->properties->getPropertyType(
- $property_id,
- true,
- $statements_analyzer,
- $context
- );
-
- if (!$class_property_type) {
- $class_property_type = Type::getMixed();
-
- $source_analyzer = $statements_analyzer->getSource()->getSource();
-
- $prop_name_name = $prop_name->name;
-
- if ($source_analyzer instanceof ClassAnalyzer
- && $fq_class_name === $source_analyzer->getFQCLN()
- ) {
- $source_analyzer->inferred_property_types[$prop_name_name] = Type::combineUnionTypes(
- $assignment_value_type,
- $source_analyzer->inferred_property_types[$prop_name_name] ?? null
- );
- }
- } else {
- $class_property_type = clone $class_property_type;
- }
-
- if ($assignment_value_type->hasMixed()) {
- return null;
- }
-
- if ($class_property_type->hasMixed()) {
- return null;
- }
-
- $class_property_type = TypeExpander::expandUnion(
- $codebase,
- $class_property_type,
- $fq_class_name,
- $fq_class_name,
- $class_storage->parent_class
- );
-
- $union_comparison_results = new TypeComparisonResult();
-
- $type_match_found = UnionTypeComparator::isContainedBy(
- $codebase,
- $assignment_value_type,
- $class_property_type,
- true,
- true,
- $union_comparison_results
- );
-
- if ($union_comparison_results->type_coerced) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- IssueBuffer::maybeAdd(
- new MixedPropertyTypeCoercion(
- $var_id . ' expects \'' . $class_property_type->getId() . '\', '
- . ' parent type `' . $assignment_value_type->getId() . '` provided',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new PropertyTypeCoercion(
- $var_id . ' expects \'' . $class_property_type->getId() . '\', '
- . ' parent type \'' . $assignment_value_type->getId() . '\' provided',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- ),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($union_comparison_results->to_string_cast) {
- IssueBuffer::maybeAdd(
- new ImplicitToStringCast(
- $var_id . ' expects \'' . $class_property_type . '\', '
- . '\'' . $assignment_value_type . '\' provided with a __toString method',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt,
- $context->include_location
- )
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$type_match_found && !$union_comparison_results->type_coerced) {
- if (UnionTypeComparator::canBeContainedBy($codebase, $assignment_value_type, $class_property_type)) {
- if (IssueBuffer::accepts(
- new PossiblyInvalidPropertyAssignmentValue(
- $var_id . ' with declared type \''
- . $class_property_type->getId() . '\' cannot be assigned type \''
- . $assignment_value_type->getId() . '\'',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt
- ),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- } else {
- if (IssueBuffer::accepts(
- new InvalidPropertyAssignmentValue(
- $var_id . ' with declared type \'' . $class_property_type->getId()
- . '\' cannot be assigned type \''
- . $assignment_value_type->getId() . '\'',
- new CodeLocation(
- $statements_analyzer->getSource(),
- $assignment_value ?? $stmt
- ),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
- }
-
- if ($var_id) {
- $context->vars_in_scope[$var_id] = $assignment_value_type;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php
deleted file mode 100644
index 51e9191..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php
+++ /dev/null
@@ -1,1728 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\ArrayAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\StaticPropertyAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\Codebase\DataFlowGraph;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\ReferenceConstraint;
-use Psalm\Internal\Scanner\VarDocblockComment;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\AssignmentToVoid;
-use Psalm\Issue\ImpureByReferenceAssignment;
-use Psalm\Issue\ImpurePropertyAssignment;
-use Psalm\Issue\InvalidArrayAccess;
-use Psalm\Issue\InvalidArrayOffset;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\InvalidScope;
-use Psalm\Issue\LoopInvalidation;
-use Psalm\Issue\MissingDocblockType;
-use Psalm\Issue\MixedArrayAccess;
-use Psalm\Issue\MixedAssignment;
-use Psalm\Issue\NoValue;
-use Psalm\Issue\NullReference;
-use Psalm\Issue\PossiblyInvalidArrayAccess;
-use Psalm\Issue\PossiblyNullArrayAccess;
-use Psalm\Issue\PossiblyUndefinedArrayOffset;
-use Psalm\Issue\ReferenceConstraintViolation;
-use Psalm\Issue\UnnecessaryVarAnnotation;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\BinaryOp\VirtualBitwiseAnd;
-use Psalm\Node\Expr\BinaryOp\VirtualBitwiseOr;
-use Psalm\Node\Expr\BinaryOp\VirtualBitwiseXor;
-use Psalm\Node\Expr\BinaryOp\VirtualCoalesce;
-use Psalm\Node\Expr\BinaryOp\VirtualConcat;
-use Psalm\Node\Expr\BinaryOp\VirtualDiv;
-use Psalm\Node\Expr\BinaryOp\VirtualMinus;
-use Psalm\Node\Expr\BinaryOp\VirtualMod;
-use Psalm\Node\Expr\BinaryOp\VirtualMul;
-use Psalm\Node\Expr\BinaryOp\VirtualPlus;
-use Psalm\Node\Expr\BinaryOp\VirtualPow;
-use Psalm\Node\Expr\BinaryOp\VirtualShiftLeft;
-use Psalm\Node\Expr\BinaryOp\VirtualShiftRight;
-use Psalm\Node\Expr\VirtualAssign;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_merge;
-use function count;
-use function in_array;
-use function is_string;
-use function reset;
-use function spl_object_id;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class AssignmentAnalyzer
-{
- /**
- * @param PhpParser\Node\Expr|null $assign_value This has to be null to support list destructuring
- *
- * @return false|Union
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $assign_var,
- ?PhpParser\Node\Expr $assign_value,
- ?Union $assign_value_type,
- Context $context,
- ?PhpParser\Comment\Doc $doc_comment,
- array $not_ignored_docblock_var_ids = []
- ) {
- $var_id = ExpressionIdentifier::getVarId(
- $assign_var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- // gets a variable id that *may* contain array keys
- $array_var_id = ExpressionIdentifier::getArrayVarId(
- $assign_var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $var_comments = [];
- $comment_type = null;
- $comment_type_location = null;
-
- $was_in_assignment = $context->inside_assignment;
-
- $context->inside_assignment = true;
-
- $codebase = $statements_analyzer->getCodebase();
-
- $base_assign_value = $assign_value;
-
- while ($base_assign_value instanceof PhpParser\Node\Expr\Assign) {
- $base_assign_value = $base_assign_value->expr;
- }
-
- if ($base_assign_value !== $assign_value) {
- ExpressionAnalyzer::analyze($statements_analyzer, $base_assign_value, $context);
-
- $assign_value_type = $statements_analyzer->node_data->getType($base_assign_value) ?? $assign_value_type;
- }
-
- $removed_taints = [];
-
- if ($doc_comment) {
- $file_path = $statements_analyzer->getRootFilePath();
-
- $file_storage_provider = $codebase->file_storage_provider;
-
- $file_storage = $file_storage_provider->get($file_path);
-
- $template_type_map = $statements_analyzer->getTemplateTypeMap();
-
- try {
- $var_comments = $codebase->config->disable_var_parsing
- ? []
- : CommentAnalyzer::getTypeFromComment(
- $doc_comment,
- $statements_analyzer->getSource(),
- $statements_analyzer->getAliases(),
- $template_type_map,
- $file_storage->type_aliases
- );
- } catch (IncorrectDocblockException $e) {
- IssueBuffer::maybeAdd(
- new MissingDocblockType(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- )
- );
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- )
- );
- }
-
- foreach ($var_comments as $var_comment) {
- if ($var_comment->removed_taints) {
- $removed_taints = $var_comment->removed_taints;
- }
-
- self::assignTypeFromVarDocblock(
- $statements_analyzer,
- $assign_var,
- $var_comment,
- $context,
- $var_id,
- $comment_type,
- $comment_type_location,
- $not_ignored_docblock_var_ids
- );
-
- if ($var_id === $var_comment->var_id && $assign_value_type && $comment_type) {
- $comment_type->by_ref = $assign_value_type->by_ref;
- }
- }
- }
-
- if ($array_var_id) {
- unset($context->referenced_var_ids[$array_var_id]);
- $context->assigned_var_ids[$array_var_id] = (int) $assign_var->getAttribute('startFilePos');
- $context->possibly_assigned_var_ids[$array_var_id] = true;
- }
-
- if ($assign_value) {
- if ($var_id && $assign_value instanceof PhpParser\Node\Expr\Closure) {
- foreach ($assign_value->uses as $closure_use) {
- if ($closure_use->byRef
- && is_string($closure_use->var->name)
- && $var_id === '$' . $closure_use->var->name
- ) {
- $context->vars_in_scope[$var_id] = Type::getClosure();
- $context->vars_possibly_in_scope[$var_id] = true;
- }
- }
- }
-
- $was_inside_general_use = $context->inside_general_use;
-
- $root_expr = $assign_var;
-
- while ($root_expr instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- $root_expr = $root_expr->var;
- }
-
- // if we don't know where this data is going, treat as a dead-end usage
- if (!$root_expr instanceof PhpParser\Node\Expr\Variable
- || (is_string($root_expr->name)
- && in_array('$' . $root_expr->name, VariableFetchAnalyzer::SUPER_GLOBALS, true))
- ) {
- $context->inside_general_use = true;
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_value, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- if ($var_id) {
- if ($array_var_id) {
- $context->removeDescendents($array_var_id, null, $assign_value_type);
- }
-
- // if we're not exiting immediately, make everything mixed
- $context->vars_in_scope[$var_id] = $comment_type ?? Type::getMixed();
- }
-
- return false;
- }
-
- $context->inside_general_use = $was_inside_general_use;
- }
-
- if ($comment_type && $comment_type_location) {
- $temp_assign_value_type = $assign_value_type
- ?? ($assign_value ? $statements_analyzer->node_data->getType($assign_value) : null);
-
- if ($codebase->find_unused_variables
- && $temp_assign_value_type
- && $array_var_id
- && (!$not_ignored_docblock_var_ids || isset($not_ignored_docblock_var_ids[$array_var_id]))
- && $temp_assign_value_type->getId() === $comment_type->getId()
- && !$comment_type->isMixed()
- ) {
- if ($codebase->alter_code
- && isset($statements_analyzer->getProjectAnalyzer()->getIssuesToFix()['UnnecessaryVarAnnotation'])
- ) {
- FileManipulationBuffer::addVarAnnotationToRemove($comment_type_location);
- } elseif (IssueBuffer::accepts(
- new UnnecessaryVarAnnotation(
- 'The @var ' . $comment_type . ' annotation for '
- . $array_var_id . ' is unnecessary',
- $comment_type_location
- ),
- $statements_analyzer->getSuppressedIssues(),
- true
- )) {
- // fall through
- }
- }
-
- $parent_nodes = $temp_assign_value_type->parent_nodes ?? [];
-
- $assign_value_type = $comment_type;
- $assign_value_type->parent_nodes = $parent_nodes;
- } elseif (!$assign_value_type) {
- if ($assign_value) {
- $assign_value_type = $statements_analyzer->node_data->getType($assign_value);
- }
-
- if ($assign_value_type) {
- $assign_value_type = clone $assign_value_type;
- $assign_value_type->from_property = false;
- $assign_value_type->from_static_property = false;
- $assign_value_type->ignore_isset = false;
- } else {
- $assign_value_type = Type::getMixed();
- }
- }
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- && !$assign_value_type->parent_nodes
- ) {
- if ($array_var_id) {
- $assignment_node = DataFlowNode::getForAssignment(
- $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- );
- } else {
- $assignment_node = new DataFlowNode('unknown-origin', 'unknown origin', null);
- }
-
- $assign_value_type->parent_nodes = [
- $assignment_node->id => $assignment_node
- ];
-
- if ($context->inside_try) {
- // Copy previous assignment's parent nodes inside a try. Since an exception could be thrown at any
- // point this is a workaround to ensure that use of a variable also uses all previous assignments.
- if (isset($context->vars_in_scope[$array_var_id])) {
- $assign_value_type->parent_nodes += $context->vars_in_scope[$array_var_id]->parent_nodes;
- }
- }
- }
-
- if ($array_var_id && isset($context->vars_in_scope[$array_var_id])) {
- if ($context->vars_in_scope[$array_var_id]->by_ref) {
- if ($context->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureByReferenceAssignment(
- 'Variable ' . $array_var_id . ' cannot be assigned to as it is passed by reference',
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- )
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- }
-
- $assign_value_type->by_ref = true;
- }
-
- // removes dependent vars from $context
- $context->removeDescendents(
- $array_var_id,
- $context->vars_in_scope[$array_var_id],
- $assign_value_type,
- $statements_analyzer
- );
- } else {
- $root_var_id = ExpressionIdentifier::getRootVarId(
- $assign_var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($root_var_id && isset($context->vars_in_scope[$root_var_id])) {
- $context->removeVarFromConflictingClauses(
- $root_var_id,
- $context->vars_in_scope[$root_var_id],
- $statements_analyzer
- );
- }
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($assign_value_type->hasMixed()) {
- $root_var_id = ExpressionIdentifier::getRootVarId(
- $assign_var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- if (!$assign_var instanceof PhpParser\Node\Expr\PropertyFetch
- && !strpos($root_var_id ?? '', '->')
- && !$comment_type
- && strpos($var_id ?? '', '$_') !== 0
- ) {
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($assign_value_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- $message = $var_id
- ? 'Unable to determine the type that ' . $var_id . ' is being assigned to'
- : 'Unable to determine the type of this assignment';
-
- $issue_location = new CodeLocation($statements_analyzer->getSource(), $assign_var);
-
- if ($origin_location && $origin_location->getHash() === $issue_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedAssignment(
- $message,
- $issue_location,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } else {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($var_id
- && isset($context->byref_constraints[$var_id])
- && ($outer_constraint_type = $context->byref_constraints[$var_id]->type)
- ) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $assign_value_type,
- $outer_constraint_type,
- $assign_value_type->ignore_nullable_issues,
- $assign_value_type->ignore_falsable_issues
- )
- ) {
- IssueBuffer::maybeAdd(
- new ReferenceConstraintViolation(
- 'Variable ' . $var_id . ' is limited to values of type '
- . $context->byref_constraints[$var_id]->type
- . ' because it is passed by reference, '
- . $assign_value_type->getId() . ' type found',
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($var_id === '$this' && IssueBuffer::accepts(
- new InvalidScope(
- 'Cannot re-assign ' . $var_id,
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- if (isset($context->protected_var_ids[$var_id])
- && $assign_value_type->hasLiteralInt()
- ) {
- IssueBuffer::maybeAdd(
- new LoopInvalidation(
- 'Variable ' . $var_id . ' has already been assigned in a for/foreach loop',
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($assign_var instanceof PhpParser\Node\Expr\Variable) {
- self::analyzeAssignmentToVariable(
- $statements_analyzer,
- $codebase,
- $assign_var,
- $assign_value,
- $assign_value_type,
- $var_id,
- $context
- );
- } elseif ($assign_var instanceof PhpParser\Node\Expr\List_
- || $assign_var instanceof PhpParser\Node\Expr\Array_
- ) {
- self::analyzeDestructuringAssignment(
- $statements_analyzer,
- $codebase,
- $assign_var,
- $assign_value,
- $assign_value_type,
- $context,
- $doc_comment,
- $array_var_id,
- $var_comments,
- $removed_taints
- );
- } elseif ($assign_var instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- ArrayAssignmentAnalyzer::analyze(
- $statements_analyzer,
- $assign_var,
- $context,
- $assign_value,
- $assign_value_type
- );
- } elseif ($assign_var instanceof PhpParser\Node\Expr\PropertyFetch) {
- self::analyzePropertyAssignment(
- $statements_analyzer,
- $codebase,
- $assign_var,
- $context,
- $assign_value,
- $assign_value_type,
- $var_id
- );
- } elseif ($assign_var instanceof PhpParser\Node\Expr\StaticPropertyFetch &&
- $assign_var->class instanceof PhpParser\Node\Name
- ) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var, $context) === false) {
- return false;
- }
-
- if ($context->check_classes) {
- if (StaticPropertyAssignmentAnalyzer::analyze(
- $statements_analyzer,
- $assign_var,
- $assign_value,
- $assign_value_type,
- $context
- ) === false) {
- return false;
- }
- }
-
- if ($var_id) {
- $context->vars_possibly_in_scope[$var_id] = true;
- }
- }
-
- if ($var_id && isset($context->vars_in_scope[$var_id])) {
- if ($context->vars_in_scope[$var_id]->isVoid()) {
- IssueBuffer::maybeAdd(
- new AssignmentToVoid(
- 'Cannot assign ' . $var_id . ' to type void',
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $context->vars_in_scope[$var_id] = Type::getNull();
-
- $context->inside_assignment = $was_in_assignment;
-
- return $context->vars_in_scope[$var_id];
- }
-
- if ($context->vars_in_scope[$var_id]->isNever()) {
- if (IssueBuffer::accepts(
- new NoValue(
- 'This function or method call never returns output',
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- $context->vars_in_scope[$var_id] = Type::getEmpty();
-
- $context->inside_assignment = $was_in_assignment;
-
- return $context->vars_in_scope[$var_id];
- }
-
- if ($statements_analyzer->data_flow_graph) {
- $data_flow_graph = $statements_analyzer->data_flow_graph;
-
- if ($context->vars_in_scope[$var_id]->parent_nodes) {
- $context->vars_in_scope[$var_id] = clone $context->vars_in_scope[$var_id];
-
- if ($data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $context->vars_in_scope[$var_id]->parent_nodes = [];
- } else {
- $var_location = new CodeLocation($statements_analyzer->getSource(), $assign_var);
-
- $event = new AddRemoveTaintsEvent($assign_var, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = array_merge(
- $removed_taints,
- $codebase->config->eventDispatcher->dispatchRemoveTaints($event)
- );
-
- self::taintAssignment(
- $context->vars_in_scope[$var_id],
- $data_flow_graph,
- $var_id,
- $var_location,
- $removed_taints,
- $added_taints
- );
- }
- }
- }
- }
-
- $context->inside_assignment = $was_in_assignment;
-
- return $assign_value_type;
- }
-
- public static function assignTypeFromVarDocblock(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node $stmt,
- VarDocblockComment $var_comment,
- Context $context,
- ?string $var_id = null,
- ?Union &$comment_type = null,
- ?DocblockTypeLocation &$comment_type_location = null,
- array $not_ignored_docblock_var_ids = []
- ): void {
- if (!$var_comment->type) {
- return;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- try {
- $var_comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
-
- $var_comment_type->setFromDocblock();
-
- $var_comment_type->check(
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues(),
- [],
- false,
- false,
- false,
- $context->calling_method_id
- );
-
- $type_location = null;
-
- if ($var_comment->type_start
- && $var_comment->type_end
- && $var_comment->line_number
- ) {
- $type_location = new DocblockTypeLocation(
- $statements_analyzer,
- $var_comment->type_start,
- $var_comment->type_end,
- $var_comment->line_number
- );
-
- if ($codebase->alter_code) {
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $statements_analyzer,
- $var_comment_type,
- $type_location,
- $context->calling_method_id
- );
- }
- }
-
- if (!$var_comment->var_id || $var_comment->var_id === $var_id) {
- $comment_type = $var_comment_type;
- $comment_type_location = $type_location;
- return;
- }
-
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- if ($codebase->find_unused_variables
- && $type_location
- && (!$not_ignored_docblock_var_ids || isset($not_ignored_docblock_var_ids[$var_comment->var_id]))
- && isset($context->vars_in_scope[$var_comment->var_id])
- && $context->vars_in_scope[$var_comment->var_id]->getId() === $var_comment_type->getId()
- && !$var_comment_type->isMixed()
- ) {
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['UnnecessaryVarAnnotation'])
- ) {
- FileManipulationBuffer::addVarAnnotationToRemove($type_location);
- } elseif (IssueBuffer::accepts(
- new UnnecessaryVarAnnotation(
- 'The @var ' . $var_comment_type . ' annotation for '
- . $var_comment->var_id . ' is unnecessary',
- $type_location
- ),
- $statements_analyzer->getSuppressedIssues(),
- true
- )) {
- // fall through
- }
- }
-
- $parent_nodes = $context->vars_in_scope[$var_comment->var_id]->parent_nodes ?? [];
- $var_comment_type->parent_nodes = $parent_nodes;
-
- $context->vars_in_scope[$var_comment->var_id] = $var_comment_type;
- } catch (UnexpectedValueException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- )
- );
- }
- }
-
- /**
- * @param array<string> $removed_taints
- * @param array<string> $added_taints
- */
- private static function taintAssignment(
- Union $type,
- DataFlowGraph $data_flow_graph,
- string $var_id,
- CodeLocation $var_location,
- array $removed_taints,
- array $added_taints
- ): void {
- $parent_nodes = $type->parent_nodes;
-
- $unspecialized_parent_nodes = array_filter(
- $parent_nodes,
- function ($parent_node) {
- return !$parent_node->specialization_key;
- }
- );
-
- $specialized_parent_nodes = array_filter(
- $parent_nodes,
- function ($parent_node) {
- return (bool) $parent_node->specialization_key;
- }
- );
-
- $new_parent_nodes = [];
-
- foreach ($specialized_parent_nodes as $parent_node) {
- $new_parent_node = DataFlowNode::getForAssignment($var_id, $var_location);
- $new_parent_node->specialization_key = $parent_node->specialization_key;
-
- $data_flow_graph->addNode($new_parent_node);
- $new_parent_nodes += [$new_parent_node->id => $new_parent_node];
- $data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- '=',
- $added_taints,
- $removed_taints
- );
- }
-
- if ($unspecialized_parent_nodes) {
- $new_parent_node = DataFlowNode::getForAssignment($var_id, $var_location);
- $data_flow_graph->addNode($new_parent_node);
- $new_parent_nodes += [$new_parent_node->id => $new_parent_node];
-
- foreach ($unspecialized_parent_nodes as $parent_node) {
- $data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- '=',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $type->parent_nodes = $new_parent_nodes;
- }
-
- public static function analyzeAssignmentOperation(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\AssignOp $stmt,
- Context $context
- ): bool {
- if ($stmt instanceof PhpParser\Node\Expr\AssignOp\BitwiseAnd) {
- $operation = new VirtualBitwiseAnd($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\BitwiseOr) {
- $operation = new VirtualBitwiseOr($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\BitwiseXor) {
- $operation = new VirtualBitwiseXor($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Coalesce) {
- $operation = new VirtualCoalesce($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Concat) {
- $operation = new VirtualConcat($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Div) {
- $operation = new VirtualDiv($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Minus) {
- $operation = new VirtualMinus($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Mod) {
- $operation = new VirtualMod($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Mul) {
- $operation = new VirtualMul($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Plus) {
- $operation = new VirtualPlus($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\Pow) {
- $operation = new VirtualPow($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\ShiftLeft) {
- $operation = new VirtualShiftLeft($stmt->var, $stmt->expr, $stmt->getAttributes());
- } elseif ($stmt instanceof PhpParser\Node\Expr\AssignOp\ShiftRight) {
- $operation = new VirtualShiftRight($stmt->var, $stmt->expr, $stmt->getAttributes());
- } else {
- throw new UnexpectedValueException('Unknown assign op');
- }
-
- $fake_assignment = new VirtualAssign(
- $stmt->var,
- $operation,
- $stmt->getAttributes()
- );
-
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $fake_assignment, $context) === false) {
- return false;
- }
-
- $old_node_data->setType(
- $stmt,
- $statements_analyzer->node_data->getType($operation) ?? Type::getMixed()
- );
-
- $statements_analyzer->node_data = $old_node_data;
-
- return true;
- }
-
- public static function analyzeAssignmentRef(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\AssignRef $stmt,
- Context $context
- ): bool {
- $assignment_type = self::analyze(
- $statements_analyzer,
- $stmt->var,
- $stmt->expr,
- null,
- $context,
- $stmt->getDocComment()
- );
-
- if ($assignment_type === false) {
- return false;
- }
-
- $assignment_type->by_ref = true;
-
- $lhs_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $rhs_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->expr,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($lhs_var_id) {
- $context->vars_in_scope[$lhs_var_id] = $assignment_type;
- $context->hasVariable($lhs_var_id);
- $context->byref_constraints[$lhs_var_id] = new ReferenceConstraint();
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($context->vars_in_scope[$lhs_var_id]->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
- }
-
- if ($rhs_var_id) {
- if (!isset($context->vars_in_scope[$rhs_var_id])) {
- $context->vars_in_scope[$rhs_var_id] = Type::getMixed();
- }
-
- $context->byref_constraints[$rhs_var_id] = new ReferenceConstraint();
- }
-
- if ($statements_analyzer->data_flow_graph
- && $lhs_var_id
- && $rhs_var_id
- && isset($context->vars_in_scope[$rhs_var_id])
- ) {
- $rhs_type = $context->vars_in_scope[$rhs_var_id];
-
- $data_flow_graph = $statements_analyzer->data_flow_graph;
-
- $lhs_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var);
-
- $lhs_node = DataFlowNode::getForAssignment($lhs_var_id, $lhs_location);
-
- foreach ($rhs_type->parent_nodes as $byref_destination_node) {
- $data_flow_graph->addPath($lhs_node, $byref_destination_node, '=');
- }
- }
-
- return true;
- }
-
- public static function assignByRefParam(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Union $by_ref_type,
- Union $by_ref_out_type,
- Context $context,
- bool $constrain_type = true,
- bool $prevent_null = false
- ): void {
- if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch && $stmt->name instanceof PhpParser\Node\Identifier) {
- $prop_name = $stmt->name->name;
-
- InstancePropertyAssignmentAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $prop_name,
- null,
- $by_ref_out_type,
- $context
- );
-
- return;
- }
-
- $var_id = ExpressionIdentifier::getVarId(
- $stmt,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id) {
- $var_not_in_scope = false;
-
- if (!$by_ref_type->hasMixed() && $constrain_type) {
- $context->byref_constraints[$var_id] = new ReferenceConstraint($by_ref_type);
- }
-
- if (!$context->hasVariable($var_id)) {
- $context->vars_possibly_in_scope[$var_id] = true;
-
- $location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- if (!$statements_analyzer->hasVariable($var_id)) {
- if ($constrain_type
- && $prevent_null
- && !$by_ref_type->isMixed()
- && !$by_ref_type->isNullable()
- && !strpos($var_id, '->')
- && !strpos($var_id, '::')
- ) {
- IssueBuffer::maybeAdd(
- new NullReference(
- 'Not expecting null argument passed by reference',
- $location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Variable) {
- $statements_analyzer->registerVariable(
- $var_id,
- $location,
- $context->branch_point
- );
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- $byref_node = DataFlowNode::getForAssignment($var_id, $location);
-
- $statements_analyzer->data_flow_graph->addPath(
- $byref_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
-
- $context->hasVariable($var_id);
- } else {
- $var_not_in_scope = true;
- }
- } elseif ($var_id === '$this') {
- // don't allow changing $this
- return;
- } else {
- $existing_type = $context->vars_in_scope[$var_id];
-
- // removes dependent vars from $context
- $context->removeDescendents(
- $var_id,
- $existing_type,
- $by_ref_type,
- $statements_analyzer
- );
-
- $by_ref_out_type = clone $by_ref_out_type;
-
- if ($existing_type->parent_nodes) {
- $by_ref_out_type->parent_nodes += $existing_type->parent_nodes;
- }
-
- if ($existing_type->getId() !== 'array<empty, empty>') {
- $context->vars_in_scope[$var_id] = $by_ref_out_type;
-
- if (!($stmt_type = $statements_analyzer->node_data->getType($stmt))
- || $stmt_type->isEmpty()
- ) {
- $statements_analyzer->node_data->setType($stmt, clone $by_ref_type);
- }
-
- return;
- }
- }
-
- $context->assigned_var_ids[$var_id] = (int) $stmt->getAttribute('startFilePos');
-
- $context->vars_in_scope[$var_id] = $by_ref_out_type;
-
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
-
- if (!$stmt_type || $stmt_type->isEmpty()) {
- $statements_analyzer->node_data->setType($stmt, clone $by_ref_type);
- }
-
- if ($var_not_in_scope && $stmt instanceof PhpParser\Node\Expr\Variable) {
- $statements_analyzer->registerPossiblyUndefinedVariable($var_id, $stmt);
- }
- }
- }
-
- /**
- * @param PhpParser\Node\Expr\List_|PhpParser\Node\Expr\Array_ $assign_var
- * @param list<VarDocblockComment> $var_comments
- * @param list<string> $removed_taints
- */
- private static function analyzeDestructuringAssignment(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr $assign_var,
- ?PhpParser\Node\Expr $assign_value,
- Union $assign_value_type,
- Context $context,
- ?PhpParser\Comment\Doc $doc_comment,
- ?string $array_var_id,
- array $var_comments,
- array $removed_taints
- ): void {
- if (!$assign_value_type->hasArray()
- && !$assign_value_type->isMixed()
- && !$assign_value_type->hasArrayAccessInterface($codebase)
- ) {
- IssueBuffer::maybeAdd(
- new InvalidArrayOffset(
- 'Cannot destructure non-array of type ' . $assign_value_type->getId(),
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $can_be_empty = true;
-
- foreach ($assign_var->items as $offset => $assign_var_item) {
- // $assign_var_item can be null e.g. list($a, ) = ['a', 'b']
- if (!$assign_var_item) {
- continue;
- }
-
- $var = $assign_var_item->value;
-
- if ($assign_value instanceof PhpParser\Node\Expr\Array_
- && $statements_analyzer->node_data->getType($assign_var_item->value)
- ) {
- self::analyze(
- $statements_analyzer,
- $var,
- $assign_var_item->value,
- null,
- $context,
- $doc_comment
- );
-
- continue;
- }
-
- $offset_value = null;
-
- if (!$assign_var_item->key) {
- $offset_value = $offset;
- } elseif ($assign_var_item->key instanceof PhpParser\Node\Scalar\String_) {
- $offset_value = $assign_var_item->key->value;
- }
-
- $list_var_id = ExpressionIdentifier::getArrayVarId(
- $var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $new_assign_type = null;
- $assigned = false;
- $has_null = false;
-
- foreach ($assign_value_type->getAtomicTypes() as $assign_value_atomic_type) {
- if ($assign_value_atomic_type instanceof TKeyedArray
- && !$assign_var_item->key
- ) {
- // if object-like has int offsets
- if ($offset_value !== null
- && isset($assign_value_atomic_type->properties[$offset_value])
- ) {
- $value_type = $assign_value_atomic_type->properties[$offset_value];
-
- if ($value_type->possibly_undefined) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedArrayOffset(
- 'Possibly undefined array key',
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $value_type = clone $value_type;
- $value_type->possibly_undefined = false;
- }
-
- if ($statements_analyzer->data_flow_graph
- && $assign_value
- ) {
- $assign_value_id = ExpressionIdentifier::getArrayVarId(
- $assign_value,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $keyed_array_var_id = null;
-
- if ($assign_value_id) {
- $keyed_array_var_id = $assign_value_id . '[\'' . $offset_value . '\']';
- }
-
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $assign_value,
- $keyed_array_var_id,
- $value_type,
- Type::getString((string)$offset_value)
- );
- }
-
- self::analyze(
- $statements_analyzer,
- $var,
- null,
- $value_type,
- $context,
- $doc_comment
- );
-
- $assigned = true;
-
- continue;
- }
-
- if ($assign_value_atomic_type->sealed) {
- IssueBuffer::maybeAdd(
- new InvalidArrayOffset(
- 'Cannot access value with offset ' . $offset,
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($assign_value_atomic_type instanceof TMixed) {
- IssueBuffer::maybeAdd(
- new MixedArrayAccess(
- 'Cannot access array value on mixed variable ' . $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($assign_value_atomic_type instanceof TNull) {
- $has_null = true;
- } elseif (!$assign_value_atomic_type instanceof TArray
- && !$assign_value_atomic_type instanceof TKeyedArray
- && !$assign_value_atomic_type instanceof TList
- && !$assign_value_type->hasArrayAccessInterface($codebase)
- ) {
- if ($assign_value_type->hasArray()) {
- if ($assign_value_atomic_type instanceof TFalse && $assign_value_type->ignore_falsable_issues) {
- // do nothing
- } elseif (IssueBuffer::accepts(
- new PossiblyInvalidArrayAccess(
- 'Cannot access array value on non-array variable '
- . $array_var_id . ' of type ' . $assign_value_atomic_type->getId(),
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // do nothing
- }
- } else {
- if (IssueBuffer::accepts(
- new InvalidArrayAccess(
- 'Cannot access array value on non-array variable '
- . $array_var_id . ' of type ' . $assign_value_atomic_type->getId(),
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // do nothing
- }
- }
- }
-
- if ($var instanceof PhpParser\Node\Expr\List_
- || $var instanceof PhpParser\Node\Expr\Array_
- ) {
- if ($assign_value_atomic_type instanceof TKeyedArray) {
- $assign_value_atomic_type = $assign_value_atomic_type->getGenericArrayType();
- }
-
- if ($assign_value_atomic_type instanceof TList) {
- $assign_value_atomic_type = new TArray([
- Type::getInt(),
- $assign_value_atomic_type->type_param
- ]);
- }
-
- $array_value_type = $assign_value_atomic_type instanceof TArray
- ? clone $assign_value_atomic_type->type_params[1]
- : Type::getMixed();
-
- self::analyze(
- $statements_analyzer,
- $var,
- null,
- $array_value_type,
- $context,
- $doc_comment
- );
-
- continue;
- }
-
- if ($list_var_id) {
- $context->vars_possibly_in_scope[$list_var_id] = true;
- $context->assigned_var_ids[$list_var_id] = (int)$var->getAttribute('startFilePos');
- $context->possibly_assigned_var_ids[$list_var_id] = true;
-
- $already_in_scope = isset($context->vars_in_scope[$list_var_id]);
-
- if (strpos($list_var_id, '-') === false && strpos($list_var_id, '[') === false) {
- $location = new CodeLocation($statements_analyzer, $var);
-
- if (!$statements_analyzer->hasVariable($list_var_id)) {
- $statements_analyzer->registerVariable(
- $list_var_id,
- $location,
- $context->branch_point
- );
- } else {
- $statements_analyzer->registerVariableAssignment(
- $list_var_id,
- $location
- );
- }
-
- if (isset($context->byref_constraints[$list_var_id])) {
- // something
- }
- }
-
- if ($assign_value_atomic_type instanceof TArray) {
- $new_assign_type = clone $assign_value_atomic_type->type_params[1];
-
- if ($statements_analyzer->data_flow_graph
- && $assign_value
- ) {
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $assign_value,
- null,
- $new_assign_type,
- Type::getArrayKey()
- );
- }
-
- $can_be_empty = !$assign_value_atomic_type instanceof TNonEmptyArray;
- } elseif ($assign_value_atomic_type instanceof TList) {
- $new_assign_type = clone $assign_value_atomic_type->type_param;
-
- if ($statements_analyzer->data_flow_graph && $assign_value) {
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $assign_value,
- null,
- $new_assign_type,
- Type::getArrayKey()
- );
- }
-
- $can_be_empty = !$assign_value_atomic_type instanceof TNonEmptyList;
- } elseif ($assign_value_atomic_type instanceof TKeyedArray) {
- if (($assign_var_item->key instanceof PhpParser\Node\Scalar\String_
- || $assign_var_item->key instanceof PhpParser\Node\Scalar\LNumber)
- && isset($assign_value_atomic_type->properties[$assign_var_item->key->value])
- ) {
- $new_assign_type =
- clone $assign_value_atomic_type->properties[$assign_var_item->key->value];
-
- if ($new_assign_type->possibly_undefined) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedArrayOffset(
- 'Possibly undefined array key',
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $new_assign_type->possibly_undefined = false;
- }
- }
-
- if ($statements_analyzer->data_flow_graph && $assign_value && $new_assign_type) {
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $assign_value,
- null,
- $new_assign_type,
- Type::getArrayKey()
- );
- }
-
- $can_be_empty = !$assign_value_atomic_type->sealed;
- } elseif ($assign_value_atomic_type->hasArrayAccessInterface($codebase)) {
- ForeachAnalyzer::getKeyValueParamsForTraversableObject(
- $assign_value_atomic_type,
- $codebase,
- $array_access_key_type,
- $array_access_value_type
- );
-
- $new_assign_type = $array_access_value_type;
- }
-
- if ($already_in_scope) {
- // removes dependent vars from $context
- $context->removeDescendents(
- $list_var_id,
- $context->vars_in_scope[$list_var_id],
- $new_assign_type,
- $statements_analyzer
- );
- }
- }
- }
-
-
-
- if (!$assigned) {
- if ($has_null) {
- IssueBuffer::maybeAdd(
- new PossiblyNullArrayAccess(
- 'Cannot access array value on null variable ' . $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->type) {
- continue;
- }
-
- try {
- if ($var_comment->var_id === $list_var_id) {
- $var_comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
-
- $var_comment_type->setFromDocblock();
-
- $new_assign_type = $var_comment_type;
- break;
- }
- } catch (UnexpectedValueException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $assign_var)
- )
- );
- }
- }
-
- if ($list_var_id) {
- $context->vars_in_scope[$list_var_id] = $new_assign_type ?: Type::getMixed();
-
- if ($statements_analyzer->data_flow_graph) {
- $data_flow_graph = $statements_analyzer->data_flow_graph;
-
- $var_location = new CodeLocation($statements_analyzer->getSource(), $var);
-
- if (!$context->vars_in_scope[$list_var_id]->parent_nodes) {
- $assignment_node = DataFlowNode::getForAssignment(
- $list_var_id,
- $var_location
- );
-
- $context->vars_in_scope[$list_var_id]->parent_nodes = [
- $assignment_node->id => $assignment_node
- ];
- } else {
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $context->vars_in_scope[$list_var_id]->parent_nodes = [];
- } else {
- $event = new AddRemoveTaintsEvent($var, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = array_merge(
- $removed_taints,
- $codebase->config->eventDispatcher->dispatchRemoveTaints($event)
- );
-
- self::taintAssignment(
- $context->vars_in_scope[$list_var_id],
- $data_flow_graph,
- $list_var_id,
- $var_location,
- $removed_taints,
- $added_taints
- );
- }
- }
- }
- }
- }
-
- if ($list_var_id) {
- if (($context->error_suppressing && ($offset || $can_be_empty))
- || $has_null
- ) {
- $context->vars_in_scope[$list_var_id]->addType(new TNull);
- }
- }
- }
- }
-
- private static function analyzePropertyAssignment(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\PropertyFetch $assign_var,
- Context $context,
- ?PhpParser\Node\Expr $assign_value,
- Union $assign_value_type,
- ?string $var_id
- ): void {
- if (!$assign_var->name instanceof PhpParser\Node\Identifier) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- // this can happen when the user actually means to type $this-><autocompleted>, but there's
- // a variable on the next line
- if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->var, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return;
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->name, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return;
- }
-
- $context->inside_general_use = $was_inside_general_use;
- }
-
- if ($assign_var->name instanceof PhpParser\Node\Identifier) {
- $prop_name = $assign_var->name->name;
- } elseif (($assign_var_name_type = $statements_analyzer->node_data->getType($assign_var->name))
- && $assign_var_name_type->isSingleStringLiteral()
- ) {
- $prop_name = $assign_var_name_type->getSingleStringLiteral()->value;
- } else {
- $prop_name = null;
- }
-
- if ($prop_name) {
- InstancePropertyAssignmentAnalyzer::analyze(
- $statements_analyzer,
- $assign_var,
- $prop_name,
- $assign_value,
- $assign_value_type,
- $context
- );
- } else {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->var, $context) === false) {
- return;
- }
-
- if (($assign_var_type = $statements_analyzer->node_data->getType($assign_var->var))
- && !$context->ignore_variable_property
- ) {
- $stmt_var_type = $assign_var_type;
-
- if ($stmt_var_type->hasObjectType()) {
- foreach ($stmt_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TNamedObject) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($type->value) . '::$',
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
- }
- }
- }
- }
-
- if ($var_id) {
- $context->vars_possibly_in_scope[$var_id] = true;
- }
-
- $property_var_pure_compatible = $statements_analyzer->node_data->isPureCompatible($assign_var->var);
-
- // prevents writing to any properties in a mutation-free context
- if (!$property_var_pure_compatible
- && !$context->collect_mutations
- && !$context->collect_initializations
- ) {
- if ($context->mutation_free || $context->external_mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpurePropertyAssignment(
- 'Cannot assign to a property from a mutation-free context',
- new CodeLocation($statements_analyzer, $assign_var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- if (!$assign_var->var instanceof PhpParser\Node\Expr\Variable
- || $assign_var->var->name !== 'this'
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- }
-
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
- }
-
- private static function analyzeAssignmentToVariable(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\Variable $assign_var,
- ?PhpParser\Node\Expr $assign_value,
- Union $assign_value_type,
- ?string $var_id,
- Context $context
- ): void {
- if (is_string($assign_var->name)) {
- if ($var_id) {
- $context->vars_in_scope[$var_id] = $assign_value_type;
- $context->vars_possibly_in_scope[$var_id] = true;
-
- $location = new CodeLocation($statements_analyzer, $assign_var);
-
- if (!$statements_analyzer->hasVariable($var_id)) {
- $statements_analyzer->registerVariable(
- $var_id,
- $location,
- $context->branch_point
- );
- } elseif (!$context->inside_isset) {
- $statements_analyzer->registerVariableAssignment(
- $var_id,
- $location
- );
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $location = new CodeLocation($statements_analyzer, $assign_var);
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $assign_var,
- $location->raw_file_start
- . '-' . $location->raw_file_end
- . ':' . $assign_value_type->getId()
- );
- }
-
- if (isset($context->byref_constraints[$var_id])) {
- $assign_value_type->by_ref = true;
- }
-
- if ($assign_value_type->by_ref) {
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- && $assign_value_type->parent_nodes
- ) {
- $location = new CodeLocation($statements_analyzer, $assign_var);
-
- $byref_node = DataFlowNode::getForAssignment($var_id, $location);
-
- foreach ($assign_value_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
-
- $statements_analyzer->data_flow_graph->addPath(
- $byref_node,
- $parent_node,
- 'byref-assignment'
- );
- }
- }
- }
-
- if ($assign_value_type->getId() === 'bool'
- && ($assign_value instanceof PhpParser\Node\Expr\BinaryOp
- || ($assign_value instanceof PhpParser\Node\Expr\BooleanNot
- && $assign_value->expr instanceof PhpParser\Node\Expr\BinaryOp))
- ) {
- $var_object_id = spl_object_id($assign_var);
- $cond_object_id = spl_object_id($assign_value);
-
- $right_clauses = FormulaGenerator::getFormula(
- $cond_object_id,
- $cond_object_id,
- $assign_value,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- $right_clauses = Context::filterClauses(
- $var_id,
- $right_clauses
- );
-
- $assignment_clauses = Algebra::combineOredClauses(
- [new Clause([$var_id => ['falsy']], $var_object_id, $var_object_id)],
- $right_clauses,
- $cond_object_id
- );
-
- $context->clauses = array_merge($context->clauses, $assignment_clauses);
- }
- }
- } else {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $assign_var->name, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return;
- }
-
- $context->inside_general_use = $was_inside_general_use;
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- && $assign_value_type->parent_nodes
- ) {
- foreach ($assign_value_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php
deleted file mode 100644
index 1f4c28f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/AndAnalyzer.php
+++ /dev/null
@@ -1,232 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\BinaryOp;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\Statements\Block\IfConditionalAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElseAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Node\Stmt\VirtualExpression;
-use Psalm\Node\Stmt\VirtualIf;
-use Psalm\Type\Reconciler;
-
-use function array_diff_key;
-use function array_filter;
-use function array_map;
-use function array_merge;
-use function array_values;
-use function count;
-use function in_array;
-use function spl_object_id;
-
-/**
- * @internal
- */
-class AndAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BinaryOp $stmt,
- Context $context,
- bool $from_stmt = false
- ): bool {
- if ($from_stmt) {
- $fake_if_stmt = new VirtualIf(
- $stmt->left,
- [
- 'stmts' => [
- new VirtualExpression(
- $stmt->right
- )
- ]
- ],
- $stmt->getAttributes()
- );
-
- return IfElseAnalyzer::analyze($statements_analyzer, $fake_if_stmt, $context) !== false;
- }
-
- $pre_referenced_var_ids = $context->referenced_var_ids;
-
- $pre_assigned_var_ids = $context->assigned_var_ids;
-
- $left_context = clone $context;
-
- $left_context->referenced_var_ids = [];
- $left_context->assigned_var_ids = [];
-
- /** @var list<string> $left_context->reconciled_expression_clauses */
- $left_context->reconciled_expression_clauses = [];
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $left_context) === false) {
- return false;
- }
-
- IfConditionalAnalyzer::handleParadoxicalCondition($statements_analyzer, $stmt->left);
-
- $codebase = $statements_analyzer->getCodebase();
-
- $left_cond_id = spl_object_id($stmt->left);
-
- $left_clauses = FormulaGenerator::getFormula(
- $left_cond_id,
- $left_cond_id,
- $stmt->left,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- foreach ($left_context->vars_in_scope as $var_id => $type) {
- if (isset($left_context->assigned_var_ids[$var_id])) {
- $context->vars_in_scope[$var_id] = $type;
- }
- }
-
- /** @var array<string, bool> */
- $left_referenced_var_ids = $left_context->referenced_var_ids;
- $context->referenced_var_ids = array_merge($pre_referenced_var_ids, $left_referenced_var_ids);
-
- $left_assigned_var_ids = array_diff_key($left_context->assigned_var_ids, $pre_assigned_var_ids);
-
- $left_referenced_var_ids = array_diff_key($left_referenced_var_ids, $left_assigned_var_ids);
-
- $context_clauses = array_merge($left_context->clauses, $left_clauses);
-
- if ($left_context->reconciled_expression_clauses) {
- $reconciled_expression_clauses = $left_context->reconciled_expression_clauses;
-
- $context_clauses = array_values(
- array_filter(
- $context_clauses,
- function ($c) use ($reconciled_expression_clauses): bool {
- return !in_array($c->hash, $reconciled_expression_clauses);
- }
- )
- );
-
- if (count($context_clauses) === 1
- && $context_clauses[0]->wedge
- && !$context_clauses[0]->possibilities
- ) {
- $context_clauses = [];
- }
- }
-
- $simplified_clauses = Algebra::simplifyCNF($context_clauses);
-
- $active_left_assertions = [];
-
- $left_type_assertions = Algebra::getTruthsFromFormula(
- $simplified_clauses,
- $left_cond_id,
- $left_referenced_var_ids,
- $active_left_assertions
- );
-
- $changed_var_ids = [];
-
- $right_context = clone $left_context;
-
- if ($left_type_assertions) {
- // while in an and, we allow scope to boil over to support
- // statements of the form if ($x && $x->foo())
- $right_vars_in_scope = Reconciler::reconcileKeyedTypes(
- $left_type_assertions,
- $active_left_assertions,
- $context->vars_in_scope,
- $changed_var_ids,
- $left_referenced_var_ids,
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt->left),
- $context->inside_negation
- );
-
- $right_context->vars_in_scope = $right_vars_in_scope;
-
- if ($context->if_scope) {
- $context->if_scope->if_cond_changed_var_ids += $changed_var_ids;
- }
- }
-
- $partitioned_clauses = Context::removeReconciledClauses($left_clauses, $changed_var_ids);
-
- $right_context->clauses = $partitioned_clauses[0];
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->right, $right_context) === false) {
- return false;
- }
-
- IfConditionalAnalyzer::handleParadoxicalCondition($statements_analyzer, $stmt->right);
-
- $context->referenced_var_ids = array_merge(
- $right_context->referenced_var_ids,
- $left_context->referenced_var_ids
- );
-
- if ($context->inside_conditional) {
- $context->updateChecks($right_context);
-
- $context->vars_possibly_in_scope = array_merge(
- $right_context->vars_possibly_in_scope,
- $left_context->vars_possibly_in_scope
- );
-
- $context->assigned_var_ids = array_merge(
- $left_context->assigned_var_ids,
- $right_context->assigned_var_ids
- );
- }
-
- if ($context->if_context && !$context->inside_negation) {
- $context->vars_in_scope = $right_context->vars_in_scope;
- $if_context = $context->if_context;
-
- foreach ($right_context->vars_in_scope as $var_id => $type) {
- if (!isset($if_context->vars_in_scope[$var_id])) {
- $if_context->vars_in_scope[$var_id] = $type;
- } elseif (isset($context->vars_in_scope[$var_id])) {
- $if_context->vars_in_scope[$var_id] = $context->vars_in_scope[$var_id];
- }
- }
-
- $if_context->referenced_var_ids = array_merge(
- $context->referenced_var_ids,
- $if_context->referenced_var_ids
- );
-
- $if_context->assigned_var_ids = array_merge(
- $context->assigned_var_ids,
- $if_context->assigned_var_ids
- );
-
- $if_context->reconciled_expression_clauses = array_merge(
- $if_context->reconciled_expression_clauses,
- array_map(
- function ($c) {
- return $c->hash;
- },
- $partitioned_clauses[1]
- )
- );
-
- $if_context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $if_context->vars_possibly_in_scope
- );
-
- $if_context->updateChecks($context);
- } else {
- $context->vars_in_scope = $left_context->vars_in_scope;
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php
deleted file mode 100644
index 2aa814c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ArithmeticOpAnalyzer.php
+++ /dev/null
@@ -1,1373 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\BinaryOp;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\ArrayAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Issue\FalseOperand;
-use Psalm\Issue\InvalidOperand;
-use Psalm\Issue\MixedOperand;
-use Psalm\Issue\NullOperand;
-use Psalm\Issue\PossiblyFalseOperand;
-use Psalm\Issue\PossiblyInvalidOperand;
-use Psalm\Issue\PossiblyNullOperand;
-use Psalm\Issue\StringIncrement;
-use Psalm\IssueBuffer;
-use Psalm\StatementsSource;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_diff_key;
-use function array_values;
-use function count;
-use function is_int;
-use function is_numeric;
-use function max;
-use function min;
-use function preg_match;
-use function strtolower;
-
-/**
- * @internal
- */
-class ArithmeticOpAnalyzer
-{
- public static function analyze(
- ?StatementsSource $statements_source,
- NodeDataProvider $nodes,
- PhpParser\Node\Expr $left,
- PhpParser\Node\Expr $right,
- PhpParser\Node $parent,
- ?Union &$result_type = null,
- ?Context $context = null
- ): void {
- $codebase = $statements_source ? $statements_source->getCodebase() : null;
-
- $left_type = $nodes->getType($left);
- $right_type = $nodes->getType($right);
- $config = Config::getInstance();
-
- if ($left_type && $left_type->isEmpty()) {
- $left_type = $right_type;
- } elseif ($right_type && $right_type->isEmpty()) {
- $right_type = $left_type;
- }
-
- if ($left_type && $right_type) {
- if ($left_type->isNull()) {
- if ($statements_source && IssueBuffer::accepts(
- new NullOperand(
- 'Left operand cannot be null',
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- $result_type = Type::getMixed();
-
- return;
- }
-
- if ($left_type->isNullable() && !$left_type->ignore_nullable_issues) {
- if ($statements_source && IssueBuffer::accepts(
- new PossiblyNullOperand(
- 'Left operand cannot be nullable, got ' . $left_type,
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($right_type->isNull()) {
- if ($statements_source && IssueBuffer::accepts(
- new NullOperand(
- 'Right operand cannot be null',
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- $result_type = Type::getMixed();
-
- return;
- }
-
- if ($right_type->isNullable() && !$right_type->ignore_nullable_issues) {
- if ($statements_source && IssueBuffer::accepts(
- new PossiblyNullOperand(
- 'Right operand cannot be nullable, got ' . $right_type,
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($left_type->isFalse()) {
- if ($statements_source && IssueBuffer::accepts(
- new FalseOperand(
- 'Left operand cannot be false',
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
-
- return;
- }
-
- if ($left_type->isFalsable() && !$left_type->ignore_falsable_issues) {
- if ($statements_source && IssueBuffer::accepts(
- new PossiblyFalseOperand(
- 'Left operand cannot be falsable, got ' . $left_type,
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($right_type->isFalse()) {
- if ($statements_source && IssueBuffer::accepts(
- new FalseOperand(
- 'Right operand cannot be false',
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
-
- return;
- }
-
- if ($right_type->isFalsable() && !$right_type->ignore_falsable_issues) {
- if ($statements_source && IssueBuffer::accepts(
- new PossiblyFalseOperand(
- 'Right operand cannot be falsable, got ' . $right_type,
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- $invalid_left_messages = [];
- $invalid_right_messages = [];
- $has_valid_left_operand = false;
- $has_valid_right_operand = false;
- $has_string_increment = false;
-
- foreach ($left_type->getAtomicTypes() as $left_type_part) {
- foreach ($right_type->getAtomicTypes() as $right_type_part) {
- $candidate_result_type = self::analyzeOperands(
- $statements_source,
- $codebase,
- $config,
- $context,
- $left,
- $right,
- $parent,
- $left_type_part,
- $right_type_part,
- $invalid_left_messages,
- $invalid_right_messages,
- $has_valid_left_operand,
- $has_valid_right_operand,
- $has_string_increment,
- $result_type
- );
-
- if ($candidate_result_type) {
- $result_type = $candidate_result_type;
- return;
- }
- }
- }
-
- if ($invalid_left_messages && $statements_source) {
- $first_left_message = $invalid_left_messages[0];
-
- if ($has_valid_left_operand) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidOperand(
- $first_left_message,
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidOperand(
- $first_left_message,
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- );
- }
- }
-
- if ($invalid_right_messages && $statements_source) {
- $first_right_message = $invalid_right_messages[0];
-
- if ($has_valid_right_operand) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidOperand(
- $first_right_message,
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidOperand(
- $first_right_message,
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- );
- }
- }
-
- if ($has_string_increment && $statements_source) {
- IssueBuffer::maybeAdd(
- new StringIncrement(
- 'Possibly unintended string increment',
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- );
- }
- }
- }
-
- /**
- * @param int|float $result
- */
- private static function getNumericalType($result): Union
- {
- if (is_int($result)) {
- return Type::getInt(false, $result);
- }
-
- return Type::getFloat($result);
- }
-
- /**
- * @param string[] $invalid_left_messages
- * @param string[] $invalid_right_messages
- */
- private static function analyzeOperands(
- ?StatementsSource $statements_source,
- ?Codebase $codebase,
- Config $config,
- ?Context $context,
- PhpParser\Node\Expr $left,
- PhpParser\Node\Expr $right,
- PhpParser\Node $parent,
- Atomic $left_type_part,
- Atomic $right_type_part,
- array &$invalid_left_messages,
- array &$invalid_right_messages,
- bool &$has_valid_left_operand,
- bool &$has_valid_right_operand,
- bool &$has_string_increment,
- Union &$result_type = null
- ): ?Union {
- if ($left_type_part instanceof TLiteralInt
- && $right_type_part instanceof TLiteralInt
- && (
- //we don't try to do arithmetics on variables in loops
- $context === null
- || $context->inside_loop === false
- || (!$left instanceof PhpParser\Node\Expr\Variable && !$right instanceof PhpParser\Node\Expr\Variable)
- )
- ) {
- // time for some arithmetic!
- $calculated_type = self::arithmeticOperation(
- $parent,
- $left_type_part->value,
- $right_type_part->value,
- true
- );
-
- if ($calculated_type) {
- $result_type = Type::combineUnionTypes(
- $calculated_type,
- $result_type
- );
-
- $has_valid_left_operand = true;
- $has_valid_right_operand = true;
-
- return null;
- }
- }
-
- if ($left_type_part instanceof TNull || $right_type_part instanceof TNull) {
- // null case is handled above
- return null;
- }
-
- if ($left_type_part instanceof TFalse || $right_type_part instanceof TFalse) {
- // null case is handled above
- return null;
- }
-
- if ($left_type_part instanceof TString
- && $right_type_part instanceof TInt
- && (
- $parent instanceof PhpParser\Node\Expr\PostInc ||
- $parent instanceof PhpParser\Node\Expr\PreInc
- )
- ) {
- if ($left_type_part instanceof TNumericString ||
- ($left_type_part instanceof TLiteralString && is_numeric($left_type_part->value))
- ) {
- $new_result_type = new Union([new TFloat(), new TInt()]);
- $new_result_type->from_calculation = true;
- } else {
- $new_result_type = Type::getNonEmptyString();
- $has_string_increment = true;
- }
-
- $result_type = Type::combineUnionTypes($new_result_type, $result_type);
-
- $has_valid_left_operand = true;
- $has_valid_right_operand = true;
-
- return null;
- }
-
- if ($left_type_part instanceof TTemplateParam
- && $right_type_part instanceof TTemplateParam
- ) {
- $combined_type = Type::combineUnionTypes(
- $left_type_part->as,
- $right_type_part->as
- );
-
- $combined_atomic_types = array_values($combined_type->getAtomicTypes());
-
- if (count($combined_atomic_types) <= 2) {
- $left_type_part = $combined_atomic_types[0];
- $right_type_part = $combined_atomic_types[1] ?? $combined_atomic_types[0];
- }
- }
-
- if ($left_type_part instanceof TMixed || $right_type_part instanceof TMixed) {
- if ($statements_source && $codebase && $context) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_source->getFilePath() === $statements_source->getRootFilePath()
- && (!(($source = $statements_source->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_source->getFilePath());
- }
- }
-
- if ($left_type_part instanceof TMixed) {
- if ($statements_source && IssueBuffer::accepts(
- new MixedOperand(
- 'Left operand cannot be mixed',
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- } else {
- if ($statements_source && IssueBuffer::accepts(
- new MixedOperand(
- 'Right operand cannot be mixed',
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($left_type_part instanceof TMixed
- && $left_type_part->from_loop_isset
- && $parent instanceof PhpParser\Node\Expr\AssignOp\Plus
- && !$right_type_part instanceof TMixed
- ) {
- $result_type = Type::combineUnionTypes(new Union([$right_type_part]), $result_type);
-
- return null;
- }
-
- $from_loop_isset = (!($left_type_part instanceof TMixed) || $left_type_part->from_loop_isset)
- && (!($right_type_part instanceof TMixed) || $right_type_part->from_loop_isset);
-
- $result_type = Type::getMixed($from_loop_isset);
-
- return $result_type;
- }
-
- if ($left_type_part instanceof TTemplateParam || $right_type_part instanceof TTemplateParam) {
- if ($left_type_part instanceof TTemplateParam
- && !$left_type_part->as->isInt()
- && !$left_type_part->as->isFloat()
- ) {
- if ($statements_source && IssueBuffer::accepts(
- new MixedOperand(
- 'Left operand cannot be a non-numeric template',
- new CodeLocation($statements_source, $left)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- } elseif ($right_type_part instanceof TTemplateParam
- && !$right_type_part->as->isInt()
- && !$right_type_part->as->isFloat()
- ) {
- if ($statements_source && IssueBuffer::accepts(
- new MixedOperand(
- 'Right operand cannot be a non-numeric template',
- new CodeLocation($statements_source, $right)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- return null;
- }
-
- if ($statements_source && $codebase && $context) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_source->getFilePath() === $statements_source->getRootFilePath()
- && (!(($parent_source = $statements_source->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_source->getFilePath());
- }
- }
-
- if ($left_type_part instanceof TArray
- || $right_type_part instanceof TArray
- || $left_type_part instanceof TKeyedArray
- || $right_type_part instanceof TKeyedArray
- || $left_type_part instanceof TList
- || $right_type_part instanceof TList
- ) {
- if ((!$right_type_part instanceof TArray
- && !$right_type_part instanceof TKeyedArray
- && !$right_type_part instanceof TList)
- || (!$left_type_part instanceof TArray
- && !$left_type_part instanceof TKeyedArray
- && !$left_type_part instanceof TList)
- ) {
- if (!$left_type_part instanceof TArray
- && !$left_type_part instanceof TKeyedArray
- && !$left_type_part instanceof TList
- ) {
- $invalid_left_messages[] = 'Cannot add an array to a non-array ' . $left_type_part;
- } else {
- $invalid_right_messages[] = 'Cannot add an array to a non-array ' . $right_type_part;
- }
-
- if ($left_type_part instanceof TArray
- || $left_type_part instanceof TKeyedArray
- || $left_type_part instanceof TList
- ) {
- $has_valid_left_operand = true;
- } elseif ($right_type_part instanceof TArray
- || $right_type_part instanceof TKeyedArray
- || $right_type_part instanceof TList
- ) {
- $has_valid_right_operand = true;
- }
-
- $result_type = Type::getArray();
-
- return null;
- }
-
- $has_valid_right_operand = true;
- $has_valid_left_operand = true;
-
- if ($left_type_part instanceof TKeyedArray
- && $right_type_part instanceof TKeyedArray
- ) {
- $definitely_existing_mixed_right_properties = array_diff_key(
- $right_type_part->properties,
- $left_type_part->properties
- );
-
- $properties = $left_type_part->properties;
-
- foreach ($right_type_part->properties as $key => $type) {
- if (!isset($properties[$key])) {
- $properties[$key] = $type;
- } elseif ($properties[$key]->possibly_undefined) {
- $properties[$key] = Type::combineUnionTypes(
- $properties[$key],
- $type,
- $codebase
- );
-
- $properties[$key]->possibly_undefined = $type->possibly_undefined;
- }
- }
-
- if (!$left_type_part->sealed) {
- foreach ($definitely_existing_mixed_right_properties as $key => $type) {
- $properties[$key] = Type::combineUnionTypes(Type::getMixed(), $type);
- }
- }
-
- $new_keyed_array = new TKeyedArray($properties);
- $new_keyed_array->sealed = $left_type_part->sealed && $right_type_part->sealed;
- $result_type_member = new Union([$new_keyed_array]);
- } else {
- $result_type_member = TypeCombiner::combine(
- [$left_type_part, $right_type_part],
- $codebase,
- true
- );
- }
-
- $result_type = Type::combineUnionTypes($result_type_member, $result_type, $codebase, true);
-
- if ($left instanceof PhpParser\Node\Expr\ArrayDimFetch
- && $context
- && $statements_source instanceof StatementsAnalyzer
- ) {
- ArrayAssignmentAnalyzer::updateArrayType(
- $statements_source,
- $left,
- $right,
- $result_type,
- $context
- );
- }
-
- return null;
- }
-
- if (($left_type_part instanceof TNamedObject && strtolower($left_type_part->value) === 'gmp')
- || ($right_type_part instanceof TNamedObject && strtolower($right_type_part->value) === 'gmp')
- ) {
- if ((($left_type_part instanceof TNamedObject
- && strtolower($left_type_part->value) === 'gmp')
- && (($right_type_part instanceof TNamedObject
- && strtolower($right_type_part->value) === 'gmp')
- || ($right_type_part->isNumericType() || $right_type_part instanceof TMixed)))
- || (($right_type_part instanceof TNamedObject
- && strtolower($right_type_part->value) === 'gmp')
- && (($left_type_part instanceof TNamedObject
- && strtolower($left_type_part->value) === 'gmp')
- || ($left_type_part->isNumericType() || $left_type_part instanceof TMixed)))
- ) {
- $result_type = Type::combineUnionTypes(
- new Union([new TNamedObject('GMP')]),
- $result_type
- );
- } else {
- if ($statements_source && IssueBuffer::accepts(
- new InvalidOperand(
- 'Cannot add GMP to non-numeric type',
- new CodeLocation($statements_source, $parent)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- return null;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\Minus
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\Mul
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\Div
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\Mod
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\Pow
- ) {
- $non_decimal_type = null;
- if ($left_type_part instanceof TNamedObject
- && strtolower($left_type_part->value) === "decimal\\decimal"
- ) {
- $non_decimal_type = $right_type_part;
- } elseif ($right_type_part instanceof TNamedObject
- && strtolower($right_type_part->value) === "decimal\\decimal"
- ) {
- $non_decimal_type = $left_type_part;
- }
- if ($non_decimal_type !== null) {
- if ($non_decimal_type instanceof TInt
- || $non_decimal_type instanceof TNumericString
- || $non_decimal_type instanceof TNamedObject
- && strtolower($non_decimal_type->value) === "decimal\\decimal"
- ) {
- $result_type = Type::combineUnionTypes(
- new Union([new TNamedObject("Decimal\\Decimal")]),
- $result_type
- );
- } else {
- if ($statements_source) {
- IssueBuffer::maybeAdd(
- new InvalidOperand(
- "Cannot add Decimal\\Decimal to {$non_decimal_type->getId()}",
- new CodeLocation($statements_source, $parent)
- ),
- $statements_source->getSuppressedIssues()
- );
- }
- }
-
- return null;
- }
- }
-
- if ($left_type_part instanceof TLiteralString) {
- if (preg_match('/^\-?\d+$/', $left_type_part->value)) {
- $left_type_part = new TLiteralInt((int) $left_type_part->value);
- } elseif (preg_match('/^\-?\d?\.\d+$/', $left_type_part->value)) {
- $left_type_part = new TLiteralFloat((float) $left_type_part->value);
- }
- }
-
- if ($right_type_part instanceof TLiteralString) {
- if (preg_match('/^\-?\d+$/', $right_type_part->value)) {
- $right_type_part = new TLiteralInt((int) $right_type_part->value);
- } elseif (preg_match('/^\-?\d?\.\d+$/', $right_type_part->value)) {
- $right_type_part = new TLiteralFloat((float) $right_type_part->value);
- }
- }
-
- if ($left_type_part->isNumericType() || $right_type_part->isNumericType()) {
- if (($left_type_part instanceof TNumeric || $right_type_part instanceof TNumeric)
- && ($left_type_part->isNumericType() && $right_type_part->isNumericType())
- ) {
- if ($config->strict_binary_operands) {
- if ($statements_source && IssueBuffer::accepts(
- new InvalidOperand(
- 'Cannot process different numeric types together in strict binary operands mode, '.
- 'please cast explicitly',
- new CodeLocation($statements_source, $parent)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- $new_result_type = Type::getInt();
- } else {
- $new_result_type = new Union([new TFloat(), new TInt()]);
- }
-
- $result_type = Type::combineUnionTypes($new_result_type, $result_type);
-
- $has_valid_right_operand = true;
- $has_valid_left_operand = true;
-
- return null;
- }
-
- if ($left_type_part instanceof TIntRange && $right_type_part instanceof TIntRange) {
- self::analyzeOperandsBetweenIntRange($parent, $result_type, $left_type_part, $right_type_part);
- return null;
- }
-
- if (($left_type_part instanceof TIntRange && $right_type_part instanceof TInt) ||
- ($left_type_part instanceof TInt && $right_type_part instanceof TIntRange)
- ) {
- self::analyzeOperandsBetweenIntRangeAndInt(
- $parent,
- $result_type,
- $left_type_part,
- $right_type_part
- );
- return null;
- }
-
- if ($left_type_part instanceof TInt && $right_type_part instanceof TInt) {
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Div) {
- $result_type = new Union([new TInt(), new TFloat()]);
- } else {
- $left_is_positive = $left_type_part instanceof TPositiveInt
- || ($left_type_part instanceof TLiteralInt && $left_type_part->value > 0);
-
- $right_is_positive = $right_type_part instanceof TPositiveInt
- || ($right_type_part instanceof TLiteralInt && $right_type_part->value > 0);
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
- $always_positive = false;
- } elseif ($left_is_positive && $right_is_positive) {
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\ShiftLeft
- || $parent instanceof PhpParser\Node\Expr\BinaryOp\ShiftRight
- ) {
- $always_positive = false;
- } else {
- $always_positive = true;
- }
- } elseif ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus
- && ($left_type_part instanceof TLiteralInt && $left_type_part->value === 0)
- && $right_is_positive
- ) {
- $always_positive = true;
- } elseif ($parent instanceof PhpParser\Node\Expr\BinaryOp\Plus
- && ($right_type_part instanceof TLiteralInt && $right_type_part->value === 0)
- && $left_is_positive
- ) {
- $always_positive = true;
- } else {
- $always_positive = false;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- if ($right_type_part instanceof TLiteralInt) {
- $literal_value_max = $right_type_part->value - 1;
- if ($always_positive) {
- $result_type = new Union([new TIntRange(0, $literal_value_max)]);
- } else {
- $result_type = new Union(
- [new TIntRange(-$literal_value_max, $literal_value_max)]
- );
- }
- } else {
- if ($always_positive) {
- $result_type = new Union([
- new TPositiveInt(),
- new TLiteralInt(0)
- ]);
- } else {
- $result_type = Type::getInt();
- }
- }
- } else {
- $result_type = Type::combineUnionTypes(
- $always_positive ? Type::getPositiveInt(true) : Type::getInt(true),
- $result_type
- );
- }
- }
-
- $has_valid_right_operand = true;
- $has_valid_left_operand = true;
-
- return null;
- }
-
- if ($left_type_part instanceof TFloat && $right_type_part instanceof TFloat) {
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- $result_type = Type::getInt();
- } else {
- $result_type = Type::combineUnionTypes(Type::getFloat(), $result_type);
- }
-
- $has_valid_right_operand = true;
- $has_valid_left_operand = true;
-
- return null;
- }
-
- if (($left_type_part instanceof TFloat && $right_type_part instanceof TInt)
- || ($left_type_part instanceof TInt && $right_type_part instanceof TFloat)
- ) {
- if ($config->strict_binary_operands) {
- if ($statements_source && IssueBuffer::accepts(
- new InvalidOperand(
- 'Cannot process ints and floats in strict binary operands mode, '.
- 'please cast explicitly',
- new CodeLocation($statements_source, $parent)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- $result_type = Type::getInt();
- } else {
- $result_type = Type::combineUnionTypes(Type::getFloat(), $result_type);
- }
-
- $has_valid_right_operand = true;
- $has_valid_left_operand = true;
-
- return null;
- }
-
- if ($left_type_part->isNumericType() && $right_type_part->isNumericType()) {
- if ($config->strict_binary_operands) {
- if ($statements_source && IssueBuffer::accepts(
- new InvalidOperand(
- 'Cannot process numeric types together in strict operands mode, '.
- 'please cast explicitly',
- new CodeLocation($statements_source, $parent)
- ),
- $statements_source->getSuppressedIssues()
- )) {
- // fall through
- }
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- $result_type = Type::getInt();
- } else {
- $result_type = new Union([new TInt, new TFloat]);
- }
-
- $has_valid_right_operand = true;
- $has_valid_left_operand = true;
-
- return null;
- }
-
- if (!$left_type_part->isNumericType()) {
- $invalid_left_messages[] = 'Cannot perform a numeric operation with a non-numeric type '
- . $left_type_part;
- $has_valid_right_operand = true;
- } else {
- $invalid_right_messages[] = 'Cannot perform a numeric operation with a non-numeric type '
- . $right_type_part;
- $has_valid_left_operand = true;
- }
- } else {
- $invalid_left_messages[] =
- 'Cannot perform a numeric operation with non-numeric types ' . $left_type_part
- . ' and ' . $right_type_part;
- }
-
- return null;
- }
-
- /**
- * @param PhpParser\Node $operation
- * @param float|int $operand1
- * @param float|int $operand2
- */
- public static function arithmeticOperation(
- PhpParser\Node $operation,
- $operand1,
- $operand2,
- bool $allow_float_result
- ): ?Union {
- if ($operation instanceof PhpParser\Node\Expr\BinaryOp\Plus) {
- $result = $operand1 + $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
- $result = $operand1 - $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- if ($operand2 === 0) {
- return Type::getEmpty();
- }
-
- $result = $operand1 % $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Mul) {
- $result = $operand1 * $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Pow) {
- $result = $operand1 ** $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\BitwiseOr) {
- $result = $operand1 | $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd) {
- $result = $operand1 & $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor) {
- $result = $operand1 ^ $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\ShiftLeft) {
- $result = $operand1 << $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\ShiftRight) {
- $result = $operand1 >> $operand2;
- } elseif ($operation instanceof PhpParser\Node\Expr\BinaryOp\Div) {
- if ($operand2 === 0) {
- return Type::getEmpty();
- }
-
- $result = $operand1 / $operand2;
- } else {
- return null;
- }
-
- $calculated_type = self::getNumericalType($result);
- if (!$allow_float_result && $calculated_type->isFloat()) {
- return null;
- }
-
- return $calculated_type;
- }
-
- private static function analyzeOperandsBetweenIntRange(
- PhpParser\Node $parent,
- ?Union &$result_type,
- TIntRange $left_type_part,
- TIntRange $right_type_part
- ): void {
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Div) {
- //can't assume an int range will stay int after division
- $result_type = Type::combineUnionTypes(
- new Union([new TInt(), new TFloat()]),
- $result_type
- );
- return;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mod) {
- self::analyzeModBetweenIntRange($result_type, $left_type_part, $right_type_part);
- return;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd ||
- $parent instanceof PhpParser\Node\Expr\BinaryOp\BitwiseOr ||
- $parent instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor
- ) {
- //really complex to calculate
- $result_type = Type::combineUnionTypes(
- Type::getInt(),
- $result_type
- );
- return;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\ShiftLeft ||
- $parent instanceof PhpParser\Node\Expr\BinaryOp\ShiftRight
- ) {
- //really complex to calculate
- $result_type = Type::combineUnionTypes(
- new Union([new TInt()]),
- $result_type
- );
- return;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Mul) {
- self::analyzeMulBetweenIntRange($parent, $result_type, $left_type_part, $right_type_part);
- return;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Pow) {
- self::analyzePowBetweenIntRange($result_type, $left_type_part, $right_type_part);
- return;
- }
-
- if ($parent instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
- //for Minus, we have to assume the min is the min from first range minus the max from the second
- $min_operand1 = $left_type_part->min_bound;
- $min_operand2 = $right_type_part->max_bound;
- //and the max is the max from first range minus the min from the second
- $max_operand1 = $left_type_part->max_bound;
- $max_operand2 = $right_type_part->min_bound;
- } else {
- $min_operand1 = $left_type_part->min_bound;
- $min_operand2 = $right_type_part->min_bound;
-
- $max_operand1 = $left_type_part->max_bound;
- $max_operand2 = $right_type_part->max_bound;
- }
-
- $calculated_min_type = null;
- if ($min_operand1 !== null && $min_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_min_type = self::arithmeticOperation(
- $parent,
- $min_operand1,
- $min_operand2,
- false
- );
- }
-
- $calculated_max_type = null;
- if ($max_operand1 !== null && $max_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_max_type = self::arithmeticOperation(
- $parent,
- $max_operand1,
- $max_operand2,
- false
- );
- }
-
- $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
- $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
-
- $new_result_type = new Union([new TIntRange($min_value, $max_value)]);
-
- $result_type = Type::combineUnionTypes($new_result_type, $result_type);
- }
-
- /**
- * @param TIntRange|TInt $left_type_part
- * @param TIntRange|TInt $right_type_part
- */
- private static function analyzeOperandsBetweenIntRangeAndInt(
- PhpParser\Node $parent,
- ?Union &$result_type,
- Atomic $left_type_part,
- Atomic $right_type_part
- ): void {
- if (!$left_type_part instanceof TIntRange) {
- $left_type_part = TIntRange::convertToIntRange($left_type_part);
- }
- if (!$right_type_part instanceof TIntRange) {
- $right_type_part = TIntRange::convertToIntRange($right_type_part);
- }
-
- self::analyzeOperandsBetweenIntRange($parent, $result_type, $left_type_part, $right_type_part);
- }
-
- private static function analyzeMulBetweenIntRange(
- PhpParser\Node\Expr\BinaryOp\Mul $parent,
- ?Union &$result_type,
- TIntRange $left_type_part,
- TIntRange $right_type_part
- ): void {
- //Mul is a special case because of double negatives. We can only infer when we know both signs strictly
- if ($right_type_part->min_bound !== null
- && $right_type_part->max_bound !== null
- && $left_type_part->min_bound !== null
- && $left_type_part->max_bound !== null
- ) {
- //everything is known, we can do calculations
- //[ x_1 , x_2 ] ⋆ [ y_1 , y_2 ] =
- // [
- // min(x_1 ⋆ y_1 , x_1 ⋆ y_2 , x_2 ⋆ y_1 , x_2 ⋆ y_2),
- // max(x_1 ⋆ y_1 , x_1 ⋆ y_2 , x_2 ⋆ y_1 , x_2 ⋆ y_2)
- // ]
- $x_1 = $right_type_part->min_bound;
- $x_2 = $right_type_part->max_bound;
- $y_1 = $left_type_part->min_bound;
- $y_2 = $left_type_part->max_bound;
- $min_value = min($x_1 * $y_1, $x_1 * $y_2, $x_2 * $y_1, $x_2 * $y_2);
- $max_value = max($x_1 * $y_1, $x_1 * $y_2, $x_2 * $y_1, $x_2 * $y_2);
-
- $new_result_type = new Union([new TIntRange($min_value, $max_value)]);
- } elseif ($right_type_part->isPositiveOrZero() && $left_type_part->isPositiveOrZero()) {
- // both operands are positive, result will be only positive
- $min_operand1 = $left_type_part->min_bound;
- $min_operand2 = $right_type_part->min_bound;
-
- $max_operand1 = $left_type_part->max_bound;
- $max_operand2 = $right_type_part->max_bound;
-
- $calculated_min_type = null;
- if ($min_operand1 !== null && $min_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_min_type = self::arithmeticOperation(
- $parent,
- $min_operand1,
- $min_operand2,
- false
- );
- }
-
- $calculated_max_type = null;
- if ($max_operand1 !== null && $max_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_max_type = self::arithmeticOperation(
- $parent,
- $max_operand1,
- $max_operand2,
- false
- );
- }
-
- $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
- $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
-
- $new_result_type = new Union([new TIntRange($min_value, $max_value)]);
- } elseif ($right_type_part->isPositiveOrZero() && $left_type_part->isNegativeOrZero()) {
- // one operand is negative, result will be negative and we have to check min vs max
- $min_operand1 = $left_type_part->max_bound;
- $min_operand2 = $right_type_part->min_bound;
-
- $max_operand1 = $left_type_part->min_bound;
- $max_operand2 = $right_type_part->max_bound;
-
- $calculated_min_type = null;
- if ($min_operand1 !== null && $min_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_min_type = self::arithmeticOperation(
- $parent,
- $min_operand1,
- $min_operand2,
- false
- );
- }
-
- $calculated_max_type = null;
- if ($max_operand1 !== null && $max_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_max_type = self::arithmeticOperation(
- $parent,
- $max_operand1,
- $max_operand2,
- false
- );
- }
-
- $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
- $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
-
- if ($min_value > $max_value) {
- [$min_value, $max_value] = [$max_value, $min_value];
- }
-
- $new_result_type = new Union([new TIntRange($min_value, $max_value)]);
- } elseif ($right_type_part->isNegativeOrZero() && $left_type_part->isPositiveOrZero()) {
- // one operand is negative, result will be negative and we have to check min vs max
- $min_operand1 = $left_type_part->min_bound;
- $min_operand2 = $right_type_part->max_bound;
-
- $max_operand1 = $left_type_part->max_bound;
- $max_operand2 = $right_type_part->min_bound;
-
- $calculated_min_type = null;
- if ($min_operand1 !== null && $min_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_min_type = self::arithmeticOperation(
- $parent,
- $min_operand1,
- $min_operand2,
- false
- );
- }
-
- $calculated_max_type = null;
- if ($max_operand1 !== null && $max_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_max_type = self::arithmeticOperation(
- $parent,
- $max_operand1,
- $max_operand2,
- false
- );
- }
-
- $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
- $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
-
- if ($min_value > $max_value) {
- [$min_value, $max_value] = [$max_value, $min_value];
- }
-
- $new_result_type = new Union([new TIntRange($min_value, $max_value)]);
- } elseif ($right_type_part->isNegativeOrZero() && $left_type_part->isNegativeOrZero()) {
- // both operand are negative, result will be positive
- $min_operand1 = $left_type_part->max_bound;
- $min_operand2 = $right_type_part->max_bound;
-
- $max_operand1 = $left_type_part->min_bound;
- $max_operand2 = $right_type_part->min_bound;
-
- $calculated_min_type = null;
- if ($min_operand1 !== null && $min_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_min_type = self::arithmeticOperation(
- $parent,
- $min_operand1,
- $min_operand2,
- false
- );
- }
-
- $calculated_max_type = null;
- if ($max_operand1 !== null && $max_operand2 !== null) {
- // when there are two valid numbers, make any operation
- $calculated_max_type = self::arithmeticOperation(
- $parent,
- $max_operand1,
- $max_operand2,
- false
- );
- }
-
- $min_value = $calculated_min_type !== null ? $calculated_min_type->getSingleIntLiteral()->value : null;
- $max_value = $calculated_max_type !== null ? $calculated_max_type->getSingleIntLiteral()->value : null;
-
- $new_result_type = new Union([new TIntRange($min_value, $max_value)]);
- } else {
- $new_result_type = Type::getInt(true);
- }
-
- $result_type = Type::combineUnionTypes($new_result_type, $result_type);
- }
-
- private static function analyzePowBetweenIntRange(
- ?Union &$result_type,
- TIntRange $left_type_part,
- TIntRange $right_type_part
- ): void {
- //If Pow first operand is negative, the result could be positive or negative, else it will be positive
- //If Pow second operand is negative, the result will be float, if it's 0, it will be 1/-1, else positive
- if ($left_type_part->isPositive()) {
- if ($right_type_part->isPositive()) {
- $new_result_type = new Union([new TIntRange(1, null)]);
- } elseif ($right_type_part->isNegative()) {
- $new_result_type = Type::getFloat();
- } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) {
- $new_result_type = Type::getInt(true, 1);
- } else {
- //$right_type_part may be a mix of positive, negative and 0
- $new_result_type = new Union([new TInt(), new TFloat()]);
- }
- } elseif ($left_type_part->isNegative()) {
- if ($right_type_part->isPositive()) {
- if ($right_type_part->min_bound === $right_type_part->max_bound) {
- if ($right_type_part->max_bound % 2 === 0) {
- $new_result_type = new Union([new TIntRange(1, null)]);
- } else {
- $new_result_type = new Union([new TIntRange(null, -1)]);
- }
- } else {
- $new_result_type = Type::getInt(true);
- }
- } elseif ($right_type_part->isNegative()) {
- $new_result_type = Type::getFloat();
- } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) {
- $new_result_type = Type::getInt(true, -1);
- } else {
- //$right_type_part may be a mix of positive, negative and 0
- $new_result_type = new Union([new TInt(), new TFloat()]);
- }
- } elseif ($left_type_part->min_bound === 0 && $left_type_part->max_bound === 0) {
- if ($right_type_part->isPositive()) {
- $new_result_type = Type::getInt(true, 0);
- } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) {
- $new_result_type = Type::getInt(true, 1);
- } else {
- //technically could be a float(INF)...
- $new_result_type = Type::getEmpty();
- }
- } else {
- //$left_type_part may be a mix of positive, negative and 0
- if ($right_type_part->isPositive()) {
- if ($right_type_part->min_bound === $right_type_part->max_bound
- && $right_type_part->max_bound % 2 === 0
- ) {
- $new_result_type = new Union([new TIntRange(1, null)]);
- } else {
- $new_result_type = Type::getInt(true);
- }
- } elseif ($right_type_part->isNegative()) {
- $new_result_type = Type::getFloat();
- } elseif ($right_type_part->min_bound === 0 && $right_type_part->max_bound === 0) {
- $new_result_type = Type::getInt(true, 1);
- } else {
- //$left_type_part may be a mix of positive, negative and 0
- $new_result_type = new Union([new TInt(), new TFloat()]);
- }
- }
-
- $result_type = Type::combineUnionTypes($new_result_type, $result_type);
- }
-
- private static function analyzeModBetweenIntRange(
- ?Union &$result_type,
- TIntRange $left_type_part,
- TIntRange $right_type_part
- ): void {
- //result of Mod is not directly dependant on the bounds of the range
- if ($right_type_part->min_bound !== null && $right_type_part->min_bound === $right_type_part->max_bound) {
- //if the second operand is a literal, we can be pretty detailed
- if ($right_type_part->max_bound === 0) {
- $new_result_type = Type::getEmpty();
- } else {
- if ($left_type_part->isPositiveOrZero()) {
- if ($right_type_part->isPositive()) {
- $max = $right_type_part->min_bound - 1;
- $new_result_type = new Union([new TIntRange(0, $max)]);
- } else {
- $max = $right_type_part->min_bound + 1;
- $new_result_type = new Union([new TIntRange($max, 0)]);
- }
- } elseif ($left_type_part->isNegativeOrZero()) {
- if ($right_type_part->isPositive()) {
- $max = $right_type_part->min_bound - 1;
- $new_result_type = new Union([new TIntRange(-$max, 0)]);
- } else {
- $max = $right_type_part->min_bound + 1;
- $new_result_type = new Union([new TIntRange(-$max, 0)]);
- }
- } else {
- if ($right_type_part->isPositive()) {
- $max = $right_type_part->min_bound - 1;
- } else {
- $max = -$right_type_part->min_bound - 1;
- }
- $new_result_type = new Union([new TIntRange(-$max, $max)]);
- }
- }
- } elseif ($right_type_part->isPositive()) {
- if ($left_type_part->isPositiveOrZero()) {
- if ($right_type_part->max_bound !== null) {
- //we now that the result will be a range between 0 and $right->max - 1
- $new_result_type = new Union(
- [new TIntRange(0, $right_type_part->max_bound - 1)]
- );
- } else {
- $new_result_type = new Union([new TIntRange(0, null)]);
- }
- } elseif ($left_type_part->isNegativeOrZero()) {
- $new_result_type = new Union([new TIntRange(null, 0)]);
- } else {
- $new_result_type = Type::getInt(true);
- }
- } elseif ($right_type_part->isNegative()) {
- if ($left_type_part->isPositiveOrZero()) {
- $new_result_type = new Union([new TIntRange(null, 0)]);
- } elseif ($left_type_part->isNegativeOrZero()) {
- $new_result_type = new Union([new TIntRange(null, 0)]);
- } else {
- $new_result_type = Type::getInt(true);
- }
- } else {
- $new_result_type = Type::getInt(true);
- }
-
- $result_type = Type::combineUnionTypes(
- $new_result_type,
- $result_type
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php
deleted file mode 100644
index f5c7580..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/CoalesceAnalyzer.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\BinaryOp;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Node\Expr\VirtualIsset;
-use Psalm\Node\Expr\VirtualTernary;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Type;
-
-use function substr;
-
-/**
- * @internal
- */
-class CoalesceAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BinaryOp\Coalesce $stmt,
- Context $context
- ): bool {
- $left_expr = $stmt->left;
-
- $root_expr = $left_expr;
-
- while ($root_expr instanceof PhpParser\Node\Expr\ArrayDimFetch
- || $root_expr instanceof PhpParser\Node\Expr\PropertyFetch
- ) {
- $root_expr = $root_expr->var;
- }
-
- if ($root_expr instanceof PhpParser\Node\Expr\FuncCall
- || $root_expr instanceof PhpParser\Node\Expr\MethodCall
- || $root_expr instanceof PhpParser\Node\Expr\StaticCall
- || $root_expr instanceof PhpParser\Node\Expr\Cast
- || $root_expr instanceof PhpParser\Node\Expr\NullsafePropertyFetch
- || $root_expr instanceof PhpParser\Node\Expr\NullsafeMethodCall
- || $root_expr instanceof PhpParser\Node\Expr\Ternary
- ) {
- $left_var_id = '$<tmp coalesce var>' . (int) $left_expr->getAttribute('startFilePos');
-
- $cloned = clone $context;
- $cloned->inside_isset = true;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $left_expr, $cloned);
-
- $condition_type = $statements_analyzer->node_data->getType($left_expr) ?? Type::getMixed();
-
- if ($root_expr !== $left_expr) {
- $condition_type->possibly_undefined = true;
- }
-
- $context->vars_in_scope[$left_var_id] = $condition_type;
-
- $left_expr = new VirtualVariable(
- substr($left_var_id, 1),
- $left_expr->getAttributes()
- );
- }
-
- $ternary = new VirtualTernary(
- new VirtualIsset(
- [$left_expr],
- $stmt->left->getAttributes()
- ),
- $left_expr,
- $stmt->right,
- $stmt->getAttributes()
- );
-
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $ternary, $context);
-
- $ternary_type = $statements_analyzer->node_data->getType($ternary) ?? Type::getMixed();
-
- $statements_analyzer->node_data = $old_node_data;
-
- $statements_analyzer->node_data->setType($stmt, $ternary_type);
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php
deleted file mode 100644
index e317de4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/ConcatAnalyzer.php
+++ /dev/null
@@ -1,432 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\BinaryOp;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\FalseOperand;
-use Psalm\Issue\ImplicitToStringCast;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\Issue\InvalidOperand;
-use Psalm\Issue\MixedOperand;
-use Psalm\Issue\NullOperand;
-use Psalm\Issue\PossiblyFalseOperand;
-use Psalm\Issue\PossiblyInvalidOperand;
-use Psalm\Issue\PossiblyNullOperand;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function assert;
-use function count;
-use function reset;
-use function strlen;
-
-/**
- * @internal
- */
-class ConcatAnalyzer
-{
- private const MAX_LITERALS = 64;
-
- /**
- * @param Union|null $result_type
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $left,
- PhpParser\Node\Expr $right,
- Context $context,
- Union &$result_type = null
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- $left_type = $statements_analyzer->node_data->getType($left);
- $right_type = $statements_analyzer->node_data->getType($right);
- $config = Config::getInstance();
-
- if ($left_type && $right_type) {
- $result_type = Type::getString();
-
- if ($left_type->hasMixed() || $right_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($left_type->hasMixed()) {
- $arg_location = new CodeLocation($statements_analyzer->getSource(), $left);
-
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($left_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- if ($origin_location && $origin_location->getHash() === $arg_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedOperand(
- 'Left operand cannot be mixed',
- $arg_location,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $arg_location = new CodeLocation($statements_analyzer->getSource(), $right);
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($right_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- if ($origin_location && $origin_location->getHash() === $arg_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedOperand(
- 'Right operand cannot be mixed',
- $arg_location,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return;
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- self::analyzeOperand($statements_analyzer, $left, $left_type, 'Left', $context);
- self::analyzeOperand($statements_analyzer, $right, $right_type, 'Right', $context);
-
- // If both types are specific literals, combine them into new literals
- $literal_concat = false;
-
- if ($left_type->allSpecificLiterals() && $right_type->allSpecificLiterals()) {
- $left_type_parts = $left_type->getAtomicTypes();
- $right_type_parts = $right_type->getAtomicTypes();
- $combinations = count($left_type_parts) * count($right_type_parts);
- if ($combinations < self::MAX_LITERALS) {
- $literal_concat = true;
- $result_type_parts = [];
-
- foreach ($left_type->getAtomicTypes() as $left_type_part) {
- foreach ($right_type->getAtomicTypes() as $right_type_part) {
- $literal = $left_type_part->value . $right_type_part->value;
- if (strlen($literal) >= $config->max_string_length) {
- // Literal too long, use non-literal type instead
- $literal_concat = false;
- break 2;
- }
-
- $result_type_parts[] = new TLiteralString($literal);
- }
- }
-
- if ($literal_concat) {
- assert(count($result_type_parts) === $combinations);
- assert(count($result_type_parts) !== 0); // #8163
- $result_type = new Union($result_type_parts);
- }
- }
- }
-
- if (!$literal_concat) {
- $numeric_type = Type::getNumericString();
- $numeric_type->addType(new TInt());
- $numeric_type->addType(new TFloat());
- $left_is_numeric = UnionTypeComparator::isContainedBy(
- $codebase,
- $left_type,
- $numeric_type
- );
-
- if ($left_is_numeric) {
- $right_uint = Type::getPositiveInt();
- $right_uint->addType(new TLiteralInt(0));
- $right_is_uint = UnionTypeComparator::isContainedBy(
- $codebase,
- $right_type,
- $right_uint
- );
-
- if ($right_is_uint) {
- $result_type = Type::getNumericString();
- return;
- }
- }
-
- $lowercase_type = clone $numeric_type;
- $lowercase_type->addType(new TLowercaseString());
-
- $all_lowercase = UnionTypeComparator::isContainedBy(
- $codebase,
- $left_type,
- $lowercase_type
- ) && UnionTypeComparator::isContainedBy(
- $codebase,
- $right_type,
- $lowercase_type
- );
-
- $non_empty_string = clone $numeric_type;
- $non_empty_string->addType(new TNonEmptyString());
-
- $has_non_empty = UnionTypeComparator::isContainedBy(
- $codebase,
- $left_type,
- $non_empty_string
- ) || UnionTypeComparator::isContainedBy(
- $codebase,
- $right_type,
- $non_empty_string
- );
-
- $all_literals = $left_type->allLiterals() && $right_type->allLiterals();
-
- if ($has_non_empty) {
- if ($all_literals) {
- $result_type = new Union([new TNonEmptyNonspecificLiteralString]);
- } elseif ($all_lowercase) {
- $result_type = Type::getNonEmptyLowercaseString();
- } else {
- $result_type = Type::getNonEmptyString();
- }
- } else {
- if ($all_literals) {
- $result_type = new Union([new TNonspecificLiteralString]);
- } elseif ($all_lowercase) {
- $result_type = Type::getLowercaseString();
- } else {
- $result_type = Type::getString();
- }
- }
- }
- }
- }
-
- private static function analyzeOperand(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $operand,
- Union $operand_type,
- string $side,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
- $config = Config::getInstance();
-
- if ($operand_type->isNull()) {
- IssueBuffer::maybeAdd(
- new NullOperand(
- 'Cannot concatenate with a ' . $operand_type,
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($operand_type->isFalse()) {
- IssueBuffer::maybeAdd(
- new FalseOperand(
- 'Cannot concatenate with a ' . $operand_type,
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($operand_type->isNullable() && !$operand_type->ignore_nullable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyNullOperand(
- 'Cannot concatenate with a possibly null ' . $operand_type,
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($operand_type->isFalsable() && !$operand_type->ignore_falsable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyFalseOperand(
- 'Cannot concatenate with a possibly false ' . $operand_type,
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $operand_type_match = true;
- $has_valid_operand = false;
- $comparison_result = new TypeComparisonResult();
-
- foreach ($operand_type->getAtomicTypes() as $operand_type_part) {
- if ($operand_type_part instanceof TTemplateParam && !$operand_type_part->as->isString()) {
- IssueBuffer::maybeAdd(
- new MixedOperand(
- "$side operand cannot be a non-string template param",
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($operand_type_part instanceof TNull || $operand_type_part instanceof TFalse) {
- continue;
- }
-
- $operand_type_part_match = AtomicTypeComparator::isContainedBy(
- $codebase,
- $operand_type_part,
- new TString,
- false,
- false,
- $comparison_result
- );
-
- $operand_type_match = $operand_type_match && $operand_type_part_match;
-
- $has_valid_operand = $has_valid_operand || $operand_type_part_match;
-
- if ($comparison_result->to_string_cast && $config->strict_binary_operands) {
- IssueBuffer::maybeAdd(
- new ImplicitToStringCast(
- "$side side of concat op expects string, '$operand_type' provided with a __toString method",
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- foreach ($operand_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TNamedObject) {
- $to_string_method_id = new MethodIdentifier(
- $atomic_type->value,
- '__tostring'
- );
-
- if ($codebase->methods->methodExists(
- $to_string_method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $operand)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath()
- )) {
- try {
- $storage = $codebase->methods->getStorage($to_string_method_id);
- } catch (UnexpectedValueException $e) {
- continue;
- }
-
- if ($context->mutation_free && !$storage->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method '
- . $atomic_type->value . '::__toString from a pure context',
- new CodeLocation($statements_analyzer, $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
- }
- }
- }
-
- if (!$operand_type_match
- && (!$comparison_result->scalar_type_match_found || $config->strict_binary_operands)
- ) {
- if ($has_valid_operand) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidOperand(
- 'Cannot concatenate with a ' . $operand_type,
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidOperand(
- 'Cannot concatenate with a ' . $operand_type,
- new CodeLocation($statements_analyzer->getSource(), $operand)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php
deleted file mode 100644
index c438111..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/NonComparisonOpAnalyzer.php
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\BinaryOp;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOpAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class NonComparisonOpAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BinaryOp $stmt,
- Context $context
- ): void {
- $stmt_left_type = $statements_analyzer->node_data->getType($stmt->left);
- $stmt_right_type = $statements_analyzer->node_data->getType($stmt->right);
-
- if (!$stmt_left_type || !$stmt_right_type) {
- return;
- }
-
- if (($stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd
- )
- && $stmt_left_type->hasString()
- && $stmt_right_type->hasString()
- ) {
- $stmt_type = Type::getString();
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
- BinaryOpAnalyzer::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- 'nondivop'
- );
-
- return;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Plus
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Minus
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Mod
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Mul
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Pow
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\ShiftLeft
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\ShiftRight
- ) {
- ArithmeticOpAnalyzer::analyze(
- $statements_analyzer,
- $statements_analyzer->node_data,
- $stmt->left,
- $stmt->right,
- $stmt,
- $result_type,
- $context
- );
-
- if (!$result_type) {
- $result_type = new Union([new TInt(), new TFloat()]);
- }
-
- $statements_analyzer->node_data->setType($stmt, $result_type);
-
- BinaryOpAnalyzer::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- 'nondivop'
- );
-
- return;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalXor) {
- if ($stmt_left_type->hasBool() || $stmt_right_type->hasBool()) {
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
- }
-
- BinaryOpAnalyzer::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- 'xor'
- );
-
- return;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Div) {
- ArithmeticOpAnalyzer::analyze(
- $statements_analyzer,
- $statements_analyzer->node_data,
- $stmt->left,
- $stmt->right,
- $stmt,
- $result_type,
- $context
- );
-
- if (!$result_type) {
- $result_type = new Union([new TInt(), new TFloat()]);
- }
-
- $statements_analyzer->node_data->setType($stmt, $result_type);
-
- BinaryOpAnalyzer::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- 'div'
- );
-
- return;
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php
deleted file mode 100644
index d584d43..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOp/OrAnalyzer.php
+++ /dev/null
@@ -1,411 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\BinaryOp;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Exception\ScopeAnalysisException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\Statements\Block\IfConditionalAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElse\IfAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElseAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Internal\Type\AssertionReconciler;
-use Psalm\Node\Expr\VirtualBooleanNot;
-use Psalm\Node\Stmt\VirtualExpression;
-use Psalm\Node\Stmt\VirtualIf;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-
-use function array_diff_key;
-use function array_filter;
-use function array_map;
-use function array_merge;
-use function array_values;
-use function count;
-use function in_array;
-use function spl_object_id;
-
-/**
- * @internal
- */
-class OrAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BinaryOp $stmt,
- Context $context,
- bool $from_stmt = false
- ): bool {
- if ($from_stmt) {
- $fake_if_stmt = new VirtualIf(
- new VirtualBooleanNot($stmt->left, $stmt->left->getAttributes()),
- [
- 'stmts' => [
- new VirtualExpression(
- $stmt->right
- )
- ]
- ],
- $stmt->getAttributes()
- );
-
- return IfElseAnalyzer::analyze($statements_analyzer, $fake_if_stmt, $context) !== false;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $post_leaving_if_context = null;
-
- if (!$stmt->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || !$stmt->left->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || !$stmt->left->left->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- ) {
- $if_scope = new IfScope();
-
- try {
- $if_conditional_scope = IfConditionalAnalyzer::analyze(
- $statements_analyzer,
- $stmt->left,
- $context,
- $codebase,
- $if_scope,
- $context->branch_point ?: (int) $stmt->getAttribute('startFilePos')
- );
-
- $left_context = $if_conditional_scope->if_context;
-
- $left_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
- $left_assigned_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
-
- if ($stmt->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr) {
- $post_leaving_if_context = clone $context;
- }
- } catch (ScopeAnalysisException $e) {
- return false;
- }
- } else {
- $pre_referenced_var_ids = $context->referenced_var_ids;
- $context->referenced_var_ids = [];
-
- $pre_assigned_var_ids = $context->assigned_var_ids;
-
- $post_leaving_if_context = clone $context;
-
- $left_context = clone $context;
- $left_context->parent_context = $context;
- $left_context->if_context = null;
- $left_context->assigned_var_ids = [];
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $left_context) === false) {
- return false;
- }
-
- IfConditionalAnalyzer::handleParadoxicalCondition($statements_analyzer, $stmt->left);
-
- foreach ($left_context->vars_in_scope as $var_id => $type) {
- if (!isset($context->vars_in_scope[$var_id])) {
- if (isset($left_context->assigned_var_ids[$var_id])) {
- $context->vars_in_scope[$var_id] = clone $type;
- }
- } else {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->vars_in_scope[$var_id],
- $type,
- $codebase
- );
- }
- }
-
- $left_referenced_var_ids = $left_context->referenced_var_ids;
- $left_context->referenced_var_ids = array_merge($pre_referenced_var_ids, $left_referenced_var_ids);
-
- $left_assigned_var_ids = array_diff_key($left_context->assigned_var_ids, $pre_assigned_var_ids);
- $left_context->assigned_var_ids = array_merge($pre_assigned_var_ids, $left_context->assigned_var_ids);
-
- $left_referenced_var_ids = array_diff_key($left_referenced_var_ids, $left_assigned_var_ids);
- }
-
- $left_cond_id = spl_object_id($stmt->left);
-
- $left_clauses = FormulaGenerator::getFormula(
- $left_cond_id,
- $left_cond_id,
- $stmt->left,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- try {
- $negated_left_clauses = Algebra::negateFormula($left_clauses);
- } catch (ComplicatedExpressionException $e) {
- try {
- $negated_left_clauses = FormulaGenerator::getFormula(
- $left_cond_id,
- $left_cond_id,
- new VirtualBooleanNot($stmt->left),
- $context->self,
- $statements_analyzer,
- $codebase,
- false
- );
- } catch (ComplicatedExpressionException $e) {
- return false;
- }
- }
-
- if ($left_context->reconciled_expression_clauses) {
- $reconciled_expression_clauses = $left_context->reconciled_expression_clauses;
-
- $negated_left_clauses = array_values(
- array_filter(
- $negated_left_clauses,
- function ($c) use ($reconciled_expression_clauses): bool {
- return !in_array($c->hash, $reconciled_expression_clauses);
- }
- )
- );
-
- if (count($negated_left_clauses) === 1
- && $negated_left_clauses[0]->wedge
- && !$negated_left_clauses[0]->possibilities
- ) {
- $negated_left_clauses = [];
- }
- }
-
- $clauses_for_right_analysis = Algebra::simplifyCNF(
- array_merge(
- $context->clauses,
- $negated_left_clauses
- )
- );
-
- $active_negated_type_assertions = [];
-
- $negated_type_assertions = Algebra::getTruthsFromFormula(
- $clauses_for_right_analysis,
- $left_cond_id,
- $left_referenced_var_ids,
- $active_negated_type_assertions
- );
-
- $changed_var_ids = [];
-
- $right_context = clone $context;
-
- if ($stmt->left instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- && $left_assigned_var_ids
- && $post_leaving_if_context
- ) {
- IfAnalyzer::addConditionallyAssignedVarsToContext(
- $statements_analyzer,
- $stmt->left,
- $post_leaving_if_context,
- $right_context,
- $left_assigned_var_ids
- );
- }
-
- if ($negated_type_assertions) {
- // while in an or, we allow scope to boil over to support
- // statements of the form if ($x === null || $x->foo())
- $right_vars_in_scope = Reconciler::reconcileKeyedTypes(
- $negated_type_assertions,
- $active_negated_type_assertions,
- $right_context->vars_in_scope,
- $changed_var_ids,
- $left_referenced_var_ids,
- $statements_analyzer,
- [],
- $left_context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt->left),
- !$context->inside_negation
- );
- $right_context->vars_in_scope = $right_vars_in_scope;
- }
-
- $right_context->clauses = $clauses_for_right_analysis;
-
- if ($changed_var_ids) {
- $partitioned_clauses = Context::removeReconciledClauses($right_context->clauses, $changed_var_ids);
- $right_context->clauses = $partitioned_clauses[0];
- $right_context->reconciled_expression_clauses = array_merge(
- $context->reconciled_expression_clauses,
- array_map(
- function ($c) {
- return $c->hash;
- },
- $partitioned_clauses[1]
- )
- );
-
- $partitioned_clauses = Context::removeReconciledClauses($context->clauses, $changed_var_ids);
- $context->clauses = $partitioned_clauses[0];
- $context->reconciled_expression_clauses = array_merge(
- $context->reconciled_expression_clauses,
- array_map(
- function ($c) {
- return $c->hash;
- },
- $partitioned_clauses[1]
- )
- );
- }
-
- $right_context->if_context = null;
-
- $pre_referenced_var_ids = $right_context->referenced_var_ids;
- $right_context->referenced_var_ids = [];
-
- $pre_assigned_var_ids = $right_context->assigned_var_ids;
- $right_context->assigned_var_ids = [];
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->right, $right_context) === false) {
- return false;
- }
-
- IfConditionalAnalyzer::handleParadoxicalCondition($statements_analyzer, $stmt->right);
-
- $right_referenced_var_ids = $right_context->referenced_var_ids;
- $right_context->referenced_var_ids = array_merge($pre_referenced_var_ids, $right_referenced_var_ids);
-
- $right_assigned_var_ids = $right_context->assigned_var_ids;
- $right_context->assigned_var_ids = array_merge($pre_assigned_var_ids, $right_assigned_var_ids);
-
- $right_cond_id = spl_object_id($stmt->right);
-
- $right_clauses = FormulaGenerator::getFormula(
- $right_cond_id,
- $right_cond_id,
- $stmt->right,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- $clauses_for_right_analysis = Context::removeReconciledClauses(
- $clauses_for_right_analysis,
- $right_assigned_var_ids
- )[0];
-
- $combined_right_clauses = Algebra::simplifyCNF(
- array_merge($clauses_for_right_analysis, $right_clauses)
- );
-
- $active_right_type_assertions = [];
-
- $right_type_assertions = Algebra::getTruthsFromFormula(
- $combined_right_clauses,
- $right_cond_id,
- $right_referenced_var_ids,
- $active_right_type_assertions
- );
-
- if ($right_type_assertions) {
- $right_changed_var_ids = [];
-
- Reconciler::reconcileKeyedTypes(
- $right_type_assertions,
- $active_right_type_assertions,
- $right_context->vars_in_scope,
- $right_changed_var_ids,
- $right_referenced_var_ids,
- $statements_analyzer,
- [],
- $left_context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt->right),
- $context->inside_negation
- );
- }
-
- if (!($stmt->right instanceof PhpParser\Node\Expr\Exit_)) {
- foreach ($right_context->vars_in_scope as $var_id => $type) {
- if (isset($context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->vars_in_scope[$var_id],
- $type,
- $codebase
- );
- }
- }
- } elseif ($stmt->left instanceof PhpParser\Node\Expr\Assign) {
- $var_id = ExpressionIdentifier::getVarId($stmt->left->var, $context->self);
-
- if ($var_id && isset($left_context->vars_in_scope[$var_id])) {
- $left_inferred_reconciled = AssertionReconciler::reconcile(
- '!falsy',
- clone $left_context->vars_in_scope[$var_id],
- '',
- $statements_analyzer,
- $context->inside_loop,
- [],
- new CodeLocation($statements_analyzer->getSource(), $stmt->left),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $context->vars_in_scope[$var_id] = $left_inferred_reconciled;
- }
- }
-
- if ($context->inside_conditional) {
- $context->updateChecks($right_context);
- }
-
- $context->referenced_var_ids = array_merge(
- $right_context->referenced_var_ids,
- $context->referenced_var_ids
- );
-
- $context->assigned_var_ids = array_merge(
- $context->assigned_var_ids,
- $right_context->assigned_var_ids
- );
-
- if ($context->if_context) {
- $if_context = $context->if_context;
-
- foreach ($right_context->vars_in_scope as $var_id => $type) {
- if (isset($if_context->vars_in_scope[$var_id])) {
- $if_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $if_context->vars_in_scope[$var_id],
- $codebase
- );
- } elseif (isset($left_context->vars_in_scope[$var_id])) {
- $if_context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $type,
- $left_context->vars_in_scope[$var_id],
- $codebase
- );
- }
- }
-
- $if_context->referenced_var_ids = array_merge(
- $context->referenced_var_ids,
- $if_context->referenced_var_ids
- );
-
- $if_context->assigned_var_ids = array_merge(
- $context->assigned_var_ids,
- $if_context->assigned_var_ids
- );
-
- $if_context->updateChecks($context);
- }
-
- $context->vars_possibly_in_scope = array_merge(
- $right_context->vars_possibly_in_scope,
- $context->vars_possibly_in_scope
- );
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php
deleted file mode 100644
index 5a4108c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BinaryOpAnalyzer.php
+++ /dev/null
@@ -1,530 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\AndAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\CoalesceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\ConcatAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\NonComparisonOpAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\OrAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Issue\DocblockTypeContradiction;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\Issue\InvalidOperand;
-use Psalm\Issue\RedundantCondition;
-use Psalm\Issue\RedundantConditionGivenDocblockType;
-use Psalm\Issue\TypeDoesNotContainType;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function in_array;
-use function strlen;
-
-/**
- * @internal
- */
-class BinaryOpAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BinaryOp $stmt,
- Context $context,
- int $nesting = 0,
- bool $from_stmt = false
- ): bool {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat && $nesting > 100) {
- $statements_analyzer->node_data->setType($stmt, Type::getString());
-
- // ignore deeply-nested string concatenation
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd ||
- $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalAnd
- ) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- $expr_result = AndAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- $from_stmt
- );
-
- $context->inside_general_use = $was_inside_general_use;
-
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
-
- return $expr_result;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr ||
- $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
- ) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- $expr_result = OrAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- $from_stmt
- );
-
- $context->inside_general_use = $was_inside_general_use;
-
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
-
- return $expr_result;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Coalesce) {
- $expr_result = CoalesceAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context
- );
-
- self::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- 'coalesce'
- );
-
- return $expr_result;
- }
-
- if ($stmt->left instanceof PhpParser\Node\Expr\BinaryOp) {
- if (self::analyze($statements_analyzer, $stmt->left, $context, $nesting + 1) === false) {
- return false;
- }
- } else {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->left, $context) === false) {
- return false;
- }
- }
-
- if ($stmt->right instanceof PhpParser\Node\Expr\BinaryOp) {
- if (self::analyze($statements_analyzer, $stmt->right, $context, $nesting + 1) === false) {
- return false;
- }
- } else {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->right, $context) === false) {
- return false;
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
- $stmt_type = Type::getString();
-
- ConcatAnalyzer::analyze(
- $statements_analyzer,
- $stmt->left,
- $stmt->right,
- $context,
- $result_type
- );
-
- if ($result_type) {
- $stmt_type = $result_type;
- }
-
- if ($statements_analyzer->data_flow_graph
- && ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- || !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues()))
- ) {
- $stmt_left_type = $statements_analyzer->node_data->getType($stmt->left);
- $stmt_right_type = $statements_analyzer->node_data->getType($stmt->right);
-
- $var_location = new CodeLocation($statements_analyzer, $stmt);
-
- $new_parent_node = DataFlowNode::getForAssignment('concat', $var_location);
- $statements_analyzer->data_flow_graph->addNode($new_parent_node);
-
- $stmt_type->parent_nodes = [
- $new_parent_node->id => $new_parent_node
- ];
-
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- if ($stmt_left_type && $stmt_left_type->parent_nodes) {
- foreach ($stmt_left_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'concat',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- if ($stmt_right_type && $stmt_right_type->parent_nodes) {
- foreach ($stmt_right_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'concat',
- $added_taints,
- $removed_taints
- );
- }
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Spaceship) {
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union(
- [
- new TLiteralInt(-1),
- new TLiteralInt(0),
- new TLiteralInt(1)
- ]
- )
- );
-
- self::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- '<=>'
- );
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Greater
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Smaller
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
- ) {
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
-
- $stmt_left_type = $statements_analyzer->node_data->getType($stmt->left);
- $stmt_right_type = $statements_analyzer->node_data->getType($stmt->right);
-
- if (($stmt instanceof PhpParser\Node\Expr\BinaryOp\Greater
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Smaller
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual)
- && $statements_analyzer->getCodebase()->config->strict_binary_operands
- && $stmt_left_type
- && $stmt_right_type
- && (($stmt_left_type->isSingle() && $stmt_left_type->hasBool())
- || ($stmt_right_type->isSingle() && $stmt_right_type->hasBool()))
- ) {
- IssueBuffer::maybeAdd(
- new InvalidOperand(
- 'Cannot compare ' . $stmt_left_type->getId() . ' to ' . $stmt_right_type->getId(),
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical)
- && $stmt->left instanceof PhpParser\Node\Expr\FuncCall
- && $stmt->left->name instanceof PhpParser\Node\Name
- && $stmt->left->name->parts === ['substr']
- && isset($stmt->left->getArgs()[1])
- && $stmt_right_type
- && $stmt_right_type->hasLiteralString()
- ) {
- $from_type = $statements_analyzer->node_data->getType($stmt->left->getArgs()[1]->value);
-
- $length_type = isset($stmt->left->getArgs()[2])
- ? ($statements_analyzer->node_data->getType($stmt->left->getArgs()[2]->value) ?? Type::getMixed())
- : null;
-
- $string_length = null;
-
- if ($from_type && $from_type->isSingleIntLiteral() && $length_type === null) {
- $string_length = -$from_type->getSingleIntLiteral()->value;
- } elseif ($length_type && $length_type->isSingleIntLiteral()) {
- $string_length = $length_type->getSingleIntLiteral()->value;
- }
-
- if ($string_length > 0) {
- foreach ($stmt_right_type->getAtomicTypes() as $atomic_right_type) {
- if ($atomic_right_type instanceof TLiteralString) {
- if (strlen($atomic_right_type->value) !== $string_length) {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
- ) {
- if ($atomic_right_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- $atomic_right_type . ' string length is not ' . $string_length,
- new CodeLocation($statements_analyzer, $stmt),
- null
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- $atomic_right_type . ' string length is not ' . $string_length,
- new CodeLocation($statements_analyzer, $stmt),
- null
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } else {
- if ($atomic_right_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantConditionGivenDocblockType(
- $atomic_right_type . ' string length is never ' . $string_length,
- new CodeLocation($statements_analyzer, $stmt),
- null
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantCondition(
- $atomic_right_type . ' string length is never ' . $string_length,
- new CodeLocation($statements_analyzer, $stmt),
- null
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
- }
- }
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- && $stmt_left_type
- && $stmt_right_type
- && ($context->mutation_free || $codebase->alter_code)
- ) {
- self::checkForImpureEqualityComparison(
- $statements_analyzer,
- $stmt,
- $stmt_left_type,
- $stmt_right_type
- );
- }
-
- self::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->left,
- $stmt->right,
- 'comparison'
- );
-
- return true;
- }
-
- NonComparisonOpAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context
- );
-
- return true;
- }
-
- public static function addDataFlow(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- PhpParser\Node\Expr $left,
- PhpParser\Node\Expr $right,
- string $type = 'binaryop'
- ): void {
- if ($stmt->getLine() === -1) {
- throw new UnexpectedValueException('bad');
- }
- $result_type = $statements_analyzer->node_data->getType($stmt);
- if (!$result_type) {
- return;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $stmt instanceof PhpParser\Node\Expr\BinaryOp
- && !$stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat
- && !$stmt instanceof PhpParser\Node\Expr\BinaryOp\Coalesce
- && (!$stmt instanceof PhpParser\Node\Expr\BinaryOp\Plus || !$result_type->hasArray())
- ) {
- //among BinaryOp, only Concat and Coalesce can pass tainted value to the result. Also Plus on arrays only
- return;
- }
-
- if ($statements_analyzer->data_flow_graph) {
- $stmt_left_type = $statements_analyzer->node_data->getType($left);
- $stmt_right_type = $statements_analyzer->node_data->getType($right);
-
- $var_location = new CodeLocation($statements_analyzer, $stmt);
-
- $new_parent_node = DataFlowNode::getForAssignment($type, $var_location);
- $statements_analyzer->data_flow_graph->addNode($new_parent_node);
-
- $result_type->parent_nodes = [
- $new_parent_node->id => $new_parent_node
- ];
-
- if ($stmt_left_type && $stmt_left_type->parent_nodes) {
- foreach ($stmt_left_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath($parent_node, $new_parent_node, $type);
- }
- }
-
- if ($stmt_right_type && $stmt_right_type->parent_nodes) {
- foreach ($stmt_right_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath($parent_node, $new_parent_node, $type);
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\AssignOp
- && $statements_analyzer->data_flow_graph instanceof VariableUseGraph
- ) {
- $root_expr = $left;
-
- while ($root_expr instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- $root_expr = $root_expr->var;
- }
-
- if ($left instanceof PhpParser\Node\Expr\PropertyFetch) {
- $statements_analyzer->data_flow_graph->addPath(
- $new_parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'used-by-instance-property'
- );
- } if ($left instanceof PhpParser\Node\Expr\StaticPropertyFetch) {
- $statements_analyzer->data_flow_graph->addPath(
- $new_parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'use-in-static-property'
- );
- } elseif (!$left instanceof PhpParser\Node\Expr\Variable) {
- $statements_analyzer->data_flow_graph->addPath(
- $new_parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
- }
- }
-
- private static function checkForImpureEqualityComparison(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BinaryOp\Equal $stmt,
- Union $stmt_left_type,
- Union $stmt_right_type
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- if ($stmt_left_type->hasString() && $stmt_right_type->hasObjectType()) {
- foreach ($stmt_right_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TNamedObject) {
- try {
- $storage = $codebase->methods->getStorage(
- new MethodIdentifier(
- $atomic_type->value,
- '__tostring'
- )
- );
- } catch (UnexpectedValueException $e) {
- continue;
- }
-
- if (!$storage->mutation_free) {
- if ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- } else {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method '
- . $atomic_type->value . '::__toString from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
- } elseif ($stmt_right_type->hasString() && $stmt_left_type->hasObjectType()) {
- foreach ($stmt_left_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TNamedObject) {
- try {
- $storage = $codebase->methods->getStorage(
- new MethodIdentifier(
- $atomic_type->value,
- '__tostring'
- )
- );
- } catch (UnexpectedValueException $e) {
- continue;
- }
-
- if (!$storage->mutation_free) {
- if ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- } else {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method '
- . $atomic_type->value . '::__toString from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php
deleted file mode 100644
index fa8c19d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BitwiseNotAnalyzer.php
+++ /dev/null
@@ -1,124 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Issue\InvalidOperand;
-use Psalm\Issue\PossiblyInvalidOperand;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Union;
-
-class BitwiseNotAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BitwiseNot $stmt,
- Context $context
- ): bool {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- if (!($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr))) {
- $statements_analyzer->node_data->setType($stmt, new Union([new TInt(), new TString()]));
- } elseif ($stmt_expr_type->isMixed()) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- } else {
- $acceptable_types = [];
- $unacceptable_type = null;
- $has_valid_operand = false;
-
- foreach ($stmt_expr_type->getAtomicTypes() as $type_string => $type_part) {
- if ($type_part instanceof TInt || $type_part instanceof TString) {
- if ($type_part instanceof TLiteralInt) {
- $type_part->value = ~$type_part->value;
- } elseif ($type_part instanceof TLiteralString) {
- $type_part->value = ~$type_part->value;
- }
-
- $acceptable_types[] = $type_part;
- $has_valid_operand = true;
- } elseif ($type_part instanceof TFloat) {
- $type_part = ($type_part instanceof TLiteralFloat) ?
- new TLiteralInt(~$type_part->value) :
- new TInt;
-
- $stmt_expr_type->removeType($type_string);
- $stmt_expr_type->addType($type_part);
-
- $acceptable_types[] = $type_part;
- $has_valid_operand = true;
- } elseif (!$unacceptable_type) {
- $unacceptable_type = $type_part;
- }
- }
-
- if ($unacceptable_type || !$acceptable_types) {
- $message = 'Cannot negate a non-numeric non-string type ' . $unacceptable_type;
- if ($has_valid_operand) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidOperand(
- $message,
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidOperand(
- $message,
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- } else {
- $statements_analyzer->node_data->setType($stmt, new Union($acceptable_types));
- }
- }
-
- self::addDataFlow($statements_analyzer, $stmt, $stmt->expr);
-
- return true;
- }
-
- private static function addDataFlow(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- PhpParser\Node\Expr $value
- ): void {
- $result_type = $statements_analyzer->node_data->getType($stmt);
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph && $result_type) {
- $var_location = new CodeLocation($statements_analyzer, $stmt);
-
- $stmt_value_type = $statements_analyzer->node_data->getType($value);
-
- $new_parent_node = DataFlowNode::getForAssignment('bitwisenot', $var_location);
- $statements_analyzer->data_flow_graph->addNode($new_parent_node);
- $result_type->parent_nodes = [
- $new_parent_node->id => $new_parent_node,
- ];
-
- if ($stmt_value_type && $stmt_value_type->parent_nodes) {
- foreach ($stmt_value_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath($parent_node, $new_parent_node, 'bitwisenot');
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php
deleted file mode 100644
index cf135ee..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/BooleanNotAnalyzer.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-
-class BooleanNotAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\BooleanNot $stmt,
- Context $context
- ): bool {
-
-
- $inside_negation = $context->inside_negation;
-
- $context->inside_negation = !$inside_negation;
-
- $result = ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context);
-
- $context->inside_negation = $inside_negation;
-
- $expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- $stmt_type = Type::getBool();
- if ($expr_type) {
- if ($expr_type->isAlwaysTruthy()) {
- $stmt_type = Type::getFalse();
- } elseif ($expr_type->isAlwaysFalsy()) {
- $stmt_type = Type::getTrue();
- }
-
- $stmt_type->from_docblock = $expr_type->from_docblock;
- $stmt_type->parent_nodes = $expr_type->parent_nodes;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return $result;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php
deleted file mode 100644
index 84b777c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php
+++ /dev/null
@@ -1,1629 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\MethodAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CastAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\ConstantTypeResolver;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\CallableTypeComparator;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateBound;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ArgumentTypeCoercion;
-use Psalm\Issue\ImplicitToStringCast;
-use Psalm\Issue\InvalidArgument;
-use Psalm\Issue\InvalidLiteralArgument;
-use Psalm\Issue\InvalidScalarArgument;
-use Psalm\Issue\MixedArgument;
-use Psalm\Issue\MixedArgumentTypeCoercion;
-use Psalm\Issue\NamedArgumentNotAllowed;
-use Psalm\Issue\NoValue;
-use Psalm\Issue\NullArgument;
-use Psalm\Issue\PossiblyFalseArgument;
-use Psalm\Issue\PossiblyInvalidArgument;
-use Psalm\Issue\PossiblyNullArgument;
-use Psalm\IssueBuffer;
-use Psalm\Node\VirtualArg;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function count;
-use function explode;
-use function in_array;
-use function ord;
-use function preg_split;
-use function reset;
-use function strpos;
-use function strtolower;
-use function substr;
-
-use const PREG_SPLIT_NO_EMPTY;
-
-/**
- * @internal
- */
-class ArgumentAnalyzer
-{
- /**
- * @param array<string, array<string, Union>> $class_generic_params
- * @return false|null
- */
- public static function checkArgumentMatches(
- StatementsAnalyzer $statements_analyzer,
- ?string $cased_method_id,
- ?MethodIdentifier $method_id,
- ?string $self_fq_class_name,
- ?string $static_fq_class_name,
- CodeLocation $function_call_location,
- ?FunctionLikeParameter $function_param,
- int $argument_offset,
- int $unpacked_argument_offset,
- bool $allow_named_args,
- PhpParser\Node\Arg $arg,
- ?Union $arg_value_type,
- Context $context,
- array $class_generic_params,
- ?TemplateResult $template_result,
- bool $specialize_taint,
- bool $in_call_map
- ): ?bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$arg_value_type) {
- if ($function_param && !$function_param->by_ref) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- $param_type = $function_param->type;
-
- if ($function_param->is_variadic
- && $param_type
- && $param_type->hasArray()
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TList|TArray
- */
- $array_type = $param_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TList) {
- $param_type = $array_type->type_param;
- } else {
- $param_type = $array_type->type_params[1];
- }
- }
-
- if ($param_type && !$param_type->hasMixed()) {
- IssueBuffer::maybeAdd(
- new MixedArgument(
- 'Argument ' . ($argument_offset + 1) . ' of ' . $cased_method_id
- . ' cannot be mixed, expecting ' . $param_type,
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return null;
- }
-
- if (!$function_param) {
- return null;
- }
-
- if ($function_param->expect_variable
- && $arg_value_type->isSingleStringLiteral()
- && !$arg->value instanceof PhpParser\Node\Scalar\MagicConst
- && !$arg->value instanceof PhpParser\Node\Expr\ConstFetch
- && !$arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
- ) {
- $values = preg_split('//u', $arg_value_type->getSingleStringLiteral()->value, -1, PREG_SPLIT_NO_EMPTY);
-
- if ($values !== false) {
- $prev_ord = 0;
-
- $gt_count = 0;
-
- foreach ($values as $value) {
- $ord = ord($value);
-
- if ($ord > $prev_ord) {
- $gt_count++;
- }
-
- $prev_ord = $ord;
- }
-
- if (count($values) < 12 || ($gt_count / count($values)) < 0.8) {
- IssueBuffer::maybeAdd(
- new InvalidLiteralArgument(
- 'Argument ' . ($argument_offset + 1) . ' of ' . $cased_method_id
- . ' expects a non-literal value, ' . $arg_value_type->getId() . ' provided',
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if (self::checkFunctionLikeTypeMatches(
- $statements_analyzer,
- $codebase,
- $cased_method_id,
- $method_id,
- $self_fq_class_name,
- $static_fq_class_name,
- $function_call_location,
- $function_param,
- $allow_named_args,
- $arg_value_type,
- $argument_offset,
- $unpacked_argument_offset,
- $arg,
- $context,
- $class_generic_params,
- $template_result,
- $specialize_taint,
- $in_call_map
- ) === false) {
- return false;
- }
-
- return null;
- }
-
- /**
- * @param array<string, array<string, Union>> $class_generic_params
- * @return false|null
- */
- private static function checkFunctionLikeTypeMatches(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- ?string $cased_method_id,
- ?MethodIdentifier $method_id,
- ?string $self_fq_class_name,
- ?string $static_fq_class_name,
- CodeLocation $function_call_location,
- FunctionLikeParameter $function_param,
- bool $allow_named_args,
- Union $arg_type,
- int $argument_offset,
- int $unpacked_argument_offset,
- PhpParser\Node\Arg $arg,
- Context $context,
- ?array $class_generic_params,
- ?TemplateResult $template_result,
- bool $specialize_taint,
- bool $in_call_map
- ): ?bool {
- if (!$function_param->type) {
- if (!$codebase->infer_types_from_usage && !$statements_analyzer->data_flow_graph) {
- return null;
- }
-
- $param_type = Type::getMixed();
- } else {
- $param_type = clone $function_param->type;
- }
-
- $bindable_template_params = [];
-
- if ($template_result) {
- $bindable_template_params = $param_type->getTemplateTypes();
- }
-
- $parent_class = null;
-
- $classlike_storage = null;
- $static_classlike_storage = null;
-
- if ($self_fq_class_name) {
- $classlike_storage = $codebase->classlike_storage_provider->get($self_fq_class_name);
- $parent_class = $classlike_storage->parent_class;
- $static_classlike_storage = $classlike_storage;
-
- if ($static_fq_class_name && $static_fq_class_name !== $self_fq_class_name) {
- $static_classlike_storage = $codebase->classlike_storage_provider->get($static_fq_class_name);
- }
- }
-
- $param_type = TypeExpander::expandUnion(
- $codebase,
- $param_type,
- $classlike_storage->name ?? null,
- $static_classlike_storage->name ?? null,
- $parent_class,
- true,
- false,
- $static_classlike_storage->final ?? false,
- true
- );
-
- if ($class_generic_params) {
- // here we're replacing the param types and arg types with the bound
- // class template params.
- //
- // For example, if we're operating on a class Foo with params TKey and TValue,
- // and we're calling a method "add(TKey $key, TValue $value)" on an instance
- // of that class where we know that TKey is int and TValue is string, then we
- // want to substitute the expected parameters so it's as if we were actually
- // calling "add(int $key, string $value)"
- $readonly_template_result = new TemplateResult($class_generic_params, []);
-
- // This flag ensures that the template results will never be written to
- // It also supercedes the `$add_lower_bounds` flag so that closure params
- // don’t get overwritten
- $readonly_template_result->readonly = true;
-
- $arg_value_type = $statements_analyzer->node_data->getType($arg->value);
-
- $param_type = TemplateStandinTypeReplacer::replace(
- $param_type,
- $readonly_template_result,
- $codebase,
- $statements_analyzer,
- $arg_value_type,
- $argument_offset,
- $context->self,
- $context->calling_function_id ?: $context->calling_method_id
- );
-
- $arg_type = TemplateStandinTypeReplacer::replace(
- $arg_type,
- $readonly_template_result,
- $codebase,
- $statements_analyzer,
- $arg_value_type,
- $argument_offset,
- $context->self,
- $context->calling_function_id ?: $context->calling_method_id
- );
- }
-
- if ($template_result && $template_result->template_types) {
- $arg_type_param = $arg_type;
-
- if ($arg->unpack) {
- $arg_type_param = null;
-
- foreach ($arg_type->getAtomicTypes() as $arg_atomic_type) {
- if ($arg_atomic_type instanceof TArray
- || $arg_atomic_type instanceof TList
- || $arg_atomic_type instanceof TKeyedArray
- ) {
- if ($arg_atomic_type instanceof TKeyedArray) {
- $arg_type_param = $arg_atomic_type->getGenericValueType();
- } elseif ($arg_atomic_type instanceof TList) {
- $arg_type_param = $arg_atomic_type->type_param;
- } else {
- $arg_type_param = $arg_atomic_type->type_params[1];
- }
- } elseif ($arg_atomic_type instanceof TIterable) {
- $arg_type_param = $arg_atomic_type->type_params[1];
- } elseif ($arg_atomic_type instanceof TNamedObject) {
- ForeachAnalyzer::getKeyValueParamsForTraversableObject(
- $arg_atomic_type,
- $codebase,
- $key_type,
- $arg_type_param
- );
- }
- }
-
- if (!$arg_type_param) {
- $arg_type_param = Type::getMixed();
- $arg_type_param->parent_nodes = $arg_type->parent_nodes;
- }
- }
-
- $param_type = TemplateStandinTypeReplacer::replace(
- $param_type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $arg_type_param,
- $argument_offset,
- !$statements_analyzer->isStatic()
- && (!$method_id || $method_id->method_name !== '__construct')
- ? $context->self
- : null,
- $context->calling_method_id ?: $context->calling_function_id
- );
-
- foreach ($bindable_template_params as $template_type) {
- if (!isset(
- $template_result->lower_bounds
- [$template_type->param_name]
- [$template_type->defining_class]
- )) {
- if (isset(
- $template_result->upper_bounds
- [$template_type->param_name]
- [$template_type->defining_class]
- )) {
- $template_result->lower_bounds[$template_type->param_name][$template_type->defining_class] = [
- new TemplateBound(
- clone $template_result->upper_bounds
- [$template_type->param_name]
- [$template_type->defining_class]->type
- )
- ];
- } else {
- $template_result->lower_bounds[$template_type->param_name][$template_type->defining_class] = [
- new TemplateBound(
- clone $template_type->as
- )
- ];
- }
- }
- }
-
- $param_type = TypeExpander::expandUnion(
- $codebase,
- $param_type,
- $classlike_storage->name ?? null,
- $static_classlike_storage->name ?? null,
- $parent_class,
- true,
- false,
- $static_classlike_storage->final ?? false,
- true
- );
- }
-
- $fleshed_out_signature_type = $function_param->signature_type
- ? TypeExpander::expandUnion(
- $codebase,
- $function_param->signature_type,
- $classlike_storage->name ?? null,
- $static_classlike_storage->name ?? null,
- $parent_class
- )
- : null;
-
- $unpacked_atomic_array = null;
-
- if ($arg->unpack) {
- if ($arg_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- IssueBuffer::maybeAdd(
- new MixedArgument(
- 'Argument ' . ($argument_offset + 1) . ' of ' . $cased_method_id
- . ' cannot unpack ' . $arg_type->getId() . ', expecting iterable',
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- if ($cased_method_id) {
- $arg_location = new CodeLocation($statements_analyzer->getSource(), $arg->value);
-
- self::processTaintedness(
- $statements_analyzer,
- $cased_method_id,
- $method_id,
- $argument_offset,
- $arg_location,
- $function_call_location,
- $function_param,
- $arg_type,
- $arg->value,
- $context,
- $specialize_taint
- );
- }
-
- return null;
- }
-
- if ($arg_type->hasArray()) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TList|TKeyedArray|TClassStringMap
- */
- $unpacked_atomic_array = $arg_type->getAtomicTypes()['array'];
- $arg_key_allowed = true;
-
- if ($unpacked_atomic_array instanceof TKeyedArray) {
- if (!$allow_named_args && !$unpacked_atomic_array->getGenericKeyType()->isInt()) {
- $arg_key_allowed = false;
- }
-
- if ($function_param->is_variadic) {
- $arg_type = $unpacked_atomic_array->getGenericValueType();
- } elseif ($codebase->php_major_version >= 8
- && $allow_named_args
- && isset($unpacked_atomic_array->properties[$function_param->name])
- ) {
- $arg_type = clone $unpacked_atomic_array->properties[$function_param->name];
- } elseif ($unpacked_atomic_array->is_list
- && isset($unpacked_atomic_array->properties[$unpacked_argument_offset])
- ) {
- $arg_type = clone $unpacked_atomic_array->properties[$unpacked_argument_offset];
- } elseif ($function_param->is_optional && $function_param->default_type) {
- if ($function_param->default_type instanceof Union) {
- $arg_type = $function_param->default_type;
- } else {
- $arg_type_atomic = ConstantTypeResolver::resolve(
- $codebase->classlikes,
- $function_param->default_type,
- $statements_analyzer
- );
-
- $arg_type = new Union([$arg_type_atomic]);
- }
- } else {
- $arg_type = Type::getMixed();
- }
- } elseif ($unpacked_atomic_array instanceof TList) {
- $arg_type = $unpacked_atomic_array->type_param;
- } elseif ($unpacked_atomic_array instanceof TClassStringMap) {
- $arg_type = Type::getMixed();
- } else {
- if (!$allow_named_args && !$unpacked_atomic_array->type_params[0]->isInt()) {
- $arg_key_allowed = false;
- }
- $arg_type = $unpacked_atomic_array->type_params[1];
- }
-
- if (!$arg_key_allowed) {
- IssueBuffer::maybeAdd(
- new NamedArgumentNotAllowed(
- 'Method ' . $cased_method_id
- . ' called with named unpacked array ' . $unpacked_atomic_array->getId()
- . ' (array with string keys)',
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } else {
- $non_iterable = false;
- $invalid_key = false;
- $invalid_string_key = false;
- $possibly_matches = false;
- foreach ($arg_type->getAtomicTypes() as $atomic_type) {
- if (!$atomic_type->isIterable($codebase)) {
- $non_iterable = true;
- } else {
- $key_type = $codebase->getKeyValueParamsForTraversableObject($atomic_type)[0];
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $key_type,
- Type::getArrayKey()
- )) {
- $invalid_key = true;
-
- continue;
- }
- if (($codebase->php_major_version < 8 || !$allow_named_args) && !$key_type->isInt()) {
- $invalid_string_key = true;
-
- continue;
- }
- $possibly_matches = true;
- }
- }
-
- $issue_type = $possibly_matches ? PossiblyInvalidArgument::class : InvalidArgument::class;
- if ($non_iterable) {
- IssueBuffer::maybeAdd(
- new $issue_type(
- 'Tried to unpack non-iterable ' . $arg_type->getId(),
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- if ($invalid_key) {
- IssueBuffer::maybeAdd(
- new $issue_type(
- 'Method ' . $cased_method_id
- . ' called with unpacked iterable ' . $arg_type->getId()
- . ' with invalid key (must be '
- . ($codebase->php_major_version < 8 ? 'int' : 'int|string') . ')',
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- if ($invalid_string_key) {
- if ($codebase->php_major_version < 8) {
- IssueBuffer::maybeAdd(
- new $issue_type(
- 'String keys not supported in unpacked arguments',
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new NamedArgumentNotAllowed(
- 'Method ' . $cased_method_id
- . ' called with named unpacked iterable ' . $arg_type->getId()
- . ' (iterable with string keys)',
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return null;
- }
- } else {
- if (!$allow_named_args && $arg->name !== null) {
- IssueBuffer::maybeAdd(
- new NamedArgumentNotAllowed(
- 'Method ' . $cased_method_id. ' called with named argument ' . $arg->name->name,
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- // bypass verifying argument types when collecting initialisations,
- // because the argument locations are not reliable (file names normally differ)
- // See https://github.com/vimeo/psalm/issues/5662
- if ($arg instanceof VirtualArg
- && $context->collect_initializations
- ) {
- return null;
- }
-
- if (self::verifyType(
- $statements_analyzer,
- $arg_type,
- $param_type,
- $fleshed_out_signature_type,
- $cased_method_id,
- $method_id,
- $argument_offset,
- new CodeLocation($statements_analyzer->getSource(), $arg->value),
- $arg->value,
- $context,
- $function_param,
- $arg->unpack,
- $unpacked_atomic_array,
- $specialize_taint,
- $in_call_map,
- $function_call_location
- ) === false) {
- return false;
- }
-
- return null;
- }
-
- /**
- * @param TKeyedArray|TArray|TList|TClassStringMap|null $unpacked_atomic_array
- * @return null|false
- * @psalm-suppress ComplexMethod
- */
- public static function verifyType(
- StatementsAnalyzer $statements_analyzer,
- Union $input_type,
- Union $param_type,
- ?Union $signature_param_type,
- ?string $cased_method_id,
- ?MethodIdentifier $method_id,
- int $argument_offset,
- CodeLocation $arg_location,
- PhpParser\Node\Expr $input_expr,
- Context $context,
- FunctionLikeParameter $function_param,
- bool $unpack,
- ?Atomic $unpacked_atomic_array,
- bool $specialize_taint,
- bool $in_call_map,
- CodeLocation $function_call_location
- ): ?bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if ($param_type->hasMixed()) {
- if ($codebase->infer_types_from_usage
- && !$input_type->hasMixed()
- && !$param_type->from_docblock
- && !$param_type->had_template
- && $method_id
- && strpos($method_id->method_name, '__') !== 0
- ) {
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id) {
- $id_lc = strtolower((string) $declaring_method_id);
- $codebase->analyzer->possible_method_param_types[$id_lc][$argument_offset]
- = Type::combineUnionTypes(
- $codebase->analyzer->possible_method_param_types[$id_lc][$argument_offset] ?? null,
- clone $input_type,
- $codebase
- );
- }
- }
-
- if ($cased_method_id) {
- self::processTaintedness(
- $statements_analyzer,
- $cased_method_id,
- $method_id,
- $argument_offset,
- $arg_location,
- $function_call_location,
- $function_param,
- $input_type,
- $input_expr,
- $context,
- $specialize_taint
- );
- }
-
- return null;
- }
-
- $method_identifier = $cased_method_id ? ' of ' . $cased_method_id : '';
-
- if ($input_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($input_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- if ($origin_location && $origin_location->getHash() === $arg_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier
- . ' cannot be ' . $input_type->getId() . ', expecting ' .
- $param_type,
- $arg_location,
- $cased_method_id,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- if ($input_type->isMixed()) {
- if (!$function_param->by_ref
- && !($function_param->is_variadic xor $unpack)
- && $cased_method_id !== 'echo'
- && $cased_method_id !== 'print'
- && (!$in_call_map || $context->strict_types)
- ) {
- self::coerceValueAfterGatekeeperArgument(
- $statements_analyzer,
- $input_type,
- false,
- $input_expr,
- $param_type,
- $signature_param_type,
- $context,
- $unpack,
- $unpacked_atomic_array
- );
- }
- }
-
- if ($cased_method_id) {
- $input_type = self::processTaintedness(
- $statements_analyzer,
- $cased_method_id,
- $method_id,
- $argument_offset,
- $arg_location,
- $function_call_location,
- $function_param,
- $input_type,
- $input_expr,
- $context,
- $specialize_taint
- );
- }
-
- if ($input_type->isMixed()) {
- return null;
- }
- }
-
- if ($input_type->isNever()) {
- IssueBuffer::maybeAdd(
- new NoValue(
- 'This function or method call never returns output',
- $arg_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($function_param->by_ref || $function_param->is_optional) {
- //if the param is optional or a ref, we'll allow the input to be possibly_undefined
- $param_type->possibly_undefined = true;
- }
-
- if ($param_type->hasCallableType() && $param_type->isSingle()) {
- // we do this replacement early because later we don't have access to the
- // $statements_analyzer, which is necessary to understand string function names
- foreach ($input_type->getAtomicTypes() as $key => $atomic_type) {
- if (!$atomic_type instanceof TLiteralString
- || InternalCallMapHandler::inCallMap($atomic_type->value)
- ) {
- continue;
- }
-
- $candidate_callable = CallableTypeComparator::getCallableFromAtomic(
- $codebase,
- $atomic_type,
- null,
- $statements_analyzer,
- true
- );
-
- if ($candidate_callable) {
- $input_type->removeType($key);
- $input_type->addType($candidate_callable);
- }
- }
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- $type_match_found = UnionTypeComparator::isContainedBy(
- $codebase,
- $input_type,
- $param_type,
- true,
- true,
- $union_comparison_results
- );
-
- $replace_input_type = false;
-
- if ($union_comparison_results->replacement_union_type) {
- $replace_input_type = true;
- $input_type = $union_comparison_results->replacement_union_type;
- }
-
- if ($cased_method_id) {
- $old_input_type = $input_type;
-
- $input_type = self::processTaintedness(
- $statements_analyzer,
- $cased_method_id,
- $method_id,
- $argument_offset,
- $arg_location,
- $function_call_location,
- $function_param,
- $input_type,
- $input_expr,
- $context,
- $specialize_taint
- );
-
- if ($old_input_type !== $input_type) {
- $replace_input_type = true;
- }
- }
-
- if ($type_match_found
- && $param_type->hasCallableType()
- ) {
- $potential_method_ids = [];
-
- foreach ($input_type->getAtomicTypes() as $input_type_part) {
- if ($input_type_part instanceof TKeyedArray) {
- $potential_method_id = CallableTypeComparator::getCallableMethodIdFromTKeyedArray(
- $input_type_part,
- $codebase,
- $context->calling_method_id,
- $statements_analyzer->getFilePath()
- );
-
- if ($potential_method_id && $potential_method_id !== 'not-callable') {
- $potential_method_ids[] = $potential_method_id;
- }
- } elseif ($input_type_part instanceof TLiteralString
- && strpos($input_type_part->value, '::')
- ) {
- $parts = explode('::', $input_type_part->value);
- $potential_method_ids[] = new MethodIdentifier(
- $parts[0],
- strtolower($parts[1])
- );
- }
- }
-
- foreach ($potential_method_ids as $potential_method_id) {
- $codebase->methods->methodExists(
- $potential_method_id,
- $context->calling_method_id,
- null,
- $statements_analyzer,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- );
- }
- }
-
- if ($context->strict_types
- && !$input_type->hasArray()
- && !$param_type->from_docblock
- && $cased_method_id !== 'echo'
- && $cased_method_id !== 'print'
- && $cased_method_id !== 'sprintf'
- ) {
- $union_comparison_results->scalar_type_match_found = false;
-
- if ($union_comparison_results->to_string_cast) {
- $union_comparison_results->to_string_cast = false;
- $type_match_found = false;
- }
- }
-
- if ($union_comparison_results->type_coerced && !$input_type->hasMixed()) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($input_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- if ($origin_location && $origin_location->getHash() === $arg_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedArgumentTypeCoercion(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId() .
- ', parent type ' . $input_type->getId() . ' provided',
- $arg_location,
- $cased_method_id,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new ArgumentTypeCoercion(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId() .
- ', parent type ' . $input_type->getId() . ' provided',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($union_comparison_results->to_string_cast && $cased_method_id !== 'echo' && $cased_method_id !== 'print') {
- IssueBuffer::maybeAdd(
- new ImplicitToStringCast(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' .
- $param_type->getId() . ', ' . $input_type->getId() . ' provided with a __toString method',
- $arg_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$type_match_found && !$union_comparison_results->type_coerced) {
- $types_can_be_identical = UnionTypeComparator::canBeContainedBy(
- $codebase,
- $input_type,
- $param_type,
- true,
- true
- );
-
- $type = ($input_type->possibly_undefined ? 'possibly undefined ' : '') . $input_type->getId();
- if ($union_comparison_results->scalar_type_match_found) {
- if ($cased_method_id !== 'echo' && $cased_method_id !== 'print') {
- IssueBuffer::maybeAdd(
- new InvalidScalarArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' .
- $param_type->getId() . ', ' . $type . ' provided',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($types_can_be_identical) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId() .
- ', possibly different type ' . $type . ' provided',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' expects ' . $param_type->getId() .
- ', ' . $type . ' provided',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return null;
- }
-
- if ($input_expr instanceof PhpParser\Node\Scalar\String_
- || $input_expr instanceof PhpParser\Node\Expr\Array_
- || $input_expr instanceof PhpParser\Node\Expr\BinaryOp\Concat
- ) {
- self::verifyExplicitParam(
- $statements_analyzer,
- $param_type,
- $arg_location,
- $input_expr,
- $context
- );
-
- return null;
- }
-
- if (!$param_type->isNullable() && $cased_method_id !== 'echo' && $cased_method_id !== 'print') {
- if ($input_type->isNull()) {
- IssueBuffer::maybeAdd(
- new NullArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' cannot be null, ' .
- 'null value provided to parameter with type ' . $param_type->getId(),
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
-
- if ($input_type->isNullable() && !$input_type->ignore_nullable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyNullArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' cannot be null, possibly ' .
- 'null value provided',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (!$param_type->isFalsable() &&
- !$param_type->hasBool() &&
- !$param_type->hasScalar() &&
- $cased_method_id !== 'echo' &&
- $cased_method_id !== 'print'
- ) {
- if ($input_type->isFalse()) {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' cannot be false, ' .
- $param_type->getId() . ' value expected',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
-
- if ($input_type->isFalsable() && !$input_type->ignore_falsable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyFalseArgument(
- 'Argument ' . ($argument_offset + 1) . $method_identifier . ' cannot be false, possibly ' .
- $param_type->getId() . ' value expected',
- $arg_location,
- $cased_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (($type_match_found || $input_type->hasMixed())
- && !$function_param->by_ref
- && !($function_param->is_variadic xor $unpack)
- && $cased_method_id !== 'echo'
- && $cased_method_id !== 'print'
- && (!$in_call_map || $context->strict_types)
- ) {
- self::coerceValueAfterGatekeeperArgument(
- $statements_analyzer,
- $input_type,
- $replace_input_type,
- $input_expr,
- $param_type,
- $signature_param_type,
- $context,
- $unpack,
- $unpacked_atomic_array
- );
- }
-
- return null;
- }
-
- /**
- * @param PhpParser\Node\Scalar\String_|PhpParser\Node\Expr\Array_|PhpParser\Node\Expr\BinaryOp\Concat $input_expr
- */
- private static function verifyExplicitParam(
- StatementsAnalyzer $statements_analyzer,
- Union $param_type,
- CodeLocation $arg_location,
- PhpParser\Node\Expr $input_expr,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- foreach ($param_type->getAtomicTypes() as $param_type_part) {
- if ($param_type_part instanceof TClassString
- && $input_expr instanceof PhpParser\Node\Scalar\String_
- && $param_type->isSingle()
- ) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $input_expr->value,
- $arg_location,
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false
- ) {
- return;
- }
- } elseif ($param_type_part instanceof TArray
- && $input_expr instanceof PhpParser\Node\Expr\Array_
- ) {
- foreach ($param_type_part->type_params[1]->getAtomicTypes() as $param_array_type_part) {
- if ($param_array_type_part instanceof TClassString) {
- foreach ($input_expr->items as $item) {
- if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $item->value->value,
- $arg_location,
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false
- ) {
- return;
- }
- }
- }
- }
- }
- } elseif ($param_type_part instanceof TCallable) {
- $can_be_callable_like_array = false;
- if ($param_type->hasArray()) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- */
- $param_array_type = $param_type->getAtomicTypes()['array'];
-
- $row_type = null;
- if ($param_array_type instanceof TList) {
- $row_type = $param_array_type->type_param;
- } elseif ($param_array_type instanceof TArray) {
- $row_type = $param_array_type->type_params[1];
- } elseif ($param_array_type instanceof TKeyedArray) {
- $row_type = $param_array_type->getGenericArrayType()->type_params[1];
- }
-
- if ($row_type &&
- ($row_type->hasMixed() || $row_type->hasString())
- ) {
- $can_be_callable_like_array = true;
- }
- }
-
- if (!$can_be_callable_like_array) {
- $function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
- $statements_analyzer,
- $input_expr
- );
-
- foreach ($function_ids as $function_id) {
- if (strpos($function_id, '::') !== false) {
- if ($function_id[0] === '$') {
- $function_id = substr($function_id, 1);
- }
-
- $function_id_parts = explode('&', $function_id);
-
- $non_existent_method_ids = [];
- $has_valid_method = false;
-
- foreach ($function_id_parts as $function_id_part) {
- [$callable_fq_class_name, $method_name] = explode('::', $function_id_part);
-
- switch ($callable_fq_class_name) {
- case 'self':
- case 'static':
- case 'parent':
- $container_class = $statements_analyzer->getFQCLN();
-
- if ($callable_fq_class_name === 'parent') {
- $container_class = $statements_analyzer->getParentFQCLN();
- }
-
- if (!$container_class) {
- continue 2;
- }
-
- $callable_fq_class_name = $container_class;
- }
-
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $callable_fq_class_name,
- $arg_location,
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false
- ) {
- return;
- }
-
- $function_id_part = new MethodIdentifier(
- $callable_fq_class_name,
- strtolower($method_name)
- );
-
- $call_method_id = new MethodIdentifier(
- $callable_fq_class_name,
- '__call'
- );
-
- if (!$codebase->classOrInterfaceOrEnumExists($callable_fq_class_name)) {
- return;
- }
-
- if (!$codebase->methods->methodExists($function_id_part)
- && !$codebase->methods->methodExists($call_method_id)
- ) {
- $non_existent_method_ids[] = $function_id_part;
- } else {
- $has_valid_method = true;
- }
- }
-
- if (!$has_valid_method && !$param_type->hasString() && !$param_type->hasArray()) {
- if (MethodAnalyzer::checkMethodExists(
- $codebase,
- $non_existent_method_ids[0],
- $arg_location,
- $statements_analyzer->getSuppressedIssues()
- ) === false
- ) {
- return;
- }
- }
- } else {
- if (!$param_type->hasString()
- && !$param_type->hasArray()
- && CallAnalyzer::checkFunctionExists(
- $statements_analyzer,
- $function_id,
- $arg_location,
- false
- ) === false
- ) {
- return;
- }
- }
- }
- }
- }
- }
- }
-
- /**
- * @param TKeyedArray|TArray|TList|TClassStringMap $unpacked_atomic_array
- */
- private static function coerceValueAfterGatekeeperArgument(
- StatementsAnalyzer $statements_analyzer,
- Union $input_type,
- bool $input_type_changed,
- PhpParser\Node\Expr $input_expr,
- Union $param_type,
- ?Union $signature_param_type,
- Context $context,
- bool $unpack,
- ?Atomic $unpacked_atomic_array
- ): void {
- if ($param_type->hasMixed()) {
- return;
- }
-
- if (!$input_type_changed && $param_type->from_docblock && !$input_type->hasMixed()) {
- $input_type = clone $input_type;
-
- foreach ($param_type->getAtomicTypes() as $param_atomic_type) {
- if ($param_atomic_type instanceof TGenericObject) {
- foreach ($input_type->getAtomicTypes() as $input_atomic_type) {
- if ($input_atomic_type instanceof TGenericObject
- && $input_atomic_type->value === $param_atomic_type->value
- ) {
- foreach ($input_atomic_type->type_params as $i => $type_param) {
- if ($type_param->isEmpty() && isset($param_atomic_type->type_params[$i])) {
- $input_type_changed = true;
-
- $input_atomic_type->type_params[$i] = clone $param_atomic_type->type_params[$i];
- }
- }
- }
- }
- }
- }
-
- if (!$input_type_changed) {
- return;
- }
- }
-
- $var_id = ExpressionIdentifier::getVarId(
- $input_expr,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id) {
- $was_cloned = false;
-
- if ($input_type->isNullable() && !$param_type->isNullable()) {
- $input_type = clone $input_type;
- $was_cloned = true;
- $input_type->removeType('null');
- }
-
- if ($input_type->getId() === $param_type->getId()) {
- if ($input_type->from_docblock) {
- if (!$was_cloned) {
- $was_cloned = true;
- $input_type = clone $input_type;
- }
-
- $input_type->from_docblock = false;
-
- foreach ($input_type->getAtomicTypes() as $atomic_type) {
- $atomic_type->from_docblock = false;
- }
- }
- } elseif ($input_type->hasMixed() && $signature_param_type) {
- $was_cloned = true;
- $parent_nodes = $input_type->parent_nodes;
- $by_ref = $input_type->by_ref;
- $input_type = clone $signature_param_type;
-
- if ($input_type->isNullable()) {
- $input_type->ignore_nullable_issues = true;
- }
-
- $input_type->parent_nodes = $parent_nodes;
- $input_type->by_ref = $by_ref;
- }
-
- if ($context->inside_conditional && !isset($context->assigned_var_ids[$var_id])) {
- $context->assigned_var_ids[$var_id] = 0;
- }
-
- if ($was_cloned) {
- $context->removeVarFromConflictingClauses($var_id, null, $statements_analyzer);
- }
-
- if ($unpack) {
- if ($unpacked_atomic_array instanceof TList) {
- $unpacked_atomic_array = clone $unpacked_atomic_array;
- $unpacked_atomic_array->type_param = $input_type;
-
- $context->vars_in_scope[$var_id] = new Union([$unpacked_atomic_array]);
- } elseif ($unpacked_atomic_array instanceof TArray) {
- $unpacked_atomic_array = clone $unpacked_atomic_array;
- $unpacked_atomic_array->type_params[1] = $input_type;
-
- $context->vars_in_scope[$var_id] = new Union([$unpacked_atomic_array]);
- } elseif ($unpacked_atomic_array instanceof TKeyedArray
- && $unpacked_atomic_array->is_list
- ) {
- $unpacked_atomic_array = $unpacked_atomic_array->getList();
- $unpacked_atomic_array->type_param = $input_type;
-
- $context->vars_in_scope[$var_id] = new Union([$unpacked_atomic_array]);
- } else {
- $context->vars_in_scope[$var_id] = new Union([
- new TArray([
- Type::getInt(),
- $input_type
- ]),
- ]);
- }
- } else {
- $context->vars_in_scope[$var_id] = $input_type;
- }
- }
- }
-
- private static function processTaintedness(
- StatementsAnalyzer $statements_analyzer,
- string $cased_method_id,
- ?MethodIdentifier $method_id,
- int $argument_offset,
- CodeLocation $arg_location,
- CodeLocation $function_call_location,
- FunctionLikeParameter $function_param,
- Union $input_type,
- PhpParser\Node\Expr $expr,
- Context $context,
- bool $specialize_taint
- ): Union {
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$statements_analyzer->data_flow_graph
- || ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues()))
- ) {
- return $input_type;
- }
-
- // literal data can’t be tainted
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $input_type->isSingle()
- && $input_type->hasLiteralValue()
- ) {
- return $input_type;
- }
-
- // numeric types can't be tainted, neither can bool
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $input_type->isSingle()
- && ($input_type->isInt() || $input_type->isFloat() || $input_type->isBool())
- ) {
- return $input_type;
- }
-
- $event = new AddRemoveTaintsEvent($expr, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- if ($function_param->type && $function_param->type->isString() && !$input_type->isString()) {
- $cast_type = CastAnalyzer::castStringAttempt(
- $statements_analyzer,
- $context,
- $input_type,
- $expr,
- false
- );
-
- $input_type = clone $input_type;
- $input_type->parent_nodes += $cast_type->parent_nodes;
- }
-
- if ($specialize_taint) {
- $method_node = DataFlowNode::getForMethodArgument(
- $cased_method_id,
- $cased_method_id,
- $argument_offset,
- $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ? $function_param->location
- : null,
- $function_call_location
- );
- } else {
- $method_node = DataFlowNode::getForMethodArgument(
- $cased_method_id,
- $cased_method_id,
- $argument_offset,
- $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ? $function_param->location
- : null
- );
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $method_id
- && $method_id->method_name !== '__construct'
- ) {
- $fq_classlike_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
- $cased_method_name = explode('::', $cased_method_id)[1];
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_classlike_name);
-
- foreach ($class_storage->dependent_classlikes as $dependent_classlike_lc => $_) {
- $dependent_classlike_storage = $codebase->classlike_storage_provider->get(
- $dependent_classlike_lc
- );
- $new_sink = DataFlowNode::getForMethodArgument(
- $dependent_classlike_lc . '::' . $method_name,
- $dependent_classlike_storage->name . '::' . $cased_method_name,
- $argument_offset,
- $arg_location,
- null
- );
-
- $statements_analyzer->data_flow_graph->addNode($new_sink);
- $statements_analyzer->data_flow_graph->addPath(
- $method_node,
- $new_sink,
- 'arg',
- $added_taints,
- $removed_taints
- );
- }
- }
- }
-
- if ($method_id && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id && (string) $declaring_method_id !== (string) $method_id) {
- $new_sink = DataFlowNode::getForMethodArgument(
- (string) $declaring_method_id,
- $codebase->methods->getCasedMethodId($declaring_method_id),
- $argument_offset,
- $arg_location,
- null
- );
-
- $statements_analyzer->data_flow_graph->addNode($new_sink);
- $statements_analyzer->data_flow_graph->addPath(
- $method_node,
- $new_sink,
- 'arg',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $statements_analyzer->data_flow_graph->addNode($method_node);
-
- $argument_value_node = DataFlowNode::getForAssignment(
- 'call to ' . $cased_method_id,
- $arg_location
- );
-
- $statements_analyzer->data_flow_graph->addNode($argument_value_node);
-
- $statements_analyzer->data_flow_graph->addPath(
- $argument_value_node,
- $method_node,
- 'arg',
- $added_taints,
- $removed_taints
- );
-
- foreach ($input_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addNode($method_node);
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $argument_value_node,
- 'arg',
- $added_taints,
- $removed_taints
- );
- }
-
- if ($function_param->assert_untainted) {
- $input_type = clone $input_type;
- $input_type->parent_nodes = [];
- }
-
- return $input_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php
deleted file mode 100644
index cf5fe6f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentMapPopulator.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser\Node\Expr;
-use PhpParser\Node\Expr\FuncCall;
-use PhpParser\Node\Expr\MethodCall;
-use PhpParser\Node\Expr\New_;
-use PhpParser\Node\Expr\StaticCall;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-
-use function array_reverse;
-use function array_shift;
-use function is_string;
-use function reset;
-use function strlen;
-use function substr;
-use function token_get_all;
-
-class ArgumentMapPopulator
-{
- /**
- * @param MethodCall|StaticCall|FuncCall|New_ $stmt
- */
- public static function recordArgumentPositions(
- StatementsAnalyzer $statements_analyzer,
- Expr $stmt,
- Codebase $codebase,
- string $function_reference
- ): void {
- $file_content = $codebase->file_provider->getContents($statements_analyzer->getFilePath());
-
- // Find opening paren
- $first_argument = $stmt->getArgs()[0] ?? null;
- $first_argument_character = $first_argument !== null
- ? $first_argument->getStartFilePos()
- : $stmt->getEndFilePos();
- $method_name_and_first_paren_source_code_length = $first_argument_character - $stmt->getStartFilePos();
- // FIXME: There are weird ::__construct calls in the AST for `extends`
- if ($method_name_and_first_paren_source_code_length <= 0) {
- return;
- }
- $method_name_and_first_paren_source_code = substr(
- $file_content,
- $stmt->getStartFilePos(),
- $method_name_and_first_paren_source_code_length
- );
- $method_name_and_first_paren_tokens = token_get_all('<?php ' . $method_name_and_first_paren_source_code);
- $opening_paren_position = $first_argument_character;
- foreach (array_reverse($method_name_and_first_paren_tokens) as $token) {
- $token = is_string($token) ? $token : $token[1];
- $opening_paren_position -= strlen($token);
-
- if ($token === '(') {
- break;
- }
- }
-
- // New instances can be created without parens
- if ($opening_paren_position < $stmt->getStartFilePos()) {
- return;
- }
-
- // Record ranges of the source code that need to be tokenized to find commas
- /** @var array{0: int, 1: int}[] $ranges */
- $ranges = [];
-
- // Add range between opening paren and first argument
- $first_argument = $stmt->getArgs()[0] ?? null;
- $first_argument_starting_position = $first_argument !== null
- ? $first_argument->getStartFilePos()
- : $stmt->getEndFilePos();
- $first_range_starting_position = $opening_paren_position + 1;
- if ($first_range_starting_position !== $first_argument_starting_position) {
- $ranges[] = [$first_range_starting_position, $first_argument_starting_position];
- }
-
- // Add range between arguments
- foreach ($stmt->getArgs() as $i => $argument) {
- $range_start = $argument->getEndFilePos() + 1;
- $next_argument = $stmt->getArgs()[$i + 1] ?? null;
- $range_end = $next_argument !== null
- ? $next_argument->getStartFilePos()
- : $stmt->getEndFilePos();
-
- if ($range_start !== $range_end) {
- $ranges[] = [$range_start, $range_end];
- }
- }
-
- $commas = [];
- foreach ($ranges as $range) {
- $position = $range[0];
- $length = $range[1] - $position;
-
- if ($length > 0) {
- $range_source_code = substr($file_content, $position, $length);
- $range_tokens = token_get_all('<?php ' . $range_source_code);
-
- array_shift($range_tokens);
-
- $current_position = $position;
- foreach ($range_tokens as $token) {
- $token = is_string($token) ? $token : $token[1];
-
- if ($token === ',') {
- $commas[] = $current_position;
- }
-
- $current_position += strlen($token);
- }
- }
- }
-
- $argument_start_position = $opening_paren_position + 1;
- $argument_number = 0;
- while (!empty($commas)) {
- $comma = reset($commas);
- array_shift($commas);
-
- $codebase->analyzer->addNodeArgument(
- $statements_analyzer->getFilePath(),
- $argument_start_position,
- $comma,
- $function_reference,
- $argument_number
- );
-
- ++$argument_number;
- $argument_start_position = $comma + 1;
- }
-
- $codebase->analyzer->addNodeArgument(
- $statements_analyzer->getFilePath(),
- $argument_start_position,
- $stmt->getEndFilePos(),
- $function_reference,
- $argument_number
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php
deleted file mode 100644
index c3930b7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php
+++ /dev/null
@@ -1,1620 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\AttributesAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\ConstantTypeResolver;
-use Psalm\Internal\Codebase\Functions;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Stubs\Generator\StubsGenerator;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\InvalidNamedArgument;
-use Psalm\Issue\InvalidPassByReference;
-use Psalm\Issue\PossiblyUndefinedVariable;
-use Psalm\Issue\TooFewArguments;
-use Psalm\Issue\TooManyArguments;
-use Psalm\IssueBuffer;
-use Psalm\Node\VirtualArg;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TCallableList;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_map;
-use function array_reverse;
-use function array_slice;
-use function array_values;
-use function count;
-use function in_array;
-use function is_string;
-use function max;
-use function min;
-use function reset;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class ArgumentsAnalyzer
-{
- /**
- * @param list<PhpParser\Node\Arg> $args
- * @param array<int, FunctionLikeParameter>|null $function_params
- *
- * @return false|null
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- array $args,
- ?array $function_params,
- ?string $method_id,
- bool $allow_named_args,
- Context $context,
- ?TemplateResult $template_result = null
- ): ?bool {
- $last_param = $function_params
- ? $function_params[count($function_params) - 1]
- : null;
-
- // if this modifies the array type based on further args
- if (in_array($method_id, ['array_push', 'array_unshift'], true)
- && $function_params
- && isset($args[0])
- && isset($args[1])
- ) {
- if (ArrayFunctionArgumentsAnalyzer::handleAddition(
- $statements_analyzer,
- $args,
- $context,
- $method_id
- ) === false
- ) {
- return false;
- }
-
- return null;
- }
-
- if ($method_id === 'array_splice' && $function_params && count($args) > 1) {
- if (ArrayFunctionArgumentsAnalyzer::handleSplice($statements_analyzer, $args, $context) === false) {
- return false;
- }
-
- return null;
- }
-
- if ($method_id === 'array_map') {
- $args = array_reverse($args, true);
- }
-
- foreach ($args as $argument_offset => $arg) {
- if ($function_params === null) {
- if (self::evaluateArbitraryParam(
- $statements_analyzer,
- $arg,
- $context
- ) === false) {
- return false;
- }
-
- continue;
- }
-
- $param = null;
-
- if ($arg->name && $allow_named_args) {
- foreach ($function_params as $candidate_param) {
- if ($candidate_param->name === $arg->name->name) {
- $param = $candidate_param;
- break;
- }
- }
-
- if ($last_param && $last_param->is_variadic) {
- $param = $last_param;
- }
- } elseif ($argument_offset < count($function_params)) {
- $param = $function_params[$argument_offset];
- } elseif ($last_param && $last_param->is_variadic) {
- $param = $last_param;
- }
-
- $by_ref = $param && $param->by_ref;
-
- $by_ref_type = null;
-
- if ($by_ref) {
- $by_ref_type = $param->type ? clone $param->type : Type::getMixed();
- }
-
- if ($by_ref
- && $by_ref_type
- && !($arg->value instanceof PhpParser\Node\Expr\Closure
- || $arg->value instanceof PhpParser\Node\Expr\ConstFetch
- || $arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
- || $arg->value instanceof PhpParser\Node\Expr\FuncCall
- || $arg->value instanceof PhpParser\Node\Expr\MethodCall
- || $arg->value instanceof PhpParser\Node\Expr\StaticCall
- || $arg->value instanceof PhpParser\Node\Expr\New_
- || $arg->value instanceof PhpParser\Node\Expr\Assign
- || $arg->value instanceof PhpParser\Node\Expr\Array_
- || $arg->value instanceof PhpParser\Node\Expr\Ternary
- || $arg->value instanceof PhpParser\Node\Expr\BinaryOp
- )
- ) {
- if (self::handleByRefFunctionArg(
- $statements_analyzer,
- $method_id,
- $argument_offset,
- $arg,
- $context
- ) === false) {
- return false;
- }
-
- continue;
- }
-
- $toggled_class_exists = false;
-
- if ($method_id === 'class_exists'
- && $argument_offset === 0
- && !$context->inside_class_exists
- ) {
- $context->inside_class_exists = true;
- $toggled_class_exists = true;
- }
-
- if (($arg->value instanceof PhpParser\Node\Expr\Closure
- || $arg->value instanceof PhpParser\Node\Expr\ArrowFunction)
- && $param
- && !$arg->value->getDocComment()
- ) {
- self::handleClosureArg(
- $statements_analyzer,
- $args,
- $method_id,
- $context,
- $template_result ?? new TemplateResult([], []),
- $argument_offset,
- $arg,
- $param
- );
- }
-
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $arg->value, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
-
- $context->inside_call = $was_inside_call;
-
- if (($argument_offset === 0 && $method_id === 'array_filter' && count($args) === 2)
- || ($argument_offset > 0 && $method_id === 'array_map' && count($args) >= 2)
- ) {
- self::handleArrayMapFilterArrayArg(
- $statements_analyzer,
- $method_id,
- $argument_offset,
- $arg,
- $context,
- $template_result
- );
- }
-
- $inferred_arg_type = $statements_analyzer->node_data->getType($arg->value);
-
- if (null !== $inferred_arg_type && null !== $template_result && null !== $param && null !== $param->type) {
- $codebase = $statements_analyzer->getCodebase();
-
- TemplateStandinTypeReplacer::replace(
- clone $param->type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $inferred_arg_type,
- $argument_offset,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id
- );
- }
-
- if ($toggled_class_exists) {
- $context->inside_class_exists = false;
- }
- }
-
- if ($method_id === "ReflectionClass::getattributes"
- || $method_id === "ReflectionClassConstant::getattributes"
- || $method_id === "ReflectionFunction::getattributes"
- || $method_id === "ReflectionMethod::getattributes"
- || $method_id === "ReflectionParameter::getattributes"
- || $method_id === "ReflectionProperty::getattributes"
- ) {
- AttributesAnalyzer::analyzeGetAttributes($statements_analyzer, $method_id, array_values($args));
- }
-
- return null;
- }
-
- private static function handleArrayMapFilterArrayArg(
- StatementsAnalyzer $statements_analyzer,
- string $method_id,
- int $argument_offset,
- PhpParser\Node\Arg $arg,
- Context $context,
- ?TemplateResult &$template_result
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- $generic_param_type = new Union([
- new TArray([
- Type::getArrayKey(),
- new Union([
- new TTemplateParam(
- 'ArrayValue' . $argument_offset,
- Type::getMixed(),
- $method_id
- )
- ])
- ])
- ]);
-
- $template_types = ['ArrayValue' . $argument_offset => [$method_id => Type::getMixed()]];
-
- $replace_template_result = new TemplateResult(
- $template_types,
- []
- );
-
- $existing_type = $statements_analyzer->node_data->getType($arg->value);
-
- TemplateStandinTypeReplacer::replace(
- $generic_param_type,
- $replace_template_result,
- $codebase,
- $statements_analyzer,
- $existing_type,
- $argument_offset,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id
- );
-
- if ($replace_template_result->lower_bounds) {
- if (!$template_result) {
- $template_result = new TemplateResult([], []);
- }
-
- $template_result->lower_bounds += $replace_template_result->lower_bounds;
- }
- }
-
- /**
- * @param array<int, PhpParser\Node\Arg> $args
- */
- private static function handleClosureArg(
- StatementsAnalyzer $statements_analyzer,
- array $args,
- ?string $method_id,
- Context $context,
- TemplateResult $template_result,
- int $argument_offset,
- PhpParser\Node\Arg $arg,
- FunctionLikeParameter $param
- ): void {
- if (!$param->type) {
- return;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (($argument_offset === 1 && $method_id === 'array_filter' && count($args) === 2)
- || ($argument_offset === 0 && $method_id === 'array_map' && count($args) >= 2)
- ) {
- $function_like_params = [];
-
- foreach ($template_result->lower_bounds as $template_name => $_) {
- $function_like_params[] = new FunctionLikeParameter(
- 'function',
- false,
- new Union([
- new TTemplateParam(
- $template_name,
- Type::getMixed(),
- $method_id
- )
- ])
- );
- }
-
- $replaced_type = new Union([
- new TCallable(
- 'callable',
- array_reverse($function_like_params)
- )
- ]);
- } else {
- $replaced_type = clone $param->type;
- }
-
- $replace_template_result = new TemplateResult(
- array_map(
- function ($template_map) use ($codebase) {
- return array_map(
- function ($lower_bounds) use ($codebase) {
- return TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $lower_bounds,
- $codebase
- );
- },
- $template_map
- );
- },
- $template_result->lower_bounds
- ),
- []
- );
-
- $replaced_type = TemplateStandinTypeReplacer::replace(
- $replaced_type,
- $replace_template_result,
- $codebase,
- $statements_analyzer,
- null,
- null,
- null,
- $context->calling_method_id ?: $context->calling_function_id
- );
-
- TemplateInferredTypeReplacer::replace(
- $replaced_type,
- $replace_template_result,
- $codebase
- );
-
- $closure_id = strtolower($statements_analyzer->getFilePath())
- . ':' . $arg->value->getLine()
- . ':' . (int)$arg->value->getAttribute('startFilePos')
- . ':-:closure';
-
- try {
- $closure_storage = $codebase->getClosureStorage(
- $statements_analyzer->getFilePath(),
- $closure_id
- );
- } catch (UnexpectedValueException $e) {
- return;
- }
-
- foreach ($closure_storage->params as $closure_param_offset => $param_storage) {
- $param_type_inferred = $param_storage->type_inferred;
-
- $newly_inferred_type = null;
- $has_different_docblock_type = false;
-
- if ($param_storage->type && !$param_type_inferred) {
- if ($param_storage->type !== $param_storage->signature_type) {
- $has_different_docblock_type = true;
- }
- }
-
- if (!$has_different_docblock_type) {
- foreach ($replaced_type->getAtomicTypes() as $replaced_type_part) {
- if ($replaced_type_part instanceof TCallable
- || $replaced_type_part instanceof TClosure
- ) {
- if (isset($replaced_type_part->params[$closure_param_offset]->type)) {
- $replaced_param_type = $replaced_type_part->params[$closure_param_offset]->type;
-
- if ($replaced_param_type->hasTemplate()) {
- $replaced_param_type = TypeExpander::expandUnion(
- $codebase,
- $replaced_param_type,
- null,
- null,
- null,
- true,
- false,
- false,
- true,
- true
- );
- }
-
- if ($param_storage->type && !$param_type_inferred) {
- $type_match_found = UnionTypeComparator::isContainedBy(
- $codebase,
- $replaced_param_type,
- $param_storage->type
- );
-
- if (!$type_match_found) {
- continue;
- }
- }
-
- $newly_inferred_type = Type::combineUnionTypes(
- $newly_inferred_type,
- $replaced_param_type,
- $codebase
- );
- }
- }
- }
- }
-
- if ($newly_inferred_type) {
- $param_storage->type = $newly_inferred_type;
- $param_storage->type_inferred = true;
- }
-
- if ($param_storage->type && ($method_id === 'array_map' || $method_id === 'array_filter')) {
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_analyzer,
- $args[1 - $argument_offset]->value,
- null,
- $param_storage->type,
- Type::getMixed()
- );
- }
- }
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- * @param string|MethodIdentifier|null $method_id
- * @param array<int,FunctionLikeParameter> $function_params
- *
- * @return false|null
- *
- * @psalm-suppress ComplexMethod there's just not much that can be done about this
- */
- public static function checkArgumentsMatch(
- StatementsAnalyzer $statements_analyzer,
- array $args,
- $method_id,
- array $function_params,
- ?FunctionLikeStorage $function_storage,
- ?ClassLikeStorage $class_storage,
- ?TemplateResult $class_template_result,
- CodeLocation $code_location,
- Context $context
- ): ?bool {
- $in_call_map = $method_id ? InternalCallMapHandler::inCallMap((string) $method_id) : false;
-
- $cased_method_id = (string) $method_id;
-
- $is_variadic = false;
-
- $fq_class_name = null;
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($method_id) {
- if (!$in_call_map && $method_id instanceof MethodIdentifier) {
- $fq_class_name = $method_id->fq_class_name;
- }
-
- if ($function_storage) {
- $is_variadic = $function_storage->variadic;
- } elseif (is_string($method_id)) {
- $is_variadic = Functions::isVariadic(
- $codebase,
- strtolower($method_id),
- $statements_analyzer->getRootFilePath()
- );
- } else {
- $is_variadic = $codebase->methods->isVariadic($method_id);
- }
- }
-
- if ($method_id instanceof MethodIdentifier) {
- $cased_method_id = $codebase->methods->getCasedMethodId($method_id);
- } elseif ($function_storage) {
- $cased_method_id = $function_storage->cased_name;
- }
-
- $calling_class_storage = $class_storage;
-
- $static_fq_class_name = $fq_class_name;
- $self_fq_class_name = $fq_class_name;
-
- if ($method_id instanceof MethodIdentifier) {
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id && (string)$declaring_method_id !== (string)$method_id) {
- $self_fq_class_name = $declaring_method_id->fq_class_name;
- $class_storage = $codebase->classlike_storage_provider->get($self_fq_class_name);
- }
-
- $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
-
- if ($appearing_method_id && $declaring_method_id !== $appearing_method_id) {
- $self_fq_class_name = $appearing_method_id->fq_class_name;
- }
- }
-
- if ($function_params) {
- foreach ($function_params as $function_param) {
- $is_variadic = $is_variadic || $function_param->is_variadic;
- }
- }
-
- $has_packed_var = false;
-
- foreach ($args as $arg) {
- if ($arg->unpack) {
- $has_packed_var = true;
- }
- }
-
- $last_param = $function_params
- ? $function_params[count($function_params) - 1]
- : null;
-
- $template_result = null;
-
- $class_generic_params = [];
-
- if ($class_template_result) {
- foreach ($class_template_result->lower_bounds as $template_name => $type_map) {
- foreach ($type_map as $class => $lower_bounds) {
- if (count($lower_bounds) === 1) {
- $class_generic_params[$template_name][$class] = clone reset($lower_bounds)->type;
- }
- }
- }
- }
-
- if ($function_storage) {
- $template_result = self::getProvisionalTemplateResultForFunctionLike(
- $statements_analyzer,
- $codebase,
- $context,
- $class_storage,
- $self_fq_class_name,
- $calling_class_storage,
- $function_storage,
- $class_generic_params,
- $class_template_result,
- $args,
- $function_params,
- $last_param
- );
- }
-
- $function_param_count = count($function_params);
-
- if (count($function_params) > count($args) && !$has_packed_var) {
- for ($i = count($args), $iMax = count($function_params); $i < $iMax; $i++) {
- if ($function_params[$i]->default_type
- && $function_params[$i]->type
- && $function_params[$i]->type->hasTemplate()
- ) {
- if ($function_params[$i]->default_type instanceof Union) {
- $default_type = $function_params[$i]->default_type;
- } else {
- $default_type_atomic = ConstantTypeResolver::resolve(
- $codebase->classlikes,
- $function_params[$i]->default_type,
- $statements_analyzer
- );
-
- $default_type = new Union([$default_type_atomic]);
- }
-
- if ($default_type->hasLiteralValue()) {
- ArgumentAnalyzer::checkArgumentMatches(
- $statements_analyzer,
- $cased_method_id,
- $method_id instanceof MethodIdentifier ? $method_id : null,
- $self_fq_class_name,
- $static_fq_class_name,
- $code_location,
- $function_params[$i],
- $i,
- $i,
- $function_storage->allow_named_arg_calls ?? true,
- new VirtualArg(
- StubsGenerator::getExpressionFromType($default_type)
- ),
- $default_type,
- $context,
- $class_generic_params,
- $template_result,
- $function_storage->specialize_call ?? true,
- $in_call_map
- );
- }
- }
- }
- }
-
- if (($method_id === 'preg_match_all' || $method_id === 'preg_match') && count($args) > 3) {
- $args = array_reverse($args, true);
- }
-
- $arg_function_params = [];
- $matched_args = [];
- $named_args_was_used = false;
-
- foreach ($args as $argument_offset => $arg) {
- if ($named_args_was_used && !$arg->name) {
- IssueBuffer::maybeAdd(
- new InvalidNamedArgument(
- 'Cannot use positional argument after named argument',
- new CodeLocation($statements_analyzer, $arg),
- (string)$method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($arg->unpack) {
- if ($function_param_count > $argument_offset) {
- for ($i = $argument_offset; $i < $function_param_count; $i++) {
- $arg_function_params[$argument_offset][] = $function_params[$i];
- }
- }
-
- if (($arg_value_type = $statements_analyzer->node_data->getType($arg->value))
- && $arg_value_type->hasArray()) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TList|TKeyedArray
- */
- $array_type = $arg_value_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TKeyedArray) {
- $key_types = $array_type->getGenericArrayType()->getChildNodes()[0]->getChildNodes();
-
- foreach ($key_types as $key_type) {
- if (!$key_type instanceof TLiteralString
- || ($function_storage && !$function_storage->allow_named_arg_calls)) {
- continue;
- }
-
- $param_found = false;
-
- foreach ($function_params as $candidate_param) {
- if ($candidate_param->name === $key_type->value || $candidate_param->is_variadic) {
- if ($candidate_param->name === $key_type->value) {
- if (isset($matched_args[$candidate_param->name])) {
- IssueBuffer::maybeAdd(
- new InvalidNamedArgument(
- 'Parameter $' . $key_type->value . ' has already been used in '
- . ($cased_method_id ?: $method_id),
- new CodeLocation($statements_analyzer, $arg),
- (string)$method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $matched_args[$candidate_param->name] = true;
- }
-
- $param_found = true;
- break;
- }
- }
-
- if (!$param_found) {
- IssueBuffer::maybeAdd(
- new InvalidNamedArgument(
- 'Parameter $' . $key_type->value . ' does not exist on function '
- . ($cased_method_id ?: $method_id),
- new CodeLocation($statements_analyzer, $arg),
- (string)$method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
- } elseif ($arg->name && (!$function_storage || $function_storage->allow_named_arg_calls)) {
- $named_args_was_used = true;
-
- foreach ($function_params as $candidate_param) {
- if ($candidate_param->name === $arg->name->name || $candidate_param->is_variadic) {
- if ($candidate_param->name === $arg->name->name) {
- if (isset($matched_args[$candidate_param->name])) {
- IssueBuffer::maybeAdd(
- new InvalidNamedArgument(
- 'Parameter $' . $arg->name->name . ' has already been used in '
- . ($cased_method_id ?: $method_id),
- new CodeLocation($statements_analyzer, $arg->name),
- (string) $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $matched_args[$candidate_param->name] = true;
- }
-
- $arg_function_params[$argument_offset] = [$candidate_param];
- break;
- }
- }
-
- if (!isset($arg_function_params[$argument_offset])) {
- IssueBuffer::maybeAdd(
- new InvalidNamedArgument(
- 'Parameter $' . $arg->name->name . ' does not exist on function '
- . ($cased_method_id ?: $method_id),
- new CodeLocation($statements_analyzer, $arg->name),
- (string) $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($function_param_count > $argument_offset) {
- $arg_function_params[$argument_offset] = [$function_params[$argument_offset]];
- $matched_args[$function_params[$argument_offset]->name] = true;
- } elseif ($last_param && $last_param->is_variadic) {
- $arg_function_params[$argument_offset] = [$last_param];
- $matched_args[$last_param->name] = true;
- }
- }
-
- foreach ($args as $argument_offset => $arg) {
- if (!isset($arg_function_params[$argument_offset])) {
- continue;
- }
-
- if ($arg_function_params[$argument_offset][0]->by_ref
- && $method_id !== 'extract'
- ) {
- if (self::handlePossiblyMatchingByRefParam(
- $statements_analyzer,
- $codebase,
- (string) $method_id,
- $cased_method_id,
- $last_param,
- $function_params,
- $argument_offset,
- $arg,
- $context,
- $template_result
- ) === false) {
- return null;
- }
- }
-
- $arg_value_type = $statements_analyzer->node_data->getType($arg->value);
-
- foreach ($arg_function_params[$argument_offset] as $i => $function_param) {
- if (ArgumentAnalyzer::checkArgumentMatches(
- $statements_analyzer,
- $cased_method_id,
- $method_id instanceof MethodIdentifier ? $method_id : null,
- $self_fq_class_name,
- $static_fq_class_name,
- $code_location,
- $function_param,
- $argument_offset + $i,
- $i,
- $function_storage->allow_named_arg_calls ?? true,
- $arg,
- $arg_value_type,
- $context,
- $class_generic_params,
- $template_result,
- $function_storage->specialize_call ?? true,
- $in_call_map
- ) === false) {
- return false;
- }
- }
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $cased_method_id
- ) {
- foreach ($args as $argument_offset => $_) {
- if (!isset($arg_function_params[$argument_offset])) {
- continue;
- }
-
- foreach ($arg_function_params[$argument_offset] as $function_param) {
- if ($function_param->sinks) {
- if (!$function_storage || $function_storage->specialize_call) {
- $sink = TaintSink::getForMethodArgument(
- $cased_method_id,
- $cased_method_id,
- $argument_offset,
- $function_param->location,
- $code_location
- );
- } else {
- $sink = TaintSink::getForMethodArgument(
- $cased_method_id,
- $cased_method_id,
- $argument_offset,
- $function_param->location
- );
- }
-
- $sink->taints = $function_param->sinks;
-
- $statements_analyzer->data_flow_graph->addSink($sink);
- }
- }
- }
- }
-
- if ($method_id === 'array_map' || $method_id === 'array_filter') {
- if ($method_id === 'array_map' && count($args) < 2) {
- IssueBuffer::maybeAdd(
- new TooFewArguments(
- 'Too few arguments for ' . $method_id,
- $code_location,
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($method_id === 'array_filter' && count($args) < 1) {
- IssueBuffer::maybeAdd(
- new TooFewArguments(
- 'Too few arguments for ' . $method_id,
- $code_location,
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- ArrayFunctionArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $context,
- $args,
- $method_id,
- $context->check_functions
- );
-
- return null;
- }
-
- if ($method_id === 'get_class' && $args === []) {
- //get_class without args only works when inside a class
- if (!$context->self) {
- IssueBuffer::maybeAdd(
- new TooFewArguments(
- 'Cannot call get_class() without argument outside of class scope',
- $code_location,
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return null;
- }
- }
-
- self::checkArgCount(
- $statements_analyzer,
- $codebase,
- $function_storage,
- $context,
- $template_result,
- $is_variadic,
- $args,
- $function_params,
- $in_call_map,
- $method_id,
- $cased_method_id,
- $code_location
- );
-
- return null;
- }
-
- /**
- * @param array<int, FunctionLikeParameter> $function_params
- * @return false|null
- */
- private static function handlePossiblyMatchingByRefParam(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- string $method_id,
- ?string $cased_method_id,
- ?FunctionLikeParameter $last_param,
- array $function_params,
- int $argument_offset,
- PhpParser\Node\Arg $arg,
- Context $context,
- ?TemplateResult $template_result
- ): ?bool {
- if ($arg->value instanceof PhpParser\Node\Scalar
- || $arg->value instanceof PhpParser\Node\Expr\Cast
- || $arg->value instanceof PhpParser\Node\Expr\Array_
- || $arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
- || $arg->value instanceof PhpParser\Node\Expr\BinaryOp
- || $arg->value instanceof PhpParser\Node\Expr\Ternary
- || (
- (
- $arg->value instanceof PhpParser\Node\Expr\ConstFetch
- || $arg->value instanceof PhpParser\Node\Expr\FuncCall
- || $arg->value instanceof PhpParser\Node\Expr\MethodCall
- || $arg->value instanceof PhpParser\Node\Expr\StaticCall
- ) && (
- !($arg_value_type = $statements_analyzer->node_data->getType($arg->value))
- || !$arg_value_type->by_ref
- )
- )
- ) {
- IssueBuffer::maybeAdd(
- new InvalidPassByReference(
- 'Parameter ' . ($argument_offset + 1) . ' of ' . $cased_method_id . ' expects a variable',
- new CodeLocation($statements_analyzer->getSource(), $arg->value)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return false;
- }
-
- if (!in_array(
- $method_id,
- [
- 'ksort', 'asort', 'krsort', 'arsort', 'natcasesort', 'natsort',
- 'reset', 'end', 'next', 'prev', 'array_pop', 'array_shift',
- 'array_push', 'array_unshift', 'socket_select', 'array_splice',
- ],
- true
- )) {
- $by_ref_type = null;
- $by_ref_out_type = null;
-
- $check_null_ref = true;
-
- if ($last_param) {
- if ($argument_offset < count($function_params)) {
- $function_param = $function_params[$argument_offset];
- } else {
- $function_param = $last_param;
- }
-
- if ($function_param->type) {
- $by_ref_type = clone $function_param->type;
- }
- if ($function_param->out_type) {
- $by_ref_out_type = clone $function_param->out_type;
- }
-
- if ($by_ref_type && $by_ref_type->isNullable()) {
- $check_null_ref = false;
- }
-
- if ($template_result && $by_ref_type) {
- $original_by_ref_type = clone $by_ref_type;
-
- $by_ref_type = TemplateStandinTypeReplacer::replace(
- clone $by_ref_type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $statements_analyzer->node_data->getType($arg->value),
- $argument_offset,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id
- );
-
- if ($template_result->lower_bounds) {
- TemplateInferredTypeReplacer::replace(
- $original_by_ref_type,
- $template_result,
- $codebase
- );
-
- $by_ref_type = $original_by_ref_type;
- }
- }
-
- if ($template_result && $by_ref_out_type) {
- $original_by_ref_out_type = clone $by_ref_out_type;
-
- $by_ref_out_type = TemplateStandinTypeReplacer::replace(
- clone $by_ref_out_type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $statements_analyzer->node_data->getType($arg->value),
- $argument_offset,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id
- );
-
- if ($template_result->lower_bounds) {
- TemplateInferredTypeReplacer::replace(
- $original_by_ref_out_type,
- $template_result,
- $codebase
- );
-
- $by_ref_out_type = $original_by_ref_out_type;
- }
- }
-
- if ($by_ref_type && $function_param->is_variadic && $arg->unpack) {
- $by_ref_type = new Union([
- new TArray([
- Type::getInt(),
- $by_ref_type,
- ]),
- ]);
- }
- }
-
- $by_ref_type = $by_ref_type ?: Type::getMixed();
-
- AssignmentAnalyzer::assignByRefParam(
- $statements_analyzer,
- $arg->value,
- $by_ref_type,
- $by_ref_out_type ?: $by_ref_type,
- $context,
- $method_id && (strpos($method_id, '::') !== false || !InternalCallMapHandler::inCallMap($method_id)),
- $check_null_ref
- );
- }
-
- return null;
- }
-
- /**
- * @return false|null
- */
- private static function evaluateArbitraryParam(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Arg $arg,
- Context $context
- ): ?bool {
- // there are a bunch of things we want to evaluate even when we don't
- // know what function/method is being called
- if ($arg->value instanceof PhpParser\Node\Expr\Closure
- || $arg->value instanceof PhpParser\Node\Expr\ConstFetch
- || $arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
- || $arg->value instanceof PhpParser\Node\Expr\FuncCall
- || $arg->value instanceof PhpParser\Node\Expr\MethodCall
- || $arg->value instanceof PhpParser\Node\Expr\StaticCall
- || $arg->value instanceof PhpParser\Node\Expr\New_
- || $arg->value instanceof PhpParser\Node\Expr\Cast
- || $arg->value instanceof PhpParser\Node\Expr\Assign
- || $arg->value instanceof PhpParser\Node\Expr\ArrayDimFetch
- || $arg->value instanceof PhpParser\Node\Expr\PropertyFetch
- || $arg->value instanceof PhpParser\Node\Expr\Array_
- || $arg->value instanceof PhpParser\Node\Expr\BinaryOp
- || $arg->value instanceof PhpParser\Node\Expr\Ternary
- || $arg->value instanceof PhpParser\Node\Scalar\Encapsed
- || $arg->value instanceof PhpParser\Node\Expr\PostInc
- || $arg->value instanceof PhpParser\Node\Expr\PostDec
- || $arg->value instanceof PhpParser\Node\Expr\PreInc
- || $arg->value instanceof PhpParser\Node\Expr\PreDec
- ) {
- $was_inside_call = $context->inside_call;
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $arg->value, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
-
- $context->inside_call = $was_inside_call;
- }
-
- if ($arg->value instanceof PhpParser\Node\Expr\PropertyFetch
- && $arg->value->name instanceof PhpParser\Node\Identifier
- ) {
- $var_id = '$' . $arg->value->name->name;
- } else {
- $var_id = ExpressionIdentifier::getVarId(
- $arg->value,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
- }
-
- if ($var_id) {
- if ($arg->value instanceof PhpParser\Node\Expr\Variable) {
- $statements_analyzer->registerPossiblyUndefinedVariable($var_id, $arg->value);
- }
-
- if (!$context->hasVariable($var_id)
- || $context->vars_in_scope[$var_id]->isNull()
- ) {
- if (!isset($context->vars_in_scope[$var_id])
- && $arg->value instanceof PhpParser\Node\Expr\Variable
- ) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedVariable(
- 'Variable ' . $var_id
- . ' must be defined prior to use within an unknown function or method',
- new CodeLocation($statements_analyzer->getSource(), $arg->value)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- // we don't know if it exists, assume it's passed by reference
- $context->vars_in_scope[$var_id] = Type::getMixed();
- $context->vars_possibly_in_scope[$var_id] = true;
- } else {
- $was_inside_call = $context->inside_call;
- $context->inside_call = true;
- ExpressionAnalyzer::analyze($statements_analyzer, $arg->value, $context);
- $context->inside_call = $was_inside_call;
-
- $context->removeVarFromConflictingClauses(
- $var_id,
- $context->vars_in_scope[$var_id],
- $statements_analyzer
- );
-
- foreach ($context->vars_in_scope[$var_id]->getAtomicTypes() as $type) {
- if ($type instanceof TArray && $type->type_params[1]->isEmpty()) {
- $context->vars_in_scope[$var_id]->removeType('array');
- $context->vars_in_scope[$var_id]->addType(
- new TArray(
- [Type::getArrayKey(), Type::getMixed()]
- )
- );
- }
- }
- }
- }
-
- return null;
- }
-
- /**
- * @return false|null
- */
- private static function handleByRefFunctionArg(
- StatementsAnalyzer $statements_analyzer,
- ?string $method_id,
- int $argument_offset,
- PhpParser\Node\Arg $arg,
- Context $context
- ): ?bool {
- $var_id = ExpressionIdentifier::getVarId(
- $arg->value,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $builtin_array_functions = [
- 'ksort', 'asort', 'krsort', 'arsort', 'natcasesort', 'natsort',
- 'reset', 'end', 'next', 'prev', 'array_pop', 'array_shift',
- ];
-
- if (($var_id && isset($context->vars_in_scope[$var_id]))
- || ($method_id
- && in_array(
- $method_id,
- $builtin_array_functions,
- true
- ))
- ) {
- $was_inside_assignment = $context->inside_assignment;
- $context->inside_assignment = true;
-
- // if the variable is in scope, get or we're in a special array function,
- // figure out its type before proceeding
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $arg->value,
- $context
- ) === false) {
- $context->inside_assignment = $was_inside_assignment;
-
- return false;
- }
-
- $context->inside_assignment = $was_inside_assignment;
- }
-
- // special handling for array sort
- if ($argument_offset === 0
- && $method_id
- && in_array(
- $method_id,
- $builtin_array_functions,
- true
- )
- ) {
- if (in_array($method_id, ['array_pop', 'array_shift'], true)) {
- ArrayFunctionArgumentsAnalyzer::handleByRefArrayAdjustment(
- $statements_analyzer,
- $arg,
- $context,
- $method_id === 'array_shift'
- );
-
- return null;
- }
-
- // noops
- if (in_array($method_id, ['reset', 'end', 'next', 'prev', 'ksort'], true)) {
- return null;
- }
-
- if (($arg_value_type = $statements_analyzer->node_data->getType($arg->value))
- && $arg_value_type->hasArray()
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TList|TKeyedArray
- */
- $array_type = $arg_value_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TKeyedArray) {
- $array_type = $array_type->getGenericArrayType();
- }
-
- if ($array_type instanceof TList) {
- $array_type = new TArray([Type::getInt(), $array_type->type_param]);
- }
-
- $by_ref_type = new Union([clone $array_type]);
-
- AssignmentAnalyzer::assignByRefParam(
- $statements_analyzer,
- $arg->value,
- $by_ref_type,
- $by_ref_type,
- $context,
- false
- );
-
- return null;
- }
- }
-
- if ($method_id === 'socket_select') {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $arg->value,
- $context
- ) === false) {
- return false;
- }
- }
-
- if (!$arg->value instanceof PhpParser\Node\Expr\Variable) {
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('EmptyArrayAccess', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['EmptyArrayAccess']);
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $arg->value, $context) === false) {
- return false;
- }
-
- if (!in_array('EmptyArrayAccess', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['EmptyArrayAccess']);
- }
- }
-
- return null;
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- * @param array<int,FunctionLikeParameter> $function_params
- * @param array<string, array<string, Union>> $class_generic_params
- */
- private static function getProvisionalTemplateResultForFunctionLike(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- Context $context,
- ?ClassLikeStorage $class_storage,
- ?string $self_fq_class_name,
- ?ClassLikeStorage $calling_class_storage,
- FunctionLikeStorage $function_storage,
- array $class_generic_params,
- ?TemplateResult $class_template_result,
- array $args,
- array $function_params,
- ?FunctionLikeParameter $last_param
- ): ?TemplateResult {
- $template_types = CallAnalyzer::getTemplateTypesForCall(
- $codebase,
- $class_storage,
- $self_fq_class_name,
- $calling_class_storage,
- $function_storage->template_types ?: [],
- $class_generic_params
- );
-
- if (!$template_types) {
- return null;
- }
-
- if (!$class_template_result) {
- return new TemplateResult($template_types, []);
- }
-
- $template_result = $class_template_result;
-
- if (!$template_result->template_types) {
- $template_result->template_types = $template_types;
- }
-
- foreach ($args as $argument_offset => $arg) {
- $function_param = null;
-
- if ($arg->name && $function_storage->allow_named_arg_calls) {
- foreach ($function_params as $candidate_param) {
- if ($candidate_param->name === $arg->name->name) {
- $function_param = $candidate_param;
- break;
- }
- }
- } elseif ($argument_offset < count($function_params)) {
- $function_param = $function_params[$argument_offset];
- } elseif ($last_param && $last_param->is_variadic) {
- $function_param = $last_param;
- }
-
- if (!$function_param
- || !$function_param->type
- ) {
- continue;
- }
-
- $arg_value_type = $statements_analyzer->node_data->getType($arg->value);
-
- if (!$arg_value_type) {
- continue;
- }
-
- $fleshed_out_param_type = TypeExpander::expandUnion(
- $codebase,
- $function_param->type,
- $class_storage->name ?? null,
- $calling_class_storage->name ?? null,
- null,
- true,
- false,
- $calling_class_storage->final ?? false
- );
-
- TemplateStandinTypeReplacer::replace(
- $fleshed_out_param_type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $arg_value_type,
- $argument_offset,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id,
- false
- );
- }
-
- return $template_result;
- }
-
- /**
- * @param array<int, PhpParser\Node\Arg> $args
- * @param string|MethodIdentifier|null $method_id
- * @param array<int,FunctionLikeParameter> $function_params
- */
- private static function checkArgCount(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- ?FunctionLikeStorage $function_storage,
- Context $context,
- ?TemplateResult $template_result,
- bool $is_variadic,
- array $args,
- array $function_params,
- bool $in_call_map,
- $method_id,
- ?string $cased_method_id,
- CodeLocation $code_location
- ): void {
- if (!$is_variadic
- && count($args) > count($function_params)
- && (!count($function_params) || $function_params[count($function_params) - 1]->name !== '...=')
- && ($in_call_map
- || !$function_storage instanceof MethodStorage
- || $function_storage->is_static
- || ($method_id instanceof MethodIdentifier
- && $method_id->method_name === '__construct'))
- ) {
- IssueBuffer::maybeAdd(
- new TooManyArguments(
- 'Too many arguments for ' . ($cased_method_id ?: $method_id)
- . ' - expecting ' . count($function_params) . ' but saw ' . count($args),
- $code_location,
- (string)$method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if (count($args) < count($function_params)) {
- //we're gonna loop over given args and unset them from the function_params.
- // If some mandatory params are left at the end, we'll throw an error
- foreach ($args as $arg) {
- // when the argument is not named, we can remove the params in order
- if ($arg->name === null) {
- // if we're unpacking, we try to unset the exact number of params, if we can't we give up and return
- if ($arg->unpack) {
- $arg_value_type = $statements_analyzer->node_data->getType($arg->value);
-
- if (!$arg_value_type || !$arg_value_type->hasArray()) {
- return;
- }
-
- if ($arg_value_type->isSingle()
- && ($atomic_arg_type = $arg_value_type->getSingleAtomic())
- && $atomic_arg_type instanceof TKeyedArray
- && !$atomic_arg_type->is_list
- ) {
- //if we have a single shape, we'll check param names
- foreach ($atomic_arg_type->properties as $property_name => $_property_type) {
- foreach ($function_params as $k => $param) {
- if ($param->name === $property_name) {
- unset($function_params[$k]);
- }
- }
- }
- continue;
- }
-
- foreach ($arg_value_type->getAtomicTypes() as $atomic_arg_type) {
- $packed_var_definite_args_tmp = [];
- if ($atomic_arg_type instanceof TCallableArray ||
- $atomic_arg_type instanceof TCallableList ||
- $atomic_arg_type instanceof TCallableKeyedArray
- ) {
- $packed_var_definite_args_tmp[] = 2;
- } elseif ($atomic_arg_type instanceof TKeyedArray) {
- if (!$atomic_arg_type->sealed) {
- return;
- }
-
- foreach ($atomic_arg_type->properties as $property_type) {
- if ($property_type->possibly_undefined) {
- return;
- }
- }
- //we did not return. The number of packed params is the number of properties
- $packed_var_definite_args_tmp[] = count($atomic_arg_type->properties);
- } elseif ($atomic_arg_type instanceof TNonEmptyArray ||
- $atomic_arg_type instanceof TNonEmptyList
- ) {
- if ($atomic_arg_type->count === null) {
- return;
- }
-
- $packed_var_definite_args_tmp[] = $atomic_arg_type->count;
- } elseif ($atomic_arg_type instanceof TArray
- && $atomic_arg_type->type_params[1]->isEmpty()
- ) {
- $packed_var_definite_args_tmp[] = 0;
- } else {
- return;
- }
-
-
- if (min($packed_var_definite_args_tmp) === max($packed_var_definite_args_tmp)) {
- //we have a stable number of params
- $packed_var_definite_args = $packed_var_definite_args_tmp[0];
- } else {
- return;
- }
- }
- } else {
- //if we're not unpacking, we remove the first param
- $packed_var_definite_args = 1;
- }
-
- $function_params = array_slice($function_params, $packed_var_definite_args);
- continue;
- }
-
- foreach ($function_params as $k => $param) {
- if ($param->name === $arg->name->name) {
- unset($function_params[$k]);
- continue;
- }
- }
- }
-
- //we're now left with an array of params that were not passed.
- // If they're mandatory, throw an error. Otherwise, we compute the default value
- foreach ($function_params as $i => $param) {
- if (!$param->is_optional && !$param->is_variadic) {
- IssueBuffer::maybeAdd(
- new TooFewArguments(
- 'Too few arguments for ' . $cased_method_id
- . ' - expecting ' . $param->name . ' to be passed',
- $code_location,
- (string)$method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- continue;
- }
-
- if ($param->type
- && $param->default_type
- && !$param->is_variadic
- && $template_result
- ) {
- if ($param->default_type instanceof Union) {
- $default_type = clone $param->default_type;
- } else {
- $default_type_atomic = ConstantTypeResolver::resolve(
- $codebase->classlikes,
- $param->default_type,
- $statements_analyzer
- );
-
- $default_type = new Union([$default_type_atomic]);
- }
-
- TemplateStandinTypeReplacer::replace(
- $param->type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $default_type,
- $i,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id,
- true
- );
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php
deleted file mode 100644
index 654e9bd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php
+++ /dev/null
@@ -1,957 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\ArrayAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ArgumentTypeCoercion;
-use Psalm\Issue\InvalidArgument;
-use Psalm\Issue\InvalidScalarArgument;
-use Psalm\Issue\MixedArgumentTypeCoercion;
-use Psalm\Issue\PossiblyInvalidArgument;
-use Psalm\Issue\TooFewArguments;
-use Psalm\Issue\TooManyArguments;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualArrayDimFetch;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_shift;
-use function array_unshift;
-use function assert;
-use function count;
-use function explode;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class ArrayFunctionArgumentsAnalyzer
-{
- /**
- * @param array<int, PhpParser\Node\Arg> $args
- */
- public static function checkArgumentsMatch(
- StatementsAnalyzer $statements_analyzer,
- Context $context,
- array $args,
- string $method_id,
- bool $check_functions
- ): void {
- $closure_index = $method_id === 'array_map' ? 0 : 1;
-
- $array_arg_types = [];
-
- foreach ($args as $i => $arg) {
- if ($i === 0 && $method_id === 'array_map') {
- continue;
- }
-
- if ($i === 1 && $method_id === 'array_filter') {
- break;
- }
-
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TKeyedArray|TArray|TList|null
- */
- $array_arg_type = ($arg_value_type = $statements_analyzer->node_data->getType($arg->value))
- && ($types = $arg_value_type->getAtomicTypes())
- && isset($types['array'])
- ? $types['array']
- : null;
-
- if ($array_arg_type instanceof TKeyedArray) {
- $array_arg_type = $array_arg_type->getGenericArrayType();
- }
-
- if ($array_arg_type instanceof TList) {
- $array_arg_type = new TArray([Type::getInt(), $array_arg_type->type_param]);
- }
-
- $array_arg_types[] = $array_arg_type;
- }
-
- $closure_arg = $args[$closure_index] ?? null;
-
- $closure_arg_type = null;
-
- if ($closure_arg) {
- $closure_arg_type = $statements_analyzer->node_data->getType($closure_arg->value);
- }
-
- if ($closure_arg && $closure_arg_type) {
- $min_closure_param_count = $max_closure_param_count = count($array_arg_types);
-
- if ($method_id === 'array_filter') {
- $max_closure_param_count = count($args) > 2 ? 2 : 1;
- }
-
- foreach ($closure_arg_type->getAtomicTypes() as $closure_type) {
- self::checkClosureType(
- $statements_analyzer,
- $context,
- $method_id,
- $closure_type,
- $closure_arg,
- $min_closure_param_count,
- $max_closure_param_count,
- $array_arg_types,
- $check_functions
- );
- }
- }
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- *
- * @return false|null
- */
- public static function handleAddition(
- StatementsAnalyzer $statements_analyzer,
- array $args,
- Context $context,
- string $method_id
- ): ?bool {
- $array_arg = $args[0]->value;
- $nb_args = count($args);
-
- $unpacked_args = array_filter(
- $args,
- function ($arg) {
- return $arg->unpack;
- }
- );
-
- if ($method_id === 'array_push' && !$unpacked_args) {
- for ($i = 1; $i < $nb_args; $i++) {
- $was_inside_assignment = $context->inside_assignment;
-
- $context->inside_assignment = true;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $args[$i]->value,
- $context
- ) === false) {
- $context->inside_assignment = $was_inside_assignment;
-
- return false;
- }
-
- $context->inside_assignment = $was_inside_assignment;
-
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- ArrayAssignmentAnalyzer::analyze(
- $statements_analyzer,
- new VirtualArrayDimFetch(
- $args[0]->value,
- null,
- $args[$i]->value->getAttributes()
- ),
- $context,
- $args[$i]->value,
- $statements_analyzer->node_data->getType($args[$i]->value) ?? Type::getMixed()
- );
-
- $statements_analyzer->node_data = $old_node_data;
- }
-
- return null;
- }
-
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $array_arg,
- $context
- ) === false) {
- return false;
- }
-
- for ($i = 1; $i < $nb_args; $i++) {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $args[$i]->value,
- $context
- ) === false) {
- return false;
- }
- }
-
- if (($array_arg_type = $statements_analyzer->node_data->getType($array_arg))
- && $array_arg_type->hasArray()
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_type = $array_arg_type->getAtomicTypes()['array'];
-
- $objectlike_list = null;
-
- if ($array_type instanceof TKeyedArray) {
- if ($array_type->is_list) {
- $objectlike_list = clone $array_type;
- }
-
- $array_type = $array_type->getGenericArrayType();
-
- if ($objectlike_list) {
- if ($array_type instanceof TNonEmptyArray) {
- $array_type = new TNonEmptyList($array_type->type_params[1]);
- } else {
- $array_type = new TList($array_type->type_params[1]);
- }
- }
- }
-
- $by_ref_type = new Union([clone $array_type]);
-
- foreach ($args as $argument_offset => $arg) {
- if ($argument_offset === 0) {
- continue;
- }
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $arg->value,
- $context
- ) === false) {
- return false;
- }
-
- if ($method_id === 'array_unshift' && $nb_args === 2 && !$unpacked_args) {
- $new_offset_type = Type::getInt(false, 0);
- } else {
- $new_offset_type = Type::getInt();
- }
-
- if (!($arg_value_type = $statements_analyzer->node_data->getType($arg->value))
- || $arg_value_type->hasMixed()
- ) {
- $by_ref_type = Type::combineUnionTypes(
- $by_ref_type,
- new Union([new TArray([$new_offset_type, Type::getMixed()])])
- );
- } elseif ($arg->unpack) {
- $arg_value_type = clone $arg_value_type;
-
- foreach ($arg_value_type->getAtomicTypes() as $arg_value_atomic_type) {
- if ($arg_value_atomic_type instanceof TKeyedArray) {
- $was_list = $arg_value_atomic_type->is_list;
-
- $arg_value_atomic_type = $arg_value_atomic_type->getGenericArrayType();
-
- if ($was_list) {
- if ($arg_value_atomic_type instanceof TNonEmptyArray) {
- $arg_value_atomic_type = new TNonEmptyList($arg_value_atomic_type->type_params[1]);
- } else {
- $arg_value_atomic_type = new TList($arg_value_atomic_type->type_params[1]);
- }
- }
-
- $arg_value_type->addType($arg_value_atomic_type);
- }
- }
-
- $by_ref_type = Type::combineUnionTypes(
- $by_ref_type,
- $arg_value_type
- );
- } else {
- if ($objectlike_list) {
- array_unshift($objectlike_list->properties, $arg_value_type);
-
- $by_ref_type = new Union([$objectlike_list]);
- } elseif ($array_type instanceof TList) {
- $by_ref_type = Type::combineUnionTypes(
- $by_ref_type,
- new Union(
- [
- new TNonEmptyList(clone $arg_value_type),
- ]
- )
- );
- } else {
- $by_ref_type = Type::combineUnionTypes(
- $by_ref_type,
- new Union(
- [
- new TNonEmptyArray(
- [
- $new_offset_type,
- clone $arg_value_type
- ]
- ),
- ]
- ),
- null,
- true
- );
- }
- }
- }
-
- AssignmentAnalyzer::assignByRefParam(
- $statements_analyzer,
- $array_arg,
- $by_ref_type,
- $by_ref_type,
- $context,
- false
- );
- }
-
- $context->inside_call = false;
-
- return null;
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- *
- * @return false|null
- */
- public static function handleSplice(
- StatementsAnalyzer $statements_analyzer,
- array $args,
- Context $context
- ): ?bool {
- $context->inside_call = true;
- $array_arg = $args[0]->value;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $array_arg,
- $context
- ) === false) {
- return false;
- }
-
- $offset_arg = $args[1]->value;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $offset_arg,
- $context
- ) === false) {
- return false;
- }
-
- if (!isset($args[2])) {
- return null;
- }
-
- $length_arg = $args[2]->value;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $length_arg,
- $context
- ) === false) {
- return false;
- }
-
- if (!isset($args[3])) {
- return null;
- }
-
- $replacement_arg = $args[3]->value;
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $replacement_arg,
- $context
- ) === false) {
- return false;
- }
-
- $context->inside_call = false;
-
- $replacement_arg_type = $statements_analyzer->node_data->getType($replacement_arg);
-
- if ($replacement_arg_type
- && !$replacement_arg_type->hasArray()
- && $replacement_arg_type->hasString()
- && $replacement_arg_type->isSingle()
- ) {
- $replacement_arg_type = new Union([
- new TArray([Type::getInt(), $replacement_arg_type])
- ]);
-
- $statements_analyzer->node_data->setType($replacement_arg, $replacement_arg_type);
- }
-
- if (($array_arg_type = $statements_analyzer->node_data->getType($array_arg))
- && $array_arg_type->hasArray()
- && $replacement_arg_type
- && $replacement_arg_type->hasArray()
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_type = $array_arg_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TKeyedArray) {
- if ($array_type->is_list) {
- $array_type = new TNonEmptyList($array_type->getGenericValueType());
- } else {
- $array_type = $array_type->getGenericArrayType();
- }
- }
-
- if ($array_type instanceof TArray
- && $array_type->type_params[0]->hasInt()
- && !$array_type->type_params[0]->hasString()
- ) {
- if ($array_type instanceof TNonEmptyArray) {
- $array_type = new TNonEmptyList($array_type->type_params[1]);
- } else {
- $array_type = new TList($array_type->type_params[1]);
- }
- }
-
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $replacement_array_type = $replacement_arg_type->getAtomicTypes()['array'];
-
- if ($replacement_array_type instanceof TKeyedArray) {
- $was_list = $replacement_array_type->is_list;
-
- $replacement_array_type = $replacement_array_type->getGenericArrayType();
-
- if ($was_list) {
- if ($replacement_array_type instanceof TNonEmptyArray) {
- $replacement_array_type = new TNonEmptyList($replacement_array_type->type_params[1]);
- } else {
- $replacement_array_type = new TList($replacement_array_type->type_params[1]);
- }
- }
- }
-
- $by_ref_type = TypeCombiner::combine([$array_type, $replacement_array_type]);
-
- AssignmentAnalyzer::assignByRefParam(
- $statements_analyzer,
- $array_arg,
- $by_ref_type,
- $by_ref_type,
- $context,
- false
- );
-
- return null;
- }
-
- $array_type = Type::getArray();
-
- AssignmentAnalyzer::assignByRefParam(
- $statements_analyzer,
- $array_arg,
- $array_type,
- $array_type,
- $context,
- false
- );
-
- return null;
- }
-
- public static function handleByRefArrayAdjustment(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Arg $arg,
- Context $context,
- bool $is_array_shift
- ): void {
- $var_id = ExpressionIdentifier::getVarId(
- $arg->value,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id) {
- $context->removeVarFromConflictingClauses($var_id, null, $statements_analyzer);
-
- if (isset($context->vars_in_scope[$var_id])) {
- $array_type = clone $context->vars_in_scope[$var_id];
-
- $array_atomic_types = $array_type->getAtomicTypes();
-
- foreach ($array_atomic_types as $array_atomic_type) {
- if ($array_atomic_type instanceof TKeyedArray) {
- if ($is_array_shift && $array_atomic_type->is_list) {
- $array_atomic_type = clone $array_atomic_type;
-
- $array_properties = $array_atomic_type->properties;
-
- array_shift($array_properties);
-
- if (!$array_properties) {
- $array_atomic_type = new TList(
- $array_atomic_type->previous_value_type ?: Type::getMixed()
- );
-
- $array_type->addType($array_atomic_type);
- } else {
- $array_atomic_type->properties = $array_properties;
- }
- }
-
- if ($array_atomic_type instanceof TKeyedArray) {
- $array_atomic_type = $array_atomic_type->getGenericArrayType();
- }
- }
-
- if ($array_atomic_type instanceof TNonEmptyArray) {
- if (!$context->inside_loop && $array_atomic_type->count !== null) {
- if ($array_atomic_type->count === 0) {
- $array_atomic_type = new TArray(
- [
- new Union([new TEmpty]),
- new Union([new TEmpty]),
- ]
- );
- } else {
- $array_atomic_type->count--;
- }
- } else {
- $array_atomic_type = new TArray($array_atomic_type->type_params);
- }
-
- $array_type->addType($array_atomic_type);
- } elseif ($array_atomic_type instanceof TNonEmptyList) {
- if (!$context->inside_loop && $array_atomic_type->count !== null) {
- if ($array_atomic_type->count === 0) {
- $array_atomic_type = new TArray(
- [
- new Union([new TEmpty]),
- new Union([new TEmpty]),
- ]
- );
- } else {
- $array_atomic_type->count--;
- }
- } else {
- $array_atomic_type = new TList($array_atomic_type->type_param);
- }
-
- $array_type->addType($array_atomic_type);
- }
- }
-
- $context->removeDescendents($var_id, $array_type);
- $context->vars_in_scope[$var_id] = $array_type;
- }
- }
- }
-
- /**
- * @param (TArray|null)[] $array_arg_types
- *
- */
- private static function checkClosureType(
- StatementsAnalyzer $statements_analyzer,
- Context $context,
- string $method_id,
- Atomic $closure_type,
- PhpParser\Node\Arg $closure_arg,
- int $min_closure_param_count,
- int $max_closure_param_count,
- array $array_arg_types,
- bool $check_functions
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$closure_type instanceof TClosure) {
- if ($method_id === 'array_map') {
- return;
- }
-
- if (!$closure_arg->value instanceof PhpParser\Node\Scalar\String_
- && !$closure_arg->value instanceof PhpParser\Node\Expr\Array_
- && !$closure_arg->value instanceof PhpParser\Node\Expr\BinaryOp\Concat
- ) {
- return;
- }
-
- $function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
- $statements_analyzer,
- $closure_arg->value
- );
-
- $closure_types = [];
-
- foreach ($function_ids as $function_id) {
- $function_id = strtolower($function_id);
-
- if (strpos($function_id, '::') !== false) {
- if ($function_id[0] === '$') {
- $function_id = substr($function_id, 1);
- }
-
- $function_id_parts = explode('&', $function_id);
-
- foreach ($function_id_parts as $function_id_part) {
- [$callable_fq_class_name, $method_name] = explode('::', $function_id_part);
-
- switch ($callable_fq_class_name) {
- case 'self':
- case 'static':
- case 'parent':
- $container_class = $statements_analyzer->getFQCLN();
-
- if ($callable_fq_class_name === 'parent') {
- $container_class = $statements_analyzer->getParentFQCLN();
- }
-
- if (!$container_class) {
- continue 2;
- }
-
- $callable_fq_class_name = $container_class;
- }
-
- if (!$codebase->classOrInterfaceExists($callable_fq_class_name)) {
- return;
- }
-
- $function_id_part = new MethodIdentifier(
- $callable_fq_class_name,
- strtolower($method_name)
- );
-
- try {
- $method_storage = $codebase->methods->getStorage($function_id_part);
- } catch (UnexpectedValueException $e) {
- // the method may not exist, but we're suppressing that issue
- continue;
- }
-
- $closure_types[] = new TClosure(
- 'Closure',
- $method_storage->params,
- $method_storage->return_type ?: Type::getMixed()
- );
- }
- } else {
- if (!$check_functions) {
- continue;
- }
-
- if (!$codebase->functions->functionExists($statements_analyzer, $function_id)) {
- continue;
- }
-
- $function_storage = $codebase->functions->getStorage(
- $statements_analyzer,
- $function_id
- );
-
- if (InternalCallMapHandler::inCallMap($function_id)) {
- $callmap_callables = InternalCallMapHandler::getCallablesFromCallMap($function_id);
-
- if ($callmap_callables === null) {
- throw new UnexpectedValueException('This should not happen');
- }
-
- $passing_callmap_callables = [];
-
- foreach ($callmap_callables as $callmap_callable) {
- $required_param_count = 0;
-
- assert($callmap_callable->params !== null);
-
- foreach ($callmap_callable->params as $i => $param) {
- if (!$param->is_optional && !$param->is_variadic) {
- $required_param_count = $i + 1;
- }
- }
-
- if ($required_param_count <= $max_closure_param_count) {
- $passing_callmap_callables[] = $callmap_callable;
- }
- }
-
- if ($passing_callmap_callables) {
- foreach ($passing_callmap_callables as $passing_callmap_callable) {
- $closure_types[] = $passing_callmap_callable;
- }
- } else {
- $closure_types[] = $callmap_callables[0];
- }
- } else {
- $closure_types[] = new TClosure(
- 'Closure',
- $function_storage->params,
- $function_storage->return_type ?: Type::getMixed()
- );
- }
- }
- }
- } else {
- $closure_types = [$closure_type];
- }
-
- foreach ($closure_types as $closure_type) {
- if ($closure_type->params === null) {
- continue;
- }
-
- self::checkClosureTypeArgs(
- $statements_analyzer,
- $context,
- $method_id,
- $closure_type,
- $closure_arg,
- $min_closure_param_count,
- $max_closure_param_count,
- $array_arg_types
- );
- }
- }
-
- /**
- * @param TClosure|TCallable $closure_type
- * @param (TArray|null)[] $array_arg_types
- */
- private static function checkClosureTypeArgs(
- StatementsAnalyzer $statements_analyzer,
- Context $context,
- string $method_id,
- Atomic $closure_type,
- PhpParser\Node\Arg $closure_arg,
- int $min_closure_param_count,
- int $max_closure_param_count,
- array $array_arg_types
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- $closure_params = $closure_type->params;
-
- if ($closure_params === null) {
- throw new UnexpectedValueException('Closure params should not be null here');
- }
-
- $required_param_count = 0;
-
- foreach ($closure_params as $i => $param) {
- if (!$param->is_optional && !$param->is_variadic) {
- $required_param_count = $i + 1;
- }
- }
-
- if (count($closure_params) < $min_closure_param_count) {
- $argument_text = $min_closure_param_count === 1 ? 'one argument' : $min_closure_param_count . ' arguments';
-
- IssueBuffer::maybeAdd(
- new TooManyArguments(
- 'The callable passed to ' . $method_id . ' will be called with ' . $argument_text . ', expecting '
- . $required_param_count,
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($required_param_count > $max_closure_param_count) {
- $argument_text = $max_closure_param_count === 1 ? 'one argument' : $max_closure_param_count . ' arguments';
-
- IssueBuffer::maybeAdd(
- new TooFewArguments(
- 'The callable passed to ' . $method_id . ' will be called with ' . $argument_text . ', expecting '
- . $required_param_count,
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- // abandon attempt to validate closure params if we have an extra arg for ARRAY_FILTER
- if ($method_id === 'array_filter' && $max_closure_param_count > 1) {
- return;
- }
-
- foreach ($closure_params as $i => $closure_param) {
- if (!isset($array_arg_types[$i])) {
- continue;
- }
-
- $array_arg_type = $array_arg_types[$i];
-
- $input_type = $array_arg_type->type_params[1];
-
- if ($input_type->hasMixed()) {
- continue;
- }
-
- $closure_param_type = $closure_param->type;
-
- if (!$closure_param_type) {
- continue;
- }
-
- if ($method_id === 'array_map'
- && $i === 0
- && $closure_type->return_type
- && $closure_param_type->hasTemplate()
- ) {
- $closure_param_type = clone $closure_param_type;
- $closure_type->return_type = clone $closure_type->return_type;
-
- $template_result = new TemplateResult(
- [],
- []
- );
-
- foreach ($closure_param_type->getTemplateTypes() as $template_type) {
- $template_result->template_types[$template_type->param_name] = [
- ($template_type->defining_class) => $template_type->as
- ];
- }
-
- $closure_param_type = TemplateStandinTypeReplacer::replace(
- $closure_param_type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $input_type,
- $i,
- $context->self,
- $context->calling_method_id ?: $context->calling_function_id
- );
-
- $closure_type->replaceTemplateTypesWithArgTypes(
- $template_result,
- $codebase
- );
- }
-
- $closure_param_type = TypeExpander::expandUnion(
- $codebase,
- $closure_param_type,
- $context->self,
- null,
- $statements_analyzer->getParentFQCLN()
- );
-
- $union_comparison_results = new TypeComparisonResult();
-
- $type_match_found = UnionTypeComparator::isContainedBy(
- $codebase,
- $input_type,
- $closure_param_type,
- $input_type->ignore_nullable_issues,
- $input_type->ignore_falsable_issues,
- $union_comparison_results
- );
-
- if ($union_comparison_results->type_coerced) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- IssueBuffer::maybeAdd(
- new MixedArgumentTypeCoercion(
- 'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
- $closure_param_type->getId() . ', parent type ' . $input_type->getId() . ' provided',
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new ArgumentTypeCoercion(
- 'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
- $closure_param_type->getId() . ', parent type ' . $input_type->getId() . ' provided',
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (!$union_comparison_results->type_coerced && !$type_match_found) {
- $types_can_be_identical = UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $input_type,
- $closure_param_type
- );
-
- if ($union_comparison_results->scalar_type_match_found) {
- IssueBuffer::maybeAdd(
- new InvalidScalarArgument(
- 'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
- $closure_param_type->getId() . ', ' . $input_type->getId() . ' provided',
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($types_can_be_identical) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidArgument(
- 'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects '
- . $closure_param_type->getId() . ', possibly different type '
- . $input_type->getId() . ' provided',
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif (IssueBuffer::accepts(
- new InvalidArgument(
- 'Parameter ' . ($i + 1) . ' of closure passed to function ' . $method_id . ' expects ' .
- $closure_param_type->getId() . ', ' . $input_type->getId() . ' provided',
- new CodeLocation($statements_analyzer->getSource(), $closure_arg),
- $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- // fall through
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php
deleted file mode 100644
index 1450e9e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ClassTemplateParamCollector.php
+++ /dev/null
@@ -1,282 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use Psalm\Codebase;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_keys;
-use function array_merge;
-use function array_search;
-
-class ClassTemplateParamCollector
-{
- /**
- * @param lowercase-string $method_name
- * @return array<string, non-empty-array<string, Union>>|null
- * @psalm-suppress MoreSpecificReturnType
- * @psalm-suppress LessSpecificReturnStatement
- */
- public static function collect(
- Codebase $codebase,
- ClassLikeStorage $class_storage,
- ClassLikeStorage $static_class_storage,
- ?string $method_name = null,
- ?Atomic $lhs_type_part = null,
- bool $self_call = false
- ): ?array {
- $static_fq_class_name = $static_class_storage->name;
-
- $non_trait_class_storage = $class_storage->is_trait
- ? $static_class_storage
- : $class_storage;
-
- $template_types = $class_storage->template_types;
-
- $candidate_class_storages = [$class_storage];
-
- if ($static_class_storage->template_extended_params
- && $method_name
- && !empty($non_trait_class_storage->overridden_method_ids[$method_name])
- && isset($class_storage->methods[$method_name])
- && (!isset($non_trait_class_storage->methods[$method_name]->return_type)
- || $class_storage->methods[$method_name]->inherited_return_type)
- ) {
- foreach ($non_trait_class_storage->overridden_method_ids[$method_name] as $overridden_method_id) {
- $overridden_storage = $codebase->methods->getStorage($overridden_method_id);
-
- if (!$overridden_storage->return_type) {
- continue;
- }
-
- if ($overridden_storage->return_type->isNull()) {
- continue;
- }
-
- $fq_overridden_class = $overridden_method_id->fq_class_name;
-
- $overridden_class_storage = $codebase->classlike_storage_provider->get($fq_overridden_class);
-
- $overridden_template_types = $overridden_class_storage->template_types;
-
- if (!$template_types) {
- $template_types = $overridden_template_types;
- } elseif ($overridden_template_types) {
- foreach ($overridden_template_types as $template_name => $template_map) {
- if (isset($template_types[$template_name])) {
- $template_types[$template_name] = array_merge(
- $template_types[$template_name],
- $template_map
- );
- } else {
- $template_types[$template_name] = $template_map;
- }
- }
- }
-
- $candidate_class_storages[] = $overridden_class_storage;
- }
- }
-
- if (!$template_types) {
- return null;
- }
-
- $class_template_params = [];
- $e = $static_class_storage->template_extended_params;
-
- if ($lhs_type_part instanceof TGenericObject) {
- if ($class_storage === $static_class_storage && $class_storage->template_types) {
- $i = 0;
-
- foreach ($class_storage->template_types as $type_name => $_) {
- if (isset($lhs_type_part->type_params[$i])) {
- $class_template_params[$type_name][$class_storage->name]
- = $lhs_type_part->type_params[$i];
- }
-
- $i++;
- }
- }
-
- foreach ($template_types as $type_name => $_) {
- if (isset($class_template_params[$type_name])) {
- continue;
- }
-
- if ($class_storage !== $static_class_storage
- && isset($e[$class_storage->name][$type_name])
- ) {
- $input_type_extends = $e[$class_storage->name][$type_name];
-
- $output_type_extends = self::resolveTemplateParam(
- $input_type_extends,
- $static_class_storage,
- $lhs_type_part
- );
- if (!$self_call || $static_fq_class_name !== $class_storage->name) {
- $class_template_params[$type_name][$class_storage->name]
- = $output_type_extends ?? Type::getMixed();
- }
- }
-
- if ((!$self_call || $static_fq_class_name !== $class_storage->name)
- && !isset($class_template_params[$type_name])
- ) {
- $class_template_params[$type_name] = [$class_storage->name => Type::getMixed()];
- }
- }
- }
-
- foreach ($template_types as $type_name => $type_map) {
- foreach ($type_map as $type) {
- foreach ($candidate_class_storages as $candidate_class_storage) {
- if ($candidate_class_storage !== $static_class_storage
- && isset($e[$candidate_class_storage->name][$type_name])
- && !isset($class_template_params[$type_name][$candidate_class_storage->name])
- ) {
- $class_template_params[$type_name][$candidate_class_storage->name] = new Union(
- self::expandType(
- $codebase,
- $e[$candidate_class_storage->name][$type_name],
- $e,
- $static_class_storage->name,
- $static_class_storage->template_types
- )
- );
- }
- }
-
- if (!$self_call) {
- if (!isset($class_template_params[$type_name])) {
- $class_template_params[$type_name][$class_storage->name] = $type;
- }
- }
- }
- }
-
- return $class_template_params;
- }
-
- public static function resolveTemplateParam(
- Union $input_type_extends,
- ClassLikeStorage $static_class_storage,
- TGenericObject $lhs_type_part
- ): ?Union {
- $output_type_extends = null;
- foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) {
- if ($type_extends_atomic instanceof TTemplateParam) {
- if (isset(
- $static_class_storage
- ->template_types
- [$type_extends_atomic->param_name]
- [$type_extends_atomic->defining_class]
- )
- ) {
- $mapped_offset = array_search(
- $type_extends_atomic->param_name,
- array_keys($static_class_storage->template_types),
- true
- );
-
- if ($mapped_offset !== false
- && isset($lhs_type_part->type_params[$mapped_offset])
- ) {
- $output_type_extends = Type::combineUnionTypes(
- $lhs_type_part->type_params[$mapped_offset],
- $output_type_extends
- );
- }
- } elseif (isset(
- $static_class_storage
- ->template_extended_params
- [$type_extends_atomic->defining_class]
- [$type_extends_atomic->param_name]
- )) {
- $nested_output_type = self::resolveTemplateParam(
- $static_class_storage
- ->template_extended_params
- [$type_extends_atomic->defining_class]
- [$type_extends_atomic->param_name],
- $static_class_storage,
- $lhs_type_part
- );
- if ($nested_output_type !== null) {
- $output_type_extends = Type::combineUnionTypes(
- $nested_output_type,
- $output_type_extends
- );
- }
- }
- } else {
- $output_type_extends = Type::combineUnionTypes(
- new Union([$type_extends_atomic]),
- $output_type_extends
- );
- }
- }
- return $output_type_extends;
- }
-
- /**
- * @param array<string, array<string, Union>> $e
- * @return non-empty-list<Atomic>
- */
- private static function expandType(
- Codebase $codebase,
- Union $input_type_extends,
- array $e,
- string $static_fq_class_name,
- ?array $static_template_types
- ): array {
- $output_type_extends = [];
-
- foreach ($input_type_extends->getAtomicTypes() as $type_extends_atomic) {
- if ($type_extends_atomic instanceof TTemplateParam
- && ($static_fq_class_name !== $type_extends_atomic->defining_class
- || !isset($static_template_types[$type_extends_atomic->param_name]))
- && isset($e[$type_extends_atomic->defining_class][$type_extends_atomic->param_name])
- ) {
- $output_type_extends = array_merge(
- $output_type_extends,
- self::expandType(
- $codebase,
- $e[$type_extends_atomic->defining_class][$type_extends_atomic->param_name],
- $e,
- $static_fq_class_name,
- $static_template_types
- )
- );
- } elseif ($type_extends_atomic instanceof TClassConstant) {
- $expanded = TypeExpander::expandAtomic(
- $codebase,
- $type_extends_atomic,
- $type_extends_atomic->fq_classlike_name,
- $type_extends_atomic->fq_classlike_name,
- null,
- true,
- true
- );
-
- if ($expanded instanceof Atomic) {
- $output_type_extends[] = $expanded;
- } else {
- foreach ($expanded as $type) {
- $output_type_extends[] = $type;
- }
- }
- } else {
- $output_type_extends[] = $type_extends_atomic;
- }
- }
-
- return $output_type_extends;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php
deleted file mode 100644
index d98cf1f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php
+++ /dev/null
@@ -1,1129 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\AlgebraAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\CallableTypeComparator;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Issue\DeprecatedFunction;
-use Psalm\Issue\ImpureFunctionCall;
-use Psalm\Issue\InvalidFunctionCall;
-use Psalm\Issue\MixedFunctionCall;
-use Psalm\Issue\NullFunctionCall;
-use Psalm\Issue\PossiblyInvalidFunctionCall;
-use Psalm\Issue\PossiblyNullFunctionCall;
-use Psalm\Issue\UnusedFunctionCall;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualFuncCall;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Plugin\EventHandler\Event\AfterEveryFunctionCallAnalysisEvent;
-use Psalm\Storage\Assertion;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableObject;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Reconciler;
-use Psalm\Type\TaintKind;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_map;
-use function array_merge;
-use function array_shift;
-use function array_slice;
-use function count;
-use function explode;
-use function implode;
-use function in_array;
-use function preg_replace;
-use function reset;
-use function spl_object_id;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class FunctionCallAnalyzer extends CallAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\FuncCall $stmt,
- Context $context
- ): bool {
- $function_name = $stmt->name;
-
- $codebase = $statements_analyzer->getCodebase();
-
- $code_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
- $config = $codebase->config;
-
- $is_first_class_callable = $stmt->isFirstClassCallable();
-
- $real_stmt = $stmt;
-
- if ($function_name instanceof PhpParser\Node\Name
- && !$is_first_class_callable
- && isset($stmt->getArgs()[0])
- && !$stmt->getArgs()[0]->unpack
- ) {
- $original_function_id = implode('\\', $function_name->parts);
-
- if ($original_function_id === 'call_user_func') {
- $other_args = array_slice($stmt->getArgs(), 1);
-
- $function_name = $stmt->getArgs()[0]->value;
-
- $stmt = new VirtualFuncCall(
- $function_name,
- $other_args,
- $stmt->getAttributes()
- );
- }
-
- if ($original_function_id === 'call_user_func_array' && isset($stmt->getArgs()[1])) {
- $function_name = $stmt->getArgs()[0]->value;
-
- $stmt = new VirtualFuncCall(
- $function_name,
- [new VirtualArg($stmt->getArgs()[1]->value, false, true)],
- $stmt->getAttributes()
- );
- }
- }
-
- if ($function_name instanceof PhpParser\Node\Expr) {
- $function_call_info = self::getAnalyzeNamedExpression(
- $statements_analyzer,
- $stmt,
- $real_stmt,
- $function_name,
- $context
- );
-
- if ($function_call_info->function_exists === false) {
- return true;
- }
-
- if ($function_call_info->new_function_name) {
- $function_name = $function_call_info->new_function_name;
- }
- } else {
- $function_call_info = self::handleNamedFunction(
- $statements_analyzer,
- $stmt,
- $function_name,
- $context,
- $code_location
- );
-
- if (!$function_call_info->function_exists) {
- return true;
- }
- }
-
- $set_inside_conditional = false;
-
- if ($function_name instanceof PhpParser\Node\Name
- && $function_name->parts === ['assert']
- && !$context->inside_conditional
- ) {
- $context->inside_conditional = true;
- $set_inside_conditional = true;
- }
-
- if (!$is_first_class_callable) {
- $template_result = null;
-
- if (isset($function_call_info->function_storage->template_types)) {
- $template_result = new TemplateResult($function_call_info->function_storage->template_types ?: [], []);
- }
-
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- $function_call_info->function_params,
- $function_call_info->function_id,
- $function_call_info->allow_named_args,
- $context,
- $template_result
- );
- }
-
- if ($set_inside_conditional) {
- $context->inside_conditional = false;
- }
-
- $function_callable = null;
-
- if (!$is_first_class_callable
- && $function_name instanceof PhpParser\Node\Name
- && $function_call_info->function_id
- ) {
- if (!$function_call_info->is_stubbed && $function_call_info->in_call_map) {
- $function_callable = InternalCallMapHandler::getCallableFromCallMapById(
- $codebase,
- $function_call_info->function_id,
- $stmt->getArgs(),
- $statements_analyzer->node_data
- );
-
- $function_call_info->function_params = $function_callable->params;
- }
- }
-
- $template_result = new TemplateResult([], []);
-
- // do this here to allow closure param checks
- if (!$is_first_class_callable && $function_call_info->function_params !== null) {
- ArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $stmt->getArgs(),
- $function_call_info->function_id,
- $function_call_info->function_params,
- $function_call_info->function_storage,
- null,
- $template_result,
- $code_location,
- $context
- );
- }
-
- CallAnalyzer::checkTemplateResult(
- $statements_analyzer,
- $template_result,
- $code_location,
- $function_call_info->function_id
- );
-
- if ($function_name instanceof PhpParser\Node\Name && $function_call_info->function_id) {
- $stmt_type = FunctionCallReturnTypeFetcher::fetch(
- $statements_analyzer,
- $codebase,
- $stmt,
- $function_name,
- $function_call_info->function_id,
- $function_call_info->in_call_map,
- $function_call_info->is_stubbed,
- $function_call_info->function_storage,
- $function_callable,
- $template_result,
- $context
- );
-
- $statements_analyzer->node_data->setType($real_stmt, $stmt_type);
-
- if ($stmt_type->isNever()) {
- $context->has_returned = true;
- }
-
- $event = new AfterEveryFunctionCallAnalysisEvent(
- $stmt,
- $function_call_info->function_id,
- $context,
- $statements_analyzer->getSource(),
- $codebase
- );
-
- $config->eventDispatcher->dispatchAfterEveryFunctionCallAnalysis($event);
-
- if ($is_first_class_callable) {
- return true;
- }
- }
-
- if ($is_first_class_callable) {
- $type_provider = $statements_analyzer->getNodeTypeProvider();
- $closure_types = [];
-
- if ($input_type = $type_provider->getType($function_name)) {
- foreach ($input_type->getAtomicTypes() as $atomic_type) {
- $candidate_callable = CallableTypeComparator::getCallableFromAtomic(
- $codebase,
- $atomic_type,
- null,
- $statements_analyzer
- );
-
- if ($candidate_callable) {
- $closure_types[] = new TClosure(
- 'Closure',
- $candidate_callable->params,
- $candidate_callable->return_type,
- $candidate_callable->is_pure
- );
- }
- }
- }
-
- if ($closure_types) {
- $stmt_type = TypeCombiner::combine($closure_types, $codebase);
- } else {
- $stmt_type = Type::getClosure();
- }
-
- $statements_analyzer->node_data->setType($real_stmt, $stmt_type);
-
- return true;
- }
-
- foreach ($function_call_info->defined_constants as $const_name => $const_type) {
- $context->constants[$const_name] = clone $const_type;
- $context->vars_in_scope[$const_name] = clone $const_type;
- }
-
- foreach ($function_call_info->global_variables as $var_id => $_) {
- $context->vars_in_scope[$var_id] = Type::getMixed();
- $context->vars_possibly_in_scope[$var_id] = true;
- }
-
- if ($function_name instanceof PhpParser\Node\Name
- && $function_name->parts === ['assert']
- && isset($stmt->getArgs()[0])
- ) {
- self::processAssertFunctionEffects(
- $statements_analyzer,
- $codebase,
- $stmt,
- $stmt->getArgs()[0],
- $context
- );
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- && ($stmt_type = $statements_analyzer->node_data->getType($real_stmt))
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt,
- $stmt_type->getId()
- );
- }
-
- self::checkFunctionCallPurity(
- $statements_analyzer,
- $codebase,
- $stmt,
- $function_name,
- $function_call_info,
- $context
- );
-
- if ($function_call_info->function_storage) {
- $inferred_lower_bounds = $template_result->lower_bounds;
-
- if ($function_call_info->function_storage->assertions && $function_name instanceof PhpParser\Node\Name) {
- self::applyAssertionsToContext(
- $function_name,
- null,
- $function_call_info->function_storage->assertions,
- $stmt->getArgs(),
- $inferred_lower_bounds,
- $context,
- $statements_analyzer
- );
- }
-
- if ($function_call_info->function_storage->if_true_assertions) {
- $statements_analyzer->node_data->setIfTrueAssertions(
- $stmt,
- array_map(
- function (Assertion $assertion) use ($inferred_lower_bounds, $codebase): Assertion {
- return $assertion->getUntemplatedCopy($inferred_lower_bounds ?: [], null, $codebase);
- },
- $function_call_info->function_storage->if_true_assertions
- )
- );
- }
-
- if ($function_call_info->function_storage->if_false_assertions) {
- $statements_analyzer->node_data->setIfFalseAssertions(
- $stmt,
- array_map(
- function (Assertion $assertion) use ($inferred_lower_bounds, $codebase): Assertion {
- return $assertion->getUntemplatedCopy($inferred_lower_bounds ?: [], null, $codebase);
- },
- $function_call_info->function_storage->if_false_assertions
- )
- );
- }
-
- if ($function_call_info->function_storage->deprecated && $function_call_info->function_id) {
- IssueBuffer::maybeAdd(
- new DeprecatedFunction(
- 'The function ' . $function_call_info->function_id . ' has been marked as deprecated',
- $code_location,
- $function_call_info->function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($function_call_info->byref_uses) {
- foreach ($function_call_info->byref_uses as $byref_use_var => $_) {
- $context->vars_in_scope['$' . $byref_use_var] = Type::getMixed();
- $context->vars_possibly_in_scope['$' . $byref_use_var] = true;
- }
- }
-
- if ($function_name instanceof PhpParser\Node\Name && $function_call_info->function_id) {
- NamedFunctionCallHandler::handle(
- $statements_analyzer,
- $codebase,
- $stmt,
- $real_stmt,
- $function_name,
- strtolower($function_call_info->function_id),
- $context
- );
- }
-
- if (!$statements_analyzer->node_data->getType($real_stmt)) {
- $statements_analyzer->node_data->setType($real_stmt, Type::getMixed());
- }
-
- return true;
- }
-
- private static function handleNamedFunction(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Name $function_name,
- Context $context,
- CodeLocation $code_location
- ): FunctionCallInfo {
- $function_call_info = new FunctionCallInfo();
-
- $codebase = $statements_analyzer->getCodebase();
- $codebase_functions = $codebase->functions;
-
- $original_function_id = implode('\\', $function_name->parts);
-
- if (!$function_name instanceof PhpParser\Node\Name\FullyQualified) {
- $function_call_info->function_id = $codebase_functions->getFullyQualifiedFunctionNameFromString(
- $original_function_id,
- $statements_analyzer
- );
- } else {
- $function_call_info->function_id = $original_function_id;
- }
-
- $namespaced_function_exists = $codebase_functions->functionExists(
- $statements_analyzer,
- strtolower($function_call_info->function_id)
- );
-
- if (!$namespaced_function_exists
- && !$function_name instanceof PhpParser\Node\Name\FullyQualified
- ) {
- $function_call_info->in_call_map = InternalCallMapHandler::inCallMap($original_function_id);
- $function_call_info->is_stubbed = $codebase_functions->hasStubbedFunction($original_function_id);
-
- if ($function_call_info->is_stubbed || $function_call_info->in_call_map) {
- $function_call_info->function_id = $original_function_id;
- }
- } else {
- $function_call_info->in_call_map = InternalCallMapHandler::inCallMap($function_call_info->function_id);
- $function_call_info->is_stubbed = $codebase_functions->hasStubbedFunction($function_call_info->function_id);
- }
-
- $function_call_info->function_exists
- = $function_call_info->is_stubbed || $function_call_info->in_call_map || $namespaced_function_exists;
-
- if ($function_call_info->function_exists
- && $codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- ArgumentMapPopulator::recordArgumentPositions(
- $statements_analyzer,
- $stmt,
- $codebase,
- $function_call_info->function_id
- );
- }
-
- $is_predefined = true;
-
- $is_maybe_root_function = !$function_name instanceof PhpParser\Node\Name\FullyQualified
- && count($function_name->parts) === 1;
-
- $args = $stmt->isFirstClassCallable() ? [] : $stmt->getArgs();
-
- if (!$function_call_info->in_call_map) {
- $predefined_functions = $codebase->config->getPredefinedFunctions();
- $is_predefined = isset($predefined_functions[strtolower($original_function_id)])
- || isset($predefined_functions[strtolower($function_call_info->function_id)]);
-
- if ($context->check_functions) {
- if (self::checkFunctionExists(
- $statements_analyzer,
- $function_call_info->function_id,
- $code_location,
- $is_maybe_root_function
- ) === false) {
- if ($args && ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $args,
- null,
- null,
- true,
- $context
- ) === false) {
- // fall through
- }
-
- return $function_call_info;
- }
-
- $function_call_info->function_exists = true;
- }
- } else {
- $function_call_info->function_exists = true;
- }
-
- $function_call_info->function_params = null;
- $function_call_info->defined_constants = [];
- $function_call_info->global_variables = [];
- $args = $stmt->isFirstClassCallable() ? [] : $stmt->getArgs();
-
- if ($function_call_info->function_exists) {
- if ($codebase->functions->params_provider->has($function_call_info->function_id)) {
- $function_call_info->function_params = $codebase->functions->params_provider->getFunctionParams(
- $statements_analyzer,
- $function_call_info->function_id,
- $args,
- null,
- $code_location
- );
- }
-
- if ($function_call_info->function_params === null) {
- if (!$function_call_info->in_call_map || $function_call_info->is_stubbed) {
- try {
- $function_call_info->function_storage = $function_storage = $codebase_functions->getStorage(
- $statements_analyzer,
- strtolower($function_call_info->function_id)
- );
-
- $function_call_info->function_params = $function_call_info->function_storage->params;
-
- if (!$function_storage->allow_named_arg_calls) {
- $function_call_info->allow_named_args = false;
- }
-
- if (!$is_predefined) {
- $function_call_info->defined_constants = $function_storage->defined_constants;
- $function_call_info->global_variables = $function_storage->global_variables;
- }
- } catch (UnexpectedValueException $e) {
- $function_call_info->function_params = [
- new FunctionLikeParameter('args', false, null, null, null, false, false, true)
- ];
- }
- } else {
- $function_callable = InternalCallMapHandler::getCallableFromCallMapById(
- $codebase,
- $function_call_info->function_id,
- $args,
- $statements_analyzer->node_data
- );
-
- $function_call_info->function_params = $function_callable->params;
- }
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $function_name,
- $function_call_info->function_id . '()'
- );
- }
- }
-
- return $function_call_info;
- }
-
- private static function getAnalyzeNamedExpression(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Expr\FuncCall $real_stmt,
- PhpParser\Node\Expr $function_name,
- Context $context
- ): FunctionCallInfo {
- $function_call_info = new FunctionCallInfo();
-
- $codebase = $statements_analyzer->getCodebase();
-
- $was_in_call = $context->inside_call;
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $function_name, $context) === false) {
- $context->inside_call = $was_in_call;
-
- return $function_call_info;
- }
-
- $context->inside_call = $was_in_call;
-
- $function_call_info->byref_uses = [];
-
- if ($stmt_name_type = $statements_analyzer->node_data->getType($function_name)) {
- if ($stmt_name_type->isNull()) {
- IssueBuffer::maybeAdd(
- new NullFunctionCall(
- 'Cannot call function on null value',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return $function_call_info;
- }
-
- if ($stmt_name_type->isNullable()) {
- IssueBuffer::maybeAdd(
- new PossiblyNullFunctionCall(
- 'Cannot call function on possibly null value',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $invalid_function_call_types = [];
- $has_valid_function_call_type = false;
-
- $var_atomic_types = $stmt_name_type->getAtomicTypes();
-
- while ($var_atomic_types) {
- $var_type_part = array_shift($var_atomic_types);
-
- if ($var_type_part instanceof TTemplateParam) {
- $var_atomic_types = array_merge($var_atomic_types, $var_type_part->as->getAtomicTypes());
- continue;
- }
-
- if ($var_type_part instanceof TClosure || $var_type_part instanceof TCallable) {
- if (!$var_type_part->is_pure) {
- if ($context->pure || $context->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureFunctionCall(
- 'Cannot call an impure function from a mutation-free context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$function_call_info->function_storage) {
- $function_call_info->function_storage = new FunctionStorage();
- }
-
- $function_call_info->function_storage->pure = false;
- $function_call_info->function_storage->mutation_free = false;
- }
-
- $function_call_info->function_params = $var_type_part->params;
-
- if (($stmt_type = $statements_analyzer->node_data->getType($real_stmt))
- && $var_type_part->return_type
- ) {
- $statements_analyzer->node_data->setType(
- $real_stmt,
- Type::combineUnionTypes(
- $stmt_type,
- $var_type_part->return_type
- )
- );
- } else {
- $statements_analyzer->node_data->setType(
- $real_stmt,
- $var_type_part->return_type ?? Type::getMixed()
- );
- }
-
- if ($var_type_part instanceof TClosure) {
- $function_call_info->byref_uses += $var_type_part->byref_uses;
- }
-
- $function_call_info->function_exists = true;
- $has_valid_function_call_type = true;
- } elseif ($var_type_part instanceof TMixed) {
- $has_valid_function_call_type = true;
-
- IssueBuffer::maybeAdd(
- new MixedFunctionCall(
- 'Cannot call function on ' . $var_type_part->getId(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($var_type_part instanceof TCallableObject
- || $var_type_part instanceof TCallableString
- || ($var_type_part instanceof TNamedObject && $var_type_part->value === 'Closure')
- || ($var_type_part instanceof TObjectWithProperties && isset($var_type_part->methods['__invoke']))
- ) {
- // this is fine
- $has_valid_function_call_type = true;
- } elseif ($var_type_part instanceof TString
- || $var_type_part instanceof TArray
- || $var_type_part instanceof TList
- || ($var_type_part instanceof TKeyedArray
- && count($var_type_part->properties) === 2)
- ) {
- $potential_method_id = null;
-
- if ($var_type_part instanceof TKeyedArray) {
- $potential_method_id = CallableTypeComparator::getCallableMethodIdFromTKeyedArray(
- $var_type_part,
- $codebase,
- $context->calling_method_id,
- $statements_analyzer->getFilePath()
- );
-
- if ($potential_method_id === 'not-callable') {
- $potential_method_id = null;
- }
- } elseif ($var_type_part instanceof TLiteralString) {
- if (!$var_type_part->value) {
- $invalid_function_call_types[] = '\'\'';
- continue;
- }
-
- if (strpos($var_type_part->value, '::')) {
- $parts = explode('::', strtolower($var_type_part->value));
- $fq_class_name = $parts[0];
- $fq_class_name = preg_replace('/^\\\\/', '', $fq_class_name);
- $potential_method_id = new MethodIdentifier($fq_class_name, $parts[1]);
- } else {
- $function_call_info->new_function_name = new VirtualFullyQualified(
- $var_type_part->value,
- $function_name->getAttributes()
- );
- }
- }
-
- if ($potential_method_id) {
- $codebase->methods->methodExists(
- $potential_method_id,
- $context->calling_method_id,
- null,
- $statements_analyzer,
- $statements_analyzer->getFilePath()
- );
- }
-
- // this is also kind of fine
- $has_valid_function_call_type = true;
- } elseif ($var_type_part instanceof TNull) {
- // handled above
- } elseif (!$var_type_part instanceof TNamedObject
- || !$codebase->classlikes->classOrInterfaceExists($var_type_part->value)
- || !$codebase->methods->methodExists(
- new MethodIdentifier(
- $var_type_part->value,
- '__invoke'
- )
- )
- ) {
- $invalid_function_call_types[] = (string)$var_type_part;
- } else {
- self::analyzeInvokeCall(
- $statements_analyzer,
- $stmt,
- $real_stmt,
- $function_name,
- $context,
- $var_type_part
- );
- }
- }
-
- if ($invalid_function_call_types) {
- $var_type_part = reset($invalid_function_call_types);
-
- if ($has_valid_function_call_type) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidFunctionCall(
- 'Cannot treat type ' . $var_type_part . ' as callable',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidFunctionCall(
- 'Cannot treat type ' . $var_type_part . ' as callable',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return $function_call_info;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $stmt_name_type->parent_nodes
- && $stmt_name_type->hasString()
- && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $arg_location = new CodeLocation($statements_analyzer->getSource(), $function_name);
-
- $custom_call_sink = TaintSink::getForMethodArgument(
- 'variable-call',
- 'variable-call',
- 0,
- $arg_location,
- $arg_location
- );
-
- $custom_call_sink->taints = [TaintKind::INPUT_CALLABLE];
-
- $statements_analyzer->data_flow_graph->addSink($custom_call_sink);
-
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- foreach ($stmt_name_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $custom_call_sink,
- 'call',
- $added_taints,
- $removed_taints
- );
- }
- }
- }
-
- if (!$statements_analyzer->node_data->getType($real_stmt)) {
- $statements_analyzer->node_data->setType($real_stmt, Type::getMixed());
- }
-
- return $function_call_info;
- }
-
- private static function analyzeInvokeCall(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Expr\FuncCall $real_stmt,
- PhpParser\Node\Expr $function_name,
- Context $context,
- Atomic $atomic_type
- ): void {
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_method_call = new VirtualMethodCall(
- $function_name,
- new VirtualIdentifier('__invoke', $function_name->getAttributes()),
- $stmt->args
- );
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('InternalMethod', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['InternalMethod']);
- }
-
- $statements_analyzer->node_data->setType($function_name, new Union([$atomic_type]));
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call,
- $context,
- false
- );
-
- if (!in_array('InternalMethod', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['InternalMethod']);
- }
-
- $fake_method_call_type = $statements_analyzer->node_data->getType($fake_method_call);
-
- $statements_analyzer->node_data = $old_data_provider;
-
- if ($stmt_type = $statements_analyzer->node_data->getType($real_stmt)) {
- $statements_analyzer->node_data->setType(
- $real_stmt,
- Type::combineUnionTypes(
- $fake_method_call_type ?? Type::getMixed(),
- $stmt_type
- )
- );
- } else {
- $statements_analyzer->node_data->setType(
- $real_stmt,
- $fake_method_call_type ?? Type::getMixed()
- );
- }
- }
-
- private static function processAssertFunctionEffects(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Arg $first_arg,
- Context $context
- ): void {
- $first_arg_value_id = spl_object_id($first_arg->value);
-
- $assert_clauses = FormulaGenerator::getFormula(
- $first_arg_value_id,
- $first_arg_value_id,
- $first_arg->value,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- AlgebraAnalyzer::checkForParadox(
- $context->clauses,
- $assert_clauses,
- $statements_analyzer,
- $stmt,
- []
- );
-
- $simplified_clauses = Algebra::simplifyCNF(array_merge($context->clauses, $assert_clauses));
-
- $assert_type_assertions = Algebra::getTruthsFromFormula($simplified_clauses);
-
- $changed_var_ids = [];
-
- if ($assert_type_assertions) {
- // while in an and, we allow scope to boil over to support
- // statements of the form if ($x && $x->foo())
- $op_vars_in_scope = Reconciler::reconcileKeyedTypes(
- $assert_type_assertions,
- $assert_type_assertions,
- $context->vars_in_scope,
- $changed_var_ids,
- array_map(
- function ($_): bool {
- return true;
- },
- $assert_type_assertions
- ),
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
-
- foreach ($changed_var_ids as $var_id => $_) {
- $first_appearance = $statements_analyzer->getFirstAppearance($var_id);
-
- if ($first_appearance
- && isset($context->vars_in_scope[$var_id])
- && $context->vars_in_scope[$var_id]->hasMixed()
- ) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->decrementMixedCount($statements_analyzer->getFilePath());
- }
-
- IssueBuffer::remove(
- $statements_analyzer->getFilePath(),
- 'MixedAssignment',
- $first_appearance->raw_file_start
- );
- }
-
- if (isset($op_vars_in_scope[$var_id])) {
- $op_vars_in_scope[$var_id]->from_docblock = true;
- }
- }
-
- $context->vars_in_scope = $op_vars_in_scope;
- }
-
- if ($changed_var_ids) {
- $simplified_clauses = Context::removeReconciledClauses($simplified_clauses, $changed_var_ids)[0];
- }
-
- $context->clauses = $simplified_clauses;
- }
-
- private static function checkFunctionCallPurity(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node $function_name,
- FunctionCallInfo $function_call_info,
- Context $context
- ): void {
- $config = $codebase->config;
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && ($context->mutation_free
- || $context->external_mutation_free
- || $codebase->find_unused_variables
- || !$config->remember_property_assignments_after_call
- || ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations))
- ) {
- $must_use = true;
-
- $callmap_function_pure = $function_call_info->function_id && $function_call_info->in_call_map
- ? $codebase->functions->isCallMapFunctionPure(
- $codebase,
- $statements_analyzer->node_data,
- $function_call_info->function_id,
- $stmt->isFirstClassCallable() ? [] : $stmt->getArgs(),
- $must_use
- )
- : null;
-
- if ((!$function_call_info->in_call_map
- && $function_call_info->function_storage
- && !$function_call_info->function_storage->pure
- && !$function_call_info->function_storage->mutation_free)
- || ($callmap_function_pure === false)
- ) {
- if ($context->mutation_free || $context->external_mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureFunctionCall(
- 'Cannot call an impure function from a mutation-free context',
- new CodeLocation($statements_analyzer, $function_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
-
- if (!$config->remember_property_assignments_after_call) {
- $context->removeMutableObjectVars();
- }
- } elseif ($function_call_info->function_id
- && (($function_call_info->function_storage
- && $function_call_info->function_storage->pure
- && !$function_call_info->function_storage->assertions
- && $must_use)
- || ($callmap_function_pure === true && $must_use))
- && $codebase->find_unused_variables
- && !$context->inside_conditional
- && !$context->inside_unset
- ) {
- /**
- * If a function is pure, and has the return type of 'no-return',
- * it's okay to dismiss it's return value.
- */
- if (!$context->insideUse()
- && !self::callUsesByReferenceArguments($function_call_info, $stmt)
- && !(
- $function_call_info->function_storage &&
- $function_call_info->function_storage->return_type &&
- $function_call_info->function_storage->return_type->isNever()
- )
- ) {
- IssueBuffer::maybeAdd(
- new UnusedFunctionCall(
- 'The call to ' . $function_call_info->function_id . ' is not used',
- new CodeLocation($statements_analyzer, $function_name),
- $function_call_info->function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $stmt->setAttribute('pure', true);
- }
- }
- }
- }
-
- private static function callUsesByReferenceArguments(
- FunctionCallInfo $function_call_info,
- PhpParser\Node\Expr\FuncCall $stmt
- ): bool {
- // If the function doesn't have any by-reference parameters
- // we shouldn't look any further.
- if (!$function_call_info->hasByReferenceParameters() || null === $function_call_info->function_params) {
- return false;
- }
-
- $parameters = $function_call_info->function_params;
-
- // If no arguments were passed
- if (0 === count($stmt->getArgs())) {
- return false;
- }
-
- foreach ($stmt->getArgs() as $index => $argument) {
- $parameter = null;
- if (null !== $argument->name) {
- $argument_name = $argument->name->toString();
- foreach ($parameters as $param) {
- if ($param->name === $argument_name) {
- $parameter = $param;
- break;
- }
- }
- } else {
- $parameter = $parameters[$index] ?? null;
- }
-
- if ($parameter && $parameter->by_ref) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php
deleted file mode 100644
index 61984a8..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallInfo.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class FunctionCallInfo
-{
- /**
- * @var ?string
- */
- public $function_id;
-
- /**
- * @var ?bool
- */
- public $function_exists;
-
- /**
- * @var bool
- */
- public $is_stubbed = false;
-
- /**
- * @var bool
- */
- public $in_call_map = false;
-
- /**
- * @var array<string, Union>
- */
- public $defined_constants = [];
-
- /**
- * @var array<string, bool>
- */
- public $global_variables = [];
-
- /**
- * @var ?array<int, FunctionLikeParameter>
- */
- public $function_params;
-
- /**
- * @var ?FunctionLikeStorage
- */
- public $function_storage;
-
- /**
- * @var ?PhpParser\Node\Name
- */
- public $new_function_name;
-
- /**
- * @var bool
- */
- public $allow_named_args = true;
-
- /**
- * @var array
- */
- public $byref_uses = [];
-
- /**
- * @mutation-free
- */
- public function hasByReferenceParameters(): bool
- {
- if (null === $this->function_params) {
- return false;
- }
-
- foreach ($this->function_params as $value) {
- if ($value->by_ref) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php
deleted file mode 100644
index a843e94..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php
+++ /dev/null
@@ -1,861 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use InvalidArgumentException;
-use PhpParser;
-use PhpParser\BuilderFactory;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\TaintSource;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Type\Comparator\CallableTypeComparator;
-use Psalm\Internal\Type\TemplateBound;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Plugin\EventHandler\Event\AfterFunctionCallAnalysisEvent;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TCallableList;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_values;
-use function count;
-use function explode;
-use function in_array;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class FunctionCallReturnTypeFetcher
-{
- /**
- * @param non-empty-string $function_id
- */
- public static function fetch(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Name $function_name,
- string $function_id,
- bool $in_call_map,
- bool $is_stubbed,
- ?FunctionLikeStorage $function_storage,
- ?TCallable $callmap_callable,
- TemplateResult $template_result,
- Context $context
- ): Union {
- $stmt_type = null;
- $config = $codebase->config;
-
- if ($stmt->isFirstClassCallable()) {
- $candidate_callable = CallableTypeComparator::getCallableFromAtomic(
- $codebase,
- new TLiteralString($function_id),
- null,
- $statements_analyzer,
- true
- );
-
- if ($candidate_callable) {
- $stmt_type = new Union([new TClosure(
- 'Closure',
- $candidate_callable->params,
- $candidate_callable->return_type,
- $candidate_callable->is_pure
- )]);
- } else {
- $stmt_type = Type::getClosure();
- }
- } elseif ($codebase->functions->return_type_provider->has($function_id)) {
- $stmt_type = $codebase->functions->return_type_provider->getReturnType(
- $statements_analyzer,
- $function_id,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $function_name)
- );
- }
-
- if (!$stmt_type) {
- if (!$in_call_map || $is_stubbed) {
- if ($function_storage && $function_storage->template_types) {
- foreach ($function_storage->template_types as $template_name => $_) {
- if (!isset($template_result->lower_bounds[$template_name])) {
- if ($template_name === 'TFunctionArgCount') {
- $template_result->lower_bounds[$template_name] = [
- 'fn-' . $function_id => [
- new TemplateBound(
- Type::getInt(false, count($stmt->getArgs()))
- )
- ]
- ];
- } elseif ($template_name === 'TPhpMajorVersion') {
- $template_result->lower_bounds[$template_name] = [
- 'fn-' . $function_id => [
- new TemplateBound(
- Type::getInt(false, $codebase->php_major_version)
- )
- ]
- ];
- } elseif ($template_name === 'TPhpVersionId') {
- $template_result->lower_bounds[$template_name] = [
- 'fn-' . $function_id => [
- new TemplateBound(
- Type::getInt(
- false,
- 10000 * $codebase->php_major_version
- + 100 * $codebase->php_minor_version
- )
- )
- ]
- ];
- } else {
- $template_result->lower_bounds[$template_name] = [
- 'fn-' . $function_id => [
- new TemplateBound(
- Type::getEmpty()
- )
- ]
- ];
- }
- }
- }
- }
-
- if ($function_storage && !$context->isSuppressingExceptions($statements_analyzer)) {
- $context->mergeFunctionExceptions(
- $function_storage,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
- }
-
- try {
- if ($function_storage && $function_storage->return_type) {
- $return_type = clone $function_storage->return_type;
-
- if ($template_result->lower_bounds && $function_storage->template_types) {
- $return_type = TypeExpander::expandUnion(
- $codebase,
- $return_type,
- null,
- null,
- null
- );
-
- TemplateInferredTypeReplacer::replace(
- $return_type,
- $template_result,
- $codebase
- );
- }
-
- $return_type = TypeExpander::expandUnion(
- $codebase,
- $return_type,
- null,
- null,
- null,
- true,
- false,
- false,
- true
- );
-
- $return_type_location = $function_storage->return_type_location;
-
- $event = new AfterFunctionCallAnalysisEvent(
- $stmt,
- $function_id,
- $context,
- $statements_analyzer->getSource(),
- $codebase,
- $return_type,
- []
- );
-
- $config->eventDispatcher->dispatchAfterFunctionCallAnalysis($event);
- $file_manipulations = $event->getFileReplacements();
-
- if ($file_manipulations) {
- FileManipulationBuffer::add(
- $statements_analyzer->getFilePath(),
- $file_manipulations
- );
- }
-
- $stmt_type = $return_type;
- $return_type->by_ref = $function_storage->returns_by_ref;
-
- // only check the type locally if it's defined externally
- if ($return_type_location &&
- !$is_stubbed && // makes lookups or array_* functions quicker
- !$config->isInProjectDirs($return_type_location->file_path)
- ) {
- $return_type->check(
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues(),
- $context->phantom_classes,
- true,
- false,
- false,
- $context->calling_method_id
- );
- }
- }
- } catch (InvalidArgumentException $e) {
- // this can happen when the function was defined in the Config startup script
- $stmt_type = Type::getMixed();
- }
- } else {
- if (!$callmap_callable) {
- throw new UnexpectedValueException('We should have a callmap callable here');
- }
-
- $stmt_type = self::getReturnTypeFromCallMapWithArgs(
- $statements_analyzer,
- $function_id,
- $stmt->getArgs(),
- $callmap_callable,
- $context
- );
- }
- }
-
- if (!$stmt_type) {
- $stmt_type = Type::getMixed();
- }
-
- if (!$statements_analyzer->data_flow_graph || !$function_storage) {
- return $stmt_type;
- }
-
- $return_node = self::taintReturnType(
- $statements_analyzer,
- $stmt,
- $function_id,
- $function_storage,
- $stmt_type,
- $template_result,
- $context
- );
-
- if ($function_storage->proxy_calls !== null) {
- foreach ($function_storage->proxy_calls as $proxy_call) {
- $fake_call_arguments = [];
- foreach ($proxy_call['params'] as $i) {
- $fake_call_arguments[] = $stmt->getArgs()[$i];
- }
-
- $fake_call_factory = new BuilderFactory();
-
- if (strpos($proxy_call['fqn'], '::') !== false) {
- [$fqcn, $method] = explode('::', $proxy_call['fqn']);
- $fake_call = $fake_call_factory->staticCall($fqcn, $method, $fake_call_arguments);
- } else {
- $fake_call = $fake_call_factory->funcCall($proxy_call['fqn'], $fake_call_arguments);
- }
-
- $old_node_data = $statements_analyzer->node_data;
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $fake_call, $context);
-
- $statements_analyzer->node_data = $old_node_data;
-
- if ($return_node && $proxy_call['return']) {
- $fake_call_type = $statements_analyzer->node_data->getType($fake_call);
- if (null !== $fake_call_type) {
- foreach ($fake_call_type->parent_nodes as $fake_call_node) {
- $statements_analyzer->data_flow_graph->addPath($fake_call_node, $return_node, 'return');
- }
- }
- }
- }
- }
-
- return $stmt_type;
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $call_args
- */
- private static function getReturnTypeFromCallMapWithArgs(
- StatementsAnalyzer $statements_analyzer,
- string $function_id,
- array $call_args,
- TCallable $callmap_callable,
- Context $context
- ): Union {
- $call_map_key = strtolower($function_id);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$call_args) {
- switch ($call_map_key) {
- case 'hrtime':
- $keyed_array = new TKeyedArray([
- Type::getInt(),
- Type::getInt()
- ]);
- $keyed_array->sealed = true;
- $keyed_array->is_list = true;
- return new Union([$keyed_array]);
-
- case 'get_called_class':
- return new Union([
- new TClassString(
- $context->self ?: 'object',
- $context->self ? new TNamedObject($context->self, true) : null
- )
- ]);
-
- case 'get_parent_class':
- if ($context->self && $codebase->classExists($context->self)) {
- $classlike_storage = $codebase->classlike_storage_provider->get($context->self);
-
- if ($classlike_storage->parent_classes) {
- return new Union([
- new TClassString(
- array_values($classlike_storage->parent_classes)[0]
- )
- ]);
- }
- }
- }
- } else {
- switch ($call_map_key) {
- case 'count':
- if (($first_arg_type = $statements_analyzer->node_data->getType($call_args[0]->value))) {
- $atomic_types = $first_arg_type->getAtomicTypes();
-
- if (count($atomic_types) === 1) {
- if (isset($atomic_types['array'])) {
- if ($atomic_types['array'] instanceof TCallableArray
- || $atomic_types['array'] instanceof TCallableList
- || $atomic_types['array'] instanceof TCallableKeyedArray
- ) {
- return Type::getInt(false, 2);
- }
-
- if ($atomic_types['array'] instanceof TNonEmptyArray) {
- return new Union([
- $atomic_types['array']->count !== null
- ? new TLiteralInt($atomic_types['array']->count)
- : new TPositiveInt
- ]);
- }
-
- if ($atomic_types['array'] instanceof TNonEmptyList) {
- return new Union([
- $atomic_types['array']->count !== null
- ? new TLiteralInt($atomic_types['array']->count)
- : new TPositiveInt
- ]);
- }
-
- if ($atomic_types['array'] instanceof TKeyedArray) {
- $min = 0;
- $max = 0;
- foreach ($atomic_types['array']->properties as $property) {
- // empty, never and possibly undefined can't count for min value
- if (!$property->possibly_undefined
- && !$property->isEmpty()
- && !$property->isNever()
- ) {
- $min++;
- }
-
- //empty and never can't count for max value because we know keys are undefined
- if (!$property->isEmpty() && !$property->isNever()) {
- $max++;
- }
- }
-
- if ($atomic_types['array']->sealed) {
- //the KeyedArray is sealed, we can use the min and max
- if ($min === $max) {
- return new Union([new TLiteralInt($max)]);
- }
-
- return new Union([new TIntRange($min, $max)]);
- }
-
- //the type is not sealed, we can only use the min
- return new Union([new TIntRange($min, null)]);
- }
-
- if ($atomic_types['array'] instanceof TArray
- && $atomic_types['array']->type_params[0]->isEmpty()
- && $atomic_types['array']->type_params[1]->isEmpty()
- ) {
- return Type::getInt(false, 0);
- }
-
- return new Union([
- new TLiteralInt(0),
- new TPositiveInt
- ]);
- }
- }
- }
-
- break;
-
- case 'hrtime':
- if (($first_arg_type = $statements_analyzer->node_data->getType($call_args[0]->value))) {
- if ((string) $first_arg_type === 'true') {
- $int = Type::getInt();
- $int->from_calculation = true;
- return $int;
- }
-
- $keyed_array = new TKeyedArray([
- Type::getInt(),
- Type::getInt()
- ]);
- $keyed_array->sealed = true;
- $keyed_array->is_list = true;
-
- if ((string) $first_arg_type === 'false') {
- return new Union([$keyed_array]);
- }
-
- return new Union([
- $keyed_array,
- new TInt()
- ]);
- }
-
- $int = Type::getInt();
- $int->from_calculation = true;
- return $int;
-
- case 'min':
- case 'max':
- if (isset($call_args[0])) {
- $first_arg = $call_args[0]->value;
-
- if ($first_arg_type = $statements_analyzer->node_data->getType($first_arg)) {
- if ($first_arg_type->hasArray()) {
- /** @psalm-suppress PossiblyUndefinedStringArrayOffset */
- $array_type = $first_arg_type->getAtomicTypes()['array'];
- if ($array_type instanceof TKeyedArray) {
- return $array_type->getGenericValueType();
- }
-
- if ($array_type instanceof TArray) {
- return clone $array_type->type_params[1];
- }
-
- if ($array_type instanceof TList) {
- return clone $array_type->type_param;
- }
- } elseif ($first_arg_type->hasScalarType()
- && ($second_arg = ($call_args[1]->value ?? null))
- && ($second_arg_type = $statements_analyzer->node_data->getType($second_arg))
- && $second_arg_type->hasScalarType()
- ) {
- return Type::combineUnionTypes($first_arg_type, $second_arg_type);
- }
- }
- }
-
- break;
-
- case 'get_parent_class':
- // this is unreliable, as it's hard to know exactly what's wanted - attempted this in
- // https://github.com/vimeo/psalm/commit/355ed831e1c69c96bbf9bf2654ef64786cbe9fd7
- // but caused problems where it didn’t know exactly what level of child we
- // were receiving.
- //
- // Really this should only work on instances we've created with new Foo(),
- // but that requires more work
- break;
-
- case 'fgetcsv':
- $string_type = Type::getString();
- $string_type->addType(new TNull);
- $string_type->ignore_nullable_issues = true;
-
- $call_map_return_type = new Union([
- new TNonEmptyList(
- $string_type
- ),
- new TFalse,
- new TNull
- ]);
-
- if ($codebase->config->ignore_internal_nullable_issues) {
- $call_map_return_type->ignore_nullable_issues = true;
- }
-
- if ($codebase->config->ignore_internal_falsable_issues) {
- $call_map_return_type->ignore_falsable_issues = true;
- }
-
- return $call_map_return_type;
- case 'mb_strtolower':
- if (count($call_args) < 2) {
- return Type::getLowercaseString();
- } else {
- $second_arg_type = $statements_analyzer->node_data->getType($call_args[1]->value);
- if ($second_arg_type && $second_arg_type->isNull()) {
- return Type::getLowercaseString();
- }
- }
- return Type::getString();
- }
- }
-
- $stmt_type = $callmap_callable->return_type
- ? clone $callmap_callable->return_type
- : Type::getMixed();
-
- switch ($function_id) {
- case 'mb_strpos':
- case 'mb_strrpos':
- case 'mb_stripos':
- case 'mb_strripos':
- case 'strpos':
- case 'strrpos':
- case 'stripos':
- case 'strripos':
- case 'strstr':
- case 'stristr':
- case 'strrchr':
- case 'strpbrk':
- case 'array_search':
- break;
-
- default:
- if ($stmt_type->isFalsable()
- && $codebase->config->ignore_internal_falsable_issues
- ) {
- $stmt_type->ignore_falsable_issues = true;
- }
- }
-
- return $stmt_type;
- }
-
- private static function taintReturnType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\FuncCall $stmt,
- string $function_id,
- FunctionLikeStorage $function_storage,
- Union $stmt_type,
- TemplateResult $template_result,
- Context $context
- ): ?DataFlowNode {
- if (!$statements_analyzer->data_flow_graph) {
- return null;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- return null;
- }
-
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- $node_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $function_call_node = DataFlowNode::getForMethodReturn(
- $function_id,
- $function_id,
- $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ? ($function_storage->signature_return_type_location ?: $function_storage->location)
- : ($function_storage->return_type_location ?: $function_storage->location),
- $function_storage->specialize_call ? $node_location : null
- );
-
- $statements_analyzer->data_flow_graph->addNode($function_call_node);
-
- $codebase = $statements_analyzer->getCodebase();
-
- $conditionally_removed_taints = [];
-
- foreach ($function_storage->conditionally_removed_taints as $conditionally_removed_taint) {
- $conditionally_removed_taint = clone $conditionally_removed_taint;
-
- TemplateInferredTypeReplacer::replace(
- $conditionally_removed_taint,
- $template_result,
- $codebase
- );
-
- $expanded_type = TypeExpander::expandUnion(
- $statements_analyzer->getCodebase(),
- $conditionally_removed_taint,
- null,
- null,
- null,
- true,
- true
- );
-
- if (!$expanded_type->isNullable()) {
- foreach ($expanded_type->getLiteralStrings() as $literal_string) {
- $conditionally_removed_taints[] = $literal_string->value;
- }
- }
- }
-
- if ($conditionally_removed_taints && $function_storage->location) {
- $assignment_node = DataFlowNode::getForAssignment(
- $function_id . '-escaped',
- $function_storage->signature_return_type_location ?: $function_storage->location,
- $function_call_node->specialization_key
- );
-
- $statements_analyzer->data_flow_graph->addPath(
- $function_call_node,
- $assignment_node,
- 'conditionally-escaped',
- $added_taints,
- array_merge($removed_taints, $conditionally_removed_taints)
- );
-
- $stmt_type->parent_nodes[$assignment_node->id] = $assignment_node;
- } else {
- $stmt_type->parent_nodes[$function_call_node->id] = $function_call_node;
- }
-
- if ($function_storage->return_source_params
- && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ) {
- $removed_taints = $function_storage->removed_taints;
-
- if ($function_id === 'preg_replace' && count($stmt->getArgs()) > 2) {
- $first_stmt_type = $statements_analyzer->node_data->getType($stmt->getArgs()[0]->value);
- $second_stmt_type = $statements_analyzer->node_data->getType($stmt->getArgs()[1]->value);
-
- if ($first_stmt_type
- && $second_stmt_type
- && $first_stmt_type->isSingleStringLiteral()
- && $second_stmt_type->isSingleStringLiteral()
- ) {
- $first_arg_value = $first_stmt_type->getSingleStringLiteral()->value;
-
- $pattern = substr($first_arg_value, 1, -1);
-
- if ($pattern[0] === '['
- && $pattern[1] === '^'
- && substr($pattern, -1) === ']'
- ) {
- $pattern = substr($pattern, 2, -1);
-
- if (self::simpleExclusion($pattern, $first_arg_value[0])) {
- $removed_taints[] = 'html';
- $removed_taints[] = 'has_quotes';
- $removed_taints[] = 'sql';
- }
- }
- }
- }
-
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = array_merge(
- $removed_taints,
- $codebase->config->eventDispatcher->dispatchRemoveTaints($event)
- );
-
- self::taintUsingFlows(
- $statements_analyzer,
- $function_storage,
- $statements_analyzer->data_flow_graph,
- $function_id,
- $stmt->getArgs(),
- $node_location,
- $function_call_node,
- $removed_taints,
- $added_taints
- );
- }
-
- if ($function_storage->taint_source_types && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- $method_node = TaintSource::getForMethodReturn(
- $function_id,
- $function_id,
- $node_location
- );
-
- $method_node->taints = $function_storage->taint_source_types;
-
- $statements_analyzer->data_flow_graph->addSource($method_node);
- }
-
- return $function_call_node;
- }
-
- /**
- * @param array<PhpParser\Node\Arg> $args
- * @param array<string> $removed_taints
- * @param array<string> $added_taints
- */
- public static function taintUsingFlows(
- StatementsAnalyzer $statements_analyzer,
- FunctionLikeStorage $function_storage,
- TaintFlowGraph $graph,
- string $function_id,
- array $args,
- CodeLocation $node_location,
- DataFlowNode $function_call_node,
- array $removed_taints,
- array $added_taints = []
- ): void {
- foreach ($function_storage->return_source_params as $i => $path_type) {
- if (!isset($args[$i])) {
- continue;
- }
-
- $current_arg_is_variadic = $function_storage->params[$i]->is_variadic;
- $taintable_arg_index = [$i];
-
- if ($current_arg_is_variadic) {
- $max_params = count($args) - 1;
- for ($arg_index = $i + 1; $arg_index <= $max_params; $arg_index++) {
- $taintable_arg_index[] = $arg_index;
- }
- }
-
- foreach ($taintable_arg_index as $arg_index) {
- $arg_location = new CodeLocation(
- $statements_analyzer,
- $args[$arg_index]->value
- );
-
- $function_param_sink = DataFlowNode::getForMethodArgument(
- $function_id,
- $function_id,
- $arg_index,
- $arg_location,
- $function_storage->specialize_call ? $node_location : null
- );
-
- $graph->addNode($function_param_sink);
-
- $graph->addPath(
- $function_param_sink,
- $function_call_node,
- $path_type,
- array_merge($added_taints, $function_storage->added_taints),
- $removed_taints
- );
- }
- }
- }
-
- /**
- * @psalm-pure
- */
- private static function simpleExclusion(string $pattern, string $escape_char): bool
- {
- $str_length = strlen($pattern);
-
- for ($i = 0; $i < $str_length; $i++) {
- $current = $pattern[$i];
- $next = $pattern[$i + 1] ?? null;
-
- if ($current === '\\') {
- if ($next === null
- || $next === 'x'
- || $next === 'u'
- ) {
- return false;
- }
-
- if ($next === '.'
- || $next === '('
- || $next === ')'
- || $next === '['
- || $next === ']'
- || $next === 's'
- || $next === 'w'
- || $next === $escape_char
- ) {
- $i++;
- continue;
- }
-
- return false;
- }
-
- if ($next !== '-') {
- if ($current === '_'
- || $current === '-'
- || $current === '|'
- || $current === ':'
- || $current === '#'
- || $current === '.'
- || $current === ' '
- ) {
- continue;
- }
-
- return false;
- }
-
- if ($current === ']') {
- return false;
- }
-
- if (!isset($pattern[$i + 2])) {
- return false;
- }
-
- if (($current === 'a' && $pattern[$i + 2] === 'z')
- || ($current === 'a' && $pattern[$i + 2] === 'Z')
- || ($current === 'A' && $pattern[$i + 2] === 'Z')
- || ($current === '0' && $pattern[$i + 2] === '9')
- ) {
- $i += 2;
- continue;
- }
-
- return false;
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php
deleted file mode 100644
index fe0c730..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicCallContext.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use PhpParser;
-use Psalm\Internal\MethodIdentifier;
-
-class AtomicCallContext
-{
- /** @var MethodIdentifier */
- public $method_id;
-
- /** @var list<PhpParser\Node\Arg> */
- public $args;
-
- /** @param list<PhpParser\Node\Arg> $args */
- public function __construct(MethodIdentifier $method_id, array $args)
- {
- $this->method_id = $method_id;
- $this->args = $args;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php
deleted file mode 100644
index e4d5d5e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalysisResult.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Type\Union;
-
-class AtomicMethodCallAnalysisResult
-{
- /**
- * @var ?Union
- */
- public $return_type;
-
- /**
- * @var bool
- */
- public $returns_by_ref = false;
-
- /**
- * @var bool
- */
- public $has_mock = false;
-
- /**
- * @var bool
- */
- public $has_valid_method_call_type = false;
-
- /**
- * @var bool
- */
- public $has_mixed_method_call = false;
-
- /**
- * @var array<string>
- */
- public $invalid_method_call_types = [];
-
- /**
- * @var array<string>
- */
- public $existent_method_ids = [];
-
- /**
- * @var array<string>
- */
- public $non_existent_class_method_ids = [];
-
- /**
- * @var array<string>
- */
- public $non_existent_interface_method_ids = [];
-
- /**
- * @var array<string>
- */
- public $non_existent_magic_method_ids = [];
-
- /**
- * @var bool
- */
- public $check_visibility = true;
-
- /**
- * @var bool
- */
- public $too_many_arguments = true;
-
- /**
- * @var list<MethodIdentifier>
- */
- public $too_many_arguments_method_ids = [];
-
- /**
- * @var bool
- */
- public $too_few_arguments = false;
-
- /**
- * @var list<MethodIdentifier>
- */
- public $too_few_arguments_method_ids = [];
-
- /**
- * @var bool
- */
- public $can_memoize = false;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php
deleted file mode 100644
index 6183fe7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php
+++ /dev/null
@@ -1,890 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\MethodAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\MixedMethodCall;
-use Psalm\IssueBuffer;
-use Psalm\StatementsSource;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TEmptyMixed;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyMixed;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_keys;
-use function array_merge;
-use function array_search;
-use function array_shift;
-use function array_values;
-use function count;
-use function get_class;
-use function reset;
-use function strtolower;
-
-/**
- * This is a bunch of complex logic to handle the potential for missing methods,
- * use of intersection types and/or mixins, together with handling for fallback magic
- * methods.
- *
- * The happy path (i.e 99% of method calls) is handled in ExistingAtomicMethodCallAnalyzer
- */
-class AtomicMethodCallAnalyzer extends CallAnalyzer
-{
- /**
- * @param TNamedObject|TTemplateParam|null $static_type
- *
- * @psalm-suppress ComplexMethod it's really complex, but unavoidably so
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\MethodCall $stmt,
- Codebase $codebase,
- Context $context,
- Union $lhs_type,
- Atomic $lhs_type_part,
- ?Atomic $static_type,
- bool $is_intersection,
- ?string $lhs_var_id,
- AtomicMethodCallAnalysisResult $result
- ): void {
- if ($lhs_type_part instanceof TTemplateParam
- && !$lhs_type_part->as->isMixed()
- ) {
- $extra_types = $lhs_type_part->extra_types;
-
- $lhs_type_part = array_values(
- $lhs_type_part->as->getAtomicTypes()
- )[0];
-
- $lhs_type_part->from_docblock = true;
-
- if ($lhs_type_part instanceof TNamedObject) {
- $lhs_type_part->extra_types = $extra_types;
- } elseif ($lhs_type_part instanceof TObject && $extra_types) {
- $lhs_type_part = array_shift($extra_types);
- if ($extra_types) {
- $lhs_type_part->extra_types = $extra_types;
- }
- }
-
- $result->has_mixed_method_call = true;
- }
-
- $source = $statements_analyzer->getSource();
-
- if (!$lhs_type_part instanceof TNamedObject) {
- self::handleInvalidClass(
- $statements_analyzer,
- $codebase,
- $stmt,
- $lhs_type,
- $lhs_type_part,
- $lhs_var_id,
- $context,
- $is_intersection,
- $result
- );
-
- return;
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- $result->has_valid_method_call_type = true;
-
- $fq_class_name = $lhs_type_part->value;
-
- $is_mock = ExpressionAnalyzer::isMock($fq_class_name);
-
- $result->has_mock = $result->has_mock || $is_mock;
-
- if ($fq_class_name === 'static') {
- $fq_class_name = (string) $context->self;
- }
-
- if ($is_mock ||
- $context->isPhantomClass($fq_class_name)
- ) {
- $result->return_type = Type::getMixed();
-
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- return;
- }
-
- if ($lhs_var_id === '$this') {
- $does_class_exist = true;
- } else {
- $does_class_exist = ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($source, $stmt->var),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true, false, true, true, $lhs_type_part->from_docblock)
- );
- }
-
- if (!$does_class_exist) {
- return;
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $result->check_visibility = $result->check_visibility && !$class_storage->override_method_visibility;
-
- $intersection_types = $lhs_type_part->getIntersectionTypes();
-
- if (!$stmt->name instanceof PhpParser\Node\Identifier) {
- if (!$context->ignore_variable_method) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($fq_class_name) . '::',
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- if ($stmt->isFirstClassCallable()) {
- $return_type_candidate = null;
- $method_name_type = $statements_analyzer->node_data->getType($stmt->name);
- if ($method_name_type && $method_name_type->isSingleStringLiteral()) {
- $method_identifier = new MethodIdentifier(
- $fq_class_name,
- strtolower($method_name_type->getSingleStringLiteral()->value)
- );
- //the call to methodExists will register that the method was called from somewhere
- if ($codebase->methods->methodExists(
- $method_identifier,
- $context->calling_method_id,
- null,
- $statements_analyzer,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )) {
- $method_storage = $codebase->methods->getStorage($method_identifier);
-
- $return_type_candidate = new Union([new TClosure(
- 'Closure',
- $method_storage->params,
- $method_storage->return_type,
- $method_storage->pure
- )]);
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, $return_type_candidate ?? Type::getClosure());
-
-
- return;
- }
-
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- $result->return_type = Type::getMixed();
- return;
- }
-
- $method_name_lc = strtolower($stmt->name->name);
-
- $method_id = new MethodIdentifier($fq_class_name, $method_name_lc);
-
- $args = $stmt->isFirstClassCallable() ? [] : $stmt->getArgs();
-
- $naive_method_id = $method_id;
-
- // this tells us whether or not we can stay on the happy path
- $naive_method_exists = $codebase->methods->methodExists(
- $method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($source, $stmt->name)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath(),
- false,
- $context->insideUse()
- );
-
- $fake_method_exists = false;
-
- if (!$naive_method_exists) {
- // if the method doesn't exist we check for any method existence providers
- if ($codebase->methods->existence_provider->has($fq_class_name)) {
- $method_exists = $codebase->methods->existence_provider->doesMethodExist(
- $fq_class_name,
- $method_id->method_name,
- $source,
- null
- );
-
- if ($method_exists) {
- $fake_method_exists = true;
- }
- }
-
- $naive_method_exists = false;
-
- // @mixin attributes are an absolute pain! Lots of complexity here,
- // as they can redefine the called class, method id etc.
- if ($class_storage->templatedMixins
- && $lhs_type_part instanceof TGenericObject
- && $class_storage->template_types
- ) {
- [$lhs_type_part, $class_storage, $naive_method_exists, $method_id, $fq_class_name]
- = self::handleTemplatedMixins(
- $class_storage,
- $lhs_type_part,
- $method_name_lc,
- $codebase,
- $context,
- $method_id,
- $source,
- $stmt,
- $statements_analyzer,
- $fq_class_name
- );
- } elseif ($class_storage->mixin_declaring_fqcln
- && $class_storage->namedMixins
- ) {
- [$lhs_type_part, $class_storage, $naive_method_exists, $method_id, $fq_class_name]
- = self::handleRegularMixins(
- $class_storage,
- $lhs_type_part,
- $method_name_lc,
- $codebase,
- $context,
- $method_id,
- $source,
- $stmt,
- $statements_analyzer,
- $fq_class_name,
- $lhs_var_id
- );
- }
- }
-
- $all_intersection_return_type = null;
- $all_intersection_existent_method_ids = [];
-
- // insersection types are also fun, they also complicate matters
- if ($intersection_types) {
- [$all_intersection_return_type, $all_intersection_existent_method_ids]
- = self::getIntersectionReturnType(
- $statements_analyzer,
- $stmt,
- $codebase,
- $context,
- $lhs_type,
- $lhs_type_part,
- $lhs_var_id,
- $result,
- $intersection_types
- );
- }
-
- if (($fake_method_exists
- && $codebase->methods->methodExists(new MethodIdentifier($fq_class_name, '__call')))
- || !$naive_method_exists
- || !MethodAnalyzer::isMethodVisible(
- $method_id,
- $context,
- $statements_analyzer->getSource()
- )
- ) {
- $interface_has_method = false;
-
- if ($class_storage->abstract && $class_storage->class_implements) {
- foreach ($class_storage->class_implements as $interface_fqcln_lc => $_) {
- $interface_storage = $codebase->classlike_storage_provider->get($interface_fqcln_lc);
-
- if (isset($interface_storage->methods[$method_name_lc])) {
- $interface_has_method = true;
- $fq_class_name = $interface_storage->name;
- $method_id = new MethodIdentifier(
- $fq_class_name,
- $method_name_lc
- );
- break;
- }
- }
- }
-
- if (!$interface_has_method
- && $codebase->methods->methodExists(
- new MethodIdentifier($fq_class_name, '__call'),
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($source, $stmt->name)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )
- ) {
- $new_call_context = MissingMethodCallHandler::handleMagicMethod(
- $statements_analyzer,
- $codebase,
- $stmt,
- $method_id,
- $class_storage,
- $context,
- $codebase->config,
- $all_intersection_return_type,
- $result,
- $lhs_type_part
- );
-
- if ($new_call_context) {
- if ($method_id === $new_call_context->method_id) {
- return;
- }
-
- $method_id = $new_call_context->method_id;
- $args = $new_call_context->args;
- } else {
- return;
- }
- }
- }
-
- $intersection_method_id = $intersection_types
- ? '(' . $lhs_type_part . ')' . '::' . $stmt->name->name
- : null;
- $cased_method_id = $fq_class_name . '::' . $stmt->name->name;
-
- if ($lhs_var_id === '$this'
- && $context->self
- && $fq_class_name !== $context->self
- && $codebase->methods->methodExists(
- new MethodIdentifier($context->self, $method_name_lc)
- )
- ) {
- $method_id = new MethodIdentifier($context->self, $method_name_lc);
- $cased_method_id = $context->self . '::' . $stmt->name->name;
- $fq_class_name = $context->self;
- }
-
- $source_method_id = $source instanceof FunctionLikeAnalyzer
- ? $source->getId()
- : null;
-
- $corrected_method_exists = ($naive_method_exists && $method_id === $naive_method_id)
- || ($method_id !== $naive_method_id
- && $codebase->methods->methodExists(
- $method_id,
- $context->calling_method_id,
- $codebase->collect_locations && $method_id !== $source_method_id
- ? new CodeLocation($source, $stmt->name)
- : null
- ));
-
- if (!$corrected_method_exists
- || ($codebase->config->use_phpdoc_method_without_magic_or_parent
- && isset($class_storage->pseudo_methods[$method_name_lc]))
- ) {
- MissingMethodCallHandler::handleMissingOrMagicMethod(
- $statements_analyzer,
- $codebase,
- $stmt,
- $method_id,
- $codebase->interfaceExists($fq_class_name),
- $context,
- $codebase->config,
- $all_intersection_return_type,
- $all_intersection_existent_method_ids,
- $intersection_method_id,
- $cased_method_id,
- $result,
- $lhs_type_part
- );
-
- return;
- }
-
- $old_node_data = $statements_analyzer->node_data;
-
- $return_type_candidate = ExistingAtomicMethodCallAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $stmt->name,
- $args,
- $codebase,
- $context,
- $lhs_type_part,
- $static_type,
- $lhs_var_id,
- $method_id,
- $result
- );
-
- $statements_analyzer->node_data = $old_node_data;
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- $in_call_map = InternalCallMapHandler::inCallMap((string) ($declaring_method_id ?? $method_id));
-
- if (!$in_call_map) {
- if ($result->check_visibility) {
- $name_code_location = new CodeLocation($statements_analyzer, $stmt->name);
-
- MethodVisibilityAnalyzer::analyze(
- $method_id,
- $context,
- $statements_analyzer->getSource(),
- $name_code_location,
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- self::updateResultReturnType(
- $result,
- $return_type_candidate,
- $all_intersection_return_type,
- $codebase
- );
- }
-
- /**
- * @param TNamedObject|TTemplateParam $lhs_type_part
- * @param array<string, Atomic> $intersection_types
- *
- * @return array{?Union, array<string>}
- */
- private static function getIntersectionReturnType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\MethodCall $stmt,
- Codebase $codebase,
- Context $context,
- Union $lhs_type,
- Atomic $lhs_type_part,
- ?string $lhs_var_id,
- AtomicMethodCallAnalysisResult $result,
- array $intersection_types
- ): array {
- $all_intersection_return_type = null;
- $all_intersection_existent_method_ids = [];
-
- foreach ($intersection_types as $intersection_type) {
- $intersection_result = clone $result;
-
- /** @var ?Union $intersection_result->return_type */
- $intersection_result->return_type = null;
-
- self::analyze(
- $statements_analyzer,
- $stmt,
- $codebase,
- $context,
- $lhs_type,
- $intersection_type,
- $lhs_type_part,
- true,
- $lhs_var_id,
- $intersection_result
- );
-
- $result->returns_by_ref = $intersection_result->returns_by_ref;
- $result->has_mock = $intersection_result->has_mock;
- $result->has_valid_method_call_type = $intersection_result->has_valid_method_call_type;
- $result->has_mixed_method_call = $intersection_result->has_mixed_method_call;
- $result->invalid_method_call_types = $intersection_result->invalid_method_call_types;
- $result->check_visibility = $intersection_result->check_visibility;
- $result->too_many_arguments = $intersection_result->too_many_arguments;
-
- $all_intersection_existent_method_ids = array_merge(
- $all_intersection_existent_method_ids,
- $intersection_result->existent_method_ids
- );
-
- if ($intersection_result->return_type) {
- if (!$all_intersection_return_type || $all_intersection_return_type->isMixed()) {
- $all_intersection_return_type = $intersection_result->return_type;
- } else {
- $all_intersection_return_type = Type::intersectUnionTypes(
- $all_intersection_return_type,
- $intersection_result->return_type,
- $codebase
- ) ?? Type::getMixed();
- }
- }
- }
-
- return [$all_intersection_return_type, $all_intersection_existent_method_ids];
- }
-
- private static function updateResultReturnType(
- AtomicMethodCallAnalysisResult $result,
- Union $return_type_candidate,
- ?Union $all_intersection_return_type,
- Codebase $codebase
- ): void {
- if ($all_intersection_return_type) {
- $return_type_candidate = Type::intersectUnionTypes(
- $all_intersection_return_type,
- $return_type_candidate,
- $codebase
- ) ?? Type::getMixed();
- }
-
- $result->return_type = Type::combineUnionTypes($return_type_candidate, $result->return_type);
- }
-
- private static function handleInvalidClass(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\MethodCall $stmt,
- Union $lhs_type,
- Atomic $lhs_type_part,
- ?string $lhs_var_id,
- Context $context,
- bool $is_intersection,
- AtomicMethodCallAnalysisResult $result
- ): void {
- switch (get_class($lhs_type_part)) {
- case TNull::class:
- case TFalse::class:
- // handled above
- return;
-
- case TTemplateParam::class:
- case TEmptyMixed::class:
- case TEmpty::class:
- case TMixed::class:
- case TNonEmptyMixed::class:
- case TObject::class:
- case TObjectWithProperties::class:
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- $result->has_mixed_method_call = true;
-
- if ($lhs_type_part instanceof TObjectWithProperties
- && $stmt->name instanceof PhpParser\Node\Identifier
- && isset($lhs_type_part->methods[$stmt->name->name])
- ) {
- $result->existent_method_ids[] = $lhs_type_part->methods[$stmt->name->name];
- } elseif (!$is_intersection) {
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($stmt->name->name),
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- if ($context->check_methods) {
- $message = 'Cannot determine the type of the object'
- . ' on the left hand side of this expression';
-
- if ($lhs_var_id) {
- $message = 'Cannot determine the type of ' . $lhs_var_id;
-
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- $message .= ' when calling method ' . $stmt->name->name;
- }
- }
-
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($lhs_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- $name_code_location = new CodeLocation($statements_analyzer, $stmt->name);
-
- if ($origin_location && $origin_location->getHash() === $name_code_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedMethodCall(
- $message,
- $name_code_location,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return;
- }
-
- $result->return_type = Type::getMixed();
- return;
-
- default:
- $result->invalid_method_call_types[] = (string)$lhs_type_part;
- return;
- }
- }
-
- /**
- * @param lowercase-string $method_name_lc
- * @return array{TNamedObject, ClassLikeStorage, bool, MethodIdentifier, string}
- */
- private static function handleTemplatedMixins(
- ClassLikeStorage $class_storage,
- TNamedObject $lhs_type_part,
- string $method_name_lc,
- Codebase $codebase,
- Context $context,
- MethodIdentifier $method_id,
- StatementsSource $source,
- PhpParser\Node\Expr\MethodCall $stmt,
- StatementsAnalyzer $statements_analyzer,
- string $fq_class_name
- ): array {
- $naive_method_exists = false;
-
- if ($class_storage->templatedMixins
- && $lhs_type_part instanceof TGenericObject
- && $class_storage->template_types
- ) {
- $template_type_keys = array_keys($class_storage->template_types);
-
- foreach ($class_storage->templatedMixins as $mixin) {
- $param_position = array_search(
- $mixin->param_name,
- $template_type_keys
- );
-
- if ($param_position !== false
- && isset($lhs_type_part->type_params[$param_position])
- ) {
- $current_type_param = $lhs_type_part->type_params[$param_position];
- if ($current_type_param->isSingle()) {
- $lhs_type_part_new = array_values(
- $current_type_param->getAtomicTypes()
- )[0];
-
- if ($lhs_type_part_new instanceof TNamedObject) {
- $new_method_id = new MethodIdentifier(
- $lhs_type_part_new->value,
- $method_name_lc
- );
-
- $mixin_class_storage = $codebase->classlike_storage_provider->get(
- $lhs_type_part_new->value
- );
-
- if ($codebase->methods->methodExists(
- $new_method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($source, $stmt->name)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )) {
- $lhs_type_part = clone $lhs_type_part_new;
- $class_storage = $mixin_class_storage;
-
- $naive_method_exists = true;
- $method_id = $new_method_id;
- } elseif (isset($mixin_class_storage->pseudo_methods[$method_name_lc])) {
- $lhs_type_part = clone $lhs_type_part_new;
- $class_storage = $mixin_class_storage;
- $method_id = $new_method_id;
- }
- }
- }
- }
- }
- }
-
- return [
- $lhs_type_part,
- $class_storage,
- $naive_method_exists,
- $method_id,
- $fq_class_name
- ];
- }
-
- /**
- * @param lowercase-string $method_name_lc
- * @return array{TNamedObject, ClassLikeStorage, bool, MethodIdentifier, string}
- */
- private static function handleRegularMixins(
- ClassLikeStorage $class_storage,
- TNamedObject $lhs_type_part,
- string $method_name_lc,
- Codebase $codebase,
- Context $context,
- MethodIdentifier $method_id,
- StatementsSource $source,
- PhpParser\Node\Expr\MethodCall $stmt,
- StatementsAnalyzer $statements_analyzer,
- string $fq_class_name,
- ?string $lhs_var_id
- ): array {
- $naive_method_exists = false;
-
- foreach ($class_storage->namedMixins as $mixin) {
- if (!$class_storage->mixin_declaring_fqcln) {
- continue;
- }
-
- $new_method_id = new MethodIdentifier(
- $mixin->value,
- $method_name_lc
- );
-
- if ($codebase->methods->methodExists(
- $new_method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($source, $stmt->name)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )) {
- $mixin_declaring_class_storage = $codebase->classlike_storage_provider->get(
- $class_storage->mixin_declaring_fqcln
- );
-
- $mixin_class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $mixin_declaring_class_storage,
- $codebase->classlike_storage_provider->get($fq_class_name),
- null,
- $lhs_type_part,
- $lhs_var_id === '$this'
- );
-
- $lhs_type_part = clone $mixin;
-
- $lhs_type_part->replaceTemplateTypesWithArgTypes(
- new TemplateResult([], $mixin_class_template_params ?: []),
- $codebase
- );
-
- $lhs_type_expanded = TypeExpander::expandUnion(
- $codebase,
- new Union([$lhs_type_part]),
- $mixin_declaring_class_storage->name,
- $fq_class_name,
- $class_storage->parent_class,
- true,
- false,
- $class_storage->final
- );
-
- $new_lhs_type_part = $lhs_type_expanded->getSingleAtomic();
-
- if ($new_lhs_type_part instanceof TNamedObject) {
- $lhs_type_part = $new_lhs_type_part;
- }
-
- $mixin_class_storage = $codebase->classlike_storage_provider->get($mixin->value);
-
- $fq_class_name = $mixin_class_storage->name;
- $mixin_class_storage->mixin_declaring_fqcln = $class_storage->mixin_declaring_fqcln;
- $class_storage = $mixin_class_storage;
- $naive_method_exists = true;
- $method_id = $new_method_id;
- }
- }
-
- return [
- $lhs_type_part,
- $class_storage,
- $naive_method_exists,
- $method_id,
- $fq_class_name
- ];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php
deleted file mode 100644
index 6dee09a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php
+++ /dev/null
@@ -1,673 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentMapPopulator;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\IfThisIsMismatch;
-use Psalm\Issue\InvalidPropertyAssignmentValue;
-use Psalm\Issue\MixedPropertyTypeCoercion;
-use Psalm\Issue\PossiblyInvalidPropertyAssignmentValue;
-use Psalm\Issue\PropertyTypeCoercion;
-use Psalm\Issue\UndefinedThisPropertyAssignment;
-use Psalm\Issue\UndefinedThisPropertyFetch;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualFuncCall;
-use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
-use Psalm\Storage\Assertion;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_map;
-use function count;
-use function explode;
-use function in_array;
-use function strtolower;
-
-class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer
-{
- /**
- * @param TNamedObject|TTemplateParam|null $static_type
- * @param list<PhpParser\Node\Arg> $args
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\MethodCall $stmt,
- PhpParser\Node\Identifier $stmt_name,
- array $args,
- Codebase $codebase,
- Context $context,
- TNamedObject $lhs_type_part,
- ?Atomic $static_type,
- ?string $lhs_var_id,
- MethodIdentifier $method_id,
- AtomicMethodCallAnalysisResult $result
- ): Union {
- $config = $codebase->config;
-
- $fq_class_name = $lhs_type_part->value;
-
- if ($fq_class_name === 'static') {
- $fq_class_name = (string) $context->self;
- }
-
- $method_name_lc = $method_id->method_name;
-
- $cased_method_id = $fq_class_name . '::' . $stmt_name->name;
-
- $result->existent_method_ids[] = $method_id->__toString();
-
- if ($context->collect_initializations && $context->calling_method_id) {
- [$calling_method_class] = explode('::', $context->calling_method_id);
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $calling_method_class . '::__construct',
- strtolower((string) $method_id),
- false
- );
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- ArgumentMapPopulator::recordArgumentPositions(
- $statements_analyzer,
- $stmt,
- $codebase,
- (string) $method_id
- );
- }
-
- if ($fq_class_name === 'Closure' && $method_name_lc === '__invoke') {
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_function_call = new VirtualFuncCall(
- $stmt->var,
- $args,
- $stmt->getAttributes()
- );
-
- FunctionCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_function_call,
- $context
- );
-
- return $statements_analyzer->node_data->getType($fake_function_call) ?? Type::getMixed();
- }
-
- $source = $statements_analyzer->getSource();
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt_name,
- $method_id . '()'
- );
- }
-
- if ($context->collect_initializations && $context->calling_method_id) {
- [$calling_method_class] = explode('::', $context->calling_method_id);
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $calling_method_class . '::__construct',
- strtolower((string) $method_id),
- false
- );
- }
-
- if ($stmt->var instanceof PhpParser\Node\Expr\Variable
- && ($context->collect_initializations || $context->collect_mutations)
- && $stmt->var->name === 'this'
- && $source instanceof FunctionLikeAnalyzer
- ) {
- self::collectSpecialInformation($source, $stmt_name->name, $context);
- }
-
- $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $parent_source = $statements_analyzer->getSource();
-
- $class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $codebase->methods->getClassLikeStorageForMethod($method_id),
- $class_storage,
- $method_name_lc,
- $lhs_type_part,
- $lhs_var_id === '$this'
- );
-
- if ($lhs_var_id === '$this' && $parent_source instanceof FunctionLikeAnalyzer) {
- $grandparent_source = $parent_source->getSource();
-
- if ($grandparent_source instanceof TraitAnalyzer) {
- $fq_trait_name = $grandparent_source->getFQCLN();
-
- $fq_trait_name_lc = strtolower($fq_trait_name);
-
- $trait_storage = $codebase->classlike_storage_provider->get($fq_trait_name_lc);
-
- if (isset($trait_storage->methods[$method_name_lc])) {
- $trait_method_id = new MethodIdentifier($trait_storage->name, $method_name_lc);
-
- $class_template_params = ClassTemplateParamCollector::collect(
- $codebase,
- $codebase->methods->getClassLikeStorageForMethod($trait_method_id),
- $class_storage,
- $method_name_lc,
- $lhs_type_part,
- true
- );
- }
- }
- }
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- try {
- $method_storage = $codebase->methods->getStorage($declaring_method_id ?? $method_id);
- } catch (UnexpectedValueException $e) {
- $method_storage = null;
- }
-
- $method_template_params = [];
-
- if ($method_storage && $method_storage->if_this_is_type) {
- $method_template_result = new TemplateResult($method_storage->template_types ?: [], []);
-
- TemplateStandinTypeReplacer::replace(
- clone $method_storage->if_this_is_type,
- $method_template_result,
- $codebase,
- null,
- new Union([$lhs_type_part])
- );
-
- $method_template_params = $method_template_result->lower_bounds;
- }
-
- $template_result = new TemplateResult([], $class_template_params ?: []);
- $template_result->lower_bounds += $method_template_params;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- ArgumentMapPopulator::recordArgumentPositions(
- $statements_analyzer,
- $stmt,
- $codebase,
- (string) $method_id
- );
- }
-
- $is_first_class_callable = $stmt->isFirstClassCallable();
-
- if (!$is_first_class_callable && self::checkMethodArgs(
- $method_id,
- $args,
- $template_result,
- $context,
- new CodeLocation($source, $stmt_name),
- $statements_analyzer
- ) === false) {
- return Type::getMixed();
- }
-
- $return_type_candidate = MethodCallReturnTypeFetcher::fetch(
- $statements_analyzer,
- $codebase,
- $stmt,
- $context,
- $method_id,
- $declaring_method_id,
- $method_id,
- $cased_method_id,
- $lhs_type_part,
- $static_type,
- $args,
- $result,
- $template_result
- );
-
- if ($is_first_class_callable) {
- return $return_type_candidate;
- }
-
- $in_call_map = InternalCallMapHandler::inCallMap((string) ($declaring_method_id ?? $method_id));
-
- if (!$in_call_map) {
- $name_code_location = new CodeLocation($statements_analyzer, $stmt_name);
-
- MethodCallProhibitionAnalyzer::analyze(
- $codebase,
- $context,
- $method_id,
- $statements_analyzer->getFullyQualifiedFunctionMethodOrNamespaceName(),
- $name_code_location,
- $statements_analyzer->getSuppressedIssues()
- );
-
- $getter_return_type = self::getMagicGetterOrSetterProperty(
- $statements_analyzer,
- $stmt,
- $stmt_name,
- $context,
- $fq_class_name
- );
-
- if ($getter_return_type) {
- $return_type_candidate = $getter_return_type;
- }
- }
-
- if ($method_storage) {
- if ($method_storage->if_this_is_type) {
- $class_type = new Union([$lhs_type_part]);
- $if_this_is_type = clone $method_storage->if_this_is_type;
-
- TemplateInferredTypeReplacer::replace($if_this_is_type, $template_result, $codebase);
-
- if (!UnionTypeComparator::isContainedBy($codebase, $class_type, $if_this_is_type)) {
- IssueBuffer::maybeAdd(
- new IfThisIsMismatch(
- 'Class type must be ' . $method_storage->if_this_is_type->getId()
- . ' current type ' . $class_type->getId(),
- new CodeLocation($source, $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($method_storage->self_out_type && $lhs_var_id) {
- $self_out_candidate = clone $method_storage->self_out_type;
-
- if ($template_result->lower_bounds) {
- $self_out_candidate = TypeExpander::expandUnion(
- $codebase,
- $self_out_candidate,
- $fq_class_name,
- null,
- $class_storage->parent_class,
- true,
- false,
- $static_type instanceof TNamedObject
- && $codebase->classlike_storage_provider->get($static_type->value)->final,
- true
- );
- }
-
- $self_out_candidate = MethodCallReturnTypeFetcher::replaceTemplateTypes(
- $self_out_candidate,
- $template_result,
- $method_id,
- count($args),
- $codebase
- );
-
- $self_out_candidate = TypeExpander::expandUnion(
- $codebase,
- $self_out_candidate,
- $fq_class_name,
- $static_type,
- $class_storage->parent_class,
- true,
- false,
- $static_type instanceof TNamedObject
- && $codebase->classlike_storage_provider->get($static_type->value)->final,
- true
- );
-
- $context->vars_in_scope[$lhs_var_id] = $self_out_candidate;
- }
-
- if (!$context->collect_mutations && !$context->collect_initializations) {
- MethodCallPurityAnalyzer::analyze(
- $statements_analyzer,
- $codebase,
- $stmt,
- $lhs_var_id,
- $cased_method_id,
- $method_id,
- $method_storage,
- $class_storage,
- $context,
- $config,
- $result
- );
- }
-
- $has_packed_arg = false;
- foreach ($args as $arg) {
- $has_packed_arg = $has_packed_arg || $arg->unpack;
- }
-
- if (!$has_packed_arg) {
- $has_variadic_param = $method_storage->variadic;
-
- foreach ($method_storage->params as $param) {
- $has_variadic_param = $has_variadic_param || $param->is_variadic;
- }
-
- for ($i = count($args), $j = count($method_storage->params); $i < $j; ++$i) {
- $param = $method_storage->params[$i];
-
- if (!$param->is_optional
- && !$param->is_variadic
- && !$in_call_map
- ) {
- $result->too_few_arguments = true;
- $result->too_few_arguments_method_ids[] = $declaring_method_id ?? $method_id;
- }
- }
-
- if ($has_variadic_param || count($method_storage->params) >= count($args) || $in_call_map) {
- $result->too_many_arguments = false;
- } else {
- $result->too_many_arguments_method_ids[] = $declaring_method_id ?? $method_id;
- }
- }
-
- $class_template_params = $template_result->lower_bounds;
-
- if ($method_storage->assertions) {
- self::applyAssertionsToContext(
- $stmt_name,
- ExpressionIdentifier::getArrayVarId($stmt->var, null, $statements_analyzer),
- $method_storage->assertions,
- $args,
- $class_template_params,
- $context,
- $statements_analyzer
- );
- }
-
- if ($method_storage->if_true_assertions) {
- $statements_analyzer->node_data->setIfTrueAssertions(
- $stmt,
- array_map(
- function (Assertion $assertion) use (
- $class_template_params,
- $lhs_var_id,
- $codebase
- ): Assertion {
- return $assertion->getUntemplatedCopy(
- $class_template_params ?: [],
- $lhs_var_id,
- $codebase
- );
- },
- $method_storage->if_true_assertions
- )
- );
- }
-
- if ($method_storage->if_false_assertions) {
- $statements_analyzer->node_data->setIfFalseAssertions(
- $stmt,
- array_map(
- function (Assertion $assertion) use (
- $class_template_params,
- $lhs_var_id,
- $codebase
- ): Assertion {
- return $assertion->getUntemplatedCopy(
- $class_template_params ?: [],
- $lhs_var_id,
- $codebase
- );
- },
- $method_storage->if_false_assertions
- )
- );
- }
- }
-
- if ($codebase->methods_to_rename) {
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- foreach ($codebase->methods_to_rename as $original_method_id => $new_method_name) {
- if ($declaring_method_id && (strtolower((string) $declaring_method_id)) === $original_method_id) {
- $file_manipulations = [
- new FileManipulation(
- (int) $stmt_name->getAttribute('startFilePos'),
- (int) $stmt_name->getAttribute('endFilePos') + 1,
- $new_method_name
- )
- ];
-
- FileManipulationBuffer::add(
- $statements_analyzer->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
-
- if ($config->eventDispatcher->hasAfterMethodCallAnalysisHandlers()) {
- $file_manipulations = [];
-
- $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($appearing_method_id !== null && $declaring_method_id !== null) {
- $event = new AfterMethodCallAnalysisEvent(
- $stmt,
- (string) $method_id,
- (string) $appearing_method_id,
- (string) $declaring_method_id,
- $context,
- $statements_analyzer,
- $codebase,
- $file_manipulations,
- $return_type_candidate
- );
-
- $config->eventDispatcher->dispatchAfterMethodCallAnalysis($event);
- $file_manipulations = $event->getFileReplacements();
- $return_type_candidate = $event->getReturnTypeCandidate();
- }
-
- if ($file_manipulations) {
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
- }
-
- return $return_type_candidate ?? Type::getMixed();
- }
-
- /**
- * Check properties accessed with magic getters and setters.
- * If `@psalm-seal-properties` is set, they must be defined.
- * If an `@property` annotation is specified, the setter must set something with the correct
- * type.
- */
- private static function getMagicGetterOrSetterProperty(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\MethodCall $stmt,
- PhpParser\Node\Identifier $stmt_name,
- Context $context,
- string $fq_class_name
- ): ?Union {
- $method_name = strtolower($stmt_name->name);
- if (!in_array($method_name, ['__get', '__set'], true)) {
- return null;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $first_arg_value = $stmt->getArgs()[0]->value ?? null;
-
- if (!$first_arg_value instanceof PhpParser\Node\Scalar\String_) {
- return null;
- }
-
- $prop_name = $first_arg_value->value;
- $property_id = $fq_class_name . '::$' . $prop_name;
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $codebase->properties->propertyExists(
- $property_id,
- $method_name === '__get',
- $statements_analyzer,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
-
- switch ($method_name) {
- case '__set':
- // If `@psalm-seal-properties` is set, the property must be defined with
- // a `@property` annotation
- if (($class_storage->sealed_properties || $codebase->config->seal_all_properties)
- && !isset($class_storage->pseudo_property_set_types['$' . $prop_name])
- && IssueBuffer::accepts(
- new UndefinedThisPropertyAssignment(
- 'Instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // fall through
- }
-
- // If a `@property` annotation is set, the type of the value passed to the
- // magic setter must match the annotation.
- $second_arg_type = isset($stmt->getArgs()[1])
- ? $statements_analyzer->node_data->getType($stmt->getArgs()[1]->value)
- : null;
-
- if (isset($class_storage->pseudo_property_set_types['$' . $prop_name]) && $second_arg_type) {
- $pseudo_set_type = TypeExpander::expandUnion(
- $codebase,
- $class_storage->pseudo_property_set_types['$' . $prop_name],
- $fq_class_name,
- new TNamedObject($fq_class_name),
- $class_storage->parent_class
- );
-
- $union_comparison_results = new TypeComparisonResult();
-
- $type_match_found = UnionTypeComparator::isContainedBy(
- $codebase,
- $second_arg_type,
- $pseudo_set_type,
- $second_arg_type->ignore_nullable_issues,
- $second_arg_type->ignore_falsable_issues,
- $union_comparison_results
- );
-
- if ($union_comparison_results->type_coerced) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- IssueBuffer::maybeAdd(
- new MixedPropertyTypeCoercion(
- $prop_name . ' expects \'' . $pseudo_set_type->getId() . '\', '
- . ' parent type `' . $second_arg_type . '` provided',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new PropertyTypeCoercion(
- $prop_name . ' expects \'' . $pseudo_set_type->getId() . '\', '
- . ' parent type `' . $second_arg_type . '` provided',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (!$type_match_found && !$union_comparison_results->type_coerced_from_mixed) {
- if (UnionTypeComparator::canBeContainedBy(
- $codebase,
- $second_arg_type,
- $pseudo_set_type
- )) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidPropertyAssignmentValue(
- $prop_name . ' with declared type \''
- . $pseudo_set_type
- . '\' cannot be assigned possibly different type \'' . $second_arg_type . '\'',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidPropertyAssignmentValue(
- $prop_name . ' with declared type \''
- . $pseudo_set_type
- . '\' cannot be assigned type \'' . $second_arg_type . '\'',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- break;
-
- case '__get':
- // If `@psalm-seal-properties` is set, the property must be defined with
- // a `@property` annotation
- if (($class_storage->sealed_properties || $codebase->config->seal_all_properties)
- && !isset($class_storage->pseudo_property_get_types['$' . $prop_name])
- && IssueBuffer::accepts(
- new UndefinedThisPropertyFetch(
- 'Instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // fall through
- }
-
- if (isset($class_storage->pseudo_property_get_types['$' . $prop_name])) {
- return clone $class_storage->pseudo_property_get_types['$' . $prop_name];
- }
-
- break;
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php
deleted file mode 100644
index 29c8bf2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallProhibitionAnalyzer.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Issue\DeprecatedMethod;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\InternalMethod;
-use Psalm\IssueBuffer;
-
-class MethodCallProhibitionAnalyzer
-{
- /**
- * @param string[] $suppressed_issues
- *
- * @return false|null
- */
- public static function analyze(
- Codebase $codebase,
- Context $context,
- MethodIdentifier $method_id,
- ?string $caller_identifier,
- CodeLocation $code_location,
- array $suppressed_issues
- ): ?bool {
- $codebase_methods = $codebase->methods;
-
- $method_id = $codebase_methods->getDeclaringMethodId($method_id);
-
- if ($method_id === null) {
- return null;
- }
-
- $storage = $codebase_methods->getStorage($method_id);
-
- if ($storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedMethod(
- 'The method ' . $codebase_methods->getCasedMethodId($method_id) .
- ' has been marked as deprecated',
- $code_location,
- (string) $method_id
- ),
- $suppressed_issues
- );
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- ) {
- if (!NamespaceAnalyzer::isWithinAny($caller_identifier ?? "", $storage->internal)) {
- IssueBuffer::maybeAdd(
- new InternalMethod(
- 'The method ' . $codebase_methods->getCasedMethodId($method_id)
- . ' is internal to ' . InternalClass::listToPhrase($storage->internal)
- . ' but called from ' . ($caller_identifier ?: 'root namespace'),
- $code_location,
- (string) $method_id
- ),
- $suppressed_issues
- );
- }
- }
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php
deleted file mode 100644
index d0d49fd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallPurityAnalyzer.php
+++ /dev/null
@@ -1,173 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer as AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\Issue\UnusedMethodCall;
-use Psalm\IssueBuffer;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-
-class MethodCallPurityAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\MethodCall $stmt,
- ?string $lhs_var_id,
- string $cased_method_id,
- MethodIdentifier $method_id,
- MethodStorage $method_storage,
- ClassLikeStorage $class_storage,
- Context $context,
- Config $config,
- AtomicMethodCallAnalysisResult $result
- ): void {
- $method_pure_compatible = $method_storage->external_mutation_free
- && $statements_analyzer->node_data->isPureCompatible($stmt->var);
-
- if ($context->pure
- && !$method_storage->mutation_free
- && !$method_pure_compatible
- ) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a non-mutation-free method '
- . $cased_method_id . ' from a pure context',
- new CodeLocation($statements_analyzer, $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->mutation_free
- && !$method_storage->mutation_free
- && !$method_pure_compatible
- ) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method '
- . $cased_method_id . ' from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->external_mutation_free
- && !$method_storage->mutation_free
- && $method_id->fq_class_name !== $context->self
- && !$method_pure_compatible
- ) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method '
- . $cased_method_id . ' from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif (($method_storage->mutation_free
- || ($method_storage->external_mutation_free
- && ($stmt->var->getAttribute('external_mutation_free', false)
- || $stmt->var->getAttribute('pure', false))
- ))
- && !$context->inside_unset
- ) {
- if ($method_storage->mutation_free
- && (!$method_storage->mutation_free_inferred
- || $method_storage->final
- || $method_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE)
- && ($method_storage->immutable || $config->remember_property_assignments_after_call)
- ) {
- if ($context->inside_conditional
- && !$method_storage->assertions
- && !$method_storage->if_true_assertions
- ) {
- $stmt->setAttribute('memoizable', true);
-
- if ($method_storage->immutable) {
- $stmt->setAttribute('pure', true);
- }
- }
-
- $result->can_memoize = true;
- }
-
- if ($codebase->find_unused_variables
- && !$context->inside_conditional
- && !$context->inside_general_use
- && !$context->inside_throw
- ) {
- if (!$context->inside_assignment
- && !$context->inside_call
- && !$context->inside_return
- && !$method_storage->assertions
- && !$method_storage->if_true_assertions
- && !$method_storage->if_false_assertions
- && !$method_storage->throws
- ) {
- IssueBuffer::maybeAdd(
- new UnusedMethodCall(
- 'The call to ' . $cased_method_id . ' is not used',
- new CodeLocation($statements_analyzer, $stmt->name),
- (string) $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif (!$method_storage->mutation_free_inferred) {
- $stmt->setAttribute('pure', true);
- }
- }
- }
-
- if ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- && !$method_storage->mutation_free
- && !$method_pure_compatible
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
-
- if (!$config->remember_property_assignments_after_call
- && !$method_storage->mutation_free
- && !$method_pure_compatible
- ) {
- $context->removeMutableObjectVars();
- } elseif ($method_storage->this_property_mutations) {
- if (!$method_pure_compatible) {
- $context->removeMutableObjectVars(true);
- }
-
- foreach ($method_storage->this_property_mutations as $name => $_) {
- $mutation_var_id = $lhs_var_id . '->' . $name;
-
- $this_property_didnt_exist = $lhs_var_id === '$this'
- && isset($context->vars_in_scope[$mutation_var_id])
- && !isset($class_storage->declaring_property_ids[$name]);
-
- if ($this_property_didnt_exist) {
- $context->vars_in_scope[$mutation_var_id] = Type::getMixed();
- } else {
- $new_type = AssignmentAnalyzer::getExpandedPropertyType(
- $codebase,
- $class_storage->name,
- $name,
- $class_storage
- ) ?? Type::getMixed();
-
- $context->vars_in_scope[$mutation_var_id] = $new_type;
- $context->possibly_assigned_var_ids[$mutation_var_id] = true;
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php
deleted file mode 100644
index 76d3dfa..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodCallReturnTypeFetcher.php
+++ /dev/null
@@ -1,634 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use Exception;
-use PDOException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallReturnTypeFetcher;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\TaintSource;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TemplateBound;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use RuntimeException;
-use Throwable;
-use UnexpectedValueException;
-
-use function array_filter;
-use function count;
-use function in_array;
-use function strtolower;
-
-class MethodCallReturnTypeFetcher
-{
- /**
- * @param TNamedObject|TTemplateParam|null $static_type
- * @param list<PhpParser\Node\Arg> $args
- */
- public static function fetch(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\MethodCall $stmt,
- Context $context,
- MethodIdentifier $method_id,
- ?MethodIdentifier $declaring_method_id,
- MethodIdentifier $premixin_method_id,
- string $cased_method_id,
- Atomic $lhs_type_part,
- ?Atomic $static_type,
- array $args,
- AtomicMethodCallAnalysisResult $result,
- TemplateResult $template_result
- ): Union {
- $call_map_id = $declaring_method_id ?? $method_id;
-
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- $class_storage = $codebase->methods->getClassLikeStorageForMethod($method_id);
- $method_storage = ($class_storage->methods[$method_id->method_name] ?? null);
-
- if ($stmt->isFirstClassCallable()) {
- if ($method_storage) {
- return new Union([new TClosure(
- 'Closure',
- $method_storage->params,
- $method_storage->return_type,
- $method_storage->pure
- )]);
- }
-
- return Type::getClosure();
- }
-
- if ($codebase->methods->return_type_provider->has($premixin_method_id->fq_class_name)) {
- $return_type_candidate = $codebase->methods->return_type_provider->getReturnType(
- $statements_analyzer,
- $premixin_method_id->fq_class_name,
- $premixin_method_id->method_name,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt->name),
- $lhs_type_part instanceof TGenericObject ? $lhs_type_part->type_params : null
- );
-
- if ($return_type_candidate) {
- return $return_type_candidate;
- }
- }
-
- if ($premixin_method_id->method_name === 'getcode'
- && $premixin_method_id->fq_class_name !== Exception::class
- && $premixin_method_id->fq_class_name !== RuntimeException::class
- && $premixin_method_id->fq_class_name !== PDOException::class
- && (
- $codebase->classImplements($premixin_method_id->fq_class_name, Throwable::class)
- || $codebase->interfaceExtends($premixin_method_id->fq_class_name, Throwable::class)
- )
- ) {
- return Type::getInt(true); // TODO: Remove the flag in Psalm 5
- }
-
- if ($declaring_method_id && $declaring_method_id !== $method_id) {
- $declaring_fq_class_name = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
-
- if ($codebase->methods->return_type_provider->has($declaring_fq_class_name)) {
- $return_type_candidate = $codebase->methods->return_type_provider->getReturnType(
- $statements_analyzer,
- $declaring_fq_class_name,
- $declaring_method_name,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt->name),
- $lhs_type_part instanceof TGenericObject ? $lhs_type_part->type_params : null,
- $fq_class_name,
- $method_name
- );
-
- if ($return_type_candidate) {
- return $return_type_candidate;
- }
- }
- }
-
- if (InternalCallMapHandler::inCallMap((string) $call_map_id)) {
- if (($template_result->lower_bounds || $class_storage->stubbed)
- && ($method_storage = ($class_storage->methods[$method_id->method_name] ?? null))
- && $method_storage->return_type
- ) {
- $return_type_candidate = clone $method_storage->return_type;
-
- $return_type_candidate = self::replaceTemplateTypes(
- $return_type_candidate,
- $template_result,
- $method_id,
- count($stmt->getArgs()),
- $codebase
- );
- } else {
- $callmap_callables = InternalCallMapHandler::getCallablesFromCallMap((string) $call_map_id);
-
- if (!$callmap_callables || $callmap_callables[0]->return_type === null) {
- throw new UnexpectedValueException('Shouldn’t get here');
- }
-
- $return_type_candidate = $callmap_callables[0]->return_type;
- }
-
- if ($return_type_candidate->isFalsable()) {
- $return_type_candidate->ignore_falsable_issues = true;
- }
-
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- $fq_class_name,
- $static_type,
- $class_storage->parent_class,
- true,
- false,
- false,
- true
- );
- } else {
- $self_fq_class_name = $fq_class_name;
-
- $return_type_candidate = $codebase->methods->getMethodReturnType(
- $method_id,
- $self_fq_class_name,
- $statements_analyzer,
- $args
- );
-
- if ($return_type_candidate) {
- $return_type_candidate = clone $return_type_candidate;
-
- if ($template_result->lower_bounds) {
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- $fq_class_name,
- null,
- $class_storage->parent_class,
- true,
- false,
- $static_type instanceof TNamedObject
- && $codebase->classlike_storage_provider->get($static_type->value)->final,
- true
- );
- }
-
- $return_type_candidate = self::replaceTemplateTypes(
- $return_type_candidate,
- $template_result,
- $method_id,
- count($stmt->getArgs()),
- $codebase
- );
-
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- $self_fq_class_name,
- $static_type,
- $class_storage->parent_class,
- true,
- false,
- $static_type instanceof TNamedObject
- && $codebase->classlike_storage_provider->get($static_type->value)->final,
- true
- );
-
- $return_type_location = $codebase->methods->getMethodReturnTypeLocation(
- $method_id,
- $secondary_return_type_location
- );
-
- if ($secondary_return_type_location) {
- $return_type_location = $secondary_return_type_location;
- }
-
- $config = Config::getInstance();
-
- // only check the type locally if it's defined externally
- if ($return_type_location && !$config->isInProjectDirs($return_type_location->file_path)) {
- $return_type_candidate->check(
- $statements_analyzer,
- new CodeLocation($statements_analyzer, $stmt),
- $statements_analyzer->getSuppressedIssues(),
- $context->phantom_classes,
- true,
- false,
- false,
- $context->calling_method_id
- );
- }
- } else {
- $result->returns_by_ref =
- $result->returns_by_ref
- || $codebase->methods->getMethodReturnsByRef($method_id);
- }
- }
-
- if (!$return_type_candidate) {
- $return_type_candidate = $method_name === '__tostring' ? Type::getString() : Type::getMixed();
- }
-
- self::taintMethodCallResult(
- $statements_analyzer,
- $return_type_candidate,
- $stmt->name,
- $stmt->var,
- $args,
- $method_id,
- $declaring_method_id,
- $cased_method_id,
- $context
- );
-
- return $return_type_candidate;
- }
-
- /**
- * @param array<PhpParser\Node\Arg> $args
- */
- public static function taintMethodCallResult(
- StatementsAnalyzer $statements_analyzer,
- Union $return_type_candidate,
- PhpParser\Node $name_expr,
- PhpParser\Node\Expr $var_expr,
- array $args,
- MethodIdentifier $method_id,
- ?MethodIdentifier $declaring_method_id,
- string $cased_method_id,
- Context $context
- ): void {
- if (!$statements_analyzer->data_flow_graph
- || !$declaring_method_id
- ) {
- return;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- return;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $event = new AddRemoveTaintsEvent($var_expr, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- $method_storage = $codebase->methods->getStorage(
- $declaring_method_id
- );
-
- $node_location = new CodeLocation($statements_analyzer, $name_expr);
-
- $is_declaring = (string) $declaring_method_id === (string) $method_id;
-
- $var_id = ExpressionIdentifier::getArrayVarId(
- $var_expr,
- null,
- $statements_analyzer
- );
-
- if ($method_storage->specialize_call
- && $var_id
- && isset($context->vars_in_scope[$var_id])
- && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ) {
- $var_nodes = [];
-
- $parent_nodes = $context->vars_in_scope[$var_id]->parent_nodes;
-
- $unspecialized_parent_nodes = array_filter(
- $parent_nodes,
- function ($parent_node) {
- return !$parent_node->specialization_key;
- }
- );
-
- $specialized_parent_nodes = array_filter(
- $parent_nodes,
- function ($parent_node) {
- return (bool) $parent_node->specialization_key;
- }
- );
-
- $var_node = DataFlowNode::getForAssignment(
- $var_id,
- new CodeLocation($statements_analyzer, $var_expr)
- );
-
- if ($method_storage->location) {
- $this_parent_node = DataFlowNode::getForAssignment(
- '$this in ' . $method_id,
- $method_storage->location
- );
-
- foreach ($parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $this_parent_node,
- '=',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $var_nodes[$var_node->id] = $var_node;
-
- $method_call_nodes = [];
-
- if ($unspecialized_parent_nodes) {
- $method_call_node = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $is_declaring ? ($method_storage->signature_return_type_location
- ?: $method_storage->location) : null,
- $node_location
- );
-
- $method_call_nodes[$method_call_node->id] = $method_call_node;
- }
-
- foreach ($specialized_parent_nodes as $parent_node) {
- $universal_method_call_node = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $is_declaring ? ($method_storage->signature_return_type_location
- ?: $method_storage->location) : null,
- null
- );
-
- $method_call_node = new DataFlowNode(
- strtolower((string) $method_id),
- $cased_method_id,
- $is_declaring ? ($method_storage->signature_return_type_location
- ?: $method_storage->location) : null,
- $parent_node->specialization_key
- );
-
- $statements_analyzer->data_flow_graph->addPath(
- $universal_method_call_node,
- $method_call_node,
- '=',
- $added_taints,
- $removed_taints
- );
-
- $method_call_nodes[$method_call_node->id] = $method_call_node;
- }
-
- if (!$method_call_nodes) {
- return;
- }
-
- foreach ($method_call_nodes as $method_call_node) {
- $statements_analyzer->data_flow_graph->addNode($method_call_node);
-
- foreach ($var_nodes as $var_node) {
- $statements_analyzer->data_flow_graph->addNode($var_node);
-
- $statements_analyzer->data_flow_graph->addPath(
- $method_call_node,
- $var_node,
- 'method-call-' . $method_id->method_name,
- $added_taints,
- $removed_taints
- );
- }
-
- if (!$is_declaring) {
- $cased_declaring_method_id = $codebase->methods->getCasedMethodId($declaring_method_id);
-
- $declaring_method_call_node = new DataFlowNode(
- strtolower((string) $declaring_method_id),
- $cased_declaring_method_id,
- $method_storage->signature_return_type_location ?: $method_storage->location,
- $method_call_node->specialization_key
- );
-
- $statements_analyzer->data_flow_graph->addNode($declaring_method_call_node);
- $statements_analyzer->data_flow_graph->addPath(
- $declaring_method_call_node,
- $method_call_node,
- 'parent',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $return_type_candidate->parent_nodes = $method_call_nodes;
-
- $stmt_var_type = clone $context->vars_in_scope[$var_id];
-
- $stmt_var_type->parent_nodes = $var_nodes;
-
- $context->vars_in_scope[$var_id] = $stmt_var_type;
- } elseif ($method_storage->specialize_call
- && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ) {
- $method_call_node = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $is_declaring
- ? ($method_storage->signature_return_type_location ?: $method_storage->location)
- : null,
- $node_location
- );
-
- if (!$is_declaring) {
- $cased_declaring_method_id = $codebase->methods->getCasedMethodId($declaring_method_id);
-
- $declaring_method_call_node = DataFlowNode::getForMethodReturn(
- (string) $declaring_method_id,
- $cased_declaring_method_id,
- $method_storage->signature_return_type_location ?: $method_storage->location,
- $node_location
- );
-
- $statements_analyzer->data_flow_graph->addNode($declaring_method_call_node);
- $statements_analyzer->data_flow_graph->addPath(
- $declaring_method_call_node,
- $method_call_node,
- 'parent',
- $added_taints,
- $removed_taints
- );
- }
-
- $statements_analyzer->data_flow_graph->addNode($method_call_node);
-
- $return_type_candidate->parent_nodes = [
- $method_call_node->id => $method_call_node
- ];
- } else {
- $method_call_node = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $is_declaring
- ? ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ? ($method_storage->signature_return_type_location ?: $method_storage->location)
- : ($method_storage->return_type_location ?: $method_storage->location))
- : null,
- null
- );
-
- if (!$is_declaring) {
- $cased_declaring_method_id = $codebase->methods->getCasedMethodId($declaring_method_id);
-
- $declaring_method_call_node = DataFlowNode::getForMethodReturn(
- (string) $declaring_method_id,
- $cased_declaring_method_id,
- $method_storage->signature_return_type_location ?: $method_storage->location,
- null
- );
-
- $statements_analyzer->data_flow_graph->addNode($declaring_method_call_node);
- $statements_analyzer->data_flow_graph->addPath(
- $declaring_method_call_node,
- $method_call_node,
- 'parent',
- $added_taints,
- $removed_taints
- );
- }
-
- $statements_analyzer->data_flow_graph->addNode($method_call_node);
-
- $return_type_candidate->parent_nodes = [
- $method_call_node->id => $method_call_node
- ];
- }
-
- if ($method_storage->taint_source_types && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- $method_node = TaintSource::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $method_storage->signature_return_type_location ?: $method_storage->location
- );
-
- $method_node->taints = $method_storage->taint_source_types;
-
- $statements_analyzer->data_flow_graph->addSource($method_node);
- }
-
- if (!$statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- return;
- }
-
- FunctionCallReturnTypeFetcher::taintUsingFlows(
- $statements_analyzer,
- $method_storage,
- $statements_analyzer->data_flow_graph,
- (string) $method_id,
- $args,
- $node_location,
- $method_call_node,
- $method_storage->removed_taints
- );
- }
-
- public static function replaceTemplateTypes(
- Union $return_type_candidate,
- TemplateResult $template_result,
- MethodIdentifier $method_id,
- int $arg_count,
- Codebase $codebase
- ): Union {
- if ($template_result->template_types) {
- $bindable_template_types = $return_type_candidate->getTemplateTypes();
-
- foreach ($bindable_template_types as $template_type) {
- if ($template_type->defining_class !== $method_id->fq_class_name
- && !isset(
- $template_result->lower_bounds
- [$template_type->param_name]
- [$template_type->defining_class]
- )
- ) {
- if ($template_type->param_name === 'TFunctionArgCount') {
- $template_result->lower_bounds[$template_type->param_name] = [
- 'fn-' . strtolower((string) $method_id) => [
- new TemplateBound(
- Type::getInt(false, $arg_count)
- )
- ]
- ];
- } elseif ($template_type->param_name === 'TPhpMajorVersion') {
- $template_result->lower_bounds[$template_type->param_name] = [
- 'fn-' . strtolower((string) $method_id) => [
- new TemplateBound(
- Type::getInt(false, $codebase->php_major_version)
- )
- ]
- ];
- } elseif ($template_type->param_name === 'TPhpVersionId') {
- $template_result->lower_bounds[$template_type->param_name] = [
- 'fn-' . strtolower((string) $method_id) => [
- new TemplateBound(
- Type::getInt(
- false,
- 10000 * $codebase->php_major_version
- + 100 * $codebase->php_minor_version
- )
- )
- ]
- ];
- } else {
- $template_result->lower_bounds[$template_type->param_name] = [
- ($template_type->defining_class) => [
- new TemplateBound(Type::getEmpty())
- ]
- ];
- }
- }
- }
- }
-
- if ($template_result->lower_bounds) {
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- null,
- null,
- null
- );
-
- TemplateInferredTypeReplacer::replace(
- $return_type_candidate,
- $template_result,
- $codebase
- );
- }
-
- return $return_type_candidate;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php
deleted file mode 100644
index 48ff4da..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MethodVisibilityAnalyzer.php
+++ /dev/null
@@ -1,196 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Issue\InaccessibleMethod;
-use Psalm\IssueBuffer;
-use Psalm\StatementsSource;
-use UnexpectedValueException;
-
-use function array_pop;
-use function end;
-use function strtolower;
-
-class MethodVisibilityAnalyzer
-{
- /**
- * @param string[] $suppressed_issues
- *
- * @return false|null
- */
- public static function analyze(
- MethodIdentifier $method_id,
- Context $context,
- StatementsSource $source,
- CodeLocation $code_location,
- array $suppressed_issues
- ): ?bool {
- $codebase = $source->getCodebase();
- $codebase_methods = $codebase->methods;
- $codebase_classlikes = $codebase->classlikes;
-
- $fq_classlike_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- if ($codebase_methods->visibility_provider->has($fq_classlike_name)) {
- $method_visible = $codebase_methods->visibility_provider->isMethodVisible(
- $source,
- $fq_classlike_name,
- $method_name,
- $context,
- $code_location
- );
-
- if ($method_visible === false) {
- if (IssueBuffer::accepts(
- new InaccessibleMethod(
- 'Cannot access method ' . $codebase_methods->getCasedMethodId($method_id) .
- ' from context ' . $context->self,
- $code_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- } elseif ($method_visible === true) {
- return false;
- }
- }
-
- $declaring_method_id = $codebase_methods->getDeclaringMethodId($method_id);
-
- if (!$declaring_method_id) {
- if ($method_name === '__construct'
- || ($method_id->fq_class_name === 'Closure'
- && ($method_id->method_name === 'fromcallable'
- || $method_id->method_name === '__invoke'))
- ) {
- return null;
- }
-
- if (InternalCallMapHandler::inCallMap((string) $method_id)) {
- return null;
- }
-
- throw new UnexpectedValueException('$declaring_method_id not expected to be null here');
- }
-
- $appearing_method_id = $codebase_methods->getAppearingMethodId($method_id);
-
- $appearing_method_class = null;
- $appearing_class_storage = null;
- $appearing_method_name = null;
-
- if ($appearing_method_id) {
- $appearing_method_class = $appearing_method_id->fq_class_name;
- $appearing_method_name = $appearing_method_id->method_name;
-
- // if the calling class is the same, we know the method exists, so it must be visible
- if ($appearing_method_class === $context->self) {
- return null;
- }
-
- $appearing_class_storage = $codebase->classlike_storage_provider->get($appearing_method_class);
- }
-
- $declaring_method_class = $declaring_method_id->fq_class_name;
-
- if ($source->getSource() instanceof TraitAnalyzer
- && strtolower($declaring_method_class) === strtolower((string) $source->getFQCLN())
- ) {
- return null;
- }
-
- $storage = $codebase->methods->getStorage($declaring_method_id);
- $visibility = $storage->visibility;
-
- if ($appearing_method_name
- && isset($appearing_class_storage->trait_visibility_map[$appearing_method_name])
- ) {
- $visibility = $appearing_class_storage->trait_visibility_map[$appearing_method_name];
- }
-
- // Get oldest ancestor declaring $method_id
- $overridden_method_ids = $codebase_methods->getOverriddenMethodIds($method_id);
- // Remove traits and interfaces
- while (($oldest_declaring_method_id = end($overridden_method_ids))
- && !$codebase_classlikes->hasFullyQualifiedClassName($oldest_declaring_method_id->fq_class_name)
- ) {
- array_pop($overridden_method_ids);
- }
- if (empty($overridden_method_ids)) {
- // We prefer appearing method id over declaring method id because declaring method id could be a trait
- $oldest_ancestor_declaring_method_id = $appearing_method_id;
- } else {
- // Oldest ancestor is at end of array
- $oldest_ancestor_declaring_method_id = array_pop($overridden_method_ids);
- }
- $oldest_ancestor_declaring_method_class = $oldest_ancestor_declaring_method_id->fq_class_name ?? null;
-
- switch ($visibility) {
- case ClassLikeAnalyzer::VISIBILITY_PUBLIC:
- return null;
-
- case ClassLikeAnalyzer::VISIBILITY_PRIVATE:
- if (!$context->self || $appearing_method_class !== $context->self) {
- if (IssueBuffer::accepts(
- new InaccessibleMethod(
- 'Cannot access private method ' . $codebase_methods->getCasedMethodId($method_id) .
- ' from context ' . $context->self,
- $code_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
-
- return null;
-
- case ClassLikeAnalyzer::VISIBILITY_PROTECTED:
- if (!$context->self) {
- if (IssueBuffer::accepts(
- new InaccessibleMethod(
- 'Cannot access protected method ' . $method_id,
- $code_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
-
- return null;
- }
-
- if ($oldest_ancestor_declaring_method_class !== null
- && $codebase_classlikes->classExtends($oldest_ancestor_declaring_method_class, $context->self)
- ) {
- return null;
- }
-
- if ($oldest_ancestor_declaring_method_class !== null
- && !$codebase_classlikes->classExtends($context->self, $oldest_ancestor_declaring_method_class)
- && !$codebase_classlikes->classExtends($declaring_method_class, $context->self)
- ) {
- if (IssueBuffer::accepts(
- new InaccessibleMethod(
- 'Cannot access protected method ' . $codebase_methods->getCasedMethodId($method_id) .
- ' from context ' . $context->self,
- $code_location
- ),
- $suppressed_issues
- )) {
- return false;
- }
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php
deleted file mode 100644
index c8c3c70..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php
+++ /dev/null
@@ -1,454 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\Method;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Node\Expr\VirtualArray;
-use Psalm\Node\Expr\VirtualArrayItem;
-use Psalm\Node\Scalar\VirtualString;
-use Psalm\Node\VirtualArg;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Union;
-
-use function array_map;
-use function array_merge;
-
-class MissingMethodCallHandler
-{
- public static function handleMagicMethod(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\MethodCall $stmt,
- MethodIdentifier $method_id,
- ClassLikeStorage $class_storage,
- Context $context,
- Config $config,
- ?Union $all_intersection_return_type,
- AtomicMethodCallAnalysisResult $result,
- ?Atomic $lhs_type_part
- ): ?AtomicCallContext {
- $fq_class_name = $method_id->fq_class_name;
- $method_name_lc = $method_id->method_name;
-
- if ($stmt->isFirstClassCallable()) {
- if (isset($class_storage->pseudo_methods[$method_name_lc])) {
- $result->has_valid_method_call_type = true;
- $result->existent_method_ids[] = $method_id->__toString();
- $result->return_type = self::createFirstClassCallableReturnType(
- $class_storage->pseudo_methods[$method_name_lc]
- );
- } else {
- $result->non_existent_magic_method_ids[] = $method_id->__toString();
- $result->return_type = self::createFirstClassCallableReturnType();
- }
-
- return null;
- }
-
- if ($codebase->methods->return_type_provider->has($fq_class_name)) {
- $return_type_candidate = $codebase->methods->return_type_provider->getReturnType(
- $statements_analyzer,
- $method_id->fq_class_name,
- $method_id->method_name,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt->name)
- );
-
- if ($return_type_candidate) {
- if ($all_intersection_return_type) {
- $return_type_candidate = Type::intersectUnionTypes(
- $all_intersection_return_type,
- $return_type_candidate,
- $codebase
- ) ?? Type::getMixed();
- }
-
- $result->return_type = Type::combineUnionTypes(
- $return_type_candidate,
- $result->return_type,
- $codebase
- );
-
- CallAnalyzer::checkMethodArgs(
- $method_id,
- $stmt->getArgs(),
- null,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- );
-
- return null;
- }
- }
-
- $found_method_and_class_storage = self::findPseudoMethodAndClassStorages(
- $codebase,
- $class_storage,
- $method_name_lc
- );
-
- if ($found_method_and_class_storage) {
- $result->has_valid_method_call_type = true;
- $result->existent_method_ids[] = $method_id->__toString();
-
- [$pseudo_method_storage, $defining_class_storage] = $found_method_and_class_storage;
-
- $found_generic_params = ClassTemplateParamCollector::collect(
- $codebase,
- $defining_class_storage,
- $class_storage,
- $method_name_lc,
- $lhs_type_part,
- !$statements_analyzer->isStatic() && $method_id->fq_class_name === $context->self
- );
-
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- $pseudo_method_storage->params,
- (string) $method_id,
- true,
- $context,
- $found_generic_params ? new TemplateResult([], $found_generic_params) : null
- );
-
- ArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- $pseudo_method_storage->params,
- $pseudo_method_storage,
- null,
- $found_generic_params ? new TemplateResult([], $found_generic_params) : null,
- new CodeLocation($statements_analyzer, $stmt),
- $context
- );
-
- if ($pseudo_method_storage->return_type) {
- $return_type_candidate = clone $pseudo_method_storage->return_type;
-
- if ($found_generic_params) {
- TemplateInferredTypeReplacer::replace(
- $return_type_candidate,
- new TemplateResult([], $found_generic_params),
- $codebase
- );
- }
-
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- $defining_class_storage->name,
- $lhs_type_part instanceof Atomic\TNamedObject ? $lhs_type_part : $fq_class_name,
- $defining_class_storage->parent_class
- );
-
- if ($all_intersection_return_type) {
- $return_type_candidate = Type::intersectUnionTypes(
- $all_intersection_return_type,
- $return_type_candidate,
- $codebase
- ) ?? Type::getMixed();
- }
-
- $result->return_type = Type::combineUnionTypes(
- $return_type_candidate,
- $result->return_type,
- $codebase
- );
-
- return null;
- }
- } elseif ($all_intersection_return_type === null) {
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- if ($class_storage->sealed_methods || $config->seal_all_methods) {
- $result->non_existent_magic_method_ids[] = $method_id->__toString();
-
- return null;
- }
- }
-
- $result->has_valid_method_call_type = true;
- $result->existent_method_ids[] = $method_id->__toString();
-
- $array_values = array_map(
- /**
- * @return PhpParser\Node\Expr\ArrayItem
- */
- function (PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem {
- return new VirtualArrayItem(
- $arg->value,
- null,
- false,
- $arg->getAttributes()
- );
- },
- $stmt->getArgs()
- );
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- return new AtomicCallContext(
- new MethodIdentifier($fq_class_name, '__call'),
- [
- new VirtualArg(
- new VirtualString($method_name_lc),
- false,
- false,
- $stmt->getAttributes()
- ),
- new VirtualArg(
- new VirtualArray(
- $array_values,
- $stmt->getAttributes()
- ),
- false,
- false,
- $stmt->getAttributes()
- ),
- ]
- );
- }
-
- /**
- * @param array<string> $all_intersection_existent_method_ids
- */
- public static function handleMissingOrMagicMethod(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\MethodCall $stmt,
- MethodIdentifier $method_id,
- bool $is_interface,
- Context $context,
- Config $config,
- ?Union $all_intersection_return_type,
- array $all_intersection_existent_method_ids,
- ?string $intersection_method_id,
- string $cased_method_id,
- AtomicMethodCallAnalysisResult $result,
- ?Atomic $lhs_type_part
- ): void {
- $fq_class_name = $method_id->fq_class_name;
- $method_name_lc = $method_id->method_name;
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $found_method_and_class_storage = self::findPseudoMethodAndClassStorages(
- $codebase,
- $class_storage,
- $method_name_lc
- );
-
- if (($is_interface || $config->use_phpdoc_method_without_magic_or_parent)
- && $found_method_and_class_storage
- ) {
- $result->has_valid_method_call_type = true;
- $result->existent_method_ids[] = $method_id->__toString();
-
- [$pseudo_method_storage, $defining_class_storage] = $found_method_and_class_storage;
-
- if ($stmt->isFirstClassCallable()) {
- $result->return_type = self::createFirstClassCallableReturnType($pseudo_method_storage);
- return;
- }
-
- $found_generic_params = ClassTemplateParamCollector::collect(
- $codebase,
- $defining_class_storage,
- $class_storage,
- $method_name_lc,
- $lhs_type_part,
- !$statements_analyzer->isStatic() && $method_id->fq_class_name === $context->self
- );
-
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- $pseudo_method_storage->params,
- (string) $method_id,
- true,
- $context,
- $found_generic_params ? new TemplateResult([], $found_generic_params) : null
- ) === false) {
- return;
- }
-
- if (ArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- $pseudo_method_storage->params,
- $pseudo_method_storage,
- null,
- $found_generic_params ? new TemplateResult([], $found_generic_params) : null,
- new CodeLocation($statements_analyzer, $stmt->name),
- $context
- ) === false) {
- return;
- }
-
- if ($pseudo_method_storage->return_type) {
- $return_type_candidate = clone $pseudo_method_storage->return_type;
-
- if ($found_generic_params) {
- TemplateInferredTypeReplacer::replace(
- $return_type_candidate,
- new TemplateResult([], $found_generic_params),
- $codebase
- );
- }
-
- if ($all_intersection_return_type) {
- $return_type_candidate = Type::intersectUnionTypes(
- $all_intersection_return_type,
- $return_type_candidate,
- $codebase
- ) ?? Type::getMixed();
- }
-
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- $defining_class_storage->name,
- $lhs_type_part instanceof Atomic\TNamedObject ? $lhs_type_part : $fq_class_name,
- $defining_class_storage->parent_class,
- true,
- false,
- $class_storage->final
- );
-
- $result->return_type = Type::combineUnionTypes($return_type_candidate, $result->return_type);
-
- return;
- }
-
- $result->return_type = Type::getMixed();
-
- return;
- }
-
- if ($stmt->isFirstClassCallable()) {
- $result->non_existent_class_method_ids[] = $method_id->__toString();
- $result->return_type = self::createFirstClassCallableReturnType();
- return;
- }
-
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return;
- }
-
- if ($all_intersection_return_type && $all_intersection_existent_method_ids) {
- $result->existent_method_ids = array_merge(
- $result->existent_method_ids,
- $all_intersection_existent_method_ids
- );
-
- $result->return_type = Type::combineUnionTypes($all_intersection_return_type, $result->return_type);
-
- return;
- }
-
- if ((!$is_interface && !$config->use_phpdoc_method_without_magic_or_parent)
- || !isset($class_storage->pseudo_methods[$method_name_lc])
- ) {
- if ($is_interface) {
- $result->non_existent_interface_method_ids[] = $intersection_method_id ?: $cased_method_id;
- } else {
- $result->non_existent_class_method_ids[] = $intersection_method_id ?: $cased_method_id;
- }
- }
- }
-
- private static function createFirstClassCallableReturnType(?MethodStorage $method_storage = null): Union
- {
- if ($method_storage) {
- return new Union([new TClosure(
- 'Closure',
- $method_storage->params,
- $method_storage->return_type,
- $method_storage->pure
- )]);
- }
-
- return Type::getClosure();
- }
-
- /**
- * Try to find matching pseudo method over ancestors (including interfaces).
- *
- * Returns the pseudo method if exists, with its defining class storage.
- * If the method is not declared, null is returned.
- *
- * @param Codebase $codebase
- * @param ClassLikeStorage $static_class_storage The called class
- * @param lowercase-string $method_name_lc
- *
- * @return array{MethodStorage, ClassLikeStorage}
- */
- private static function findPseudoMethodAndClassStorages(
- Codebase $codebase,
- ClassLikeStorage $static_class_storage,
- string $method_name_lc
- ): ?array {
- if (isset($static_class_storage->declaring_pseudo_method_ids[$method_name_lc])) {
- $method_id = $static_class_storage->declaring_pseudo_method_ids[$method_name_lc];
- $class_storage = $codebase->classlikes->getStorageFor($method_id->fq_class_name);
-
- if ($class_storage && isset($class_storage->pseudo_methods[$method_name_lc])) {
- return [$class_storage->pseudo_methods[$method_name_lc], $class_storage];
- }
- }
-
- if ($pseudo_method_storage = $static_class_storage->pseudo_methods[$method_name_lc] ?? null) {
- return [$pseudo_method_storage, $static_class_storage];
- }
-
- $ancestors = $static_class_storage->class_implements;
-
- foreach ($ancestors as $fq_class_name => $_) {
- $class_storage = $codebase->classlikes->getStorageFor($fq_class_name);
-
- if ($class_storage && isset($class_storage->pseudo_methods[$method_name_lc])) {
- return [
- $class_storage->pseudo_methods[$method_name_lc],
- $class_storage
- ];
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php
deleted file mode 100644
index 04f0b96..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/MethodCallAnalyzer.php
+++ /dev/null
@@ -1,437 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalysisResult;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\AtomicMethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Issue\InvalidMethodCall;
-use Psalm\Issue\InvalidScope;
-use Psalm\Issue\NullReference;
-use Psalm\Issue\PossiblyFalseReference;
-use Psalm\Issue\PossiblyInvalidMethodCall;
-use Psalm\Issue\PossiblyNullReference;
-use Psalm\Issue\PossiblyUndefinedMethod;
-use Psalm\Issue\TooFewArguments;
-use Psalm\Issue\TooManyArguments;
-use Psalm\Issue\UndefinedInterfaceMethod;
-use Psalm\Issue\UndefinedMagicMethod;
-use Psalm\Issue\UndefinedMethod;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_reduce;
-use function count;
-use function is_string;
-use function strtolower;
-
-/**
- * @internal
- */
-class MethodCallAnalyzer extends CallAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\MethodCall $stmt,
- Context $context,
- bool $real_method_call = true
- ): bool {
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- $existing_stmt_var_type = null;
-
- if (!$real_method_call) {
- $existing_stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
- }
-
- if ($existing_stmt_var_type) {
- $statements_analyzer->node_data->setType($stmt->var, $existing_stmt_var_type);
- } elseif (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
-
- if (!$stmt->name instanceof PhpParser\Node\Identifier) {
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
- }
-
- $context->inside_call = $was_inside_call;
-
- if ($stmt->var instanceof PhpParser\Node\Expr\Variable) {
- if (is_string($stmt->var->name) && $stmt->var->name === 'this' && !$statements_analyzer->getFQCLN()) {
- if (IssueBuffer::accepts(
- new InvalidScope(
- 'Use of $this in non-class context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
- }
-
- $lhs_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $class_type = $lhs_var_id && $context->hasVariable($lhs_var_id)
- ? $context->vars_in_scope[$lhs_var_id]
- : null;
-
- if ($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var)) {
- $class_type = $stmt_var_type;
- } elseif (!$class_type) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- if (!$context->check_classes) {
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return false;
- }
-
- return true;
- }
-
- if ($class_type
- && $stmt->name instanceof PhpParser\Node\Identifier
- && ($class_type->isNull() || $class_type->isVoid())
- ) {
- if (IssueBuffer::accepts(
- new NullReference(
- 'Cannot call method ' . $stmt->name->name . ' on null value',
- new CodeLocation($statements_analyzer->getSource(), $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- if ($class_type
- && $stmt->name instanceof PhpParser\Node\Identifier
- && $class_type->isNullable()
- && !$class_type->ignore_nullable_issues
- && !($stmt->name->name === 'offsetGet' && $context->inside_isset)
- && !self::hasNullsafe($stmt->var)
- ) {
- IssueBuffer::maybeAdd(
- new PossiblyNullReference(
- 'Cannot call method ' . $stmt->name->name . ' on possibly null value',
- new CodeLocation($statements_analyzer->getSource(), $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($class_type
- && $stmt->name instanceof PhpParser\Node\Identifier
- && $class_type->isFalsable()
- && !$class_type->ignore_falsable_issues
- ) {
- IssueBuffer::maybeAdd(
- new PossiblyFalseReference(
- 'Cannot call method ' . $stmt->name->name . ' on possibly false value',
- new CodeLocation($statements_analyzer->getSource(), $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $source = $statements_analyzer->getSource();
-
- if (!$class_type) {
- $class_type = Type::getMixed();
- }
-
- $lhs_types = $class_type->getAtomicTypes();
-
- $result = new AtomicMethodCallAnalysisResult();
-
- $possible_new_class_types = [];
- foreach ($lhs_types as $lhs_type_part) {
- AtomicMethodCallAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $codebase,
- $context,
- $class_type,
- $lhs_type_part,
- $lhs_type_part instanceof TNamedObject
- || $lhs_type_part instanceof TTemplateParam
- ? $lhs_type_part
- : null,
- false,
- $lhs_var_id,
- $result
- );
- if (isset($context->vars_in_scope[$lhs_var_id])
- && ($possible_new_class_type = $context->vars_in_scope[$lhs_var_id]) instanceof Union
- && !$possible_new_class_type->equals($class_type)) {
- $possible_new_class_types[] = $context->vars_in_scope[$lhs_var_id];
- }
- }
- if (!$stmt->isFirstClassCallable()
- && !$stmt->getArgs()
- && $lhs_var_id && $stmt->name instanceof PhpParser\Node\Identifier
- ) {
- if ($codebase->config->memoize_method_calls || $result->can_memoize) {
- $method_var_id = $lhs_var_id . '->' . strtolower($stmt->name->name) . '()';
-
- if (isset($context->vars_in_scope[$method_var_id])) {
- $result->return_type = clone $context->vars_in_scope[$method_var_id];
- } elseif ($result->return_type !== null) {
- $context->vars_in_scope[$method_var_id] = $result->return_type;
- $context->vars_in_scope[$method_var_id]->has_mutations = false;
- }
-
- if ($result->can_memoize) {
- $stmt->setAttribute('memoizable', true);
- }
- }
- }
-
- if (count($possible_new_class_types) > 0) {
- $class_type = array_reduce(
- $possible_new_class_types,
- function (?Union $type_1, Union $type_2) use ($codebase): Union {
- return Type::combineUnionTypes($type_1, $type_2, $codebase);
- }
- );
- }
-
- if ($result->invalid_method_call_types) {
- $invalid_class_type = $result->invalid_method_call_types[0];
-
- if ($result->has_valid_method_call_type || $result->has_mixed_method_call) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidMethodCall(
- 'Cannot call method on possible ' . $invalid_class_type . ' variable ' . $lhs_var_id,
- new CodeLocation($source, $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidMethodCall(
- 'Cannot call method on ' . $invalid_class_type . ' variable ' . $lhs_var_id,
- new CodeLocation($source, $stmt->name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($result->non_existent_magic_method_ids) {
- if ($context->check_methods) {
- IssueBuffer::maybeAdd(
- new UndefinedMagicMethod(
- 'Magic method ' . $result->non_existent_magic_method_ids[0] . ' does not exist',
- new CodeLocation($source, $stmt->name),
- $result->non_existent_magic_method_ids[0]
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($result->non_existent_class_method_ids) {
- if ($context->check_methods) {
- if ($result->existent_method_ids || $result->has_mixed_method_call) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedMethod(
- 'Method ' . $result->non_existent_class_method_ids[0] . ' does not exist',
- new CodeLocation($source, $stmt->name),
- $result->non_existent_class_method_ids[0]
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedMethod(
- 'Method ' . $result->non_existent_class_method_ids[0] . ' does not exist',
- new CodeLocation($source, $stmt->name),
- $result->non_existent_class_method_ids[0]
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return true;
- }
-
- if ($result->non_existent_interface_method_ids) {
- if ($context->check_methods) {
- if ($result->existent_method_ids || $result->has_mixed_method_call) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedMethod(
- 'Method ' . $result->non_existent_interface_method_ids[0] . ' does not exist',
- new CodeLocation($source, $stmt->name),
- $result->non_existent_interface_method_ids[0]
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedInterfaceMethod(
- 'Method ' . $result->non_existent_interface_method_ids[0] . ' does not exist',
- new CodeLocation($source, $stmt->name),
- $result->non_existent_interface_method_ids[0]
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return true;
- }
-
- if ($result->too_many_arguments && $result->too_many_arguments_method_ids) {
- $error_method_id = $result->too_many_arguments_method_ids[0];
-
- IssueBuffer::maybeAdd(
- new TooManyArguments(
- 'Too many arguments for method ' . $error_method_id . ' - saw ' . count($stmt->getArgs()),
- new CodeLocation($source, $stmt->name),
- (string) $error_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($result->too_few_arguments && $result->too_few_arguments_method_ids) {
- $error_method_id = $result->too_few_arguments_method_ids[0];
-
- IssueBuffer::maybeAdd(
- new TooFewArguments(
- 'Too few arguments for method ' . $error_method_id . ' saw ' . count($stmt->getArgs()),
- new CodeLocation($source, $stmt->name),
- (string) $error_method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $stmt_type = $result->return_type;
-
- if ($stmt_type) {
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if ($stmt_type->isNever()) {
- $context->has_returned = true;
- }
- }
-
- if ($result->returns_by_ref) {
- if (!$stmt_type) {
- $stmt_type = Type::getMixed();
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
- }
-
- $stmt_type->by_ref = $result->returns_by_ref;
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- && $stmt_type
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_type->getId(),
- $stmt
- );
- }
-
- if (!$result->existent_method_ids) {
- return $stmt->isFirstClassCallable() || self::checkMethodArgs(
- null,
- $stmt->getArgs(),
- null,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- );
- }
-
- // if we called a method on this nullable variable, remove the nullable status here
- // because any further calls must have worked
- if ($lhs_var_id
- && !$class_type->isMixed()
- && $result->has_valid_method_call_type
- && !$result->has_mixed_method_call
- && !$result->invalid_method_call_types
- && ($class_type->from_docblock || $class_type->isNullable())
- && $real_method_call
- ) {
- $keys_to_remove = [];
-
- $class_type = clone $class_type;
-
- foreach ($class_type->getAtomicTypes() as $key => $type) {
- if (!$type instanceof TNamedObject) {
- $keys_to_remove[] = $key;
- } else {
- $type->from_docblock = false;
- }
- }
-
- foreach ($keys_to_remove as $key) {
- $class_type->removeType($key);
- }
-
- $class_type->from_docblock = false;
-
- $context->removeVarFromConflictingClauses($lhs_var_id, null, $statements_analyzer);
-
- $context->vars_in_scope[$lhs_var_id] = $class_type;
- }
-
- return true;
- }
-
- public static function hasNullsafe(PhpParser\Node\Expr $expr): bool
- {
- if ($expr instanceof PhpParser\Node\Expr\MethodCall
- || $expr instanceof PhpParser\Node\Expr\PropertyFetch
- ) {
- return self::hasNullsafe($expr->var);
- }
-
- return $expr instanceof PhpParser\Node\Expr\NullsafeMethodCall
- || $expr instanceof PhpParser\Node\Expr\NullsafePropertyFetch;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php
deleted file mode 100644
index cb91434..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NamedFunctionCallHandler.php
+++ /dev/null
@@ -1,586 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssertionFinder;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\Issue\PossibleRawObjectIteration;
-use Psalm\Issue\RawObjectIteration;
-use Psalm\Issue\RedundantFunctionCall;
-use Psalm\Issue\RedundantFunctionCallGivenDocblockType;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualArray;
-use Psalm\Node\Expr\VirtualArrayItem;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Scalar\VirtualString;
-use Psalm\Type;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosedResource;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TDependentGetDebugType;
-use Psalm\Type\Atomic\TDependentGetType;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function array_map;
-use function extension_loaded;
-use function implode;
-use function in_array;
-use function is_string;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class NamedFunctionCallHandler
-{
- /**
- * @param lowercase-string $function_id
- */
- public static function handle(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Expr\FuncCall $real_stmt,
- PhpParser\Node\Name $function_name,
- ?string $function_id,
- Context $context
- ): void {
- if ($function_id === 'get_class'
- || $function_id === 'gettype'
- || $function_id === 'get_debug_type'
- ) {
- self::handleDependentTypeFunction(
- $statements_analyzer,
- $stmt,
- $real_stmt,
- $function_id,
- $context
- );
-
- return;
- }
-
- if ($stmt->isFirstClassCallable()) {
- return;
- }
-
- $first_arg = $stmt->getArgs()[0] ?? null;
-
- if ($function_id === 'method_exists') {
- $second_arg = $stmt->getArgs()[1] ?? null;
-
- if ($first_arg
- && $first_arg->value instanceof PhpParser\Node\Expr\Variable
- && $second_arg
- && $second_arg->value instanceof PhpParser\Node\Scalar\String_
- ) {
- // do nothing
- } else {
- $context->check_methods = false;
- }
-
- return;
- }
-
- if ($function_id === 'class_exists') {
- if ($first_arg) {
- if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) {
- if (!$codebase->classlikes->classExists($first_arg->value->value)) {
- $context->phantom_classes[strtolower($first_arg->value->value)] = true;
- }
- } elseif ($first_arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $first_arg->value->class instanceof PhpParser\Node\Name
- && $first_arg->value->name instanceof PhpParser\Node\Identifier
- && $first_arg->value->name->name === 'class'
- ) {
- $resolved_name = (string) $first_arg->value->class->getAttribute('resolvedName');
-
- if (!$codebase->classlikes->classExists($resolved_name)) {
- $context->phantom_classes[strtolower($resolved_name)] = true;
- }
- }
- }
-
- return;
- }
-
- if ($function_id === 'interface_exists') {
- if ($first_arg) {
- if ($first_arg->value instanceof PhpParser\Node\Scalar\String_) {
- $context->phantom_classes[strtolower($first_arg->value->value)] = true;
- } elseif ($first_arg->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $first_arg->value->class instanceof PhpParser\Node\Name
- && $first_arg->value->name instanceof PhpParser\Node\Identifier
- && $first_arg->value->name->name === 'class'
- ) {
- $resolved_name = (string) $first_arg->value->class->getAttribute('resolvedName');
-
- if (!$codebase->classlikes->interfaceExists($resolved_name)) {
- $context->phantom_classes[strtolower($resolved_name)] = true;
- }
- }
- }
-
- return;
- }
-
- if (in_array($function_id, ['is_file', 'file_exists']) && $first_arg) {
- $var_id = ExpressionIdentifier::getArrayVarId($first_arg->value, null);
-
- if ($var_id) {
- $context->phantom_files[$var_id] = true;
- }
-
- return;
- }
-
- if ($function_id === 'extension_loaded') {
- if ($first_arg
- && $first_arg->value instanceof PhpParser\Node\Scalar\String_
- ) {
- if (@extension_loaded($first_arg->value->value)) {
- // do nothing
- } else {
- $context->check_classes = false;
- }
- }
-
- return;
- }
-
- if ($function_id === 'function_exists') {
- $context->check_functions = false;
- return;
- }
-
- if ($function_id === 'is_callable') {
- $context->check_methods = false;
- $context->check_functions = false;
- return;
- }
-
- if ($function_id === 'defined') {
- $context->check_consts = false;
- return;
- }
-
- if ($function_id === 'extract') {
- $context->check_variables = false;
-
- foreach ($context->vars_in_scope as $var_id => $_) {
- if ($var_id === '$this' || strpos($var_id, '[') || strpos($var_id, '>')) {
- continue;
- }
-
- $mixed_type = Type::getMixed();
- $mixed_type->parent_nodes = $context->vars_in_scope[$var_id]->parent_nodes;
-
- $context->vars_in_scope[$var_id] = $mixed_type;
- $context->assigned_var_ids[$var_id] = (int) $stmt->getAttribute('startFilePos');
- $context->possibly_assigned_var_ids[$var_id] = true;
- }
-
- return;
- }
-
- if ($function_id === 'compact') {
- $all_args_string_literals = true;
- $new_items = [];
-
- foreach ($stmt->getArgs() as $arg) {
- $arg_type = $statements_analyzer->node_data->getType($arg->value);
-
- if (!$arg_type || !$arg_type->isSingleStringLiteral()) {
- $all_args_string_literals = false;
- break;
- }
-
- $var_name = $arg_type->getSingleStringLiteral()->value;
-
- $new_items[] = new VirtualArrayItem(
- new VirtualVariable($var_name, $arg->value->getAttributes()),
- new VirtualString($var_name, $arg->value->getAttributes()),
- false,
- $arg->getAttributes()
- );
- }
-
- if ($all_args_string_literals) {
- $arr = new VirtualArray($new_items, $stmt->getAttributes());
- $old_node_data = $statements_analyzer->node_data;
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $arr, $context);
-
- $arr_type = $statements_analyzer->node_data->getType($arr);
-
- $statements_analyzer->node_data = $old_node_data;
-
- if ($arr_type) {
- $statements_analyzer->node_data->setType($stmt, $arr_type);
- }
- }
-
- return;
- }
-
- if ($function_id === 'func_get_args') {
- $source = $statements_analyzer->getSource();
-
- if ($source instanceof FunctionLikeAnalyzer) {
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($source->param_nodes as $param_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $param_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
- }
-
- return;
- }
-
- if ($function_id === 'var_dump'
- || $function_id === 'shell_exec'
- ) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'Unsafe ' . implode('', $function_name->parts),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (isset($codebase->config->forbidden_functions[strtolower((string) $function_name)])) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'You have forbidden the use of ' . $function_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($function_id === 'define') {
- if ($first_arg) {
- $fq_const_name = ConstFetchAnalyzer::getConstName(
- $first_arg->value,
- $statements_analyzer->node_data,
- $codebase,
- $statements_analyzer->getAliases()
- );
-
- if ($fq_const_name !== null && isset($stmt->getArgs()[1])) {
- $second_arg = $stmt->getArgs()[1];
- $was_in_call = $context->inside_call;
- $context->inside_call = true;
- ExpressionAnalyzer::analyze($statements_analyzer, $second_arg->value, $context);
- $context->inside_call = $was_in_call;
-
- ConstFetchAnalyzer::setConstType(
- $statements_analyzer,
- $fq_const_name,
- $statements_analyzer->node_data->getType($second_arg->value) ?? Type::getMixed(),
- $context
- );
- }
- } else {
- $context->check_consts = false;
- }
-
- return;
- }
-
- if ($function_id === 'constant') {
- if ($first_arg) {
- $fq_const_name = ConstFetchAnalyzer::getConstName(
- $first_arg->value,
- $statements_analyzer->node_data,
- $codebase,
- $statements_analyzer->getAliases()
- );
-
- if ($fq_const_name !== null) {
- $const_type = ConstFetchAnalyzer::getConstType(
- $statements_analyzer,
- $fq_const_name,
- true,
- $context
- );
-
- if ($const_type) {
- $statements_analyzer->node_data->setType($real_stmt, $const_type);
- }
- }
- } else {
- $context->check_consts = false;
- }
- }
-
- if ($first_arg
- && $function_id
- && strpos($function_id, 'is_') === 0
- && $function_id !== 'is_a'
- && !$context->inside_negation
- ) {
- $stmt_assertions = $statements_analyzer->node_data->getAssertions($stmt);
-
- $anded_assertions = $stmt_assertions ?? AssertionFinder::processFunctionCall(
- $stmt,
- $context->self,
- $statements_analyzer,
- $codebase,
- $context->inside_negation
- );
-
- $changed_vars = [];
-
- foreach ($anded_assertions as $assertions) {
- $referenced_var_ids = array_map(
- function (array $_): bool {
- return true;
- },
- $assertions
- );
-
- Reconciler::reconcileKeyedTypes(
- $assertions,
- $assertions,
- $context->vars_in_scope,
- $changed_vars,
- $referenced_var_ids,
- $statements_analyzer,
- [],
- $context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
- }
-
- return;
- }
-
- if ($first_arg && ($function_id === 'array_values' || $function_id === 'ksort')) {
- $first_arg_type = $statements_analyzer->node_data->getType($first_arg->value);
-
- if ($first_arg_type
- && UnionTypeComparator::isContainedBy(
- $codebase,
- $first_arg_type,
- Type::getList()
- )
- ) {
- if ($first_arg_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantFunctionCallGivenDocblockType(
- "The call to $function_id is unnecessary given the list docblock type $first_arg_type",
- new CodeLocation($statements_analyzer, $function_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantFunctionCall(
- "The call to $function_id is unnecessary, $first_arg_type is already a list",
- new CodeLocation($statements_analyzer, $function_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- if ($first_arg && $function_id === 'strtolower') {
- $first_arg_type = $statements_analyzer->node_data->getType($first_arg->value);
-
- if ($first_arg_type
- && UnionTypeComparator::isContainedBy(
- $codebase,
- $first_arg_type,
- new Union([new TLowercaseString()])
- )
- ) {
- if ($first_arg_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new RedundantFunctionCallGivenDocblockType(
- 'The call to strtolower is unnecessary given the docblock type',
- new CodeLocation($statements_analyzer, $function_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new RedundantFunctionCall(
- 'The call to strtolower is unnecessary',
- new CodeLocation($statements_analyzer, $function_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($first_arg
- && ($function_id === 'array_walk'
- || $function_id === 'array_walk_recursive'
- )
- ) {
- $first_arg_type = $statements_analyzer->node_data->getType($first_arg->value);
-
- if ($first_arg_type && $first_arg_type->hasObjectType()) {
- if ($first_arg_type->isSingle()) {
- IssueBuffer::maybeAdd(
- new RawObjectIteration(
- 'Possibly undesired iteration over object properties',
- new CodeLocation($statements_analyzer, $function_name)
- )
- );
- } else {
- IssueBuffer::maybeAdd(
- new PossibleRawObjectIteration(
- 'Possibly undesired iteration over object properties',
- new CodeLocation($statements_analyzer, $function_name)
- )
- );
- }
- }
- }
- }
-
- private static function handleDependentTypeFunction(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\FuncCall $stmt,
- PhpParser\Node\Expr\FuncCall $real_stmt,
- string $function_id,
- Context $context
- ): void {
- $first_arg = $stmt->getArgs()[0] ?? null;
-
- if ($first_arg) {
- $var = $first_arg->value;
-
- if ($var instanceof PhpParser\Node\Expr\Variable
- && is_string($var->name)
- ) {
- $var_id = '$' . $var->name;
-
- if (isset($context->vars_in_scope[$var_id])) {
- if (!$context->vars_in_scope[$var_id]->hasTemplate()) {
- if ($function_id === 'get_class') {
- $atomic_type = new TDependentGetClass(
- $var_id,
- $context->vars_in_scope[$var_id]->hasMixed()
- ? Type::getObject()
- : $context->vars_in_scope[$var_id]
- );
- } elseif ($function_id === 'gettype') {
- $atomic_type = new TDependentGetType($var_id);
- } else {
- $atomic_type = new TDependentGetDebugType($var_id);
- }
-
- $statements_analyzer->node_data->setType($real_stmt, new Union([$atomic_type]));
-
- return;
- }
- }
- }
-
- if (($var_type = $statements_analyzer->node_data->getType($var))
- && ($function_id === 'get_class'
- || $function_id === 'get_debug_type'
- )
- ) {
- $class_string_types = [];
-
- foreach ($var_type->getAtomicTypes() as $class_type) {
- if ($class_type instanceof TNamedObject) {
- $class_string_types[] = new TClassString($class_type->value, clone $class_type);
- } elseif ($class_type instanceof TTemplateParam
- && $class_type->as->isSingle()
- ) {
- $as_atomic_type = $class_type->as->getSingleAtomic();
-
- if ($as_atomic_type instanceof TObject) {
- $class_string_types[] = new TTemplateParamClass(
- $class_type->param_name,
- 'object',
- null,
- $class_type->defining_class
- );
- } elseif ($as_atomic_type instanceof TNamedObject) {
- $class_string_types[] = new TTemplateParamClass(
- $class_type->param_name,
- $as_atomic_type->value,
- $as_atomic_type,
- $class_type->defining_class
- );
- }
- } elseif ($function_id === 'get_class') {
- $class_string_types[] = new TClassString();
- } else {
- if ($class_type instanceof TInt) {
- $class_string_types[] = new TLiteralString('int');
- } elseif ($class_type instanceof TString) {
- $class_string_types[] = new TLiteralString('string');
- } elseif ($class_type instanceof TFloat) {
- $class_string_types[] = new TLiteralString('float');
- } elseif ($class_type instanceof TBool) {
- $class_string_types[] = new TLiteralString('bool');
- } elseif ($class_type instanceof TClosedResource) {
- $class_string_types[] = new TLiteralString('resource (closed)');
- } elseif ($class_type instanceof TNull) {
- $class_string_types[] = new TLiteralString('null');
- } else {
- $class_string_types[] = new TString();
- }
- }
- }
-
- if ($class_string_types) {
- $statements_analyzer->node_data->setType($real_stmt, new Union($class_string_types));
- }
- }
- } elseif ($function_id === 'get_class'
- && ($get_class_name = $statements_analyzer->getFQCLN())
- ) {
- $statements_analyzer->node_data->setType(
- $real_stmt,
- new Union([
- new TClassString(
- $get_class_name,
- new TNamedObject($get_class_name)
- )
- ])
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php
deleted file mode 100644
index 863c38e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php
+++ /dev/null
@@ -1,866 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodVisibilityAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Issue\AbstractInstantiation;
-use Psalm\Issue\DeprecatedClass;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\Issue\InterfaceInstantiation;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\InternalMethod;
-use Psalm\Issue\InvalidStringClass;
-use Psalm\Issue\MixedMethodCall;
-use Psalm\Issue\TooManyArguments;
-use Psalm\Issue\UndefinedClass;
-use Psalm\Issue\UnsafeGenericInstantiation;
-use Psalm\Issue\UnsafeInstantiation;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TAnonymousClassInstance;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-
-use function array_map;
-use function array_merge;
-use function array_shift;
-use function array_values;
-use function implode;
-use function in_array;
-use function md5;
-use function preg_match;
-use function reset;
-use function strtolower;
-
-/**
- * @internal
- */
-class NewAnalyzer extends CallAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\New_ $stmt,
- Context $context
- ): bool {
- $fq_class_name = null;
-
- $codebase = $statements_analyzer->getCodebase();
- $config = $codebase->config;
-
- $can_extend = false;
-
- $from_static = false;
-
- if ($stmt->class instanceof PhpParser\Node\Name) {
- if (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
- $aliases = $statements_analyzer->getAliases();
-
- if ($context->calling_method_id
- && !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
- ) {
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $context->calling_method_id,
- 'use:' . $stmt->class->parts[0] . ':' . md5($statements_analyzer->getFilePath()),
- false
- );
- }
-
- $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $aliases
- );
-
- $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
- } elseif ($context->self !== null) {
- switch ($stmt->class->parts[0]) {
- case 'self':
- $class_storage = $codebase->classlike_storage_provider->get($context->self);
- $fq_class_name = $class_storage->name;
- break;
-
- case 'parent':
- $fq_class_name = $context->parent;
- break;
-
- case 'static':
- // @todo maybe we can do better here
- $class_storage = $codebase->classlike_storage_provider->get($context->self);
- $fq_class_name = $class_storage->name;
-
- if (!$class_storage->final) {
- $can_extend = true;
- $from_static = true;
- }
-
- break;
- }
- }
-
- if ($codebase->store_node_types
- && $fq_class_name
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $codebase->classlikes->classExists($fq_class_name)
- ? $fq_class_name
- : '*'
- . ($stmt->class instanceof PhpParser\Node\Name\FullyQualified
- ? '\\'
- : $statements_analyzer->getNamespace() . '-')
- . implode('\\', $stmt->class->parts)
- );
- }
- } elseif ($stmt->class instanceof PhpParser\Node\Stmt\Class_) {
- $statements_analyzer->analyze([$stmt->class], $context);
- $fq_class_name = ClassAnalyzer::getAnonymousClassName($stmt->class, $statements_analyzer->getFilePath());
- } else {
- self::analyzeConstructorExpression(
- $statements_analyzer,
- $codebase,
- $context,
- $stmt,
- $stmt->class,
- $config,
- $fq_class_name,
- $can_extend
- );
- }
-
- if ($fq_class_name) {
- if ($codebase->alter_code
- && $stmt->class instanceof PhpParser\Node\Name
- && !in_array($stmt->class->parts[0], ['parent', 'static'])
- ) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id
- );
- }
-
- if ($context->check_classes) {
- if ($context->isPhantomClass($fq_class_name)) {
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- return true;
- }
-
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt->class),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- return true;
- }
-
- if ($codebase->interfaceExists($fq_class_name)) {
- if (IssueBuffer::accepts(
- new InterfaceInstantiation(
- 'Interface ' . $fq_class_name . ' cannot be instantiated',
- new CodeLocation($statements_analyzer->getSource(), $stmt->class)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- }
-
- return true;
- }
- }
-
- if ($stmt->class instanceof PhpParser\Node\Stmt\Class_) {
- $extends = $stmt->class->extends ? (string) $stmt->class->extends : null;
- $result_atomic_type = new TAnonymousClassInstance($fq_class_name, false, $extends);
- } else {
- //if the class is a Name, it can't represent a child
- $definite_class = $stmt->class instanceof PhpParser\Node\Name;
- $result_atomic_type = new TNamedObject($fq_class_name, $from_static, $definite_class);
- }
-
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union([$result_atomic_type])
- );
-
- if (strtolower($fq_class_name) !== 'stdclass' &&
- $codebase->classlikes->classExists($fq_class_name)
- ) {
- self::analyzeNamedConstructor(
- $statements_analyzer,
- $codebase,
- $stmt,
- $context,
- $fq_class_name,
- $from_static,
- $can_extend
- );
- } else {
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- if ($codebase->classlikes->enumExists($fq_class_name)) {
- if (IssueBuffer::accepts(new UndefinedClass(
- 'Enums cannot be instantiated',
- new CodeLocation($statements_analyzer, $stmt),
- $fq_class_name
- ))) {
- // fall through
- }
- }
- }
- }
-
- if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
- $context->removeMutableObjectVars();
- }
-
- return true;
- }
-
- private static function analyzeNamedConstructor(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\New_ $stmt,
- Context $context,
- string $fq_class_name,
- bool $from_static,
- bool $can_extend
- ): void {
- $storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- if ($from_static) {
- if (!$storage->preserve_constructor_signature) {
- IssueBuffer::maybeAdd(
- new UnsafeInstantiation(
- 'Cannot safely instantiate class ' . $fq_class_name . ' with "new static" as'
- . ' its constructor might change in child classes',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($storage->template_types
- && !$storage->enforce_template_inheritance
- ) {
- $source = $statements_analyzer->getSource();
-
- if ($source instanceof FunctionLikeAnalyzer) {
- $function_storage = $source->getFunctionLikeStorage($statements_analyzer);
-
- if ($function_storage->return_type
- && preg_match('/\bstatic\b/', $function_storage->return_type->getId())
- ) {
- IssueBuffer::maybeAdd(
- new UnsafeGenericInstantiation(
- 'Cannot safely instantiate generic class ' . $fq_class_name
- . ' with "new static" as'
- . ' its generic parameters may be constrained in child classes.',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- // if we're not calling this constructor via new static()
- if ($storage->abstract && !$can_extend) {
- if (IssueBuffer::accepts(
- new AbstractInstantiation(
- 'Unable to instantiate a abstract class ' . $fq_class_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return;
- }
- }
-
- if ($storage->deprecated && strtolower($fq_class_name) !== strtolower((string)$context->self)) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- $fq_class_name . ' is marked deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
-
- if ($context->self
- && !$context->collect_initializations
- && !$context->collect_mutations
- && !NamespaceAnalyzer::isWithinAny($context->self, $storage->internal)
- ) {
- IssueBuffer::maybeAdd(
- new InternalClass(
- $fq_class_name . ' is internal to ' . InternalClass::listToPhrase($storage->internal)
- . ' but called from ' . $context->self,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $method_id = new MethodIdentifier($fq_class_name, '__construct');
-
- if ($codebase->methods->methodExists(
- $method_id,
- $context->calling_method_id,
- $codebase->collect_locations ? new CodeLocation($statements_analyzer->getSource(), $stmt) : null,
- $statements_analyzer,
- $statements_analyzer->getFilePath()
- )) {
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- ArgumentMapPopulator::recordArgumentPositions(
- $statements_analyzer,
- $stmt,
- $codebase,
- (string)$method_id
- );
- }
-
- $template_result = new TemplateResult([], []);
-
- if (self::checkMethodArgs(
- $method_id,
- $stmt->getArgs(),
- $template_result,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- ) === false) {
- return;
- }
-
- if (MethodVisibilityAnalyzer::analyze(
- $method_id,
- $context,
- $statements_analyzer->getSource(),
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return;
- }
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id) {
- $method_storage = $codebase->methods->getStorage($declaring_method_id);
-
- $caller_identifier = $statements_analyzer->getFullyQualifiedFunctionMethodOrNamespaceName() ?: '';
- if (!NamespaceAnalyzer::isWithinAny($caller_identifier, $method_storage->internal)) {
- IssueBuffer::maybeAdd(
- new InternalMethod(
- 'Constructor ' . $codebase->methods->getCasedMethodId($declaring_method_id)
- . ' is internal to ' . InternalClass::listToPhrase($method_storage->internal)
- . ' but called from ' . ($caller_identifier ?: 'root namespace'),
- new CodeLocation($statements_analyzer, $stmt),
- (string) $method_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$method_storage->external_mutation_free && !$context->inside_throw) {
- if ($context->pure) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call an impure constructor from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- $generic_params = $template_result->lower_bounds;
-
- if ($method_storage->assertions && $stmt->class instanceof PhpParser\Node\Name) {
- self::applyAssertionsToContext(
- $stmt->class,
- null,
- $method_storage->assertions,
- $stmt->getArgs(),
- $generic_params,
- $context,
- $statements_analyzer
- );
- }
-
- if ($method_storage->if_true_assertions) {
- $statements_analyzer->node_data->setIfTrueAssertions(
- $stmt,
- array_map(
- function ($assertion) use ($generic_params, $codebase) {
- return $assertion->getUntemplatedCopy($generic_params, null, $codebase);
- },
- $method_storage->if_true_assertions
- )
- );
- }
-
- if ($method_storage->if_false_assertions) {
- $statements_analyzer->node_data->setIfFalseAssertions(
- $stmt,
- array_map(
- function ($assertion) use ($generic_params, $codebase) {
- return $assertion->getUntemplatedCopy($generic_params, null, $codebase);
- },
- $method_storage->if_false_assertions
- )
- );
- }
- }
-
- $generic_param_types = null;
-
- if ($storage->template_types) {
- foreach ($storage->template_types as $template_name => $base_type) {
- if (isset($template_result->lower_bounds[$template_name][$fq_class_name])) {
- $generic_param_type = TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $template_result->lower_bounds[$template_name][$fq_class_name],
- $codebase
- );
- } elseif ($storage->template_extended_params && $template_result->lower_bounds) {
- $generic_param_type = self::getGenericParamForOffset(
- $fq_class_name,
- $template_name,
- $storage->template_extended_params,
- array_map(
- function ($type_map) use ($codebase) {
- return array_map(
- function ($bounds) use ($codebase) {
- return TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $bounds,
- $codebase
- );
- },
- $type_map
- );
- },
- $template_result->lower_bounds
- )
- );
- } else {
- if ($fq_class_name === 'SplObjectStorage') {
- $generic_param_type = Type::getEmpty();
- } else {
- $generic_param_type = clone array_values($base_type)[0];
- }
- }
-
- $generic_param_type->had_template = true;
-
- $generic_param_types[] = $generic_param_type;
- }
- }
-
- if ($generic_param_types) {
- $result_atomic_type = new TGenericObject(
- $fq_class_name,
- $generic_param_types
- );
-
- $result_atomic_type->was_static = $from_static;
-
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union([$result_atomic_type])
- );
- }
- } elseif ($stmt->getArgs()) {
- IssueBuffer::maybeAdd(
- new TooManyArguments(
- 'Class ' . $fq_class_name . ' has no __construct, but arguments were passed',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name . '::__construct'
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($storage->template_types) {
- $result_atomic_type = new TGenericObject(
- $fq_class_name,
- array_values(
- array_map(
- function ($map) {
- return clone reset($map);
- },
- $storage->template_types
- )
- )
- );
-
- $result_atomic_type->was_static = $from_static;
-
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union([$result_atomic_type])
- );
- }
-
- if ($storage->external_mutation_free) {
- $stmt->setAttribute('external_mutation_free', true);
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
-
- if ($stmt_type) {
- $stmt_type->reference_free = true;
- }
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- && ($stmt_type = $statements_analyzer->node_data->getType($stmt))
- ) {
- $code_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $method_storage = null;
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id) {
- $method_storage = $codebase->methods->getStorage($declaring_method_id);
- }
-
- if ($storage->external_mutation_free
- || ($method_storage && $method_storage->specialize_call)
- ) {
- $method_source = DataFlowNode::getForMethodReturn(
- (string)$method_id,
- $fq_class_name . '::__construct',
- $storage->location,
- $code_location
- );
- } else {
- $method_source = DataFlowNode::getForMethodReturn(
- (string)$method_id,
- $fq_class_name . '::__construct',
- $storage->location
- );
- }
-
- $statements_analyzer->data_flow_graph->addNode($method_source);
-
- $stmt_type->parent_nodes = [$method_source->id => $method_source];
- }
- }
-
- private static function analyzeConstructorExpression(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- Context $context,
- PhpParser\Node\Expr\New_ $stmt,
- PhpParser\Node\Expr $stmt_class,
- Config $config,
- ?string &$fq_class_name,
- bool &$can_extend
- ): void {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt_class, $context);
- $context->inside_general_use = $was_inside_general_use;
-
- $stmt_class_type = $statements_analyzer->node_data->getType($stmt_class);
-
- if (!$stmt_class_type) {
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- return;
- }
-
- $has_single_class = $stmt_class_type->isSingleStringLiteral();
-
- if ($has_single_class) {
- $fq_class_name = $stmt_class_type->getSingleStringLiteral()->value;
- } else {
- if (self::checkMethodArgs(
- null,
- $stmt->getArgs(),
- null,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- ) === false) {
- return;
- }
- }
-
- $new_type = null;
-
- $stmt_class_types = $stmt_class_type->getAtomicTypes();
-
- while ($stmt_class_types) {
- $lhs_type_part = array_shift($stmt_class_types);
-
- if ($lhs_type_part instanceof TTemplateParam) {
- $stmt_class_types = array_merge($stmt_class_types, $lhs_type_part->as->getAtomicTypes());
- continue;
- }
-
- if ($lhs_type_part instanceof TTemplateParamClass) {
- if (!$statements_analyzer->node_data->getType($stmt)) {
- $new_type_part = new TTemplateParam(
- $lhs_type_part->param_name,
- $lhs_type_part->as_type
- ? new Union([$lhs_type_part->as_type])
- : Type::getObject(),
- $lhs_type_part->defining_class
- );
-
- if (!$lhs_type_part->as_type) {
- IssueBuffer::maybeAdd(
- new MixedMethodCall(
- 'Cannot call constructor on an unknown class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $new_type = Type::combineUnionTypes($new_type, new Union([$new_type_part]));
-
- if ($lhs_type_part->as_type
- && $codebase->classlikes->classExists($lhs_type_part->as_type->value)
- ) {
- $as_storage = $codebase->classlike_storage_provider->get(
- $lhs_type_part->as_type->value
- );
-
- if (!$as_storage->preserve_constructor_signature) {
- IssueBuffer::maybeAdd(
- new UnsafeInstantiation(
- 'Cannot safely instantiate class ' . $lhs_type_part->as_type->value
- . ' with "new $class_name" as'
- . ' its constructor might change in child classes',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($lhs_type_part->as_type) {
- $codebase->methods->methodExists(
- new MethodIdentifier(
- $lhs_type_part->as_type->value,
- '__construct'
- ),
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt) : null,
- $statements_analyzer,
- $statements_analyzer->getFilePath()
- );
- }
-
- continue;
- }
-
- if ($lhs_type_part instanceof TLiteralClassString
- || $lhs_type_part instanceof TClassString
- || $lhs_type_part instanceof TDependentGetClass
- ) {
- if (!$statements_analyzer->node_data->getType($stmt)) {
- if ($lhs_type_part instanceof TClassString) {
- $generated_type = $lhs_type_part->as_type
- ? clone $lhs_type_part->as_type
- : new TObject();
-
- if ($lhs_type_part->as_type
- && $codebase->classlikes->classExists($lhs_type_part->as_type->value)
- ) {
- $as_storage = $codebase->classlike_storage_provider->get(
- $lhs_type_part->as_type->value
- );
-
- if (!$as_storage->preserve_constructor_signature) {
- IssueBuffer::maybeAdd(
- new UnsafeInstantiation(
- 'Cannot safely instantiate class ' . $lhs_type_part->as_type->value
- . ' with "new $class_name" as'
- . ' its constructor might change in child classes',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- } elseif ($lhs_type_part instanceof TDependentGetClass) {
- $generated_type = new TObject();
-
- if ($lhs_type_part->as_type->hasObjectType()
- && $lhs_type_part->as_type->isSingle()
- ) {
- foreach ($lhs_type_part->as_type->getAtomicTypes() as $typeof_type_atomic) {
- if ($typeof_type_atomic instanceof TNamedObject) {
- $generated_type = new TNamedObject(
- $typeof_type_atomic->value
- );
- }
- }
- }
- } else {
- $generated_type = new TNamedObject(
- $lhs_type_part->value
- );
- }
-
- if ($lhs_type_part instanceof TClassString) {
- $can_extend = true;
- }
-
- if ($generated_type instanceof TObject) {
- IssueBuffer::maybeAdd(
- new MixedMethodCall(
- 'Cannot call constructor on an unknown class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $new_type = Type::combineUnionTypes($new_type, new Union([$generated_type]));
- }
-
- continue;
- }
-
- if ($lhs_type_part instanceof TString) {
- if ($config->allow_string_standin_for_class
- && !$lhs_type_part instanceof TNumericString
- ) {
- // do nothing
- } elseif (IssueBuffer::accepts(
- new InvalidStringClass(
- 'String cannot be used as a class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- // fall through
- }
- } elseif ($lhs_type_part instanceof TMixed) {
- IssueBuffer::maybeAdd(
- new MixedMethodCall(
- 'Cannot call constructor on an unknown class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($lhs_type_part instanceof TFalse
- && $stmt_class_type->ignore_falsable_issues
- ) {
- // do nothing
- } elseif ($lhs_type_part instanceof TNull
- && $stmt_class_type->ignore_nullable_issues
- ) {
- // do nothing
- } elseif (IssueBuffer::accepts(
- new UndefinedClass(
- 'Type ' . $lhs_type_part . ' cannot be called as a class',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- (string)$lhs_type_part
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- // fall through
- }
-
- $new_type = Type::combineUnionTypes($new_type, Type::getObject());
- }
-
- if (!$has_single_class) {
- if ($new_type) {
- $statements_analyzer->node_data->setType($stmt, $new_type);
- }
-
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- );
-
- return;
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php
deleted file mode 100644
index da43d41..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticCallAnalyzer.php
+++ /dev/null
@@ -1,382 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod\AtomicStaticCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\TaintSource;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\NonStaticSelfCall;
-use Psalm\Issue\ParentNotFound;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function count;
-use function in_array;
-use function md5;
-use function strtolower;
-
-/**
- * @internal
- */
-class StaticCallAnalyzer extends CallAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- Context $context
- ): bool {
- $method_id = null;
-
- $lhs_type = null;
-
- $codebase = $statements_analyzer->getCodebase();
- $source = $statements_analyzer->getSource();
-
- $config = $codebase->config;
-
- if ($stmt->class instanceof PhpParser\Node\Name) {
- $fq_class_name = null;
-
- if (count($stmt->class->parts) === 1
- && in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
- ) {
- if ($stmt->class->parts[0] === 'parent') {
- $child_fq_class_name = $context->self;
-
- $class_storage = $child_fq_class_name
- ? $codebase->classlike_storage_provider->get($child_fq_class_name)
- : null;
-
- if (!$class_storage || !$class_storage->parent_class) {
- if (IssueBuffer::accepts(
- new ParentNotFound(
- 'Cannot call method on parent as this class does not extend another',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- $fq_class_name = $class_storage->parent_class;
-
- $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $fq_class_name = $class_storage->name;
- } elseif ($context->self) {
- if ($stmt->class->parts[0] === 'static' && isset($context->vars_in_scope['$this'])) {
- $fq_class_name = (string) $context->vars_in_scope['$this'];
- $lhs_type = clone $context->vars_in_scope['$this'];
- } else {
- $fq_class_name = $context->self;
- }
- } else {
- if (IssueBuffer::accepts(
- new NonStaticSelfCall(
- 'Cannot use ' . $stmt->class->parts[0] . ' outside class context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- if ($context->isPhantomClass($fq_class_name)) {
- return true;
- }
- } elseif ($context->check_classes) {
- $aliases = $statements_analyzer->getAliases();
-
- if ($context->calling_method_id
- && !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
- ) {
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $context->calling_method_id,
- 'use:' . $stmt->class->parts[0] . ':' . md5($statements_analyzer->getFilePath()),
- false
- );
- }
-
- $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $aliases
- );
-
- if ($context->isPhantomClass($fq_class_name)) {
- return true;
- }
-
- $does_class_exist = false;
-
- if ($context->self) {
- $self_storage = $codebase->classlike_storage_provider->get($context->self);
-
- if (isset($self_storage->used_traits[strtolower($fq_class_name)])) {
- $fq_class_name = $context->self;
- $does_class_exist = true;
- }
- }
-
- if (!isset($context->phantom_classes[strtolower($fq_class_name)])
- && !$does_class_exist
- ) {
- $does_class_exist = ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($source, $stmt->class),
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $context->self
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $context->calling_method_id
- : null,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(false, false, false, true)
- );
- }
-
- if (!$does_class_exist) {
- return $does_class_exist !== false;
- }
- }
-
- if ($codebase->store_node_types
- && $fq_class_name
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $fq_class_name
- );
- }
-
- if ($fq_class_name && !$lhs_type) {
- $lhs_type = new Union([new TNamedObject($fq_class_name)]);
- }
- } else {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt->class, $context);
- $context->inside_general_use = $was_inside_general_use;
- $lhs_type = $statements_analyzer->node_data->getType($stmt->class) ?? Type::getMixed();
- }
-
- if (!$lhs_type) {
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return false;
- }
-
- return true;
- }
-
- $has_mock = false;
- $moved_call = false;
- $has_existing_method = false;
-
- foreach ($lhs_type->getAtomicTypes() as $lhs_type_part) {
- AtomicStaticCallAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- $lhs_type_part,
- $lhs_type->ignore_nullable_issues,
- $moved_call,
- $has_mock,
- $has_existing_method
- );
- }
-
- if (!$stmt->isFirstClassCallable() && !$has_existing_method) {
- return self::checkMethodArgs(
- $method_id,
- $stmt->getArgs(),
- null,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- );
- }
-
- if (!$config->remember_property_assignments_after_call && !$context->collect_initializations) {
- $context->removeMutableObjectVars();
- }
-
- if (!$statements_analyzer->node_data->getType($stmt)) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- return true;
- }
-
- public static function taintReturnType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- MethodIdentifier $method_id,
- string $cased_method_id,
- Union $return_type_candidate,
- ?MethodStorage $method_storage,
- ?TemplateResult $template_result,
- ?Context $context = null
- ): void {
- if (!$statements_analyzer->data_flow_graph) {
- return;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- return;
- }
-
- $node_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $method_location = $method_storage
- ? ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ? ($method_storage->signature_return_type_location ?: $method_storage->location)
- : ($method_storage->return_type_location ?: $method_storage->location))
- : null;
-
- if ($method_storage && $method_storage->specialize_call) {
- $method_source = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $method_location,
- $node_location
- );
- } else {
- $method_source = DataFlowNode::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $method_location
- );
- }
-
- $statements_analyzer->data_flow_graph->addNode($method_source);
-
- $codebase = $statements_analyzer->getCodebase();
-
- $conditionally_removed_taints = [];
-
- if ($method_storage && $template_result) {
- foreach ($method_storage->conditionally_removed_taints as $conditionally_removed_taint) {
- $conditionally_removed_taint = clone $conditionally_removed_taint;
-
- TemplateInferredTypeReplacer::replace(
- $conditionally_removed_taint,
- $template_result,
- $codebase
- );
-
- $expanded_type = TypeExpander::expandUnion(
- $statements_analyzer->getCodebase(),
- $conditionally_removed_taint,
- null,
- null,
- null,
- true,
- true
- );
-
- foreach ($expanded_type->getLiteralStrings() as $literal_string) {
- $conditionally_removed_taints[] = $literal_string->value;
- }
- }
- }
-
- $added_taints = [];
- $removed_taints = [];
-
- if ($context) {
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
- }
-
- if ($conditionally_removed_taints && $method_location) {
- $assignment_node = DataFlowNode::getForAssignment(
- $method_id . '-escaped',
- $method_location,
- $method_source->specialization_key
- );
-
- $statements_analyzer->data_flow_graph->addPath(
- $method_source,
- $assignment_node,
- 'conditionally-escaped',
- $added_taints,
- array_merge($conditionally_removed_taints, $removed_taints)
- );
-
- $return_type_candidate->parent_nodes[$assignment_node->id] = $assignment_node;
- } else {
- $return_type_candidate->parent_nodes = [$method_source->id => $method_source];
- }
-
- if ($method_storage
- && $method_storage->taint_source_types
- && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- ) {
- $method_node = TaintSource::getForMethodReturn(
- (string) $method_id,
- $cased_method_id,
- $method_storage->signature_return_type_location ?: $method_storage->location
- );
-
- $method_node->taints = $method_storage->taint_source_types;
-
- $statements_analyzer->data_flow_graph->addSource($method_node);
- }
-
- if ($method_storage && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- FunctionCallReturnTypeFetcher::taintUsingFlows(
- $statements_analyzer,
- $method_storage,
- $statements_analyzer->data_flow_graph,
- (string) $method_id,
- $stmt->getArgs(),
- $node_location,
- $method_source,
- array_merge($method_storage->removed_taints, $removed_taints),
- $added_taints
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php
deleted file mode 100644
index 673dc56..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/AtomicStaticCallAnalyzer.php
+++ /dev/null
@@ -1,1136 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod;
-
-use Exception;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\MethodAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentMapPopulator;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodVisibilityAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\DeprecatedClass;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\InvalidStringClass;
-use Psalm\Issue\MixedMethodCall;
-use Psalm\Issue\UndefinedClass;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualArray;
-use Psalm\Node\Expr\VirtualArrayItem;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Scalar\VirtualString;
-use Psalm\Node\VirtualArg;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_filter;
-use function array_map;
-use function assert;
-use function count;
-use function in_array;
-use function strtolower;
-
-class AtomicStaticCallAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- Context $context,
- Atomic $lhs_type_part,
- bool $ignore_nullable_issues,
- bool &$moved_call,
- bool &$has_mock,
- bool &$has_existing_method
- ): void {
- $intersection_types = [];
-
- if ($lhs_type_part instanceof TNamedObject) {
- $fq_class_name = $lhs_type_part->value;
-
- if (!ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer, $stmt->class),
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $context->self
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $context->calling_method_id
- : null,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(
- $stmt->class instanceof PhpParser\Node\Name
- && count($stmt->class->parts) === 1
- && in_array(strtolower($stmt->class->parts[0]), ['self', 'static'], true)
- )
- )) {
- return;
- }
-
- $intersection_types = $lhs_type_part->extra_types;
- } elseif ($lhs_type_part instanceof TClassString
- && $lhs_type_part->as_type
- ) {
- $fq_class_name = $lhs_type_part->as_type->value;
-
- if (!ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer, $stmt->class),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues()
- )) {
- return;
- }
-
- $intersection_types = $lhs_type_part->as_type->extra_types;
- } elseif ($lhs_type_part instanceof TDependentGetClass
- && !$lhs_type_part->as_type->hasObject()
- ) {
- $fq_class_name = 'object';
-
- if ($lhs_type_part->as_type->hasObjectType()
- && $lhs_type_part->as_type->isSingle()
- ) {
- foreach ($lhs_type_part->as_type->getAtomicTypes() as $typeof_type_atomic) {
- if ($typeof_type_atomic instanceof TNamedObject) {
- $fq_class_name = $typeof_type_atomic->value;
- }
- }
- }
-
- if ($fq_class_name === 'object') {
- return;
- }
- } elseif ($lhs_type_part instanceof TLiteralClassString) {
- $fq_class_name = $lhs_type_part->value;
-
- if (!ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer, $stmt->class),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues()
- )) {
- return;
- }
- } elseif ($lhs_type_part instanceof TTemplateParam
- && !$lhs_type_part->as->isMixed()
- && !$lhs_type_part->as->hasObject()
- ) {
- $fq_class_name = null;
-
- foreach ($lhs_type_part->as->getAtomicTypes() as $generic_param_type) {
- if (!$generic_param_type instanceof TNamedObject) {
- return;
- }
-
- $fq_class_name = $generic_param_type->value;
- break;
- }
-
- if (!$fq_class_name) {
- IssueBuffer::maybeAdd(
- new UndefinedClass(
- 'Type ' . $lhs_type_part->as . ' cannot be called as a class',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- (string) $lhs_type_part
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
- } else {
- self::handleNonObjectCall(
- $statements_analyzer,
- $stmt,
- $context,
- $lhs_type_part,
- $ignore_nullable_issues
- );
-
- return;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
-
- $is_mock = ExpressionAnalyzer::isMock($fq_class_name);
-
- $has_mock = $has_mock || $is_mock;
-
- if ($stmt->name instanceof PhpParser\Node\Identifier && !$is_mock) {
- self::handleNamedCall(
- $statements_analyzer,
- $stmt,
- $stmt->name,
- $context,
- $lhs_type_part,
- $intersection_types ?: [],
- $fq_class_name,
- $moved_call,
- $has_existing_method
- );
- } else {
- if ($stmt->name instanceof PhpParser\Node\Expr) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context);
-
- $context->inside_general_use = $was_inside_general_use;
- }
-
- if (!$context->ignore_variable_method) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($fq_class_name) . '::',
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- if ($stmt->isFirstClassCallable()) {
- $return_type_candidate = null;
- if (!$stmt->name instanceof PhpParser\Node\Identifier) {
- $method_name_type = $statements_analyzer->node_data->getType($stmt->name);
- if ($method_name_type && $method_name_type->isSingleStringLiteral()) {
- $method_identifier = new MethodIdentifier(
- $fq_class_name,
- strtolower($method_name_type->getSingleStringLiteral()->value)
- );
- //the call to methodExists will register that the method was called from somewhere
- if ($codebase->methods->methodExists(
- $method_identifier,
- $context->calling_method_id,
- null,
- $statements_analyzer,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )) {
- $method_storage = $codebase->methods->getStorage($method_identifier);
-
- $return_type_candidate = new Union([new TClosure(
- 'Closure',
- $method_storage->params,
- $method_storage->return_type,
- $method_storage->pure
- )]);
- }
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, $return_type_candidate ?? Type::getClosure());
-
- return;
- }
-
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return;
- }
- }
-
- if ($codebase->alter_code
- && $fq_class_name
- && !$moved_call
- && $stmt->class instanceof PhpParser\Node\Name
- && !in_array($stmt->class->parts[0], ['parent', 'static'])
- ) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id,
- false,
- $stmt->class->parts[0] === 'self'
- );
- }
- }
-
- /**
- * @psalm-suppress UnusedReturnValue not used but seems important
- * @psalm-suppress ComplexMethod to be refactored
- */
- private static function handleNamedCall(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- PhpParser\Node\Identifier $stmt_name,
- Context $context,
- Atomic $lhs_type_part,
- array $intersection_types,
- string $fq_class_name,
- bool &$moved_call,
- bool &$has_existing_method
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
-
- $method_name_lc = strtolower($stmt_name->name);
- $method_id = new MethodIdentifier($fq_class_name, $method_name_lc);
-
- $cased_method_id = $fq_class_name . '::' . $stmt_name->name;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- ArgumentMapPopulator::recordArgumentPositions(
- $statements_analyzer,
- $stmt,
- $codebase,
- (string) $method_id
- );
- }
-
- if ($intersection_types
- && !$codebase->methods->methodExists($method_id)
- ) {
- foreach ($intersection_types as $intersection_type) {
- if (!$intersection_type instanceof TNamedObject) {
- continue;
- }
-
- $intersection_method_id = new MethodIdentifier(
- $intersection_type->value,
- $method_name_lc
- );
-
- if ($codebase->methods->methodExists($intersection_method_id)) {
- $method_id = $intersection_method_id;
- $cased_method_id = $intersection_type->value . '::' . $stmt_name->name;
- $fq_class_name = $intersection_type->value;
- break;
- }
- }
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $naive_method_exists = $codebase->methods->methodExists(
- $method_id,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $context->calling_method_id
- : null,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer, $stmt_name)
- : null,
- $statements_analyzer,
- $statements_analyzer->getFilePath(),
- false,
- $context->insideUse()
- );
-
- $fake_method_exists = false;
-
- if (!$naive_method_exists
- && $codebase->methods->existence_provider->has($fq_class_name)
- ) {
- $method_exists = $codebase->methods->existence_provider->doesMethodExist(
- $fq_class_name,
- $method_id->method_name,
- $statements_analyzer,
- null
- );
-
- if ($method_exists) {
- $fake_method_exists = true;
- }
- }
-
- if ($stmt->isFirstClassCallable()) {
- $method_storage = ($class_storage->methods[$method_name_lc] ??
- ($class_storage->pseudo_static_methods[$method_name_lc] ?? null));
-
- if ($method_storage) {
- $return_type_candidate = new Union([new TClosure(
- 'Closure',
- $method_storage->params,
- $method_storage->return_type,
- $method_storage->pure
- )]);
- } else {
- $return_type_candidate = Type::getClosure();
- }
-
- $statements_analyzer->node_data->setType($stmt, $return_type_candidate);
-
- return true;
- }
-
- $args = $stmt->getArgs();
-
- if (!$naive_method_exists
- && $class_storage->mixin_declaring_fqcln
- && $class_storage->namedMixins
- ) {
- foreach ($class_storage->namedMixins as $mixin) {
- $new_method_id = new MethodIdentifier(
- $mixin->value,
- $method_name_lc
- );
-
- if ($codebase->methods->methodExists(
- $new_method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer, $stmt_name)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )) {
- $mixin_candidates = [];
- foreach ($class_storage->templatedMixins as $mixin_candidate) {
- $mixin_candidates[] = clone $mixin_candidate;
- }
-
- foreach ($class_storage->namedMixins as $mixin_candidate) {
- $mixin_candidates[] = clone $mixin_candidate;
- }
-
- $mixin_candidates_no_generic = array_filter($mixin_candidates, function ($check): bool {
- return !($check instanceof TGenericObject);
- });
-
- // $mixin_candidates_no_generic will only be empty when there are TGenericObject entries.
- // In that case, Union will be initialized with an empty array but
- // replaced with non-empty types in the following loop.
- /** @psalm-suppress ArgumentTypeCoercion */
- $mixin_candidate_type = new Union($mixin_candidates_no_generic);
-
- foreach ($mixin_candidates as $tGenericMixin) {
- if (!($tGenericMixin instanceof TGenericObject)) {
- continue;
- }
-
- $mixin_declaring_class_storage = $codebase->classlike_storage_provider->get(
- $class_storage->mixin_declaring_fqcln
- );
-
- $new_mixin_candidate_type = AtomicPropertyFetchAnalyzer::localizePropertyType(
- $codebase,
- new Union([$lhs_type_part]),
- $tGenericMixin,
- $class_storage,
- $mixin_declaring_class_storage
- );
-
- foreach ($mixin_candidate_type->getAtomicTypes() as $type) {
- $new_mixin_candidate_type->addType($type);
- }
-
- $mixin_candidate_type = $new_mixin_candidate_type;
- }
-
- $new_lhs_type = TypeExpander::expandUnion(
- $codebase,
- $mixin_candidate_type,
- $fq_class_name,
- $fq_class_name,
- $class_storage->parent_class,
- true,
- false,
- $class_storage->final
- );
-
- $mixin_context = clone $context;
- $mixin_context->vars_in_scope['$__tmp_mixin_var__'] = $new_lhs_type;
-
- return self::forwardCallToInstanceMethod(
- $statements_analyzer,
- $stmt,
- $stmt_name,
- $mixin_context,
- '__tmp_mixin_var__',
- true
- );
- }
- }
- }
-
- $config = $codebase->config;
-
- $found_method_and_class_storage = self::findPseudoMethodAndClassStorages(
- $codebase,
- $class_storage,
- $method_name_lc
- );
-
- if (!$naive_method_exists
- || !MethodAnalyzer::isMethodVisible(
- $method_id,
- $context,
- $statements_analyzer->getSource()
- )
- || $fake_method_exists
- || ($found_method_and_class_storage
- && ($config->use_phpdoc_method_without_magic_or_parent || $class_storage->parent_class))
- ) {
- $callstatic_id = new MethodIdentifier(
- $fq_class_name,
- '__callstatic'
- );
-
- if ($codebase->methods->methodExists(
- $callstatic_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer, $stmt_name)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath(),
- true,
- $context->insideUse()
- )) {
- $callstatic_appearing_id = $codebase->methods->getAppearingMethodId($callstatic_id);
- assert($callstatic_appearing_id !== null);
- $callstatic_pure = false;
- $callstatic_mutation_free = false;
- if ($codebase->methods->hasStorage($callstatic_appearing_id)) {
- $callstatic_storage = $codebase->methods->getStorage($callstatic_appearing_id);
- $callstatic_pure = $callstatic_storage->pure;
- $callstatic_mutation_free = $callstatic_storage->mutation_free;
- }
- if ($codebase->methods->return_type_provider->has($fq_class_name)) {
- $return_type_candidate = $codebase->methods->return_type_provider->getReturnType(
- $statements_analyzer,
- $method_id->fq_class_name,
- $method_id->method_name,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt_name),
- null,
- null,
- strtolower($stmt_name->name)
- );
-
- if ($return_type_candidate) {
- CallAnalyzer::checkMethodArgs(
- $method_id,
- $stmt->getArgs(),
- null,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- );
-
- $statements_analyzer->node_data->setType($stmt, $return_type_candidate);
-
- return true;
- }
- }
-
- if ($found_method_and_class_storage) {
- [$pseudo_method_storage, $defining_class_storage] = $found_method_and_class_storage;
-
- if (self::checkPseudoMethod(
- $statements_analyzer,
- $stmt,
- $method_id,
- $fq_class_name,
- $args,
- $defining_class_storage,
- $pseudo_method_storage,
- $context
- ) === false
- ) {
- return false;
- }
-
- if (!$context->inside_throw) {
- if ($context->pure && !$callstatic_pure) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call an impure method from a pure context',
- new CodeLocation($statements_analyzer, $stmt_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->mutation_free && !$callstatic_mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- && !$callstatic_pure
- ) {
- if (!$callstatic_mutation_free) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- }
-
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- if ($pseudo_method_storage->return_type) {
- return true;
- }
- } else {
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $args,
- null,
- null,
- true,
- $context
- ) === false) {
- return false;
- }
- }
-
- $array_values = array_map(
- function (PhpParser\Node\Arg $arg): PhpParser\Node\Expr\ArrayItem {
- return new VirtualArrayItem(
- $arg->value,
- null,
- false,
- $arg->getAttributes()
- );
- },
- $args
- );
-
- $args = [
- new VirtualArg(
- new VirtualString((string) $method_id, $stmt_name->getAttributes()),
- false,
- false,
- $stmt_name->getAttributes()
- ),
- new VirtualArg(
- new VirtualArray($array_values, $stmt->getAttributes()),
- false,
- false,
- $stmt->getAttributes()
- ),
- ];
-
- $method_id = new MethodIdentifier(
- $fq_class_name,
- '__callstatic'
- );
- } elseif ($found_method_and_class_storage
- && ($config->use_phpdoc_method_without_magic_or_parent || $class_storage->parent_class)
- ) {
- [$pseudo_method_storage, $defining_class_storage] = $found_method_and_class_storage;
-
- if (self::checkPseudoMethod(
- $statements_analyzer,
- $stmt,
- $method_id,
- $fq_class_name,
- $args,
- $defining_class_storage,
- $pseudo_method_storage,
- $context
- ) === false
- ) {
- return false;
- }
-
- if ($pseudo_method_storage->return_type) {
- return true;
- }
- } elseif ($stmt->class instanceof PhpParser\Node\Name && $stmt->class->parts[0] === 'parent'
- && !$codebase->methodExists($method_id)
- && !$statements_analyzer->isStatic()
- ) {
- // In case of parent::xxx() call on instance method context (i.e. not static context)
- // with nonexistent method, we try to forward to instance method call for resolve pseudo method.
-
- // Use parent type as static type for the method call
- $tmp_context = clone $context;
- $tmp_context->vars_in_scope['$__tmp_parent_var__'] = new Union([$lhs_type_part]);
-
- if (self::forwardCallToInstanceMethod(
- $statements_analyzer,
- $stmt,
- $stmt_name,
- $tmp_context,
- '__tmp_parent_var__'
- ) === false) {
- return false;
- }
-
- unset($tmp_context);
-
- // Resolve actual static return type according to caller (i.e. $this) static type
- if (isset($context->vars_in_scope['$this'])
- && $method_call_type = $statements_analyzer->node_data->getType($stmt)
- ) {
- $method_call_type = clone $method_call_type;
-
- foreach ($method_call_type->getAtomicTypes() as $name => $type) {
- if ($type instanceof TNamedObject && $type->was_static && $type->value === $fq_class_name) {
- // Replace parent&static type to actual static type
- $method_call_type->removeType($name);
- $method_call_type->addType($context->vars_in_scope['$this']->getSingleAtomic());
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, $method_call_type);
- }
-
- return true;
- }
-
- if (!$context->check_methods) {
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return false;
- }
-
- return true;
- }
- }
-
- $does_method_exist = MethodAnalyzer::checkMethodExists(
- $codebase,
- $method_id,
- new CodeLocation($statements_analyzer, $stmt),
- $statements_analyzer->getSuppressedIssues(),
- $context->calling_method_id
- );
-
- if (!$does_method_exist) {
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $stmt->getArgs(),
- null,
- null,
- true,
- $context
- ) === false) {
- return false;
- }
-
- if ($codebase->alter_code && $fq_class_name && !$moved_call) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id
- );
- }
-
- return true;
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- if ($class_storage->deprecated && $fq_class_name !== $context->self) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- $fq_class_name . ' is marked deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($context->self && ! NamespaceAnalyzer::isWithinAny($context->self, $class_storage->internal)) {
- IssueBuffer::maybeAdd(
- new InternalClass(
- $fq_class_name . ' is internal to ' . InternalClass::listToPhrase($class_storage->internal)
- . ' but called from ' . $context->self,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (MethodVisibilityAnalyzer::analyze(
- $method_id,
- $context,
- $statements_analyzer->getSource(),
- new CodeLocation($statements_analyzer, $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return false;
- }
-
- if ((!$stmt->class instanceof PhpParser\Node\Name
- || $stmt->class->parts[0] !== 'parent'
- || $statements_analyzer->isStatic())
- && (
- !$context->self
- || $statements_analyzer->isStatic()
- || !$codebase->classExtends($context->self, $fq_class_name)
- )
- ) {
- if (MethodAnalyzer::checkStatic(
- $method_id,
- ($stmt->class instanceof PhpParser\Node\Name
- && strtolower($stmt->class->parts[0]) === 'self')
- || $context->self === $fq_class_name,
- !$statements_analyzer->isStatic(),
- $codebase,
- new CodeLocation($statements_analyzer, $stmt),
- $statements_analyzer->getSuppressedIssues(),
- $is_dynamic_this_method
- ) === false) {
- // fall through
- }
-
- if ($is_dynamic_this_method) {
- return self::forwardCallToInstanceMethod(
- $statements_analyzer,
- $stmt,
- $stmt_name,
- $context
- );
- }
- }
-
- $has_existing_method = true;
-
- ExistingAtomicStaticCallAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $stmt_name,
- $args,
- $context,
- $lhs_type_part,
- $method_id,
- $cased_method_id,
- $class_storage,
- $moved_call
- );
-
- return true;
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- * @return false|null
- */
- private static function checkPseudoMethod(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- MethodIdentifier $method_id,
- string $static_fq_class_name,
- array $args,
- ClassLikeStorage $class_storage,
- MethodStorage $pseudo_method_storage,
- Context $context
- ): ?bool {
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $args,
- $pseudo_method_storage->params,
- (string) $method_id,
- true,
- $context
- ) === false) {
- return false;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (ArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $args,
- $method_id,
- $pseudo_method_storage->params,
- $pseudo_method_storage,
- null,
- null,
- new CodeLocation($statements_analyzer, $stmt),
- $context
- ) === false) {
- return false;
- }
-
- $method_storage = null;
-
- if ($statements_analyzer->data_flow_graph) {
- try {
- $method_storage = $codebase->methods->getStorage($method_id);
-
- ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $args,
- $method_storage->params,
- (string) $method_id,
- true,
- $context
- );
-
- ArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $args,
- $method_id,
- $method_storage->params,
- $method_storage,
- null,
- null,
- new CodeLocation($statements_analyzer, $stmt),
- $context
- );
- } catch (Exception $e) {
- // do nothing
- }
- }
-
- if ($pseudo_method_storage->return_type) {
- $return_type_candidate = clone $pseudo_method_storage->return_type;
-
- $return_type_candidate = TypeExpander::expandUnion(
- $statements_analyzer->getCodebase(),
- $return_type_candidate,
- $class_storage->name,
- $static_fq_class_name,
- $class_storage->parent_class
- );
-
- if ($method_storage) {
- StaticCallAnalyzer::taintReturnType(
- $statements_analyzer,
- $stmt,
- $method_id,
- (string) $method_id,
- $return_type_candidate,
- $method_storage,
- null,
- $context
- );
- }
-
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
-
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes(
- $return_type_candidate,
- $stmt_type
- )
- );
- }
-
- return null;
- }
-
- public static function handleNonObjectCall(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- Context $context,
- Atomic $lhs_type_part,
- bool $ignore_nullable_issues
- ): void {
- $codebase = $statements_analyzer->getCodebase();
- $config = $codebase->config;
-
- if ($lhs_type_part instanceof TMixed
- || $lhs_type_part instanceof TTemplateParam
- || $lhs_type_part instanceof TClassString
- || $lhs_type_part instanceof TObject
- ) {
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($stmt->name->name),
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- IssueBuffer::maybeAdd(
- new MixedMethodCall(
- 'Cannot call method on an unknown class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($lhs_type_part instanceof TString) {
- if ($config->allow_string_standin_for_class
- && !$lhs_type_part instanceof TNumericString
- ) {
- return;
- }
-
- IssueBuffer::maybeAdd(
- new InvalidStringClass(
- 'String cannot be used as a class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- if ($lhs_type_part instanceof TNull
- && $ignore_nullable_issues
- ) {
- return;
- }
-
- IssueBuffer::maybeAdd(
- new UndefinedClass(
- 'Type ' . $lhs_type_part . ' cannot be called as a class',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- (string) $lhs_type_part
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- /**
- * Try to find matching pseudo method over ancestors (including interfaces).
- *
- * Returns the pseudo method if exists, with its defining class storage.
- * If the method is not declared, null is returned.
- *
- * @param Codebase $codebase
- * @param ClassLikeStorage $static_class_storage The called class
- * @param lowercase-string $method_name_lc
- *
- * @return array{MethodStorage, ClassLikeStorage}|null
- */
- private static function findPseudoMethodAndClassStorages(
- Codebase $codebase,
- ClassLikeStorage $static_class_storage,
- string $method_name_lc
- ): ?array {
- if ($pseudo_method_storage = $static_class_storage->pseudo_static_methods[$method_name_lc] ?? null) {
- return [$pseudo_method_storage, $static_class_storage];
- }
-
- $ancestors = $static_class_storage->class_implements + $static_class_storage->parent_classes;
-
- foreach ($ancestors as $fq_class_name => $_) {
- $class_storage = $codebase->classlikes->getStorageFor($fq_class_name);
-
- if ($class_storage && isset($class_storage->pseudo_static_methods[$method_name_lc])) {
- return [
- $class_storage->pseudo_static_methods[$method_name_lc],
- $class_storage
- ];
- }
- }
-
- return null;
- }
-
- /**
- * Forward static call to instance call, using `VirtualMethodCall` and `MethodCallAnalyzer::analyze()`
- * The resolved method return type will be set as type of the $stmt node.
- *
- * @param StatementsAnalyzer $statements_analyzer
- * @param PhpParser\Node\Expr\StaticCall $stmt
- * @param PhpParser\Node\Identifier $stmt_name
- * @param Context $context
- * @param string $virtual_var_name Temporary var name to use for create the fake MethodCall statement.
- * @param bool $always_set_node_type If true, when the method has no declared typed, mixed will be set on node.
- *
- * @return bool Result of analysis. False if the call is invalid.
- *
- * @see MethodCallAnalyzer::analyze()
- */
- private static function forwardCallToInstanceMethod(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- PhpParser\Node\Identifier $stmt_name,
- Context $context,
- string $virtual_var_name = 'this',
- bool $always_set_node_type = false
- ): bool {
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_method_call_expr = new VirtualMethodCall(
- new VirtualVariable($virtual_var_name, $stmt->class->getAttributes()),
- $stmt_name,
- $stmt->getArgs(),
- $stmt->getAttributes()
- );
-
- if (MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call_expr,
- $context
- ) === false) {
- return false;
- }
-
- $fake_method_call_type = $statements_analyzer->node_data->getType($fake_method_call_expr);
-
- $statements_analyzer->node_data = $old_data_provider;
-
- if ($fake_method_call_type) {
- $statements_analyzer->node_data->setType($stmt, $fake_method_call_type);
- } elseif ($always_set_node_type) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php
deleted file mode 100644
index 3785023..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php
+++ /dev/null
@@ -1,642 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallProhibitionAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateBound;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\AbstractMethodCall;
-use Psalm\Issue\ImpureMethodCall;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
-use Psalm\Storage\Assertion;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-
-use function array_map;
-use function count;
-use function explode;
-use function in_array;
-use function is_string;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-
-class ExistingAtomicStaticCallAnalyzer
-{
- /**
- * @param list<PhpParser\Node\Arg> $args
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticCall $stmt,
- PhpParser\Node\Identifier $stmt_name,
- array $args,
- Context $context,
- Atomic $lhs_type_part,
- MethodIdentifier $method_id,
- string $cased_method_id,
- ClassLikeStorage $class_storage,
- bool &$moved_call
- ): void {
- $fq_class_name = $method_id->fq_class_name;
- $method_name_lc = $method_id->method_name;
-
- $codebase = $statements_analyzer->getCodebase();
- $config = $codebase->config;
-
- if (MethodCallProhibitionAnalyzer::analyze(
- $codebase,
- $context,
- $method_id,
- $statements_analyzer->getFullyQualifiedFunctionMethodOrNamespaceName(),
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- // fall through
- }
-
- if ($class_storage->user_defined
- && $context->self
- && ($context->collect_mutations || $context->collect_initializations)
- ) {
- $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
-
- if (!$appearing_method_id) {
- return;
- }
-
- $appearing_method_class_name = $appearing_method_id->fq_class_name;
-
- if ($codebase->classExtends($context->self, $appearing_method_class_name)) {
- $old_context_include_location = $context->include_location;
- $old_self = $context->self;
- $context->include_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
- $context->self = $appearing_method_class_name;
-
- $file_analyzer = $statements_analyzer->getFileAnalyzer();
-
- if ($context->collect_mutations) {
- $file_analyzer->getMethodMutations($appearing_method_id, $context);
- } else {
- // collecting initializations
- $local_vars_in_scope = [];
- $local_vars_possibly_in_scope = [];
-
- foreach ($context->vars_in_scope as $var => $_) {
- if (strpos($var, '$this->') !== 0 && $var !== '$this') {
- $local_vars_in_scope[$var] = $context->vars_in_scope[$var];
- }
- }
-
- foreach ($context->vars_possibly_in_scope as $var => $_) {
- if (strpos($var, '$this->') !== 0 && $var !== '$this') {
- $local_vars_possibly_in_scope[$var] = $context->vars_possibly_in_scope[$var];
- }
- }
-
- if (!isset($context->initialized_methods[(string) $appearing_method_id])) {
- if ($context->initialized_methods === null) {
- $context->initialized_methods = [];
- }
-
- $context->initialized_methods[(string) $appearing_method_id] = true;
-
- $file_analyzer->getMethodMutations($appearing_method_id, $context);
-
- foreach ($local_vars_in_scope as $var => $type) {
- $context->vars_in_scope[$var] = $type;
- }
-
- foreach ($local_vars_possibly_in_scope as $var => $type) {
- $context->vars_possibly_in_scope[$var] = $type;
- }
- }
- }
-
- $context->include_location = $old_context_include_location;
- $context->self = $old_self;
- }
- }
-
- $found_generic_params = ClassTemplateParamCollector::collect(
- $codebase,
- $class_storage,
- $class_storage,
- $method_name_lc,
- $lhs_type_part,
- !$statements_analyzer->isStatic() && $method_id->fq_class_name === $context->self
- );
-
- if ($found_generic_params
- && $stmt->class instanceof PhpParser\Node\Name
- && $stmt->class->parts === ['parent']
- && $context->self
- && ($self_class_storage = $codebase->classlike_storage_provider->get($context->self))
- && $self_class_storage->template_extended_params
- ) {
- foreach ($self_class_storage->template_extended_params as $template_fq_class_name => $extended_types) {
- foreach ($extended_types as $type_key => $extended_type) {
- if (isset($found_generic_params[$type_key][$template_fq_class_name])) {
- $found_generic_params[$type_key][$template_fq_class_name] = clone $extended_type;
- continue;
- }
-
- foreach ($extended_type->getAtomicTypes() as $t) {
- if ($t instanceof TTemplateParam
- && isset($found_generic_params[$t->param_name][$t->defining_class])
- ) {
- $found_generic_params[$type_key][$template_fq_class_name]
- = $found_generic_params[$t->param_name][$t->defining_class];
- } else {
- $found_generic_params[$type_key][$template_fq_class_name]
- = clone $extended_type;
- break;
- }
- }
- }
- }
- }
-
- $template_result = new TemplateResult([], $found_generic_params ?: []);
-
- if (CallAnalyzer::checkMethodArgs(
- $method_id,
- $args,
- $template_result,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer
- ) === false) {
- return;
- }
-
- $fq_class_name = $stmt->class instanceof PhpParser\Node\Name && $stmt->class->parts === ['parent']
- ? (string) $statements_analyzer->getFQCLN()
- : $fq_class_name;
-
- $self_fq_class_name = $fq_class_name;
-
- $return_type_candidate = null;
-
- if ($codebase->methods->return_type_provider->has($fq_class_name)) {
- $return_type_candidate = $codebase->methods->return_type_provider->getReturnType(
- $statements_analyzer,
- $fq_class_name,
- $stmt_name->name,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt_name)
- );
- }
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if (!$return_type_candidate
- && $declaring_method_id
- && (string) $declaring_method_id !== (string) $method_id
- ) {
- $declaring_fq_class_name = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
-
- if ($codebase->methods->return_type_provider->has($declaring_fq_class_name)) {
- $return_type_candidate = $codebase->methods->return_type_provider->getReturnType(
- $statements_analyzer,
- $declaring_fq_class_name,
- $declaring_method_name,
- $stmt,
- $context,
- new CodeLocation($statements_analyzer->getSource(), $stmt_name),
- null,
- $fq_class_name,
- $stmt_name->name
- );
- }
- }
-
- if (!$return_type_candidate) {
- $return_type_candidate = self::getMethodReturnType(
- $statements_analyzer,
- $codebase,
- $stmt,
- $method_id,
- $args,
- $template_result,
- $self_fq_class_name,
- $lhs_type_part,
- $context,
- $fq_class_name,
- $class_storage,
- $config
- );
- }
-
- $method_storage = $codebase->methods->getUserMethodStorage($method_id);
-
- if ($method_storage) {
- if ($method_storage->abstract
- && $stmt->class instanceof PhpParser\Node\Name
- && (!$context->self
- || !UnionTypeComparator::isContainedBy(
- $codebase,
- $context->vars_in_scope['$this']
- ?? new Union([
- new TNamedObject($context->self)
- ]),
- new Union([
- new TNamedObject($method_id->fq_class_name)
- ])
- ))
- ) {
- IssueBuffer::maybeAdd(
- new AbstractMethodCall(
- 'Cannot call an abstract static method ' . $method_id . ' directly',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$context->inside_throw) {
- if ($context->pure && !$method_storage->pure) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call an impure method from a pure context',
- new CodeLocation($statements_analyzer, $stmt_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->mutation_free && !$method_storage->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureMethodCall(
- 'Cannot call a possibly-mutating method from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt_name)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- && !$method_storage->pure
- ) {
- if (!$method_storage->mutation_free) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- }
-
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- $generic_params = $template_result->lower_bounds;
-
- if ($method_storage->assertions) {
- CallAnalyzer::applyAssertionsToContext(
- $stmt_name,
- null,
- $method_storage->assertions,
- $stmt->getArgs(),
- $generic_params,
- $context,
- $statements_analyzer
- );
- }
-
- if ($method_storage->if_true_assertions) {
- $statements_analyzer->node_data->setIfTrueAssertions(
- $stmt,
- array_map(
- function (Assertion $assertion) use ($generic_params, $codebase): Assertion {
- return $assertion->getUntemplatedCopy($generic_params, null, $codebase);
- },
- $method_storage->if_true_assertions
- )
- );
- }
-
- if ($method_storage->if_false_assertions) {
- $statements_analyzer->node_data->setIfFalseAssertions(
- $stmt,
- array_map(
- function (Assertion $assertion) use ($generic_params, $codebase): Assertion {
- return $assertion->getUntemplatedCopy($generic_params, null, $codebase);
- },
- $method_storage->if_false_assertions
- )
- );
- }
- }
-
- if ($codebase->alter_code) {
- foreach ($codebase->call_transforms as $original_pattern => $transformation) {
- if ($declaring_method_id
- && strtolower((string) $declaring_method_id) . '\((.*\))' === $original_pattern
- ) {
- if (strpos($transformation, '($1)') === strlen($transformation) - 4
- && $stmt->class instanceof PhpParser\Node\Name
- ) {
- $new_method_id = substr($transformation, 0, -4);
- $old_declaring_fq_class_name = $declaring_method_id->fq_class_name;
- [$new_fq_class_name, $new_method_name] = explode('::', $new_method_id);
-
- if ($codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $new_fq_class_name,
- $context->calling_method_id,
- strtolower($old_declaring_fq_class_name) !== strtolower($new_fq_class_name),
- $stmt->class->parts[0] === 'self'
- )) {
- $moved_call = true;
- }
-
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- (int) $stmt_name->getAttribute('startFilePos'),
- (int) $stmt_name->getAttribute('endFilePos') + 1,
- $new_method_name
- );
-
- FileManipulationBuffer::add(
- $statements_analyzer->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
- }
-
- if ($config->eventDispatcher->hasAfterMethodCallAnalysisHandlers()) {
- $file_manipulations = [];
-
- $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
-
- if ($appearing_method_id !== null && $declaring_method_id) {
- $event = new AfterMethodCallAnalysisEvent(
- $stmt,
- (string) $method_id,
- (string) $appearing_method_id,
- (string) $declaring_method_id,
- $context,
- $statements_analyzer,
- $codebase,
- $file_manipulations,
- $return_type_candidate
- );
- $config->eventDispatcher->dispatchAfterMethodCallAnalysis($event);
- $file_manipulations = $event->getFileReplacements();
- $return_type_candidate = $event->getReturnTypeCandidate();
- }
-
- if ($file_manipulations) {
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
- }
-
- $return_type_candidate = $return_type_candidate ?? Type::getMixed();
-
- StaticCallAnalyzer::taintReturnType(
- $statements_analyzer,
- $stmt,
- $method_id,
- $cased_method_id,
- $return_type_candidate,
- $method_storage,
- $template_result,
- $context
- );
-
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes($stmt_type, $return_type_candidate)
- );
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $method_id . '()'
- );
-
- if ($stmt_type = $statements_analyzer->node_data->getType($stmt)) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_type->getId(),
- $stmt
- );
- }
- }
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- */
- private static function getMethodReturnType(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\StaticCall $stmt,
- MethodIdentifier $method_id,
- array $args,
- TemplateResult $template_result,
- ?string &$self_fq_class_name,
- Atomic $lhs_type_part,
- Context $context,
- string $fq_class_name,
- ClassLikeStorage $class_storage,
- Config $config
- ): ?Union {
- $return_type_candidate = $codebase->methods->getMethodReturnType(
- $method_id,
- $self_fq_class_name,
- $statements_analyzer,
- $args
- );
-
- if ($return_type_candidate) {
- $return_type_candidate = clone $return_type_candidate;
-
- if ($template_result->template_types) {
- $bindable_template_types = $return_type_candidate->getTemplateTypes();
-
- foreach ($bindable_template_types as $template_type) {
- if (!isset(
- $template_result->lower_bounds
- [$template_type->param_name]
- [$template_type->defining_class]
- )) {
- if ($template_type->param_name === 'TFunctionArgCount') {
- $template_result->lower_bounds[$template_type->param_name] = [
- 'fn-' . strtolower((string)$method_id) => [
- new TemplateBound(
- Type::getInt(false, count($stmt->getArgs()))
- )
- ]
- ];
- } elseif ($template_type->param_name === 'TPhpMajorVersion') {
- $template_result->lower_bounds[$template_type->param_name] = [
- 'fn-' . strtolower((string)$method_id) => [
- new TemplateBound(
- Type::getInt(false, $codebase->php_major_version)
- )
- ]
- ];
- } elseif ($template_type->param_name === 'TPhpVersionId') {
- $template_result->lower_bounds[$template_type->param_name] = [
- 'fn-' . strtolower((string) $method_id) => [
- new TemplateBound(
- Type::getInt(
- false,
- 10000 * $codebase->php_major_version
- + 100 * $codebase->php_minor_version
- )
- )
- ]
- ];
- } else {
- $template_result->lower_bounds[$template_type->param_name] = [
- ($template_type->defining_class) => [
- new TemplateBound(Type::getEmpty())
- ]
- ];
- }
- }
- }
- }
-
- $context_final = false;
-
- if ($lhs_type_part instanceof TTemplateParam) {
- $static_type = $lhs_type_part;
- } elseif ($lhs_type_part instanceof TTemplateParamClass) {
- $static_type = new TTemplateParam(
- $lhs_type_part->param_name,
- $lhs_type_part->as_type
- ? new Union([$lhs_type_part->as_type])
- : Type::getObject(),
- $lhs_type_part->defining_class
- );
- } elseif ($stmt->class instanceof PhpParser\Node\Name
- && count($stmt->class->parts) === 1
- && in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
- && $lhs_type_part instanceof TNamedObject
- && $context->self
- ) {
- $static_type = $context->self;
- $context_final = $codebase->classlike_storage_provider->get($context->self)->final;
- } elseif ($context->calling_method_id !== null) {
- // differentiate between these cases:
- // 1. "static" comes from the CALLED static method - use $fq_class_name.
- // 2. "static" in return type comes from return type of the
- // method CALLING the currently analyzed static method - use $context->self.
- $static_type = self::hasStaticInType($return_type_candidate)
- ? $fq_class_name
- : $context->self;
- } else {
- $static_type = $fq_class_name;
- }
-
- if ($template_result->lower_bounds) {
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- null,
- null,
- null
- );
-
- TemplateInferredTypeReplacer::replace(
- $return_type_candidate,
- $template_result,
- $codebase
- );
- }
-
- $return_type_candidate = TypeExpander::expandUnion(
- $codebase,
- $return_type_candidate,
- $self_fq_class_name,
- $static_type,
- $class_storage->parent_class,
- true,
- false,
- is_string($static_type)
- && ($static_type !== $context->self
- || $class_storage->final
- || $context_final)
- );
-
- $secondary_return_type_location = null;
-
- $return_type_location = $codebase->methods->getMethodReturnTypeLocation(
- $method_id,
- $secondary_return_type_location
- );
-
- if ($secondary_return_type_location) {
- $return_type_location = $secondary_return_type_location;
- }
-
- // only check the type locally if it's defined externally
- if ($return_type_location && !$config->isInProjectDirs($return_type_location->file_path)) {
- $return_type_candidate->check(
- $statements_analyzer,
- new CodeLocation($statements_analyzer, $stmt),
- $statements_analyzer->getSuppressedIssues(),
- $context->phantom_classes,
- true,
- false,
- false,
- $context->calling_method_id
- );
- }
- }
-
- return $return_type_candidate;
- }
-
- /**
- * Dumb way to determine whether a type contains "static" somewhere inside.
- */
- private static function hasStaticInType(Type\TypeNode $type): bool
- {
- if ($type instanceof TNamedObject && ($type->value === 'static' || $type->was_static)) {
- return true;
- }
-
- foreach ($type->getChildNodes() as $child_type) {
- if (self::hasStaticInType($child_type)) {
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php
deleted file mode 100644
index 9cc9d85..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php
+++ /dev/null
@@ -1,1171 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use PhpParser\Node\Identifier;
-use PhpParser\Node\Name;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\FileSource;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentsAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateBound;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ArgumentTypeCoercion;
-use Psalm\Issue\InvalidArgument;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\InvalidScalarArgument;
-use Psalm\Issue\MixedArgumentTypeCoercion;
-use Psalm\Issue\UndefinedFunction;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\BinaryOp\VirtualIdentical;
-use Psalm\Node\Expr\VirtualConstFetch;
-use Psalm\Node\VirtualName;
-use Psalm\Storage\Assertion;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_map;
-use function array_merge;
-use function array_unique;
-use function count;
-use function explode;
-use function implode;
-use function in_array;
-use function is_int;
-use function is_numeric;
-use function mt_rand;
-use function preg_match;
-use function preg_replace;
-use function spl_object_id;
-use function str_replace;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class CallAnalyzer
-{
- public static function collectSpecialInformation(
- FunctionLikeAnalyzer $source,
- string $method_name,
- Context $context
- ): void {
- $method_name_lc = strtolower($method_name);
- $fq_class_name = (string)$source->getFQCLN();
-
- $project_analyzer = $source->getFileAnalyzer()->project_analyzer;
- $codebase = $source->getCodebase();
-
- if ($context->collect_mutations &&
- $context->self &&
- (
- $context->self === $fq_class_name ||
- $codebase->classExtends(
- $context->self,
- $fq_class_name
- )
- )
- ) {
- $method_id = new MethodIdentifier(
- $fq_class_name,
- $method_name_lc
- );
-
- if ((string) $method_id !== $source->getId()) {
- if ($context->collect_initializations) {
- if (isset($context->initialized_methods[(string) $method_id])) {
- return;
- }
-
- if ($context->initialized_methods === null) {
- $context->initialized_methods = [];
- }
-
- $context->initialized_methods[(string) $method_id] = true;
- }
-
- $project_analyzer->getMethodMutations(
- $method_id,
- $context,
- $source->getRootFilePath(),
- $source->getRootFileName()
- );
- }
- } elseif ($context->collect_initializations &&
- $context->self &&
- (
- $context->self === $fq_class_name
- || $codebase->classlikes->classExtends(
- $context->self,
- $fq_class_name
- )
- ) &&
- $source->getMethodName() !== $method_name
- ) {
- $method_id = new MethodIdentifier($fq_class_name, $method_name_lc);
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if (isset($context->vars_in_scope['$this'])) {
- foreach ($context->vars_in_scope['$this']->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TNamedObject) {
- if ($fq_class_name === $atomic_type->value) {
- $alt_declaring_method_id = $declaring_method_id;
- } else {
- $fq_class_name = $atomic_type->value;
-
- $method_id = new MethodIdentifier(
- $fq_class_name,
- $method_name_lc
- );
-
- $alt_declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
- }
-
- if ($alt_declaring_method_id) {
- $declaring_method_id = $alt_declaring_method_id;
- break;
- }
-
- if (!$atomic_type->extra_types) {
- continue;
- }
-
- foreach ($atomic_type->extra_types as $intersection_type) {
- if ($intersection_type instanceof TNamedObject) {
- $fq_class_name = $intersection_type->value;
- $method_id = new MethodIdentifier(
- $fq_class_name,
- $method_name_lc
- );
-
- $alt_declaring_method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if ($alt_declaring_method_id) {
- $declaring_method_id = $alt_declaring_method_id;
- break 2;
- }
- }
- }
- }
- }
- }
-
- if (!$declaring_method_id) {
- // can happen for __call
- return;
- }
-
- if (isset($context->initialized_methods[(string) $declaring_method_id])) {
- return;
- }
-
- if ($context->initialized_methods === null) {
- $context->initialized_methods = [];
- }
-
- $context->initialized_methods[(string) $declaring_method_id] = true;
-
- $method_storage = $codebase->methods->getStorage($declaring_method_id);
-
- $class_analyzer = $source->getSource();
-
- $is_final = $method_storage->final;
-
- if ($method_name !== $declaring_method_id->method_name) {
- $appearing_method_id = $codebase->methods->getAppearingMethodId($method_id);
-
- if ($appearing_method_id) {
- $appearing_class_storage = $codebase->classlike_storage_provider->get(
- $appearing_method_id->fq_class_name
- );
-
- if (isset($appearing_class_storage->trait_final_map[$method_name_lc])) {
- $is_final = true;
- }
- }
- }
-
- if ($class_analyzer instanceof ClassLikeAnalyzer
- && !$method_storage->is_static
- && ($context->collect_nonprivate_initializations
- || $method_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- || $is_final)
- ) {
- $local_vars_in_scope = [];
-
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (strpos($var_id, '$this->') === 0) {
- if ($type->initialized) {
- $local_vars_in_scope[$var_id] = $context->vars_in_scope[$var_id];
-
- unset($context->vars_in_scope[$var_id]);
- unset($context->vars_possibly_in_scope[$var_id]);
- }
- } elseif ($var_id !== '$this') {
- $local_vars_in_scope[$var_id] = $context->vars_in_scope[$var_id];
- }
- }
-
- $local_vars_possibly_in_scope = $context->vars_possibly_in_scope;
-
- $old_calling_method_id = $context->calling_method_id;
-
- if ($fq_class_name === $source->getFQCLN()) {
- $class_analyzer->getMethodMutations($declaring_method_id->method_name, $context);
- } else {
- $declaring_fq_class_name = $declaring_method_id->fq_class_name;
-
- $old_self = $context->self;
- $context->self = $declaring_fq_class_name;
- $project_analyzer->getMethodMutations(
- $declaring_method_id,
- $context,
- $source->getRootFilePath(),
- $source->getRootFileName()
- );
- $context->self = $old_self;
- }
-
- $context->calling_method_id = $old_calling_method_id;
-
- foreach ($local_vars_in_scope as $var => $type) {
- $context->vars_in_scope[$var] = $type;
- }
-
- foreach ($local_vars_possibly_in_scope as $var => $_) {
- $context->vars_possibly_in_scope[$var] = true;
- }
- }
- }
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- */
- public static function checkMethodArgs(
- ?MethodIdentifier $method_id,
- array $args,
- ?TemplateResult $class_template_result,
- Context $context,
- CodeLocation $code_location,
- StatementsAnalyzer $statements_analyzer
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$method_id) {
- return ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $args,
- null,
- null,
- true,
- $context,
- $class_template_result
- ) !== false;
- }
-
- $method_params = $codebase->methods->getMethodParams($method_id, $statements_analyzer, $args, $context);
-
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- $fq_class_name = strtolower($codebase->classlikes->getUnAliasedName($fq_class_name));
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $method_storage = null;
-
- if (isset($class_storage->declaring_method_ids[$method_name])) {
- $declaring_method_id = $class_storage->declaring_method_ids[$method_name];
-
- $declaring_fq_class_name = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
-
- if ($declaring_fq_class_name !== $fq_class_name) {
- $declaring_class_storage = $codebase->classlike_storage_provider->get($declaring_fq_class_name);
- } else {
- $declaring_class_storage = $class_storage;
- }
-
- if (!isset($declaring_class_storage->methods[$declaring_method_name])) {
- throw new UnexpectedValueException('Storage should not be empty here');
- }
-
- $method_storage = $declaring_class_storage->methods[$declaring_method_name];
-
- if ($declaring_class_storage->user_defined
- && !$method_storage->has_docblock_param_types
- && isset($declaring_class_storage->documenting_method_ids[$method_name])
- ) {
- $documenting_method_id = $declaring_class_storage->documenting_method_ids[$method_name];
-
- $documenting_method_storage = $codebase->methods->getStorage($documenting_method_id);
-
- if ($documenting_method_storage->template_types) {
- $method_storage = $documenting_method_storage;
- }
- }
-
- if (!$context->isSuppressingExceptions($statements_analyzer)) {
- $context->mergeFunctionExceptions($method_storage, $code_location);
- }
- }
-
- if (ArgumentsAnalyzer::analyze(
- $statements_analyzer,
- $args,
- $method_params,
- (string) $method_id,
- $method_storage->allow_named_arg_calls ?? true,
- $context,
- $class_template_result
- ) === false) {
- return false;
- }
-
- if (ArgumentsAnalyzer::checkArgumentsMatch(
- $statements_analyzer,
- $args,
- $method_id,
- $method_params,
- $method_storage,
- $class_storage,
- $class_template_result,
- $code_location,
- $context
- ) === false) {
- return false;
- }
-
- if ($class_template_result) {
- self::checkTemplateResult(
- $statements_analyzer,
- $class_template_result,
- $code_location,
- strtolower((string) $method_id)
- );
- }
-
- return true;
- }
-
- /**
- * This gets all the template params (and their types) that we think
- * we'll need to know about
- *
- * @return array<string, array<string, Union>>
- * @param array<string, non-empty-array<string, Union>> $existing_template_types
- * @param array<string, array<string, Union>> $class_template_params
- */
- public static function getTemplateTypesForCall(
- Codebase $codebase,
- ?ClassLikeStorage $declaring_class_storage,
- ?string $appearing_class_name,
- ?ClassLikeStorage $calling_class_storage,
- array $existing_template_types = [],
- array $class_template_params = []
- ): array {
- $template_types = $existing_template_types;
-
- if ($declaring_class_storage) {
- if ($calling_class_storage
- && $declaring_class_storage !== $calling_class_storage
- && $calling_class_storage->template_extended_params
- ) {
- foreach ($calling_class_storage->template_extended_params as $class_name => $type_map) {
- foreach ($type_map as $template_name => $type) {
- if ($class_name === $declaring_class_storage->name) {
- $output_type = null;
-
- foreach ($type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TTemplateParam) {
- $output_type_candidate = self::getGenericParamForOffset(
- $atomic_type->defining_class,
- $atomic_type->param_name,
- $calling_class_storage->template_extended_params,
- $class_template_params + $template_types
- );
- } else {
- $output_type_candidate = new Union([$atomic_type]);
- }
-
- $output_type = Type::combineUnionTypes(
- $output_type_candidate,
- $output_type
- );
- }
-
- $template_types[$template_name][$declaring_class_storage->name] = $output_type;
- }
- }
- }
- } elseif ($declaring_class_storage->template_types) {
- foreach ($declaring_class_storage->template_types as $template_name => $type_map) {
- foreach ($type_map as $key => $type) {
- $template_types[$template_name][$key]
- = $class_template_params[$template_name][$key] ?? $type;
- }
- }
- }
- }
-
- foreach ($template_types as $key => $type_map) {
- foreach ($type_map as $class => $type) {
- $template_types[$key][$class] = TypeExpander::expandUnion(
- $codebase,
- $type,
- $appearing_class_name,
- $calling_class_storage->name ?? null,
- null,
- true,
- false,
- $calling_class_storage->final ?? false
- );
- }
- }
-
- return $template_types;
- }
-
- /**
- * @param array<string, array<string, Union>> $template_extended_params
- * @param array<string, array<string, Union>> $found_generic_params
- */
- public static function getGenericParamForOffset(
- string $fq_class_name,
- string $template_name,
- array $template_extended_params,
- array $found_generic_params
- ): Union {
- if (isset($found_generic_params[$template_name][$fq_class_name])) {
- return $found_generic_params[$template_name][$fq_class_name];
- }
-
- foreach ($template_extended_params as $extended_class_name => $type_map) {
- foreach ($type_map as $extended_template_name => $extended_type) {
- foreach ($extended_type->getAtomicTypes() as $extended_atomic_type) {
- if ($extended_atomic_type instanceof TTemplateParam
- && $extended_atomic_type->param_name === $template_name
- && $extended_atomic_type->defining_class === $fq_class_name
- ) {
- return self::getGenericParamForOffset(
- $extended_class_name,
- $extended_template_name,
- $template_extended_params,
- $found_generic_params
- );
- }
- }
- }
- }
-
- return Type::getMixed();
- }
-
- /**
- * @param PhpParser\Node\Scalar\String_|PhpParser\Node\Expr\Array_|PhpParser\Node\Expr\BinaryOp\Concat $callable_arg
- *
- * @return list<non-empty-string>
- *
- * @psalm-suppress LessSpecificReturnStatement
- * @psalm-suppress MoreSpecificReturnType
- */
- public static function getFunctionIdsFromCallableArg(
- FileSource $file_source,
- PhpParser\Node\Expr $callable_arg
- ): array {
- if ($callable_arg instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
- if ($callable_arg->left instanceof PhpParser\Node\Expr\ClassConstFetch
- && $callable_arg->left->class instanceof Name
- && $callable_arg->left->name instanceof Identifier
- && strtolower($callable_arg->left->name->name) === 'class'
- && !in_array(strtolower($callable_arg->left->class->parts[0]), ['self', 'static', 'parent'])
- && $callable_arg->right instanceof PhpParser\Node\Scalar\String_
- && preg_match('/^::[A-Za-z0-9]+$/', $callable_arg->right->value)
- ) {
- return [
- (string) $callable_arg->left->class->getAttribute('resolvedName') . $callable_arg->right->value
- ];
- }
-
- return [];
- }
-
- if ($callable_arg instanceof PhpParser\Node\Scalar\String_) {
- $potential_id = preg_replace('/^\\\/', '', $callable_arg->value);
-
- if (preg_match('/^[A-Za-z0-9_]+(\\\[A-Za-z0-9_]+)*(::[A-Za-z0-9_]+)?$/', $potential_id)) {
- return [$potential_id];
- }
-
- return [];
- }
-
- if (count($callable_arg->items) !== 2) {
- return [];
- }
-
- /** @psalm-suppress PossiblyNullPropertyFetch */
- if ($callable_arg->items[0]->key || $callable_arg->items[1]->key) {
- return [];
- }
-
- if (!isset($callable_arg->items[0]) || !isset($callable_arg->items[1])) {
- throw new UnexpectedValueException('These should never be unset');
- }
-
- $class_arg = $callable_arg->items[0]->value;
- $method_name_arg = $callable_arg->items[1]->value;
-
- if (!$method_name_arg instanceof PhpParser\Node\Scalar\String_) {
- return [];
- }
-
- if ($class_arg instanceof PhpParser\Node\Scalar\String_) {
- return [preg_replace('/^\\\/', '', $class_arg->value) . '::' . $method_name_arg->value];
- }
-
- if ($class_arg instanceof PhpParser\Node\Expr\ClassConstFetch
- && $class_arg->name instanceof Identifier
- && strtolower($class_arg->name->name) === 'class'
- && $class_arg->class instanceof Name
- ) {
- $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $class_arg->class,
- $file_source->getAliases()
- );
-
- return [$fq_class_name . '::' . $method_name_arg->value];
- }
-
- if (!$file_source instanceof StatementsAnalyzer
- || !($class_arg_type = $file_source->node_data->getType($class_arg))
- ) {
- return [];
- }
-
- $method_ids = [];
-
- foreach ($class_arg_type->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TNamedObject) {
- $method_id = $type_part->value . '::' . $method_name_arg->value;
-
- if ($type_part->extra_types) {
- foreach ($type_part->extra_types as $extra_type) {
- if ($extra_type instanceof TTemplateParam
- || $extra_type instanceof TObjectWithProperties
- ) {
- throw new UnexpectedValueException('Shouldn’t get a generic param here');
- }
-
- $method_id .= '&' . $extra_type->value . '::' . $method_name_arg->value;
- }
- }
-
- $method_ids[] = '$' . $method_id;
- }
- }
-
- return $method_ids;
- }
-
- /**
- * @param non-empty-string $function_id
- * @param bool $can_be_in_root_scope if true, the function can be shortened to the root version
- *
- */
- public static function checkFunctionExists(
- StatementsAnalyzer $statements_analyzer,
- string &$function_id,
- CodeLocation $code_location,
- bool $can_be_in_root_scope
- ): bool {
- $cased_function_id = $function_id;
- $function_id = strtolower($function_id);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$codebase->functions->functionExists($statements_analyzer, $function_id)) {
- /** @var non-empty-lowercase-string */
- $root_function_id = preg_replace('/.*\\\/', '', $function_id);
-
- if ($can_be_in_root_scope
- && $function_id !== $root_function_id
- && $codebase->functions->functionExists($statements_analyzer, $root_function_id)
- ) {
- $function_id = $root_function_id;
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedFunction(
- 'Function ' . $cased_function_id . ' does not exist',
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @param Identifier|Name $expr
- * @param Assertion[] $assertions
- * @param list<PhpParser\Node\Arg> $args
- * @param array<string, array<string, non-empty-list<TemplateBound>>> $inferred_lower_bounds,
- *
- */
- public static function applyAssertionsToContext(
- PhpParser\NodeAbstract $expr,
- ?string $thisName,
- array $assertions,
- array $args,
- array $inferred_lower_bounds,
- Context $context,
- StatementsAnalyzer $statements_analyzer
- ): void {
- $type_assertions = [];
-
- $asserted_keys = [];
-
- foreach ($assertions as $assertion) {
- $assertion_var_id = null;
-
- $arg_value = null;
-
- if (is_int($assertion->var_id)) {
- if (!isset($args[$assertion->var_id])) {
- continue;
- }
-
- $arg_value = $args[$assertion->var_id]->value;
-
- $arg_var_id = ExpressionIdentifier::getArrayVarId($arg_value, null, $statements_analyzer);
-
- if ($arg_var_id) {
- $assertion_var_id = $arg_var_id;
- }
- } elseif ($assertion->var_id === '$this' && $thisName !== null) {
- $assertion_var_id = $thisName;
- } elseif (strpos($assertion->var_id, '$this->') === 0 && $thisName !== null) {
- $assertion_var_id = $thisName . str_replace('$this->', '->', $assertion->var_id);
- } elseif (strpos($assertion->var_id, 'self::') === 0 && $context->self) {
- $assertion_var_id = $context->self . str_replace('self::', '::', $assertion->var_id);
- } elseif (strpos($assertion->var_id, '::$') !== false) {
- // allow assertions to bring external static props into scope
- $assertion_var_id = $assertion->var_id;
- } elseif (isset($context->vars_in_scope[$assertion->var_id])) {
- $assertion_var_id = $assertion->var_id;
- } elseif (strpos($assertion->var_id, '->') !== false) {
- $exploded = explode('->', $assertion->var_id);
-
- if (count($exploded) < 2) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Assert notation is malformed',
- new CodeLocation($statements_analyzer, $expr)
- )
- );
- continue;
- }
-
- [$var_id, $property] = $exploded;
-
- $var_id = is_numeric($var_id) ? (int) $var_id : $var_id;
-
- if (!is_int($var_id) || !isset($args[$var_id])) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Variable ' . $var_id . ' is not an argument so cannot be asserted',
- new CodeLocation($statements_analyzer, $expr)
- )
- );
- continue;
- }
-
- /** @var PhpParser\Node\Expr\Variable $arg_value */
- $arg_value = $args[$var_id]->value;
-
- $arg_var_id = ExpressionIdentifier::getArrayVarId($arg_value, null, $statements_analyzer);
-
- if (!$arg_var_id) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Variable being asserted as argument ' . ($var_id+1) . ' cannot be found in local scope',
- new CodeLocation($statements_analyzer, $expr)
- )
- );
- continue;
- }
-
- if (count($exploded) === 2) {
- $failedMessage = AssertionFinder::isPropertyImmutableOnArgument(
- $property,
- $statements_analyzer->getNodeTypeProvider(),
- $statements_analyzer->getCodebase()->classlike_storage_provider,
- $arg_value
- );
-
- if (null !== $failedMessage) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock($failedMessage, new CodeLocation($statements_analyzer, $expr))
- );
- continue;
- }
- }
-
- $assertion_var_id = str_replace((string) $var_id, $arg_var_id, $assertion->var_id);
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($assertion_var_id) {
- $rule = $assertion->rule[0][0];
-
- $prefix = '';
- if ($rule[0] === '!') {
- $prefix .= '!';
- $rule = substr($rule, 1);
- }
- if ($rule[0] === '=') {
- $prefix .= '=';
- $rule = substr($rule, 1);
- }
- if ($rule[0] === '~') {
- $prefix .= '~';
- $rule = substr($rule, 1);
- }
-
- if (isset($inferred_lower_bounds[$rule])) {
- foreach ($inferred_lower_bounds[$rule] as $lower_bounds) {
- $lower_bound_type = TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $lower_bounds,
- $codebase
- );
-
- if ($lower_bound_type->hasMixed()) {
- continue 2;
- }
-
- $replacement_atomic_types = $lower_bound_type->getAtomicTypes();
-
- if (count($replacement_atomic_types) > 1) {
- continue 2;
- }
-
- $ored_type_assertions = [];
-
- foreach ($replacement_atomic_types as $replacement_atomic_type) {
- if ($replacement_atomic_type instanceof TMixed) {
- continue 3;
- }
-
- if ($replacement_atomic_type instanceof TArray
- || $replacement_atomic_type instanceof TKeyedArray
- || $replacement_atomic_type instanceof TList
- ) {
- $ored_type_assertions[] = $prefix . $replacement_atomic_type->getId();
- } elseif ($replacement_atomic_type instanceof TNamedObject) {
- $ored_type_assertions[] = $prefix . $replacement_atomic_type->value;
- } elseif ($replacement_atomic_type instanceof Scalar) {
- $ored_type_assertions[] = $prefix . $replacement_atomic_type->getAssertionString();
- } elseif ($replacement_atomic_type instanceof TNull) {
- $ored_type_assertions[] = $prefix . 'null';
- } elseif ($replacement_atomic_type instanceof TTemplateParam) {
- $ored_type_assertions[] = $prefix . $replacement_atomic_type->param_name;
- }
- }
-
- if ($ored_type_assertions) {
- $type_assertions[$assertion_var_id] = [$ored_type_assertions];
- }
- }
- } else {
- if (isset($type_assertions[$assertion_var_id])) {
- $type_assertions[$assertion_var_id] = array_merge(
- $type_assertions[$assertion_var_id],
- $assertion->rule
- );
- } else {
- $type_assertions[$assertion_var_id] = $assertion->rule;
- }
- }
- } elseif ($arg_value && ($assertion->rule === [['!falsy']] || $assertion->rule === [['true']])) {
- if ($assertion->rule === [['true']]) {
- $conditional = new VirtualIdentical(
- $arg_value,
- new VirtualConstFetch(new VirtualName('true'))
- );
-
- $assert_clauses = FormulaGenerator::getFormula(
- mt_rand(0, 1000000),
- mt_rand(0, 1000000),
- $conditional,
- $context->self,
- $statements_analyzer,
- $codebase
- );
- } else {
- $assert_clauses = FormulaGenerator::getFormula(
- spl_object_id($arg_value),
- spl_object_id($arg_value),
- $arg_value,
- $context->self,
- $statements_analyzer,
- $statements_analyzer->getCodebase()
- );
- }
-
- $simplified_clauses = Algebra::simplifyCNF(
- array_merge($context->clauses, $assert_clauses)
- );
-
- $assert_type_assertions = Algebra::getTruthsFromFormula(
- $simplified_clauses
- );
-
- $type_assertions = array_merge($type_assertions, $assert_type_assertions);
- } elseif ($arg_value && $assertion->rule === [['falsy']]) {
- $assert_clauses = Algebra::negateFormula(
- FormulaGenerator::getFormula(
- spl_object_id($arg_value),
- spl_object_id($arg_value),
- $arg_value,
- $context->self,
- $statements_analyzer,
- $codebase
- )
- );
-
- $simplified_clauses = Algebra::simplifyCNF(
- array_merge($context->clauses, $assert_clauses)
- );
-
- $assert_type_assertions = Algebra::getTruthsFromFormula(
- $simplified_clauses
- );
-
- $type_assertions = array_merge($type_assertions, $assert_type_assertions);
- }
- }
-
- $changed_var_ids = [];
-
- foreach ($type_assertions as $var_id => $_) {
- $asserted_keys[$var_id] = true;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($type_assertions) {
- $template_type_map = array_map(
- function ($type_map) use ($codebase) {
- return array_map(
- function ($bounds) use ($codebase) {
- return TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $bounds,
- $codebase
- );
- },
- $type_map
- );
- },
- $inferred_lower_bounds
- );
-
- foreach (($statements_analyzer->getTemplateTypeMap() ?: []) as $template_name => $map) {
- foreach ($map as $ref => $type) {
- $template_type_map[$template_name][$ref] = new Union([
- new TTemplateParam(
- $template_name,
- $type,
- $ref
- )
- ]);
- }
- }
-
- // while in an and, we allow scope to boil over to support
- // statements of the form if ($x && $x->foo())
- $op_vars_in_scope = Reconciler::reconcileKeyedTypes(
- $type_assertions,
- $type_assertions,
- $context->vars_in_scope,
- $changed_var_ids,
- $asserted_keys,
- $statements_analyzer,
- $template_type_map,
- $context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $expr)
- );
-
- foreach ($changed_var_ids as $var_id => $_) {
- if (isset($op_vars_in_scope[$var_id])) {
- $first_appearance = $statements_analyzer->getFirstAppearance($var_id);
-
- if ($first_appearance
- && isset($context->vars_in_scope[$var_id])
- && $context->vars_in_scope[$var_id]->hasMixed()
- ) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->decrementMixedCount($statements_analyzer->getFilePath());
- }
-
- IssueBuffer::remove(
- $statements_analyzer->getFilePath(),
- 'MixedAssignment',
- $first_appearance->raw_file_start
- );
- }
-
- if ($template_type_map) {
- $readonly_template_result = new TemplateResult($template_type_map, $template_type_map);
-
- TemplateInferredTypeReplacer::replace(
- $op_vars_in_scope[$var_id],
- $readonly_template_result,
- $codebase
- );
- }
-
- $op_vars_in_scope[$var_id]->from_docblock = true;
-
- foreach ($op_vars_in_scope[$var_id]->getAtomicTypes() as $changed_atomic_type) {
- $changed_atomic_type->from_docblock = true;
-
- if ($changed_atomic_type instanceof TNamedObject
- && $changed_atomic_type->extra_types
- ) {
- foreach ($changed_atomic_type->extra_types as $extra_type) {
- $extra_type->from_docblock = true;
- }
- }
- }
- }
- }
-
- $context->vars_in_scope = $op_vars_in_scope;
- }
- }
-
- /**
- * This method looks for problems with a generated TemplateResult.
- *
- * The TemplateResult object contains upper bounds and lower bounds for each template param.
- *
- * Those upper bounds represent a series of constraints like
- *
- * Lower bound:
- * T >: X (the type param T matches X, or is a supertype of X)
- * Upper bound:
- * T <: Y (the type param T matches Y, or is a subtype of Y)
- * Equality (currently represented as an upper bound with a special flag)
- * T = Z (the template T must match Z)
- *
- * This method attempts to reconcile those constraints.
- *
- * Valid constraints:
- *
- * T <: int|float, T >: int --- implies T is an int
- * T = int --- implies T is an int
- *
- * Invalid constraints:
- *
- * T <: int|string, T >: string|float --- implies T <: int and T >: float, which is impossible
- * T = int, T = string --- implies T is a string _and_ and int, which is impossible
- */
- public static function checkTemplateResult(
- StatementsAnalyzer $statements_analyzer,
- TemplateResult $template_result,
- CodeLocation $code_location,
- ?string $function_id
- ): void {
- if ($template_result->lower_bounds && $template_result->upper_bounds) {
- foreach ($template_result->upper_bounds as $template_name => $defining_map) {
- foreach ($defining_map as $defining_id => $upper_bound) {
- if (isset($template_result->lower_bounds[$template_name][$defining_id])) {
- $lower_bound_type = TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $template_result->lower_bounds[$template_name][$defining_id],
- $statements_analyzer->getCodebase()
- );
-
- $upper_bound_type = $upper_bound->type;
-
- $union_comparison_result = new TypeComparisonResult();
-
- if (count($template_result->upper_bounds_unintersectable_types) > 1) {
- [$lower_bound_type, $upper_bound_type]
- = $template_result->upper_bounds_unintersectable_types;
- }
-
- if (!UnionTypeComparator::isContainedBy(
- $statements_analyzer->getCodebase(),
- $lower_bound_type,
- $upper_bound_type,
- false,
- false,
- $union_comparison_result
- )) {
- if ($union_comparison_result->type_coerced) {
- if ($union_comparison_result->type_coerced_from_mixed) {
- IssueBuffer::maybeAdd(
- new MixedArgumentTypeCoercion(
- 'Type ' . $lower_bound_type->getId() . ' should be a subtype of '
- . $upper_bound_type->getId(),
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new ArgumentTypeCoercion(
- 'Type ' . $lower_bound_type->getId() . ' should be a subtype of '
- . $upper_bound_type->getId(),
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($union_comparison_result->scalar_type_match_found) {
- IssueBuffer::maybeAdd(
- new InvalidScalarArgument(
- 'Type ' . $lower_bound_type->getId() . ' should be a subtype of '
- . $upper_bound_type->getId(),
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'Type ' . $lower_bound_type->getId() . ' should be a subtype of '
- . $upper_bound_type->getId(),
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- } else {
- $template_result->lower_bounds[$template_name][$defining_id] = [
- new TemplateBound(
- clone $upper_bound->type
- )
- ];
- }
- }
- }
- }
-
- // Attempt to identify invalid lower bounds
- foreach ($template_result->lower_bounds as $template_name => $lower_bounds) {
- foreach ($lower_bounds as $lower_bounds) {
- if (count($lower_bounds) > 1) {
- $bounds_with_equality = array_filter(
- $lower_bounds,
- function ($lower_bound) {
- return (bool)$lower_bound->equality_bound_classlike;
- }
- );
-
- if (!$bounds_with_equality) {
- continue;
- }
-
- $equality_types = array_unique(
- array_map(
- function ($bound_with_equality) {
- return $bound_with_equality->type->getId();
- },
- $bounds_with_equality
- )
- );
-
- if (count($equality_types) > 1) {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'Incompatible types found for ' . $template_name . ' (must have only one of ' .
- implode(', ', $equality_types) . ')',
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- foreach ($lower_bounds as $lower_bound) {
- if ($lower_bound->equality_bound_classlike === null) {
- foreach ($bounds_with_equality as $bound_with_equality) {
- if (UnionTypeComparator::isContainedBy(
- $statements_analyzer->getCodebase(),
- $lower_bound->type,
- $bound_with_equality->type
- ) && UnionTypeComparator::isContainedBy(
- $statements_analyzer->getCodebase(),
- $bound_with_equality->type,
- $lower_bound->type
- )) {
- continue 2;
- }
- }
-
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'Incompatible types found for ' . $template_name . ' (' .
- $lower_bound->type->getId() . ' is not in ' .
- implode(', ', $equality_types) . ')',
- $code_location,
- $function_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php
deleted file mode 100644
index 439ee3b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CastAnalyzer.php
+++ /dev/null
@@ -1,538 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\Method\MethodCallReturnTypeFetcher;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Issue\InvalidCast;
-use Psalm\Issue\PossiblyInvalidCast;
-use Psalm\Issue\RedundantCast;
-use Psalm\Issue\RedundantCastGivenDocblockType;
-use Psalm\Issue\UnrecognizedExpression;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonspecificLiteralInt;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TResource;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function array_pop;
-use function array_values;
-use function count;
-use function get_class;
-
-class CastAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Cast $stmt,
- Context $context
- ): bool {
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Int_) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- $as_int = true;
- $valid_int_type = null;
- $maybe_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($maybe_type) {
- if ($maybe_type->isInt()) {
- $valid_int_type = $maybe_type;
- if (!$maybe_type->from_calculation) {
- self::handleRedundantCast($maybe_type, $statements_analyzer, $stmt);
- }
- }
-
- if (count($maybe_type->getAtomicTypes()) === 1
- && $maybe_type->getSingleAtomic() instanceof TBool) {
- $as_int = false;
- $type = new Union([
- new TLiteralInt(0),
- new TLiteralInt(1),
- ]);
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- ) {
- $type->parent_nodes = $maybe_type->parent_nodes;
- }
-
- $statements_analyzer->node_data->setType($stmt, $type);
- }
- }
-
- if ($as_int) {
- $type = $valid_int_type ?? Type::getInt();
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- ) {
- $type->parent_nodes = $maybe_type->parent_nodes ?? [];
- }
-
- $statements_analyzer->node_data->setType($stmt, $type);
- }
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Double) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- $maybe_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($maybe_type) {
- if ($maybe_type->isFloat()) {
- self::handleRedundantCast($maybe_type, $statements_analyzer, $stmt);
- }
- }
-
- $type = Type::getFloat();
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- ) {
- $type->parent_nodes = $maybe_type->parent_nodes ?? [];
- }
-
- $statements_analyzer->node_data->setType($stmt, $type);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Bool_) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- $maybe_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($maybe_type) {
- if ($maybe_type->isBool()) {
- self::handleRedundantCast($maybe_type, $statements_analyzer, $stmt);
- }
- }
-
- $type = Type::getBool();
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- ) {
- $type->parent_nodes = $maybe_type->parent_nodes ?? [];
- }
-
- $statements_analyzer->node_data->setType($stmt, $type);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\String_) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- $stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($stmt_expr_type) {
- if ($stmt_expr_type->isString()) {
- self::handleRedundantCast($stmt_expr_type, $statements_analyzer, $stmt);
- }
-
- $stmt_type = self::castStringAttempt(
- $statements_analyzer,
- $context,
- $stmt_expr_type,
- $stmt->expr,
- true
- );
- } else {
- $stmt_type = Type::getString();
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Object_) {
- if (!self::checkExprGeneralUse($statements_analyzer, $stmt, $context)) {
- return false;
- }
-
- $permissible_atomic_types = [];
- $all_permissible = false;
-
- if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
- if ($stmt_expr_type->isObjectType()) {
- self::handleRedundantCast($stmt_expr_type, $statements_analyzer, $stmt);
- }
-
- $all_permissible = true;
-
- foreach ($stmt_expr_type->getAtomicTypes() as $type) {
- if ($type instanceof Scalar) {
- $objWithProps = new TObjectWithProperties(['scalar' => new Union([$type])]);
- $permissible_atomic_types[] = $objWithProps;
- } elseif ($type instanceof TKeyedArray) {
- $permissible_atomic_types[] = new TObjectWithProperties($type->properties);
- } else {
- $all_permissible = false;
- break;
- }
- }
- }
-
- if ($permissible_atomic_types && $all_permissible) {
- $type = TypeCombiner::combine($permissible_atomic_types);
- } else {
- $type = Type::getObject();
- }
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph
- ) {
- $type->parent_nodes = $stmt_expr_type->parent_nodes ?? [];
- }
-
- $statements_analyzer->node_data->setType($stmt, $type);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Array_) {
- if (!self::checkExprGeneralUse($statements_analyzer, $stmt, $context)) {
- return false;
- }
-
- $permissible_atomic_types = [];
- $all_permissible = false;
-
- if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
- if ($stmt_expr_type->isArray()) {
- self::handleRedundantCast($stmt_expr_type, $statements_analyzer, $stmt);
- }
-
- $all_permissible = true;
-
- foreach ($stmt_expr_type->getAtomicTypes() as $type) {
- if ($type instanceof Scalar) {
- $keyed_array = new TKeyedArray([new Union([$type])]);
- $keyed_array->is_list = true;
- $keyed_array->sealed = true;
- $permissible_atomic_types[] = $keyed_array;
- } elseif ($type instanceof TNull) {
- $permissible_atomic_types[] = new TArray([Type::getEmpty(), Type::getEmpty()]);
- } elseif ($type instanceof TArray
- || $type instanceof TList
- || $type instanceof TKeyedArray
- ) {
- $permissible_atomic_types[] = clone $type;
- } else {
- $all_permissible = false;
- break;
- }
- }
- }
-
- if ($permissible_atomic_types && $all_permissible) {
- $type = TypeCombiner::combine($permissible_atomic_types);
- } else {
- $type = Type::getArray();
- }
-
- if ($statements_analyzer->data_flow_graph) {
- $type->parent_nodes = $stmt_expr_type->parent_nodes ?? [];
- }
-
- $statements_analyzer->node_data->setType($stmt, $type);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Unset_
- && $statements_analyzer->getCodebase()->php_major_version < 8
- ) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getNull());
-
- return true;
- }
-
- IssueBuffer::maybeAdd(
- new UnrecognizedExpression(
- 'Psalm does not understand the cast ' . get_class($stmt),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return false;
- }
-
- public static function castStringAttempt(
- StatementsAnalyzer $statements_analyzer,
- Context $context,
- Union $stmt_type,
- PhpParser\Node\Expr $stmt,
- bool $explicit_cast = false
- ): Union {
- $codebase = $statements_analyzer->getCodebase();
-
- $invalid_casts = [];
- $valid_strings = [];
- $castable_types = [];
-
- $atomic_types = $stmt_type->getAtomicTypes();
-
- $parent_nodes = [];
-
- if ($statements_analyzer->data_flow_graph) {
- $parent_nodes = $stmt_type->parent_nodes;
- }
-
- while ($atomic_types) {
- $atomic_type = array_pop($atomic_types);
-
- if ($atomic_type instanceof TFloat
- || $atomic_type instanceof TInt
- || $atomic_type instanceof TNumeric
- ) {
- if ($atomic_type instanceof TLiteralInt || $atomic_type instanceof TLiteralFloat) {
- $castable_types[] = new TLiteralString((string) $atomic_type->value);
- } elseif ($atomic_type instanceof TNonspecificLiteralInt) {
- $castable_types[] = new TNonspecificLiteralString();
- } else {
- $castable_types[] = new TNumericString();
- }
-
- continue;
- }
-
- if ($atomic_type instanceof TString) {
- $valid_strings[] = $atomic_type;
-
- continue;
- }
-
- if ($atomic_type instanceof TNull
- || $atomic_type instanceof TFalse
- ) {
- $valid_strings[] = new TLiteralString('');
- continue;
- }
-
- if ($atomic_type instanceof TMixed
- || $atomic_type instanceof TResource
- || $atomic_type instanceof Scalar
- ) {
- $castable_types[] = new TString();
-
- continue;
- }
-
- if ($atomic_type instanceof TNamedObject
- || $atomic_type instanceof TObjectWithProperties
- ) {
- $intersection_types = [$atomic_type];
-
- if ($atomic_type->extra_types) {
- $intersection_types = array_merge($intersection_types, $atomic_type->extra_types);
- }
-
- foreach ($intersection_types as $intersection_type) {
- if ($intersection_type instanceof TNamedObject) {
- $intersection_method_id = new MethodIdentifier(
- $intersection_type->value,
- '__tostring'
- );
-
- if ($codebase->methods->methodExists(
- $intersection_method_id,
- $context->calling_method_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- )) {
- $return_type = $codebase->methods->getMethodReturnType(
- $intersection_method_id,
- $self_class
- ) ?? Type::getString();
-
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($intersection_method_id);
-
- MethodCallReturnTypeFetcher::taintMethodCallResult(
- $statements_analyzer,
- $return_type,
- $stmt,
- $stmt,
- [],
- $intersection_method_id,
- $declaring_method_id,
- $intersection_type->value . '::__toString',
- $context
- );
-
- if ($statements_analyzer->data_flow_graph) {
- $parent_nodes = array_merge($return_type->parent_nodes, $parent_nodes);
- }
-
- $castable_types = array_merge(
- $castable_types,
- array_values($return_type->getAtomicTypes())
- );
-
- continue 2;
- }
- }
-
- if ($intersection_type instanceof TObjectWithProperties
- && isset($intersection_type->methods['__toString'])
- ) {
- $castable_types[] = new TString();
-
- continue 2;
- }
- }
- }
-
- if ($atomic_type instanceof TTemplateParam) {
- $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes());
-
- continue;
- }
-
- $invalid_casts[] = $atomic_type->getId();
- }
-
- if ($invalid_casts) {
- if ($valid_strings || $castable_types) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidCast(
- $invalid_casts[0] . ' cannot be cast to string',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidCast(
- $invalid_casts[0] . ' cannot be cast to string',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } elseif ($explicit_cast && !$castable_types) {
- // todo: emit error here
- }
-
- $valid_types = array_merge($valid_strings, $castable_types);
-
- if (!$valid_types) {
- $str_type = Type::getString();
- } else {
- $str_type = TypeCombiner::combine(
- $valid_types,
- $codebase
- );
- }
-
- if ($statements_analyzer->data_flow_graph) {
- $str_type->parent_nodes = $parent_nodes;
- }
-
- return $str_type;
- }
-
- private static function checkExprGeneralUse(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Cast $stmt,
- Context $context
- ): bool {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
- $retVal = ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context);
- $context->inside_general_use = $was_inside_general_use;
- return $retVal;
- }
-
- private static function handleRedundantCast(
- Union $maybe_type,
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Cast $stmt
- ): void {
- $codebase = $statements_analyzer->getCodebase();
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- $file_manipulation = null;
- if ($maybe_type->from_docblock) {
- $issue = new RedundantCastGivenDocblockType(
- 'Redundant cast to ' . $maybe_type->getKey() . ' given docblock-provided type',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['RedundantCastGivenDocblockType'])
- ) {
- $file_manipulation = new FileManipulation(
- (int) $stmt->getAttribute('startFilePos'),
- (int) $stmt->expr->getAttribute('startFilePos'),
- ''
- );
- }
- } else {
- $issue = new RedundantCast(
- 'Redundant cast to ' . $maybe_type->getKey(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['RedundantCast'])
- ) {
- $file_manipulation = new FileManipulation(
- (int) $stmt->getAttribute('startFilePos'),
- (int) $stmt->expr->getAttribute('startFilePos'),
- ''
- );
- }
- }
-
- if ($file_manipulation) {
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), [$file_manipulation]);
- }
-
-
- if (IssueBuffer::accepts($issue, $statements_analyzer->getSuppressedIssues())) {
- // fall through
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php
deleted file mode 100644
index 45ed9e9..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CloneAnalyzer.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\MethodAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Issue\InvalidClone;
-use Psalm\Issue\MixedClone;
-use Psalm\Issue\PossiblyInvalidClone;
-use Psalm\IssueBuffer;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TTemplateParam;
-
-use function array_merge;
-use function array_pop;
-
-class CloneAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Clone_ $stmt,
- Context $context
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
- $codebase_methods = $codebase->methods;
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- $stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($stmt_expr_type) {
- $clone_type = $stmt_expr_type;
-
- $immutable_cloned = false;
-
- $invalid_clones = [];
- $mixed_clone = false;
-
- $possibly_valid = false;
- $atomic_types = $clone_type->getAtomicTypes();
-
- while ($atomic_types) {
- $clone_type_part = array_pop($atomic_types);
-
- if ($clone_type_part instanceof TMixed) {
- $mixed_clone = true;
- } elseif ($clone_type_part instanceof TObject) {
- $possibly_valid = true;
- } elseif ($clone_type_part instanceof TNamedObject) {
- if (!$codebase->classlikes->classOrInterfaceExists($clone_type_part->value)) {
- $invalid_clones[] = $clone_type_part->getId();
- } else {
- $clone_method_id = new MethodIdentifier(
- $clone_type_part->value,
- '__clone'
- );
-
- $does_method_exist = $codebase_methods->methodExists(
- $clone_method_id,
- $context->calling_method_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
- $is_method_visible = MethodAnalyzer::isMethodVisible(
- $clone_method_id,
- $context,
- $statements_analyzer->getSource()
- );
- if ($does_method_exist && !$is_method_visible) {
- $invalid_clones[] = $clone_type_part->getId();
- } else {
- $possibly_valid = true;
- $immutable_cloned = true;
- }
- }
- } elseif ($clone_type_part instanceof TTemplateParam) {
- $atomic_types = array_merge($atomic_types, $clone_type_part->as->getAtomicTypes());
- } else {
- if ($clone_type_part instanceof TFalse
- && $clone_type->ignore_falsable_issues
- ) {
- continue;
- }
-
- if ($clone_type_part instanceof TNull
- && $clone_type->ignore_nullable_issues
- ) {
- continue;
- }
-
- $invalid_clones[] = $clone_type_part->getId();
- }
- }
-
- if ($mixed_clone) {
- IssueBuffer::maybeAdd(
- new MixedClone(
- 'Cannot clone mixed',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($invalid_clones) {
- if ($possibly_valid) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidClone(
- 'Cannot clone ' . $invalid_clones[0],
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidClone(
- 'Cannot clone ' . $invalid_clones[0],
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return true;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_expr_type);
-
- if ($immutable_cloned) {
- $stmt_expr_type = clone $stmt_expr_type;
- $statements_analyzer->node_data->setType($stmt, $stmt_expr_type);
- $stmt_expr_type->reference_free = true;
- $stmt_expr_type->allow_mutations = true;
- }
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php
deleted file mode 100644
index e874371..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EmptyAnalyzer.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\Issue\InvalidArgument;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-
-class EmptyAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Empty_ $stmt,
- Context $context
- ): void {
- IssetAnalyzer::analyzeIssetVar($statements_analyzer, $stmt->expr, $context);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (isset($codebase->config->forbidden_functions['empty'])) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'You have forbidden the use of empty',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr))
- && $stmt_expr_type->hasBool()
- && $stmt_expr_type->isSingle()
- && !$stmt_expr_type->from_docblock
- ) {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'Calling empty on a boolean value is almost certainly unintended',
- new CodeLocation($statements_analyzer->getSource(), $stmt->expr),
- 'empty'
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php
deleted file mode 100644
index d90b9dc..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EncapsulatedStringAnalyzer.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use PhpParser\Node\Scalar\EncapsedStringPart;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonspecificLiteralInt;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Union;
-
-use function assert;
-use function in_array;
-
-class EncapsulatedStringAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Scalar\Encapsed $stmt,
- Context $context
- ): bool {
- $stmt_type = Type::getString();
-
- $non_empty = false;
-
- $all_literals = true;
-
- $literal_string = "";
-
- foreach ($stmt->parts as $part) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $part, $context) === false) {
- return false;
- }
-
- $part_type = $statements_analyzer->node_data->getType($part);
-
- if ($part_type !== null) {
- $casted_part_type = CastAnalyzer::castStringAttempt(
- $statements_analyzer,
- $context,
- $part_type,
- $part
- );
-
- if (!$casted_part_type->allLiterals()) {
- $all_literals = false;
- } elseif (!$non_empty) {
- // Check if all literals are nonempty
- $non_empty = true;
- foreach ($casted_part_type->getAtomicTypes() as $atomic_literal) {
- if (!$atomic_literal instanceof TLiteralInt
- && !$atomic_literal instanceof TNonspecificLiteralInt
- && !$atomic_literal instanceof TLiteralFloat
- && !$atomic_literal instanceof TNonEmptyNonspecificLiteralString
- && !($atomic_literal instanceof TLiteralString && $atomic_literal->value !== "")
- ) {
- $non_empty = false;
- break;
- }
- }
- }
-
- if ($literal_string !== null) {
- if ($casted_part_type->isSingleLiteral()) {
- $literal_string .= $casted_part_type->getSingleLiteral()->value;
- } else {
- $literal_string = null;
- }
- }
-
- if ($statements_analyzer->data_flow_graph
- && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $var_location = new CodeLocation($statements_analyzer, $part);
-
- $new_parent_node = DataFlowNode::getForAssignment('concat', $var_location);
- $statements_analyzer->data_flow_graph->addNode($new_parent_node);
-
- $stmt_type->parent_nodes[$new_parent_node->id] = $new_parent_node;
-
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- if ($casted_part_type->parent_nodes) {
- foreach ($casted_part_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'concat',
- $added_taints,
- $removed_taints
- );
- }
- }
- }
- } elseif ($part instanceof EncapsedStringPart) {
- if ($literal_string !== null) {
- $literal_string .= $part->value;
- }
- $non_empty = $non_empty || $part->value !== "";
- } else {
- $all_literals = false;
- $literal_string = null;
- }
- }
-
- if ($non_empty) {
- if ($literal_string !== null) {
- $new_type = Type::getString($literal_string);
- } elseif ($all_literals) {
- $new_type = new Union([new TNonEmptyNonspecificLiteralString()]);
- } else {
- $new_type = new Union([new TNonEmptyString()]);
- }
- } elseif ($all_literals) {
- $new_type = new Union([new TNonspecificLiteralString()]);
- }
- if (isset($new_type)) {
- assert($new_type instanceof Union);
- $new_type->parent_nodes = $stmt_type->parent_nodes;
- $stmt_type = $new_type;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php
deleted file mode 100644
index c8df04d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php
+++ /dev/null
@@ -1,85 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type\TaintKind;
-
-use function in_array;
-
-/**
- * @internal
- */
-class EvalAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Eval_ $stmt,
- Context $context
- ): void {
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context);
-
- $codebase = $statements_analyzer->getCodebase();
-
- $expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($expr_type) {
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $expr_type->parent_nodes
- && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $arg_location = new CodeLocation($statements_analyzer->getSource(), $stmt->expr);
-
- $eval_param_sink = TaintSink::getForMethodArgument(
- 'eval',
- 'eval',
- 0,
- $arg_location,
- $arg_location
- );
-
- $eval_param_sink->taints = [TaintKind::INPUT_EVAL];
-
- $statements_analyzer->data_flow_graph->addSink($eval_param_sink);
-
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- foreach ($expr_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $eval_param_sink,
- 'arg',
- $added_taints,
- $removed_taints
- );
- }
- }
- }
-
- if (isset($codebase->config->forbidden_functions['eval'])) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'You have forbidden the use of eval',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $context->check_classes = false;
- $context->check_variables = false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php
deleted file mode 100644
index 308a5e7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExitAnalyzer.php
+++ /dev/null
@@ -1,145 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser\Node\Expr\Exit_;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\Issue\ImpureFunctionCall;
-use Psalm\IssueBuffer;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\TaintKind;
-use Psalm\Type\Union;
-
-class ExitAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- Exit_ $stmt,
- Context $context
- ): bool {
- $expr_type = null;
-
- $config = $statements_analyzer->getProjectAnalyzer()->getConfig();
-
- $forbidden = null;
-
- if (isset($config->forbidden_functions['exit'])
- && $stmt->getAttribute('kind') === Exit_::KIND_EXIT
- ) {
- $forbidden = 'exit';
- } elseif (isset($config->forbidden_functions['die'])
- && $stmt->getAttribute('kind') === Exit_::KIND_DIE
- ) {
- $forbidden = 'die';
- }
-
- if ($forbidden) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'You have forbidden the use of ' . $forbidden,
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($stmt->expr) {
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- $call_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $echo_param_sink = TaintSink::getForMethodArgument(
- 'exit',
- 'exit',
- 0,
- null,
- $call_location
- );
-
- $echo_param_sink->taints = [
- TaintKind::INPUT_HTML,
- TaintKind::INPUT_HAS_QUOTES,
- TaintKind::USER_SECRET,
- TaintKind::SYSTEM_SECRET
- ];
-
- $statements_analyzer->data_flow_graph->addSink($echo_param_sink);
- }
-
- if ($expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
- $exit_param = new FunctionLikeParameter(
- 'var',
- false
- );
-
- if (ArgumentAnalyzer::verifyType(
- $statements_analyzer,
- $expr_type,
- new Union([new TInt(), new TString()]),
- null,
- 'exit',
- null,
- 0,
- new CodeLocation($statements_analyzer->getSource(), $stmt->expr),
- $stmt->expr,
- $context,
- $exit_param,
- false,
- null,
- true,
- true,
- new CodeLocation($statements_analyzer, $stmt)
- ) === false) {
- return false;
- }
- }
-
- $context->inside_call = false;
- }
-
- if ($expr_type
- && !$expr_type->isInt()
- && !$context->collect_mutations
- && !$context->collect_initializations
- ) {
- if ($context->mutation_free || $context->external_mutation_free) {
- $function_name = $stmt->getAttribute('kind') === Exit_::KIND_DIE ? 'die' : 'exit';
-
- IssueBuffer::maybeAdd(
- new ImpureFunctionCall(
- 'Cannot call ' . $function_name . ' with a non-integer argument from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getEmpty());
-
- $context->has_returned = true;
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php
deleted file mode 100644
index ed779f5..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ExpressionIdentifier.php
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\Config;
-use Psalm\FileSource;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-
-use function count;
-use function implode;
-use function in_array;
-use function is_string;
-use function strtolower;
-
-class ExpressionIdentifier
-{
- public static function getVarId(
- PhpParser\Node\Expr $stmt,
- ?string $this_class_name,
- ?FileSource $source = null,
- ?int &$nesting = null
- ): ?string {
- if ($stmt instanceof PhpParser\Node\Expr\Variable && is_string($stmt->name)) {
- return '$' . $stmt->name;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\StaticPropertyFetch
- && $stmt->name instanceof PhpParser\Node\Identifier
- && $stmt->class instanceof PhpParser\Node\Name
- ) {
- if (count($stmt->class->parts) === 1
- && in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
- ) {
- if (!$this_class_name) {
- $fq_class_name = $stmt->class->parts[0];
- } else {
- $fq_class_name = $this_class_name;
- }
- } else {
- $fq_class_name = $source
- ? ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $source->getAliases()
- )
- : implode('\\', $stmt->class->parts);
- }
-
- return $fq_class_name . '::$' . $stmt->name->name;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch && $stmt->name instanceof PhpParser\Node\Identifier) {
- $object_id = self::getVarId($stmt->var, $this_class_name, $source);
-
- if (!$object_id) {
- return null;
- }
-
- return $object_id . '->' . $stmt->name->name;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch && $nesting !== null) {
- ++$nesting;
-
- return self::getVarId($stmt->var, $this_class_name, $source, $nesting);
- }
-
- return null;
- }
-
- public static function getRootVarId(
- PhpParser\Node\Expr $stmt,
- ?string $this_class_name,
- ?FileSource $source = null
- ): ?string {
- if ($stmt instanceof PhpParser\Node\Expr\Variable
- || $stmt instanceof PhpParser\Node\Expr\StaticPropertyFetch
- ) {
- return self::getVarId($stmt, $this_class_name, $source);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch && $stmt->name instanceof PhpParser\Node\Identifier) {
- $property_root = self::getRootVarId($stmt->var, $this_class_name, $source);
-
- if ($property_root) {
- return $property_root . '->' . $stmt->name->name;
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- return self::getRootVarId($stmt->var, $this_class_name, $source);
- }
-
- return null;
- }
-
- public static function getArrayVarId(
- PhpParser\Node\Expr $stmt,
- ?string $this_class_name,
- ?FileSource $source = null
- ): ?string {
- if ($stmt instanceof PhpParser\Node\Expr\Assign) {
- return self::getArrayVarId($stmt->var, $this_class_name, $source);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- $root_var_id = self::getArrayVarId($stmt->var, $this_class_name, $source);
-
- $offset = null;
-
- if ($root_var_id) {
- if ($stmt->dim instanceof PhpParser\Node\Scalar\String_
- || $stmt->dim instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $offset = $stmt->dim instanceof PhpParser\Node\Scalar\String_
- ? '\'' . $stmt->dim->value . '\''
- : $stmt->dim->value;
- } elseif ($stmt->dim instanceof PhpParser\Node\Expr\Variable
- && is_string($stmt->dim->name)
- ) {
- $offset = '$' . $stmt->dim->name;
- } elseif ($stmt->dim instanceof PhpParser\Node\Expr\ConstFetch) {
- $offset = implode('\\', $stmt->dim->name->parts);
- } elseif ($stmt->dim instanceof PhpParser\Node\Expr\PropertyFetch) {
- $object_id = self::getArrayVarId($stmt->dim->var, $this_class_name, $source);
-
- if ($object_id && $stmt->dim->name instanceof PhpParser\Node\Identifier) {
- $offset = $object_id . '->' . $stmt->dim->name;
- }
- } elseif ($stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch
- && $stmt->dim->name instanceof PhpParser\Node\Identifier
- && $stmt->dim->class instanceof PhpParser\Node\Name
- && $stmt->dim->class->parts[0] === 'static'
- ) {
- $offset = 'static::' . $stmt->dim->name;
- } elseif ($stmt->dim
- && $source instanceof StatementsAnalyzer
- && ($stmt_dim_type = $source->node_data->getType($stmt->dim))
- && (!$stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch
- || !$stmt->dim->name instanceof PhpParser\Node\Identifier
- || $stmt->dim->name->name !== 'class'
- )
- ) {
- if ($stmt_dim_type->isSingleStringLiteral()) {
- $offset = '\'' . $stmt_dim_type->getSingleStringLiteral()->value . '\'';
- } elseif ($stmt_dim_type->isSingleIntLiteral()) {
- $offset = $stmt_dim_type->getSingleIntLiteral()->value;
- }
- } elseif ($stmt->dim instanceof PhpParser\Node\Expr\ClassConstFetch
- && $stmt->dim->name instanceof PhpParser\Node\Identifier
- ) {
- /** @var string|null */
- $resolved_name = $stmt->dim->class->getAttribute('resolvedName');
-
- if ($resolved_name) {
- $offset = $resolved_name . '::' . $stmt->dim->name;
- }
- }
-
- return $offset !== null ? $root_var_id . '[' . $offset . ']' : null;
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch) {
- $object_id = self::getArrayVarId($stmt->var, $this_class_name, $source);
-
- if (!$object_id) {
- return null;
- }
-
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- return $object_id . '->' . $stmt->name;
- }
-
- if ($source instanceof StatementsAnalyzer
- && ($stmt_name_type = $source->node_data->getType($stmt->name))
- && $stmt_name_type->isSingleStringLiteral()) {
- return $object_id . '->' . $stmt_name_type->getSingleStringLiteral()->value;
- }
-
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch
- && $stmt->name instanceof PhpParser\Node\Identifier
- ) {
- /** @var string|null */
- $resolved_name = $stmt->class->getAttribute('resolvedName');
-
- if ($resolved_name) {
- if (($resolved_name === 'self' || $resolved_name === 'static') && $this_class_name) {
- $resolved_name = $this_class_name;
- }
-
- return $resolved_name . '::' . $stmt->name;
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\MethodCall
- && $stmt->name instanceof PhpParser\Node\Identifier
- && !$stmt->isFirstClassCallable()
- && !$stmt->getArgs()
- ) {
- $config = Config::getInstance();
-
- if ($config->memoize_method_calls || $stmt->getAttribute('memoizable', false)) {
- $lhs_var_name = self::getArrayVarId(
- $stmt->var,
- $this_class_name,
- $source
- );
-
- if (!$lhs_var_name) {
- return null;
- }
-
- return $lhs_var_name . '->' . strtolower($stmt->name->name) . '()';
- }
- }
-
- return self::getVarId($stmt, $this_class_name, $source);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php
deleted file mode 100644
index 6dd4463..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ArrayFetchAnalyzer.php
+++ /dev/null
@@ -1,2060 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\EmptyArrayAccess;
-use Psalm\Issue\InvalidArrayAccess;
-use Psalm\Issue\InvalidArrayAssignment;
-use Psalm\Issue\InvalidArrayOffset;
-use Psalm\Issue\MixedArrayAccess;
-use Psalm\Issue\MixedArrayAssignment;
-use Psalm\Issue\MixedArrayOffset;
-use Psalm\Issue\MixedArrayTypeCoercion;
-use Psalm\Issue\MixedStringOffsetAssignment;
-use Psalm\Issue\NullArrayAccess;
-use Psalm\Issue\NullArrayOffset;
-use Psalm\Issue\PossiblyInvalidArrayAccess;
-use Psalm\Issue\PossiblyInvalidArrayAssignment;
-use Psalm\Issue\PossiblyInvalidArrayOffset;
-use Psalm\Issue\PossiblyNullArrayAccess;
-use Psalm\Issue\PossiblyNullArrayAssignment;
-use Psalm\Issue\PossiblyNullArrayOffset;
-use Psalm\Issue\PossiblyUndefinedArrayOffset;
-use Psalm\Issue\PossiblyUndefinedIntArrayOffset;
-use Psalm\Issue\PossiblyUndefinedStringArrayOffset;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualConstFetch;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Node\VirtualName;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TSingleLetter;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateIndexedAccess;
-use Psalm\Type\Atomic\TTemplateKeyOf;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_keys;
-use function array_map;
-use function array_pop;
-use function array_values;
-use function count;
-use function implode;
-use function in_array;
-use function is_int;
-use function preg_match;
-use function strlen;
-use function strtolower;
-
-/**
- * @internal
- */
-class ArrayFetchAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Context $context
- ): bool {
- $array_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($stmt->dim) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- $was_inside_unset = $context->inside_unset;
- $context->inside_unset = false;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->dim, $context) === false) {
- $context->inside_unset = $was_inside_unset;
- $context->inside_general_use = $was_inside_general_use;
-
- return false;
- }
-
- $context->inside_unset = $was_inside_unset;
-
- $context->inside_general_use = $was_inside_general_use;
- }
-
- $keyed_array_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $dim_var_id = null;
- $new_offset_type = null;
-
- if ($stmt->dim) {
- $used_key_type = $statements_analyzer->node_data->getType($stmt->dim) ?? Type::getMixed();
-
- $dim_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->dim,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
- } else {
- $used_key_type = Type::getInt();
- }
-
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $stmt->var,
- $context
- ) === false) {
- return false;
- }
-
- $stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($keyed_array_var_id
- && $context->hasVariable($keyed_array_var_id)
- && !$context->vars_in_scope[$keyed_array_var_id]->possibly_undefined
- && $stmt_var_type
- && !$stmt_var_type->hasClassStringMap()
- ) {
- $stmt_type = clone $context->vars_in_scope[$keyed_array_var_id];
-
- $statements_analyzer->node_data->setType(
- $stmt,
- $stmt_type
- );
-
- self::taintArrayFetch(
- $statements_analyzer,
- $stmt->var,
- $keyed_array_var_id,
- $stmt_type,
- $used_key_type,
- $context
- );
-
- return true;
- }
-
- $can_store_result = false;
-
- if ($stmt_var_type) {
- if ($stmt_var_type->isNull()) {
- if (!$context->inside_isset) {
- IssueBuffer::maybeAdd(
- new NullArrayAccess(
- 'Cannot access array value on null variable ' . $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes($stmt_type, Type::getNull())
- );
-
- return true;
- }
-
- $stmt_type = self::getArrayAccessTypeGivenOffset(
- $statements_analyzer,
- $stmt,
- $stmt_var_type,
- $used_key_type,
- false,
- $array_var_id,
- $context,
- null
- );
-
- if ($stmt->dim && $stmt_var_type->hasArray()) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList|TClassStringMap
- */
- $array_type = $stmt_var_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TClassStringMap) {
- $array_value_type = Type::getMixed();
- } elseif ($array_type instanceof TArray) {
- $array_value_type = $array_type->type_params[1];
- } elseif ($array_type instanceof TList) {
- $array_value_type = $array_type->type_param;
- } else {
- $array_value_type = $array_type->getGenericValueType();
- }
-
- if ($context->inside_assignment || !$array_value_type->isMixed()) {
- $can_store_result = true;
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if ($context->inside_isset
- && $stmt->dim
- && ($stmt_dim_type = $statements_analyzer->node_data->getType($stmt->dim))
- && $stmt_var_type->hasArray()
- && ($stmt->var instanceof PhpParser\Node\Expr\ClassConstFetch
- || $stmt->var instanceof PhpParser\Node\Expr\ConstFetch)
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_type = $stmt_var_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TArray) {
- $const_array_key_type = $array_type->type_params[0];
- } elseif ($array_type instanceof TList) {
- $const_array_key_type = Type::getInt();
- } else {
- $const_array_key_type = $array_type->getGenericKeyType();
- }
-
- if ($dim_var_id
- && !$const_array_key_type->hasMixed()
- && !$stmt_dim_type->hasMixed()
- ) {
- $new_offset_type = clone $stmt_dim_type;
- $const_array_key_atomic_types = $const_array_key_type->getAtomicTypes();
-
- foreach ($new_offset_type->getAtomicTypes() as $offset_key => $offset_atomic_type) {
- if ($offset_atomic_type instanceof TString
- || $offset_atomic_type instanceof TInt
- ) {
- if (!isset($const_array_key_atomic_types[$offset_key])
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- new Union([$offset_atomic_type]),
- $const_array_key_type
- )
- ) {
- $new_offset_type->removeType($offset_key);
- }
- } elseif (!UnionTypeComparator::isContainedBy(
- $codebase,
- $const_array_key_type,
- new Union([$offset_atomic_type])
- )) {
- $new_offset_type->removeType($offset_key);
- }
- }
- }
- }
- }
-
- if ($keyed_array_var_id
- && $context->hasVariable($keyed_array_var_id)
- && (!($stmt_type = $statements_analyzer->node_data->getType($stmt)) || $stmt_type->isVanillaMixed())
- ) {
- $statements_analyzer->node_data->setType($stmt, $context->vars_in_scope[$keyed_array_var_id]);
- }
-
- if (!($stmt_type = $statements_analyzer->node_data->getType($stmt))) {
- $stmt_type = Type::getMixed();
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
- } else {
- if ($stmt_type->possibly_undefined
- && !$context->inside_isset
- && !$context->inside_unset
- && ($stmt_var_type && !$stmt_var_type->hasMixed())
- ) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedArrayOffset(
- 'Possibly undefined array key ' . $keyed_array_var_id
- . ' on ' . $stmt_var_type->getId(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $stmt_type->possibly_undefined = false;
- }
-
- if ($context->inside_isset && $dim_var_id && $new_offset_type && !$new_offset_type->isUnionEmpty()) {
- $context->vars_in_scope[$dim_var_id] = $new_offset_type;
- }
-
- if ($keyed_array_var_id && !$context->inside_isset && $can_store_result) {
- $context->vars_in_scope[$keyed_array_var_id] = $stmt_type;
- $context->vars_possibly_in_scope[$keyed_array_var_id] = true;
-
- // reference the variable too
- $context->hasVariable($keyed_array_var_id);
- }
-
- self::taintArrayFetch(
- $statements_analyzer,
- $stmt->var,
- $keyed_array_var_id,
- $stmt_type,
- $used_key_type,
- $context
- );
-
- return true;
- }
-
- /**
- * Used to create a path between a variable $foo and $foo["a"]
- */
- public static function taintArrayFetch(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $var,
- ?string $keyed_array_var_id,
- Union $stmt_type,
- Union $offset_type,
- ?Context $context = null
- ): void {
- if ($statements_analyzer->data_flow_graph
- && ($stmt_var_type = $statements_analyzer->node_data->getType($var))
- && $stmt_var_type->parent_nodes
- ) {
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $stmt_var_type->parent_nodes = [];
- return;
- }
-
- $added_taints = [];
- $removed_taints = [];
-
- if ($context) {
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($var, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
- }
-
- $var_location = new CodeLocation($statements_analyzer->getSource(), $var);
-
- $new_parent_node = DataFlowNode::getForAssignment(
- $keyed_array_var_id ?: 'arrayvalue-fetch',
- $var_location
- );
-
- $array_key_node = null;
-
- $statements_analyzer->data_flow_graph->addNode($new_parent_node);
-
- $dim_value = $offset_type->isSingleStringLiteral()
- ? $offset_type->getSingleStringLiteral()->value
- : ($offset_type->isSingleIntLiteral()
- ? $offset_type->getSingleIntLiteral()->value
- : null);
-
- if ($keyed_array_var_id === null && $dim_value === null) {
- $array_key_node = DataFlowNode::getForAssignment(
- 'arraykey-fetch',
- $var_location
- );
-
- $statements_analyzer->data_flow_graph->addNode($array_key_node);
- }
-
- foreach ($stmt_var_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $new_parent_node,
- 'arrayvalue-fetch' . ($dim_value !== null ? '-\'' . $dim_value . '\'' : ''),
- $added_taints,
- $removed_taints
- );
-
- if ($stmt_type->by_ref) {
- $statements_analyzer->data_flow_graph->addPath(
- $new_parent_node,
- $parent_node,
- 'arrayvalue-assignment' . ($dim_value !== null ? '-\'' . $dim_value . '\'' : ''),
- $added_taints,
- $removed_taints
- );
- }
-
- if ($array_key_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $array_key_node,
- 'arraykey-fetch',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $stmt_type->parent_nodes = [$new_parent_node->id => $new_parent_node];
-
- if ($array_key_node) {
- $offset_type->parent_nodes = [$array_key_node->id => $array_key_node];
- }
- }
- }
-
- /**
- * @psalm-suppress ComplexMethod to be refactored.
- * Good type/bad type behaviour could be mutualised with ArrayAnalyzer
- */
- public static function getArrayAccessTypeGivenOffset(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Union $array_type,
- Union $offset_type,
- bool $in_assignment,
- ?string $array_var_id,
- Context $context,
- PhpParser\Node\Expr $assign_value = null,
- Union $replacement_type = null
- ): Union {
- $codebase = $statements_analyzer->getCodebase();
-
- $has_array_access = false;
- $non_array_types = [];
-
- $has_valid_expected_offset = false;
- $expected_offset_types = [];
-
- $key_values = [];
-
- if ($stmt->dim instanceof PhpParser\Node\Scalar\String_
- || $stmt->dim instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $key_values[] = $stmt->dim->value;
- } elseif ($stmt->dim && ($stmt_dim_type = $statements_analyzer->node_data->getType($stmt->dim))) {
- $string_literals = $stmt_dim_type->getLiteralStrings();
- $int_literals = $stmt_dim_type->getLiteralInts();
-
- $all_atomic_types = $stmt_dim_type->getAtomicTypes();
-
- if (count($string_literals) + count($int_literals) === count($all_atomic_types)) {
- foreach ($string_literals as $string_literal) {
- $key_values[] = $string_literal->value;
- }
-
- foreach ($int_literals as $int_literal) {
- $key_values[] = $int_literal->value;
- }
- }
- }
-
- $array_access_type = null;
-
- if ($offset_type->isNull()) {
- IssueBuffer::maybeAdd(
- new NullArrayOffset(
- 'Cannot access value on variable ' . $array_var_id . ' using null offset',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- if ($in_assignment) {
- $offset_type->removeType('null');
- $offset_type->addType(new TLiteralString(''));
- }
- }
-
- if ($offset_type->isNullable() && !$context->inside_isset) {
- if (!$offset_type->ignore_nullable_issues) {
- IssueBuffer::maybeAdd(
- new PossiblyNullArrayOffset(
- 'Cannot access value on variable ' . $array_var_id
- . ' using possibly null offset ' . $offset_type,
- new CodeLocation($statements_analyzer->getSource(), $stmt->var)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($in_assignment) {
- $offset_type->removeType('null');
-
- if (!$offset_type->ignore_nullable_issues) {
- $offset_type->addType(new TLiteralString(''));
- }
- }
- }
-
- if ($array_type->isArray()) {
- $has_valid_absolute_offset = self::checkArrayOffsetType(
- $offset_type,
- $offset_type->getAtomicTypes(),
- $codebase
- );
-
- if ($has_valid_absolute_offset === false) {
- //we didn't find a single type that could be valid
- $expected_offset_types[] = 'array-key';
- }
- } else {
- //on not-arrays, the type is considered valid
- $has_valid_absolute_offset = true;
- }
-
- foreach ($array_type->getAtomicTypes() as $type_string => $type) {
- $original_type = $type;
-
- if ($type instanceof TMixed || $type instanceof TTemplateParam || $type instanceof TEmpty) {
- if (!$type instanceof TTemplateParam || $type->as->isMixed() || !$type->as->isSingle()) {
- $array_access_type = self::handleMixedArrayAccess(
- $context,
- $statements_analyzer,
- $codebase,
- $in_assignment,
- $array_var_id,
- $stmt,
- $array_access_type,
- $type
- );
-
- $has_valid_expected_offset = true;
-
- continue;
- }
-
- $type = clone $type->as->getSingleAtomic();
- }
-
- if ($type instanceof TNull) {
- if ($array_type->ignore_nullable_issues) {
- continue;
- }
-
- if ($in_assignment) {
- if ($replacement_type) {
- $array_access_type = Type::combineUnionTypes($array_access_type, clone $replacement_type);
- } else {
- IssueBuffer::maybeAdd(
- new PossiblyNullArrayAssignment(
- 'Cannot access array value on possibly null variable ' . $array_var_id .
- ' of type ' . $array_type,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $array_access_type = new Union([new TEmpty]);
- }
- } else {
- if (!$context->inside_isset && !MethodCallAnalyzer::hasNullsafe($stmt->var)) {
- IssueBuffer::maybeAdd(
- new PossiblyNullArrayAccess(
- 'Cannot access array value on possibly null variable ' . $array_var_id .
- ' of type ' . $array_type,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $array_access_type = Type::combineUnionTypes($array_access_type, Type::getNull());
- }
-
- continue;
- }
-
- if ($type instanceof TArray
- || $type instanceof TKeyedArray
- || $type instanceof TList
- || $type instanceof TClassStringMap
- ) {
- self::handleArrayAccessOnArray(
- $in_assignment,
- $type,
- $key_values,
- $array_type,
- $type_string,
- $stmt,
- $replacement_type,
- $offset_type,
- $original_type,
- $codebase,
- $array_var_id,
- $context,
- $statements_analyzer,
- $expected_offset_types,
- $array_access_type,
- $has_array_access,
- $has_valid_expected_offset
- );
-
- continue;
- }
-
- if ($type instanceof TString) {
- self::handleArrayAccessOnString(
- $statements_analyzer,
- $codebase,
- $stmt,
- $in_assignment,
- $context,
- $replacement_type,
- $type,
- $offset_type,
- $expected_offset_types,
- $array_access_type,
- $has_valid_expected_offset
- );
-
- continue;
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($type instanceof TFalse && $array_type->ignore_falsable_issues) {
- continue;
- }
-
- if ($type instanceof TNamedObject) {
- self::handleArrayAccessOnNamedObject(
- $statements_analyzer,
- $stmt,
- $type,
- $context,
- $in_assignment,
- $assign_value,
- $array_access_type,
- $has_array_access
- );
- } elseif (!$array_type->hasMixed()) {
- $non_array_types[] = (string)$type;
- }
- }
-
- if ($non_array_types) {
- if ($has_array_access) {
- if ($in_assignment) {
- if (IssueBuffer::accepts(
- new PossiblyInvalidArrayAssignment(
- 'Cannot access array value on non-array variable ' .
- $array_var_id . ' of type ' . $non_array_types[0],
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // do nothing
- }
- } elseif (!$context->inside_isset) {
- if (IssueBuffer::accepts(
- new PossiblyInvalidArrayAccess(
- 'Cannot access array value on non-array variable ' .
- $array_var_id . ' of type ' . $non_array_types[0],
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // do nothing
- }
- }
- } else {
- if ($in_assignment) {
- IssueBuffer::maybeAdd(
- new InvalidArrayAssignment(
- 'Cannot access array value on non-array variable ' .
- $array_var_id . ' of type ' . $non_array_types[0],
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidArrayAccess(
- 'Cannot access array value on non-array variable ' .
- $array_var_id . ' of type ' . $non_array_types[0],
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $array_access_type = Type::getMixed();
- }
- }
-
- if ($offset_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- IssueBuffer::maybeAdd(
- new MixedArrayOffset(
- 'Cannot access value on variable ' . $array_var_id . ' using mixed offset',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($expected_offset_types) {
- $invalid_offset_type = $expected_offset_types[0];
-
- $used_offset = 'using a ' . $offset_type->getId() . ' offset';
-
- if ($key_values) {
- $used_offset = "using offset value of '" . implode('|', $key_values) . "'";
- }
-
- if ($has_valid_expected_offset && $has_valid_absolute_offset && $context->inside_isset) {
- // do nothing
- } elseif ($has_valid_expected_offset && $has_valid_absolute_offset) {
- if (!$context->inside_unset) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidArrayOffset(
- 'Cannot access value on variable ' . $array_var_id . ' ' . $used_offset
- . ', expecting ' . $invalid_offset_type,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- } else {
- $good_types = [];
- $bad_types = [];
- foreach ($offset_type->getAtomicTypes() as $atomic_key_type) {
- if (!$atomic_key_type instanceof TString
- && !$atomic_key_type instanceof TInt
- && !$atomic_key_type instanceof TArrayKey
- && !$atomic_key_type instanceof TMixed
- && !$atomic_key_type instanceof TTemplateParam
- && !(
- $atomic_key_type instanceof TObjectWithProperties
- && isset($atomic_key_type->methods['__toString'])
- )
- ) {
- $bad_types[] = $atomic_key_type;
-
- if ($atomic_key_type instanceof TFalse) {
- $good_types[] = new TLiteralInt(0);
- } elseif ($atomic_key_type instanceof TTrue) {
- $good_types[] = new TLiteralInt(1);
- } elseif ($atomic_key_type instanceof TBool) {
- $good_types[] = new TLiteralInt(0);
- $good_types[] = new TLiteralInt(1);
- } elseif ($atomic_key_type instanceof TLiteralFloat) {
- $good_types[] = new TLiteralInt((int)$atomic_key_type->value);
- } elseif ($atomic_key_type instanceof TFloat) {
- $good_types[] = new TInt;
- } else {
- $good_types[] = new TArrayKey;
- }
- }
- }
-
- if ($bad_types && $good_types) {
- $offset_type->substitute(
- TypeCombiner::combine($bad_types, $codebase),
- TypeCombiner::combine($good_types, $codebase)
- );
- }
-
- IssueBuffer::maybeAdd(
- new InvalidArrayOffset(
- 'Cannot access value on variable ' . $array_var_id . ' ' . $used_offset
- . ', expecting ' . $invalid_offset_type,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- if ($array_access_type === null) {
- // shouldn’t happen, but don’t crash
- return Type::getMixed();
- }
-
- if ($array_type->by_ref) {
- $array_access_type->by_ref = true;
- }
-
- if ($in_assignment) {
- $array_type->bustCache();
- }
-
- return $array_access_type;
- }
-
- private static function checkLiteralIntArrayOffset(
- Union $offset_type,
- Union $expected_offset_type,
- ?string $array_var_id,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Context $context,
- StatementsAnalyzer $statements_analyzer
- ): void {
- if ($context->inside_isset || $context->inside_unset) {
- return;
- }
-
- if ($offset_type->hasLiteralInt()) {
- $found_match = false;
-
- foreach ($offset_type->getAtomicTypes() as $offset_type_part) {
- if ($array_var_id
- && $offset_type_part instanceof TLiteralInt
- && isset(
- $context->vars_in_scope[
- $array_var_id . '[' . $offset_type_part->value . ']'
- ]
- )
- && !$context->vars_in_scope[
- $array_var_id . '[' . $offset_type_part->value . ']'
- ]->possibly_undefined
- ) {
- $found_match = true;
- break;
- }
-
- if ($offset_type_part instanceof TPositiveInt) {
- $found_match = true;
- break;
- }
- }
-
- if (!$found_match) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedIntArrayOffset(
- 'Possibly undefined array offset \''
- . $offset_type->getId() . '\' '
- . 'is risky given expected type \''
- . $expected_offset_type->getId() . '\'.'
- . ' Consider using isset beforehand.',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- private static function checkLiteralStringArrayOffset(
- Union $offset_type,
- Union $expected_offset_type,
- ?string $array_var_id,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Context $context,
- StatementsAnalyzer $statements_analyzer
- ): void {
- if ($context->inside_isset || $context->inside_unset) {
- return;
- }
-
- if ($offset_type->hasLiteralString() && !$expected_offset_type->hasLiteralClassString()) {
- $found_match = false;
-
- foreach ($offset_type->getAtomicTypes() as $offset_type_part) {
- if ($array_var_id
- && $offset_type_part instanceof TLiteralString
- && isset(
- $context->vars_in_scope[
- $array_var_id . '[\'' . $offset_type_part->value . '\']'
- ]
- )
- && !$context->vars_in_scope[
- $array_var_id . '[\'' . $offset_type_part->value . '\']'
- ]->possibly_undefined
- ) {
- $found_match = true;
- break;
- }
- }
-
- if (!$found_match) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedStringArrayOffset(
- 'Possibly undefined array offset \''
- . $offset_type->getId() . '\' '
- . 'is risky given expected type \''
- . $expected_offset_type->getId() . '\'.'
- . ' Consider using isset beforehand.',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- public static function replaceOffsetTypeWithInts(Union $offset_type): Union
- {
- $offset_types = $offset_type->getAtomicTypes();
-
- $cloned = false;
-
- foreach ($offset_types as $key => $offset_type_part) {
- if ($offset_type_part instanceof TLiteralString) {
- if (preg_match('/^(0|[1-9][0-9]*)$/', $offset_type_part->value)) {
- if (!$cloned) {
- $offset_type = clone $offset_type;
- $cloned = true;
- }
- $offset_type->addType(new TLiteralInt((int) $offset_type_part->value));
- $offset_type->removeType($key);
- }
- } elseif ($offset_type_part instanceof TBool) {
- if (!$cloned) {
- $offset_type = clone $offset_type;
- $cloned = true;
- }
-
- if ($offset_type_part instanceof TFalse) {
- if (!$offset_type->ignore_falsable_issues) {
- $offset_type->addType(new TLiteralInt(0));
- $offset_type->removeType($key);
- }
- } elseif ($offset_type_part instanceof TTrue) {
- $offset_type->addType(new TLiteralInt(1));
- $offset_type->removeType($key);
- } else {
- $offset_type->addType(new TLiteralInt(0));
- $offset_type->addType(new TLiteralInt(1));
- $offset_type->removeType($key);
- }
- }
- }
-
- return $offset_type;
- }
-
- /**
- * @param TMixed|TTemplateParam|TEmpty $type
- */
- public static function handleMixedArrayAccess(
- Context $context,
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- bool $in_assignment,
- ?string $array_var_id,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- ?Union $array_access_type,
- Atomic $type
- ): Union {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- if (!$context->inside_isset) {
- if ($in_assignment) {
- IssueBuffer::maybeAdd(
- new MixedArrayAssignment(
- 'Cannot access array value on mixed variable ' . $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new MixedArrayAccess(
- 'Cannot access array value on mixed variable ' . $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (($data_flow_graph = $statements_analyzer->data_flow_graph)
- && $data_flow_graph instanceof VariableUseGraph
- && ($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
- ) {
- if ($stmt_var_type->parent_nodes) {
- $var_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var);
-
- $new_parent_node = DataFlowNode::getForAssignment('mixed-var-array-access', $var_location);
-
- $data_flow_graph->addNode($new_parent_node);
-
- foreach ($stmt_var_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath($parent_node, $new_parent_node, '=');
-
- $data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
-
- $stmt_var_type->parent_nodes = [
- $new_parent_node->id => $new_parent_node
- ];
- }
- }
-
- return Type::combineUnionTypes(
- $array_access_type,
- Type::getMixed($type instanceof TEmpty)
- );
- }
-
- /**
- * @param list<string> $expected_offset_types
- * @param TArray|TKeyedArray|TList|TClassStringMap $type
- * @param list<array-key> $key_values
- */
- private static function handleArrayAccessOnArray(
- bool $in_assignment,
- Atomic &$type,
- array &$key_values,
- Union $array_type,
- string $type_string,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- ?Union $replacement_type,
- Union &$offset_type,
- Atomic $original_type,
- Codebase $codebase,
- ?string $array_var_id,
- Context $context,
- StatementsAnalyzer $statements_analyzer,
- array &$expected_offset_types,
- ?Union &$array_access_type,
- bool &$has_array_access,
- bool &$has_valid_offset
- ): void {
- $has_array_access = true;
-
- if ($in_assignment
- && $type instanceof TArray
- && $type->type_params[0]->isEmpty()
- && $type->type_params[1]->isEmpty()
- ) {
- $from_empty_array = $type->type_params[0]->isEmpty() && $type->type_params[1]->isEmpty();
-
- if (count($key_values) === 1) {
- $from_mixed_array = $type->type_params[1]->isMixed();
-
- [$previous_key_type, $previous_value_type] = $type->type_params;
-
- // ok, type becomes an TKeyedArray
- $array_type->removeType($type_string);
- $type = new TKeyedArray([
- $key_values[0] => $from_mixed_array ? Type::getMixed() : Type::getEmpty()
- ]);
-
- $type->sealed = $from_empty_array;
-
- if (!$from_empty_array) {
- $type->previous_value_type = clone $previous_value_type;
- $type->previous_key_type = clone $previous_key_type;
- }
-
- $array_type->addType($type);
- } elseif (!$stmt->dim && $from_empty_array && $replacement_type) {
- $array_type->removeType($type_string);
- $array_type->addType(new TNonEmptyList($replacement_type));
- return;
- }
- } elseif ($in_assignment
- && $type instanceof TKeyedArray
- && $type->previous_value_type
- && $type->previous_value_type->isMixed()
- && count($key_values) === 1
- ) {
- $type->properties[$key_values[0]] = Type::getMixed();
- }
-
- $offset_type = self::replaceOffsetTypeWithInts($offset_type);
-
- if ($type instanceof TList
- && (($in_assignment && $stmt->dim)
- || $original_type instanceof TTemplateParam
- || !$offset_type->isInt())
- ) {
- $type = new TArray([Type::getInt(), $type->type_param]);
- }
-
- if ($type instanceof TArray) {
- self::handleArrayAccessOnTArray(
- $statements_analyzer,
- $codebase,
- $context,
- $stmt,
- $array_type,
- $array_var_id,
- $type,
- $offset_type,
- $in_assignment,
- $expected_offset_types,
- $replacement_type,
- $array_access_type,
- $original_type,
- $has_valid_offset
- );
- } elseif ($type instanceof TList) {
- self::handleArrayAccessOnList(
- $statements_analyzer,
- $codebase,
- $stmt,
- $type,
- $offset_type,
- $array_var_id,
- $key_values,
- $context,
- $in_assignment,
- $expected_offset_types,
- $replacement_type,
- $array_access_type,
- $has_valid_offset
- );
- } elseif ($type instanceof TClassStringMap) {
- self::handleArrayAccessOnClassStringMap(
- $codebase,
- $type,
- $offset_type,
- $replacement_type,
- $array_access_type
- );
- } else {
- self::handleArrayAccessOnKeyedArray(
- $statements_analyzer,
- $codebase,
- $key_values,
- $replacement_type,
- $array_access_type,
- $in_assignment,
- $stmt,
- $offset_type,
- $array_var_id,
- $context,
- $type,
- $array_type,
- $expected_offset_types,
- $type_string,
- $has_valid_offset
- );
- }
-
- if ($context->inside_isset) {
- $offset_type->ignore_isset = true;
- }
- }
-
- /**
- * @param list<string> $expected_offset_types
- */
- private static function handleArrayAccessOnTArray(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- Context $context,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Union $array_type,
- ?string $array_var_id,
- TArray $type,
- Union $offset_type,
- bool $in_assignment,
- array &$expected_offset_types,
- ?Union $replacement_type,
- ?Union &$array_access_type,
- Atomic $original_type,
- bool &$has_valid_offset
- ): void {
- // if we're assigning to an empty array with a key offset, refashion that array
- if ($in_assignment) {
- if ($type->type_params[0]->isEmpty()) {
- $type->type_params[0] = $offset_type->isMixed()
- ? Type::getArrayKey()
- : $offset_type;
- }
- } elseif (!$type->type_params[0]->isEmpty()) {
- $expected_offset_type = $type->type_params[0]->hasMixed()
- ? new Union([new TArrayKey])
- : $type->type_params[0];
-
- $templated_offset_type = null;
-
- foreach ($offset_type->getAtomicTypes() as $offset_atomic_type) {
- if ($offset_atomic_type instanceof TTemplateParam) {
- $templated_offset_type = $offset_atomic_type;
- }
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- if ($original_type instanceof TTemplateParam && $templated_offset_type) {
- foreach ($templated_offset_type->as->getAtomicTypes() as $offset_as) {
- if ($offset_as instanceof TTemplateKeyOf
- && $offset_as->param_name === $original_type->param_name
- && $offset_as->defining_class === $original_type->defining_class
- ) {
- $type->type_params[1] = new Union([
- new TTemplateIndexedAccess(
- $offset_as->param_name,
- $templated_offset_type->param_name,
- $offset_as->defining_class
- )
- ]);
-
- $has_valid_offset = true;
- }
- }
- } else {
- $offset_type_contained_by_expected = UnionTypeComparator::isContainedBy(
- $codebase,
- $offset_type,
- $expected_offset_type,
- true,
- $offset_type->ignore_falsable_issues,
- $union_comparison_results
- );
-
- if ($codebase->config->ensure_array_string_offsets_exist
- && $offset_type_contained_by_expected
- ) {
- //we already know we found a match, so if the array is non-empty and the key is a literal,
- //then no need to check for PossiblyUndefinedStringArrayOffset
- if (!$type instanceof TNonEmptyArray || !$type->type_params[0]->isSingleStringLiteral()) {
- self::checkLiteralStringArrayOffset(
- $offset_type,
- $expected_offset_type,
- $array_var_id,
- $stmt,
- $context,
- $statements_analyzer
- );
- }
- }
-
- if ($codebase->config->ensure_array_int_offsets_exist
- && $offset_type_contained_by_expected
- ) {
- self::checkLiteralIntArrayOffset(
- $offset_type,
- $expected_offset_type,
- $array_var_id,
- $stmt,
- $context,
- $statements_analyzer
- );
- }
-
- if ((!$offset_type_contained_by_expected
- && !$union_comparison_results->type_coerced_from_scalar)
- || $union_comparison_results->to_string_cast
- ) {
- if ($union_comparison_results->type_coerced_from_mixed
- && !$offset_type->isMixed()
- ) {
- IssueBuffer::maybeAdd(
- new MixedArrayTypeCoercion(
- 'Coercion from array offset type \'' . $offset_type->getId() . '\' '
- . 'to the expected type \'' . $expected_offset_type->getId() . '\'',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $expected_offset_types[] = $expected_offset_type->getId();
- }
-
- if (UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $offset_type,
- $expected_offset_type
- )) {
- $has_valid_offset = true;
- }
- } else {
- $has_valid_offset = true;
- }
- }
- }
-
- if (!$stmt->dim && $type instanceof TNonEmptyArray && $type->count !== null) {
- $type->count++;
- }
-
- if ($in_assignment && $replacement_type) {
- $type->type_params[1] = Type::combineUnionTypes(
- $type->type_params[1],
- $replacement_type,
- $codebase
- );
- }
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- $type->type_params[1]
- );
-
- if ($array_access_type->isEmpty()
- && !$array_type->hasMixed()
- && !$in_assignment
- && !$context->inside_isset
- ) {
- IssueBuffer::maybeAdd(
- new EmptyArrayAccess(
- 'Cannot access value on empty array variable ' . $array_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- if (!IssueBuffer::isRecording()) {
- $array_access_type = Type::getMixed(true);
- }
- }
- }
-
- private static function handleArrayAccessOnClassStringMap(
- Codebase $codebase,
- TClassStringMap $type,
- Union $offset_type,
- ?Union $replacement_type,
- ?Union &$array_access_type
- ): void {
- $offset_type_parts = array_values($offset_type->getAtomicTypes());
-
- foreach ($offset_type_parts as $offset_type_part) {
- if ($offset_type_part instanceof TClassString) {
- if ($offset_type_part instanceof TTemplateParamClass) {
- $template_result_get = new TemplateResult(
- [],
- [
- $type->param_name => [
- 'class-string-map' => new Union([
- new TTemplateParam(
- $offset_type_part->param_name,
- $offset_type_part->as_type
- ? new Union([$offset_type_part->as_type])
- : Type::getObject(),
- $offset_type_part->defining_class
- )
- ])
- ]
- ]
- );
-
- $template_result_set = new TemplateResult(
- [],
- [
- $offset_type_part->param_name => [
- $offset_type_part->defining_class => new Union([
- new TTemplateParam(
- $type->param_name,
- $type->as_type
- ? new Union([$type->as_type])
- : Type::getObject(),
- 'class-string-map'
- )
- ])
- ]
- ]
- );
- } else {
- $template_result_get = new TemplateResult(
- [],
- [
- $type->param_name => [
- 'class-string-map' => new Union([
- $offset_type_part->as_type
- ?: new TObject()
- ])
- ]
- ]
- );
- $template_result_set = new TemplateResult(
- [],
- []
- );
- }
-
- $expected_value_param_get = clone $type->value_param;
-
- TemplateInferredTypeReplacer::replace(
- $expected_value_param_get,
- $template_result_get,
- $codebase
- );
-
- if ($replacement_type) {
- $expected_value_param_set = clone $type->value_param;
-
- TemplateInferredTypeReplacer::replace(
- $replacement_type,
- $template_result_set,
- $codebase
- );
-
- $type->value_param = Type::combineUnionTypes(
- $replacement_type,
- $expected_value_param_set,
- $codebase
- );
- }
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- $expected_value_param_get,
- $codebase
- );
- }
- }
- }
-
- /**
- * @param list<string> $expected_offset_types
- * @param list<array-key> $key_values
- */
- private static function handleArrayAccessOnKeyedArray(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- array &$key_values,
- ?Union $replacement_type,
- ?Union &$array_access_type,
- bool $in_assignment,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- Union $offset_type,
- ?string $array_var_id,
- Context $context,
- TKeyedArray $type,
- Union $array_type,
- array &$expected_offset_types,
- string $type_string,
- bool &$has_valid_offset
- ): void {
- $generic_key_type = $type->getGenericKeyType();
-
- if (!$stmt->dim && $type->sealed && $type->is_list) {
- $key_values[] = count($type->properties);
- }
-
- if ($key_values) {
- foreach ($key_values as $key_value) {
- if (isset($type->properties[$key_value]) || $replacement_type) {
- $has_valid_offset = true;
-
- if ($replacement_type) {
- $type->properties[$key_value] = Type::combineUnionTypes(
- $type->properties[$key_value] ?? null,
- $replacement_type
- );
- }
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- clone $type->properties[$key_value]
- );
- } elseif ($in_assignment) {
- $type->properties[$key_value] = new Union([new TEmpty]);
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- clone $type->properties[$key_value]
- );
- } elseif ($type->previous_value_type) {
- if ($codebase->config->ensure_array_string_offsets_exist) {
- self::checkLiteralStringArrayOffset(
- $offset_type,
- $type->getGenericKeyType(),
- $array_var_id,
- $stmt,
- $context,
- $statements_analyzer
- );
- }
-
- if ($codebase->config->ensure_array_int_offsets_exist) {
- self::checkLiteralIntArrayOffset(
- $offset_type,
- $type->getGenericKeyType(),
- $array_var_id,
- $stmt,
- $context,
- $statements_analyzer
- );
- }
-
- $type->properties[$key_value] = clone $type->previous_value_type;
-
- $array_access_type = clone $type->previous_value_type;
- } elseif ($array_type->hasMixed()) {
- $has_valid_offset = true;
-
- $array_access_type = Type::getMixed();
- } else {
- if ($type->sealed || !$context->inside_isset) {
- $object_like_keys = array_keys($type->properties);
-
- $last_key = array_pop($object_like_keys);
-
- $key_string = '';
-
- if ($object_like_keys) {
- $formatted_keys = implode(
- ', ',
- array_map(
- function ($key) {
- return is_int($key) ? $key : '\'' . $key . '\'';
- },
- $object_like_keys
- )
- );
-
- $key_string = $formatted_keys . ' or ';
- }
-
- $key_string .= is_int($last_key) ? $last_key : '\'' . $last_key . '\'';
-
- $expected_offset_types[] = $key_string;
- }
-
- $array_access_type = Type::getMixed();
- }
- }
- } else {
- $key_type = $generic_key_type->hasMixed()
- ? Type::getArrayKey()
- : $generic_key_type;
-
- $union_comparison_results = new TypeComparisonResult();
-
- $is_contained = UnionTypeComparator::isContainedBy(
- $codebase,
- $offset_type,
- $key_type,
- true,
- $offset_type->ignore_falsable_issues,
- $union_comparison_results
- );
-
- if ($context->inside_isset && !$is_contained) {
- $is_contained = UnionTypeComparator::isContainedBy(
- $codebase,
- $key_type,
- $offset_type,
- true,
- $offset_type->ignore_falsable_issues
- );
- }
-
- if (($is_contained
- || $union_comparison_results->type_coerced_from_scalar
- || $union_comparison_results->type_coerced_from_mixed
- || $in_assignment)
- && !$union_comparison_results->to_string_cast
- ) {
- if ($replacement_type) {
- $generic_params = Type::combineUnionTypes(
- $type->getGenericValueType(),
- $replacement_type
- );
-
- $new_key_type = Type::combineUnionTypes(
- $generic_key_type,
- $offset_type->isMixed() ? Type::getArrayKey() : $offset_type
- );
-
- $property_count = $type->sealed ? count($type->properties) : null;
-
- if (!$stmt->dim && $property_count) {
- ++$property_count;
- $array_type->removeType($type_string);
- $type = new TNonEmptyArray([
- $new_key_type,
- $generic_params,
- ]);
- $array_type->addType($type);
- $type->count = $property_count;
- } else {
- $array_type->removeType($type_string);
-
- if (!$stmt->dim && $type->is_list) {
- $type = new TList($generic_params);
- } else {
- $type = new TArray([
- $new_key_type,
- $generic_params,
- ]);
- }
-
- $array_type->addType($type);
- }
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- clone $generic_params
- );
- } else {
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- $type->getGenericValueType()
- );
- }
-
- $has_valid_offset = true;
- } else {
- if (!$context->inside_isset
- || ($type->sealed && !$union_comparison_results->type_coerced)
- ) {
- $expected_offset_types[] = $generic_key_type->getId();
- }
-
- $array_access_type = Type::getMixed();
- }
- }
- }
-
- /**
- * @param list<string> $expected_offset_types
- * @param list<array-key> $key_values
- */
- private static function handleArrayAccessOnList(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- TList $type,
- Union $offset_type,
- ?string $array_var_id,
- array $key_values,
- Context $context,
- bool $in_assignment,
- array &$expected_offset_types,
- ?Union $replacement_type,
- ?Union &$array_access_type,
- bool &$has_valid_offset
- ): void {
- // if we're assigning to an empty array with a key offset, refashion that array
- if (!$in_assignment) {
- if (!$type instanceof TNonEmptyList
- || (count($key_values) === 1
- && is_int($key_values[0])
- && $key_values[0] > 0
- && $key_values[0] > ($type->count - 1))
- ) {
- $expected_offset_type = Type::getInt();
-
- if ($codebase->config->ensure_array_int_offsets_exist) {
- self::checkLiteralIntArrayOffset(
- $offset_type,
- $expected_offset_type,
- $array_var_id,
- $stmt,
- $context,
- $statements_analyzer
- );
- }
- $has_valid_offset = true;
- } elseif (count($key_values) === 1
- && is_int($key_values[0])
- && $key_values[0] < 0
- ) {
- $expected_offset_types[] = 'positive-int';
- $has_valid_offset = false;
- } else {
- $has_valid_offset = true;
- }
- }
-
- if ($in_assignment && $type instanceof TNonEmptyList && $type->count !== null) {
- $type->count++;
- }
-
- if ($in_assignment && $replacement_type) {
- $type->type_param = Type::combineUnionTypes(
- $type->type_param,
- $replacement_type,
- $codebase
- );
- }
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- $type->type_param
- );
- }
-
- private static function handleArrayAccessOnNamedObject(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- TNamedObject $type,
- Context $context,
- bool $in_assignment,
- ?PhpParser\Node\Expr $assign_value,
- ?Union &$array_access_type,
- bool &$has_array_access
- ): void {
- if (strtolower($type->value) === 'simplexmlelement') {
- $call_array_access_type = new Union([new TNamedObject('SimpleXMLElement')]);
- } elseif (strtolower($type->value) === 'domnodelist' && $stmt->dim) {
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_method_call = new VirtualMethodCall(
- $stmt->var,
- new VirtualIdentifier('item', $stmt->var->getAttributes()),
- [
- new VirtualArg($stmt->dim)
- ]
- );
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('MixedMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['MixedMethodCall']);
- }
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call,
- $context
- );
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('MixedMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['MixedMethodCall']);
- }
-
- $call_array_access_type = $statements_analyzer->node_data->getType($fake_method_call) ?? Type::getMixed();
-
- $statements_analyzer->node_data = $old_data_provider;
- } else {
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('MixedMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['MixedMethodCall']);
- }
-
- if ($in_assignment) {
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_set_method_call = new VirtualMethodCall(
- $stmt->var,
- new VirtualIdentifier('offsetSet', $stmt->var->getAttributes()),
- [
- new VirtualArg(
- $stmt->dim
- ?? new VirtualConstFetch(
- new VirtualName('null'),
- $stmt->var->getAttributes()
- )
- ),
- new VirtualArg(
- $assign_value ?? new VirtualConstFetch(
- new VirtualName('null'),
- $stmt->var->getAttributes()
- )
- ),
- ]
- );
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_set_method_call,
- $context
- );
-
- $statements_analyzer->node_data = $old_node_data;
- }
-
- if ($stmt->dim) {
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $fake_get_method_call = new VirtualMethodCall(
- $stmt->var,
- new VirtualIdentifier('offsetGet', $stmt->var->getAttributes()),
- [
- new VirtualArg(
- $stmt->dim
- )
- ]
- );
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_get_method_call,
- $context
- );
-
- $call_array_access_type =
- $statements_analyzer->node_data->getType($fake_get_method_call) ?? Type::getMixed();
-
- $statements_analyzer->node_data = $old_node_data;
- } else {
- $call_array_access_type = Type::getVoid();
- }
-
- $has_array_access = true;
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('MixedMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['MixedMethodCall']);
- }
- }
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- $call_array_access_type
- );
- }
-
- /**
- * @param list<string> $expected_offset_types
- */
- private static function handleArrayAccessOnString(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\ArrayDimFetch $stmt,
- bool $in_assignment,
- Context $context,
- ?Union $replacement_type,
- TString $type,
- Union $offset_type,
- array &$expected_offset_types,
- ?Union &$array_access_type,
- bool &$has_valid_offset
- ): void {
- if ($in_assignment && $replacement_type) {
- if ($replacement_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- IssueBuffer::maybeAdd(
- new MixedStringOffsetAssignment(
- 'Right-hand-side of string offset assignment cannot be mixed',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
- }
- }
-
- if ($type instanceof TSingleLetter) {
- $valid_offset_type = Type::getInt(false, 0);
- } elseif ($type instanceof TLiteralString) {
- if ($type->value === '') {
- $valid_offset_type = Type::getEmpty();
- } elseif (strlen($type->value) < 10) {
- $valid_offsets = [];
-
- for ($i = -strlen($type->value), $l = strlen($type->value); $i < $l; $i++) {
- $valid_offsets[] = new TLiteralInt($i);
- }
-
- if (!$valid_offsets) {
- throw new UnexpectedValueException('This is weird');
- }
-
- $valid_offset_type = new Union($valid_offsets);
- } else {
- $valid_offset_type = Type::getInt();
- }
- } else {
- $valid_offset_type = Type::getInt();
- }
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $offset_type,
- $valid_offset_type,
- true
- )) {
- $expected_offset_types[] = $valid_offset_type->getId();
-
- $array_access_type = Type::getMixed();
- } else {
- $has_valid_offset = true;
-
- $array_access_type = Type::combineUnionTypes(
- $array_access_type,
- Type::getSingleLetter()
- );
- }
- }
-
- /**
- * @param Atomic[] $offset_types
- */
- private static function checkArrayOffsetType(
- Union $offset_type,
- array $offset_types,
- Codebase $codebase
- ): bool {
- $has_valid_absolute_offset = false;
- foreach ($offset_types as $atomic_offset_type) {
- if ($atomic_offset_type instanceof TClassConstant) {
- $expanded = TypeExpander::expandAtomic(
- $codebase,
- $atomic_offset_type,
- $atomic_offset_type->fq_classlike_name,
- $atomic_offset_type->fq_classlike_name,
- null,
- true,
- true
- );
-
- if ($expanded instanceof Atomic) {
- if (!$expanded instanceof TClassConstant) {
- $has_valid_absolute_offset = self::checkArrayOffsetType(
- $offset_type,
- [$expanded],
- $codebase
- );
- }
- } else {
- $has_valid_absolute_offset = self::checkArrayOffsetType(
- $offset_type,
- $expanded,
- $codebase
- );
- }
-
- if ($has_valid_absolute_offset) {
- break;
- }
- }
-
- if ($atomic_offset_type instanceof TFalse &&
- $offset_type->ignore_falsable_issues === true
- ) {
- //do nothing
- } elseif ($atomic_offset_type instanceof TNull &&
- $offset_type->ignore_nullable_issues === true
- ) {
- //do nothing
- } elseif ($atomic_offset_type instanceof TString ||
- $atomic_offset_type instanceof TInt ||
- $atomic_offset_type instanceof TArrayKey ||
- $atomic_offset_type instanceof TMixed
- ) {
- $has_valid_absolute_offset = true;
- break;
- } elseif ($atomic_offset_type instanceof TTemplateParam) {
- $has_valid_absolute_offset = self::checkArrayOffsetType(
- $offset_type,
- $atomic_offset_type->as->getAtomicTypes(),
- $codebase
- );
-
- if ($has_valid_absolute_offset) {
- break;
- }
- }
- }
- return $has_valid_absolute_offset;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php
deleted file mode 100644
index b34e983..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php
+++ /dev/null
@@ -1,1217 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use InvalidArgumentException;
-use PhpParser;
-use PhpParser\Node\Expr\PropertyFetch;
-use PhpParser\Node\Expr\StaticPropertyFetch;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\DeprecatedProperty;
-use Psalm\Issue\ImpurePropertyFetch;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\InternalProperty;
-use Psalm\Issue\MissingPropertyType;
-use Psalm\Issue\NoInterfaceProperties;
-use Psalm\Issue\UndefinedClass;
-use Psalm\Issue\UndefinedDocblockClass;
-use Psalm\Issue\UndefinedMagicPropertyFetch;
-use Psalm\Issue\UndefinedPropertyFetch;
-use Psalm\Issue\UndefinedThisPropertyFetch;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\Scalar\VirtualString;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_keys;
-use function array_search;
-use function count;
-use function in_array;
-use function is_int;
-use function is_string;
-use function strtolower;
-
-/**
- * @internal
- */
-class AtomicPropertyFetchAnalyzer
-{
- /**
- * @param array<string> $invalid_fetch_types $invalid_fetch_types
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- Context $context,
- bool $in_assignment,
- ?string $var_id,
- ?string $stmt_var_id,
- Union $stmt_var_type,
- Atomic $lhs_type_part,
- string $prop_name,
- bool &$has_valid_fetch_type,
- array &$invalid_fetch_types,
- bool $is_static_access = false
- ): void {
- if ($lhs_type_part instanceof TNull) {
- return;
- }
-
- if ($lhs_type_part instanceof TMixed) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- return;
- }
-
- if ($lhs_type_part instanceof TFalse && $stmt_var_type->ignore_falsable_issues) {
- return;
- }
-
- if (!$lhs_type_part instanceof TNamedObject && !$lhs_type_part instanceof TObject) {
- $invalid_fetch_types[] = (string)$lhs_type_part;
-
- return;
- }
-
- $has_valid_fetch_type = true;
-
- if ($lhs_type_part instanceof TObjectWithProperties
- && isset($lhs_type_part->properties[$prop_name])
- ) {
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
-
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes(
- $lhs_type_part->properties[$prop_name],
- $stmt_type
- )
- );
-
- return;
- }
-
- // stdClass and SimpleXMLElement are special cases where we cannot infer the return types
- // but we don't want to throw an error
- // Hack has a similar issue: https://github.com/facebook/hhvm/issues/5164
- if ($lhs_type_part instanceof TObject
- || in_array(strtolower($lhs_type_part->value), Config::getInstance()->getUniversalObjectCrates(), true)
- ) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
-
- return;
- }
-
- if (ExpressionAnalyzer::isMock($lhs_type_part->value)) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- return;
- }
-
- $intersection_types = $lhs_type_part->getIntersectionTypes() ?: [];
-
- $fq_class_name = $lhs_type_part->value;
-
- $override_property_visibility = false;
-
- $has_magic_getter = false;
-
- $class_exists = false;
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$codebase->classExists($lhs_type_part->value)
- && !$codebase->classlikes->enumExists($lhs_type_part->value)
- ) {
- $interface_exists = false;
-
- self::handleNonExistentClass(
- $statements_analyzer,
- $codebase,
- $stmt,
- $lhs_type_part,
- $intersection_types,
- $class_exists,
- $interface_exists,
- $fq_class_name,
- $override_property_visibility
- );
-
- if (!$class_exists && !$interface_exists) {
- return;
- }
- } else {
- $class_exists = true;
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $config = $statements_analyzer->getProjectAnalyzer()->getConfig();
-
- $property_id = $fq_class_name . '::$' . $prop_name;
-
- if ($class_storage->is_enum || in_array('UnitEnum', $codebase->getParentInterfaces($fq_class_name))) {
- if ($prop_name === 'value' && !$class_storage->is_enum) {
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union([
- new TString(),
- new TInt()
- ])
- );
- } elseif ($prop_name === 'value' && $class_storage->enum_type !== null && $class_storage->enum_cases) {
- self::handleEnumValue($statements_analyzer, $stmt, $class_storage);
- } elseif ($prop_name === 'name') {
- self::handleEnumName($statements_analyzer, $stmt, $lhs_type_part);
- } else {
- self::handleNonExistentProperty(
- $statements_analyzer,
- $codebase,
- $stmt,
- $context,
- $config,
- $class_storage,
- $prop_name,
- $lhs_type_part,
- $fq_class_name,
- $property_id,
- $in_assignment,
- $stmt_var_id,
- $has_magic_getter,
- $var_id
- );
- }
-
- return;
- }
-
- $naive_property_exists = $codebase->properties->propertyExists(
- $property_id,
- !$in_assignment,
- $statements_analyzer,
- $context,
- $codebase->collect_locations ? new CodeLocation($statements_analyzer->getSource(), $stmt) : null
- );
-
- // add method before changing fq_class_name
- $get_method_id = new MethodIdentifier($fq_class_name, '__get');
-
- if (!$naive_property_exists
- && $class_storage->namedMixins
- ) {
- foreach ($class_storage->namedMixins as $mixin) {
- $new_property_id = $mixin->value . '::$' . $prop_name;
-
- try {
- $new_class_storage = $codebase->classlike_storage_provider->get($mixin->value);
- } catch (InvalidArgumentException $e) {
- $new_class_storage = null;
- }
-
- if ($new_class_storage
- && ($codebase->properties->propertyExists(
- $new_property_id,
- !$in_assignment,
- $statements_analyzer,
- $context,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null
- )
- || isset($new_class_storage->pseudo_property_get_types['$' . $prop_name]))
- ) {
- $fq_class_name = $mixin->value;
- $lhs_type_part = clone $mixin;
- $class_storage = $new_class_storage;
-
- if (!isset($new_class_storage->pseudo_property_get_types['$' . $prop_name])) {
- $naive_property_exists = true;
- }
-
- $property_id = $new_property_id;
- }
- }
- }
-
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $property_id,
- true,
- $statements_analyzer
- );
-
- if (self::propertyFetchCanBeAnalyzed(
- $statements_analyzer,
- $codebase,
- $stmt,
- $context,
- $fq_class_name,
- $prop_name,
- $lhs_type_part,
- $property_id,
- $has_magic_getter,
- $stmt_var_id,
- $naive_property_exists,
- $override_property_visibility,
- $class_exists,
- $declaring_property_class,
- $class_storage,
- $get_method_id,
- $in_assignment
- ) === false) {
- return;
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $property_id
- );
- }
-
- if (!$naive_property_exists
- && $fq_class_name !== $context->self
- && $context->self
- && $codebase->classlikes->classExtends($fq_class_name, $context->self)
- && $codebase->properties->propertyExists(
- $context->self . '::$' . $prop_name,
- true,
- $statements_analyzer,
- $context,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null
- )
- ) {
- $property_id = $context->self . '::$' . $prop_name;
- } elseif (!$naive_property_exists
- || (!$is_static_access
- // when property existence is asserted by a plugin it doesn't necessarily has storage
- && $codebase->properties->hasStorage($property_id)
- && $codebase->properties->getStorage($property_id)->is_static
- )
- ) {
- self::handleNonExistentProperty(
- $statements_analyzer,
- $codebase,
- $stmt,
- $context,
- $config,
- $class_storage,
- $prop_name,
- $lhs_type_part,
- $declaring_property_class,
- $property_id,
- $in_assignment,
- $stmt_var_id,
- $has_magic_getter,
- $var_id
- );
-
- return;
- }
-
- if (!$override_property_visibility) {
- if (ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return;
- }
- }
-
- // FIXME: the following line look superfluous, but removing it makes
- // Psalm\Tests\PropertyTypeTest::testValidCode with data set "callInParentContext"
- // fail
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $property_id,
- true,
- $statements_analyzer
- );
-
- if ($declaring_property_class === null) {
- return;
- }
-
- if ($codebase->properties_to_rename) {
- $declaring_property_id = strtolower($declaring_property_class) . '::$' . $prop_name;
-
- foreach ($codebase->properties_to_rename as $original_property_id => $new_property_name) {
- if ($declaring_property_id === $original_property_id) {
- $file_manipulations = [
- new FileManipulation(
- (int) $stmt->name->getAttribute('startFilePos'),
- (int) $stmt->name->getAttribute('endFilePos') + 1,
- $new_property_name
- )
- ];
-
- FileManipulationBuffer::add(
- $statements_analyzer->getFilePath(),
- $file_manipulations
- );
- }
- }
- }
-
- $declaring_class_storage = $codebase->classlike_storage_provider->get(
- $declaring_property_class
- );
-
- if (isset($declaring_class_storage->properties[$prop_name])) {
- self::checkPropertyDeprecation($prop_name, $declaring_property_class, $stmt, $statements_analyzer);
-
- $property_storage = $declaring_class_storage->properties[$prop_name];
-
- if ($context->self && !NamespaceAnalyzer::isWithinAny($context->self, $property_storage->internal)) {
- IssueBuffer::maybeAdd(
- new InternalProperty(
- $property_id . ' is internal to ' . InternalClass::listToPhrase($property_storage->internal)
- . ' but called from ' . $context->self,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($context->inside_unset) {
- InstancePropertyAssignmentAnalyzer::trackPropertyImpurity(
- $statements_analyzer,
- $stmt,
- $property_id,
- $property_storage,
- $declaring_class_storage,
- $context
- );
- }
- }
-
- $class_property_type = self::getClassPropertyType(
- $statements_analyzer,
- $codebase,
- $config,
- $context,
- $stmt,
- $class_storage,
- $declaring_class_storage,
- $property_id,
- $fq_class_name,
- $prop_name,
- $lhs_type_part
- );
-
- if (!$context->collect_mutations
- && !$context->collect_initializations
- && !($class_storage->external_mutation_free
- && $class_property_type->allow_mutations)
- ) {
- if ($context->pure) {
- IssueBuffer::maybeAdd(
- new ImpurePropertyFetch(
- 'Cannot access a property on a mutable object from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- self::processTaints(
- $statements_analyzer,
- $stmt,
- $class_property_type,
- $property_id,
- $class_storage,
- $in_assignment,
- $context
- );
-
- if ($class_storage->mutation_free) {
- $class_property_type->has_mutations = false;
- }
-
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes($class_property_type, $stmt_type)
- );
- }
-
- /**
- * @param PropertyFetch|StaticPropertyFetch $stmt
- */
- public static function checkPropertyDeprecation(
- string $prop_name,
- string $declaring_property_class,
- PhpParser\Node\Expr $stmt,
- StatementsAnalyzer $statements_analyzer
- ): void {
- $property_id = $declaring_property_class . '::$' . $prop_name;
- $codebase = $statements_analyzer->getCodebase();
- $declaring_class_storage = $codebase->classlike_storage_provider->get(
- $declaring_property_class
- );
-
- if (isset($declaring_class_storage->properties[$prop_name])) {
- $property_storage = $declaring_class_storage->properties[$prop_name];
-
- if ($property_storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedProperty(
- $property_id . ' is marked deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- private static function propertyFetchCanBeAnalyzed(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- Context $context,
- string $fq_class_name,
- string $prop_name,
- TNamedObject $lhs_type_part,
- string &$property_id,
- bool &$has_magic_getter,
- ?string $stmt_var_id,
- bool $naive_property_exists,
- bool $override_property_visibility,
- bool $class_exists,
- ?string $declaring_property_class,
- ClassLikeStorage $class_storage,
- MethodIdentifier $get_method_id,
- bool $in_assignment
- ): bool {
- if ((!$naive_property_exists
- || ($stmt_var_id !== '$this'
- && $fq_class_name !== $context->self
- && ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues(),
- false
- ) !== true)
- )
- && $codebase->methods->methodExists(
- $get_method_id,
- $context->calling_method_id,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $statements_analyzer
- : null,
- $statements_analyzer->getFilePath()
- )
- ) {
- $has_magic_getter = true;
-
- if (isset($class_storage->pseudo_property_get_types['$' . $prop_name])) {
- $stmt_type = TypeExpander::expandUnion(
- $codebase,
- clone $class_storage->pseudo_property_get_types['$' . $prop_name],
- $class_storage->name,
- $class_storage->name,
- $class_storage->parent_class
- );
-
- if (count($template_types = $class_storage->getClassTemplateTypes()) !== 0) {
- if (!$lhs_type_part instanceof TGenericObject) {
- $lhs_type_part = new TGenericObject($lhs_type_part->value, $template_types);
- }
-
- $stmt_type = self::localizePropertyType(
- $codebase,
- $stmt_type,
- $lhs_type_part,
- $class_storage,
- $declaring_property_class
- ? $codebase->classlike_storage_provider->get(
- $declaring_property_class
- ) : $class_storage
- );
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- self::processTaints(
- $statements_analyzer,
- $stmt,
- $stmt_type,
- $property_id,
- $class_storage,
- $in_assignment,
- $context
- );
-
- return false;
- }
-
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $statements_analyzer->node_data->setType($stmt->var, new Union([$lhs_type_part]));
-
- $fake_method_call = new VirtualMethodCall(
- $stmt->var,
- new VirtualIdentifier('__get', $stmt->name->getAttributes()),
- [
- new VirtualArg(
- new VirtualString(
- $prop_name,
- $stmt->name->getAttributes()
- )
- )
- ]
- );
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('InternalMethod', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['InternalMethod']);
- }
-
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_method_call,
- $context,
- false
- );
-
- if (!in_array('InternalMethod', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['InternalMethod']);
- }
-
- $fake_method_call_type = $statements_analyzer->node_data->getType($fake_method_call);
-
- $statements_analyzer->node_data = $old_data_provider;
-
- if ($fake_method_call_type) {
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes($fake_method_call_type, $stmt_type)
- );
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- /*
- * If we have an explicit list of all allowed magic properties on the class, and we're
- * not in that list, fall through
- */
- if (!($class_storage->sealed_properties || $codebase->config->seal_all_properties)
- && !$override_property_visibility
- ) {
- return false;
- }
-
- if (!$class_exists) {
- $property_id = $lhs_type_part->value . '::$' . $prop_name;
-
- IssueBuffer::maybeAdd(
- new UndefinedMagicPropertyFetch(
- 'Magic instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return false;
- }
- }
-
- return true;
- }
-
- public static function localizePropertyType(
- Codebase $codebase,
- Union $class_property_type,
- TGenericObject $lhs_type_part,
- ClassLikeStorage $property_class_storage,
- ClassLikeStorage $property_declaring_class_storage
- ): Union {
- $template_types = CallAnalyzer::getTemplateTypesForCall(
- $codebase,
- $property_declaring_class_storage,
- $property_declaring_class_storage->name,
- $property_class_storage,
- $property_class_storage->template_types ?: []
- );
-
- $extended_types = $property_class_storage->template_extended_params;
-
- if ($template_types) {
- if ($property_class_storage->template_types) {
- foreach ($lhs_type_part->type_params as $param_offset => $lhs_param_type) {
- $i = -1;
-
- foreach ($property_class_storage->template_types as $calling_param_name => $_) {
- $i++;
-
- if ($i === $param_offset) {
- $template_types[$calling_param_name][$property_class_storage->name] = $lhs_param_type;
- break;
- }
- }
- }
- }
-
- foreach ($template_types as $type_name => $_) {
- if (isset($extended_types[$property_declaring_class_storage->name][$type_name])) {
- $mapped_type = $extended_types[$property_declaring_class_storage->name][$type_name];
-
- foreach ($mapped_type->getAtomicTypes() as $mapped_type_atomic) {
- if (!$mapped_type_atomic instanceof TTemplateParam) {
- continue;
- }
-
- $param_name = $mapped_type_atomic->param_name;
-
- $position = false;
-
- if (isset($property_class_storage->template_types[$param_name])) {
- $position = array_search(
- $param_name,
- array_keys($property_class_storage->template_types)
- );
- }
-
- if ($position !== false && isset($lhs_type_part->type_params[$position])) {
- $template_types[$type_name][$property_declaring_class_storage->name]
- = $lhs_type_part->type_params[$position];
- }
- }
- }
- }
-
- TemplateInferredTypeReplacer::replace(
- $class_property_type,
- new TemplateResult([], $template_types),
- $codebase
- );
- }
-
- return $class_property_type;
- }
-
- public static function processTaints(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- Union $type,
- string $property_id,
- ClassLikeStorage $class_storage,
- bool $in_assignment,
- ?Context $context = null
- ): void {
- if (!$statements_analyzer->data_flow_graph) {
- return;
- }
-
- $data_flow_graph = $statements_analyzer->data_flow_graph;
-
- $var_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var);
- $property_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $added_taints = [];
- $removed_taints = [];
-
- if ($context) {
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
- }
-
- if ($class_storage->specialize_instance) {
- $var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->var,
- null,
- $statements_analyzer
- );
-
- $var_property_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- null,
- $statements_analyzer
- );
-
- if ($var_id) {
- $var_type = $statements_analyzer->node_data->getType($stmt->var);
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $var_type
- && in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $var_type->parent_nodes = [];
- return;
- }
-
- $var_node = DataFlowNode::getForAssignment(
- $var_id,
- $var_location
- );
-
- $data_flow_graph->addNode($var_node);
-
- $property_node = DataFlowNode::getForAssignment(
- $var_property_id ?: $var_id . '->$property',
- $property_location
- );
-
- $data_flow_graph->addNode($property_node);
-
- $data_flow_graph->addPath(
- $var_node,
- $property_node,
- 'property-fetch'
- . ($stmt->name instanceof PhpParser\Node\Identifier ? '-' . $stmt->name : ''),
- $added_taints,
- $removed_taints
- );
-
- if ($var_type && $var_type->parent_nodes) {
- foreach ($var_type->parent_nodes as $parent_node) {
- $data_flow_graph->addPath(
- $parent_node,
- $var_node,
- '=',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- $type->parent_nodes = [$property_node->id => $property_node];
- }
- } else {
- $var_property_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- null,
- $statements_analyzer
- );
-
- $localized_property_node = DataFlowNode::getForAssignment(
- $var_property_id
- ?: $property_id . '-' . $property_location->file_name . ':' . $property_location->raw_file_start,
- $property_location
- );
-
- $data_flow_graph->addNode($localized_property_node);
-
- $property_node = new DataFlowNode(
- $property_id,
- $property_id,
- null,
- null
- );
-
- $data_flow_graph->addNode($property_node);
-
- if ($in_assignment) {
- $data_flow_graph->addPath(
- $localized_property_node,
- $property_node,
- 'property-assignment',
- $added_taints,
- $removed_taints
- );
- } else {
- $data_flow_graph->addPath(
- $property_node,
- $localized_property_node,
- 'property-fetch',
- $added_taints,
- $removed_taints
- );
- }
-
- $type->parent_nodes = [$localized_property_node->id => $localized_property_node];
- }
- }
-
- private static function handleEnumName(
- StatementsAnalyzer $statements_analyzer,
- PropertyFetch $stmt,
- Atomic $lhs_type_part
- ): void {
- if ($lhs_type_part instanceof TEnumCase) {
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union([new TLiteralString($lhs_type_part->case_name)])
- );
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getNonEmptyString());
- }
- }
-
- private static function handleEnumValue(
- StatementsAnalyzer $statements_analyzer,
- PropertyFetch $stmt,
- ClassLikeStorage $class_storage
- ): void {
- $case_values = [];
-
- foreach ($class_storage->enum_cases as $enum_case) {
- if (is_string($enum_case->value)) {
- $case_values[] = new TLiteralString($enum_case->value);
- } elseif (is_int($enum_case->value)) {
- $case_values[] = new TLiteralInt($enum_case->value);
- } else {
- // this should never happen
- $case_values[] = new TMixed();
- }
- }
-
- // todo: this is suboptimal when we reference enum directly, e.g. Status::Open->value
- /** @psalm-suppress ArgumentTypeCoercion */
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union($case_values)
- );
- }
-
- private static function handleUndefinedProperty(
- Context $context,
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- ?string $stmt_var_id,
- string $property_id,
- bool $has_magic_getter,
- ?string $var_id
- ): void {
- if ($context->inside_isset || $context->collect_initializations) {
- if ($context->pure) {
- IssueBuffer::maybeAdd(
- new ImpurePropertyFetch(
- 'Cannot access a property on a mutable object from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->inside_isset
- && $statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
-
- return;
- }
-
- if ($stmt_var_id === '$this') {
- IssueBuffer::maybeAdd(
- new UndefinedThisPropertyFetch(
- 'Instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- if ($has_magic_getter) {
- IssueBuffer::maybeAdd(
- new UndefinedMagicPropertyFetch(
- 'Magic instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedPropertyFetch(
- 'Instance property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- $stmt_type = Type::getMixed();
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if ($var_id) {
- $context->vars_in_scope[$var_id] = $stmt_type;
- }
- }
-
- /**
- * @param array<Atomic> $intersection_types
- */
- private static function handleNonExistentClass(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- TNamedObject $lhs_type_part,
- array $intersection_types,
- bool &$class_exists,
- bool &$interface_exists,
- string &$fq_class_name,
- bool &$override_property_visibility
- ): void {
- if ($codebase->interfaceExists($lhs_type_part->value)) {
- $interface_exists = true;
- $interface_storage = $codebase->classlike_storage_provider->get($lhs_type_part->value);
-
- $override_property_visibility = $interface_storage->override_property_visibility;
-
- foreach ($intersection_types as $intersection_type) {
- if ($intersection_type instanceof TNamedObject
- && $codebase->classExists($intersection_type->value)
- ) {
- $fq_class_name = $intersection_type->value;
- $class_exists = true;
- return;
- }
- }
-
- if (!$class_exists &&
- //interfaces can't have properties. Except when they do... In PHP Core, they can
- !in_array($fq_class_name, ['UnitEnum', 'BackedEnum'], true) &&
- !in_array('UnitEnum', $codebase->getParentInterfaces($fq_class_name))
- ) {
- if (IssueBuffer::accepts(
- new NoInterfaceProperties(
- 'Interfaces cannot have properties',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $lhs_type_part->value
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return;
- }
-
- if (!$codebase->methodExists($fq_class_name . '::__set')) {
- return;
- }
- }
- }
-
- if (!$class_exists && !$interface_exists) {
- if ($lhs_type_part->from_docblock) {
- IssueBuffer::maybeAdd(
- new UndefinedDocblockClass(
- 'Cannot get properties of undefined docblock class ' . $lhs_type_part->value,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $lhs_type_part->value
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedClass(
- 'Cannot get properties of undefined class ' . $lhs_type_part->value,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $lhs_type_part->value
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- private static function handleNonExistentProperty(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- Context $context,
- Config $config,
- ClassLikeStorage $class_storage,
- string $prop_name,
- TNamedObject $lhs_type_part,
- ?string $declaring_property_class,
- string $property_id,
- bool $in_assignment,
- ?string $stmt_var_id,
- bool $has_magic_getter,
- ?string $var_id
- ): void {
- if ($config->use_phpdoc_property_without_magic_or_parent
- && isset($class_storage->pseudo_property_get_types['$' . $prop_name])
- ) {
- $stmt_type = clone $class_storage->pseudo_property_get_types['$' . $prop_name];
-
- if (count($template_types = $class_storage->getClassTemplateTypes()) !== 0) {
- if (!$lhs_type_part instanceof TGenericObject) {
- $lhs_type_part = new TGenericObject($lhs_type_part->value, $template_types);
- }
-
- $stmt_type = self::localizePropertyType(
- $codebase,
- $stmt_type,
- $lhs_type_part,
- $class_storage,
- $declaring_property_class
- ? $codebase->classlike_storage_provider->get(
- $declaring_property_class
- ) : $class_storage
- );
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- self::processTaints(
- $statements_analyzer,
- $stmt,
- $stmt_type,
- $property_id,
- $class_storage,
- $in_assignment,
- $context
- );
-
- return;
- }
-
- if ($class_storage->is_interface) {
- return;
- }
-
- self::handleUndefinedProperty(
- $context,
- $statements_analyzer,
- $stmt,
- $stmt_var_id,
- $property_id,
- $has_magic_getter,
- $var_id
- );
- }
-
- private static function getClassPropertyType(
- StatementsAnalyzer $statements_analyzer,
- Codebase $codebase,
- Config $config,
- Context $context,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- ClassLikeStorage $class_storage,
- ClassLikeStorage $declaring_class_storage,
- string $property_id,
- string $fq_class_name,
- string $prop_name,
- TNamedObject $lhs_type_part
- ): Union {
- $class_property_type = $codebase->properties->getPropertyType(
- $property_id,
- false,
- $statements_analyzer,
- $context
- );
-
- if (!$class_property_type) {
- if ($declaring_class_storage->location
- && $config->isInProjectDirs(
- $declaring_class_storage->location->file_path
- )
- ) {
- IssueBuffer::maybeAdd(
- new MissingPropertyType(
- 'Property ' . $fq_class_name . '::$' . $prop_name
- . ' does not have a declared type',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- $class_property_type = Type::getMixed();
- } else {
- $class_property_type = TypeExpander::expandUnion(
- $codebase,
- clone $class_property_type,
- $declaring_class_storage->name,
- $declaring_class_storage->name,
- $declaring_class_storage->parent_class
- );
-
- if (count($template_types = $declaring_class_storage->getClassTemplateTypes()) !== 0) {
- if (!$lhs_type_part instanceof TGenericObject) {
- $lhs_type_part = new TGenericObject($lhs_type_part->value, $template_types);
- }
-
- $class_property_type = self::localizePropertyType(
- $codebase,
- $class_property_type,
- $lhs_type_part,
- $class_storage,
- $declaring_class_storage
- );
- } elseif ($lhs_type_part instanceof TGenericObject) {
- $class_property_type = self::localizePropertyType(
- $codebase,
- $class_property_type,
- $lhs_type_part,
- $class_storage,
- $declaring_class_storage
- );
- }
- }
-
- return $class_property_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php
deleted file mode 100644
index 8ed5643..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ClassConstFetchAnalyzer.php
+++ /dev/null
@@ -1,679 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Exception\CircularReferenceException;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Issue\CircularReference;
-use Psalm\Issue\DeprecatedClass;
-use Psalm\Issue\DeprecatedConstant;
-use Psalm\Issue\InaccessibleClassConstant;
-use Psalm\Issue\InternalClass;
-use Psalm\Issue\NonStaticSelfCall;
-use Psalm\Issue\ParentNotFound;
-use Psalm\Issue\UndefinedConstant;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-use ReflectionProperty;
-
-use function explode;
-use function in_array;
-use function strtolower;
-
-/**
- * @internal
- */
-class ClassConstFetchAnalyzer
-{
- /**
- * @psalm-suppress ComplexMethod to be refactored. We should probably regroup the two big if about $stmt->class and
- * analyse the ::class int $stmt->name separately
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ClassConstFetch $stmt,
- Context $context
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
-
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
-
- if ($stmt->class instanceof PhpParser\Node\Name) {
- $first_part_lc = strtolower($stmt->class->parts[0]);
-
- if ($first_part_lc === 'self' || $first_part_lc === 'static') {
- if (!$context->self) {
- if (IssueBuffer::accepts(
- new NonStaticSelfCall(
- 'Cannot use ' . $first_part_lc . ' outside class context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- $fq_class_name = $context->self;
- } elseif ($first_part_lc === 'parent') {
- $fq_class_name = $statements_analyzer->getParentFQCLN();
-
- if ($fq_class_name === null) {
- if (IssueBuffer::accepts(
- new ParentNotFound(
- 'Cannot check property fetch on parent as this class does not extend another',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
- } else {
- $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $statements_analyzer->getAliases()
- );
-
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- if ((!$context->inside_class_exists || $stmt->name->name !== 'class')
- && !isset($context->phantom_classes[strtolower($fq_class_name)])
- ) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt->class),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(false, true)
- ) === false) {
- return true;
- }
- }
- }
- }
-
- $fq_class_name_lc = strtolower($fq_class_name);
-
- $moved_class = false;
-
- if ($codebase->alter_code
- && !in_array($stmt->class->parts[0], ['parent', 'static'])
- ) {
- $moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id,
- false,
- $stmt->class->parts[0] === 'self'
- );
- }
-
- if ($codebase->classlikes->classExists($fq_class_name)) {
- $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
- }
-
- if ($stmt->name instanceof PhpParser\Node\Identifier && $stmt->name->name === 'class') {
- if ($codebase->classlikes->classExists($fq_class_name)) {
- $const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
- $fq_class_name = $const_class_storage->name;
-
- if ($const_class_storage->deprecated && $fq_class_name !== $context->self) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- 'Class ' . $fq_class_name . ' is deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($first_part_lc === 'static') {
- $static_named_object = new TNamedObject($fq_class_name);
- $static_named_object->was_static = true;
-
- $statements_analyzer->node_data->setType(
- $stmt,
- new Union([
- new TClassString($fq_class_name, $static_named_object)
- ])
- );
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getLiteralClassString($fq_class_name, true));
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $fq_class_name
- );
- }
-
- return true;
- }
-
- // if we're ignoring that the class doesn't exist, exit anyway
- if (!$codebase->classlikes->classOrInterfaceOrEnumExists($fq_class_name)) {
- return true;
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $fq_class_name
- );
- }
-
- if (!$stmt->name instanceof PhpParser\Node\Identifier) {
- return true;
- }
-
- $const_id = $fq_class_name . '::' . $stmt->name;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $const_id
- );
- }
-
- $const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
- if ($const_class_storage->is_enum) {
- $case = $const_class_storage->enum_cases[(string)$stmt->name] ?? null;
- if ($case && $case->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedConstant(
- "Enum Case $const_id is marked as deprecated",
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($fq_class_name === $context->self
- || (
- $statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer &&
- $fq_class_name === $statements_analyzer->getSource()->getFQCLN()
- )
- ) {
- $class_visibility = ReflectionProperty::IS_PRIVATE;
- } elseif ($context->self &&
- ($codebase->classlikes->classExtends($context->self, $fq_class_name)
- || $codebase->classlikes->classExtends($fq_class_name, $context->self))
- ) {
- $class_visibility = ReflectionProperty::IS_PROTECTED;
- } else {
- $class_visibility = ReflectionProperty::IS_PUBLIC;
- }
-
- try {
- $class_constant_type = $codebase->classlikes->getClassConstantType(
- $fq_class_name,
- $stmt->name->name,
- $class_visibility,
- $statements_analyzer
- );
- } catch (InvalidArgumentException $_) {
- return true;
- } catch (CircularReferenceException $e) {
- IssueBuffer::maybeAdd(
- new CircularReference(
- 'Constant ' . $const_id . ' contains a circular reference',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return true;
- }
-
- if (!$class_constant_type) {
- if ($fq_class_name !== $context->self) {
- $class_constant_type = $codebase->classlikes->getClassConstantType(
- $fq_class_name,
- $stmt->name->name,
- ReflectionProperty::IS_PRIVATE,
- $statements_analyzer
- );
- }
-
- if ($class_constant_type) {
- IssueBuffer::maybeAdd(
- new InaccessibleClassConstant(
- 'Constant ' . $const_id . ' is not visible in this context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->check_consts) {
- IssueBuffer::maybeAdd(
- new UndefinedConstant(
- 'Constant ' . $const_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return true;
- }
-
- if ($context->calling_method_id) {
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $context->calling_method_id,
- $fq_class_name_lc . '::' . $stmt->name->name,
- false
- );
- }
-
- $declaring_const_id = $fq_class_name_lc . '::' . $stmt->name->name;
-
- if ($codebase->alter_code && !$moved_class) {
- foreach ($codebase->class_constant_transforms as $original_pattern => $transformation) {
- if ($declaring_const_id === $original_pattern) {
- [$new_fq_class_name, $new_const_name] = explode('::', $transformation);
-
- $file_manipulations = [];
-
- if (strtolower($new_fq_class_name) !== $fq_class_name_lc) {
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->class->getAttribute('startFilePos'),
- (int) $stmt->class->getAttribute('endFilePos') + 1,
- Type::getStringFromFQCLN(
- $new_fq_class_name,
- $statements_analyzer->getNamespace(),
- $statements_analyzer->getAliasedClassesFlipped(),
- null
- )
- );
- }
-
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->name->getAttribute('startFilePos'),
- (int) $stmt->name->getAttribute('endFilePos') + 1,
- $new_const_name
- );
-
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
- }
- }
-
- if ($context->self
- && !$context->collect_initializations
- && !$context->collect_mutations
- && !NamespaceAnalyzer::isWithinAny($context->self, $const_class_storage->internal)
- ) {
- IssueBuffer::maybeAdd(
- new InternalClass(
- $fq_class_name . ' is internal to '
- . InternalClass::listToPhrase($const_class_storage->internal)
- . ' but called from ' . $context->self,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($const_class_storage->deprecated && $fq_class_name !== $context->self) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- 'Class ' . $fq_class_name . ' is deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif (isset($const_class_storage->constants[$stmt->name->name])
- && $const_class_storage->constants[$stmt->name->name]->deprecated
- ) {
- IssueBuffer::maybeAdd(
- new DeprecatedConstant(
- 'Constant ' . $const_id . ' is deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($first_part_lc !== 'static' || $const_class_storage->final) {
- $stmt_type = clone $class_constant_type;
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
- $context->vars_in_scope[$const_id] = $stmt_type;
- }
-
- return true;
- }
-
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->class, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return false;
- }
-
- $context->inside_general_use = $was_inside_general_use;
-
- $lhs_type = $statements_analyzer->node_data->getType($stmt->class);
-
- if ($lhs_type === null) {
- return true;
- }
-
- if ($stmt->name instanceof PhpParser\Node\Identifier && $stmt->name->name === 'class') {
- $class_string_types = [];
-
- $has_mixed_or_object = false;
-
- foreach ($lhs_type->getAtomicTypes() as $lhs_atomic_type) {
- if ($lhs_atomic_type instanceof TNamedObject) {
- $class_string_types[] = new TClassString(
- $lhs_atomic_type->value,
- clone $lhs_atomic_type
- );
- } elseif ($lhs_atomic_type instanceof TTemplateParam
- && $lhs_atomic_type->as->isSingle()) {
- $as_atomic_type = $lhs_atomic_type->as->getSingleAtomic();
-
- if ($as_atomic_type instanceof TObject) {
- $class_string_types[] = new TTemplateParamClass(
- $lhs_atomic_type->param_name,
- 'object',
- null,
- $lhs_atomic_type->defining_class
- );
- } elseif ($as_atomic_type instanceof TNamedObject) {
- $class_string_types[] = new TTemplateParamClass(
- $lhs_atomic_type->param_name,
- $as_atomic_type->value,
- $as_atomic_type,
- $lhs_atomic_type->defining_class
- );
- }
- } elseif ($lhs_atomic_type instanceof TObject
- || $lhs_atomic_type instanceof TMixed
- ) {
- $has_mixed_or_object = true;
- }
- }
-
- if ($has_mixed_or_object) {
- $statements_analyzer->node_data->setType($stmt, new Union([new TClassString()]));
- } elseif ($class_string_types) {
- $statements_analyzer->node_data->setType($stmt, new Union($class_string_types));
- }
-
- return true;
- }
-
- if ($stmt->class instanceof PhpParser\Node\Expr\Variable) {
- $fq_class_name = null;
- $lhs_type_definite_class = null;
- if ($lhs_type->isSingle()) {
- $atomic_type = $lhs_type->getSingleAtomic();
- if ($atomic_type instanceof TNamedObject) {
- $fq_class_name = $atomic_type->value;
- $lhs_type_definite_class = $atomic_type->definite_class;
- } elseif ($atomic_type instanceof TLiteralClassString) {
- $fq_class_name = $atomic_type->value;
- $lhs_type_definite_class = $atomic_type->definite_class;
- }
- }
-
- if ($fq_class_name === null || $lhs_type_definite_class === null) {
- return true;
- }
-
- if ($codebase->classlikes->classExists($fq_class_name)) {
- $fq_class_name = $codebase->classlikes->getUnAliasedName($fq_class_name);
- }
-
- $moved_class = false;
-
- if ($codebase->alter_code) {
- $moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id
- );
- }
-
- // if we're ignoring that the class doesn't exist, exit anyway
- if (!$codebase->classlikes->classOrInterfaceOrEnumExists($fq_class_name)) {
- return true;
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $fq_class_name
- );
- }
-
- if (!$stmt->name instanceof PhpParser\Node\Identifier) {
- return true;
- }
-
- $const_id = $fq_class_name . '::' . $stmt->name;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $const_id
- );
- }
-
- $const_class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- if ($fq_class_name === $context->self
- || (
- $statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer &&
- $fq_class_name === $statements_analyzer->getSource()->getFQCLN()
- )
- ) {
- $class_visibility = ReflectionProperty::IS_PRIVATE;
- } elseif ($context->self &&
- ($codebase->classlikes->classExtends($context->self, $fq_class_name)
- || $codebase->classlikes->classExtends($fq_class_name, $context->self))
- ) {
- $class_visibility = ReflectionProperty::IS_PROTECTED;
- } else {
- $class_visibility = ReflectionProperty::IS_PUBLIC;
- }
-
- try {
- $class_constant_type = $codebase->classlikes->getClassConstantType(
- $fq_class_name,
- $stmt->name->name,
- $class_visibility,
- $statements_analyzer
- );
- } catch (InvalidArgumentException $_) {
- return true;
- } catch (CircularReferenceException $e) {
- IssueBuffer::maybeAdd(
- new CircularReference(
- 'Constant ' . $const_id . ' contains a circular reference',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return true;
- }
-
- if (!$class_constant_type) {
- if ($fq_class_name !== $context->self) {
- $class_constant_type = $codebase->classlikes->getClassConstantType(
- $fq_class_name,
- $stmt->name->name,
- ReflectionProperty::IS_PRIVATE,
- $statements_analyzer
- );
- }
-
- if ($class_constant_type) {
- IssueBuffer::maybeAdd(
- new InaccessibleClassConstant(
- 'Constant ' . $const_id . ' is not visible in this context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($context->check_consts) {
- IssueBuffer::maybeAdd(
- new UndefinedConstant(
- 'Constant ' . $const_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return true;
- }
-
- if ($context->calling_method_id) {
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $context->calling_method_id,
- strtolower($fq_class_name) . '::' . $stmt->name->name,
- false
- );
- }
-
- $declaring_const_id = strtolower($fq_class_name) . '::' . $stmt->name->name;
-
- if ($codebase->alter_code && !$moved_class) {
- foreach ($codebase->class_constant_transforms as $original_pattern => $transformation) {
- if ($declaring_const_id === $original_pattern) {
- [, $new_const_name] = explode('::', $transformation);
-
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->name->getAttribute('startFilePos'),
- (int) $stmt->name->getAttribute('endFilePos') + 1,
- $new_const_name
- );
-
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
- }
- }
-
- if ($context->self
- && !$context->collect_initializations
- && !$context->collect_mutations
- && !NamespaceAnalyzer::isWithinAny($context->self, $const_class_storage->internal)
- ) {
- IssueBuffer::maybeAdd(
- new InternalClass(
- $fq_class_name . ' is internal to '
- . InternalClass::listToPhrase($const_class_storage->internal)
- . ' but called from ' . $context->self,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($const_class_storage->deprecated && $fq_class_name !== $context->self) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- 'Class ' . $fq_class_name . ' is deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $fq_class_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif (isset($const_class_storage->constants[$stmt->name->name])
- && $const_class_storage->constants[$stmt->name->name]->deprecated
- ) {
- IssueBuffer::maybeAdd(
- new DeprecatedConstant(
- 'Constant ' . $const_id . ' is deprecated',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($const_class_storage->final || $lhs_type_definite_class === true) {
- $stmt_type = clone $class_constant_type;
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
- $context->vars_in_scope[$const_id] = $stmt_type;
- }
-
- return true;
- }
-
- return true;
- }
-
- public static function analyzeClassConstAssignment(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\ClassConst $stmt,
- Context $context
- ): void {
- foreach ($stmt->consts as $const) {
- ExpressionAnalyzer::analyze($statements_analyzer, $const->value, $context);
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php
deleted file mode 100644
index 2d0d50e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/ConstFetchAnalyzer.php
+++ /dev/null
@@ -1,309 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Issue\UndefinedConstant;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Union;
-use ReflectionProperty;
-
-use function array_key_exists;
-use function array_pop;
-use function explode;
-use function implode;
-use function strtolower;
-
-/**
- * @internal
- */
-class ConstFetchAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\ConstFetch $stmt,
- Context $context
- ): void {
- $const_name = implode('\\', $stmt->name->parts);
-
- switch (strtolower($const_name)) {
- case 'null':
- $statements_analyzer->node_data->setType($stmt, Type::getNull());
- break;
-
- case 'false':
- // false is a subtype of bool
- $statements_analyzer->node_data->setType($stmt, Type::getFalse());
- break;
-
- case 'true':
- $statements_analyzer->node_data->setType($stmt, Type::getTrue());
- break;
-
- case 'stdin':
- $statements_analyzer->node_data->setType($stmt, Type::getResource());
- break;
-
- default:
- $const_type = self::getConstType(
- $statements_analyzer,
- $const_name,
- $stmt->name instanceof PhpParser\Node\Name\FullyQualified,
- $context
- );
-
- $codebase = $statements_analyzer->getCodebase();
-
- $aliased_constants = $statements_analyzer->getAliases()->constants;
- if (isset($aliased_constants[$const_name])) {
- $fq_const_name = $aliased_constants[$const_name];
- } elseif ($stmt->name instanceof PhpParser\Node\Name\FullyQualified) {
- $fq_const_name = $const_name;
- } else {
- $fq_const_name = Type::getFQCLNFromString($const_name, $statements_analyzer->getAliases());
- }
-
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt,
- $const_type
- ? $fq_const_name
- : '*'
- . ($stmt->name instanceof PhpParser\Node\Name\FullyQualified
- ? '\\'
- : $statements_analyzer->getNamespace() . '-')
- . $const_name
- );
-
- if ($const_type) {
- $statements_analyzer->node_data->setType($stmt, clone $const_type);
- } elseif ($context->check_consts) {
- IssueBuffer::maybeAdd(
- new UndefinedConstant(
- 'Const ' . $const_name . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
-
- public static function getGlobalConstType(
- Codebase $codebase,
- string $fq_const_name,
- string $const_name
- ): ?Union {
- if ($const_name === 'STDERR'
- || $const_name === 'STDOUT'
- || $const_name === 'STDIN'
- ) {
- return Type::getResource();
- }
-
- if ($fq_const_name) {
- $stubbed_const_type = $codebase->getStubbedConstantType(
- $fq_const_name
- );
-
- if ($stubbed_const_type) {
- return $stubbed_const_type;
- }
- }
-
- $stubbed_const_type = $codebase->getStubbedConstantType(
- $const_name
- );
-
- if ($stubbed_const_type) {
- return $stubbed_const_type;
- }
-
- $predefined_constants = $codebase->config->getPredefinedConstants();
-
- if (($fq_const_name && array_key_exists($fq_const_name, $predefined_constants))
- || array_key_exists($const_name, $predefined_constants)
- ) {
- switch ($const_name) {
- case 'PHP_VERSION':
- case 'DIRECTORY_SEPARATOR':
- case 'PATH_SEPARATOR':
- case 'PHP_EOL':
- return Type::getNonEmptyString();
-
- case 'PEAR_EXTENSION_DIR':
- case 'PEAR_INSTALL_DIR':
- case 'PHP_BINARY':
- case 'PHP_BINDIR':
- case 'PHP_CONFIG_FILE_PATH':
- case 'PHP_CONFIG_FILE_SCAN_DIR':
- case 'PHP_DATADIR':
- case 'PHP_EXTENSION_DIR':
- case 'PHP_EXTRA_VERSION':
- case 'PHP_LIBDIR':
- case 'PHP_LOCALSTATEDIR':
- case 'PHP_MANDIR':
- case 'PHP_OS':
- case 'PHP_OS_FAMILY':
- case 'PHP_PREFIX':
- case 'PHP_SAPI':
- case 'PHP_SYSCONFDIR':
- return Type::getString();
-
- case 'PHP_MAJOR_VERSION':
- case 'PHP_MINOR_VERSION':
- case 'PHP_RELEASE_VERSION':
- case 'PHP_DEBUG':
- case 'PHP_FLOAT_DIG':
- case 'PHP_INT_MIN':
- case 'PHP_ZTS':
- return Type::getInt();
-
- case 'PHP_INT_MAX':
- case 'PHP_INT_SIZE':
- case 'PHP_MAXPATHLEN':
- case 'PHP_VERSION_ID':
- return Type::getPositiveInt();
-
- case 'PHP_FLOAT_EPSILON':
- case 'PHP_FLOAT_MAX':
- case 'PHP_FLOAT_MIN':
- return Type::getFloat();
- }
-
- if ($fq_const_name && array_key_exists($fq_const_name, $predefined_constants)) {
- return ClassLikeAnalyzer::getTypeFromValue($predefined_constants[$fq_const_name]);
- }
-
- return ClassLikeAnalyzer::getTypeFromValue($predefined_constants[$const_name]);
- }
-
- return null;
- }
-
- public static function getConstType(
- StatementsAnalyzer $statements_analyzer,
- string $const_name,
- bool $is_fully_qualified,
- ?Context $context
- ): ?Union {
- $aliased_constants = $statements_analyzer->getAliases()->constants;
-
- if (isset($aliased_constants[$const_name])) {
- $fq_const_name = $aliased_constants[$const_name];
- } elseif ($is_fully_qualified) {
- $fq_const_name = $const_name;
- } else {
- $fq_const_name = Type::getFQCLNFromString($const_name, $statements_analyzer->getAliases());
- }
-
- if ($fq_const_name) {
- $const_name_parts = explode('\\', $fq_const_name);
- $const_name = array_pop($const_name_parts);
- $namespace_name = implode('\\', $const_name_parts);
- $namespace_constants = NamespaceAnalyzer::getConstantsForNamespace(
- $namespace_name,
- ReflectionProperty::IS_PUBLIC
- );
-
- if (isset($namespace_constants[$const_name])) {
- return $namespace_constants[$const_name];
- }
- }
-
- if ($context && $context->hasVariable($fq_const_name)) {
- return $context->vars_in_scope[$fq_const_name];
- }
-
- $file_path = $statements_analyzer->getRootFilePath();
- $codebase = $statements_analyzer->getCodebase();
-
- $file_storage_provider = $codebase->file_storage_provider;
-
- $file_storage = $file_storage_provider->get($file_path);
-
- if (isset($file_storage->declaring_constants[$const_name])) {
- $constant_file_path = $file_storage->declaring_constants[$const_name];
-
- return $file_storage_provider->get($constant_file_path)->constants[$const_name];
- }
-
- if (isset($file_storage->declaring_constants[$fq_const_name])) {
- $constant_file_path = $file_storage->declaring_constants[$fq_const_name];
-
- return $file_storage_provider->get($constant_file_path)->constants[$fq_const_name];
- }
-
- return self::getGlobalConstType($codebase, $fq_const_name, $const_name)
- ?? self::getGlobalConstType($codebase, $const_name, $const_name);
- }
-
- public static function setConstType(
- StatementsAnalyzer $statements_analyzer,
- string $const_name,
- Union $const_type,
- Context $context
- ): void {
- $context->vars_in_scope[$const_name] = $const_type;
- $context->constants[$const_name] = $const_type;
-
- $source = $statements_analyzer->getSource();
-
- if ($source instanceof NamespaceAnalyzer) {
- $source->setConstType($const_name, $const_type);
- }
- }
-
- public static function getConstName(
- PhpParser\Node\Expr $first_arg_value,
- NodeDataProvider $type_provider,
- Codebase $codebase,
- Aliases $aliases
- ): ?string {
- $const_name = null;
-
- if ($first_arg_value instanceof PhpParser\Node\Scalar\String_) {
- $const_name = $first_arg_value->value;
- } elseif ($first_arg_type = $type_provider->getType($first_arg_value)) {
- if ($first_arg_type->isSingleStringLiteral()) {
- $const_name = $first_arg_type->getSingleStringLiteral()->value;
- }
- } else {
- $simple_type = SimpleTypeInferer::infer($codebase, $type_provider, $first_arg_value, $aliases);
-
- if ($simple_type && $simple_type->isSingleStringLiteral()) {
- $const_name = $simple_type->getSingleStringLiteral()->value;
- }
- }
-
- return $const_name;
- }
-
- public static function analyzeConstAssignment(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Const_ $stmt,
- Context $context
- ): void {
- foreach ($stmt->consts as $const) {
- ExpressionAnalyzer::analyze($statements_analyzer, $const->value, $context);
-
- self::setConstType(
- $statements_analyzer,
- $const->name->name,
- $statements_analyzer->node_data->getType($const->value) ?? Type::getMixed(),
- $context
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php
deleted file mode 100644
index 1e258a0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/InstancePropertyFetchAnalyzer.php
+++ /dev/null
@@ -1,482 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Issue\ImpurePropertyFetch;
-use Psalm\Issue\InvalidPropertyFetch;
-use Psalm\Issue\MixedPropertyFetch;
-use Psalm\Issue\NullPropertyFetch;
-use Psalm\Issue\PossiblyInvalidPropertyFetch;
-use Psalm\Issue\PossiblyNullPropertyFetch;
-use Psalm\Issue\UninitializedProperty;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TTemplateParam;
-
-use function array_merge;
-use function array_shift;
-use function rtrim;
-use function strtolower;
-
-/**
- * @internal
- */
-class InstancePropertyFetchAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- Context $context,
- bool $in_assignment = false,
- bool $is_static_access = false
- ): bool {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- if (!$stmt->name instanceof PhpParser\Node\Identifier) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context) === false) {
- return false;
- }
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return false;
- }
-
- $context->inside_general_use = $was_inside_general_use;
-
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- $prop_name = $stmt->name->name;
- } elseif (($stmt_name_type = $statements_analyzer->node_data->getType($stmt->name))
- && $stmt_name_type->isSingleStringLiteral()
- ) {
- $prop_name = $stmt_name_type->getSingleStringLiteral()->value;
- } else {
- $prop_name = null;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $stmt_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $var_id = ExpressionIdentifier::getArrayVarId(
- $stmt,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id && $context->hasVariable($var_id)) {
- self::handleScopedProperty(
- $context,
- $var_id,
- $statements_analyzer,
- $stmt,
- $codebase,
- $stmt_var_id,
- $in_assignment
- );
-
- return true;
- }
-
- if ($stmt_var_id && $context->hasVariable($stmt_var_id)) {
- $stmt_var_type = $context->vars_in_scope[$stmt_var_id];
- } else {
- $stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
- }
-
- if (!$stmt_var_type) {
- return true;
- }
-
- if ($stmt_var_type->isNull()) {
- if (IssueBuffer::accepts(
- new NullPropertyFetch(
- 'Cannot get property on null variable ' . $stmt_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- if ($stmt_var_type->isEmpty()) {
- if (IssueBuffer::accepts(
- new MixedPropertyFetch(
- 'Cannot fetch property on empty var ' . $stmt_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- if ($stmt_var_type->hasMixed()) {
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($stmt->name instanceof PhpParser\Node\Identifier) {
- $codebase->analyzer->addMixedMemberName(
- '$' . $stmt->name->name,
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- IssueBuffer::maybeAdd(
- new MixedPropertyFetch(
- 'Cannot fetch property on mixed var ' . $stmt_var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_var_type->getId()
- );
- }
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getRootFilePath());
- }
-
- if ($stmt_var_type->isNullable() && !$stmt_var_type->ignore_nullable_issues) {
- // we can only be sure that the variable is possibly null if we know the var_id
- if (!$context->inside_isset
- && $stmt->name instanceof PhpParser\Node\Identifier
- && !MethodCallAnalyzer::hasNullsafe($stmt->var)
- ) {
- IssueBuffer::maybeAdd(
- new PossiblyNullPropertyFetch(
- rtrim('Cannot get property on possibly null variable ' . $stmt_var_id)
- . ' of type ' . $stmt_var_type,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getNull());
- }
- }
-
- if (!$prop_name) {
- if ($stmt_var_type->hasObjectType() && !$context->ignore_variable_property) {
- foreach ($stmt_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TNamedObject) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($type->value) . '::$',
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_var_type->getId()
- );
- }
-
- return true;
- }
-
- $invalid_fetch_types = [];
- $has_valid_fetch_type = false;
-
- $var_atomic_types = $stmt_var_type->getAtomicTypes();
-
- while ($lhs_type_part = array_shift($var_atomic_types)) {
- if ($lhs_type_part instanceof TTemplateParam) {
- $var_atomic_types = array_merge($var_atomic_types, $lhs_type_part->as->getAtomicTypes());
- continue;
- }
-
- AtomicPropertyFetchAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- $in_assignment,
- $var_id,
- $stmt_var_id,
- $stmt_var_type,
- $lhs_type_part,
- $prop_name,
- $has_valid_fetch_type,
- $invalid_fetch_types,
- $is_static_access
- );
- }
-
- $stmt_type = $statements_analyzer->node_data->getType($stmt);
-
- if ($stmt_var_type->isNullable() && !$context->inside_isset && $stmt_type) {
- $stmt_type->addType(new TNull);
-
- if ($stmt_var_type->ignore_nullable_issues) {
- $stmt_type->ignore_nullable_issues = true;
- }
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- && ($stmt_type = $statements_analyzer->node_data->getType($stmt))
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_type->getId()
- );
- }
-
- if ($invalid_fetch_types) {
- $lhs_type_part = $invalid_fetch_types[0];
-
- if ($has_valid_fetch_type) {
- IssueBuffer::maybeAdd(
- new PossiblyInvalidPropertyFetch(
- 'Cannot fetch property on possible non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new InvalidPropertyFetch(
- 'Cannot fetch property on non-object ' . $stmt_var_id . ' of type ' . $lhs_type_part,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($var_id) {
- $context->vars_in_scope[$var_id] = $statements_analyzer->node_data->getType($stmt) ?? Type::getMixed();
- }
-
- return true;
- }
-
- private static function handleScopedProperty(
- Context $context,
- string $var_id,
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\PropertyFetch $stmt,
- Codebase $codebase,
- ?string $stmt_var_id,
- bool $in_assignment
- ): void {
- $stmt_type = $context->vars_in_scope[$var_id];
-
- // we don't need to check anything
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && (!(($parent_source = $statements_analyzer->getSource())
- instanceof FunctionLikeAnalyzer)
- || !$parent_source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_type->getId()
- );
- }
-
- if ($stmt_var_id === '$this'
- && !$stmt_type->initialized
- && $context->collect_initializations
- && ($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
- && $stmt_var_type->hasObjectType()
- && $stmt->name instanceof PhpParser\Node\Identifier
- ) {
- $source = $statements_analyzer->getSource();
-
- $property_id = null;
-
- foreach ($stmt_var_type->getAtomicTypes() as $lhs_type_part) {
- if ($lhs_type_part instanceof TNamedObject) {
- if (!$codebase->classExists($lhs_type_part->value)) {
- continue;
- }
-
- $property_id = $lhs_type_part->value . '::$' . $stmt->name->name;
- }
- }
-
- if ($property_id
- && $source instanceof FunctionLikeAnalyzer
- && $source->getMethodName() === '__construct'
- && !$context->inside_unset
- ) {
- if ($context->inside_isset
- || ($context->inside_assignment
- && isset($context->vars_in_scope[$var_id])
- && $context->vars_in_scope[$var_id]->isNullable()
- )
- ) {
- $stmt_type->initialized = true;
- } else {
- IssueBuffer::maybeAdd(
- new UninitializedProperty(
- 'Cannot use uninitialized property ' . $var_id,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $var_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $stmt_type->addType(new TNull);
- }
- }
- }
-
-
- if (($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
- && $stmt_var_type->hasObjectType()
- && $stmt->name instanceof PhpParser\Node\Identifier
- ) {
- // log the appearance
- foreach ($stmt_var_type->getAtomicTypes() as $lhs_type_part) {
- if ($lhs_type_part instanceof TNamedObject) {
- if (!$codebase->classExists($lhs_type_part->value)) {
- continue;
- }
-
- $property_id = $lhs_type_part->value . '::$' . $stmt->name->name;
-
-
- $class_storage = $codebase->classlike_storage_provider->get($lhs_type_part->value);
-
- AtomicPropertyFetchAnalyzer::processTaints(
- $statements_analyzer,
- $stmt,
- $stmt_type,
- $property_id,
- $class_storage,
- $in_assignment
- );
-
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $property_id,
- true,
- $statements_analyzer
- );
-
- if ($declaring_property_class) {
- AtomicPropertyFetchAnalyzer::checkPropertyDeprecation(
- $stmt->name->name,
- $declaring_property_class,
- $stmt,
- $statements_analyzer
- );
- }
-
- $codebase->properties->propertyExists(
- $property_id,
- true,
- $statements_analyzer,
- $context,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null
- );
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $property_id
- );
- }
-
- if (!$context->collect_mutations
- && !$context->collect_initializations
- && !($class_storage->external_mutation_free
- && $stmt_type->allow_mutations)
- ) {
- if ($context->pure) {
- IssueBuffer::maybeAdd(
- new ImpurePropertyFetch(
- 'Cannot access a property on a mutable object from a pure context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php
deleted file mode 100644
index e28e3f0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/StaticPropertyFetchAnalyzer.php
+++ /dev/null
@@ -1,486 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ImpureStaticProperty;
-use Psalm\Issue\ParentNotFound;
-use Psalm\Issue\UndefinedPropertyAssignment;
-use Psalm\Issue\UndefinedPropertyFetch;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\VirtualPropertyFetch;
-use Psalm\Node\Expr\VirtualStaticPropertyFetch;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Type;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function count;
-use function explode;
-use function in_array;
-use function md5;
-use function strtolower;
-
-/**
- * @internal
- */
-class StaticPropertyFetchAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\StaticPropertyFetch $stmt,
- Context $context
- ): bool {
- if (!$stmt->class instanceof PhpParser\Node\Name) {
- self::analyzeVariableStaticPropertyFetch($statements_analyzer, $stmt->class, $stmt, $context);
- return true;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if (count($stmt->class->parts) === 1
- && in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)
- ) {
- if ($stmt->class->parts[0] === 'parent') {
- $fq_class_name = $statements_analyzer->getParentFQCLN();
-
- if ($fq_class_name === null) {
- if (IssueBuffer::accepts(
- new ParentNotFound(
- 'Cannot check property fetch on parent as this class does not extend another',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
- } else {
- $fq_class_name = (string)$context->self;
- }
-
- if ($context->isPhantomClass($fq_class_name)) {
- return true;
- }
- } else {
- $aliases = $statements_analyzer->getAliases();
-
- if ($context->calling_method_id
- && !$stmt->class instanceof PhpParser\Node\Name\FullyQualified
- ) {
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $context->calling_method_id,
- 'use:' . $stmt->class->parts[0] . ':' . md5($statements_analyzer->getFilePath()),
- false
- );
- }
-
- $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $aliases
- );
-
- if ($context->isPhantomClass($fq_class_name)) {
- return true;
- }
-
- if ($context->check_classes) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt->class),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues()
- ) !== true) {
- return false;
- }
- }
- }
-
- if ($fq_class_name
- && $codebase->methods_to_move
- && $context->calling_method_id
- && isset($codebase->methods_to_move[$context->calling_method_id])
- ) {
- $destination_method_id = $codebase->methods_to_move[$context->calling_method_id];
-
- $codebase->classlikes->airliftClassLikeReference(
- $fq_class_name,
- explode('::', $destination_method_id)[0],
- $statements_analyzer->getFilePath(),
- (int) $stmt->class->getAttribute('startFilePos'),
- (int) $stmt->class->getAttribute('endFilePos') + 1
- );
- }
-
- if ($fq_class_name) {
- $statements_analyzer->node_data->setType(
- $stmt->class,
- new Union([new TNamedObject($fq_class_name)])
- );
- }
-
- if ($stmt->name instanceof PhpParser\Node\VarLikeIdentifier) {
- $prop_name = $stmt->name->name;
- } elseif (($stmt_name_type = $statements_analyzer->node_data->getType($stmt->name))
- && $stmt_name_type->isSingleStringLiteral()
- ) {
- $prop_name = $stmt_name_type->getSingleStringLiteral()->value;
- } else {
- $prop_name = null;
- }
-
- if (!$prop_name) {
- if ($fq_class_name) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($fq_class_name) . '::$',
- $context->calling_method_id ?: $statements_analyzer->getFileName()
- );
- }
-
- return true;
- }
-
- if (!$fq_class_name
- || !$context->check_classes
- || !$context->check_variables
- || ExpressionAnalyzer::isMock($fq_class_name)
- ) {
- return true;
- }
-
- $var_id = ExpressionIdentifier::getVarId(
- $stmt,
- $context->self ?: $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- $property_id = $fq_class_name . '::$' . $prop_name;
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $property_id
- );
- }
-
- if ($context->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureStaticProperty(
- 'Cannot use a static property in a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource()
- instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
-
- if ($var_id && $context->hasVariable($var_id)) {
- $stmt_type = $context->vars_in_scope[$var_id];
-
- // we don't need to check anything
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if ($codebase->collect_references) {
- // log the appearance
- $codebase->properties->propertyExists(
- $property_id,
- true,
- $statements_analyzer,
- $context,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null
- );
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- && ($stmt_type = $statements_analyzer->node_data->getType($stmt))
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_type->getId()
- );
- }
-
- return true;
- }
-
- if (!$codebase->properties->propertyExists(
- $property_id,
- true,
- $statements_analyzer,
- $context,
- $codebase->collect_locations
- ? new CodeLocation($statements_analyzer->getSource(), $stmt)
- : null
- )
- ) {
- if ($context->inside_isset) {
- return true;
- }
-
- IssueBuffer::maybeAdd(
- new UndefinedPropertyFetch(
- 'Static property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return true;
- }
-
- $declaring_property_class = $codebase->properties->getDeclaringClassForProperty(
- $fq_class_name . '::$' . $prop_name,
- true,
- $statements_analyzer
- );
-
- if ($declaring_property_class === null) {
- return false;
- }
-
- AtomicPropertyFetchAnalyzer::checkPropertyDeprecation(
- $prop_name,
- $declaring_property_class,
- $stmt,
- $statements_analyzer
- );
-
- $class_storage = $codebase->classlike_storage_provider->get($declaring_property_class);
- $property = $class_storage->properties[$prop_name];
-
- if (!$property->is_static) {
- if ($context->inside_isset) {
- return true;
- }
-
- if ($context->inside_assignment) {
- IssueBuffer::maybeAdd(
- new UndefinedPropertyAssignment(
- 'Static property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedPropertyFetch(
- 'Static property ' . $property_id . ' is not defined',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $property_id
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- return true;
- }
-
- if (ClassLikeAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return false;
- }
-
- $declaring_property_id = strtolower($declaring_property_class) . '::$' . $prop_name;
-
- if ($codebase->alter_code) {
- $moved_class = $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id
- );
-
- if (!$moved_class) {
- foreach ($codebase->property_transforms as $original_pattern => $transformation) {
- if ($declaring_property_id === $original_pattern) {
- [$old_declaring_fq_class_name] = explode('::$', $declaring_property_id);
- [$new_fq_class_name, $new_property_name] = explode('::$', $transformation);
-
- $file_manipulations = [];
-
- if (strtolower($new_fq_class_name) !== $old_declaring_fq_class_name) {
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->class->getAttribute('startFilePos'),
- (int) $stmt->class->getAttribute('endFilePos') + 1,
- Type::getStringFromFQCLN(
- $new_fq_class_name,
- $statements_analyzer->getNamespace(),
- $statements_analyzer->getAliasedClassesFlipped(),
- null
- )
- );
- }
-
- $file_manipulations[] = new FileManipulation(
- (int) $stmt->name->getAttribute('startFilePos'),
- (int) $stmt->name->getAttribute('endFilePos') + 1,
- '$' . $new_property_name
- );
-
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
- }
- }
- }
-
- if ($var_id) {
- if ($property->type) {
- $context->vars_in_scope[$var_id] = TypeExpander::expandUnion(
- $codebase,
- clone $property->type,
- $class_storage->name,
- $class_storage->name,
- $class_storage->parent_class
- );
- } else {
- $context->vars_in_scope[$var_id] = Type::getMixed();
- }
-
- $stmt_type = clone $context->vars_in_scope[$var_id];
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt->name,
- $stmt_type->getId()
- );
- }
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- return true;
- }
-
- private static function analyzeVariableStaticPropertyFetch(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt_class,
- PhpParser\Node\Expr\StaticPropertyFetch $stmt,
- Context $context
- ): void {
- $was_inside_general_use = $context->inside_general_use;
-
- $context->inside_general_use = true;
-
- ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $stmt_class,
- $context
- );
-
- $context->inside_general_use = $was_inside_general_use;
-
- $stmt_class_type = $statements_analyzer->node_data->getType($stmt_class) ?? Type::getMixed();
-
- $old_data_provider = $statements_analyzer->node_data;
-
- $stmt_type = null;
-
- $codebase = $statements_analyzer->getCodebase();
-
- foreach ($stmt_class_type->getAtomicTypes() as $class_atomic_type) {
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $string_type = ($class_atomic_type instanceof TClassString
- && $class_atomic_type->as_type !== null)
- ? $class_atomic_type->as_type->value
- : ($class_atomic_type instanceof TLiteralString
- ? $class_atomic_type->value
- : null);
-
- if ($string_type) {
- $new_stmt_name = new VirtualFullyQualified(
- $string_type,
- $stmt_class->getAttributes()
- );
-
- $fake_static_property = new VirtualStaticPropertyFetch(
- $new_stmt_name,
- $stmt->name,
- $stmt->getAttributes()
- );
-
- self::analyze($statements_analyzer, $fake_static_property, $context);
-
- $fake_stmt_type = $statements_analyzer->node_data->getType($fake_static_property) ?? Type::getMixed();
- } else {
- $fake_var_name = '__fake_var_' . (string) $stmt->getAttribute('startFilePos');
-
- $fake_var = new VirtualVariable(
- $fake_var_name,
- $stmt_class->getAttributes()
- );
-
- $context->vars_in_scope['$' . $fake_var_name] = new Union([$class_atomic_type]);
-
- $fake_instance_property = new VirtualPropertyFetch(
- $fake_var,
- $stmt->name,
- $stmt->getAttributes()
- );
-
- InstancePropertyFetchAnalyzer::analyze(
- $statements_analyzer,
- $fake_instance_property,
- $context,
- false,
- true
- );
-
- $fake_stmt_type = $statements_analyzer->node_data->getType($fake_instance_property) ?? Type::getMixed();
- }
-
- $stmt_type = $stmt_type
- ? Type::combineUnionTypes($stmt_type, $fake_stmt_type, $codebase)
- : $fake_stmt_type;
-
- $statements_analyzer->node_data = $old_data_provider;
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php
deleted file mode 100644
index 3ecde22..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/VariableFetchAnalyzer.php
+++ /dev/null
@@ -1,559 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression\Fetch;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\TaintSource;
-use Psalm\Issue\ImpureVariable;
-use Psalm\Issue\InvalidScope;
-use Psalm\Issue\PossiblyUndefinedGlobalVariable;
-use Psalm\Issue\PossiblyUndefinedVariable;
-use Psalm\Issue\UndefinedGlobalVariable;
-use Psalm\Issue\UndefinedVariable;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\TaintKindGroup;
-use Psalm\Type\Union;
-
-use function in_array;
-use function is_string;
-
-/**
- * @internal
- */
-class VariableFetchAnalyzer
-{
- public const SUPER_GLOBALS = [
- '$GLOBALS',
- '$_SERVER',
- '$_GET',
- '$_POST',
- '$_FILES',
- '$_COOKIE',
- '$_SESSION',
- '$_REQUEST',
- '$_ENV',
- '$http_response_header',
- ];
-
- /**
- * @param bool $from_global - when used in a global keyword
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Variable $stmt,
- Context $context,
- bool $passed_by_reference = false,
- ?Union $by_ref_type = null,
- bool $array_assignment = false,
- bool $from_global = false
- ): bool {
- $project_analyzer = $statements_analyzer->getFileAnalyzer()->project_analyzer;
- $codebase = $statements_analyzer->getCodebase();
-
- if ($stmt->name === 'this') {
- if ($statements_analyzer->isStatic()) {
- if (IssueBuffer::accepts(
- new InvalidScope(
- 'Invalid reference to $this in a static context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- return true;
- }
-
- if (!isset($context->vars_in_scope['$this'])) {
- if (IssueBuffer::accepts(
- new InvalidScope(
- 'Invalid reference to $this in a non-class context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
-
- $context->vars_in_scope['$this'] = Type::getMixed();
- $context->vars_possibly_in_scope['$this'] = true;
-
- return true;
- }
-
- $statements_analyzer->node_data->setType($stmt, clone $context->vars_in_scope['$this']);
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- && ($stmt_type = $statements_analyzer->node_data->getType($stmt))
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt,
- $stmt_type->getId()
- );
- }
-
- if (!$context->collect_mutations && !$context->collect_initializations) {
- if ($context->pure) {
- IssueBuffer::maybeAdd(
- new ImpureVariable(
- 'Cannot reference $this in a pure context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- return true;
- }
-
- if (!$context->check_variables) {
- if (is_string($stmt->name)) {
- $var_name = '$' . $stmt->name;
-
- if (!$context->hasVariable($var_name)) {
- $context->vars_in_scope[$var_name] = Type::getMixed();
- $context->vars_possibly_in_scope[$var_name] = true;
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- } else {
- $stmt_type = clone $context->vars_in_scope[$var_name];
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- self::addDataFlowToVariable($statements_analyzer, $stmt, $var_name, $stmt_type, $context);
- }
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- return true;
- }
-
- if (is_string($stmt->name) && self::isSuperGlobal('$' . $stmt->name)) {
- $var_name = '$' . $stmt->name;
-
- if (isset($context->vars_in_scope[$var_name])) {
- $type = clone $context->vars_in_scope[$var_name];
-
- self::taintVariable($statements_analyzer, $var_name, $type, $stmt);
-
- $statements_analyzer->node_data->setType($stmt, $type);
-
- return true;
- }
-
- $type = self::getGlobalType($var_name);
-
- self::taintVariable($statements_analyzer, $var_name, $type, $stmt);
-
- $statements_analyzer->node_data->setType($stmt, $type);
- $context->vars_in_scope[$var_name] = clone $type;
- $context->vars_possibly_in_scope[$var_name] = true;
-
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt,
- $var_name
- );
-
- return true;
- }
-
- if (!is_string($stmt->name)) {
- if ($context->pure) {
- IssueBuffer::maybeAdd(
- new ImpureVariable(
- 'Cannot reference an unknown variable in a pure context',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_impure = true;
- }
-
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
- $expr_result = ExpressionAnalyzer::analyze($statements_analyzer, $stmt->name, $context);
- $context->inside_general_use = $was_inside_general_use;
-
- return $expr_result;
- }
-
- if ($passed_by_reference && $by_ref_type) {
- AssignmentAnalyzer::assignByRefParam(
- $statements_analyzer,
- $stmt,
- $by_ref_type,
- $by_ref_type,
- $context
- );
-
- return true;
- }
-
- $var_name = '$' . $stmt->name;
-
- if (!$context->hasVariable($var_name)) {
- if (!isset($context->vars_possibly_in_scope[$var_name])
- || !$statements_analyzer->getFirstAppearance($var_name)
- ) {
- if ($array_assignment) {
- // if we're in an array assignment, let's assign the variable
- // because PHP allows it
-
- $context->vars_in_scope[$var_name] = Type::getArray();
- $context->vars_possibly_in_scope[$var_name] = true;
-
- // it might have been defined first in another if/else branch
- if (!$statements_analyzer->hasVariable($var_name)) {
- $statements_analyzer->registerVariable(
- $var_name,
- new CodeLocation($statements_analyzer, $stmt),
- $context->branch_point
- );
- }
- } elseif (!$context->inside_isset
- || $statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- ) {
- if ($context->is_global || $from_global) {
- IssueBuffer::maybeAdd(
- new UndefinedGlobalVariable(
- 'Cannot find referenced variable ' . $var_name . ' in global scope',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $var_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
-
- return true;
- }
-
- IssueBuffer::maybeAdd(
- new UndefinedVariable(
- 'Cannot find referenced variable ' . $var_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
-
- return true;
- }
- }
-
- $first_appearance = $statements_analyzer->getFirstAppearance($var_name);
-
- if ($first_appearance && !$context->inside_isset && !$context->inside_unset) {
- if ($context->is_global) {
- if ($codebase->alter_code) {
- if (!isset($project_analyzer->getIssuesToFix()['PossiblyUndefinedGlobalVariable'])) {
- return true;
- }
-
- $branch_point = $statements_analyzer->getBranchPoint($var_name);
-
- if ($branch_point) {
- $statements_analyzer->addVariableInitialization($var_name, $branch_point);
- }
-
- return true;
- }
-
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedGlobalVariable(
- 'Possibly undefined global variable ' . $var_name . ', first seen on line ' .
- $first_appearance->getLineNumber(),
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $var_name
- ),
- $statements_analyzer->getSuppressedIssues(),
- (bool) $statements_analyzer->getBranchPoint($var_name)
- );
- } else {
- if ($codebase->alter_code) {
- if (!isset($project_analyzer->getIssuesToFix()['PossiblyUndefinedVariable'])) {
- return true;
- }
-
- $branch_point = $statements_analyzer->getBranchPoint($var_name);
-
- if ($branch_point) {
- $statements_analyzer->addVariableInitialization($var_name, $branch_point);
- }
-
- return true;
- }
-
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedVariable(
- 'Possibly undefined variable ' . $var_name . ', first seen on line ' .
- $first_appearance->getLineNumber(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues(),
- (bool) $statements_analyzer->getBranchPoint($var_name)
- );
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt,
- $first_appearance->raw_file_start . '-' . $first_appearance->raw_file_end . ':mixed'
- );
- }
-
- $stmt_type = Type::getMixed();
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- self::addDataFlowToVariable($statements_analyzer, $stmt, $var_name, $stmt_type, $context);
-
- $statements_analyzer->registerPossiblyUndefinedVariable($var_name, $stmt);
-
- return true;
- }
- } else {
- $stmt_type = clone $context->vars_in_scope[$var_name];
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- self::addDataFlowToVariable($statements_analyzer, $stmt, $var_name, $stmt_type, $context);
-
- if ($stmt_type->possibly_undefined_from_try && !$context->inside_isset) {
- if ($context->is_global) {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedGlobalVariable(
- 'Possibly undefined global variable ' . $var_name . ' defined in try block',
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $var_name
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new PossiblyUndefinedVariable(
- 'Possibly undefined variable ' . $var_name . ' defined in try block',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeType(
- $statements_analyzer->getFilePath(),
- $stmt,
- $stmt_type->getId()
- );
- }
-
- if ($codebase->store_node_types
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $first_appearance = $statements_analyzer->getFirstAppearance($var_name);
-
- if ($first_appearance) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt,
- $first_appearance->raw_file_start
- . '-' . $first_appearance->raw_file_end
- . ':' . $stmt_type->getId()
- );
- }
- }
- }
-
- return true;
- }
-
- private static function addDataFlowToVariable(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Variable $stmt,
- string $var_name,
- Union $stmt_type,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- if ($statements_analyzer->data_flow_graph
- && $codebase->find_unused_variables
- && ($context->inside_return
- || $context->inside_call
- || $context->inside_general_use
- || $context->inside_conditional
- || $context->inside_throw
- || $context->inside_isset)
- ) {
- if (!$stmt_type->parent_nodes) {
- $assignment_node = DataFlowNode::getForAssignment(
- $var_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- );
-
- $stmt_type->parent_nodes = [
- $assignment_node->id => $assignment_node
- ];
- }
-
- foreach ($stmt_type->parent_nodes as $parent_node) {
- if ($context->inside_call || $context->inside_return) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode(
- 'variable-use',
- 'variable use',
- null
- ),
- 'use-inside-call'
- );
- } elseif ($context->inside_conditional) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode(
- 'variable-use',
- 'variable use',
- null
- ),
- 'use-inside-conditional'
- );
- } elseif ($context->inside_isset) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode(
- 'variable-use',
- 'variable use',
- null
- ),
- 'use-inside-isset'
- );
- } else {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode(
- 'variable-use',
- 'variable use',
- null
- ),
- 'variable-use'
- );
- }
- }
- }
- }
-
- private static function taintVariable(
- StatementsAnalyzer $statements_analyzer,
- string $var_name,
- Union $type,
- PhpParser\Node\Expr\Variable $stmt
- ): void {
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- if ($var_name === '$_GET'
- || $var_name === '$_POST'
- || $var_name === '$_COOKIE'
- || $var_name === '$_REQUEST'
- ) {
- $taint_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $server_taint_source = new TaintSource(
- $var_name . ':' . $taint_location->file_name . ':' . $taint_location->raw_file_start,
- $var_name,
- null,
- null,
- TaintKindGroup::ALL_INPUT
- );
-
- $statements_analyzer->data_flow_graph->addSource($server_taint_source);
-
- $type->parent_nodes = [
- $server_taint_source->id => $server_taint_source
- ];
- }
- }
- }
-
- /**
- * @psalm-pure
- */
- public static function isSuperGlobal(string $var_id): bool
- {
- return in_array(
- $var_id,
- self::SUPER_GLOBALS,
- true
- );
- }
-
- public static function getGlobalType(string $var_id): Union
- {
- $config = Config::getInstance();
-
- if (isset($config->globals[$var_id])) {
- return Type::parseString($config->globals[$var_id]);
- }
-
- if ($var_id === '$argv') {
- return new Union([
- new TArray([Type::getInt(), Type::getString()]),
- ]);
- }
-
- if ($var_id === '$argc') {
- return Type::getInt();
- }
-
- if ($var_id === '$http_response_header') {
- return new Union([
- new TList(Type::getString())
- ]);
- }
-
- if (self::isSuperGlobal($var_id)) {
- $type = Type::getArray();
- if ($var_id === '$_SESSION') {
- $type->possibly_undefined = true;
- }
- return $type;
- }
-
- return Type::getMixed();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php
deleted file mode 100644
index 38f9a9a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncDecExpressionAnalyzer.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use PhpParser\Node\Expr\PostDec;
-use PhpParser\Node\Expr\PostInc;
-use PhpParser\Node\Expr\PreDec;
-use PhpParser\Node\Expr\PreInc;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\ArithmeticOpAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Node\Expr\BinaryOp\VirtualMinus;
-use Psalm\Node\Expr\BinaryOp\VirtualPlus;
-use Psalm\Node\Expr\VirtualAssign;
-use Psalm\Node\Scalar\VirtualLNumber;
-use Psalm\Type;
-
-class IncDecExpressionAnalyzer
-{
- /**
- * @param PostInc|PostDec|PreInc|PreDec $stmt
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Context $context
- ): bool {
- $was_inside_assignment = $context->inside_assignment;
- $context->inside_assignment = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context) === false) {
- $context->inside_assignment = $was_inside_assignment;
-
- return false;
- }
-
- $context->inside_assignment = $was_inside_assignment;
-
- $stmt_var_type = $statements_analyzer->node_data->getType($stmt->var);
-
- if ($stmt instanceof PostInc || $stmt instanceof PostDec) {
- $statements_analyzer->node_data->setType($stmt, $stmt_var_type ?? Type::getMixed());
- }
-
- if (($stmt_var_type = $statements_analyzer->node_data->getType($stmt->var))
- && $stmt_var_type->hasString()
- && ($stmt instanceof PostInc || $stmt instanceof PreInc)
- ) {
- $return_type = null;
-
- $fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes());
- $statements_analyzer->node_data->setType($fake_right_expr, Type::getInt());
-
- ArithmeticOpAnalyzer::analyze(
- $statements_analyzer,
- $statements_analyzer->node_data,
- $stmt->var,
- $fake_right_expr,
- $stmt,
- $return_type,
- $context
- );
-
- $result_type = $return_type ?? Type::getMixed();
- $statements_analyzer->node_data->setType($stmt, $result_type);
-
- BinaryOpAnalyzer::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->var,
- $fake_right_expr,
- 'inc'
- );
-
- $var_id = ExpressionIdentifier::getArrayVarId($stmt->var, null);
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($var_id && isset($context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = $result_type;
-
- if ($codebase->find_unused_variables && $stmt->var instanceof PhpParser\Node\Expr\Variable) {
- $context->assigned_var_ids[$var_id] = (int) $stmt->var->getAttribute('startFilePos');
- $context->possibly_assigned_var_ids[$var_id] = true;
- }
-
- // removes dependent vars from $context
- $context->removeDescendents(
- $var_id,
- $context->vars_in_scope[$var_id],
- $return_type,
- $statements_analyzer
- );
- }
- } else {
- $fake_right_expr = new VirtualLNumber(1, $stmt->getAttributes());
-
- $operation = $stmt instanceof PostInc || $stmt instanceof PreInc
- ? new VirtualPlus(
- $stmt->var,
- $fake_right_expr,
- $stmt->var->getAttributes()
- )
- : new VirtualMinus(
- $stmt->var,
- $fake_right_expr,
- $stmt->var->getAttributes()
- );
-
- $fake_assignment = new VirtualAssign(
- $stmt->var,
- $operation,
- $stmt->getAttributes()
- );
-
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $fake_assignment, $context) === false) {
- return false;
- }
-
- if ($stmt instanceof PreInc || $stmt instanceof PreDec) {
- $old_node_data->setType(
- $stmt,
- $statements_analyzer->node_data->getType($operation) ?? Type::getMixed()
- );
- }
-
- $statements_analyzer->node_data = $old_node_data;
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php
deleted file mode 100644
index 9fd4f16..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php
+++ /dev/null
@@ -1,423 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Exception\FileIncludeException;
-use Psalm\Exception\UnpreparedAnalysisException;
-use Psalm\Internal\Analyzer\FileAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Issue\MissingFile;
-use Psalm\Issue\UnresolvableInclude;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Type\TaintKind;
-
-use function constant;
-use function defined;
-use function dirname;
-use function explode;
-use function file_exists;
-use function get_include_path;
-use function get_included_files;
-use function implode;
-use function in_array;
-use function is_string;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function realpath;
-use function str_repeat;
-use function str_replace;
-use function substr;
-
-use const DIRECTORY_SEPARATOR;
-use const PATH_SEPARATOR;
-use const PHP_EOL;
-
-/**
- * @internal
- */
-class IncludeAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Include_ $stmt,
- Context $context,
- ?Context $global_context = null
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
- $config = $codebase->config;
-
- if (!$config->allow_includes) {
- throw new FileIncludeException(
- 'File includes are not allowed per your Psalm config - check the allowFileIncludes flag.'
- );
- }
-
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
-
- $context->inside_call = $was_inside_call;
-
- $stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($stmt->expr instanceof PhpParser\Node\Scalar\String_
- || ($stmt_expr_type && $stmt_expr_type->isSingleStringLiteral())
- ) {
- if ($stmt->expr instanceof PhpParser\Node\Scalar\String_) {
- $path_to_file = $stmt->expr->value;
- } else {
- $path_to_file = $stmt_expr_type->getSingleStringLiteral()->value;
- }
-
- $path_to_file = str_replace('/', DIRECTORY_SEPARATOR, $path_to_file);
-
- // attempts to resolve using get_include_path dirs
- $include_path = self::resolveIncludePath($path_to_file, dirname($statements_analyzer->getFilePath()));
- $path_to_file = $include_path ?: $path_to_file;
-
- if (DIRECTORY_SEPARATOR === '/') {
- $is_path_relative = $path_to_file[0] !== DIRECTORY_SEPARATOR;
- } else {
- $is_path_relative = !preg_match('~^[A-Z]:\\\\~i', $path_to_file);
- }
-
- if ($is_path_relative) {
- $path_to_file = $config->base_dir . DIRECTORY_SEPARATOR . $path_to_file;
- }
- } else {
- $path_to_file = self::getPathTo(
- $stmt->expr,
- $statements_analyzer->node_data,
- $statements_analyzer,
- $statements_analyzer->getFileName(),
- $config
- );
- }
-
- if ($stmt_expr_type
- && $statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- && $stmt_expr_type->parent_nodes
- && !in_array('TaintedInput', $statements_analyzer->getSuppressedIssues())
- ) {
- $arg_location = new CodeLocation($statements_analyzer->getSource(), $stmt->expr);
-
- $include_param_sink = TaintSink::getForMethodArgument(
- 'include',
- 'include',
- 0,
- $arg_location,
- $arg_location
- );
-
- $include_param_sink->taints = [TaintKind::INPUT_INCLUDE];
-
- $statements_analyzer->data_flow_graph->addSink($include_param_sink);
-
- $codebase = $statements_analyzer->getCodebase();
- $event = new AddRemoveTaintsEvent($stmt, $context, $statements_analyzer, $codebase);
-
- $added_taints = $codebase->config->eventDispatcher->dispatchAddTaints($event);
- $removed_taints = $codebase->config->eventDispatcher->dispatchRemoveTaints($event);
-
- foreach ($stmt_expr_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $include_param_sink,
- 'arg',
- $added_taints,
- $removed_taints
- );
- }
- }
-
- if ($path_to_file) {
- $path_to_file = self::normalizeFilePath($path_to_file);
-
- // if the file is already included, we can't check much more
- if (in_array(realpath($path_to_file), get_included_files(), true)) {
- return true;
- }
-
- $current_file_analyzer = $statements_analyzer->getFileAnalyzer();
-
- if ($current_file_analyzer->project_analyzer->fileExists($path_to_file)) {
- if ($statements_analyzer->hasParentFilePath($path_to_file)
- || !$codebase->file_storage_provider->has($path_to_file)
- || ($statements_analyzer->hasAlreadyRequiredFilePath($path_to_file)
- && !$codebase->file_storage_provider->get($path_to_file)->has_extra_statements)
- ) {
- return true;
- }
- if ($config->mustBeIgnored($path_to_file)) {
- return true;
- }
-
- $current_file_analyzer->addRequiredFilePath($path_to_file);
-
- $file_name = $config->shortenFileName($path_to_file);
-
- $nesting = $statements_analyzer->getRequireNesting() + 1;
- $current_file_analyzer->project_analyzer->progress->debug(
- str_repeat(' ', $nesting) . 'checking ' . $file_name . PHP_EOL
- );
-
- $include_file_analyzer = new FileAnalyzer(
- $current_file_analyzer->project_analyzer,
- $path_to_file,
- $file_name
- );
-
- $include_file_analyzer->setRootFilePath(
- $current_file_analyzer->getRootFilePath(),
- $current_file_analyzer->getRootFileName()
- );
-
- $include_file_analyzer->addParentFilePath($current_file_analyzer->getFilePath());
- $include_file_analyzer->addRequiredFilePath($current_file_analyzer->getFilePath());
-
- foreach ($current_file_analyzer->getRequiredFilePaths() as $required_file_path) {
- $include_file_analyzer->addRequiredFilePath($required_file_path);
- }
-
- foreach ($current_file_analyzer->getParentFilePaths() as $parent_file_path) {
- $include_file_analyzer->addParentFilePath($parent_file_path);
- }
-
- try {
- $include_file_analyzer->analyze(
- $context,
- $global_context
- );
- } catch (UnpreparedAnalysisException $e) {
- if ($config->skip_checks_on_unresolvable_includes) {
- $context->check_classes = false;
- $context->check_variables = false;
- $context->check_functions = false;
- }
- }
-
- $included_return_type = $include_file_analyzer->getReturnType();
-
- if ($included_return_type) {
- $statements_analyzer->node_data->setType($stmt, $included_return_type);
- }
-
- $context->has_returned = false;
-
- foreach ($include_file_analyzer->getRequiredFilePaths() as $required_file_path) {
- $current_file_analyzer->addRequiredFilePath($required_file_path);
- }
-
- $include_file_analyzer->clearSourceBeforeDestruction();
-
- return true;
- }
-
- $source = $statements_analyzer->getSource();
-
- IssueBuffer::maybeAdd(
- new MissingFile(
- 'Cannot find file ' . $path_to_file . ' to include',
- new CodeLocation($source, $stmt)
- ),
- $source->getSuppressedIssues()
- );
- } else {
- $var_id = ExpressionIdentifier::getArrayVarId($stmt->expr, null);
-
- if (!$var_id || !isset($context->phantom_files[$var_id])) {
- $source = $statements_analyzer->getSource();
-
- IssueBuffer::maybeAdd(
- new UnresolvableInclude(
- 'Cannot resolve the given expression to a file path',
- new CodeLocation($source, $stmt)
- ),
- $source->getSuppressedIssues()
- );
- }
- }
-
- if ($config->skip_checks_on_unresolvable_includes) {
- $context->check_classes = false;
- $context->check_variables = false;
- $context->check_functions = false;
- }
-
- return true;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public static function getPathTo(
- PhpParser\Node\Expr $stmt,
- ?NodeDataProvider $type_provider,
- ?StatementsAnalyzer $statements_analyzer,
- string $file_name,
- Config $config
- ): ?string {
- if (DIRECTORY_SEPARATOR === '/') {
- $is_path_relative = $file_name[0] !== DIRECTORY_SEPARATOR;
- } else {
- $is_path_relative = !preg_match('~^[A-Z]:\\\\~i', $file_name);
- }
-
- if ($is_path_relative) {
- $file_name = $config->base_dir . DIRECTORY_SEPARATOR . $file_name;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\String_) {
- if (DIRECTORY_SEPARATOR !== '/') {
- return str_replace('/', DIRECTORY_SEPARATOR, $stmt->value);
- }
- return $stmt->value;
- }
-
- $stmt_type = $type_provider ? $type_provider->getType($stmt) : null;
-
- if ($stmt_type && $stmt_type->isSingleStringLiteral()) {
- if (DIRECTORY_SEPARATOR !== '/') {
- return str_replace(
- '/',
- DIRECTORY_SEPARATOR,
- $stmt_type->getSingleStringLiteral()->value
- );
- }
-
- return $stmt_type->getSingleStringLiteral()->value;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- if ($stmt->var instanceof PhpParser\Node\Expr\Variable
- && $stmt->var->name === 'GLOBALS'
- && $stmt->dim instanceof PhpParser\Node\Scalar\String_
- ) {
- if (isset($GLOBALS[$stmt->dim->value]) && is_string($GLOBALS[$stmt->dim->value])) {
- /** @var string */
- return $GLOBALS[$stmt->dim->value];
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
- $left_string = self::getPathTo($stmt->left, $type_provider, $statements_analyzer, $file_name, $config);
- $right_string = self::getPathTo($stmt->right, $type_provider, $statements_analyzer, $file_name, $config);
-
- if ($left_string && $right_string) {
- return $left_string . $right_string;
- }
- } elseif ($stmt instanceof PhpParser\Node\Expr\FuncCall &&
- $stmt->name instanceof PhpParser\Node\Name &&
- $stmt->name->parts === ['dirname']
- ) {
- if ($stmt->getArgs()) {
- $dir_level = 1;
-
- if (isset($stmt->getArgs()[1])) {
- if ($stmt->getArgs()[1]->value instanceof PhpParser\Node\Scalar\LNumber) {
- $dir_level = $stmt->getArgs()[1]->value->value;
- } else {
- return null;
- }
- }
-
- $evaled_path = self::getPathTo(
- $stmt->getArgs()[0]->value,
- $type_provider,
- $statements_analyzer,
- $file_name,
- $config
- );
-
- if (!$evaled_path) {
- return null;
- }
-
- return dirname($evaled_path, $dir_level);
- }
- } elseif ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
- $const_name = implode('', $stmt->name->parts);
-
- if (defined($const_name)) {
- $constant_value = constant($const_name);
-
- if (is_string($constant_value)) {
- return $constant_value;
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir) {
- return dirname($file_name);
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\File) {
- return $file_name;
- }
-
- return null;
- }
-
- public static function resolveIncludePath(string $file_name, string $current_directory): ?string
- {
- if (!$current_directory) {
- return $file_name;
- }
-
- $paths = PATH_SEPARATOR === ':'
- ? preg_split('#(?<!phar):#', get_include_path())
- : explode(PATH_SEPARATOR, get_include_path());
-
- foreach ($paths as $prefix) {
- $ds = substr($prefix, -1) === DIRECTORY_SEPARATOR ? '' : DIRECTORY_SEPARATOR;
-
- if ($prefix === '.') {
- $prefix = $current_directory;
- }
-
- $file = $prefix . $ds . $file_name;
-
- if (file_exists($file)) {
- return $file;
- }
- }
-
- return null;
- }
-
- /**
- * @psalm-pure
- */
- public static function normalizeFilePath(string $path_to_file): string
- {
- // replace all \ with / for normalization
- $path_to_file = str_replace('\\', '/', $path_to_file);
- $path_to_file = str_replace('/./', '/', $path_to_file);
-
- // first remove unnecessary / duplicates
- $path_to_file = preg_replace('/\/[\/]+/', '/', $path_to_file);
-
- $reduce_pattern = '/\/[^\/]+\/\.\.\//';
-
- while (preg_match($reduce_pattern, $path_to_file)) {
- $path_to_file = preg_replace($reduce_pattern, '/', $path_to_file, 1);
- }
-
- if (DIRECTORY_SEPARATOR !== '/') {
- $path_to_file = str_replace('/', DIRECTORY_SEPARATOR, $path_to_file);
- }
-
- return $path_to_file;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php
deleted file mode 100644
index e87bfe2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/InstanceofAnalyzer.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-
-use function implode;
-use function in_array;
-use function strtolower;
-
-class InstanceofAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Instanceof_ $stmt,
- Context $context
- ): bool {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- $context->inside_general_use = $was_inside_general_use;
-
- return false;
- }
-
- $context->inside_general_use = $was_inside_general_use;
-
- if ($stmt->class instanceof PhpParser\Node\Expr) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->class, $context) === false) {
- return false;
- }
- } elseif (!in_array(strtolower($stmt->class->parts[0]), ['self', 'static', 'parent'], true)) {
- if ($context->check_classes) {
- $codebase = $statements_analyzer->getCodebase();
-
- $fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $statements_analyzer->getAliases()
- );
-
- if ($codebase->store_node_types
- && $fq_class_name
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- $codebase->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $stmt->class,
- $codebase->classlikes->classOrInterfaceOrEnumExists($fq_class_name)
- ? $fq_class_name
- : '*'
- . ($stmt->class instanceof PhpParser\Node\Name\FullyQualified
- ? '\\'
- : $statements_analyzer->getNamespace() . '-')
- . implode('\\', $stmt->class->parts)
- );
- }
-
- if (!isset($context->phantom_classes[strtolower($fq_class_name)])) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $fq_class_name,
- new CodeLocation($statements_analyzer->getSource(), $stmt->class),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues()
- ) === false) {
- return false;
- }
- }
-
- if ($codebase->alter_code) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt->class,
- $fq_class_name,
- $context->calling_method_id
- );
- }
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php
deleted file mode 100644
index 40f3381..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IssetAnalyzer.php
+++ /dev/null
@@ -1,49 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-
-class IssetAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Isset_ $stmt,
- Context $context
- ): void {
- foreach ($stmt->vars as $isset_var) {
- if ($isset_var instanceof PhpParser\Node\Expr\PropertyFetch
- && $isset_var->var instanceof PhpParser\Node\Expr\Variable
- && $isset_var->var->name === 'this'
- && $isset_var->name instanceof PhpParser\Node\Identifier
- ) {
- $var_id = '$this->' . $isset_var->name->name;
-
- if (!isset($context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::getMixed();
- $context->vars_possibly_in_scope[$var_id] = true;
- }
- }
-
- self::analyzeIssetVar($statements_analyzer, $isset_var, $context);
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
- }
-
- public static function analyzeIssetVar(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Context $context
- ): void {
- $context->inside_isset = true;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt, $context);
-
- $context->inside_isset = false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php
deleted file mode 100644
index 481a5af..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MagicConstAnalyzer.php
+++ /dev/null
@@ -1,107 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionAnalyzer;
-use Psalm\Internal\Analyzer\MethodAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Issue\UndefinedConstant;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Union;
-
-use function dirname;
-
-class MagicConstAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Scalar\MagicConst $stmt,
- Context $context
- ): void {
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Line) {
- $statements_analyzer->node_data->setType($stmt, Type::getInt());
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Class_) {
- $codebase = $statements_analyzer->getCodebase();
-
- if (!$context->self) {
- IssueBuffer::maybeAdd(
- new UndefinedConstant(
- 'Cannot get __class__ outside a class',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $statements_analyzer->node_data->setType($stmt, Type::getClassString());
- } else {
- if ($codebase->alter_code) {
- $codebase->classlikes->handleClassLikeReferenceInMigration(
- $codebase,
- $statements_analyzer,
- $stmt,
- $context->self,
- $context->calling_method_id
- );
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getLiteralClassString($context->self));
- }
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Namespace_) {
- $namespace = $statements_analyzer->getNamespace();
- if ($namespace === null
- && IssueBuffer::accepts(
- new UndefinedConstant(
- 'Cannot get __namespace__ outside a namespace',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )
- ) {
- // fall through
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getString($namespace));
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Method
- || $stmt instanceof PhpParser\Node\Scalar\MagicConst\Function_
- ) {
- $source = $statements_analyzer->getSource();
- if ($source instanceof MethodAnalyzer) {
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Function_) {
- $statements_analyzer->node_data->setType($stmt, Type::getString($source->getMethodName()));
- } else {
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::getString($source->getCorrectlyCasedMethodId())
- );
- }
- } elseif ($source instanceof FunctionAnalyzer) {
- $statements_analyzer->node_data->setType($stmt, Type::getString($source->getCorrectlyCasedMethodId()));
- } else {
- $statements_analyzer->node_data->setType($stmt, new Union([new TCallableString]));
- }
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir) {
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::getString(dirname($statements_analyzer->getSource()->getFilePath()))
- );
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\File) {
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::getString($statements_analyzer->getSource()->getFilePath())
- );
- } elseif ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Trait_) {
- if ($statements_analyzer->getSource() instanceof TraitAnalyzer) {
- $statements_analyzer->node_data->setType($stmt, new Union([new TNonEmptyString()]));
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getString());
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php
deleted file mode 100644
index 139fde0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/MatchAnalyzer.php
+++ /dev/null
@@ -1,351 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Issue\UnhandledMatchCondition;
-use Psalm\IssueBuffer;
-use Psalm\Node\Expr\BinaryOp\VirtualIdentical;
-use Psalm\Node\Expr\VirtualArray;
-use Psalm\Node\Expr\VirtualArrayItem;
-use Psalm\Node\Expr\VirtualConstFetch;
-use Psalm\Node\Expr\VirtualFuncCall;
-use Psalm\Node\Expr\VirtualNew;
-use Psalm\Node\Expr\VirtualTernary;
-use Psalm\Node\Expr\VirtualThrow;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\VirtualArg;
-use Psalm\Type;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Reconciler;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_map;
-use function array_merge;
-use function array_reverse;
-use function array_shift;
-use function count;
-use function in_array;
-use function spl_object_id;
-use function substr;
-
-class MatchAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Match_ $stmt,
- Context $context
- ): bool {
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- $was_inside_conditional = $context->inside_conditional;
-
- $context->inside_conditional = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->cond, $context) === false) {
- $context->inside_conditional = $was_inside_conditional;
-
- return false;
- }
-
- $context->inside_conditional = $was_inside_conditional;
-
- $switch_var_id = ExpressionIdentifier::getArrayVarId(
- $stmt->cond,
- null,
- $statements_analyzer
- );
-
- $match_condition = $stmt->cond;
-
- if (!$switch_var_id) {
- if ($stmt->cond instanceof PhpParser\Node\Expr\FuncCall
- && $stmt->cond->name instanceof PhpParser\Node\Name
- && ($stmt->cond->name->parts === ['get_class']
- || $stmt->cond->name->parts === ['gettype']
- || $stmt->cond->name->parts === ['get_debug_type'])
- && $stmt->cond->getArgs()
- ) {
- $first_arg = $stmt->cond->getArgs()[0];
-
- if (!$first_arg->value instanceof PhpParser\Node\Expr\Variable) {
- $switch_var_id = '$__tmp_switch__' . (int) $first_arg->value->getAttribute('startFilePos');
-
- $condition_type = $statements_analyzer->node_data->getType($first_arg->value) ?? Type::getMixed();
-
- $context->vars_in_scope[$switch_var_id] = $condition_type;
-
- $match_condition = new VirtualFuncCall(
- $stmt->cond->name,
- [
- new VirtualArg(
- new VirtualVariable(
- substr($switch_var_id, 1),
- $first_arg->value->getAttributes()
- ),
- false,
- false,
- $first_arg->getAttributes()
- )
- ],
- $stmt->cond->getAttributes()
- );
- }
- } elseif ($stmt->cond instanceof PhpParser\Node\Expr\FuncCall
- || $stmt->cond instanceof PhpParser\Node\Expr\MethodCall
- || $stmt->cond instanceof PhpParser\Node\Expr\StaticCall
- ) {
- $switch_var_id = '$__tmp_switch__' . (int) $stmt->cond->getAttribute('startFilePos');
-
- $condition_type = $statements_analyzer->node_data->getType($stmt->cond) ?? Type::getMixed();
-
- $context->vars_in_scope[$switch_var_id] = $condition_type;
-
- $match_condition = new VirtualVariable(
- substr($switch_var_id, 1),
- $stmt->cond->getAttributes()
- );
- }
- }
-
- $arms = $stmt->arms;
-
- foreach ($arms as $i => $arm) {
- // move default to the end
- if ($arm->conds === null) {
- unset($arms[$i]);
- $arms[] = $arm;
- }
- }
-
- $arms = array_reverse($arms);
-
- $last_arm = array_shift($arms);
-
- if (!$last_arm) {
- IssueBuffer::maybeAdd(
- new UnhandledMatchCondition(
- 'This match expression does not match anything',
- new CodeLocation($statements_analyzer->getSource(), $match_condition)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return false;
- }
-
- $old_node_data = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- if (!$last_arm->conds) {
- $ternary = $last_arm->body;
- } else {
- $ternary = new VirtualTernary(
- self::convertCondsToConditional($last_arm->conds, $match_condition, $last_arm->getAttributes()),
- $last_arm->body,
- new VirtualThrow(
- new VirtualNew(
- new VirtualFullyQualified(
- 'UnhandledMatchError',
- $stmt->getAttributes()
- ),
- [],
- $stmt->getAttributes()
- )
- ),
- $stmt->getAttributes()
- );
- }
-
- foreach ($arms as $arm) {
- if (!$arm->conds) {
- continue;
- }
-
- $ternary = new VirtualTernary(
- self::convertCondsToConditional($arm->conds, $match_condition, $arm->getAttributes()),
- $arm->body,
- $ternary,
- $arm->getAttributes()
- );
- }
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantCondition']);
- }
-
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $ternary, $context) === false) {
- return false;
- }
-
- if (!in_array('RedundantCondition', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantCondition']);
- }
-
- if (!in_array('RedundantConditionGivenDocblockType', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['RedundantConditionGivenDocblockType']);
- }
-
- if ($switch_var_id && $last_arm->conds) {
- $codebase = $statements_analyzer->getCodebase();
-
- $all_conds = $last_arm->conds;
-
- foreach ($arms as $arm) {
- if (!$arm->conds) {
- throw new UnexpectedValueException('bad');
- }
-
- $all_conds = array_merge($arm->conds, $all_conds);
- }
-
- $all_match_condition = self::convertCondsToConditional(
- $all_conds,
- $match_condition,
- $match_condition->getAttributes()
- );
-
- ExpressionAnalyzer::analyze($statements_analyzer, $all_match_condition, $context);
-
- $clauses = FormulaGenerator::getFormula(
- spl_object_id($all_match_condition),
- spl_object_id($all_match_condition),
- $all_match_condition,
- $context->self,
- $statements_analyzer,
- $codebase,
- false,
- false
- );
-
- $reconcilable_types = Algebra::getTruthsFromFormula(
- Algebra::negateFormula($clauses)
- );
-
- // if the if has an || in the conditional, we cannot easily reason about it
- if ($reconcilable_types) {
- $changed_var_ids = [];
-
- $vars_in_scope_reconciled = Reconciler::reconcileKeyedTypes(
- $reconcilable_types,
- [],
- $context->vars_in_scope,
- $changed_var_ids,
- [],
- $statements_analyzer,
- [],
- $context->inside_loop,
- null
- );
-
- if (isset($vars_in_scope_reconciled[$switch_var_id])) {
- $array_literal_types = array_filter(
- $vars_in_scope_reconciled[$switch_var_id]->getAtomicTypes(),
- function ($type) {
- return $type instanceof TLiteralInt
- || $type instanceof TLiteralString
- || $type instanceof TLiteralFloat
- || $type instanceof TEnumCase;
- }
- );
-
- if ($array_literal_types) {
- IssueBuffer::maybeAdd(
- new UnhandledMatchCondition(
- 'This match expression is not exhaustive - consider values '
- . $vars_in_scope_reconciled[$switch_var_id]->getId(),
- new CodeLocation($statements_analyzer->getSource(), $match_condition)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- $stmt_expr_type = $statements_analyzer->node_data->getType($ternary);
-
- $old_node_data->setType($stmt, $stmt_expr_type ?? Type::getMixed());
-
- $statements_analyzer->node_data = $old_node_data;
-
- $context->inside_call = $was_inside_call;
-
- return true;
- }
-
- /**
- * @param non-empty-list<PhpParser\Node\Expr> $conds
- */
- private static function convertCondsToConditional(
- array $conds,
- PhpParser\Node\Expr $match_condition,
- array $attributes
- ): PhpParser\Node\Expr {
- if (count($conds) === 1) {
- return new VirtualIdentical(
- $match_condition,
- $conds[0],
- $attributes
- );
- }
-
- $array_items = array_map(
- function ($cond): PhpParser\Node\Expr\ArrayItem {
- return new VirtualArrayItem($cond, null, false, $cond->getAttributes());
- },
- $conds
- );
-
- return new VirtualFuncCall(
- new VirtualFullyQualified(['in_array']),
- [
- new VirtualArg(
- $match_condition,
- false,
- false,
- $attributes
- ),
- new VirtualArg(
- new VirtualArray(
- $array_items,
- $attributes
- ),
- false,
- false,
- $attributes
- ),
- new VirtualArg(
- new VirtualConstFetch(
- new VirtualFullyQualified(['true']),
- $attributes
- ),
- false,
- false,
- $attributes
- ),
- ],
- $attributes
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php
deleted file mode 100644
index c1d4992..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/NullsafeAnalyzer.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Node\Expr\BinaryOp\VirtualIdentical;
-use Psalm\Node\Expr\VirtualConstFetch;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\Expr\VirtualPropertyFetch;
-use Psalm\Node\Expr\VirtualTernary;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\VirtualName;
-use Psalm\Type;
-
-/**
- * @internal
- */
-class NullsafeAnalyzer
-{
- /**
- * @param PhpParser\Node\Expr\NullsafePropertyFetch|PhpParser\Node\Expr\NullsafeMethodCall $stmt
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Context $context
- ): bool {
- if (!$stmt->var instanceof PhpParser\Node\Expr\Variable) {
- $was_inside_general_use = $context->inside_general_use;
-
- $context->inside_general_use = true;
- ExpressionAnalyzer::analyze($statements_analyzer, $stmt->var, $context);
- $context->inside_general_use = $was_inside_general_use;
-
- $tmp_name = '__tmp_nullsafe__' . (int) $stmt->var->getAttribute('startFilePos');
-
- $condition_type = $statements_analyzer->node_data->getType($stmt->var);
-
- if ($condition_type) {
- $context->vars_in_scope['$' . $tmp_name] = $condition_type;
-
- $tmp_var = new VirtualVariable($tmp_name, $stmt->var->getAttributes());
- } else {
- $tmp_var = $stmt->var;
- }
- } else {
- $tmp_var = $stmt->var;
- }
-
- $old_node_data = $statements_analyzer->node_data;
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $null_value1 = new VirtualConstFetch(
- new VirtualName('null'),
- $stmt->var->getAttributes()
- );
-
- $null_comparison = new VirtualIdentical(
- $tmp_var,
- $null_value1,
- $stmt->var->getAttributes()
- );
-
- $null_value2 = new VirtualConstFetch(
- new VirtualName('null'),
- $stmt->var->getAttributes()
- );
-
- if ($stmt instanceof PhpParser\Node\Expr\NullsafePropertyFetch) {
- $ternary = new VirtualTernary(
- $null_comparison,
- $null_value2,
- new VirtualPropertyFetch($tmp_var, $stmt->name, $stmt->getAttributes()),
- $stmt->getAttributes()
- );
- } else {
- $ternary = new VirtualTernary(
- $null_comparison,
- $null_value2,
- new VirtualMethodCall($tmp_var, $stmt->name, $stmt->args, $stmt->getAttributes()),
- $stmt->getAttributes()
- );
- }
-
- ExpressionAnalyzer::analyze($statements_analyzer, $ternary, $context);
-
- $ternary_type = $statements_analyzer->node_data->getType($ternary);
-
- $statements_analyzer->node_data = $old_node_data;
-
- $statements_analyzer->node_data->setType($stmt, $ternary_type ?? Type::getMixed());
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php
deleted file mode 100644
index b65193a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/PrintAnalyzer.php
+++ /dev/null
@@ -1,109 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\Issue\ImpureFunctionCall;
-use Psalm\IssueBuffer;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\TaintKind;
-
-class PrintAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Print_ $stmt,
- Context $context
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- $call_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- $print_param_sink = TaintSink::getForMethodArgument(
- 'print',
- 'print',
- 0,
- null,
- $call_location
- );
-
- $print_param_sink->taints = [
- TaintKind::INPUT_HTML,
- TaintKind::INPUT_HAS_QUOTES,
- TaintKind::USER_SECRET,
- TaintKind::SYSTEM_SECRET
- ];
-
- $statements_analyzer->data_flow_graph->addSink($print_param_sink);
- }
-
- if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
- if (ArgumentAnalyzer::verifyType(
- $statements_analyzer,
- $stmt_expr_type,
- Type::getString(),
- null,
- 'print',
- null,
- 0,
- new CodeLocation($statements_analyzer->getSource(), $stmt->expr),
- $stmt->expr,
- $context,
- new FunctionLikeParameter('var', false),
- false,
- null,
- true,
- true,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ) === false) {
- return false;
- }
- }
-
- if (isset($codebase->config->forbidden_functions['print'])) {
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'You have forbidden the use of print',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$context->collect_initializations && !$context->collect_mutations) {
- if ($context->mutation_free || $context->external_mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureFunctionCall(
- 'Cannot call print from a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } elseif ($statements_analyzer->getSource() instanceof FunctionLikeAnalyzer
- && $statements_analyzer->getSource()->track_mutations
- ) {
- $statements_analyzer->getSource()->inferred_has_mutation = true;
- $statements_analyzer->getSource()->inferred_impure = true;
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, Type::getInt(false, 1));
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php
deleted file mode 100644
index 081d0cf..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php
+++ /dev/null
@@ -1,760 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\Codebase;
-use Psalm\Exception\CircularReferenceException;
-use Psalm\FileSource;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOp\ArithmeticOpAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\StatementsSource;
-use Psalm\Storage\ClassConstantStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Union;
-use ReflectionProperty;
-
-use function array_merge;
-use function array_values;
-use function count;
-use function is_string;
-use function preg_match;
-use function strtolower;
-
-use const PHP_INT_MAX;
-
-/**
- * This class takes a statement and return its type by analyzing each part of the statement if necessary
- */
-class SimpleTypeInferer
-{
- /**
- * @param ?array<string, ClassConstantStorage> $existing_class_constants
- */
- public static function infer(
- Codebase $codebase,
- NodeDataProvider $nodes,
- PhpParser\Node\Expr $stmt,
- Aliases $aliases,
- FileSource $file_source = null,
- ?array $existing_class_constants = null,
- ?string $fq_classlike_name = null
- ): ?Union {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
- $left = self::infer(
- $codebase,
- $nodes,
- $stmt->left,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
- $right = self::infer(
- $codebase,
- $nodes,
- $stmt->right,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if ($left
- && $right
- ) {
- if ($left->isSingleStringLiteral()
- && $right->isSingleStringLiteral()
- ) {
- $result = $left->getSingleStringLiteral()->value . $right->getSingleStringLiteral()->value;
-
- return Type::getString($result);
- }
-
- if ($left->isSingle() && $left->getSingleAtomic() instanceof TNonEmptyString) {
- return new Union([new TNonEmptyString()]);
- }
-
- if ($right->isSingle() && $right->getSingleAtomic() instanceof TNonEmptyString) {
- return new Union([new TNonEmptyString()]);
- }
- }
-
- return Type::getString();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalAnd
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\LogicalOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Equal
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotEqual
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Identical
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\NotIdentical
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Greater
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Smaller
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
- ) {
- return Type::getBool();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Coalesce) {
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Spaceship) {
- return new Union(
- [
- new TLiteralInt(-1),
- new TLiteralInt(0),
- new TLiteralInt(1)
- ]
- );
- }
-
- $stmt_left_type = self::infer(
- $codebase,
- $nodes,
- $stmt->left,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- $stmt_right_type = self::infer(
- $codebase,
- $nodes,
- $stmt->right,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if (!$stmt_left_type || !$stmt_right_type) {
- return null;
- }
-
- $nodes->setType(
- $stmt->left,
- $stmt_left_type
- );
-
- $nodes->setType(
- $stmt->right,
- $stmt_right_type
- );
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Plus
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Minus
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Mod
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Mul
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\Pow
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\ShiftRight
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\ShiftLeft
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseOr
- || $stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd
- ) {
- ArithmeticOpAnalyzer::analyze(
- $file_source instanceof StatementsSource ? $file_source : null,
- $nodes,
- $stmt->left,
- $stmt->right,
- $stmt,
- $result_type
- );
-
- if ($result_type) {
- return $result_type;
- }
-
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Div
- && ($stmt_left_type->hasInt() || $stmt_left_type->hasFloat())
- && ($stmt_right_type->hasInt() || $stmt_right_type->hasFloat())
- ) {
- return Type::combineUnionTypes(Type::getFloat(), Type::getInt());
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
- $name = strtolower($stmt->name->parts[0]);
- if ($name === 'false') {
- return Type::getFalse();
- }
-
- if ($name === 'true') {
- return Type::getTrue();
- }
-
- if ($name === 'null') {
- return Type::getNull();
- }
-
- if ($stmt->name->parts[0] === '__NAMESPACE__') {
- return Type::getString($aliases->namespace);
- }
-
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Dir
- || $stmt instanceof PhpParser\Node\Scalar\MagicConst\File
- ) {
- return new Union([new TNonEmptyString()]);
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Line) {
- return Type::getInt();
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Class_
- || $stmt instanceof PhpParser\Node\Scalar\MagicConst\Method
- || $stmt instanceof PhpParser\Node\Scalar\MagicConst\Trait_
- || $stmt instanceof PhpParser\Node\Scalar\MagicConst\Function_
- ) {
- return Type::getString();
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Namespace_) {
- return Type::getString($aliases->namespace);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
- if ($stmt->class instanceof PhpParser\Node\Name
- && $stmt->name instanceof PhpParser\Node\Identifier
- && $fq_classlike_name
- && $stmt->class->parts !== ['static']
- && $stmt->class->parts !== ['parent']
- ) {
- if (isset($existing_class_constants[$stmt->name->name])
- && $existing_class_constants[$stmt->name->name]->type
- ) {
- if ($stmt->class->parts === ['self']) {
- return clone $existing_class_constants[$stmt->name->name]->type;
- }
- }
-
- if ($stmt->class->parts === ['self']) {
- $const_fq_class_name = $fq_classlike_name;
- } else {
- $const_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $aliases
- );
- }
-
- if (strtolower($const_fq_class_name) === strtolower($fq_classlike_name)
- && isset($existing_class_constants[$stmt->name->name])
- && $existing_class_constants[$stmt->name->name]->type
- ) {
- return clone $existing_class_constants[$stmt->name->name]->type;
- }
-
- if (strtolower($stmt->name->name) === 'class') {
- return Type::getLiteralClassString($const_fq_class_name, true);
- }
-
- if ($existing_class_constants === null
- && $file_source instanceof StatementsAnalyzer
- ) {
- try {
- $foreign_class_constant = $codebase->classlikes->getClassConstantType(
- $const_fq_class_name,
- $stmt->name->name,
- ReflectionProperty::IS_PRIVATE,
- $file_source
- );
-
- if ($foreign_class_constant) {
- return clone $foreign_class_constant;
- }
-
- return null;
- } catch (InvalidArgumentException | CircularReferenceException $e) {
- return null;
- }
- }
- }
-
- if ($stmt->name instanceof PhpParser\Node\Identifier && strtolower($stmt->name->name) === 'class') {
- return Type::getClassString();
- }
-
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\String_) {
- return Type::getString($stmt->value);
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\LNumber) {
- return Type::getInt(false, $stmt->value);
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\DNumber) {
- return Type::getFloat($stmt->value);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Array_) {
- return self::inferArrayType(
- $codebase,
- $nodes,
- $stmt,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Int_) {
- return Type::getInt();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Double) {
- return Type::getFloat();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Bool_) {
- return Type::getBool();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\String_) {
- return Type::getString();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Object_) {
- return Type::getObject();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast\Array_) {
- return Type::getArray();
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\UnaryMinus || $stmt instanceof PhpParser\Node\Expr\UnaryPlus) {
- $type_to_invert = self::infer(
- $codebase,
- $nodes,
- $stmt->expr,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if (!$type_to_invert) {
- return null;
- }
-
- foreach ($type_to_invert->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TLiteralInt
- && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
- ) {
- $type_part->value = -$type_part->value;
- } elseif ($type_part instanceof TLiteralFloat
- && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
- ) {
- $type_part->value = -$type_part->value;
- }
- }
-
- return $type_to_invert;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- if ($stmt->var instanceof PhpParser\Node\Expr\ClassConstFetch
- && $stmt->dim
- ) {
- $array_type = self::infer(
- $codebase,
- $nodes,
- $stmt->var,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- $dim_type = self::infer(
- $codebase,
- $nodes,
- $stmt->dim,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if ($array_type !== null && $dim_type !== null) {
- if ($dim_type->isSingleStringLiteral()) {
- $dim_value = $dim_type->getSingleStringLiteral()->value;
- } elseif ($dim_type->isSingleIntLiteral()) {
- $dim_value = $dim_type->getSingleIntLiteral()->value;
- } else {
- return null;
- }
-
- foreach ($array_type->getAtomicTypes() as $array_atomic_type) {
- if ($array_atomic_type instanceof TKeyedArray) {
- if (isset($array_atomic_type->properties[$dim_value])) {
- return clone $array_atomic_type->properties[$dim_value];
- }
-
- return null;
- }
- }
- }
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\New_) {
- $resolved_class_name = $stmt->class->getAttribute('resolvedName');
-
- if (!is_string($resolved_class_name)) {
- return null;
- }
-
- return new Union([
- new Type\Atomic\TNamedObject($resolved_class_name)
- ]);
- }
-
- return null;
- }
-
- /**
- * @param ?array<string, ClassConstantStorage> $existing_class_constants
- */
- private static function inferArrayType(
- Codebase $codebase,
- NodeDataProvider $nodes,
- PhpParser\Node\Expr\Array_ $stmt,
- Aliases $aliases,
- FileSource $file_source = null,
- ?array $existing_class_constants = null,
- ?string $fq_classlike_name = null
- ): ?Union {
- if (count($stmt->items) === 0) {
- return Type::getEmptyArray();
- }
-
- $array_creation_info = new ArrayCreationInfo();
-
- foreach ($stmt->items as $item) {
- if ($item === null) {
- continue;
- }
-
- if (!self::handleArrayItem(
- $codebase,
- $nodes,
- $array_creation_info,
- $item,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- )) {
- return null;
- }
- }
-
- $item_key_type = null;
- if ($array_creation_info->item_key_atomic_types) {
- $item_key_type = TypeCombiner::combine(
- $array_creation_info->item_key_atomic_types,
- null,
- false,
- true,
- 30
- );
- }
-
- $item_value_type = null;
- if ($array_creation_info->item_value_atomic_types) {
- $item_value_type = TypeCombiner::combine(
- $array_creation_info->item_value_atomic_types,
- null,
- false,
- true,
- 30
- );
- }
-
- // if this array looks like an object-like array, let's return that instead
- if ($item_value_type
- && $item_key_type
- && ($item_key_type->hasString() || $item_key_type->hasInt())
- && $array_creation_info->can_create_objectlike
- && $array_creation_info->property_types
- ) {
- $objectlike = new TKeyedArray(
- $array_creation_info->property_types,
- $array_creation_info->class_strings
- );
- $objectlike->sealed = true;
- $objectlike->is_list = $array_creation_info->all_list;
- return new Union([$objectlike]);
- }
-
- if (!$item_key_type || !$item_value_type) {
- return null;
- }
-
- if ($array_creation_info->all_list) {
- return new Union([
- new TNonEmptyList($item_value_type),
- ]);
- }
-
- return new Union([
- new TNonEmptyArray([
- $item_key_type,
- $item_value_type,
- ]),
- ]);
- }
-
- /**
- * @param ?array<string, ClassConstantStorage> $existing_class_constants
- */
- private static function handleArrayItem(
- Codebase $codebase,
- NodeDataProvider $nodes,
- ArrayCreationInfo $array_creation_info,
- PhpParser\Node\Expr\ArrayItem $item,
- Aliases $aliases,
- FileSource $file_source = null,
- ?array $existing_class_constants = null,
- ?string $fq_classlike_name = null
- ): bool {
- if ($item->unpack) {
- $unpacked_array_type = self::infer(
- $codebase,
- $nodes,
- $item->value,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if (!$unpacked_array_type) {
- return false;
- }
-
- return self::handleUnpackedArray($array_creation_info, $unpacked_array_type);
- }
-
- $single_item_key_type = null;
- $item_is_list_item = false;
- $item_key_value = null;
-
- if ($item->key) {
- $single_item_key_type = self::infer(
- $codebase,
- $nodes,
- $item->key,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if ($single_item_key_type) {
- $key_type = $single_item_key_type;
- if ($key_type->isNull()) {
- $key_type = Type::getString('');
- }
- if ($item->key instanceof PhpParser\Node\Scalar\String_
- && preg_match('/^(0|[1-9][0-9]*)$/', $item->key->value)
- && (
- (int) $item->key->value < PHP_INT_MAX ||
- $item->key->value === (string) PHP_INT_MAX
- )
- ) {
- $key_type = Type::getInt(false, (int) $item->key->value);
- }
-
- $array_creation_info->item_key_atomic_types = array_merge(
- $array_creation_info->item_key_atomic_types,
- array_values($key_type->getAtomicTypes())
- );
-
- if ($key_type->isSingleStringLiteral()) {
- $item_key_literal_type = $key_type->getSingleStringLiteral();
- $item_key_value = $item_key_literal_type->value;
-
- if ($item_key_literal_type instanceof TLiteralClassString) {
- $array_creation_info->class_strings[$item_key_value] = true;
- }
- } elseif ($key_type->isSingleIntLiteral()) {
- $item_key_value = $key_type->getSingleIntLiteral()->value;
-
- if ($item_key_value >= $array_creation_info->int_offset) {
- if ($item_key_value === $array_creation_info->int_offset) {
- $item_is_list_item = true;
- }
- $array_creation_info->int_offset = $item_key_value + 1;
- }
- }
- }
- } else {
- $item_is_list_item = true;
- $item_key_value = $array_creation_info->int_offset++;
- $array_creation_info->item_key_atomic_types[] = new TLiteralInt($item_key_value);
- }
-
- $single_item_value_type = self::infer(
- $codebase,
- $nodes,
- $item->value,
- $aliases,
- $file_source,
- $existing_class_constants,
- $fq_classlike_name
- );
-
- if (!$single_item_value_type) {
- return false;
- }
-
- $config = $codebase->config;
-
- $array_creation_info->all_list = $array_creation_info->all_list && $item_is_list_item;
-
- if ($item->key instanceof PhpParser\Node\Scalar\String_
- || $item->key instanceof PhpParser\Node\Scalar\LNumber
- || !$item->key
- ) {
- if ($item_key_value !== null
- && count($array_creation_info->property_types) <= $config->max_shaped_array_size
- ) {
- $array_creation_info->property_types[$item_key_value] = $single_item_value_type;
- } else {
- $array_creation_info->can_create_objectlike = false;
- }
- } else {
- $dim_type = $single_item_key_type;
-
- if (!$dim_type) {
- return false;
- }
-
- if (count($dim_type->getAtomicTypes()) > 1
- || $dim_type->hasMixed()
- || count($array_creation_info->property_types) > $config->max_shaped_array_size
- ) {
- $array_creation_info->can_create_objectlike = false;
- } else {
- $atomic_type = $dim_type->getSingleAtomic();
-
- if ($atomic_type instanceof TLiteralInt
- || $atomic_type instanceof TLiteralString
- ) {
- if ($atomic_type instanceof TLiteralClassString) {
- $array_creation_info->class_strings[$atomic_type->value] = true;
- }
-
- $array_creation_info->property_types[$atomic_type->value] = $single_item_value_type;
- } else {
- $array_creation_info->can_create_objectlike = false;
- }
- }
- }
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values($single_item_value_type->getAtomicTypes())
- );
-
- return true;
- }
-
- private static function handleUnpackedArray(
- ArrayCreationInfo $array_creation_info,
- Union $unpacked_array_type
- ): bool {
- foreach ($unpacked_array_type->getAtomicTypes() as $unpacked_atomic_type) {
- if ($unpacked_atomic_type instanceof TKeyedArray) {
- foreach ($unpacked_atomic_type->properties as $key => $property_value) {
- if (is_string($key)) {
- $new_offset = $key;
- $array_creation_info->item_key_atomic_types[] = new TLiteralString($new_offset);
- } else {
- $new_offset = $array_creation_info->int_offset++;
- $array_creation_info->item_key_atomic_types[] = new TLiteralInt($new_offset);
- }
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values($property_value->getAtomicTypes())
- );
-
- $array_creation_info->array_keys[$new_offset] = true;
- $array_creation_info->property_types[$new_offset] = $property_value;
- }
- } elseif ($unpacked_atomic_type instanceof TArray) {
- if ($unpacked_atomic_type->type_params[1]->isEmpty()) {
- continue;
- }
- $array_creation_info->can_create_objectlike = false;
-
- if ($unpacked_atomic_type->type_params[0]->hasString()) {
- $array_creation_info->item_key_atomic_types[] = new TString();
- }
-
- if ($unpacked_atomic_type->type_params[0]->hasInt()) {
- $array_creation_info->item_key_atomic_types[] = new TInt();
- }
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values(
- isset($unpacked_atomic_type->type_params[1])
- ? $unpacked_atomic_type->type_params[1]->getAtomicTypes()
- : [new TMixed()]
- )
- );
- } elseif ($unpacked_atomic_type instanceof TList) {
- if ($unpacked_atomic_type->type_param->isEmpty()) {
- continue;
- }
- $array_creation_info->can_create_objectlike = false;
-
- $array_creation_info->item_key_atomic_types[] = new TInt();
-
- $array_creation_info->item_value_atomic_types = array_merge(
- $array_creation_info->item_value_atomic_types,
- array_values($unpacked_atomic_type->type_param->getAtomicTypes())
- );
- }
- }
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php
deleted file mode 100644
index 872c734..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/TernaryAnalyzer.php
+++ /dev/null
@@ -1,306 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Exception\ScopeAnalysisException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\AlgebraAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfConditionalAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Clause;
-use Psalm\Internal\Scope\IfScope;
-use Psalm\Internal\Type\AssertionReconciler;
-use Psalm\Type;
-use Psalm\Type\Reconciler;
-
-use function array_diff;
-use function array_filter;
-use function array_intersect;
-use function array_intersect_key;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_values;
-use function in_array;
-use function preg_match;
-use function preg_quote;
-use function spl_object_id;
-
-/**
- * @internal
- */
-class TernaryAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Ternary $stmt,
- Context $context
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
-
- $if_scope = new IfScope();
-
- try {
- $if_conditional_scope = IfConditionalAnalyzer::analyze(
- $statements_analyzer,
- $stmt->cond,
- $context,
- $codebase,
- $if_scope,
- $context->branch_point ?: (int) $stmt->getAttribute('startFilePos')
- );
-
- $if_context = $if_conditional_scope->if_context;
-
- $cond_referenced_var_ids = $if_conditional_scope->cond_referenced_var_ids;
- $assigned_in_conditional_var_ids = $if_conditional_scope->assigned_in_conditional_var_ids;
- } catch (ScopeAnalysisException $e) {
- return false;
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $cond_id = spl_object_id($stmt->cond);
-
- $if_clauses = FormulaGenerator::getFormula(
- $cond_id,
- $cond_id,
- $stmt->cond,
- $context->self,
- $statements_analyzer,
- $codebase
- );
-
- $mixed_var_ids = [];
-
- foreach ($context->vars_in_scope as $var_id => $type) {
- if ($type->hasMixed()) {
- $mixed_var_ids[] = $var_id;
- }
- }
-
- foreach ($context->vars_possibly_in_scope as $var_id => $_) {
- if (!isset($context->vars_in_scope[$var_id])) {
- $mixed_var_ids[] = $var_id;
- }
- }
-
- $if_clauses = array_map(
- /**
- * @return Clause
- */
- function (Clause $c) use ($mixed_var_ids, $cond_id): Clause {
- $keys = array_keys($c->possibilities);
-
- $mixed_var_ids = array_diff($mixed_var_ids, $keys);
-
- foreach ($keys as $key) {
- foreach ($mixed_var_ids as $mixed_var_id) {
- if (preg_match('/^' . preg_quote($mixed_var_id, '/') . '(\[|-)/', $key)) {
- return new Clause([], $cond_id, $cond_id, true);
- }
- }
- }
-
- return $c;
- },
- $if_clauses
- );
-
- // this will see whether any of the clauses in set A conflict with the clauses in set B
- AlgebraAnalyzer::checkForParadox(
- $context->clauses,
- $if_clauses,
- $statements_analyzer,
- $stmt->cond,
- $assigned_in_conditional_var_ids
- );
-
- $ternary_clauses = array_merge($context->clauses, $if_clauses);
-
- if ($if_context->reconciled_expression_clauses) {
- $reconciled_expression_clauses = $if_context->reconciled_expression_clauses;
-
- $ternary_clauses = array_values(
- array_filter(
- $ternary_clauses,
- function ($c) use ($reconciled_expression_clauses): bool {
- return !in_array($c->hash, $reconciled_expression_clauses);
- }
- )
- );
- }
-
- $ternary_clauses = Algebra::simplifyCNF($ternary_clauses);
-
- $negated_clauses = Algebra::negateFormula($if_clauses);
-
- $negated_if_types = Algebra::getTruthsFromFormula(
- Algebra::simplifyCNF(
- array_merge($context->clauses, $negated_clauses)
- )
- );
-
- $active_if_types = [];
-
- $reconcilable_if_types = Algebra::getTruthsFromFormula(
- $ternary_clauses,
- $cond_id,
- $cond_referenced_var_ids,
- $active_if_types
- );
-
- $changed_var_ids = [];
-
- if ($reconcilable_if_types) {
- $if_vars_in_scope_reconciled = Reconciler::reconcileKeyedTypes(
- $reconcilable_if_types,
- $active_if_types,
- $if_context->vars_in_scope,
- $changed_var_ids,
- $cond_referenced_var_ids,
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $if_context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt->cond)
- );
-
- $if_context->vars_in_scope = $if_vars_in_scope_reconciled;
- }
-
- $t_else_context = clone $context;
-
- if ($stmt->if) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->if, $if_context) === false) {
- return false;
- }
-
- $context->referenced_var_ids = array_merge(
- $context->referenced_var_ids,
- $if_context->referenced_var_ids
- );
- }
-
- $t_else_context->clauses = Algebra::simplifyCNF(
- array_merge(
- $t_else_context->clauses,
- $negated_clauses
- )
- );
-
- if ($negated_if_types) {
- $changed_var_ids = [];
-
- $t_else_vars_in_scope_reconciled = Reconciler::reconcileKeyedTypes(
- $negated_if_types,
- $negated_if_types,
- $t_else_context->vars_in_scope,
- $changed_var_ids,
- $cond_referenced_var_ids,
- $statements_analyzer,
- $statements_analyzer->getTemplateTypeMap() ?: [],
- $t_else_context->inside_loop,
- new CodeLocation($statements_analyzer->getSource(), $stmt->else)
- );
-
- $t_else_context->vars_in_scope = $t_else_vars_in_scope_reconciled;
-
- $t_else_context->clauses = Context::removeReconciledClauses($t_else_context->clauses, $changed_var_ids)[0];
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->else, $t_else_context) === false) {
- return false;
- }
-
- $assign_var_ifs = $if_context->assigned_var_ids;
- $assign_var_else = $t_else_context->assigned_var_ids;
- $assign_all = array_intersect_key($assign_var_ifs, $assign_var_else);
-
- //if the same var was assigned in both branches
- foreach ($assign_all as $var_id => $_) {
- if (isset($if_context->vars_in_scope[$var_id]) && isset($t_else_context->vars_in_scope[$var_id])) {
- $context->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $if_context->vars_in_scope[$var_id],
- $t_else_context->vars_in_scope[$var_id]
- );
- }
- }
-
- $redef_var_ifs = array_keys($if_context->getRedefinedVars($context->vars_in_scope));
- $redef_var_else = array_keys($t_else_context->getRedefinedVars($context->vars_in_scope));
- $redef_all = array_intersect($redef_var_ifs, $redef_var_else);
-
- //these vars were changed in both branches
- foreach ($redef_all as $redef_var_id) {
- $context->vars_in_scope[$redef_var_id] = Type::combineUnionTypes(
- $if_context->vars_in_scope[$redef_var_id],
- $t_else_context->vars_in_scope[$redef_var_id]
- );
- }
-
- //these vars were changed in the if and existed before
- foreach ($redef_var_ifs as $redef_var_ifs_id) {
- if (isset($context->vars_in_scope[$redef_var_ifs_id])) {
- $context->vars_in_scope[$redef_var_ifs_id] = Type::combineUnionTypes(
- $context->vars_in_scope[$redef_var_ifs_id],
- $if_context->vars_in_scope[$redef_var_ifs_id]
- );
- }
- }
-
- //these vars were changed in the else and existed before
- foreach ($redef_var_else as $redef_var_else_id) {
- if (isset($context->vars_in_scope[$redef_var_else_id])) {
- $context->vars_in_scope[$redef_var_else_id] = Type::combineUnionTypes(
- $context->vars_in_scope[$redef_var_else_id],
- $t_else_context->vars_in_scope[$redef_var_else_id]
- );
- }
- }
-
- $context->vars_possibly_in_scope = array_merge(
- $context->vars_possibly_in_scope,
- $if_context->vars_possibly_in_scope,
- $t_else_context->vars_possibly_in_scope
- );
-
- $context->referenced_var_ids = array_merge(
- $context->referenced_var_ids,
- $t_else_context->referenced_var_ids
- );
-
- $lhs_type = null;
-
- if ($stmt->if) {
- if ($stmt_if_type = $statements_analyzer->node_data->getType($stmt->if)) {
- $lhs_type = $stmt_if_type;
- }
- } elseif ($stmt_cond_type = $statements_analyzer->node_data->getType($stmt->cond)) {
- $if_return_type_reconciled = AssertionReconciler::reconcile(
- '!falsy',
- clone $stmt_cond_type,
- '',
- $statements_analyzer,
- $context->inside_loop,
- [],
- new CodeLocation($statements_analyzer->getSource(), $stmt),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $lhs_type = $if_return_type_reconciled;
- }
-
- if ($lhs_type && ($stmt_else_type = $statements_analyzer->node_data->getType($stmt->else))) {
- $statements_analyzer->node_data->setType($stmt, Type::combineUnionTypes($lhs_type, $stmt_else_type));
- } else {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php
deleted file mode 100644
index 1748544..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/UnaryPlusMinusAnalyzer.php
+++ /dev/null
@@ -1,134 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use PhpParser\Node\Expr\UnaryMinus;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Union;
-
-class UnaryPlusMinusAnalyzer
-{
- /**
- * @param PhpParser\Node\Expr\UnaryMinus|PhpParser\Node\Expr\UnaryPlus $stmt
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Context $context
- ): bool {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
-
- if (!($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr))) {
- $statements_analyzer->node_data->setType($stmt, new Union([new TInt, new TFloat]));
- } elseif ($stmt_expr_type->isMixed()) {
- $statements_analyzer->node_data->setType($stmt, Type::getMixed());
- } else {
- $acceptable_types = [];
-
- foreach ($stmt_expr_type->getAtomicTypes() as $type_part) {
- if ($type_part instanceof TInt || $type_part instanceof TFloat) {
- if ($type_part instanceof TLiteralInt
- && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
- ) {
- $type_part->value = -$type_part->value;
- } elseif ($type_part instanceof TLiteralFloat
- && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
- ) {
- $type_part->value = -$type_part->value;
- }
-
- if ($type_part instanceof TIntRange
- && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
- ) {
- //we'll have to inverse min and max bound and negate any literal
- $old_min_bound = $type_part->min_bound;
- $old_max_bound = $type_part->max_bound;
- if ($old_min_bound === null) {
- //min bound is null, max bound will be null
- $type_part->max_bound = null;
- } elseif ($old_min_bound === 0) {
- $type_part->max_bound = 0;
- } else {
- $type_part->max_bound = -$old_min_bound;
- }
-
- if ($old_max_bound === null) {
- //max bound is null, min bound will be null
- $type_part->min_bound = null;
- } elseif ($old_max_bound === 0) {
- $type_part->min_bound = 0;
- } else {
- $type_part->min_bound = -$old_max_bound;
- }
- }
-
- if ($type_part instanceof TPositiveInt
- && $stmt instanceof PhpParser\Node\Expr\UnaryMinus
- ) {
- $type_part = new TIntRange(null, -1);
- }
-
- $acceptable_types[] = $type_part;
- } elseif ($type_part instanceof TString) {
- $acceptable_types[] = new TInt;
- $acceptable_types[] = new TFloat;
- } else {
- $acceptable_types[] = new TInt;
- }
- }
-
- $statements_analyzer->node_data->setType($stmt, new Union($acceptable_types));
- }
-
- self::addDataFlow(
- $statements_analyzer,
- $stmt,
- $stmt->expr,
- $stmt instanceof UnaryMinus ? 'unary-minus' : 'unary-plus'
- );
-
- return true;
- }
-
- private static function addDataFlow(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- PhpParser\Node\Expr $value,
- string $type
- ): void {
- $result_type = $statements_analyzer->node_data->getType($stmt);
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph && $result_type) {
- $var_location = new CodeLocation($statements_analyzer, $stmt);
-
- $stmt_value_type = $statements_analyzer->node_data->getType($value);
-
- $new_parent_node = DataFlowNode::getForAssignment($type, $var_location);
- $statements_analyzer->data_flow_graph->addNode($new_parent_node);
- $result_type->parent_nodes = [
- $new_parent_node->id => $new_parent_node,
- ];
-
- if ($stmt_value_type && $stmt_value_type->parent_nodes) {
- foreach ($stmt_value_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath($parent_node, $new_parent_node, $type);
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php
deleted file mode 100644
index d539c3e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldAnalyzer.php
+++ /dev/null
@@ -1,219 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Context;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\UnnecessaryVarAnnotation;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TNamedObject;
-
-class YieldAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\Yield_ $stmt,
- Context $context
- ): bool {
- $doc_comment = $stmt->getDocComment();
-
- $var_comments = [];
- $var_comment_type = null;
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($doc_comment) {
- try {
- $var_comments = CommentAnalyzer::getTypeFromComment(
- $doc_comment,
- $statements_analyzer,
- $statements_analyzer->getAliases()
- );
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- )
- );
- }
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->type) {
- continue;
- }
-
- $comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self ? new TNamedObject($context->self) : null,
- $statements_analyzer->getParentFQCLN()
- );
-
- $type_location = null;
-
- if ($var_comment->type_start
- && $var_comment->type_end
- && $var_comment->line_number
- ) {
- $type_location = new DocblockTypeLocation(
- $statements_analyzer,
- $var_comment->type_start,
- $var_comment->type_end,
- $var_comment->line_number
- );
- }
-
- if (!$var_comment->var_id) {
- $var_comment_type = $comment_type;
- continue;
- }
-
- if ($codebase->find_unused_variables
- && $type_location
- && isset($context->vars_in_scope[$var_comment->var_id])
- && $context->vars_in_scope[$var_comment->var_id]->getId() === $comment_type->getId()
- ) {
- $project_analyzer = $statements_analyzer->getProjectAnalyzer();
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['UnnecessaryVarAnnotation'])
- ) {
- FileManipulationBuffer::addVarAnnotationToRemove($type_location);
- } elseif (IssueBuffer::accepts(
- new UnnecessaryVarAnnotation(
- 'The @var annotation for ' . $var_comment->var_id . ' is unnecessary',
- $type_location
- ),
- $statements_analyzer->getSuppressedIssues(),
- true
- )) {
- // fall through
- }
- }
-
- if (isset($context->vars_in_scope[$var_comment->var_id])) {
- $comment_type->parent_nodes = $context->vars_in_scope[$var_comment->var_id]->parent_nodes;
- }
-
- $context->vars_in_scope[$var_comment->var_id] = $comment_type;
- }
- }
-
- if ($stmt->key) {
- $context->inside_call = true;
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->key, $context) === false) {
- return false;
- }
- $context->inside_call = false;
- }
-
- if ($stmt->value) {
- $context->inside_call = true;
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->value, $context) === false) {
- return false;
- }
- $context->inside_call = false;
-
- if ($var_comment_type) {
- $expression_type = clone $var_comment_type;
- } elseif ($stmt_var_type = $statements_analyzer->node_data->getType($stmt->value)) {
- $expression_type = clone $stmt_var_type;
- } else {
- $expression_type = Type::getMixed();
- }
- } else {
- $expression_type = Type::getEmpty();
- }
-
- $yield_type = null;
-
- foreach ($expression_type->getAtomicTypes() as $expression_atomic_type) {
- if ($expression_atomic_type instanceof TNamedObject) {
- if (!$codebase->classlikes->classOrInterfaceExists($expression_atomic_type->value)) {
- continue;
- }
-
- $classlike_storage = $codebase->classlike_storage_provider->get($expression_atomic_type->value);
-
- if ($classlike_storage->yield) {
- if ($expression_atomic_type instanceof TGenericObject) {
- $yield_candidate_type = AtomicPropertyFetchAnalyzer::localizePropertyType(
- $codebase,
- clone $classlike_storage->yield,
- $expression_atomic_type,
- $classlike_storage,
- $classlike_storage
- );
-
- $yield_type = Type::combineUnionTypes(
- $yield_type,
- $yield_candidate_type,
- $codebase
- );
- } else {
- $yield_type = Type::getMixed();
- }
- }
- }
- }
-
- if ($yield_type) {
- $expression_type->substitute($expression_type, $yield_type);
- }
-
- $statements_analyzer->node_data->setType($stmt, $expression_type);
-
- $source = $statements_analyzer->getSource();
-
- if ($source instanceof FunctionLikeAnalyzer
- && !($source->getSource() instanceof TraitAnalyzer)
- ) {
- $source->examineParamTypes($statements_analyzer, $context, $codebase, $stmt);
-
- $storage = $source->getFunctionLikeStorage($statements_analyzer);
-
- if ($storage->return_type && !$yield_type) {
- foreach ($storage->return_type->getAtomicTypes() as $atomic_return_type) {
- if ($atomic_return_type instanceof TNamedObject
- && $atomic_return_type->value === 'Generator'
- ) {
- if ($atomic_return_type instanceof TGenericObject) {
- if (!$atomic_return_type->type_params[2]->isVoid()) {
- $statements_analyzer->node_data->setType(
- $stmt,
- clone $atomic_return_type->type_params[2]
- );
- }
- } else {
- $statements_analyzer->node_data->setType(
- $stmt,
- Type::combineUnionTypes(
- Type::getMixed(),
- $expression_type
- )
- );
- }
- }
- }
- }
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php
deleted file mode 100644
index f9c03c4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/YieldFromAnalyzer.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements\Expression;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TKeyedArray;
-
-use function strtolower;
-
-class YieldFromAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr\YieldFrom $stmt,
- Context $context
- ): bool {
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
-
- if ($stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr)) {
- $key_type = null;
- $value_type = null;
- $always_non_empty_array = true;
- if (ForeachAnalyzer::checkIteratorType(
- $statements_analyzer,
- $stmt,
- $stmt->expr,
- $stmt_expr_type,
- $statements_analyzer->getCodebase(),
- $context,
- $key_type,
- $value_type,
- $always_non_empty_array
- ) === false
- ) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
-
- $yield_from_type = null;
-
- foreach ($stmt_expr_type->getAtomicTypes() as $atomic_type) {
- if ($yield_from_type === null) {
- if ($atomic_type instanceof TGenericObject
- && strtolower($atomic_type->value) === 'generator'
- && isset($atomic_type->type_params[3])
- ) {
- $yield_from_type = clone $atomic_type->type_params[3];
- } elseif ($atomic_type instanceof TArray) {
- $yield_from_type = clone $atomic_type->type_params[1];
- } elseif ($atomic_type instanceof TKeyedArray) {
- $yield_from_type = $atomic_type->getGenericValueType();
- }
- } else {
- $yield_from_type = Type::getMixed();
- }
- }
-
- // this should be whatever the generator above returns, but *not* the return type
- $statements_analyzer->node_data->setType($stmt, $yield_from_type ?: Type::getMixed());
- }
-
- $context->inside_call = $was_inside_call;
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php
deleted file mode 100644
index 0ce2af8..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php
+++ /dev/null
@@ -1,514 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClosureAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ArrayAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssertionFinder;
-use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BinaryOpAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BitwiseNotAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\BooleanNotAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ArgumentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\NewAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CastAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CloneAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\EmptyAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\EncapsulatedStringAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\EvalAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExitAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ClassConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\InstancePropertyFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\StaticPropertyFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\IncDecExpressionAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\IncludeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\InstanceofAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\IssetAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\MagicConstAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\MatchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\NullsafeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\PrintAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\TernaryAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\UnaryPlusMinusAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\YieldAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\YieldFromAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Issue\ForbiddenCode;
-use Psalm\Issue\UnrecognizedExpression;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\AfterExpressionAnalysisEvent;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\TaintKind;
-
-use function get_class;
-use function in_array;
-use function strtolower;
-
-/**
- * @internal
- */
-class ExpressionAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Context $context,
- bool $array_assignment = false,
- ?Context $global_context = null,
- bool $from_stmt = false
- ): bool {
- $codebase = $statements_analyzer->getCodebase();
-
- if (self::handleExpression(
- $statements_analyzer,
- $stmt,
- $context,
- $array_assignment,
- $global_context,
- $from_stmt
- ) === false
- ) {
- return false;
- }
-
- if (!$context->inside_conditional
- && ($stmt instanceof PhpParser\Node\Expr\BinaryOp
- || $stmt instanceof PhpParser\Node\Expr\Instanceof_
- || $stmt instanceof PhpParser\Node\Expr\Assign
- || $stmt instanceof PhpParser\Node\Expr\BooleanNot
- || $stmt instanceof PhpParser\Node\Expr\Empty_
- || $stmt instanceof PhpParser\Node\Expr\Isset_
- || $stmt instanceof PhpParser\Node\Expr\FuncCall)
- ) {
- $assertions = $statements_analyzer->node_data->getAssertions($stmt);
-
- if ($assertions === null) {
- $negate = $context->inside_negation;
-
- while ($stmt instanceof PhpParser\Node\Expr\BooleanNot) {
- $stmt = $stmt->expr;
- $negate = !$negate;
- }
-
- AssertionFinder::scrapeAssertions(
- $stmt,
- $context->self,
- $statements_analyzer,
- $codebase,
- $negate,
- true,
- false
- );
- }
- }
-
- $event = new AfterExpressionAnalysisEvent(
- $stmt,
- $context,
- $statements_analyzer,
- $codebase,
- []
- );
-
- if ($codebase->config->eventDispatcher->dispatchAfterExpressionAnalysis($event) === false) {
- return false;
- }
-
- $file_manipulations = $event->getFileReplacements();
-
- if ($file_manipulations) {
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
-
- return true;
- }
-
- private static function handleExpression(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $stmt,
- Context $context,
- bool $array_assignment,
- ?Context $global_context,
- bool $from_stmt
- ): bool {
- if ($stmt instanceof PhpParser\Node\Expr\Variable) {
- return VariableFetchAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- false,
- null,
- $array_assignment
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Assign) {
- $assignment_type = AssignmentAnalyzer::analyze(
- $statements_analyzer,
- $stmt->var,
- $stmt->expr,
- null,
- $context,
- $stmt->getDocComment()
- );
-
- if ($assignment_type === false) {
- return false;
- }
-
- if (!$from_stmt) {
- $statements_analyzer->node_data->setType($stmt, $assignment_type);
- }
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\AssignOp) {
- return AssignmentAnalyzer::analyzeAssignmentOperation($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\MethodCall) {
- return MethodCallAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\StaticCall) {
- return StaticCallAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
- ConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\String_) {
- $statements_analyzer->node_data->setType($stmt, Type::getString($stmt->value));
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\EncapsedStringPart) {
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst) {
- MagicConstAnalyzer::analyze($statements_analyzer, $stmt, $context);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\LNumber) {
- $statements_analyzer->node_data->setType($stmt, Type::getInt(false, $stmt->value));
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\DNumber) {
- $statements_analyzer->node_data->setType($stmt, Type::getFloat($stmt->value));
-
- return true;
- }
-
-
- if ($stmt instanceof PhpParser\Node\Expr\UnaryMinus || $stmt instanceof PhpParser\Node\Expr\UnaryPlus) {
- return UnaryPlusMinusAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Isset_) {
- IssetAnalyzer::analyze($statements_analyzer, $stmt, $context);
- $statements_analyzer->node_data->setType($stmt, Type::getBool());
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
- return ClassConstFetchAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch) {
- return InstancePropertyFetchAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- $array_assignment
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\StaticPropertyFetch) {
- return StaticPropertyFetchAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BitwiseNot) {
- return BitwiseNotAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
- return BinaryOpAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context,
- 0,
- $from_stmt
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\PostInc
- || $stmt instanceof PhpParser\Node\Expr\PostDec
- || $stmt instanceof PhpParser\Node\Expr\PreInc
- || $stmt instanceof PhpParser\Node\Expr\PreDec
- ) {
- return IncDecExpressionAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\New_) {
- return NewAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Array_) {
- return ArrayAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\Encapsed) {
- return EncapsulatedStringAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\FuncCall) {
- return FunctionCallAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Ternary) {
- return TernaryAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BooleanNot) {
- return BooleanNotAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Empty_) {
- EmptyAnalyzer::analyze($statements_analyzer, $stmt, $context);
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Closure
- || $stmt instanceof PhpParser\Node\Expr\ArrowFunction
- ) {
- return ClosureAnalyzer::analyzeExpression($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch) {
- return ArrayFetchAnalyzer::analyze(
- $statements_analyzer,
- $stmt,
- $context
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Cast) {
- return CastAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Clone_) {
- return CloneAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Instanceof_) {
- return InstanceofAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Exit_) {
- return ExitAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Include_) {
- return IncludeAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Eval_) {
- EvalAnalyzer::analyze($statements_analyzer, $stmt, $context);
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\AssignRef) {
- return AssignmentAnalyzer::analyzeAssignmentRef($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ErrorSuppress) {
- $context->error_suppressing = true;
- if (self::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
- $context->error_suppressing = false;
-
- $expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($expr_type) {
- $statements_analyzer->node_data->setType($stmt, $expr_type);
- }
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ShellExec) {
- if ($statements_analyzer->data_flow_graph) {
- $call_location = new CodeLocation($statements_analyzer->getSource(), $stmt);
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- $sink = TaintSink::getForMethodArgument(
- 'shell_exec',
- 'shell_exec',
- 0,
- null,
- $call_location
- );
-
- $sink->taints = [TaintKind::INPUT_SHELL];
-
- $statements_analyzer->data_flow_graph->addSink($sink);
- }
-
- foreach ($stmt->parts as $part) {
- if ($part instanceof PhpParser\Node\Expr\Variable) {
- if (self::analyze($statements_analyzer, $part, $context) === false) {
- break;
- }
-
- $expr_type = $statements_analyzer->node_data->getType($part);
- if ($expr_type === null) {
- break;
- }
-
- $shell_exec_param = new FunctionLikeParameter(
- 'var',
- false
- );
-
- if (ArgumentAnalyzer::verifyType(
- $statements_analyzer,
- $expr_type,
- Type::getString(),
- null,
- 'shell_exec',
- null,
- 0,
- $call_location,
- $stmt,
- $context,
- $shell_exec_param,
- false,
- null,
- true,
- true,
- new CodeLocation($statements_analyzer, $stmt)
- ) === false) {
- return false;
- }
-
- foreach ($expr_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- new DataFlowNode('variable-use', 'variable use', null),
- 'variable-use'
- );
- }
- }
- }
- }
-
- IssueBuffer::maybeAdd(
- new ForbiddenCode(
- 'Use of shell_exec',
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Print_) {
- $was_inside_call = $context->inside_call;
- $context->inside_call = true;
- if (PrintAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- $context->inside_call = $was_inside_call;
-
- return false;
- }
- $context->inside_call = $was_inside_call;
-
- return true;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Yield_) {
- return YieldAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\YieldFrom) {
- return YieldFromAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- $php_major_version = $statements_analyzer->getCodebase()->php_major_version;
- $php_minor_version = $statements_analyzer->getCodebase()->php_minor_version;
-
- if ($stmt instanceof PhpParser\Node\Expr\Match_ && $php_major_version >= 8) {
- return MatchAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Throw_ && $php_major_version >= 8) {
- return ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if (($stmt instanceof PhpParser\Node\Expr\NullsafePropertyFetch
- || $stmt instanceof PhpParser\Node\Expr\NullsafeMethodCall)
- && $php_major_version >= 8
- ) {
- return NullsafeAnalyzer::analyze($statements_analyzer, $stmt, $context);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Error) {
- // do nothing
- return true;
- }
-
- if (IssueBuffer::accepts(
- new UnrecognizedExpression(
- 'Psalm does not understand ' . get_class($stmt) . ' for PHP ' .
- $php_major_version . ' ' . $php_minor_version,
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- // fall through
- }
-
- return false;
- }
-
- public static function isMock(string $fq_class_name): bool
- {
- return in_array(strtolower($fq_class_name), Config::getInstance()->getMockClasses(), true);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php
deleted file mode 100644
index c716bc2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/GlobalAnalyzer.php
+++ /dev/null
@@ -1,83 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\ReferenceConstraint;
-use Psalm\Issue\InvalidGlobal;
-use Psalm\IssueBuffer;
-
-use function is_string;
-
-class GlobalAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Global_ $stmt,
- Context $context,
- ?Context $global_context
- ): void {
- if (!$context->collect_initializations && !$global_context) {
- IssueBuffer::maybeAdd(
- new InvalidGlobal(
- 'Cannot use global scope here',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSource()->getSuppressedIssues()
- );
- }
-
- $source = $statements_analyzer->getSource();
- $function_storage = $source instanceof FunctionLikeAnalyzer
- ? $source->getFunctionLikeStorage($statements_analyzer)
- : null;
-
- foreach ($stmt->vars as $var) {
- if ($var instanceof PhpParser\Node\Expr\Variable) {
- if (is_string($var->name)) {
- $var_id = '$' . $var->name;
-
- if ($var->name === 'argv' || $var->name === 'argc') {
- $context->vars_in_scope[$var_id] = VariableFetchAnalyzer::getGlobalType($var_id);
- } elseif (isset($function_storage->global_types[$var_id])) {
- $context->vars_in_scope[$var_id] = clone $function_storage->global_types[$var_id];
- $context->vars_possibly_in_scope[$var_id] = true;
- } else {
- $context->vars_in_scope[$var_id] =
- $global_context && $global_context->hasVariable($var_id)
- ? clone $global_context->vars_in_scope[$var_id]
- : VariableFetchAnalyzer::getGlobalType($var_id);
-
- $context->vars_possibly_in_scope[$var_id] = true;
-
- $context->byref_constraints[$var_id] = new ReferenceConstraint();
- }
- $assignment_node = DataFlowNode::getForAssignment(
- $var_id,
- new CodeLocation($statements_analyzer, $var)
- );
- $context->vars_in_scope[$var_id]->parent_nodes = [
- $assignment_node->id => $assignment_node,
- ];
- $context->vars_from_global[$var_id] = true;
- $statements_analyzer->registerVariable(
- $var_id,
- new CodeLocation($statements_analyzer, $var),
- $context->branch_point
- );
- $statements_analyzer->getCodebase()->analyzer->addNodeReference(
- $statements_analyzer->getFilePath(),
- $var,
- $var_id
- );
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php
deleted file mode 100644
index 79832cc..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ReturnAnalyzer.php
+++ /dev/null
@@ -1,658 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\ClosureAnalyzer;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\ClassTemplateParamCollector;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateInferredTypeReplacer;
-use Psalm\Internal\Type\TemplateResult;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\FalsableReturnStatement;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\InvalidReturnStatement;
-use Psalm\Issue\LessSpecificReturnStatement;
-use Psalm\Issue\MixedReturnStatement;
-use Psalm\Issue\MixedReturnTypeCoercion;
-use Psalm\Issue\NoValue;
-use Psalm\Issue\NullableReturnStatement;
-use Psalm\IssueBuffer;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function count;
-use function explode;
-use function reset;
-use function strtolower;
-
-/**
- * @internal
- */
-class ReturnAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Return_ $stmt,
- Context $context
- ): void {
- $doc_comment = $stmt->getDocComment();
-
- $var_comments = [];
- $var_comment_type = null;
-
- $source = $statements_analyzer->getSource();
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($doc_comment && ($parsed_docblock = $statements_analyzer->getParsedDocblock())) {
- $file_storage_provider = $codebase->file_storage_provider;
-
- $file_storage = $file_storage_provider->get($statements_analyzer->getFilePath());
-
- try {
- $var_comments = $codebase->config->disable_var_parsing
- ? []
- : CommentAnalyzer::arrayToDocblocks(
- $doc_comment,
- $parsed_docblock,
- $statements_analyzer->getSource(),
- $statements_analyzer->getAliases(),
- $statements_analyzer->getTemplateTypeMap(),
- $file_storage->type_aliases
- );
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($source, $stmt)
- )
- );
- }
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->type) {
- continue;
- }
-
- $comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
-
- if ($codebase->alter_code
- && $var_comment->type_start
- && $var_comment->type_end
- && $var_comment->line_number
- ) {
- $type_location = new DocblockTypeLocation(
- $statements_analyzer,
- $var_comment->type_start,
- $var_comment->type_end,
- $var_comment->line_number
- );
-
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $statements_analyzer,
- $comment_type,
- $type_location,
- $context->calling_method_id
- );
- }
-
- if (!$var_comment->var_id) {
- $var_comment_type = $comment_type;
- continue;
- }
-
- if (isset($context->vars_in_scope[$var_comment->var_id])) {
- $comment_type->parent_nodes = $context->vars_in_scope[$var_comment->var_id]->parent_nodes;
- }
-
- $context->vars_in_scope[$var_comment->var_id] = $comment_type;
- }
- }
-
- if ($stmt->expr) {
- $context->inside_return = true;
-
- if ($stmt->expr instanceof PhpParser\Node\Expr\Closure
- || $stmt->expr instanceof PhpParser\Node\Expr\ArrowFunction
- ) {
- self::potentiallyInferTypesOnClosureFromParentReturnType(
- $statements_analyzer,
- $stmt->expr,
- $context
- );
- }
-
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- $context->inside_return = false;
- return;
- }
-
- $stmt_expr_type = $statements_analyzer->node_data->getType($stmt->expr);
-
- if ($var_comment_type) {
- $stmt_type = $var_comment_type;
-
- if ($stmt_expr_type && $stmt_expr_type->parent_nodes) {
- $stmt_type->parent_nodes = $stmt_expr_type->parent_nodes;
- }
-
- $statements_analyzer->node_data->setType($stmt, $var_comment_type);
- } elseif ($stmt_expr_type) {
- $stmt_type = $stmt_expr_type;
-
- if ($stmt_type->isNever()) {
- IssueBuffer::maybeAdd(
- new NoValue(
- 'This function or method call never returns output',
- new CodeLocation($source, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- $stmt_type = Type::getEmpty();
- }
-
- if ($stmt_type->isVoid()) {
- $stmt_type = Type::getNull();
- }
- } else {
- $stmt_type = Type::getMixed();
- }
-
- $context->inside_return = false;
- } else {
- $stmt_type = Type::getVoid();
- }
-
- $statements_analyzer->node_data->setType($stmt, $stmt_type);
-
- if ($context->finally_scope) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (isset($context->finally_scope->vars_in_scope[$var_id])) {
- $context->finally_scope->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->finally_scope->vars_in_scope[$var_id],
- $type,
- $statements_analyzer->getCodebase()
- );
- } else {
- $context->finally_scope->vars_in_scope[$var_id] = $type;
- $type->possibly_undefined = true;
- $type->possibly_undefined_from_try = true;
- }
- }
- }
-
- if ($source instanceof FunctionLikeAnalyzer
- && !($source->getSource() instanceof TraitAnalyzer)
- ) {
- $source->addReturnTypes($context);
-
- $source->examineParamTypes($statements_analyzer, $context, $codebase, $stmt);
-
- $storage = $source->getFunctionLikeStorage($statements_analyzer);
-
- $cased_method_id = $source->getCorrectlyCasedMethodId();
-
- if ($stmt->expr && $storage->location) {
- $inferred_type = TypeExpander::expandUnion(
- $codebase,
- $stmt_type,
- $source->getFQCLN(),
- $source->getFQCLN(),
- $source->getParentFQCLN()
- );
-
- if ($statements_analyzer->data_flow_graph instanceof TaintFlowGraph) {
- self::handleTaints(
- $statements_analyzer,
- $stmt,
- $cased_method_id,
- $inferred_type,
- $storage
- );
- }
-
- if ($storage instanceof MethodStorage && $context->self) {
- $self_class = $context->self;
-
- $declared_return_type = $codebase->methods->getMethodReturnType(
- MethodIdentifier::wrap($cased_method_id),
- $self_class,
- $statements_analyzer,
- null
- );
- } else {
- $declared_return_type = $storage->return_type;
- }
-
- if ($declared_return_type && !$declared_return_type->hasMixed()) {
- $local_return_type = $source->getLocalReturnType(
- $declared_return_type,
- $storage instanceof MethodStorage && $storage->final
- );
-
- if ($storage instanceof MethodStorage) {
- [$fq_class_name, $method_name] = explode('::', $cased_method_id);
-
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name);
-
- $found_generic_params = ClassTemplateParamCollector::collect(
- $codebase,
- $class_storage,
- $class_storage,
- strtolower($method_name),
- null,
- true
- );
-
- if ($found_generic_params) {
- foreach ($found_generic_params as $template_name => $_) {
- unset($found_generic_params[$template_name][$fq_class_name]);
- }
-
- $local_return_type = clone $local_return_type;
-
- TemplateInferredTypeReplacer::replace(
- $local_return_type,
- new TemplateResult([], $found_generic_params),
- $codebase
- );
- }
- }
-
- if ($local_return_type->isGenerator() && $storage->has_yield) {
- return;
- }
-
- if ($stmt_type->hasMixed()) {
- if ($local_return_type->isVoid() || $local_return_type->isNever()) {
- if (IssueBuffer::accepts(
- new InvalidReturnStatement(
- 'No return values are expected for ' . $cased_method_id,
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return;
- }
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && !($source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($stmt_type->isMixed()) {
- $origin_locations = [];
-
- if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph) {
- foreach ($stmt_type->parent_nodes as $parent_node) {
- $origin_locations = array_merge(
- $origin_locations,
- $statements_analyzer->data_flow_graph->getOriginLocations($parent_node)
- );
- }
- }
-
- $origin_location = count($origin_locations) === 1 ? reset($origin_locations) : null;
-
- $return_location = new CodeLocation($source, $stmt->expr);
-
- if ($origin_location && $origin_location->getHash() === $return_location->getHash()) {
- $origin_location = null;
- }
-
- IssueBuffer::maybeAdd(
- new MixedReturnStatement(
- 'Could not infer a return type',
- $return_location,
- $origin_location
- ),
- $statements_analyzer->getSuppressedIssues()
- );
-
- return;
- }
-
- IssueBuffer::maybeAdd(
- new MixedReturnStatement(
- 'Possibly-mixed return value',
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if ($local_return_type->isMixed()) {
- return;
- }
-
- if (!$context->collect_initializations
- && !$context->collect_mutations
- && $statements_analyzer->getFilePath() === $statements_analyzer->getRootFilePath()
- && !($source->getSource() instanceof TraitAnalyzer)
- ) {
- $codebase->analyzer->incrementNonMixedCount($statements_analyzer->getFilePath());
- }
-
- if ($local_return_type->isVoid()) {
- if (IssueBuffer::accepts(
- new InvalidReturnStatement(
- 'No return values are expected for ' . $cased_method_id,
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return;
- }
-
- return;
- }
-
- $union_comparison_results = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $inferred_type,
- $local_return_type,
- true,
- true,
- $union_comparison_results
- )
- ) {
- // is the declared return type more specific than the inferred one?
- if ($union_comparison_results->type_coerced) {
- if ($union_comparison_results->type_coerced_from_mixed) {
- if (!$union_comparison_results->type_coerced_from_as_mixed) {
- if ($inferred_type->hasMixed()) {
- IssueBuffer::maybeAdd(
- new MixedReturnStatement(
- 'Could not infer a return type',
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new MixedReturnTypeCoercion(
- 'The type \'' . $stmt_type->getId() . '\' is more general than the'
- . ' declared return type \'' . $local_return_type->getId() . '\''
- . ' for ' . $cased_method_id,
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- } else {
- IssueBuffer::maybeAdd(
- new LessSpecificReturnStatement(
- 'The type \'' . $stmt_type->getId() . '\' is more general than the'
- . ' declared return type \'' . $local_return_type->getId() . '\''
- . ' for ' . $cased_method_id,
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- foreach ($local_return_type->getAtomicTypes() as $local_type_part) {
- if ($local_type_part instanceof TClassString
- && $stmt->expr instanceof PhpParser\Node\Scalar\String_
- ) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $stmt->expr->value,
- new CodeLocation($source, $stmt->expr),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false
- ) {
- return;
- }
- } elseif ($local_type_part instanceof TArray
- && $stmt->expr instanceof PhpParser\Node\Expr\Array_
- ) {
- $value_param = $local_type_part->type_params[1];
-
- foreach ($value_param->getAtomicTypes() as $local_array_type_part) {
- if ($local_array_type_part instanceof TClassString) {
- foreach ($stmt->expr->items as $item) {
- if ($item && $item->value instanceof PhpParser\Node\Scalar\String_) {
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $statements_analyzer,
- $item->value->value,
- new CodeLocation($source, $item->value),
- $context->self,
- $context->calling_method_id,
- $statements_analyzer->getSuppressedIssues(),
- new ClassLikeNameOptions(true)
- ) === false
- ) {
- return;
- }
- }
- }
- }
- }
- }
- }
- } else {
- IssueBuffer::maybeAdd(
- new InvalidReturnStatement(
- 'The inferred type \'' . $inferred_type->getId()
- . '\' does not match the declared return '
- . 'type \'' . $local_return_type->getId() . '\' for ' . $cased_method_id,
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- if (!$stmt_type->ignore_nullable_issues
- && $inferred_type->isNullable()
- && !$local_return_type->isNullable()
- && !$local_return_type->hasTemplate()
- ) {
- IssueBuffer::maybeAdd(
- new NullableReturnStatement(
- 'The declared return type \'' . $local_return_type->getId() . '\' for '
- . $cased_method_id . ' is not nullable, but the function returns \''
- . $inferred_type->getId() . '\'',
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- if (!$stmt_type->ignore_falsable_issues
- && $inferred_type->isFalsable()
- && !$local_return_type->isFalsable()
- && (!$local_return_type->hasBool() || $local_return_type->isTrue())
- && !$local_return_type->hasScalar()
- ) {
- IssueBuffer::maybeAdd(
- new FalsableReturnStatement(
- 'The declared return type \'' . $local_return_type . '\' for '
- . $cased_method_id . ' does not allow false, but the function returns \''
- . $inferred_type . '\'',
- new CodeLocation($source, $stmt->expr)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- } else {
- if ($storage->signature_return_type
- && !$storage->signature_return_type->isVoid()
- && !$storage->has_yield
- ) {
- IssueBuffer::maybeAdd(
- new InvalidReturnStatement(
- 'Empty return statement is not expected in ' . $cased_method_id,
- new CodeLocation($source, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- private static function handleTaints(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Return_ $stmt,
- string $cased_method_id,
- Union $inferred_type,
- FunctionLikeStorage $storage
- ): void {
- if (!$statements_analyzer->data_flow_graph instanceof TaintFlowGraph
- || !$stmt->expr
- || !$storage->location
- ) {
- return;
- }
-
- $method_node = DataFlowNode::getForMethodReturn(
- strtolower($cased_method_id),
- $cased_method_id,
- $storage->signature_return_type_location ?: $storage->location
- );
-
- $statements_analyzer->data_flow_graph->addNode($method_node);
-
- if ($inferred_type->parent_nodes) {
- foreach ($inferred_type->parent_nodes as $parent_node) {
- $statements_analyzer->data_flow_graph->addPath(
- $parent_node,
- $method_node,
- 'return',
- $storage->added_taints,
- $storage->removed_taints
- );
- }
- }
- }
-
- /**
- * If a function returns a closure, we try to infer the param/return types of
- * the inner closure.
- * @see \Psalm\Tests\ReturnTypeTest:756
- * @param PhpParser\Node\Expr\Closure|PhpParser\Node\Expr\ArrowFunction $expr
- */
- private static function potentiallyInferTypesOnClosureFromParentReturnType(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\FunctionLike $expr,
- Context $context
- ): void {
- // if not returning from inside of a function, return
- if (!$context->calling_method_id && !$context->calling_function_id) {
- return;
- }
-
- $closure_id = (new ClosureAnalyzer($expr, $statements_analyzer))->getClosureId();
- $closure_storage = $statements_analyzer
- ->getCodebase()
- ->getFunctionLikeStorage($statements_analyzer, $closure_id);
-
- $parent_fn_storage = $statements_analyzer
- ->getCodebase()
- ->getFunctionLikeStorage(
- $statements_analyzer,
- $context->calling_function_id ?: $context->calling_method_id
- );
-
- if ($parent_fn_storage->return_type === null) {
- return;
- }
-
- // can't infer returned closure if the parent doesn't have a callable return type
- if (!$parent_fn_storage->return_type->hasCallableType()) {
- return;
- }
-
- // cannot infer if we have union/intersection types
- if (!$parent_fn_storage->return_type->isSingle()) {
- return;
- }
-
- /** @var TClosure|TCallable $parent_callable_return_type */
- $parent_callable_return_type = $parent_fn_storage->return_type->getSingleAtomic();
-
- if ($parent_callable_return_type->params === null && $parent_callable_return_type->return_type === null) {
- return;
- }
-
- foreach ($closure_storage->params as $key => $param) {
- $parent_param = $parent_callable_return_type->params[$key] ?? null;
- $param->type = self::inferInnerClosureTypeFromParent(
- $statements_analyzer->getCodebase(),
- $param->type,
- $parent_param->type ?? null
- );
- }
-
- $closure_storage->return_type = self::inferInnerClosureTypeFromParent(
- $statements_analyzer->getCodebase(),
- $closure_storage->return_type,
- $parent_callable_return_type->return_type
- );
- }
-
- /**
- * - If non parent type, do nothing
- * - If no return type, infer from parent
- * - If parent return type is more specific, infer from parent
- * - else, do nothing
- */
- private static function inferInnerClosureTypeFromParent(
- Codebase $codebase,
- ?Union $return_type,
- ?Union $parent_return_type
- ): ?Union {
- if (!$parent_return_type) {
- return $return_type;
- }
- if (!$return_type || UnionTypeComparator::isContainedBy($codebase, $parent_return_type, $return_type)) {
- return $parent_return_type;
- }
- return $return_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php
deleted file mode 100644
index 12d068d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/StaticAnalyzer.php
+++ /dev/null
@@ -1,188 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Context;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\ReferenceConstraint;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\ImpureStaticVariable;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\MissingDocblockType;
-use Psalm\Issue\ReferenceConstraintViolation;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use UnexpectedValueException;
-
-use function is_string;
-
-class StaticAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Static_ $stmt,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- if ($context->mutation_free) {
- IssueBuffer::maybeAdd(
- new ImpureStaticVariable(
- 'Cannot use a static variable in a mutation-free context',
- new CodeLocation($statements_analyzer, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
-
- foreach ($stmt->vars as $var) {
- if (!is_string($var->var->name)) {
- continue;
- }
-
- $var_id = '$' . $var->var->name;
-
- $doc_comment = $stmt->getDocComment();
-
- $comment_type = null;
-
- if ($doc_comment && ($parsed_docblock = $statements_analyzer->getParsedDocblock())) {
- $var_comments = [];
-
- try {
- $var_comments = $codebase->config->disable_var_parsing
- ? []
- : CommentAnalyzer::arrayToDocblocks(
- $doc_comment,
- $parsed_docblock,
- $statements_analyzer->getSource(),
- $statements_analyzer->getSource()->getAliases(),
- $statements_analyzer->getSource()->getTemplateTypeMap()
- );
- } catch (IncorrectDocblockException $e) {
- IssueBuffer::maybeAdd(
- new MissingDocblockType(
- $e->getMessage(),
- new CodeLocation($statements_analyzer, $var)
- )
- );
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $var)
- )
- );
- }
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->type) {
- continue;
- }
-
- try {
- $var_comment_type = TypeExpander::expandUnion(
- $codebase,
- $var_comment->type,
- $context->self,
- $context->self,
- $statements_analyzer->getParentFQCLN()
- );
-
- $var_comment_type->setFromDocblock();
-
- $var_comment_type->check(
- $statements_analyzer,
- new CodeLocation($statements_analyzer->getSource(), $var),
- $statements_analyzer->getSuppressedIssues()
- );
-
- if ($codebase->alter_code
- && $var_comment->type_start
- && $var_comment->type_end
- && $var_comment->line_number
- ) {
- $type_location = new DocblockTypeLocation(
- $statements_analyzer,
- $var_comment->type_start,
- $var_comment->type_end,
- $var_comment->line_number
- );
-
- $codebase->classlikes->handleDocblockTypeInMigration(
- $codebase,
- $statements_analyzer,
- $var_comment_type,
- $type_location,
- $context->calling_method_id
- );
- }
-
- if (!$var_comment->var_id || $var_comment->var_id === $var_id) {
- $comment_type = $var_comment_type;
- continue;
- }
-
- $context->vars_in_scope[$var_comment->var_id] = $var_comment_type;
- } catch (UnexpectedValueException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer, $var)
- )
- );
- }
- }
-
- if ($comment_type) {
- $context->byref_constraints[$var_id] = new ReferenceConstraint($comment_type);
- }
- }
-
- if ($var->default) {
- if (ExpressionAnalyzer::analyze($statements_analyzer, $var->default, $context) === false) {
- return;
- }
-
- if ($comment_type
- && ($var_default_type = $statements_analyzer->node_data->getType($var->default))
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $var_default_type,
- $comment_type
- )
- ) {
- IssueBuffer::maybeAdd(
- new ReferenceConstraintViolation(
- $var_id . ' of type ' . $comment_type->getId() . ' cannot be assigned type '
- . $var_default_type->getId(),
- new CodeLocation($statements_analyzer, $var)
- )
- );
- }
- }
-
- if ($context->check_variables) {
- $context->vars_in_scope[$var_id] = $comment_type ? clone $comment_type : Type::getMixed();
- $context->vars_possibly_in_scope[$var_id] = true;
- $context->assigned_var_ids[$var_id] = (int) $stmt->getAttribute('startFilePos');
- $statements_analyzer->byref_uses[$var_id] = true;
-
- $location = new CodeLocation($statements_analyzer, $var);
-
- $statements_analyzer->registerVariable(
- $var_id,
- $location,
- $context->branch_point
- );
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php
deleted file mode 100644
index 665a1c4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ThrowAnalyzer.php
+++ /dev/null
@@ -1,93 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\InvalidThrow;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class ThrowAnalyzer
-{
- /**
- * @param PhpParser\Node\Stmt\Throw_|PhpParser\Node\Expr\Throw_ $stmt
- */
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node $stmt,
- Context $context
- ): bool {
- $context->inside_throw = true;
- if (ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context) === false) {
- return false;
- }
- $context->inside_throw = false;
-
- if ($context->finally_scope) {
- foreach ($context->vars_in_scope as $var_id => $type) {
- if (isset($context->finally_scope->vars_in_scope[$var_id])) {
- $context->finally_scope->vars_in_scope[$var_id] = Type::combineUnionTypes(
- $context->finally_scope->vars_in_scope[$var_id],
- $type,
- $statements_analyzer->getCodebase()
- );
- } else {
- $context->finally_scope->vars_in_scope[$var_id] = $type;
- $type->possibly_undefined = true;
- $type->possibly_undefined_from_try = true;
- }
- }
- }
-
- if ($context->check_classes
- && ($throw_type = $statements_analyzer->node_data->getType($stmt->expr))
- && !$throw_type->hasMixed()
- ) {
- $exception_type = new Union([new TNamedObject('Exception'), new TNamedObject('Throwable')]);
-
- $file_analyzer = $statements_analyzer->getFileAnalyzer();
- $codebase = $statements_analyzer->getCodebase();
-
- foreach ($throw_type->getAtomicTypes() as $throw_type_part) {
- $throw_type_candidate = new Union([$throw_type_part]);
-
- if (!UnionTypeComparator::isContainedBy($codebase, $throw_type_candidate, $exception_type)) {
- if (IssueBuffer::accepts(
- new InvalidThrow(
- 'Cannot throw ' . $throw_type_part
- . ' as it does not extend Exception or implement Throwable',
- new CodeLocation($file_analyzer, $stmt),
- (string) $throw_type_part
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- } elseif (!$context->isSuppressingExceptions($statements_analyzer)) {
- $codelocation = new CodeLocation($file_analyzer, $stmt);
- $hash = $codelocation->getHash();
- foreach ($throw_type->getAtomicTypes() as $throw_atomic_type) {
- if ($throw_atomic_type instanceof TNamedObject) {
- $context->possibly_thrown_exceptions[$throw_atomic_type->value][$hash] = $codelocation;
- }
- }
- }
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Throw_) {
- $statements_analyzer->node_data->setType($stmt, Type::getEmpty());
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php
deleted file mode 100644
index c7cfeb6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnsetAnalyzer.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyMixed;
-use Psalm\Type\Union;
-
-use function count;
-
-class UnsetAnalyzer
-{
- public static function analyze(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt\Unset_ $stmt,
- Context $context
- ): void {
- $context->inside_unset = true;
-
- foreach ($stmt->vars as $var) {
- $was_inside_general_use = $context->inside_general_use;
- $context->inside_general_use = true;
-
- ExpressionAnalyzer::analyze($statements_analyzer, $var, $context);
-
- $context->inside_general_use = $was_inside_general_use;
-
- $var_id = ExpressionIdentifier::getArrayVarId(
- $var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($var_id) {
- $context->remove($var_id);
- }
-
- if ($var instanceof PhpParser\Node\Expr\ArrayDimFetch && $var->dim) {
- $root_var_id = ExpressionIdentifier::getArrayVarId(
- $var->var,
- $statements_analyzer->getFQCLN(),
- $statements_analyzer
- );
-
- if ($root_var_id && isset($context->vars_in_scope[$root_var_id])) {
- $root_type = clone $context->vars_in_scope[$root_var_id];
-
- foreach ($root_type->getAtomicTypes() as $atomic_root_type) {
- if ($atomic_root_type instanceof TKeyedArray) {
- if ($var->dim instanceof PhpParser\Node\Scalar\String_
- || $var->dim instanceof PhpParser\Node\Scalar\LNumber
- ) {
- if (isset($atomic_root_type->properties[$var->dim->value])) {
- if ($atomic_root_type->is_list
- && $var->dim->value !== count($atomic_root_type->properties)-1
- ) {
- $atomic_root_type->is_list = false;
- }
- unset($atomic_root_type->properties[$var->dim->value]);
- $root_type->bustCache(); //remove id cache
- }
-
- if (!$atomic_root_type->properties) {
- if ($atomic_root_type->previous_value_type) {
- $root_type->addType(
- new TArray([
- $atomic_root_type->previous_key_type
- ? clone $atomic_root_type->previous_key_type
- : new Union([new TArrayKey]),
- clone $atomic_root_type->previous_value_type,
- ])
- );
- } else {
- $root_type->addType(
- new TArray([
- new Union([new TEmpty]),
- new Union([new TEmpty]),
- ])
- );
- }
- }
- } else {
- foreach ($atomic_root_type->properties as $key => $type) {
- $atomic_root_type->properties[$key] = clone $type;
- $atomic_root_type->properties[$key]->possibly_undefined = true;
- }
-
- $atomic_root_type->sealed = false;
-
- $root_type->addType(
- $atomic_root_type->getGenericArrayType(false)
- );
-
- $atomic_root_type->is_list = false;
- }
- } elseif ($atomic_root_type instanceof TNonEmptyArray) {
- $root_type->addType(
- new TArray($atomic_root_type->type_params)
- );
- } elseif ($atomic_root_type instanceof TNonEmptyMixed) {
- $root_type->addType(
- new TMixed()
- );
- } elseif ($atomic_root_type instanceof TList) {
- $root_type->addType(
- new TArray([
- Type::getInt(),
- $atomic_root_type->type_param
- ])
- );
- }
- }
-
- $context->vars_in_scope[$root_var_id] = $root_type;
-
- $context->removeVarFromConflictingClauses(
- $root_var_id,
- $context->vars_in_scope[$root_var_id],
- $statements_analyzer
- );
- }
- }
- }
-
- $context->inside_unset = false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php
deleted file mode 100644
index 30c01b4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/UnusedAssignmentRemover.php
+++ /dev/null
@@ -1,357 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer\Statements;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\FileManipulation;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\PhpVisitor\CheckTrivialExprVisitor;
-
-use function array_key_exists;
-use function array_slice;
-use function count;
-use function is_array;
-use function is_string;
-use function strlen;
-use function substr;
-use function token_get_all;
-use function trim;
-
-class UnusedAssignmentRemover
-{
- /**
- * @var array<string, CodeLocation>
- */
- private $removed_unref_vars = [];
-
- /**
- * @param array<PhpParser\Node\Stmt> $stmts
- * @param array<string, CodeLocation> $var_loc_map
- *
- */
- public function findUnusedAssignment(
- Codebase $codebase,
- array $stmts,
- array $var_loc_map,
- string $var_id,
- CodeLocation $original_location
- ): void {
- $search_result = $this->findAssignStmt($stmts, $var_id, $original_location);
- [$assign_stmt, $assign_exp] = $search_result;
- $chain_assignment = false;
-
- if ($assign_stmt !== null && $assign_exp !== null) {
- // Check if we have to remove assignment statement as expression (i.e. just "$var = ")
-
- // Consider chain of assignments
- $rhs_exp = $assign_exp->expr;
- if ($rhs_exp instanceof PhpParser\Node\Expr\Assign
- || $rhs_exp instanceof PhpParser\Node\Expr\AssignOp
- || $rhs_exp instanceof PhpParser\Node\Expr\AssignRef
- ) {
- $chain_assignment = true;
- $removable_stmt = $this->checkRemovableChainAssignment($assign_exp, $var_loc_map);
- } else {
- $removable_stmt = true;
- }
-
- if ($removable_stmt) {
- $traverser = new PhpParser\NodeTraverser();
- $visitor = new CheckTrivialExprVisitor();
- $traverser->addVisitor($visitor);
- $traverser->traverse([$rhs_exp]);
-
- $rhs_exp_trivial = (count($visitor->getNonTrivialExpr()) === 0);
-
- if ($rhs_exp_trivial) {
- $treat_as_expr = false;
- } else {
- $treat_as_expr = true;
- }
- } else {
- $treat_as_expr = true;
- }
-
- if ($treat_as_expr) {
- $is_assign_ref = $assign_exp instanceof PhpParser\Node\Expr\AssignRef;
- $new_file_manipulation = self::getPartialRemovalBounds(
- $codebase,
- $original_location,
- $assign_stmt->getEndFilePos(),
- $is_assign_ref
- );
- $this->removed_unref_vars[$var_id] = $original_location;
- } else {
- // Remove whole assignment statement
- $new_file_manipulation = new FileManipulation(
- $assign_stmt->getStartFilePos(),
- $assign_stmt->getEndFilePos() + 1,
- "",
- false,
- true
- );
-
- // If statement we are removing is a chain of assignments, mark other variables as removed
- if ($chain_assignment) {
- $this->markRemovedChainAssignVar($assign_exp, $var_loc_map);
- } else {
- $this->removed_unref_vars[$var_id] = $original_location;
- }
- }
-
- FileManipulationBuffer::add($original_location->file_path, [$new_file_manipulation]);
- } elseif ($assign_exp !== null) {
- $is_assign_ref = $assign_exp instanceof PhpParser\Node\Expr\AssignRef;
- $new_file_manipulation = self::getPartialRemovalBounds(
- $codebase,
- $original_location,
- $assign_exp->getEndFilePos(),
- $is_assign_ref
- );
-
- FileManipulationBuffer::add($original_location->file_path, [$new_file_manipulation]);
- $this->removed_unref_vars[$var_id] = $original_location;
- }
- }
-
- private static function getPartialRemovalBounds(
- Codebase $codebase,
- CodeLocation $var_loc,
- int $end_bound,
- bool $assign_ref = false
- ): FileManipulation {
- $var_start_loc= $var_loc->raw_file_start;
- $stmt_content = $codebase->file_provider->getContents(
- $var_loc->file_path
- );
- $str_for_token = "<?php\n" . substr($stmt_content, $var_start_loc, $end_bound - $var_start_loc + 1);
- $token_list = array_slice(token_get_all($str_for_token), 1); //Ignore "<?php"
-
- $offset_count = strlen($token_list[0][1]);
- $iter = 1;
-
- // Check if second token is just whitespace
- if (is_array($token_list[$iter]) && trim($token_list[$iter][1]) === '') {
- $offset_count += strlen($token_list[1][1]);
- $iter++;
- }
-
- // Add offset for assignment operator
- if (is_string($token_list[$iter])) {
- $offset_count += 1;
- } else {
- $offset_count += strlen($token_list[$iter][1]);
- }
- $iter++;
-
- // Remove any whitespace following assignment operator token (e.g "=", "+=")
- if (is_array($token_list[$iter]) && trim($token_list[$iter][1]) === '') {
- $offset_count += strlen($token_list[$iter][1]);
- $iter++;
- }
-
- // If we are dealing with assignment by reference, we need to handle "&" and any whitespace after
- if ($assign_ref) {
- $offset_count += 1;
- $iter++;
- // Handle any whitespace after "&"
- if (is_array($token_list[$iter]) && trim($token_list[$iter][1]) === '') {
- $offset_count += strlen($token_list[$iter][1]);
- }
- }
-
- $file_man_start = $var_start_loc;
- $file_man_end = $var_start_loc + $offset_count;
-
- return new FileManipulation($file_man_start, $file_man_end, "", false);
- }
-
- /**
- * @param PhpParser\Node\Expr\Assign|PhpParser\Node\Expr\AssignOp|PhpParser\Node\Expr\AssignRef $cur_assign
- * @param array<string, CodeLocation> $var_loc_map
- */
- private function markRemovedChainAssignVar(PhpParser\Node\Expr $cur_assign, array $var_loc_map): void
- {
- $var = $cur_assign->var;
- if ($var instanceof PhpParser\Node\Expr\Variable && is_string($var->name)) {
- $var_name = "$" . $var->name;
- $var_loc = $var_loc_map[$var_name];
- $this->removed_unref_vars[$var_name] = $var_loc;
-
- $rhs_exp = $cur_assign->expr;
- if ($rhs_exp instanceof PhpParser\Node\Expr\Assign
- || $rhs_exp instanceof PhpParser\Node\Expr\AssignOp
- || $rhs_exp instanceof PhpParser\Node\Expr\AssignRef
- ) {
- $this->markRemovedChainAssignVar($rhs_exp, $var_loc_map);
- }
- }
- }
-
- /**
- * @param PhpParser\Node\Expr\Assign|PhpParser\Node\Expr\AssignOp|PhpParser\Node\Expr\AssignRef $cur_assign
- * @param array<string, CodeLocation> $var_loc_map
- */
- private function checkRemovableChainAssignment(PhpParser\Node\Expr $cur_assign, array $var_loc_map): bool
- {
- // Check if current assignment expr's variable is removable
- $var = $cur_assign->var;
- if ($var instanceof PhpParser\Node\Expr\Variable && is_string($var->name)) {
- $var_loc = $cur_assign->var->getStartFilePos();
- $var_name = "$" . $var->name;
-
- if (array_key_exists($var_name, $var_loc_map) &&
- $var_loc_map[$var_name]->raw_file_start === $var_loc) {
- $curr_removable = true;
- } else {
- $curr_removable = false;
- }
-
- if ($curr_removable) {
- $rhs_exp = $cur_assign->expr;
-
- if ($rhs_exp instanceof PhpParser\Node\Expr\Assign
- || $rhs_exp instanceof PhpParser\Node\Expr\AssignOp
- || $rhs_exp instanceof PhpParser\Node\Expr\AssignRef
- ) {
- return $this->checkRemovableChainAssignment($rhs_exp, $var_loc_map);
- }
- }
- return $curr_removable;
- }
-
- return false;
- }
-
- /**
- * @param array<PhpParser\Node\Stmt> $stmts
- * @return array{
- * 0: PhpParser\Node\Stmt|null,
- * 1: PhpParser\Node\Expr\Assign|PhpParser\Node\Expr\AssignOp|PhpParser\Node\Expr\AssignRef|null
- * }
- */
- private function findAssignStmt(array $stmts, string $var_id, CodeLocation $original_location): array
- {
- $assign_stmt = null;
- $assign_exp = null;
- $assign_exp_found = false;
-
- $i = 0;
-
- while ($i < count($stmts) && !$assign_exp_found) {
- $stmt = $stmts[$i];
- if ($stmt instanceof PhpParser\Node\Stmt\Expression) {
- $search_result = $this->findAssignExp($stmt->expr, $var_id, $original_location->raw_file_start);
-
- [$target_exp, $levels_taken] = $search_result;
-
- if ($target_exp !== null) {
- $assign_exp_found = true;
- $assign_exp = $target_exp;
- $assign_stmt = $levels_taken === 1 ? $stmt : null;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
- $search_result = $this->findAssignStmt($stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
-
- foreach ($stmt->catches as $catch_stmt) {
- $search_result = $this->findAssignStmt($catch_stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Do_
- || $stmt instanceof PhpParser\Node\Stmt\While_
- ) {
- $search_result = $this->findAssignStmt($stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Foreach_) {
- $search_result = $this->findAssignStmt($stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\For_) {
- $search_result = $this->findAssignStmt($stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\If_) {
- $search_result = $this->findAssignStmt($stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
-
- foreach ($stmt->elseifs as $elseif_stmt) {
- $search_result = $this->findAssignStmt($elseif_stmt->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
- }
-
- if ($stmt->else) {
- $search_result = $this->findAssignStmt($stmt->else->stmts, $var_id, $original_location);
-
- if ($search_result[0] && $search_result[1]) {
- return $search_result;
- }
- }
- }
-
- $i++;
- }
-
- return [$assign_stmt, $assign_exp];
- }
-
- /**
- * @return array{
- * 0: PhpParser\Node\Expr\Assign|PhpParser\Node\Expr\AssignOp|PhpParser\Node\Expr\AssignRef|null,
- * 1: int
- * }
- */
- private function findAssignExp(
- PhpParser\Node\Expr $current_node,
- string $var_id,
- int $var_start_loc,
- int $search_level = 1
- ): array {
- if ($current_node instanceof PhpParser\Node\Expr\Assign
- || $current_node instanceof PhpParser\Node\Expr\AssignOp
- || $current_node instanceof PhpParser\Node\Expr\AssignRef
- ) {
- $var = $current_node->var;
-
- if ($var instanceof PhpParser\Node\Expr\Variable
- && $var->name === substr($var_id, 1)
- && $var->getStartFilePos() === $var_start_loc
- ) {
- return [$current_node, $search_level];
- }
-
- $rhs_exp = $current_node->expr;
- $rhs_search_result = $this->findAssignExp($rhs_exp, $var_id, $var_start_loc, $search_level + 1);
- return [$rhs_search_result[0], $rhs_search_result[1]];
- }
-
- return [null, $search_level];
- }
-
- public function checkIfVarRemoved(string $var_id, CodeLocation $var_loc): bool
- {
- return array_key_exists($var_id, $this->removed_unref_vars)
- && $this->removed_unref_vars[$var_id] === $var_loc;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php
deleted file mode 100644
index dff3d60..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php
+++ /dev/null
@@ -1,1076 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\DocComment;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\Statements\Block\DoAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\ForAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\IfElseAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\SwitchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\TryAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Block\WhileAnalyzer;
-use Psalm\Internal\Analyzer\Statements\BreakAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ContinueAnalyzer;
-use Psalm\Internal\Analyzer\Statements\EchoAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Assignment\InstancePropertyAssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\AssignmentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ClassConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\Statements\GlobalAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ReturnAnalyzer;
-use Psalm\Internal\Analyzer\Statements\StaticAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ThrowAnalyzer;
-use Psalm\Internal\Analyzer\Statements\UnsetAnalyzer;
-use Psalm\Internal\Analyzer\Statements\UnusedAssignmentRemover;
-use Psalm\Internal\Codebase\DataFlowGraph;
-use Psalm\Internal\Codebase\TaintFlowGraph;
-use Psalm\Internal\Codebase\VariableUseGraph;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\ReferenceConstraint;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\Issue\ComplexFunction;
-use Psalm\Issue\ComplexMethod;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\MissingDocblockType;
-use Psalm\Issue\Trace;
-use Psalm\Issue\UndefinedDocblockClass;
-use Psalm\Issue\UndefinedTrace;
-use Psalm\Issue\UnevaluatedCode;
-use Psalm\Issue\UnrecognizedStatement;
-use Psalm\Issue\UnusedForeachValue;
-use Psalm\Issue\UnusedVariable;
-use Psalm\IssueBuffer;
-use Psalm\NodeTypeProvider;
-use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent;
-use Psalm\Type;
-use UnexpectedValueException;
-
-use function array_change_key_case;
-use function array_column;
-use function array_combine;
-use function array_keys;
-use function array_merge;
-use function array_search;
-use function assert;
-use function count;
-use function fwrite;
-use function get_class;
-use function in_array;
-use function is_string;
-use function preg_split;
-use function reset;
-use function round;
-use function strlen;
-use function strpos;
-use function strrpos;
-use function strtolower;
-use function substr;
-use function trim;
-
-use const PREG_SPLIT_NO_EMPTY;
-use const STDERR;
-
-/**
- * @internal
- */
-class StatementsAnalyzer extends SourceAnalyzer
-{
- /**
- * @var SourceAnalyzer
- */
- protected $source;
-
- /**
- * @var FileAnalyzer
- */
- protected $file_analyzer;
-
- /**
- * @var Codebase
- */
- protected $codebase;
-
- /**
- * @var array<string, CodeLocation>
- */
- private $all_vars = [];
-
- /**
- * @var array<string, int>
- */
- private $var_branch_points = [];
-
- /**
- * Possibly undefined variables should be initialised if we're altering code
- *
- * @var array<string, int>|null
- */
- private $vars_to_initialize;
-
- /**
- * @var array<string, FunctionAnalyzer>
- */
- private $function_analyzers = [];
-
- /**
- * @var array<string, array{0: string, 1: CodeLocation}>
- */
- private $unused_var_locations = [];
-
- /**
- * @var ?array<string, bool>
- */
- public $byref_uses;
-
- /**
- * @var ParsedDocblock|null
- */
- private $parsed_docblock;
-
- /**
- * @var ?string
- */
- private $fake_this_class;
-
- /** @var NodeDataProvider */
- public $node_data;
-
- /** @var ?DataFlowGraph */
- public $data_flow_graph;
-
- /**
- * Locations of foreach values
- *
- * Used to discern ordinary UnusedVariables from UnusedForeachValues
- *
- * @var array<string, list<CodeLocation>>
- * @psalm-internal Psalm\Internal\Analyzer
- */
- public $foreach_var_locations = [];
-
- public function __construct(SourceAnalyzer $source, NodeDataProvider $node_data)
- {
- $this->source = $source;
- $this->file_analyzer = $source->getFileAnalyzer();
- $this->codebase = $source->getCodebase();
- $this->node_data = $node_data;
-
- if ($this->codebase->taint_flow_graph) {
- $this->data_flow_graph = new TaintFlowGraph();
- } elseif ($this->codebase->find_unused_variables) {
- $this->data_flow_graph = new VariableUseGraph();
- }
- }
-
- /**
- * Checks an array of statements for validity
- *
- * @param array<PhpParser\Node\Stmt> $stmts
- *
- * @return null|false
- */
- public function analyze(
- array $stmts,
- Context $context,
- ?Context $global_context = null,
- bool $root_scope = false
- ): ?bool {
- if (!$stmts) {
- return null;
- }
-
- // hoist functions to the top
- $this->hoistFunctions($stmts, $context);
-
- $project_analyzer = $this->getFileAnalyzer()->project_analyzer;
- $codebase = $project_analyzer->getCodebase();
-
- if ($codebase->config->hoist_constants) {
- self::hoistConstants($this, $stmts, $context);
- }
-
- foreach ($stmts as $stmt) {
- if (self::analyzeStatement($this, $stmt, $context, $global_context) === false) {
- return false;
- }
- }
-
- if ($root_scope
- && !$context->collect_initializations
- && !$context->collect_mutations
- && $codebase->find_unused_variables
- && $context->check_variables
- ) {
- //var_dump($this->data_flow_graph);
- $this->checkUnreferencedVars($stmts, $context);
- }
-
- if ($codebase->alter_code && $root_scope && $this->vars_to_initialize) {
- $file_contents = $codebase->getFileContents($this->getFilePath());
-
- foreach ($this->vars_to_initialize as $var_id => $branch_point) {
- $newline_pos = (int)strrpos($file_contents, "\n", $branch_point - strlen($file_contents)) + 1;
- $indentation = substr($file_contents, $newline_pos, $branch_point - $newline_pos);
- FileManipulationBuffer::add($this->getFilePath(), [
- new FileManipulation($branch_point, $branch_point, $var_id . ' = null;' . "\n" . $indentation),
- ]);
- }
- }
-
- if ($root_scope
- && $this->data_flow_graph instanceof TaintFlowGraph
- && $this->codebase->taint_flow_graph
- && $codebase->config->trackTaintsInPath($this->getFilePath())
- ) {
- $this->codebase->taint_flow_graph->addGraph($this->data_flow_graph);
- }
-
- return null;
- }
-
- /**
- * @param array<PhpParser\Node\Stmt> $stmts
- */
- private function hoistFunctions(array $stmts, Context $context): void
- {
- foreach ($stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Function_) {
- $function_name = strtolower($stmt->name->name);
-
- if ($ns = $this->getNamespace()) {
- $fq_function_name = strtolower($ns) . '\\' . $function_name;
- } else {
- $fq_function_name = $function_name;
- }
-
- if ($this->data_flow_graph
- && $this->codebase->find_unused_variables
- ) {
- foreach ($stmt->stmts as $function_stmt) {
- if ($function_stmt instanceof PhpParser\Node\Stmt\Global_) {
- foreach ($function_stmt->vars as $var) {
- if (!$var instanceof PhpParser\Node\Expr\Variable
- || !is_string($var->name)
- ) {
- continue;
- }
-
- $var_id = '$' . $var->name;
-
- if ($var_id !== '$argv' && $var_id !== '$argc') {
- $context->byref_constraints[$var_id] = new ReferenceConstraint();
- }
- }
- }
- }
- }
-
- try {
- $function_analyzer = new FunctionAnalyzer($stmt, $this->source);
- $this->function_analyzers[$fq_function_name] = $function_analyzer;
- } catch (UnexpectedValueException $e) {
- // do nothing
- }
- }
- }
- }
-
- /**
- * @param array<PhpParser\Node\Stmt> $stmts
- */
- private static function hoistConstants(
- StatementsAnalyzer $statements_analyzer,
- array $stmts,
- Context $context
- ): void {
- $codebase = $statements_analyzer->getCodebase();
-
- foreach ($stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Const_) {
- foreach ($stmt->consts as $const) {
- ConstFetchAnalyzer::setConstType(
- $statements_analyzer,
- $const->name->name,
- SimpleTypeInferer::infer(
- $codebase,
- $statements_analyzer->node_data,
- $const->value,
- $statements_analyzer->getAliases(),
- $statements_analyzer
- ) ?? Type::getMixed(),
- $context
- );
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Expression
- && $stmt->expr instanceof PhpParser\Node\Expr\FuncCall
- && $stmt->expr->name instanceof PhpParser\Node\Name
- && $stmt->expr->name->parts === ['define']
- && isset($stmt->expr->getArgs()[1])
- ) {
- $const_name = ConstFetchAnalyzer::getConstName(
- $stmt->expr->getArgs()[0]->value,
- $statements_analyzer->node_data,
- $codebase,
- $statements_analyzer->getAliases()
- );
-
- if ($const_name !== null) {
- ConstFetchAnalyzer::setConstType(
- $statements_analyzer,
- $const_name,
- SimpleTypeInferer::infer(
- $codebase,
- $statements_analyzer->node_data,
- $stmt->expr->getArgs()[1]->value,
- $statements_analyzer->getAliases(),
- $statements_analyzer
- ) ?? Type::getMixed(),
- $context
- );
- }
- }
- }
- }
-
- /**
- * @return false|null
- */
- private static function analyzeStatement(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Stmt $stmt,
- Context $context,
- ?Context $global_context
- ): ?bool {
- $ignore_variable_property = false;
- $ignore_variable_method = false;
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($statements_analyzer->getProjectAnalyzer()->debug_lines) {
- fwrite(STDERR, $statements_analyzer->getFilePath() . ':' . $stmt->getLine() . "\n");
- }
-
- $new_issues = null;
- $traced_variables = [];
-
- if ($docblock = $stmt->getDocComment()) {
- $statements_analyzer->parseStatementDocblock($docblock, $stmt, $context);
-
- if (isset($statements_analyzer->parsed_docblock->tags['psalm-trace'])) {
- foreach ($statements_analyzer->parsed_docblock->tags['psalm-trace'] as $traced_variable_line) {
- $possible_traced_variable_names = preg_split(
- '/(?:\s*,\s*|\s+)/',
- $traced_variable_line,
- -1,
- PREG_SPLIT_NO_EMPTY
- );
- if ($possible_traced_variable_names) {
- $traced_variables = array_merge($traced_variables, $possible_traced_variable_names);
- }
- }
- }
-
- if (isset($statements_analyzer->parsed_docblock->tags['psalm-ignore-variable-method'])) {
- $context->ignore_variable_method = $ignore_variable_method = true;
- }
-
- if (isset($statements_analyzer->parsed_docblock->tags['psalm-ignore-variable-property'])) {
- $context->ignore_variable_property = $ignore_variable_property = true;
- }
-
- if (isset($statements_analyzer->parsed_docblock->tags['psalm-suppress'])) {
- $suppressed = $statements_analyzer->parsed_docblock->tags['psalm-suppress'];
- if ($suppressed) {
- $new_issues = [];
-
- foreach ($suppressed as $offset => $suppress_entry) {
- foreach (DocComment::parseSuppressList($suppress_entry) as $issue_offset => $issue_type) {
- $new_issues[$issue_offset + $offset] = $issue_type;
- }
- }
-
- if ($codebase->track_unused_suppressions
- && (
- (count($new_issues) === 1) // UnusedPsalmSuppress by itself should be marked as unused
- || !in_array("UnusedPsalmSuppress", $new_issues)
- )
- ) {
- foreach ($new_issues as $offset => $issue_type) {
- if ($issue_type === 'InaccessibleMethod') {
- continue;
- }
-
- IssueBuffer::addUnusedSuppression(
- $statements_analyzer->getFilePath(),
- $offset,
- $issue_type
- );
- }
- }
-
- $statements_analyzer->addSuppressedIssues($new_issues);
- }
- }
-
- if (isset($statements_analyzer->parsed_docblock->combined_tags['var'])
- && !($stmt instanceof PhpParser\Node\Stmt\Expression
- && $stmt->expr instanceof PhpParser\Node\Expr\Assign)
- && !$stmt instanceof PhpParser\Node\Stmt\Foreach_
- && !$stmt instanceof PhpParser\Node\Stmt\Return_
- ) {
- $file_path = $statements_analyzer->getRootFilePath();
-
- $file_storage_provider = $codebase->file_storage_provider;
-
- $file_storage = $file_storage_provider->get($file_path);
-
- $template_type_map = $statements_analyzer->getTemplateTypeMap();
-
- $var_comments = [];
-
- try {
- $var_comments = $codebase->config->disable_var_parsing
- ? []
- : CommentAnalyzer::arrayToDocblocks(
- $docblock,
- $statements_analyzer->parsed_docblock,
- $statements_analyzer->getSource(),
- $statements_analyzer->getAliases(),
- $template_type_map,
- $file_storage->type_aliases
- );
- } catch (IncorrectDocblockException $e) {
- IssueBuffer::maybeAdd(
- new MissingDocblockType(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- )
- );
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($statements_analyzer->getSource(), $stmt)
- )
- );
- }
-
- foreach ($var_comments as $var_comment) {
- AssignmentAnalyzer::assignTypeFromVarDocblock(
- $statements_analyzer,
- $stmt,
- $var_comment,
- $context
- );
-
- if ($var_comment->var_id === '$this'
- && $var_comment->type
- && $codebase->classExists((string)$var_comment->type)
- ) {
- $statements_analyzer->setFQCLN((string)$var_comment->type);
- }
- }
- }
- } else {
- $statements_analyzer->parsed_docblock = null;
- }
-
- if ($context->has_returned
- && !$context->collect_initializations
- && !$context->collect_mutations
- && !($stmt instanceof PhpParser\Node\Stmt\Nop)
- && !($stmt instanceof PhpParser\Node\Stmt\Function_)
- && !($stmt instanceof PhpParser\Node\Stmt\Class_)
- && !($stmt instanceof PhpParser\Node\Stmt\Interface_)
- && !($stmt instanceof PhpParser\Node\Stmt\Trait_)
- && !($stmt instanceof PhpParser\Node\Stmt\HaltCompiler)
- ) {
- if ($codebase->find_unused_variables) {
- if (IssueBuffer::accepts(
- new UnevaluatedCode(
- 'Expressions after return/throw/continue',
- new CodeLocation($statements_analyzer->source, $stmt)
- ),
- $statements_analyzer->source->getSuppressedIssues()
- )) {
- return null;
- }
- }
-
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\If_) {
- if (IfElseAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\TryCatch) {
- if (TryAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\For_) {
- if (ForAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Foreach_) {
- if (ForeachAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\While_) {
- if (WhileAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Do_) {
- if (DoAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Const_) {
- ConstFetchAnalyzer::analyzeConstAssignment($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Unset_) {
- UnsetAnalyzer::analyze($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Return_) {
- ReturnAnalyzer::analyze($statements_analyzer, $stmt, $context);
- $context->has_returned = true;
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Throw_) {
- ThrowAnalyzer::analyze($statements_analyzer, $stmt, $context);
- $context->has_returned = true;
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Switch_) {
- SwitchAnalyzer::analyze($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Break_) {
- BreakAnalyzer::analyze($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Continue_) {
- ContinueAnalyzer::analyze($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Static_) {
- StaticAnalyzer::analyze($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Echo_) {
- if (EchoAnalyzer::analyze($statements_analyzer, $stmt, $context) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Function_) {
- FunctionAnalyzer::analyzeStatement($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Expression) {
- if (ExpressionAnalyzer::analyze(
- $statements_analyzer,
- $stmt->expr,
- $context,
- false,
- $global_context,
- true
- ) === false) {
- return false;
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\InlineHTML) {
- // do nothing
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Global_) {
- GlobalAnalyzer::analyze($statements_analyzer, $stmt, $context, $global_context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Property) {
- InstancePropertyAssignmentAnalyzer::analyzeStatement($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\ClassConst) {
- ClassConstFetchAnalyzer::analyzeClassConstAssignment($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Class_) {
- try {
- $class_analyzer = new ClassAnalyzer(
- $stmt,
- $statements_analyzer->source,
- $stmt->name->name ?? null
- );
-
- $class_analyzer->analyze(null, $global_context);
- } catch (InvalidArgumentException $e) {
- // disregard this exception, we'll likely see it elsewhere in the form
- // of an issue
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Trait_) {
- TraitAnalyzer::analyze($statements_analyzer, $stmt, $context);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Nop) {
- // do nothing
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Goto_) {
- // do nothing
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Label) {
- // do nothing
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Declare_) {
- foreach ($stmt->declares as $declaration) {
- if ((string) $declaration->key === 'strict_types'
- && $declaration->value instanceof PhpParser\Node\Scalar\LNumber
- && $declaration->value->value === 1
- ) {
- $context->strict_types = true;
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\HaltCompiler) {
- $context->has_returned = true;
- } else {
- if (IssueBuffer::accepts(
- new UnrecognizedStatement(
- 'Psalm does not understand ' . get_class($stmt),
- new CodeLocation($statements_analyzer->source, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- )) {
- return false;
- }
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- $event = new AfterStatementAnalysisEvent(
- $stmt,
- $context,
- $statements_analyzer,
- $codebase,
- []
- );
-
- if ($codebase->config->eventDispatcher->dispatchAfterStatementAnalysis($event) === false) {
- return false;
- }
-
- $file_manipulations = $event->getFileReplacements();
- if ($file_manipulations) {
- FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations);
- }
-
- if ($new_issues) {
- $statements_analyzer->removeSuppressedIssues($new_issues);
- }
-
- if ($ignore_variable_property) {
- $context->ignore_variable_property = false;
- }
-
- if ($ignore_variable_method) {
- $context->ignore_variable_method = false;
- }
-
- foreach ($traced_variables as $traced_variable) {
- if (isset($context->vars_in_scope[$traced_variable])) {
- IssueBuffer::maybeAdd(
- new Trace(
- $traced_variable . ': ' . $context->vars_in_scope[$traced_variable]->getId(),
- new CodeLocation($statements_analyzer->source, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- } else {
- IssueBuffer::maybeAdd(
- new UndefinedTrace(
- 'Attempt to trace undefined variable ' . $traced_variable,
- new CodeLocation($statements_analyzer->source, $stmt)
- ),
- $statements_analyzer->getSuppressedIssues()
- );
- }
- }
-
- return null;
- }
-
- private function parseStatementDocblock(
- PhpParser\Comment\Doc $docblock,
- PhpParser\Node\Stmt $stmt,
- Context $context
- ): void {
- $codebase = $this->getCodebase();
-
- try {
- $this->parsed_docblock = DocComment::parsePreservingLength($docblock);
- } catch (DocblockParseException $e) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($this->getSource(), $stmt, null, true)
- )
- );
-
- $this->parsed_docblock = null;
- }
-
- $comments = $this->parsed_docblock;
-
- if (isset($comments->tags['psalm-scope-this'])) {
- $trimmed = trim(reset($comments->tags['psalm-scope-this']));
-
- if (!$codebase->classExists($trimmed)) {
- IssueBuffer::maybeAdd(
- new UndefinedDocblockClass(
- 'Scope class ' . $trimmed . ' does not exist',
- new CodeLocation($this->getSource(), $stmt, null, true),
- $trimmed
- )
- );
- } else {
- $this_type = Type::parseString($trimmed);
- $context->self = $trimmed;
- $context->vars_in_scope['$this'] = $this_type;
- $this->setFQCLN($trimmed);
- }
- }
- }
-
- /**
- * @param array<PhpParser\Node\Stmt> $stmts
- */
- public function checkUnreferencedVars(array $stmts, Context $context): void
- {
- $source = $this->getSource();
- $codebase = $source->getCodebase();
- $function_storage = $source instanceof FunctionLikeAnalyzer ? $source->getFunctionLikeStorage($this) : null;
- $var_list = array_column($this->unused_var_locations, 0);
- $loc_list = array_column($this->unused_var_locations, 1);
-
- $project_analyzer = $this->getProjectAnalyzer();
-
- $unused_var_remover = new UnusedAssignmentRemover();
-
- if ($this->data_flow_graph instanceof VariableUseGraph
- && $codebase->config->limit_method_complexity
- && $source instanceof FunctionLikeAnalyzer
- && !$source instanceof ClosureAnalyzer
- && $function_storage
- && $function_storage->location
- ) {
- [$count, , $unique_destinations, $mean] = $this->data_flow_graph->getEdgeStats();
-
- $average_destination_branches_converging = $unique_destinations > 0 ? $count / $unique_destinations : 0;
-
- if ($count > $codebase->config->max_graph_size
- && $mean > $codebase->config->max_avg_path_length
- && $average_destination_branches_converging > 1.1
- ) {
- if ($source instanceof FunctionAnalyzer) {
- IssueBuffer::maybeAdd(
- new ComplexFunction(
- 'This function’s complexity is greater than the project limit'
- . ' (method graph size = ' . $count .', average path length = ' . round($mean). ')',
- $function_storage->location
- ),
- $this->getSuppressedIssues()
- );
- } elseif ($source instanceof MethodAnalyzer) {
- IssueBuffer::maybeAdd(
- new ComplexMethod(
- 'This method’s complexity is greater than the project limit'
- . ' (method graph size = ' . $count .', average path length = ' . round($mean) . ')',
- $function_storage->location
- ),
- $this->getSuppressedIssues()
- );
- }
- }
- }
-
- foreach ($this->unused_var_locations as [$var_id, $original_location]) {
- if (strpos($var_id, '$_') === 0) {
- continue;
- }
-
- if ($function_storage) {
- $param_index = array_search(substr($var_id, 1), array_keys($function_storage->param_lookup));
- if ($param_index !== false) {
- $param = $function_storage->params[$param_index];
-
- if ($param->location
- && ($original_location->raw_file_end === $param->location->raw_file_end
- || $param->by_ref)
- ) {
- continue;
- }
- }
- }
-
- $assignment_node = DataFlowNode::getForAssignment($var_id, $original_location);
-
- if (!isset($this->byref_uses[$var_id])
- && !isset($context->vars_from_global[$var_id])
- && !VariableFetchAnalyzer::isSuperGlobal($var_id)
- && $this->data_flow_graph instanceof VariableUseGraph
- && !$this->data_flow_graph->isVariableUsed($assignment_node)
- ) {
- $is_foreach_var = false;
-
- if (isset($this->foreach_var_locations[$var_id])) {
- foreach ($this->foreach_var_locations[$var_id] as $location) {
- if ($location->raw_file_start === $original_location->raw_file_start) {
- $is_foreach_var = true;
- break;
- }
- }
- }
-
- if ($is_foreach_var) {
- $issue = new UnusedForeachValue(
- $var_id . ' is never referenced or the value is not used',
- $original_location
- );
- } else {
- $issue = new UnusedVariable(
- $var_id . ' is never referenced or the value is not used',
- $original_location
- );
- }
-
- if ($codebase->alter_code
- && $issue instanceof UnusedVariable
- && !$unused_var_remover->checkIfVarRemoved($var_id, $original_location)
- && isset($project_analyzer->getIssuesToFix()['UnusedVariable'])
- && !IssueBuffer::isSuppressed($issue, $this->getSuppressedIssues())
- ) {
- $unused_var_remover->findUnusedAssignment(
- $this->getCodebase(),
- $stmts,
- array_combine($var_list, $loc_list),
- $var_id,
- $original_location
- );
- }
-
- IssueBuffer::maybeAdd(
- $issue,
- $this->getSuppressedIssues(),
- $issue instanceof UnusedVariable
- );
- }
- }
- }
-
- public function hasVariable(string $var_name): bool
- {
- return isset($this->all_vars[$var_name]);
- }
-
- public function registerVariable(string $var_id, CodeLocation $location, ?int $branch_point): void
- {
- $this->all_vars[$var_id] = $location;
-
- if ($branch_point) {
- $this->var_branch_points[$var_id] = $branch_point;
- }
-
- $this->registerVariableAssignment($var_id, $location);
- }
-
- public function registerVariableAssignment(string $var_id, CodeLocation $location): void
- {
- $this->unused_var_locations[$location->getHash()] = [$var_id, $location];
- }
-
- /**
- * @return array<string, array{0: string, 1: CodeLocation}>
- */
- public function getUnusedVarLocations(): array
- {
- return $this->unused_var_locations;
- }
-
- public function registerPossiblyUndefinedVariable(
- string $undefined_var_id,
- PhpParser\Node\Expr\Variable $stmt
- ): void {
- if (!$this->data_flow_graph) {
- return;
- }
-
- $use_location = new CodeLocation($this->getSource(), $stmt);
- $use_node = DataFlowNode::getForAssignment($undefined_var_id, $use_location);
-
- $stmt_type = $this->node_data->getType($stmt);
-
- if ($stmt_type) {
- $stmt_type->parent_nodes[$use_node->id] = $use_node;
- }
-
- foreach ($this->unused_var_locations as [$var_id, $original_location]) {
- if ($var_id === $undefined_var_id) {
- $parent_node = DataFlowNode::getForAssignment($var_id, $original_location);
-
- $this->data_flow_graph->addPath($parent_node, $use_node, '=');
- }
- }
- }
-
- /**
- * @return array<string, DataFlowNode>
- */
- public function getParentNodesForPossiblyUndefinedVariable(string $undefined_var_id): array
- {
- if (!$this->data_flow_graph) {
- return [];
- }
-
- $parent_nodes = [];
-
- foreach ($this->unused_var_locations as [$var_id, $original_location]) {
- if ($var_id === $undefined_var_id) {
- $assignment_node = DataFlowNode::getForAssignment($var_id, $original_location);
- $parent_nodes[$assignment_node->id] = $assignment_node;
- }
- }
-
- return $parent_nodes;
- }
-
- /**
- * The first appearance of the variable in this set of statements being evaluated
- */
- public function getFirstAppearance(string $var_id): ?CodeLocation
- {
- return $this->all_vars[$var_id] ?? null;
- }
-
- public function getBranchPoint(string $var_id): ?int
- {
- return $this->var_branch_points[$var_id] ?? null;
- }
-
- public function addVariableInitialization(string $var_id, int $branch_point): void
- {
- $this->vars_to_initialize[$var_id] = $branch_point;
- }
-
- public function getFileAnalyzer(): FileAnalyzer
- {
- return $this->file_analyzer;
- }
-
- public function getCodebase(): Codebase
- {
- return $this->codebase;
- }
-
- /**
- * @return array<string, FunctionAnalyzer>
- */
- public function getFunctionAnalyzers(): array
- {
- return $this->function_analyzers;
- }
-
- /**
- * @param array<string, bool> $byref_uses
- */
- public function setByRefUses(array $byref_uses): void
- {
- $this->byref_uses = $byref_uses;
- }
-
- /**
- * @return array<string, array<array-key, CodeLocation>>
- */
- public function getUncaughtThrows(Context $context): array
- {
- $uncaught_throws = [];
-
- if ($context->collect_exceptions) {
- if ($context->possibly_thrown_exceptions) {
- $config = $this->codebase->config;
- $ignored_exceptions = array_change_key_case(
- $context->is_global ?
- $config->ignored_exceptions_in_global_scope :
- $config->ignored_exceptions
- );
- $ignored_exceptions_and_descendants = array_change_key_case(
- $context->is_global ?
- $config->ignored_exceptions_and_descendants_in_global_scope :
- $config->ignored_exceptions_and_descendants
- );
-
- foreach ($context->possibly_thrown_exceptions as $possibly_thrown_exception => $codelocations) {
- if (isset($ignored_exceptions[strtolower($possibly_thrown_exception)])) {
- continue;
- }
-
- $is_expected = false;
-
- foreach ($ignored_exceptions_and_descendants as $expected_exception => $_) {
- try {
- if ($expected_exception === strtolower($possibly_thrown_exception)
- || $this->codebase->classExtends($possibly_thrown_exception, $expected_exception)
- || $this->codebase->interfaceExtends($possibly_thrown_exception, $expected_exception)
- ) {
- $is_expected = true;
- break;
- }
- } catch (InvalidArgumentException $e) {
- $is_expected = true;
- break;
- }
- }
-
- if (!$is_expected) {
- $uncaught_throws[$possibly_thrown_exception] = $codelocations;
- }
- }
- }
- }
-
- return $uncaught_throws;
- }
-
- public function getFunctionAnalyzer(string $function_id): ?FunctionAnalyzer
- {
- return $this->function_analyzers[$function_id] ?? null;
- }
-
- public function getParsedDocblock(): ?ParsedDocblock
- {
- return $this->parsed_docblock;
- }
-
- public function getFQCLN(): ?string
- {
- if ($this->fake_this_class) {
- return $this->fake_this_class;
- }
-
- return parent::getFQCLN();
- }
-
- public function setFQCLN(string $fake_this_class): void
- {
- $this->fake_this_class = $fake_this_class;
- }
-
- /**
- * @return NodeDataProvider
- */
- public function getNodeTypeProvider(): NodeTypeProvider
- {
- return $this->node_data;
- }
-
- public function getFullyQualifiedFunctionMethodOrNamespaceName(): ?string
- {
- if ($this->source instanceof MethodAnalyzer) {
- $fqcn = $this->getFQCLN();
- $method_name = $this->source->getFunctionLikeStorage($this)->cased_name;
- assert($fqcn !== null && $method_name !== null);
-
- return "$fqcn::$method_name";
- }
-
- if ($this->source instanceof FunctionAnalyzer) {
- $namespace = $this->getNamespace();
- $namespace = $namespace === "" ? "" : "$namespace\\";
- $function_name = $this->source->getFunctionLikeStorage($this)->cased_name;
- assert($function_name !== null);
-
- return "{$namespace}{$function_name}";
- }
-
- return $this->getNamespace();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TraitAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TraitAnalyzer.php
deleted file mode 100644
index 1260c34..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TraitAnalyzer.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use PhpParser\Node\Stmt\Trait_;
-use Psalm\Aliases;
-use Psalm\Context;
-
-use function assert;
-
-/**
- * @internal
- */
-class TraitAnalyzer extends ClassLikeAnalyzer
-{
- /**
- * @var Aliases
- */
- private $aliases;
-
- public function __construct(
- Trait_ $class,
- SourceAnalyzer $source,
- string $fq_class_name,
- Aliases $aliases
- ) {
- $this->source = $source;
- $this->file_analyzer = $source->getFileAnalyzer();
- $this->aliases = $source->getAliases();
- $this->class = $class;
- $this->fq_class_name = $fq_class_name;
- $codebase = $source->getCodebase();
- $this->storage = $codebase->classlike_storage_provider->get($fq_class_name);
- $this->aliases = $aliases;
- }
-
- public function getNamespace(): ?string
- {
- return $this->aliases->namespace;
- }
-
- public function getAliases(): Aliases
- {
- return $this->aliases;
- }
-
- /**
- * @return array<lowercase-string, string>
- */
- public function getAliasedClassesFlipped(): array
- {
- return [];
- }
-
- /**
- * @return array<string, string>
- */
- public function getAliasedClassesFlippedReplaceable(): array
- {
- return [];
- }
-
- public static function analyze(StatementsAnalyzer $statements_analyzer, Trait_ $stmt, Context $context): void
- {
- assert($stmt->name !== null);
- $storage = $statements_analyzer->getCodebase()->classlike_storage_provider->get($stmt->name->name);
- AttributesAnalyzer::analyze(
- $statements_analyzer,
- $context,
- $storage,
- $stmt->attrGroups,
- AttributesAnalyzer::TARGET_CLASS,
- $storage->suppressed_issues + $statements_analyzer->getSuppressedIssues()
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php
deleted file mode 100644
index 2a8451c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/TypeAnalyzer.php
+++ /dev/null
@@ -1,118 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Analyzer;
-
-use Psalm\Codebase;
-use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Union;
-
-use function array_keys;
-use function array_merge;
-use function array_unique;
-
-/**
- * @internal
- */
-class TypeAnalyzer
-{
- /**
- * Does the input param type match the given param type
- *
- * @deprecated in favour of UnionTypeComparator, going to be removed in Psalm 5
- * @psalm-suppress PossiblyUnusedMethod
- */
- public static function isContainedBy(
- Codebase $codebase,
- Union $input_type,
- Union $container_type,
- bool $ignore_null = false,
- bool $ignore_false = false,
- ?TypeComparisonResult $union_comparison_result = null,
- bool $allow_interface_equality = false
- ): bool {
- return UnionTypeComparator::isContainedBy(
- $codebase,
- $input_type,
- $container_type,
- $ignore_null,
- $ignore_false,
- $union_comparison_result,
- $allow_interface_equality
- );
- }
-
- /**
- * Does the input param atomic type match the given param atomic type
- *
- * @deprecated in favour of AtomicTypeComparator, going to be removed in Psalm 5
- * @psalm-suppress PossiblyUnusedMethod
- */
- public static function isAtomicContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- bool $allow_interface_equality = false,
- bool $allow_float_int_equality = true,
- ?TypeComparisonResult $atomic_comparison_result = null
- ): bool {
- return AtomicTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- );
- }
-
- /**
- * Takes two arrays of types and merges them
- *
- * @param array<string, Union> $new_types
- * @param array<string, Union> $existing_types
- *
- * @return array<string, Union>
- */
- public static function combineKeyedTypes(array $new_types, array $existing_types): array
- {
- $keys = array_merge(array_keys($new_types), array_keys($existing_types));
- $keys = array_unique($keys);
-
- $result_types = [];
-
- if (empty($new_types)) {
- return $existing_types;
- }
-
- if (empty($existing_types)) {
- return $new_types;
- }
-
- foreach ($keys as $key) {
- if (!isset($existing_types[$key])) {
- $result_types[$key] = $new_types[$key];
- continue;
- }
-
- if (!isset($new_types[$key])) {
- $result_types[$key] = $existing_types[$key];
- continue;
- }
-
- $existing_var_types = $existing_types[$key];
- $new_var_types = $new_types[$key];
-
- if ($new_var_types->getId() === $existing_var_types->getId()) {
- $result_types[$key] = $new_var_types;
- } else {
- $result_types[$key] = Type::combineUnionTypes($new_var_types, $existing_var_types);
- }
- }
-
- return $result_types;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Clause.php b/vendor/vimeo/psalm/src/Psalm/Internal/Clause.php
deleted file mode 100644
index 7996923..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Clause.php
+++ /dev/null
@@ -1,295 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use Psalm\Internal\Algebra;
-
-use function array_diff;
-use function array_keys;
-use function array_map;
-use function array_unique;
-use function array_values;
-use function count;
-use function hash;
-use function implode;
-use function ksort;
-use function reset;
-use function serialize;
-use function sort;
-use function strpos;
-use function substr;
-
-use const PHP_VERSION_ID;
-
-/**
- * @internal
- *
- * @psalm-immutable
- */
-class Clause
-{
- /** @var int */
- public $creating_conditional_id;
-
- /** @var int */
- public $creating_object_id;
-
- /**
- * An array of strings of the form
- * [
- * '$a' => ['falsy'],
- * '$b' => ['!falsy'],
- * '$c' => ['!null'],
- * '$d' => ['string', 'int']
- * ]
- *
- * representing the formula
- *
- * !$a || $b || $c !== null || is_string($d) || is_int($d)
- *
- * @var array<string, non-empty-list<string>>
- */
- public $possibilities;
-
- /**
- * An array of things that are not true
- * [
- * '$a' => ['!falsy'],
- * '$b' => ['falsy'],
- * '$c' => ['null'],
- * '$d' => ['!string', '!int']
- * ]
- * represents the formula
- *
- * $a && !$b && $c === null && !is_string($d) && !is_int($d)
- *
- * @var array<string, non-empty-list<string>>|null
- */
- public $impossibilities;
-
- /** @var bool */
- public $wedge;
-
- /** @var bool */
- public $reconcilable;
-
- /** @var bool */
- public $generated = false;
-
- /** @var array<string, bool> */
- public $redefined_vars = [];
-
- /** @var string|int */
- public $hash;
-
- /**
- * @param array<string, non-empty-list<string>> $possibilities
- * @param array<string, bool> $redefined_vars
- */
- public function __construct(
- array $possibilities,
- int $creating_conditional_id,
- int $creating_object_id,
- bool $wedge = false,
- bool $reconcilable = true,
- bool $generated = false,
- array $redefined_vars = []
- ) {
- $this->possibilities = $possibilities;
- $this->wedge = $wedge;
- $this->reconcilable = $reconcilable;
- $this->generated = $generated;
- $this->redefined_vars = $redefined_vars;
- $this->creating_conditional_id = $creating_conditional_id;
- $this->creating_object_id = $creating_object_id;
-
- if ($wedge || !$reconcilable) {
- $this->hash = ($wedge ? 'w' : '') . $creating_object_id;
- } else {
- ksort($possibilities);
-
- foreach ($possibilities as $i => $v) {
- if (count($v) < 2) {
- continue;
- }
- sort($possibilities[$i]);
- }
-
- $data = serialize($possibilities);
- $this->hash = PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data);
- }
- }
-
- public function contains(Clause $other_clause): bool
- {
- if (count($other_clause->possibilities) > count($this->possibilities)) {
- return false;
- }
-
- foreach ($other_clause->possibilities as $var => $_) {
- if (!isset($this->possibilities[$var])) {
- return false;
- }
- }
-
- foreach ($other_clause->possibilities as $var => $possible_types) {
- if (count(array_diff($possible_types, $this->possibilities[$var]))) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @psalm-mutation-free
- */
- public function __toString(): string
- {
- $clause_strings = array_map(
- /**
- * @param non-empty-list<string> $values
- */
- function (string $var_id, array $values): string {
- if ($var_id[0] === '*') {
- $var_id = '<expr>';
- }
-
- $var_id_clauses = array_map(
- function (string $value) use ($var_id): string {
- if ($value === 'falsy') {
- return '!' . $var_id;
- }
-
- if ($value === '!falsy') {
- return $var_id;
- }
-
- $negate = false;
-
- if ($value[0] === '!') {
- $negate = true;
- $value = substr($value, 1);
- }
-
- if ($value[0] === '=') {
- $value = substr($value, 1);
- }
-
- if ($negate) {
- return $var_id . ' is not ' . $value;
- }
-
- return $var_id . ' is ' . $value;
- },
- $values
- );
-
- if (count($var_id_clauses) > 1) {
- return '(' . implode(') || (', $var_id_clauses) . ')';
- }
-
- return $var_id_clauses[0];
- },
- array_keys($this->possibilities),
- array_values($this->possibilities)
- );
-
- if (count($clause_strings) > 1) {
- return '(' . implode(') || (', $clause_strings) . ')';
- }
-
- return reset($clause_strings);
- }
-
- public function makeUnique(): self
- {
- $possibilities = $this->possibilities;
-
- foreach ($possibilities as $var_id => $var_possibilities) {
- $possibilities[$var_id] = array_values(array_unique($var_possibilities));
- }
-
- return new self(
- $possibilities,
- $this->creating_conditional_id,
- $this->creating_object_id,
- $this->wedge,
- $this->reconcilable,
- $this->generated,
- $this->redefined_vars
- );
- }
-
- public function removePossibilities(string $var_id): ?self
- {
- $possibilities = $this->possibilities;
- unset($possibilities[$var_id]);
-
- if (!$possibilities) {
- return null;
- }
-
- return new self(
- $possibilities,
- $this->creating_conditional_id,
- $this->creating_object_id,
- $this->wedge,
- $this->reconcilable,
- $this->generated,
- $this->redefined_vars
- );
- }
-
- /**
- * @param non-empty-list<string> $clause_var_possibilities
- */
- public function addPossibilities(string $var_id, array $clause_var_possibilities): self
- {
- $possibilities = $this->possibilities;
- $possibilities[$var_id] = $clause_var_possibilities;
-
- return new self(
- $possibilities,
- $this->creating_conditional_id,
- $this->creating_object_id,
- $this->wedge,
- $this->reconcilable,
- $this->generated,
- $this->redefined_vars
- );
- }
-
- public function calculateNegation(): self
- {
- if ($this->impossibilities !== null) {
- return $this;
- }
-
- $impossibilities = [];
-
- foreach ($this->possibilities as $var_id => $possibility) {
- $impossibility = [];
-
- foreach ($possibility as $type) {
- if (($type[0] !== '=' && $type[0] !== '~'
- && (!isset($type[1]) || ($type[1] !== '=' && $type[1] !== '~')))
- || strpos($type, '(')
- || strpos($type, 'getclass-')
- ) {
- $impossibility[] = Algebra::negateType($type);
- }
- }
-
- if ($impossibility) {
- $impossibilities[$var_id] = $impossibility;
- }
- }
-
- $clause = clone $this;
-
- $clause->impossibilities = $impossibilities;
-
- return $clause;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php
deleted file mode 100644
index e8689ee..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php
+++ /dev/null
@@ -1,323 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Cli;
-
-use Psalm\Config;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\CliUtils;
-use Psalm\Internal\Composer;
-use Psalm\Internal\ErrorHandler;
-use Psalm\Internal\Fork\PsalmRestarter;
-use Psalm\Internal\IncludeCollector;
-use Psalm\Internal\Provider\ClassLikeStorageCacheProvider;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileReferenceCacheProvider;
-use Psalm\Internal\Provider\FileStorageCacheProvider;
-use Psalm\Internal\Provider\ParserCacheProvider;
-use Psalm\Internal\Provider\ProjectCacheProvider;
-use Psalm\Internal\Provider\Providers;
-use Psalm\Report;
-
-use function array_key_exists;
-use function array_map;
-use function array_search;
-use function array_slice;
-use function chdir;
-use function error_log;
-use function fwrite;
-use function gc_disable;
-use function getcwd;
-use function getopt;
-use function implode;
-use function in_array;
-use function ini_set;
-use function is_array;
-use function is_string;
-use function preg_replace;
-use function realpath;
-use function setlocale;
-use function strpos;
-use function strtolower;
-use function substr;
-
-use const DIRECTORY_SEPARATOR;
-use const LC_CTYPE;
-use const PHP_EOL;
-use const STDERR;
-
-// phpcs:disable PSR1.Files.SideEffects
-
-require_once __DIR__ . '/../ErrorHandler.php';
-require_once __DIR__ . '/../CliUtils.php';
-require_once __DIR__ . '/../Composer.php';
-require_once __DIR__ . '/../IncludeCollector.php';
-
-final class LanguageServer
-{
- /** @param array<int,string> $argv */
- public static function run(array $argv): void
- {
- gc_disable();
- ErrorHandler::install();
- $valid_short_options = [
- 'h',
- 'v',
- 'c:',
- 'r:',
- ];
-
- $valid_long_options = [
- 'clear-cache',
- 'config:',
- 'find-dead-code',
- 'help',
- 'root:',
- 'use-ini-defaults',
- 'version',
- 'tcp:',
- 'tcp-server',
- 'disable-on-change::',
- 'enable-autocomplete::',
- 'use-extended-diagnostic-codes',
- 'verbose'
- ];
-
- $args = array_slice($argv, 1);
-
- $psalm_proxy = array_search('--language-server', $args, true);
-
- if ($psalm_proxy !== false) {
- unset($args[$psalm_proxy]);
- }
-
- array_map(
- function (string $arg) use ($valid_long_options): void {
- if (strpos($arg, '--') === 0 && $arg !== '--') {
- $arg_name = preg_replace('/=.*$/', '', substr($arg, 2));
-
- if (!in_array($arg_name, $valid_long_options, true)
- && !in_array($arg_name . ':', $valid_long_options, true)
- && !in_array($arg_name . '::', $valid_long_options, true)
- ) {
- fwrite(
- STDERR,
- 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL
- . 'Type --help to see a list of supported arguments' . PHP_EOL
- );
- error_log('Bad argument');
- exit(1);
- }
- }
- },
- $args
- );
-
- // get options from command line
- $options = getopt(implode('', $valid_short_options), $valid_long_options);
-
- if (!array_key_exists('use-ini-defaults', $options)) {
- ini_set('display_errors', '1');
- ini_set('display_startup_errors', '1');
- ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024));
- }
-
- if (array_key_exists('help', $options)) {
- $options['h'] = false;
- }
-
- if (array_key_exists('version', $options)) {
- $options['v'] = false;
- }
-
- if (isset($options['config'])) {
- $options['c'] = $options['config'];
- }
-
- if (isset($options['c']) && is_array($options['c'])) {
- fwrite(STDERR, 'Too many config files provided' . PHP_EOL);
- exit(1);
- }
-
- if (array_key_exists('h', $options)) {
- echo <<<HELP
-Usage:
- psalm-language-server [options]
-
-Options:
- -h, --help
- Display this help message
-
- -v, --version
- Display the Psalm version
-
- -c, --config=psalm.xml
- Path to a psalm.xml configuration file. Run psalm --init to create one.
-
- -r, --root
- If running Psalm globally you'll need to specify a project root. Defaults to cwd
-
- --find-dead-code
- Look for dead code
-
- --clear-cache
- Clears all cache files that the language server uses for this specific project
-
- --use-ini-defaults
- Use PHP-provided ini defaults for memory and error display
-
- --tcp=url
- Use TCP mode (by default Psalm uses STDIO)
-
- --tcp-server
- Use TCP in server mode (default is client)
-
- --disable-on-change[=line-number-threshold]
- If added, the language server will not respond to onChange events.
- You can also specify a line count over which Psalm will not run on-change events.
-
- --enable-autocomplete[=BOOL]
- Enables or disables autocomplete on methods and properties. Default is true.
-
- --use-extended-diagnostic-codes
- Enables sending help uri links with the code in diagnostic messages.
-
- --verbose
- Will send log messages to the client with information.
-
-HELP;
-
- exit;
- }
-
- if (getcwd() === false) {
- fwrite(STDERR, 'Cannot get current working directory' . PHP_EOL);
- exit(1);
- }
-
- if (isset($options['root'])) {
- $options['r'] = $options['root'];
- }
-
- $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR;
-
- if (isset($options['r']) && is_string($options['r'])) {
- $root_path = realpath($options['r']);
-
- if (!$root_path) {
- fwrite(
- STDERR,
- 'Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL
- );
- exit(1);
- }
-
- $current_dir = $root_path . DIRECTORY_SEPARATOR;
- }
-
- $vendor_dir = CliUtils::getVendorDir($current_dir);
-
- $include_collector = new IncludeCollector();
-
- $first_autoloader = $include_collector->runAndCollect(
- // we ignore the FQN because of a hack in scoper.inc that needs full path
- // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName
- function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader {
- return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir);
- }
- );
-
- if (array_key_exists('v', $options)) {
- echo 'Psalm ' . PSALM_VERSION . PHP_EOL;
- exit;
- }
-
- $ini_handler = new PsalmRestarter('PSALM');
-
- $ini_handler->disableExtension('grpc');
-
- // If Xdebug is enabled, restart without it
- $ini_handler->check();
-
- setlocale(LC_CTYPE, 'C');
-
- $path_to_config = CliUtils::getPathToConfig($options);
-
- if (isset($options['tcp'])) {
- if (!is_string($options['tcp'])) {
- fwrite(STDERR, 'tcp url should be a string' . PHP_EOL);
- exit(1);
- }
- }
-
- $find_unused_code = isset($options['find-dead-code']) ? 'auto' : null;
-
- $config = CliUtils::initializeConfig(
- $path_to_config,
- $current_dir,
- Report::TYPE_CONSOLE,
- $first_autoloader
- );
- $config->setIncludeCollector($include_collector);
-
- if ($config->resolve_from_config_file) {
- $current_dir = $config->base_dir;
- chdir($current_dir);
- }
-
- $config->setServerMode();
-
- if (isset($options['clear-cache'])) {
- $cache_directory = $config->getCacheDirectory();
-
- if ($cache_directory !== null) {
- Config::removeCacheDirectory($cache_directory);
- }
- echo 'Cache directory deleted' . PHP_EOL;
- exit;
- }
-
- $providers = new Providers(
- new FileProvider,
- new ParserCacheProvider($config),
- new FileStorageCacheProvider($config),
- new ClassLikeStorageCacheProvider($config),
- new FileReferenceCacheProvider($config),
- new ProjectCacheProvider(Composer::getLockFilePath($current_dir))
- );
-
- $project_analyzer = new ProjectAnalyzer(
- $config,
- $providers
- );
-
- if ($config->find_unused_variables) {
- $project_analyzer->getCodebase()->reportUnusedVariables();
- }
-
- if ($config->find_unused_code) {
- $find_unused_code = 'auto';
- }
-
- if (isset($options['disable-on-change'])) {
- $project_analyzer->onchange_line_limit = (int) $options['disable-on-change'];
- }
-
- $project_analyzer->provide_completion = !isset($options['enable-autocomplete'])
- || !is_string($options['enable-autocomplete'])
- || strtolower($options['enable-autocomplete']) !== 'false';
-
- if ($find_unused_code) {
- $project_analyzer->getCodebase()->reportUnusedCode($find_unused_code);
- }
-
- if (isset($options['use-extended-diagnostic-codes'])) {
- $project_analyzer->language_server_use_extended_diagnostic_codes = true;
- }
-
- if (isset($options['verbose'])) {
- $project_analyzer->language_server_verbose = true;
- }
-
- $project_analyzer->server($options['tcp'] ?? null, isset($options['tcp-server']));
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Plugin.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Plugin.php
deleted file mode 100644
index 5a89185..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Plugin.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Cli;
-
-use PackageVersions\Versions;
-use Psalm\Internal\CliUtils;
-use Psalm\Internal\PluginManager\Command\DisableCommand;
-use Psalm\Internal\PluginManager\Command\EnableCommand;
-use Psalm\Internal\PluginManager\Command\ShowCommand;
-use Psalm\Internal\PluginManager\PluginListFactory;
-use Symfony\Component\Console\Application;
-
-use function dirname;
-use function getcwd;
-
-use const DIRECTORY_SEPARATOR;
-
-// phpcs:disable PSR1.Files.SideEffects
-
-require_once __DIR__ . '/../CliUtils.php';
-require_once __DIR__ . '/../Composer.php';
-
-final class Plugin
-{
- public static function run(): void
- {
- $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR;
- $vendor_dir = CliUtils::getVendorDir($current_dir);
- CliUtils::requireAutoloaders($current_dir, false, $vendor_dir);
-
- $app = new Application('psalm-plugin', Versions::getVersion('vimeo/psalm'));
-
- $psalm_root = dirname(__DIR__, 4) . DIRECTORY_SEPARATOR;
-
- $plugin_list_factory = new PluginListFactory($current_dir, $psalm_root);
-
- $app->addCommands([
- new ShowCommand($plugin_list_factory),
- new EnableCommand($plugin_list_factory),
- new DisableCommand($plugin_list_factory),
- ]);
-
- $app->setDefaultCommand('show');
- $app->run();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php
deleted file mode 100644
index 86031b7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php
+++ /dev/null
@@ -1,1176 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Cli;
-
-use Composer\Autoload\ClassLoader;
-use Psalm\Config;
-use Psalm\Config\Creator;
-use Psalm\ErrorBaseline;
-use Psalm\Exception\ConfigCreationException;
-use Psalm\Exception\ConfigException;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\CliUtils;
-use Psalm\Internal\Codebase\ReferenceMapGenerator;
-use Psalm\Internal\Composer;
-use Psalm\Internal\ErrorHandler;
-use Psalm\Internal\Fork\Pool;
-use Psalm\Internal\Fork\PsalmRestarter;
-use Psalm\Internal\IncludeCollector;
-use Psalm\Internal\Provider\ClassLikeStorageCacheProvider;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileReferenceCacheProvider;
-use Psalm\Internal\Provider\FileStorageCacheProvider;
-use Psalm\Internal\Provider\ParserCacheProvider;
-use Psalm\Internal\Provider\ProjectCacheProvider;
-use Psalm\Internal\Provider\Providers;
-use Psalm\Internal\Stubs\Generator\StubsGenerator;
-use Psalm\IssueBuffer;
-use Psalm\Progress\DebugProgress;
-use Psalm\Progress\DefaultProgress;
-use Psalm\Progress\LongProgress;
-use Psalm\Progress\Progress;
-use Psalm\Progress\VoidProgress;
-use Psalm\Report;
-use Psalm\Report\ReportOptions;
-use RuntimeException;
-use Webmozart\PathUtil\Path;
-
-use function array_filter;
-use function array_key_exists;
-use function array_map;
-use function array_merge;
-use function array_slice;
-use function array_sum;
-use function array_values;
-use function chdir;
-use function count;
-use function file_exists;
-use function file_put_contents;
-use function fwrite;
-use function gc_collect_cycles;
-use function gc_disable;
-use function getcwd;
-use function getenv;
-use function getopt;
-use function implode;
-use function in_array;
-use function ini_get;
-use function ini_set;
-use function is_array;
-use function is_numeric;
-use function is_scalar;
-use function is_string;
-use function json_encode;
-use function max;
-use function microtime;
-use function preg_match;
-use function preg_replace;
-use function realpath;
-use function setlocale;
-use function str_repeat;
-use function strpos;
-use function substr;
-use function version_compare;
-
-use const DIRECTORY_SEPARATOR;
-use const LC_CTYPE;
-use const PHP_EOL;
-use const PHP_OS;
-use const PHP_VERSION;
-use const STDERR;
-
-// phpcs:disable PSR1.Files.SideEffects
-
-require_once __DIR__ . '/../ErrorHandler.php';
-require_once __DIR__ . '/../CliUtils.php';
-require_once __DIR__ . '/../Composer.php';
-require_once __DIR__ . '/../IncludeCollector.php';
-require_once __DIR__ . '/../../IssueBuffer.php';
-
-final class Psalm
-{
- private const SHORT_OPTIONS = [
- 'f:',
- 'm',
- 'h',
- 'v',
- 'c:',
- 'i',
- 'r:',
- ];
-
- private const LONG_OPTIONS = [
- 'clear-cache',
- 'clear-global-cache',
- 'config:',
- 'debug',
- 'debug-by-line',
- 'debug-performance',
- 'debug-emitted-issues',
- 'diff',
- 'disable-extension:',
- 'find-dead-code::',
- 'find-unused-code::',
- 'find-unused-variables',
- 'find-references-to:',
- 'help',
- 'ignore-baseline',
- 'init',
- 'memory-limit:',
- 'monochrome',
- 'no-diff',
- 'no-cache',
- 'no-reflection-cache',
- 'no-file-cache',
- 'output-format:',
- 'plugin:',
- 'report:',
- 'report-show-info:',
- 'root:',
- 'set-baseline:',
- 'show-info:',
- 'show-snippet:',
- 'stats',
- 'threads:',
- 'update-baseline',
- 'use-baseline:',
- 'use-ini-defaults',
- 'version',
- 'php-version:',
- 'generate-json-map:',
- 'generate-stubs:',
- 'alter',
- 'language-server',
- 'refactor',
- 'shepherd::',
- 'no-progress',
- 'long-progress',
- 'no-suggestions',
- 'include-php-versions', // used for baseline
- 'pretty-print', // used for JSON reports
- 'track-tainted-input',
- 'taint-analysis',
- 'security-analysis',
- 'dump-taint-graph:',
- 'find-unused-psalm-suppress',
- 'error-level:',
- ];
-
- /**
- * @param array<int,string> $argv
- */
- public static function run(array $argv): void
- {
- gc_collect_cycles();
- gc_disable();
-
- ErrorHandler::install();
-
- $args = array_slice($argv, 1);
-
- // get options from command line
- $options = getopt(implode('', self::SHORT_OPTIONS), self::LONG_OPTIONS);
- if (false === $options) {
- throw new RuntimeException('Failed to parse CLI options');
- }
-
- self::forwardCliCall($options, $argv);
-
- self::validateCliArguments($args);
-
- self::setMemoryLimit($options);
-
- self::syncShortOptions($options);
-
- if (isset($options['c']) && is_array($options['c'])) {
- fwrite(STDERR, 'Too many config files provided' . PHP_EOL);
- exit(1);
- }
-
-
- if (array_key_exists('h', $options)) {
- echo CliUtils::getPsalmHelpText();
- /*
- --shepherd[=host]
- Send data to Shepherd, Psalm's GitHub integration tool.
- `host` is the location of the Shepherd server. It defaults to shepherd.dev
- More information is available at https://psalm.dev/shepherd
- */
-
- exit;
- }
-
- $current_dir = self::getCurrentDir($options);
-
- $path_to_config = CliUtils::getPathToConfig($options);
-
- $vendor_dir = CliUtils::getVendorDir($current_dir);
-
- // capture environment before registering autoloader (it may destroy it)
- IssueBuffer::captureServer($_SERVER);
-
- $include_collector = new IncludeCollector();
- $first_autoloader = $include_collector->runAndCollect(
- // we ignore the FQN because of a hack in scoper.inc that needs full path
- // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName
- function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader {
- return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir);
- }
- );
-
- $run_taint_analysis = self::shouldRunTaintAnalysis($options);
-
- if (array_key_exists('v', $options)) {
- echo 'Psalm ' . PSALM_VERSION . PHP_EOL;
- exit;
- }
-
- $output_format = self::initOutputFormat($options);
-
- [$config, $init_source_dir] = self::initConfig(
- $current_dir,
- $args,
- $vendor_dir,
- $first_autoloader,
- $path_to_config,
- $output_format,
- $run_taint_analysis,
- $options
- );
-
- $config->setIncludeCollector($include_collector);
-
- $in_ci = CliUtils::runningInCI(); // disable progressbar on CI
-
- if ($in_ci) {
- $options['long-progress'] = true;
- }
-
- $threads = self::detectThreads($options, $config, $in_ci);
-
- self::emitMacPcreWarning($options, $threads);
-
- self::restart($options, $config, $threads);
-
- if (isset($options['debug-emitted-issues'])) {
- $config->debug_emitted_issues = true;
- }
-
-
- setlocale(LC_CTYPE, 'C');
-
- if (isset($options['set-baseline'])) {
- if (is_array($options['set-baseline'])) {
- die('Only one baseline file can be created at a time' . PHP_EOL);
- }
- }
-
- $paths_to_check = CliUtils::getPathsToCheck($options['f'] ?? null);
-
- if ($config->resolve_from_config_file) {
- $current_dir = $config->base_dir;
- chdir($current_dir);
- }
-
- $plugins = [];
-
- if (isset($options['plugin'])) {
- $plugins = $options['plugin'];
-
- if (!is_array($plugins)) {
- $plugins = [$plugins];
- }
- }
-
- $show_info = self::initShowInfo($options);
-
- $is_diff = self::initIsDiff($options);
-
- $find_unused_code = self::shouldFindUnusedCode($options, $config);
-
- $find_unused_variables = isset($options['find-unused-variables']);
-
- $find_references_to = isset($options['find-references-to']) && is_string($options['find-references-to'])
- ? $options['find-references-to']
- : null;
-
- if (isset($options['shepherd']) || getenv('PSALM_SHEPHERD')) {
- if (isset($options['shepherd'])) {
- if (is_string($options['shepherd'])) {
- $config->shepherd_host = $options['shepherd'];
- }
- } elseif (getenv('PSALM_SHEPHERD')) {
- if (false !== ($shepherd_host = getenv('PSALM_SHEPHERD_HOST'))) {
- $config->shepherd_host = $shepherd_host;
- }
- }
- $shepherd_plugin = Path::canonicalize(__DIR__ . '/../../Plugin/Shepherd.php');
-
- if (!file_exists($shepherd_plugin)) {
- die('Could not find Shepherd plugin location ' . $shepherd_plugin . PHP_EOL);
- }
-
- $plugins[] = $shepherd_plugin;
- }
-
- if (isset($options['clear-cache'])) {
- self::clearCache($config);
- }
-
- if (isset($options['clear-global-cache'])) {
- self::clearGlobalCache($config);
- }
-
- $progress = self::initProgress($options, $config);
- $providers = self::initProviders($options, $config, $current_dir);
-
- $stdout_report_options = self::initStdoutReportOptions($options, $show_info, $output_format, $in_ci);
-
- /** @var list<string>|string $report_file_paths type guaranteed by argument to getopt() */
- $report_file_paths = $options['report'] ?? [];
- if (is_string($report_file_paths)) {
- $report_file_paths = [$report_file_paths];
- }
-
- $project_analyzer = new ProjectAnalyzer(
- $config,
- $providers,
- $stdout_report_options,
- ProjectAnalyzer::getFileReportOptions(
- $report_file_paths,
- isset($options['report-show-info'])
- ? $options['report-show-info'] !== 'false' && $options['report-show-info'] !== '0'
- : true
- ),
- $threads,
- $progress
- );
-
- CliUtils::initPhpVersion($options, $config, $project_analyzer);
-
- $start_time = microtime(true);
-
- self::configureProjectAnalyzer(
- $options,
- $config,
- $project_analyzer,
- $find_references_to,
- $find_unused_code,
- $find_unused_variables,
- $run_taint_analysis
- );
-
- if ($config->run_taint_analysis || $run_taint_analysis) {
- $is_diff = false;
- }
-
- /** @var string $plugin_path */
- foreach ($plugins as $plugin_path) {
- $config->addPluginPath($plugin_path);
- }
-
- if ($paths_to_check === null) {
- $project_analyzer->check($current_dir, $is_diff);
- } elseif ($paths_to_check) {
- $project_analyzer->checkPaths($paths_to_check);
- }
-
- if ($find_references_to) {
- $project_analyzer->findReferencesTo($find_references_to);
- }
-
- self::storeFlowGraph($options, $project_analyzer);
-
- if (isset($options['generate-json-map']) && is_string($options['generate-json-map'])) {
- self::storeTypeMap($providers, $config, $options['generate-json-map']);
- }
-
- if (isset($options['generate-stubs'])) {
- self::generateStubs($options, $providers, $project_analyzer);
- }
-
- if (!isset($options['i'])) {
- IssueBuffer::finish(
- $project_analyzer,
- !$paths_to_check,
- $start_time,
- isset($options['stats']),
- self::initBaseline($options, $config, $current_dir, $path_to_config)
- );
- } else {
- self::autoGenerateConfig($project_analyzer, $current_dir, $init_source_dir, $vendor_dir);
- }
- }
-
- private static function initOutputFormat(array $options): string
- {
- return isset($options['output-format']) && is_string($options['output-format'])
- ? $options['output-format']
- : Report::TYPE_CONSOLE;
- }
-
- private static function initShowInfo(array $options): bool
- {
- return isset($options['show-info'])
- ? $options['show-info'] === 'true' || $options['show-info'] === '1'
- : false;
- }
-
- private static function initIsDiff(array $options): bool
- {
- return !isset($options['no-diff'])
- && !isset($options['set-baseline'])
- && !isset($options['update-baseline']);
- }
-
- /**
- * @param array<int,string> $args
- */
- private static function validateCliArguments(array $args): void
- {
- array_map(
- function (string $arg): void {
- if (strpos($arg, '--') === 0 && $arg !== '--') {
- $arg_name = preg_replace('/=.*$/', '', substr($arg, 2));
-
- if (!in_array($arg_name, self::LONG_OPTIONS)
- && !in_array($arg_name . ':', self::LONG_OPTIONS)
- && !in_array($arg_name . '::', self::LONG_OPTIONS)
- ) {
- fwrite(
- STDERR,
- 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL
- . 'Type --help to see a list of supported arguments'. PHP_EOL
- );
- exit(1);
- }
- } elseif (strpos($arg, '-') === 0 && $arg !== '-' && $arg !== '--') {
- $arg_name = preg_replace('/=.*$/', '', substr($arg, 1));
-
- if (!in_array($arg_name, self::SHORT_OPTIONS)
- && !in_array($arg_name . ':', self::SHORT_OPTIONS)
- ) {
- fwrite(
- STDERR,
- 'Unrecognised argument "-' . $arg_name . '"' . PHP_EOL
- . 'Type --help to see a list of supported arguments'. PHP_EOL
- );
- exit(1);
- }
- }
- },
- $args
- );
- }
-
- /**
- * @param array<string,string|false|list<mixed>> $options
- */
- private static function setMemoryLimit(array $options): void
- {
- if (!array_key_exists('use-ini-defaults', $options)) {
- ini_set('display_errors', 'stderr');
- ini_set('display_startup_errors', '1');
-
- $memoryLimit = (8 * 1024 * 1024 * 1024);
-
- if (array_key_exists('memory-limit', $options)) {
- $memoryLimit = $options['memory-limit'];
-
- if (!is_scalar($memoryLimit)) {
- throw new ConfigException('Invalid memory limit specified.');
- }
- }
-
- ini_set('memory_limit', (string) $memoryLimit);
- }
- }
-
- /**
- * @param array<int, string> $args
- */
- private static function generateConfig(string $current_dir, array &$args): void
- {
- if (file_exists($current_dir . 'psalm.xml')) {
- die('A config file already exists in the current directory' . PHP_EOL);
- }
-
- $args = array_values(array_filter(
- $args,
- function (string $arg): bool {
- return $arg !== '--ansi'
- && $arg !== '--no-ansi'
- && $arg !== '-i'
- && $arg !== '--init'
- && $arg !== '--debug'
- && $arg !== '--debug-by-line'
- && $arg !== '--debug-emitted-issues'
- && strpos($arg, '--disable-extension=') !== 0
- && strpos($arg, '--root=') !== 0
- && strpos($arg, '--r=') !== 0;
- }
- ));
-
- $init_level = null;
- $init_source_dir = null;
- if (count($args)) {
- if (count($args) > 2) {
- die('Too many arguments provided for psalm --init' . PHP_EOL);
- }
-
- if (isset($args[1])) {
- if (!preg_match('/^[1-8]$/', $args[1])) {
- die('Config strictness must be a number between 1 and 8 inclusive' . PHP_EOL);
- }
-
- $init_level = (int)$args[1];
- }
-
- $init_source_dir = $args[0];
- }
-
- $vendor_dir = CliUtils::getVendorDir($current_dir);
-
- if (null !== $init_level) {
- try {
- $template_contents = Creator::getContents(
- $current_dir,
- $init_source_dir,
- $init_level,
- $vendor_dir
- );
- } catch (ConfigCreationException $e) {
- die($e->getMessage() . PHP_EOL);
- }
-
- if (!file_put_contents($current_dir . 'psalm.xml', $template_contents)) {
- die('Could not write to psalm.xml' . PHP_EOL);
- }
-
- exit('Config file created successfully. Please re-run psalm.' . PHP_EOL);
- }
- }
-
- private static function loadConfig(
- ?string $path_to_config,
- string $current_dir,
- string $output_format,
- ?ClassLoader $first_autoloader,
- bool $run_taint_analysis,
- array $options
- ): Config {
- $config = CliUtils::initializeConfig(
- $path_to_config,
- $current_dir,
- $output_format,
- $first_autoloader,
- $run_taint_analysis
- );
-
- if (isset($options['error-level'])
- && is_numeric($options['error-level'])
- ) {
- $config_level = (int) $options['error-level'];
-
- if (!in_array($config_level, [1, 2, 3, 4, 5, 6, 7, 8], true)) {
- throw new ConfigException(
- 'Invalid error level ' . $config_level
- );
- }
-
- $config->level = $config_level;
- }
- return $config;
- }
-
- private static function initProgress(array $options, Config $config): Progress
- {
- $debug = array_key_exists('debug', $options) || array_key_exists('debug-by-line', $options);
-
- $show_info = isset($options['show-info'])
- ? $options['show-info'] === 'true' || $options['show-info'] === '1'
- : false;
-
- if ($debug) {
- $progress = new DebugProgress();
- } elseif (isset($options['no-progress'])) {
- $progress = new VoidProgress();
- } else {
- $show_errors = !$config->error_baseline || isset($options['ignore-baseline']);
- if (isset($options['long-progress'])) {
- $progress = new LongProgress($show_errors, $show_info);
- } else {
- $progress = new DefaultProgress($show_errors, $show_info);
- }
- }
- return $progress;
- }
-
- private static function initProviders(array $options, Config $config, string $current_dir): Providers
- {
- if (isset($options['no-cache']) || isset($options['i'])) {
- $providers = new Providers(
- new FileProvider
- );
- } else {
- $no_reflection_cache = isset($options['no-reflection-cache']);
- $no_file_cache = isset($options['no-file-cache']);
-
- $file_storage_cache_provider = $no_reflection_cache
- ? null
- : new FileStorageCacheProvider($config);
-
- $classlike_storage_cache_provider = $no_reflection_cache
- ? null
- : new ClassLikeStorageCacheProvider($config);
-
- $providers = new Providers(
- new FileProvider,
- new ParserCacheProvider($config, !$no_file_cache),
- $file_storage_cache_provider,
- $classlike_storage_cache_provider,
- new FileReferenceCacheProvider($config),
- new ProjectCacheProvider(Composer::getLockFilePath($current_dir))
- );
- }
- return $providers;
- }
-
- /**
- * @param array{set-baseline:string} $options
- * @return array<string,array<string,array{o:int, s: list<string>}>>
- */
- private static function generateBaseline(
- array $options,
- Config $config,
- string $current_dir,
- ?string $path_to_config
- ): array {
- fwrite(STDERR, 'Writing error baseline to file...' . PHP_EOL);
-
- try {
- $issue_baseline = ErrorBaseline::read(
- new FileProvider,
- $options['set-baseline']
- );
- } catch (ConfigException $e) {
- $issue_baseline = [];
- }
-
- ErrorBaseline::create(
- new FileProvider,
- $options['set-baseline'],
- IssueBuffer::getIssuesData(),
- $config->include_php_versions_in_error_baseline || isset($options['include-php-versions'])
- );
-
- fwrite(STDERR, "Baseline saved to {$options['set-baseline']}.");
-
- CliUtils::updateConfigFile(
- $config,
- $path_to_config ?? $current_dir,
- $options['set-baseline']
- );
-
- fwrite(STDERR, PHP_EOL);
-
- return $issue_baseline;
- }
-
- /**
- * @return array<string,array<string,array{o:int, s: list<string>}>>
- */
- private static function updateBaseline(array $options, Config $config): array
- {
- $baselineFile = $config->error_baseline;
-
- if (empty($baselineFile)) {
- die('Cannot update baseline, because no baseline file is configured.' . PHP_EOL);
- }
-
- try {
- $issue_current_baseline = ErrorBaseline::read(
- new FileProvider,
- $baselineFile
- );
- $total_issues_current_baseline = ErrorBaseline::countTotalIssues($issue_current_baseline);
-
- $issue_baseline = ErrorBaseline::update(
- new FileProvider,
- $baselineFile,
- IssueBuffer::getIssuesData(),
- $config->include_php_versions_in_error_baseline || isset($options['include-php-versions'])
- );
- $total_issues_updated_baseline = ErrorBaseline::countTotalIssues($issue_baseline);
-
- $total_fixed_issues = $total_issues_current_baseline - $total_issues_updated_baseline;
-
- if ($total_fixed_issues > 0) {
- echo str_repeat('-', 30) . "\n";
- echo $total_fixed_issues . ' errors fixed' . "\n";
- }
- } catch (ConfigException $exception) {
- fwrite(STDERR, 'Could not update baseline file: ' . $exception->getMessage() . PHP_EOL);
- exit(1);
- }
-
- return $issue_baseline;
- }
-
- private static function storeTypeMap(Providers $providers, Config $config, string $type_map_location): void
- {
- $file_map = $providers->file_reference_provider->getFileMaps();
-
- $name_file_map = [];
-
- $expected_references = [];
-
- foreach ($file_map as $file_path => $map) {
- $file_name = $config->shortenFileName($file_path);
- foreach ($map[0] as $map_parts) {
- $expected_references[$map_parts[1]] = true;
- }
- $map[2] = [];
- $name_file_map[$file_name] = $map;
- }
-
- $reference_dictionary = ReferenceMapGenerator::getReferenceMap(
- $providers->classlike_storage_provider,
- $expected_references
- );
-
- $type_map_string = json_encode(['files' => $name_file_map, 'references' => $reference_dictionary]);
-
- $providers->file_provider->setContents(
- $type_map_location,
- $type_map_string
- );
- }
-
- private static function autoGenerateConfig(
- ProjectAnalyzer $project_analyzer,
- string $current_dir,
- ?string $init_source_dir,
- string $vendor_dir
- ): void {
- $issues_by_file = IssueBuffer::getIssuesData();
-
- if (!$issues_by_file) {
- $init_level = 1;
- } else {
- $codebase = $project_analyzer->getCodebase();
- $mixed_counts = $codebase->analyzer->getTotalTypeCoverage($codebase);
-
- $init_level = Creator::getLevel(
- array_merge(...array_values($issues_by_file)),
- array_sum($mixed_counts)
- );
- }
-
- echo "\n" . 'Detected level ' . $init_level . ' as a suitable initial default' . "\n";
-
- try {
- $template_contents = Creator::getContents(
- $current_dir,
- $init_source_dir,
- $init_level,
- $vendor_dir
- );
- } catch (ConfigCreationException $e) {
- die($e->getMessage() . PHP_EOL);
- }
-
- if (!file_put_contents($current_dir . 'psalm.xml', $template_contents)) {
- die('Could not write to psalm.xml' . PHP_EOL);
- }
-
- exit('Config file created successfully. Please re-run psalm.' . PHP_EOL);
- }
-
- private static function initStdoutReportOptions(
- array $options,
- bool $show_info,
- string $output_format,
- bool $in_ci
- ): ReportOptions {
- $stdout_report_options = new ReportOptions();
- $stdout_report_options->use_color = !array_key_exists('m', $options);
- $stdout_report_options->show_info = $show_info;
- $stdout_report_options->show_suggestions = !array_key_exists('no-suggestions', $options);
- /**
- * @psalm-suppress PropertyTypeCoercion
- */
- $stdout_report_options->format = $output_format;
- $stdout_report_options->show_snippet = !isset($options['show-snippet']) || $options['show-snippet'] !== "false";
- $stdout_report_options->pretty = isset($options['pretty-print']) && $options['pretty-print'] !== "false";
- $stdout_report_options->in_ci = $in_ci;
-
- return $stdout_report_options;
- }
-
- /** @return never */
- private static function clearGlobalCache(Config $config): void
- {
- $cache_directory = $config->getGlobalCacheDirectory();
-
- if ($cache_directory) {
- Config::removeCacheDirectory($cache_directory);
- echo 'Global cache directory deleted' . PHP_EOL;
- }
-
- exit;
- }
-
- /** @return never */
- private static function clearCache(Config $config): void
- {
- $cache_directory = $config->getCacheDirectory();
-
- if ($cache_directory !== null) {
- Config::removeCacheDirectory($cache_directory);
- }
- echo 'Cache directory deleted' . PHP_EOL;
- exit;
- }
-
- private static function getCurrentDir(array $options): string
- {
- $cwd = getcwd();
- if (false === $cwd) {
- fwrite(STDERR, 'Cannot get current working directory' . PHP_EOL);
- exit(1);
- }
-
- $current_dir = $cwd . DIRECTORY_SEPARATOR;
-
- if (isset($options['r']) && is_string($options['r'])) {
- $root_path = realpath($options['r']);
-
- if (!$root_path) {
- fwrite(
- STDERR,
- 'Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL
- );
- exit(1);
- }
-
- $current_dir = $root_path . DIRECTORY_SEPARATOR;
- }
-
- return $current_dir;
- }
-
- private static function emitMacPcreWarning(array $options, int $threads): void
- {
- if (!isset($options['threads'])
- && !isset($options['debug'])
- && $threads === 1
- && ini_get('pcre.jit') === '1'
- && PHP_OS === 'Darwin'
- && version_compare(PHP_VERSION, '7.3.0') >= 0
- && version_compare(PHP_VERSION, '7.4.0') < 0
- ) {
- echo(
- 'If you want to run Psalm as a language server, or run Psalm with' . PHP_EOL
- . 'multiple processes (--threads=4), beware:' . PHP_EOL
- . Pool::MAC_PCRE_MESSAGE . PHP_EOL . PHP_EOL
- );
- }
- }
-
- private static function restart(array $options, Config $config, int $threads): void
- {
- $ini_handler = new PsalmRestarter('PSALM');
-
- if (isset($options['disable-extension'])) {
- if (is_array($options['disable-extension'])) {
- /** @psalm-suppress MixedAssignment */
- foreach ($options['disable-extension'] as $extension) {
- if (is_string($extension)) {
- $ini_handler->disableExtension($extension);
- }
- }
- } elseif (is_string($options['disable-extension'])) {
- $ini_handler->disableExtension($options['disable-extension']);
- }
- }
-
- if ($threads > 1) {
- $ini_handler->disableExtension('grpc');
- }
-
- $ini_handler->disableExtension('uopz');
-
- // If Xdebug is enabled, restart without it
- $ini_handler->check();
-
- if ($config->load_xdebug_stub === null && PsalmRestarter::getSkippedVersion() !== '') {
- $config->load_xdebug_stub = true;
- }
- }
-
- private static function detectThreads(array $options, Config $config, bool $in_ci): int
- {
- if (isset($options['threads'])) {
- $threads = (int)$options['threads'];
- } elseif (isset($options['debug']) || $in_ci) {
- $threads = 1;
- } elseif ($config->threads) {
- $threads = $config->threads;
- } else {
- $threads = max(1, ProjectAnalyzer::getCpuCount() - 1);
- }
- return $threads;
- }
-
- /** @psalm-suppress UnusedParam $argv is being reported as unused */
- private static function forwardCliCall(array $options, array $argv): void
- {
- if (isset($options['alter'])) {
- require_once __DIR__ . '/Psalter.php';
- Psalter::run($argv);
- exit;
- }
-
- if (isset($options['language-server'])) {
- require_once __DIR__ . '/LanguageServer.php';
- LanguageServer::run($argv);
- exit;
- }
-
- if (isset($options['refactor'])) {
- require_once __DIR__ . '/Refactor.php';
- Refactor::run($argv);
- exit;
- }
- }
-
- /**
- * @param array<string, false|list<mixed>|string> $options
- * @param-out array<string, false|list<mixed>|string> $options
- */
- private static function syncShortOptions(array &$options): void
- {
- if (array_key_exists('help', $options)) {
- $options['h'] = false;
- }
-
- if (array_key_exists('version', $options)) {
- $options['v'] = false;
- }
-
- if (array_key_exists('init', $options)) {
- $options['i'] = false;
- }
-
- if (array_key_exists('monochrome', $options)) {
- $options['m'] = false;
- }
-
- if (isset($options['config'])) {
- $options['c'] = $options['config'];
- }
-
- if (isset($options['root'])) {
- $options['r'] = $options['root'];
- }
- }
-
- /**
- * @param array<int, string> $args
- * @return array{Config,?string}
- */
- private static function initConfig(
- string $current_dir,
- array $args,
- string $vendor_dir,
- ?ClassLoader $first_autoloader,
- ?string $path_to_config,
- string $output_format,
- bool $run_taint_analysis,
- array $options
- ): array {
- $init_source_dir = null;
- if (isset($options['i'])) {
- self::generateConfig($current_dir, $args);
- // if we ever got here, it means we need to run Psalm once and generate the config
- // based on the errors we find
- $init_source_dir = $args[0] ?? null;
-
- echo "Calculating best config level based on project files\n";
- Creator::createBareConfig($current_dir, $init_source_dir, $vendor_dir);
- $config = Config::getInstance();
- $config->setComposerClassLoader($first_autoloader);
- } else {
- $config = self::loadConfig(
- $path_to_config,
- $current_dir,
- $output_format,
- $first_autoloader,
- $run_taint_analysis,
- $options
- );
- }
- return [$config, $init_source_dir];
- }
-
- /**
- * @return array<string,array<string,array{o:int, s: list<string>}>>
- */
- private static function initBaseline(
- array $options,
- Config $config,
- string $current_dir,
- ?string $path_to_config
- ): array {
- $issue_baseline = [];
-
- if (isset($options['set-baseline']) && is_string($options['set-baseline'])) {
- $issue_baseline = self::generateBaseline($options, $config, $current_dir, $path_to_config);
- }
-
- if (isset($options['use-baseline'])) {
- if (!is_string($options['use-baseline'])) {
- fwrite(STDERR, '--use-baseline must be a string' . PHP_EOL);
- exit(1);
- }
-
- $baseline_file_path = $options['use-baseline'];
- $config->error_baseline = $baseline_file_path;
- } else {
- $baseline_file_path = $config->error_baseline;
- }
-
- if (isset($options['update-baseline'])) {
- $issue_baseline = self::updateBaseline($options, $config);
- }
-
- if (!$issue_baseline && $baseline_file_path && !isset($options['ignore-baseline'])) {
- try {
- $issue_baseline = ErrorBaseline::read(
- new FileProvider,
- $baseline_file_path
- );
- } catch (ConfigException $exception) {
- fwrite(STDERR, 'Error while reading baseline: ' . $exception->getMessage() . PHP_EOL);
- exit(1);
- }
- }
-
- return $issue_baseline;
- }
-
- private static function storeFlowGraph(array $options, ProjectAnalyzer $project_analyzer): void
- {
- /** @var string|null $dump_taint_graph */
- $dump_taint_graph = $options['dump-taint-graph'] ?? null;
-
- $flow_graph = $project_analyzer->getCodebase()->taint_flow_graph;
- if ($flow_graph !== null && $dump_taint_graph !== null) {
- file_put_contents($dump_taint_graph, "digraph Taints {\n\t".
- implode("\n\t", array_map(
- function (array $edges) {
- return '"'.implode('" -> "', $edges).'"';
- },
- $flow_graph->summarizeEdges()
- )) .
- "\n}\n");
- }
- }
-
- /** @return false|'always'|'auto' */
- private static function shouldFindUnusedCode(array $options, Config $config)
- {
- $find_unused_code = false;
- if (isset($options['find-dead-code'])) {
- $options['find-unused-code'] = $options['find-dead-code'] === 'always' ? 'always' : 'auto';
- }
-
- if (isset($options['find-unused-code'])) {
- if ($options['find-unused-code'] === 'always') {
- $find_unused_code = 'always';
- } else {
- $find_unused_code = 'auto';
- }
- }
-
- if ($config->find_unused_code) {
- $find_unused_code = 'auto';
- }
-
- return $find_unused_code;
- }
-
- private static function shouldRunTaintAnalysis(array $options): bool
- {
- return (isset($options['track-tainted-input'])
- || isset($options['security-analysis'])
- || isset($options['taint-analysis']));
- }
-
- /**
- * @param string|bool|null $find_references_to
- * @param false|'always'|'auto' $find_unused_code
- */
- private static function configureProjectAnalyzer(
- array $options,
- Config $config,
- ProjectAnalyzer $project_analyzer,
- $find_references_to,
- $find_unused_code,
- bool $find_unused_variables,
- bool $run_taint_analysis
- ): void {
- if (isset($options['generate-json-map']) && is_string($options['generate-json-map'])) {
- $project_analyzer->getCodebase()->store_node_types = true;
- }
-
- if (array_key_exists('debug-by-line', $options)) {
- $project_analyzer->debug_lines = true;
- }
-
- if (array_key_exists('debug-performance', $options)) {
- $project_analyzer->debug_performance = true;
- }
-
- if ($find_references_to !== null) {
- $project_analyzer->getCodebase()->collectLocations();
- $project_analyzer->show_issues = false;
- }
-
- if ($find_unused_code) {
- $project_analyzer->getCodebase()->reportUnusedCode($find_unused_code);
- }
-
- if ($config->find_unused_variables || $find_unused_variables) {
- $project_analyzer->getCodebase()->reportUnusedVariables();
- }
-
- if ($config->run_taint_analysis || $run_taint_analysis) {
- $project_analyzer->trackTaintedInputs();
- }
-
- if ($config->find_unused_psalm_suppress || isset($options['find-unused-psalm-suppress'])) {
- $project_analyzer->trackUnusedSuppressions();
- }
- }
-
- private static function generateStubs(
- array $options,
- Providers $providers,
- ProjectAnalyzer $project_analyzer
- ): void {
- if (isset($options['generate-stubs']) && is_string($options['generate-stubs'])) {
- $stubs_location = $options['generate-stubs'];
-
- $providers->file_provider->setContents(
- $stubs_location,
- StubsGenerator::getAll(
- $project_analyzer->getCodebase(),
- $providers->classlike_storage_provider,
- $providers->file_storage_provider
- )
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php
deleted file mode 100644
index a294914..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php
+++ /dev/null
@@ -1,578 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Cli;
-
-use Composer\XdebugHandler\XdebugHandler;
-use Psalm\Config;
-use Psalm\Exception\UnsupportedIssueToFixException;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\CliUtils;
-use Psalm\Internal\Composer;
-use Psalm\Internal\ErrorHandler;
-use Psalm\Internal\IncludeCollector;
-use Psalm\Internal\Provider\ClassLikeStorageCacheProvider;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileStorageCacheProvider;
-use Psalm\Internal\Provider\ParserCacheProvider;
-use Psalm\Internal\Provider\ProjectCacheProvider;
-use Psalm\Internal\Provider\Providers;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\IssueBuffer;
-use Psalm\Progress\DebugProgress;
-use Psalm\Progress\DefaultProgress;
-use Psalm\Report;
-use Psalm\Report\ReportOptions;
-
-use function array_filter;
-use function array_key_exists;
-use function array_map;
-use function array_merge;
-use function array_shift;
-use function array_slice;
-use function chdir;
-use function count;
-use function explode;
-use function file_exists;
-use function file_get_contents;
-use function filter_var;
-use function fwrite;
-use function gc_collect_cycles;
-use function gc_disable;
-use function getcwd;
-use function getopt;
-use function implode;
-use function in_array;
-use function ini_set;
-use function is_array;
-use function is_dir;
-use function is_string;
-use function microtime;
-use function pathinfo;
-use function preg_replace;
-use function preg_split;
-use function realpath;
-use function strpos;
-use function strtolower;
-use function substr;
-use function trim;
-
-use const DIRECTORY_SEPARATOR;
-use const FILTER_NULL_ON_FAILURE;
-use const FILTER_VALIDATE_BOOLEAN;
-use const PATHINFO_EXTENSION;
-use const PHP_EOL;
-use const STDERR;
-
-// phpcs:disable PSR1.Files.SideEffects
-
-require_once __DIR__ . '/../ErrorHandler.php';
-require_once __DIR__ . '/../CliUtils.php';
-require_once __DIR__ . '/../Composer.php';
-require_once __DIR__ . '/../IncludeCollector.php';
-require_once __DIR__ . '/../../IssueBuffer.php';
-
-final class Psalter
-{
- private const SHORT_OPTIONS = ['f:', 'm', 'h', 'r:', 'c:'];
-
- private const LONG_OPTIONS = [
- 'help', 'debug', 'debug-by-line', 'debug-emitted-issues', 'config:', 'file:', 'root:',
- 'plugin:', 'issues:', 'list-supported-issues', 'php-version:', 'dry-run', 'safe-types',
- 'find-unused-code', 'threads:', 'codeowner:',
- 'allow-backwards-incompatible-changes:',
- 'add-newline-between-docblock-annotations:',
- 'no-cache'
- ];
-
- /** @param array<int,string> $argv */
- public static function run(array $argv): void
- {
- gc_collect_cycles();
- gc_disable();
-
- ErrorHandler::install();
-
- self::setMemoryLimit();
-
- $args = array_slice($argv, 1);
-
- // get options from command line
- $options = getopt(implode('', self::SHORT_OPTIONS), self::LONG_OPTIONS);
-
- self::validateCliArguments($args);
-
- self::syncShortOptions($options);
-
- if (isset($options['c']) && is_array($options['c'])) {
- die('Too many config files provided' . PHP_EOL);
- }
-
- if (array_key_exists('h', $options)) {
- echo <<<HELP
-Usage:
- psalter [options] [file...]
-
-Options:
- -h, --help
- Display this help message
-
- --debug, --debug-by-line, --debug-emitted-issues
- Debug information
-
- -c, --config=psalm.xml
- Path to a psalm.xml configuration file. Run psalm --init to create one.
-
- -m, --monochrome
- Enable monochrome output
-
- -r, --root
- If running Psalm globally you'll need to specify a project root. Defaults to cwd
-
- --plugin=PATH
- Executes a plugin, an alternative to using the Psalm config
-
- --dry-run
- Shows a diff of all the changes, without making them
-
- --safe-types
- Only update PHP types when the new type information comes from other PHP types,
- as opposed to type information that just comes from docblocks
-
- --php-version=PHP_MAJOR_VERSION.PHP_MINOR_VERSION
-
- --issues=IssueType1,IssueType2
- If any issues can be fixed automatically, Psalm will update the codebase. To fix as many issues as possible,
- use --issues=all
-
- --list-supported-issues
- Display the list of issues that psalter knows how to fix
-
- --find-unused-code
- Include unused code as a candidate for removal
-
- --threads=INT
- If greater than one, Psalm will run analysis on multiple threads, speeding things up.
-
- --codeowner=[codeowner]
- You can specify a GitHub code ownership group, and only that owner's code will be updated.
-
- --allow-backwards-incompatible-changes=BOOL
- Allow Psalm modify method signatures that could break code outside the project. Defaults to true.
-
- --add-newline-between-docblock-annotations=BOOL
- Whether to add or not add a new line between docblock annotations. Defaults to true.
-
- --no-cache
- Runs Psalm without using cache
-
-HELP;
-
- exit;
- }
-
- if (!isset($options['issues']) &&
- !isset($options['list-supported-issues']) &&
- (!isset($options['plugin']) || $options['plugin'] === false)
- ) {
- fwrite(
- STDERR,
- 'Please specify the issues you want to fix with --issues=IssueOne,IssueTwo or --issues=all, '
- . 'or provide a plugin that has its own manipulations with --plugin=path/to/plugin.php'
- . PHP_EOL
- );
- exit(1);
- }
-
- $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR;
-
- if (isset($options['r']) && is_string($options['r'])) {
- $root_path = realpath($options['r']);
-
- if (!$root_path) {
- die('Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL);
- }
-
- $current_dir = $root_path . DIRECTORY_SEPARATOR;
- }
-
- $vendor_dir = CliUtils::getVendorDir($current_dir);
-
- // capture environment before registering autoloader (it may destroy it)
- IssueBuffer::captureServer($_SERVER);
-
- $include_collector = new IncludeCollector();
- $first_autoloader = $include_collector->runAndCollect(
- // we ignore the FQN because of a hack in scoper.inc that needs full path
- // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName
- function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader {
- return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir);
- }
- );
-
-
- // If Xdebug is enabled, restart without it
- (new XdebugHandler('PSALTER'))->check();
-
- $paths_to_check = CliUtils::getPathsToCheck($options['f'] ?? null);
-
- $path_to_config = CliUtils::getPathToConfig($options);
-
- $config = CliUtils::initializeConfig(
- $path_to_config,
- $current_dir,
- Report::TYPE_CONSOLE,
- $first_autoloader
- );
- $config->setIncludeCollector($include_collector);
-
- if ($config->resolve_from_config_file) {
- $current_dir = $config->base_dir;
- chdir($current_dir);
- }
-
- $threads = isset($options['threads']) ? (int)$options['threads'] : 1;
-
- if (isset($options['no-cache'])) {
- $providers = new Providers(
- new FileProvider()
- );
- } else {
- $providers = new Providers(
- new FileProvider(),
- new ParserCacheProvider($config, false),
- new FileStorageCacheProvider($config),
- new ClassLikeStorageCacheProvider($config),
- null,
- new ProjectCacheProvider(Composer::getLockFilePath($current_dir))
- );
- }
-
- if (array_key_exists('list-supported-issues', $options)) {
- echo implode(',', ProjectAnalyzer::getSupportedIssuesToFix()) . PHP_EOL;
- exit();
- }
-
- $debug = array_key_exists('debug', $options) || array_key_exists('debug-by-line', $options);
- $progress = $debug
- ? new DebugProgress()
- : new DefaultProgress();
-
- $stdout_report_options = new ReportOptions();
- $stdout_report_options->use_color = !array_key_exists('m', $options);
-
- $project_analyzer = new ProjectAnalyzer(
- $config,
- $providers,
- $stdout_report_options,
- [],
- $threads,
- $progress
- );
-
- if (array_key_exists('debug-by-line', $options)) {
- $project_analyzer->debug_lines = true;
- }
-
- if (array_key_exists('debug-emitted-issues', $options)) {
- $config->debug_emitted_issues = true;
- }
-
- if (array_key_exists('issues', $options)) {
- if (!is_string($options['issues']) || !$options['issues']) {
- die('Expecting a comma-separated list of issues' . PHP_EOL);
- }
-
- $issues = explode(',', $options['issues']);
-
- $keyed_issues = [];
-
- foreach ($issues as $issue) {
- $keyed_issues[$issue] = true;
- }
- } else {
- $keyed_issues = [];
- }
-
- CliUtils::initPhpVersion($options, $config, $project_analyzer);
-
- if (isset($options['codeowner'])) {
- $codeowner_files = self::loadCodeowners($providers);
-
- $desired_codeowners = is_array($options['codeowner']) ? $options['codeowner'] : [$options['codeowner']];
-
- $files_for_codeowners = self::loadCodeownersFiles($desired_codeowners, $codeowner_files);
- $paths_to_check = is_array($paths_to_check) ?
- array_merge($paths_to_check, $files_for_codeowners) :
- $files_for_codeowners;
- }
-
- if (isset($options['allow-backwards-incompatible-changes'])) {
- $allow_backwards_incompatible_changes = filter_var(
- $options['allow-backwards-incompatible-changes'],
- FILTER_VALIDATE_BOOLEAN,
- ['flags' => FILTER_NULL_ON_FAILURE]
- );
-
- if ($allow_backwards_incompatible_changes === null) {
- die('--allow-backwards-incompatible-changes expects a boolean value [true|false|1|0]' . PHP_EOL);
- }
-
- $project_analyzer->getCodebase()->allow_backwards_incompatible_changes
- = $allow_backwards_incompatible_changes;
- }
-
- if (isset($options['add-newline-between-docblock-annotations'])) {
- $doc_block_add_new_line_before_return = filter_var(
- $options['add-newline-between-docblock-annotations'],
- FILTER_VALIDATE_BOOLEAN,
- ['flags' => FILTER_NULL_ON_FAILURE]
- );
-
- if ($doc_block_add_new_line_before_return === null) {
- die('--add-newline-between-docblock-annotations expects a boolean value [true|false|1|0]' . PHP_EOL);
- }
-
- ParsedDocblock::addNewLineBetweenAnnotations($doc_block_add_new_line_before_return);
- }
-
- $plugins = [];
-
- if (isset($options['plugin'])) {
- $plugins = $options['plugin'];
-
- if (!is_array($plugins)) {
- $plugins = [$plugins];
- }
- }
-
- /** @var string $plugin_path */
- foreach ($plugins as $plugin_path) {
- Config::getInstance()->addPluginPath($current_dir . $plugin_path);
- }
-
- $find_unused_code = array_key_exists('find-unused-code', $options);
-
- foreach ($keyed_issues as $issue_name => $_) {
- // MissingParamType requires the scanning of all files to inform possible params
- if (strpos($issue_name, 'Unused') !== false
- || $issue_name === 'MissingParamType'
- || $issue_name === 'UnnecessaryVarAnnotation'
- || $issue_name === 'all'
- ) {
- $find_unused_code = true;
- break;
- }
- }
-
- if ($find_unused_code) {
- $project_analyzer->getCodebase()->reportUnusedCode();
- }
-
- $project_analyzer->alterCodeAfterCompletion(
- array_key_exists('dry-run', $options),
- array_key_exists('safe-types', $options)
- );
-
- if ($keyed_issues === ['all' => true]) {
- $project_analyzer->setAllIssuesToFix();
- } else {
- try {
- $project_analyzer->setIssuesToFix($keyed_issues);
- } catch (UnsupportedIssueToFixException $e) {
- fwrite(STDERR, $e->getMessage() . PHP_EOL);
- exit(1);
- }
- }
-
- $start_time = microtime(true);
-
- if ($paths_to_check === null || count($paths_to_check) > 1 || $find_unused_code) {
- if ($paths_to_check) {
- $files_to_update = [];
-
- foreach ($paths_to_check as $path_to_check) {
- if (!is_dir($path_to_check)) {
- $files_to_update[] = (string) realpath($path_to_check);
- } else {
- foreach ($providers->file_provider->getFilesInDir($path_to_check, ['php']) as $php_file_path) {
- $files_to_update[] = $php_file_path;
- }
- }
- }
-
- $project_analyzer->getCodebase()->analyzer->setFilesToUpdate($files_to_update);
- }
-
- $project_analyzer->check($current_dir);
- } elseif ($paths_to_check) {
- foreach ($paths_to_check as $path_to_check) {
- if (is_dir($path_to_check)) {
- $project_analyzer->checkDir($path_to_check);
- } else {
- $project_analyzer->checkFile($path_to_check);
- }
- }
- }
-
- IssueBuffer::finish($project_analyzer, false, $start_time);
- }
-
- private static function setMemoryLimit(): void
- {
- $memLimit = CliUtils::getMemoryLimitInBytes();
- // Magic number is 4096M in bytes
- if ($memLimit > 0 && $memLimit < 8 * 1024 * 1024 * 1024) {
- ini_set('memory_limit', (string) (8 * 1024 * 1024 * 1024));
- }
- }
-
- /** @param array<int,string> $args */
- private static function validateCliArguments(array $args): void
- {
- array_map(
- function (string $arg): void {
- if (strpos($arg, '--') === 0 && $arg !== '--') {
- $arg_name = preg_replace('/=.*$/', '', substr($arg, 2));
-
- if ($arg_name === 'alter') {
- // valid option for psalm, ignored by psalter
- return;
- }
-
- if (!in_array($arg_name, self::LONG_OPTIONS)
- && !in_array($arg_name . ':', self::LONG_OPTIONS)
- && !in_array($arg_name . '::', self::LONG_OPTIONS)
- ) {
- fwrite(
- STDERR,
- 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL
- . 'Type --help to see a list of supported arguments'. PHP_EOL
- );
- exit(1);
- }
- }
- },
- $args
- );
- }
-
- /**
- * @param array<string, false|list<mixed>|string> $options
- * @param-out array<string, false|list<mixed>|string> $options
- */
- private static function syncShortOptions(array &$options): void
- {
- if (array_key_exists('help', $options)) {
- $options['h'] = false;
- }
-
- if (array_key_exists('monochrome', $options)) {
- $options['m'] = false;
- }
-
- if (isset($options['config'])) {
- $options['c'] = $options['config'];
- }
-
- if (isset($options['root'])) {
- $options['r'] = $options['root'];
- }
- }
-
- /** @return array<string, array<int, string>> */
- private static function loadCodeowners(Providers $providers): array
- {
- if (file_exists('CODEOWNERS')) {
- $codeowners_file_path = realpath('CODEOWNERS');
- } elseif (file_exists('.github/CODEOWNERS')) {
- $codeowners_file_path = realpath('.github/CODEOWNERS');
- } elseif (file_exists('docs/CODEOWNERS')) {
- $codeowners_file_path = realpath('docs/CODEOWNERS');
- } else {
- die('Cannot use --codeowner without a CODEOWNERS file' . PHP_EOL);
- }
-
- $codeowners_file = file_get_contents($codeowners_file_path);
-
- $codeowner_lines = array_map(
- function (string $line): array {
- $line_parts = preg_split('/\s+/', $line);
-
- $file_selector = substr(array_shift($line_parts), 1);
- return [$file_selector, $line_parts];
- },
- array_filter(
- explode("\n", $codeowners_file),
- function (string $line): bool {
- $line = trim($line);
-
- // currently we don’t match wildcard files or files that could appear anywhere
- // in the repo
- return $line && $line[0] === '/' && strpos($line, '*') === false;
- }
- )
- );
-
- $codeowner_files = [];
-
- foreach ($codeowner_lines as [$path, $owners]) {
- if (!file_exists($path)) {
- continue;
- }
-
- foreach ($owners as $i => $owner) {
- $owners[$i] = strtolower($owner);
- }
-
- if (!is_dir($path)) {
- if (pathinfo($path, PATHINFO_EXTENSION) === 'php') {
- $codeowner_files[$path] = $owners;
- }
- } else {
- foreach ($providers->file_provider->getFilesInDir($path, ['php']) as $php_file_path) {
- $codeowner_files[$php_file_path] = $owners;
- }
- }
- }
-
- if (!$codeowner_files) {
- die('Could not find any available entries in CODEOWNERS' . PHP_EOL);
- }
-
- return $codeowner_files;
- }
-
- /**
- * @param array<string, array<int, string>> $codeowner_files
- * @return list<string>
- */
- private static function loadCodeownersFiles(array $desired_codeowners, array $codeowner_files): array
- {
- $paths_to_check = [];
- /** @psalm-suppress MixedAssignment */
- foreach ($desired_codeowners as $desired_codeowner) {
- if (!is_string($desired_codeowner)) {
- die('Invalid --codeowner ' . (string)$desired_codeowner . PHP_EOL);
- }
-
- if ($desired_codeowner[0] !== '@') {
- die('--codeowner option must start with @' . PHP_EOL);
- }
-
- $matched_file = false;
-
- foreach ($codeowner_files as $file_path => $owners) {
- if (in_array(strtolower($desired_codeowner), $owners)) {
- $paths_to_check[] = $file_path;
- $matched_file = true;
- }
- }
-
- if (!$matched_file) {
- die('User/group ' . $desired_codeowner . ' does not own any PHP files' . PHP_EOL);
- }
- }
-
- return $paths_to_check;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Refactor.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Refactor.php
deleted file mode 100644
index 1c87f4e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Refactor.php
+++ /dev/null
@@ -1,330 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Cli;
-
-use Composer\XdebugHandler\XdebugHandler;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\CliUtils;
-use Psalm\Internal\Composer;
-use Psalm\Internal\ErrorHandler;
-use Psalm\Internal\IncludeCollector;
-use Psalm\Internal\Provider\ClassLikeStorageCacheProvider;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileStorageCacheProvider;
-use Psalm\Internal\Provider\ParserCacheProvider;
-use Psalm\Internal\Provider\ProjectCacheProvider;
-use Psalm\Internal\Provider\Providers;
-use Psalm\IssueBuffer;
-use Psalm\Progress\DebugProgress;
-use Psalm\Progress\DefaultProgress;
-use Psalm\Report;
-use Psalm\Report\ReportOptions;
-
-use function array_key_exists;
-use function array_map;
-use function array_slice;
-use function chdir;
-use function end;
-use function explode;
-use function fwrite;
-use function gc_collect_cycles;
-use function gc_disable;
-use function getcwd;
-use function getopt;
-use function implode;
-use function in_array;
-use function ini_set;
-use function is_array;
-use function is_string;
-use function max;
-use function microtime;
-use function preg_replace;
-use function preg_split;
-use function realpath;
-use function strpos;
-use function substr;
-
-use const DIRECTORY_SEPARATOR;
-use const PHP_EOL;
-use const STDERR;
-
-// phpcs:disable PSR1.Files.SideEffects
-
-require_once __DIR__ . '/../ErrorHandler.php';
-require_once __DIR__ . '/../CliUtils.php';
-require_once __DIR__ . '/../Composer.php';
-require_once __DIR__ . '/../IncludeCollector.php';
-require_once __DIR__ . '/../../IssueBuffer.php';
-
-final class Refactor
-{
- /** @param array<int,string> $argv */
- public static function run(array $argv): void
- {
- ini_set('memory_limit', '8192M');
-
- gc_collect_cycles();
- gc_disable();
-
- ErrorHandler::install();
-
- $args = array_slice($argv, 1);
-
- $valid_short_options = ['f:', 'm', 'h', 'r:', 'c:'];
- $valid_long_options = [
- 'help', 'debug', 'debug-by-line', 'debug-emitted-issues', 'config:', 'root:',
- 'threads:', 'move:', 'into:', 'rename:', 'to:',
- ];
-
- // get options from command line
- $options = getopt(implode('', $valid_short_options), $valid_long_options);
-
- array_map(
- function (string $arg) use ($valid_long_options): void {
- if (strpos($arg, '--') === 0 && $arg !== '--') {
- $arg_name = preg_replace('/=.*$/', '', substr($arg, 2));
-
- if ($arg_name === 'refactor') {
- // valid option for psalm, ignored by psalter
- return;
- }
-
- if (!in_array($arg_name, $valid_long_options)
- && !in_array($arg_name . ':', $valid_long_options)
- && !in_array($arg_name . '::', $valid_long_options)
- ) {
- fwrite(
- STDERR,
- 'Unrecognised argument "--' . $arg_name . '"' . PHP_EOL
- . 'Type --help to see a list of supported arguments'. PHP_EOL
- );
- exit(1);
- }
- }
- },
- $args
- );
-
- if (array_key_exists('help', $options)) {
- $options['h'] = false;
- }
-
- if (isset($options['config'])) {
- $options['c'] = $options['config'];
- }
-
- if (isset($options['c']) && is_array($options['c'])) {
- die('Too many config files provided' . PHP_EOL);
- }
-
- if (array_key_exists('h', $options)) {
- echo <<<HELP
-Usage:
- psalm-refactor [options] [symbol1] into [symbol2]
-
-Options:
- -h, --help
- Display this help message
-
- --debug, --debug-by-line, --debug-emitted-issues
- Debug information
-
- -c, --config=psalm.xml
- Path to a psalm.xml configuration file. Run psalm --init to create one.
-
- -r, --root
- If running Psalm globally you'll need to specify a project root. Defaults to cwd
-
- --threads=auto
- If greater than one, Psalm will run analysis on multiple threads, speeding things up.
- By default
-
- --move "[Identifier]" --into "[Class]"
- Moves the specified item into the class. More than one item can be moved into a class
- by passing a comma-separated list of values e.g.
-
- --move "Ns\Foo::bar,Ns\Foo::baz" --into "Biz\Bang\DestinationClass"
-
- --rename "[Identifier]" --to "[NewIdentifier]"
- Renames a specified item (e.g. method) and updates all references to it that Psalm can
- identify.
-
-HELP;
-
- exit;
- }
-
- if (isset($options['root'])) {
- $options['r'] = $options['root'];
- }
-
- $current_dir = (string)getcwd() . DIRECTORY_SEPARATOR;
-
- if (isset($options['r']) && is_string($options['r'])) {
- $root_path = realpath($options['r']);
-
- if (!$root_path) {
- die('Could not locate root directory ' . $current_dir . DIRECTORY_SEPARATOR . $options['r'] . PHP_EOL);
- }
-
- $current_dir = $root_path . DIRECTORY_SEPARATOR;
- }
-
- $vendor_dir = CliUtils::getVendorDir($current_dir);
-
- // capture environment before registering autoloader (it may destroy it)
- IssueBuffer::captureServer($_SERVER);
-
- $include_collector = new IncludeCollector();
- $first_autoloader = $include_collector->runAndCollect(
- // we ignore the FQN because of a hack in scoper.inc that needs full path
- // phpcs:ignore SlevomatCodingStandard.Namespaces.ReferenceUsedNamesOnly.ReferenceViaFullyQualifiedName
- function () use ($current_dir, $options, $vendor_dir): ?\Composer\Autoload\ClassLoader {
- return CliUtils::requireAutoloaders($current_dir, isset($options['r']), $vendor_dir);
- }
- );
-
- // If Xdebug is enabled, restart without it
- (new XdebugHandler('PSALTER'))->check();
-
- $path_to_config = CliUtils::getPathToConfig($options);
-
- $args = CliUtils::getArguments();
-
- $operation = null;
- $last_arg = null;
-
- $to_refactor = [];
-
- foreach ($args as $arg) {
- if ($arg === '--move') {
- $operation = 'move';
- continue;
- }
-
- if ($arg === '--into') {
- if ($operation !== 'move' || !$last_arg) {
- die('--into is not expected here' . PHP_EOL);
- }
-
- $operation = 'move_into';
- continue;
- }
-
- if ($arg === '--rename') {
- $operation = 'rename';
- continue;
- }
-
- if ($arg === '--to') {
- if ($operation !== 'rename' || !$last_arg) {
- die('--to is not expected here' . PHP_EOL);
- }
-
- $operation = 'rename_to';
-
- continue;
- }
-
- if ($arg[0] === '-') {
- $operation = null;
- continue;
- }
-
- if ($operation === 'move_into' || $operation === 'rename_to') {
- if (!$last_arg) {
- die('Expecting a previous argument' . PHP_EOL);
- }
-
- if ($operation === 'move_into') {
- $last_arg_parts = preg_split('/, ?/', $last_arg);
-
- foreach ($last_arg_parts as $last_arg_part) {
- if (strpos($last_arg_part, '::')) {
- [, $identifier_name] = explode('::', $last_arg_part);
- $to_refactor[$last_arg_part] = $arg . '::' . $identifier_name;
- } else {
- $namespace_parts = explode('\\', $last_arg_part);
- $class_name = end($namespace_parts);
- $to_refactor[$last_arg_part] = $arg . '\\' . $class_name;
- }
- }
- } else {
- $to_refactor[$last_arg] = $arg;
- }
-
- $last_arg = null;
- $operation = null;
- continue;
- }
-
- if ($operation === 'move' || $operation === 'rename') {
- $last_arg = $arg;
-
- continue;
- }
-
- die('Unexpected argument "' . $arg . '"' . PHP_EOL);
- }
-
- if (!$to_refactor) {
- die('No --move or --rename arguments supplied' . PHP_EOL);
- }
-
- $config = CliUtils::initializeConfig(
- $path_to_config,
- $current_dir,
- Report::TYPE_CONSOLE,
- $first_autoloader
- );
- $config->setIncludeCollector($include_collector);
-
- if ($config->resolve_from_config_file) {
- $current_dir = $config->base_dir;
- chdir($current_dir);
- }
-
- $threads = isset($options['threads'])
- ? (int)$options['threads']
- : max(1, ProjectAnalyzer::getCpuCount() - 2);
-
- $providers = new Providers(
- new FileProvider(),
- new ParserCacheProvider($config, false),
- new FileStorageCacheProvider($config),
- new ClassLikeStorageCacheProvider($config),
- null,
- new ProjectCacheProvider(Composer::getLockFilePath($current_dir))
- );
-
- $debug = array_key_exists('debug', $options) || array_key_exists('debug-by-line', $options);
- $progress = $debug
- ? new DebugProgress()
- : new DefaultProgress();
-
- if (array_key_exists('debug-emitted-issues', $options)) {
- $config->debug_emitted_issues = true;
- }
-
- $project_analyzer = new ProjectAnalyzer(
- $config,
- $providers,
- new ReportOptions(),
- [],
- $threads,
- $progress
- );
-
- if (array_key_exists('debug-by-line', $options)) {
- $project_analyzer->debug_lines = true;
- }
-
- $project_analyzer->refactorCodeAfterCompletion($to_refactor);
-
- $start_time = microtime(true);
-
- $project_analyzer->check($current_dir);
-
- IssueBuffer::finish($project_analyzer, false, $start_time);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php b/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php
deleted file mode 100644
index f8c8873..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php
+++ /dev/null
@@ -1,665 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use Composer\Autoload\ClassLoader;
-use PackageVersions\Versions;
-use Phar;
-use Psalm\Config;
-use Psalm\Config\Creator;
-use Psalm\Exception\ConfigException;
-use Psalm\Exception\ConfigNotFoundException;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Composer;
-use Psalm\Report;
-
-use function array_slice;
-use function assert;
-use function count;
-use function define;
-use function dirname;
-use function fgets;
-use function file_exists;
-use function file_get_contents;
-use function file_put_contents;
-use function fwrite;
-use function implode;
-use function in_array;
-use function ini_get;
-use function is_array;
-use function is_dir;
-use function is_string;
-use function json_decode;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function realpath;
-use function stream_get_meta_data;
-use function stream_set_blocking;
-use function strlen;
-use function strpos;
-use function strtoupper;
-use function substr;
-use function substr_replace;
-use function trim;
-
-use const DIRECTORY_SEPARATOR;
-use const PHP_EOL;
-use const STDERR;
-use const STDIN;
-
-final class CliUtils
-{
- public static function requireAutoloaders(
- string $current_dir,
- bool $has_explicit_root,
- string $vendor_dir
- ): ?ClassLoader {
- $autoload_roots = [$current_dir];
-
- $psalm_dir = dirname(__DIR__, 3);
-
- $in_phar = Phar::running() || strpos(__NAMESPACE__, 'HumbugBox');
-
- if ($in_phar) {
- $stringable_path = __DIR__ . '/../../../vendor/symfony/polyfill-php80/Resources/stubs/Stringable.php';
- if (file_exists($stringable_path)) {
- require_once $stringable_path;
- }
- require_once __DIR__ . '/../../../vendor/autoload.php';
-
- // hack required for JsonMapper
- require_once __DIR__ . '/../../../vendor/netresearch/jsonmapper/src/JsonMapper.php';
- require_once __DIR__ . '/../../../vendor/netresearch/jsonmapper/src/JsonMapper/Exception.php';
- }
-
- if (!$in_phar && realpath($psalm_dir) !== realpath($current_dir)) {
- $autoload_roots[] = $psalm_dir;
- }
-
- $autoload_files = [];
-
- foreach ($autoload_roots as $autoload_root) {
- $has_autoloader = false;
-
- $nested_autoload_file = dirname($autoload_root, 2). DIRECTORY_SEPARATOR . 'autoload.php';
-
- // note: don't realpath $nested_autoload_file, or phar version will fail
- if (file_exists($nested_autoload_file)) {
- if (!in_array($nested_autoload_file, $autoload_files, false)) {
- $autoload_files[] = $nested_autoload_file;
- }
- $has_autoloader = true;
- }
-
- $vendor_autoload_file =
- $autoload_root . DIRECTORY_SEPARATOR . $vendor_dir . DIRECTORY_SEPARATOR . 'autoload.php';
-
- // note: don't realpath $vendor_autoload_file, or phar version will fail
- if (file_exists($vendor_autoload_file)) {
- if (!in_array($vendor_autoload_file, $autoload_files, false)) {
- $autoload_files[] = $vendor_autoload_file;
- }
- $has_autoloader = true;
- }
-
- $composer_json_file = Composer::getJsonFilePath($autoload_root);
- if (!$has_autoloader && file_exists($composer_json_file)) {
- $error_message = 'Could not find any composer autoloaders in ' . $autoload_root;
-
- if (!$has_explicit_root) {
- $error_message .= PHP_EOL . 'Add a --root=[your/project/directory] flag '
- . 'to specify a particular project to run Psalm on.';
- }
-
- fwrite(STDERR, $error_message . PHP_EOL);
- exit(1);
- }
- }
-
- $first_autoloader = null;
-
- foreach ($autoload_files as $file) {
- /**
- * @psalm-suppress UnresolvableInclude
- *
- * @var mixed
- */
- $autoloader = require_once $file;
-
- if (!$first_autoloader
- && $autoloader instanceof ClassLoader
- ) {
- $first_autoloader = $autoloader;
- }
- }
-
- if ($first_autoloader === null && !$in_phar) {
- if (!$autoload_files) {
- fwrite(STDERR, 'Failed to find a valid Composer autoloader' . "\n");
- } else {
- fwrite(
- STDERR,
- 'Failed to find a valid Composer autoloader in ' . implode(', ', $autoload_files) . "\n"
- );
- }
-
- fwrite(
- STDERR,
- 'Please make sure you’ve run `composer install` in the current directory before using Psalm.' . "\n"
- );
- exit(1);
- }
-
- define('PSALM_VERSION', Versions::getVersion('vimeo/psalm'));
- define('PHP_PARSER_VERSION', Versions::getVersion('nikic/php-parser'));
-
- return $first_autoloader;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public static function getVendorDir(string $current_dir): string
- {
- $composer_json_path = Composer::getJsonFilePath($current_dir);
-
- if (!file_exists($composer_json_path)) {
- return 'vendor';
- }
-
- if (!$composer_json = json_decode(file_get_contents($composer_json_path), true)) {
- fwrite(
- STDERR,
- 'Invalid composer.json at ' . $composer_json_path . "\n"
- );
- exit(1);
- }
-
- if (is_array($composer_json)
- && isset($composer_json['config'])
- && is_array($composer_json['config'])
- && isset($composer_json['config']['vendor-dir'])
- && is_string($composer_json['config']['vendor-dir'])
- ) {
- return $composer_json['config']['vendor-dir'];
- }
-
- return 'vendor';
- }
-
- /**
- * @return list<string>
- */
- public static function getRawCliArguments(): array
- {
- global $argv;
-
- if (!$argv) {
- return [];
- }
-
- return array_slice($argv, 1);
- }
-
- /**
- * @return list<string>
- */
- public static function getArguments(): array
- {
- $argv = self::getRawCliArguments();
- $filtered_input_paths = [];
-
- for ($i = 0, $iMax = count($argv); $i < $iMax; ++$i) {
- $input_path = $argv[$i];
-
- if (realpath($input_path) !== false) {
- continue;
- }
-
- if ($input_path[0] === '-' && strlen($input_path) === 2) {
- if ($input_path[1] === 'c' || $input_path[1] === 'f') {
- ++$i;
- }
- continue;
- }
-
- if ($input_path[0] === '-' && $input_path[2] === '=') {
- continue;
- }
-
- $filtered_input_paths[] = $input_path;
- }
-
- return $filtered_input_paths;
- }
-
- /**
- * @param string|array|null|false $f_paths
- *
- * @return list<string>|null
- */
- public static function getPathsToCheck($f_paths): ?array
- {
- $paths_to_check = [];
-
- if ($f_paths) {
- $input_paths = is_array($f_paths) ? $f_paths : [$f_paths];
- } else {
- $input_paths = self::getRawCliArguments();
- if (!$input_paths) {
- return null;
- }
- }
-
- $filtered_input_paths = [];
-
- for ($i = 0, $iMax = count($input_paths); $i < $iMax; ++$i) {
- /** @var string */
- $input_path = $input_paths[$i];
-
- if ($input_path[0] === '-' && strlen($input_path) === 2) {
- if ($input_path[1] === 'c' || $input_path[1] === 'f') {
- ++$i;
- }
- continue;
- }
-
- if ($input_path[0] === '-' && $input_path[2] === '=') {
- continue;
- }
-
- if (strpos($input_path, '--') === 0 && strlen($input_path) > 2) {
- if (substr($input_path, 2) === 'config') {
- ++$i;
- }
- continue;
- }
-
- $filtered_input_paths[] = $input_path;
- }
-
- if ($filtered_input_paths === ['-']) {
- $meta = stream_get_meta_data(STDIN);
- stream_set_blocking(STDIN, false);
- if ($stdin = fgets(STDIN)) {
- $filtered_input_paths = preg_split('/\s+/', trim($stdin));
- }
- $blocked = $meta['blocked'];
- stream_set_blocking(STDIN, $blocked);
- }
-
- foreach ($filtered_input_paths as $path_to_check) {
- if ($path_to_check[0] === '-') {
- fwrite(STDERR, 'Invalid usage, expecting psalm [options] [file...]' . PHP_EOL);
- exit(1);
- }
-
- if (!file_exists($path_to_check)) {
- fwrite(STDERR, 'Cannot locate ' . $path_to_check . PHP_EOL);
- exit(1);
- }
-
- $path_to_check = realpath($path_to_check);
-
- if (!$path_to_check) {
- fwrite(STDERR, 'Error getting realpath for file' . PHP_EOL);
- exit(1);
- }
-
- $paths_to_check[] = $path_to_check;
- }
-
- if (!$paths_to_check) {
- $paths_to_check = null;
- }
-
- return $paths_to_check;
- }
-
- /**
- * @psalm-pure
- * @todo move to Psalm\Internal\Cli\Psalm once \Psalm\getPsalmHelpText() is removed in Psalm 5
- */
- public static function getPsalmHelpText(): string
- {
- return <<<HELP
-Usage:
- psalm [options] [file...]
-
-Basic configuration:
- -c, --config=psalm.xml
- Path to a psalm.xml configuration file. Run psalm --init to create one.
-
- --use-ini-defaults
- Use PHP-provided ini defaults for memory and error display
-
- --memory-limit=LIMIT
- Use a specific memory limit. Cannot be combined with --use-ini-defaults
-
- --disable-extension=[extension]
- Used to disable certain extensions while Psalm is running.
-
- --threads=INT
- If greater than one, Psalm will run analysis on multiple threads, speeding things up.
-
- --no-diff
- Turns off Psalm’s diff mode, checks all files regardless of whether they’ve changed.
-
- --php-version=PHP_VERSION
- Explicitly set PHP version to analyse code against.
-
-Surfacing issues:
- --show-info[=BOOLEAN]
- Show non-exception parser findings (defaults to false).
-
- --show-snippet[=true]
- Show code snippets with errors. Options are 'true' or 'false'
-
- --find-dead-code[=auto]
- --find-unused-code[=auto]
- Look for unused code. Options are 'auto' or 'always'. If no value is specified, default is 'auto'
-
- --find-unused-psalm-suppress
- Finds all @psalm-suppress annotations that aren’t used
-
- --find-references-to=[class|method|property]
- Searches the codebase for references to the given fully-qualified class or method,
- where method is in the format class::methodName
-
- --no-suggestions
- Hide suggestions
-
- --taint-analysis
- Run Psalm in taint analysis mode – see https://psalm.dev/docs/security_analysis for more info
-
- --dump-taint-graph=OUTPUT_PATH
- Output the taint graph using the DOT language – requires --taint-analysis
-
-Issue baselines:
- --set-baseline=PATH
- Save all current error level issues to a file, to mark them as info in subsequent runs
-
- Add --include-php-versions to also include a list of PHP extension versions
-
- --use-baseline=PATH
- Allows you to use a baseline other than the default baseline provided in your config
-
- --ignore-baseline
- Ignore the error baseline
-
- --update-baseline
- Update the baseline by removing fixed issues. This will not add new issues to the baseline
-
- Add --include-php-versions to also include a list of PHP extension versions
-
-Plugins:
- --plugin=PATH
- Executes a plugin, an alternative to using the Psalm config
-
-Output:
- -m, --monochrome
- Enable monochrome output
-
- --output-format=console
- Changes the output format.
- Available formats: compact, console, text, emacs, json, pylint, xml, checkstyle, junit, sonarqube, github,
- phpstorm, codeclimate
-
- --no-progress
- Disable the progress indicator
-
- --long-progress
- Use a progress indicator suitable for Continuous Integration logs
-
- --stats
- Shows a breakdown of Psalm’s ability to infer types in the codebase
-
-Reports:
- --report=PATH
- The path where to output report file. The output format is based on the file extension.
- (Currently supported formats: ".json", ".xml", ".txt", ".emacs", ".pylint", ".console",
- ".sarif", "checkstyle.xml", "sonarqube.json", "codeclimate.json", "summary.json", "junit.xml")
-
- --report-show-info[=BOOLEAN]
- Whether the report should include non-errors in its output (defaults to true)
-
-Caching:
- --clear-cache
- Clears all cache files that Psalm uses for this specific project
-
- --clear-global-cache
- Clears all cache files that Psalm uses for all projects
-
- --no-cache
- Runs Psalm without using cache
-
- --no-reflection-cache
- Runs Psalm without using cached representations of unchanged classes and files.
- Useful if you want the afterClassLikeVisit plugin hook to run every time you visit a file.
-
- --no-file-cache
- Runs Psalm without using caching every single file for later diffing.
- This reduces the space Psalm uses on disk and file I/O.
-
-Miscellaneous:
- -h, --help
- Display this help message
-
- -v, --version
- Display the Psalm version
-
- -i, --init [source_dir=src] [level=3]
- Create a psalm config file in the current directory that points to [source_dir]
- at the required level, from 1, most strict, to 8, most permissive.
-
- --debug
- Debug information
-
- --debug-by-line
- Debug information on a line-by-line level
-
- --debug-emitted-issues
- Print a php backtrace to stderr when emitting issues.
-
- -r, --root
- If running Psalm globally you’ll need to specify a project root. Defaults to cwd
-
- --generate-json-map=PATH
- Generate a map of node references and types in JSON format, saved to the given path.
-
- --generate-stubs=PATH
- Generate stubs for the project and dump the file in the given path
-
- --shepherd[=host]
- Send data to Shepherd, Psalm’s GitHub integration tool.
-
- --alter
- Run Psalter
-
- --language-server
- Run Psalm Language Server
-
-HELP;
- }
-
- public static function initializeConfig(
- ?string $path_to_config,
- string $current_dir,
- string $output_format,
- ?ClassLoader $first_autoloader,
- bool $create_if_non_existent = false
- ): Config {
- try {
- if ($path_to_config) {
- $config = Config::loadFromXMLFile($path_to_config, $current_dir);
- } else {
- try {
- $config = Config::getConfigForPath($current_dir, $current_dir);
- } catch (ConfigNotFoundException $e) {
- if (!$create_if_non_existent) {
- if (in_array($output_format, [Report::TYPE_CONSOLE, Report::TYPE_PHP_STORM])) {
- fwrite(
- STDERR,
- 'Could not locate a config XML file in path ' . $current_dir
- . '. Have you run \'psalm --init\' ?' . PHP_EOL
- );
- exit(1);
- }
-
- throw $e;
- }
-
- $config = Creator::createBareConfig(
- $current_dir,
- null,
- self::getVendorDir($current_dir)
- );
- }
- }
- } catch (ConfigException $e) {
- fwrite(
- STDERR,
- $e->getMessage() . PHP_EOL
- );
- exit(1);
- }
-
- $config->setComposerClassLoader($first_autoloader);
-
- return $config;
- }
-
- public static function updateConfigFile(Config $config, string $config_file_path, string $baseline_path): void
- {
- if ($config->error_baseline === $baseline_path) {
- return;
- }
-
- $config_file = $config_file_path;
-
- if (is_dir($config_file_path)) {
- $config_file = Config::locateConfigFile($config_file_path);
- }
-
- if (!$config_file) {
- fwrite(STDERR, "Don't forget to set errorBaseline=\"{$baseline_path}\" to your config.");
-
- return;
- }
-
- $config_file_contents = file_get_contents($config_file);
-
- if ($config->error_baseline) {
- $amended_config_file_contents = preg_replace(
- '/errorBaseline=".*?"/',
- "errorBaseline=\"{$baseline_path}\"",
- $config_file_contents
- );
- } else {
- $end_psalm_open_tag = strpos($config_file_contents, '>', (int)strpos($config_file_contents, '<psalm'));
-
- if (!$end_psalm_open_tag) {
- fwrite(STDERR, " Don't forget to set errorBaseline=\"{$baseline_path}\" in your config.");
- return;
- }
-
- if ($config_file_contents[$end_psalm_open_tag - 1] === "\n") {
- $amended_config_file_contents = substr_replace(
- $config_file_contents,
- " errorBaseline=\"{$baseline_path}\"\n>",
- $end_psalm_open_tag,
- 1
- );
- } else {
- $amended_config_file_contents = substr_replace(
- $config_file_contents,
- " errorBaseline=\"{$baseline_path}\">",
- $end_psalm_open_tag,
- 1
- );
- }
- }
-
- file_put_contents($config_file, $amended_config_file_contents);
- }
-
- public static function getPathToConfig(array $options): ?string
- {
- $path_to_config = isset($options['c']) && is_string($options['c']) ? realpath($options['c']) : null;
-
- if ($path_to_config === false) {
- fwrite(STDERR, 'Could not resolve path to config ' . (string) ($options['c'] ?? '') . PHP_EOL);
- exit(1);
- }
- return $path_to_config;
- }
-
- /**
- * @psalm-pure
- */
- public static function getMemoryLimitInBytes(): int
- {
- return self::convertMemoryLimitToBytes(ini_get('memory_limit'));
- }
-
- /** @psalm-pure */
- public static function convertMemoryLimitToBytes(string $limit): int
- {
- // for unlimited = -1
- if ($limit < 0) {
- return -1;
- }
-
- if (preg_match('/^(\d+)(\D?)$/', $limit, $matches)) {
- assert(isset($matches[1]));
- $limit = (int)$matches[1];
- switch (strtoupper($matches[2] ?? '')) {
- case 'G':
- $limit *= 1024 * 1024 * 1024;
- break;
- case 'M':
- $limit *= 1024 * 1024;
- break;
- case 'K':
- $limit *= 1024;
- break;
- }
- }
-
- return (int)$limit;
- }
-
- public static function initPhpVersion(array $options, Config $config, ProjectAnalyzer $project_analyzer): void
- {
- $source = null;
-
- if (isset($options['php-version'])) {
- if (!is_string($options['php-version'])) {
- die('Expecting a version number in the format x.y' . PHP_EOL);
- }
- $version = $options['php-version'];
- $source = 'cli';
- } elseif ($version = $config->getPhpVersionFromConfig()) {
- $source = 'config';
- } elseif ($version = $config->getPHPVersionFromComposerJson()) {
- $source = 'composer';
- }
-
- if ($version !== null && $source !== null) {
- $project_analyzer->setPhpVersion($version, $source);
- }
- }
-
- public static function runningInCI(): bool
- {
- return isset($_SERVER['TRAVIS'])
- || isset($_SERVER['CIRCLECI'])
- || isset($_SERVER['APPVEYOR'])
- || isset($_SERVER['JENKINS_URL'])
- || isset($_SERVER['SCRUTINIZER'])
- || isset($_SERVER['GITLAB_CI'])
- || isset($_SERVER['GITHUB_WORKFLOW'])
- || isset($_SERVER['DRONE']);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php
deleted file mode 100644
index 36c9271..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Analyzer.php
+++ /dev/null
@@ -1,1644 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\FileAnalyzer;
-use Psalm\Internal\Analyzer\IssueData;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\FileManipulation\ClassDocblockManipulator;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\FileManipulation\FunctionDocblockManipulator;
-use Psalm\Internal\FileManipulation\PropertyDocblockManipulator;
-use Psalm\Internal\Fork\Pool;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\IssueBuffer;
-use Psalm\Progress\Progress;
-use Psalm\Type;
-use Psalm\Type\Union;
-use SebastianBergmann\Diff\Differ;
-use SebastianBergmann\Diff\Output\StrictUnifiedDiffOutputBuilder;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_intersect_key;
-use function array_merge;
-use function array_values;
-use function count;
-use function explode;
-use function implode;
-use function intdiv;
-use function ksort;
-use function number_format;
-use function pathinfo;
-use function preg_replace;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-use function usort;
-
-use const PATHINFO_EXTENSION;
-use const PHP_INT_MAX;
-
-/**
- * @psalm-type TaggedCodeType = array<int, array{0: int, 1: non-empty-string}>
- *
- * @psalm-type FileMapType = array{
- * 0: TaggedCodeType,
- * 1: TaggedCodeType,
- * 2: array<int, array{0: int, 1: non-empty-string, 2: int}>
- * }
- *
- * @psalm-type WorkerData = array{
- * issues: array<string, list<IssueData>>,
- * fixable_issue_counts: array<string, int>,
- * nonmethod_references_to_classes: array<string, array<string,bool>>,
- * method_references_to_classes: array<string, array<string,bool>>,
- * file_references_to_class_members: array<string, array<string,bool>>,
- * file_references_to_class_properties: array<string, array<string,bool>>,
- * file_references_to_method_returns: array<string, array<string,bool>>,
- * file_references_to_missing_class_members: array<string, array<string,bool>>,
- * mixed_counts: array<string, array{0: int, 1: int}>,
- * mixed_member_names: array<string, array<string, bool>>,
- * function_timings: array<string, float>,
- * file_manipulations: array<string, FileManipulation[]>,
- * method_references_to_class_members: array<string, array<string,bool>>,
- * method_dependencies: array<string, array<string,bool>>,
- * method_references_to_method_returns: array<string, array<string,bool>>,
- * method_references_to_class_properties: array<string, array<string,bool>>,
- * method_references_to_missing_class_members: array<string, array<string,bool>>,
- * method_param_uses: array<string, array<int, array<string, bool>>>,
- * analyzed_methods: array<string, array<string, int>>,
- * file_maps: array<string, FileMapType>,
- * class_locations: array<string, array<int, CodeLocation>>,
- * class_method_locations: array<string, array<int, CodeLocation>>,
- * class_property_locations: array<string, array<int, CodeLocation>>,
- * possible_method_param_types: array<string, array<int, Union>>,
- * taint_data: ?TaintFlowGraph,
- * unused_suppressions: array<string, array<int, int>>,
- * used_suppressions: array<string, array<int, bool>>,
- * function_docblock_manipulators: array<string, array<int, FunctionDocblockManipulator>>,
- * mutable_classes: array<string, bool>,
- * }
- */
-
-/**
- * @internal
- *
- * Called in the analysis phase of Psalm's execution
- */
-class Analyzer
-{
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var FileProvider
- */
- private $file_provider;
-
- /**
- * @var FileStorageProvider
- */
- private $file_storage_provider;
-
- /**
- * @var Progress
- */
- private $progress;
-
- /**
- * Used to store counts of mixed vs non-mixed variables
- *
- * @var array<string, array{0: int, 1: int}>
- */
- private $mixed_counts = [];
-
- /**
- * Used to store member names of mixed property/method access
- *
- * @var array<string, array<string, bool>>
- */
- private $mixed_member_names = [];
-
- /**
- * @var bool
- */
- private $count_mixed = true;
-
- /**
- * Used to store debug performance data
- *
- * @var array<string, float>
- */
- private $function_timings = [];
-
- /**
- * We analyze more files than we necessarily report errors in
- *
- * @var array<string, string>
- */
- private $files_to_analyze = [];
-
- /**
- * We can show analysis results on more files than we analyze
- * because the results can be cached
- *
- * @var array<string, string>
- */
- private $files_with_analysis_results = [];
-
- /**
- * We may update fewer files than we analyse (i.e. for dead code detection)
- *
- * @var array<string>|null
- */
- private $files_to_update;
-
- /**
- * @var array<string, array<string, int>>
- */
- private $analyzed_methods = [];
-
- /**
- * @var array<string, array<int, IssueData>>
- */
- private $existing_issues = [];
-
- /**
- * @var array<string, array<int, array{0: int, 1: non-empty-string}>>
- */
- private $reference_map = [];
-
- /**
- * @var array<string, array<int, array{0: int, 1: non-empty-string}>>
- */
- private $type_map = [];
-
- /**
- * @var array<string, array<int, array{0: int, 1: non-empty-string, 2: int}>>
- */
- private $argument_map = [];
-
- /**
- * @var array<string, array<int, Union>>
- */
- public $possible_method_param_types = [];
-
- /**
- * @var array<string, bool>
- */
- public $mutable_classes = [];
-
- public function __construct(
- Config $config,
- FileProvider $file_provider,
- FileStorageProvider $file_storage_provider,
- Progress $progress
- ) {
- $this->config = $config;
- $this->file_provider = $file_provider;
- $this->file_storage_provider = $file_storage_provider;
- $this->progress = $progress;
- }
-
- /**
- * @param array<string, string> $files_to_analyze
- *
- */
- public function addFilesToAnalyze(array $files_to_analyze): void
- {
- $this->files_to_analyze += $files_to_analyze;
- $this->files_with_analysis_results += $files_to_analyze;
- }
-
- /**
- * @param array<string, string> $files_to_analyze
- *
- */
- public function addFilesToShowResults(array $files_to_analyze): void
- {
- $this->files_with_analysis_results += $files_to_analyze;
- }
-
- /**
- * @param array<string> $files_to_update
- *
- */
- public function setFilesToUpdate(array $files_to_update): void
- {
- $this->files_to_update = $files_to_update;
- }
-
- public function canReportIssues(string $file_path): bool
- {
- return isset($this->files_with_analysis_results[$file_path]);
- }
-
- /**
- * @param array<string, class-string<FileAnalyzer>> $filetype_analyzers
- */
- private function getFileAnalyzer(
- ProjectAnalyzer $project_analyzer,
- string $file_path,
- array $filetype_analyzers
- ): FileAnalyzer {
- $extension = pathinfo($file_path, PATHINFO_EXTENSION);
-
- $file_name = $this->config->shortenFileName($file_path);
-
- if (isset($filetype_analyzers[$extension])) {
- $file_analyzer = new $filetype_analyzers[$extension]($project_analyzer, $file_path, $file_name);
- } else {
- $file_analyzer = new FileAnalyzer($project_analyzer, $file_path, $file_name);
- }
-
- $this->progress->debug('Getting ' . $file_path . "\n");
-
- return $file_analyzer;
- }
-
- public function analyzeFiles(
- ProjectAnalyzer $project_analyzer,
- int $pool_size,
- bool $alter_code,
- bool $consolidate_analyzed_data = false
- ): void {
- $this->loadCachedResults($project_analyzer);
-
- $codebase = $project_analyzer->getCodebase();
-
- if ($alter_code) {
- $project_analyzer->interpretRefactors();
- }
-
- $this->files_to_analyze = array_filter(
- $this->files_to_analyze,
- function (string $file_path): bool {
- return $this->file_provider->fileExists($file_path);
- }
- );
-
- $this->doAnalysis($project_analyzer, $pool_size);
-
- $scanned_files = $codebase->scanner->getScannedFiles();
-
- if ($codebase->taint_flow_graph) {
- $codebase->taint_flow_graph->connectSinksAndSources();
- }
-
- $this->progress->finish();
-
- if ($consolidate_analyzed_data) {
- $project_analyzer->consolidateAnalyzedData();
- }
-
- foreach (IssueBuffer::getIssuesData() as $file_path => $file_issues) {
- $codebase->file_reference_provider->clearExistingIssuesForFile($file_path);
-
- foreach ($file_issues as $issue_data) {
- $codebase->file_reference_provider->addIssue($file_path, $issue_data);
- }
- }
-
- $codebase->file_reference_provider->updateReferenceCache($codebase, $scanned_files);
-
- if ($codebase->track_unused_suppressions) {
- IssueBuffer::processUnusedSuppressions($codebase->file_provider);
- }
-
- $codebase->file_reference_provider->setAnalyzedMethods($this->analyzed_methods);
- $codebase->file_reference_provider->setFileMaps($this->getFileMaps());
- $codebase->file_reference_provider->setTypeCoverage($this->mixed_counts);
- $codebase->file_reference_provider->updateReferenceCache($codebase, $scanned_files);
-
- if ($codebase->diff_methods) {
- $codebase->statements_provider->resetDiffs();
- }
-
- if ($alter_code) {
- $this->progress->startAlteringFiles();
-
- $project_analyzer->prepareMigration();
-
- $files_to_update = $this->files_to_update ?? $this->files_to_analyze;
-
- foreach ($files_to_update as $file_path) {
- $this->updateFile($file_path, $project_analyzer->dry_run);
- }
-
- $project_analyzer->migrateCode();
- }
- }
-
- private function doAnalysis(ProjectAnalyzer $project_analyzer, int $pool_size): void
- {
- $this->progress->start(count($this->files_to_analyze));
-
- ksort($this->files_to_analyze);
-
- $codebase = $project_analyzer->getCodebase();
-
- $filetype_analyzers = $this->config->getFiletypeAnalyzers();
-
- $analysis_worker =
- /**
- * @return list<IssueData>
- */
- function (int $_, string $file_path) use ($project_analyzer, $filetype_analyzers): array {
- $file_analyzer = $this->getFileAnalyzer($project_analyzer, $file_path, $filetype_analyzers);
-
- $this->progress->debug('Analyzing ' . $file_analyzer->getFilePath() . "\n");
-
- $file_analyzer->analyze();
- $file_analyzer->context = null;
- $file_analyzer->clearSourceBeforeDestruction();
- unset($file_analyzer);
-
- return IssueBuffer::getIssuesDataForFile($file_path);
- };
-
- $task_done_closure =
- /**
- * @param array<IssueData> $issues
- */
- function (array $issues): void {
- $has_error = false;
- $has_info = false;
-
- foreach ($issues as $issue) {
- if ($issue->severity === 'error') {
- $has_error = true;
- break;
- }
-
- if ($issue->severity === 'info') {
- $has_info = true;
- }
- }
-
- $this->progress->taskDone($has_error ? 2 : ($has_info ? 1 : 0));
- };
-
- if ($pool_size > 1 && count($this->files_to_analyze) > $pool_size) {
- $shuffle_count = $pool_size + 1;
-
- $file_paths = array_values($this->files_to_analyze);
-
- $count = count($file_paths);
- $middle = intdiv($count, $shuffle_count);
- $remainder = $count % $shuffle_count;
-
- $new_file_paths = [];
-
- for ($i = 0; $i < $shuffle_count; $i++) {
- for ($j = 0; $j < $middle; $j++) {
- if ($j * $shuffle_count + $i < $count) {
- $new_file_paths[] = $file_paths[$j * $shuffle_count + $i];
- }
- }
-
- if ($remainder) {
- $new_file_paths[] = $file_paths[$middle * $shuffle_count + $remainder - 1];
- $remainder--;
- }
- }
-
- $process_file_paths = [];
-
- $i = 0;
-
- foreach ($new_file_paths as $file_path) {
- $process_file_paths[$i % $pool_size][] = $file_path;
- ++$i;
- }
-
- // Run analysis one file at a time, splitting the set of
- // files up among a given number of child processes.
- $pool = new Pool(
- $this->config,
- $process_file_paths,
- function (): void {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- $file_reference_provider = $codebase->file_reference_provider;
-
- if ($codebase->taint_flow_graph) {
- $codebase->taint_flow_graph = new TaintFlowGraph();
- }
-
- $file_reference_provider->setNonMethodReferencesToClasses([]);
- $file_reference_provider->setCallingMethodReferencesToClassMembers([]);
- $file_reference_provider->setCallingMethodReferencesToClassProperties([]);
- $file_reference_provider->setFileReferencesToClassMembers([]);
- $file_reference_provider->setFileReferencesToClassProperties([]);
- $file_reference_provider->setCallingMethodReferencesToMissingClassMembers([]);
- $file_reference_provider->setFileReferencesToMissingClassMembers([]);
- $file_reference_provider->setReferencesToMixedMemberNames([]);
- $file_reference_provider->setMethodParamUses([]);
- },
- $analysis_worker,
- /** @return WorkerData */
- function () {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
- $analyzer = $codebase->analyzer;
- $file_reference_provider = $codebase->file_reference_provider;
-
- $this->progress->debug('Gathering data for forked process' . "\n");
-
- // @codingStandardsIgnoreStart
- return [
- 'issues' => IssueBuffer::getIssuesData(),
- 'fixable_issue_counts' => IssueBuffer::getFixableIssues(),
- 'nonmethod_references_to_classes' => $file_reference_provider->getAllNonMethodReferencesToClasses(),
- 'method_references_to_classes' => $file_reference_provider->getAllMethodReferencesToClasses(),
- 'file_references_to_class_members' => $file_reference_provider->getAllFileReferencesToClassMembers(),
- 'method_references_to_class_members' => $file_reference_provider->getAllMethodReferencesToClassMembers(),
- 'method_dependencies' => $file_reference_provider->getAllMethodDependencies(),
- 'file_references_to_class_properties' => $file_reference_provider->getAllFileReferencesToClassProperties(),
- 'file_references_to_method_returns' => $file_reference_provider->getAllFileReferencesToMethodReturns(),
- 'method_references_to_class_properties' => $file_reference_provider->getAllMethodReferencesToClassProperties(),
- 'method_references_to_method_returns' => $file_reference_provider->getAllMethodReferencesToMethodReturns(),
- 'file_references_to_missing_class_members' => $file_reference_provider->getAllFileReferencesToMissingClassMembers(),
- 'method_references_to_missing_class_members' => $file_reference_provider->getAllMethodReferencesToMissingClassMembers(),
- 'method_param_uses' => $file_reference_provider->getAllMethodParamUses(),
- 'mixed_member_names' => $analyzer->getMixedMemberNames(),
- 'file_manipulations' => FileManipulationBuffer::getAll(),
- 'mixed_counts' => $analyzer->getMixedCounts(),
- 'function_timings' => $analyzer->getFunctionTimings(),
- 'analyzed_methods' => $analyzer->getAnalyzedMethods(),
- 'file_maps' => $analyzer->getFileMaps(),
- 'class_locations' => $file_reference_provider->getAllClassLocations(),
- 'class_method_locations' => $file_reference_provider->getAllClassMethodLocations(),
- 'class_property_locations' => $file_reference_provider->getAllClassPropertyLocations(),
- 'possible_method_param_types' => $analyzer->getPossibleMethodParamTypes(),
- 'taint_data' => $codebase->taint_flow_graph,
- 'unused_suppressions' => $codebase->track_unused_suppressions ? IssueBuffer::getUnusedSuppressions() : [],
- 'used_suppressions' => $codebase->track_unused_suppressions ? IssueBuffer::getUsedSuppressions() : [],
- 'function_docblock_manipulators' => FunctionDocblockManipulator::getManipulators(),
- 'mutable_classes' => $codebase->analyzer->mutable_classes,
- ];
- // @codingStandardsIgnoreEnd
- },
- $task_done_closure
- );
-
- $this->progress->debug('Forking analysis' . "\n");
-
- // Wait for all tasks to complete and collect the results.
- /**
- * @var array<int, WorkerData>
- */
- $forked_pool_data = $pool->wait();
-
- $this->progress->debug('Collecting forked analysis results' . "\n");
-
- foreach ($forked_pool_data as $pool_data) {
- IssueBuffer::addIssues($pool_data['issues']);
- IssueBuffer::addFixableIssues($pool_data['fixable_issue_counts']);
-
- if ($codebase->track_unused_suppressions) {
- IssueBuffer::addUnusedSuppressions($pool_data['unused_suppressions']);
- IssueBuffer::addUsedSuppressions($pool_data['used_suppressions']);
- }
-
- if ($codebase->taint_flow_graph && $pool_data['taint_data']) {
- $codebase->taint_flow_graph->addGraph($pool_data['taint_data']);
- }
-
- $codebase->file_reference_provider->addNonMethodReferencesToClasses(
- $pool_data['nonmethod_references_to_classes']
- );
- $codebase->file_reference_provider->addMethodReferencesToClasses(
- $pool_data['method_references_to_classes']
- );
- $codebase->file_reference_provider->addFileReferencesToClassMembers(
- $pool_data['file_references_to_class_members']
- );
- $codebase->file_reference_provider->addFileReferencesToClassProperties(
- $pool_data['file_references_to_class_properties']
- );
- $codebase->file_reference_provider->addFileReferencesToMethodReturns(
- $pool_data['file_references_to_method_returns']
- );
- $codebase->file_reference_provider->addMethodReferencesToClassMembers(
- $pool_data['method_references_to_class_members']
- );
- $codebase->file_reference_provider->addMethodDependencies(
- $pool_data['method_dependencies']
- );
- $codebase->file_reference_provider->addMethodReferencesToClassProperties(
- $pool_data['method_references_to_class_properties']
- );
- $codebase->file_reference_provider->addMethodReferencesToMethodReturns(
- $pool_data['method_references_to_method_returns']
- );
- $codebase->file_reference_provider->addFileReferencesToMissingClassMembers(
- $pool_data['file_references_to_missing_class_members']
- );
- $codebase->file_reference_provider->addMethodReferencesToMissingClassMembers(
- $pool_data['method_references_to_missing_class_members']
- );
- $codebase->file_reference_provider->addMethodParamUses(
- $pool_data['method_param_uses']
- );
- $this->addMixedMemberNames(
- $pool_data['mixed_member_names']
- );
- $this->function_timings += $pool_data['function_timings'];
- $codebase->file_reference_provider->addClassLocations(
- $pool_data['class_locations']
- );
- $codebase->file_reference_provider->addClassMethodLocations(
- $pool_data['class_method_locations']
- );
- $codebase->file_reference_provider->addClassPropertyLocations(
- $pool_data['class_property_locations']
- );
-
- $this->mutable_classes = array_merge($this->mutable_classes, $pool_data['mutable_classes']);
-
- FunctionDocblockManipulator::addManipulators($pool_data['function_docblock_manipulators']);
-
- $this->analyzed_methods = array_merge($pool_data['analyzed_methods'], $this->analyzed_methods);
-
- foreach ($pool_data['mixed_counts'] as $file_path => [$mixed_count, $nonmixed_count]) {
- if (!isset($this->mixed_counts[$file_path])) {
- $this->mixed_counts[$file_path] = [$mixed_count, $nonmixed_count];
- } else {
- $this->mixed_counts[$file_path][0] += $mixed_count;
- $this->mixed_counts[$file_path][1] += $nonmixed_count;
- }
- }
-
- foreach ($pool_data['possible_method_param_types'] as $declaring_method_id => $possible_param_types) {
- if (!isset($this->possible_method_param_types[$declaring_method_id])) {
- $this->possible_method_param_types[$declaring_method_id] = $possible_param_types;
- } else {
- foreach ($possible_param_types as $offset => $possible_param_type) {
- $this->possible_method_param_types[$declaring_method_id][$offset]
- = Type::combineUnionTypes(
- $this->possible_method_param_types[$declaring_method_id][$offset] ?? null,
- $possible_param_type,
- $codebase
- );
- }
- }
- }
-
- foreach ($pool_data['file_manipulations'] as $file_path => $manipulations) {
- FileManipulationBuffer::add($file_path, $manipulations);
- }
-
- foreach ($pool_data['file_maps'] as $file_path => $file_maps) {
- [$reference_map, $type_map, $argument_map] = $file_maps;
- $this->reference_map[$file_path] = $reference_map;
- $this->type_map[$file_path] = $type_map;
- $this->argument_map[$file_path] = $argument_map;
- }
- }
-
- if ($pool->didHaveError()) {
- exit(1);
- }
- } else {
- $i = 0;
-
- foreach ($this->files_to_analyze as $file_path => $_) {
- $analysis_worker($i, $file_path);
- ++$i;
-
- $issues = IssueBuffer::getIssuesDataForFile($file_path);
- $task_done_closure($issues);
- }
- }
- }
-
- /**
- * @psalm-suppress ComplexMethod
- */
- public function loadCachedResults(ProjectAnalyzer $project_analyzer): void
- {
- $codebase = $project_analyzer->getCodebase();
-
- if ($codebase->diff_methods) {
- $this->analyzed_methods = $codebase->file_reference_provider->getAnalyzedMethods();
- $this->existing_issues = $codebase->file_reference_provider->getExistingIssues();
- $file_maps = $codebase->file_reference_provider->getFileMaps();
-
- foreach ($file_maps as $file_path => [$reference_map, $type_map, $argument_map]) {
- $this->reference_map[$file_path] = $reference_map;
- $this->type_map[$file_path] = $type_map;
- $this->argument_map[$file_path] = $argument_map;
- }
- }
-
- $statements_provider = $codebase->statements_provider;
- $file_reference_provider = $codebase->file_reference_provider;
-
- $changed_members = $statements_provider->getChangedMembers();
- $unchanged_signature_members = $statements_provider->getUnchangedSignatureMembers();
- $errored_files = $statements_provider->getErrors();
-
- $diff_map = $statements_provider->getDiffMap();
- $deletion_ranges = $statements_provider->getDeletionRanges();
-
- $method_references_to_class_members = $file_reference_provider->getAllMethodReferencesToClassMembers();
-
- $method_dependencies = $file_reference_provider->getAllMethodDependencies();
-
- $method_references_to_class_properties = $file_reference_provider->getAllMethodReferencesToClassProperties();
-
- $method_references_to_method_returns = $file_reference_provider->getAllMethodReferencesToMethodReturns();
-
- $method_references_to_missing_class_members =
- $file_reference_provider->getAllMethodReferencesToMissingClassMembers();
-
- $all_referencing_methods = $method_references_to_class_members
- + $method_references_to_missing_class_members
- + $method_dependencies;
-
- $nonmethod_references_to_classes = $file_reference_provider->getAllNonMethodReferencesToClasses();
-
- $method_references_to_classes = $file_reference_provider->getAllMethodReferencesToClasses();
-
- $method_param_uses = $file_reference_provider->getAllMethodParamUses();
-
- $file_references_to_class_members = $file_reference_provider->getAllFileReferencesToClassMembers();
-
- $file_references_to_class_properties = $file_reference_provider->getAllFileReferencesToClassProperties();
-
- $file_references_to_method_returns = $file_reference_provider->getAllFileReferencesToMethodReturns();
-
- $file_references_to_missing_class_members
- = $file_reference_provider->getAllFileReferencesToMissingClassMembers();
-
- $references_to_mixed_member_names = $file_reference_provider->getAllReferencesToMixedMemberNames();
-
- $this->mixed_counts = $file_reference_provider->getTypeCoverage();
-
- foreach ($changed_members as $file_path => $members_by_file) {
- foreach ($members_by_file as $changed_member => $_) {
- if (!strpos($changed_member, '&')) {
- continue;
- }
-
- [$base_class, $trait] = explode('&', $changed_member);
-
- foreach ($all_referencing_methods as $member_id => $_) {
- if (strpos($member_id, $base_class . '::') !== 0) {
- continue;
- }
-
- $member_bit = substr($member_id, strlen($base_class) + 2);
-
- if (isset($all_referencing_methods[$trait . '::' . $member_bit])) {
- $changed_members[$file_path][$member_id] = true;
- }
- }
- }
- }
-
- $newly_invalidated_methods = [];
-
- foreach ($unchanged_signature_members as $file_unchanged_signature_members) {
- $newly_invalidated_methods = array_merge($newly_invalidated_methods, $file_unchanged_signature_members);
-
- foreach ($file_unchanged_signature_members as $unchanged_signature_member_id => $_) {
- // also check for things that might invalidate constructor property initialisation
- if (isset($all_referencing_methods[$unchanged_signature_member_id])) {
- foreach ($all_referencing_methods[$unchanged_signature_member_id] as $referencing_method_id => $_) {
- if (substr($referencing_method_id, -13) === '::__construct') {
- $referencing_base_classlike = explode('::', $referencing_method_id)[0];
- $unchanged_signature_classlike = explode('::', $unchanged_signature_member_id)[0];
-
- if ($referencing_base_classlike === $unchanged_signature_classlike) {
- $newly_invalidated_methods[$referencing_method_id] = true;
- } else {
- try {
- $referencing_storage = $codebase->classlike_storage_provider->get(
- $referencing_base_classlike
- );
- } catch (InvalidArgumentException $_) {
- // Workaround for #3671
- $newly_invalidated_methods[$referencing_method_id] = true;
- $referencing_storage = null;
- }
-
- if (isset($referencing_storage->used_traits[$unchanged_signature_classlike])
- || isset($referencing_storage->parent_classes[$unchanged_signature_classlike])
- ) {
- $newly_invalidated_methods[$referencing_method_id] = true;
- }
- }
- }
- }
- }
- }
- }
-
- foreach ($changed_members as $file_changed_members) {
- foreach ($file_changed_members as $member_id => $_) {
- $newly_invalidated_methods[$member_id] = true;
-
- if (isset($all_referencing_methods[$member_id])) {
- $newly_invalidated_methods = array_merge(
- $all_referencing_methods[$member_id],
- $newly_invalidated_methods
- );
- }
-
- unset(
- $method_references_to_class_members[$member_id],
- $method_dependencies[$member_id],
- $method_references_to_class_properties[$member_id],
- $method_references_to_method_returns[$member_id],
- $file_references_to_class_members[$member_id],
- $file_references_to_class_properties[$member_id],
- $file_references_to_method_returns[$member_id],
- $method_references_to_missing_class_members[$member_id],
- $file_references_to_missing_class_members[$member_id],
- $references_to_mixed_member_names[$member_id],
- $method_param_uses[$member_id]
- );
-
- $member_stub = preg_replace('/::.*$/', '::*', $member_id);
-
- if (isset($all_referencing_methods[$member_stub])) {
- $newly_invalidated_methods = array_merge(
- $all_referencing_methods[$member_stub],
- $newly_invalidated_methods
- );
- }
- }
- }
-
- foreach ($newly_invalidated_methods as $method_id => $_) {
- foreach ($method_references_to_class_members as $i => $_) {
- unset($method_references_to_class_members[$i][$method_id]);
- }
-
- foreach ($method_dependencies as $i => $_) {
- unset($method_dependencies[$i][$method_id]);
- }
-
- foreach ($method_references_to_class_properties as $i => $_) {
- unset($method_references_to_class_properties[$i][$method_id]);
- }
-
- foreach ($method_references_to_method_returns as $i => $_) {
- unset($method_references_to_method_returns[$i][$method_id]);
- }
-
- foreach ($method_references_to_classes as $i => $_) {
- unset($method_references_to_classes[$i][$method_id]);
- }
-
- foreach ($method_references_to_missing_class_members as $i => $_) {
- unset($method_references_to_missing_class_members[$i][$method_id]);
- }
-
- foreach ($references_to_mixed_member_names as $i => $_) {
- unset($references_to_mixed_member_names[$i][$method_id]);
- }
-
- foreach ($method_param_uses as $i => $_) {
- foreach ($method_param_uses[$i] as $j => $_) {
- unset($method_param_uses[$i][$j][$method_id]);
- }
- }
- }
-
- foreach ($errored_files as $file_path => $_) {
- unset($this->analyzed_methods[$file_path]);
- unset($this->existing_issues[$file_path]);
- }
-
- foreach ($this->analyzed_methods as $file_path => $analyzed_methods) {
- foreach ($analyzed_methods as $correct_method_id => $_) {
- $trait_safe_method_id = $correct_method_id;
-
- $correct_method_ids = explode('&', $correct_method_id);
-
- $correct_method_id = $correct_method_ids[0];
-
- if (isset($newly_invalidated_methods[$correct_method_id])
- || (isset($correct_method_ids[1])
- && isset($newly_invalidated_methods[$correct_method_ids[1]]))
- ) {
- unset($this->analyzed_methods[$file_path][$trait_safe_method_id]);
- }
- }
- }
-
- $this->shiftFileOffsets($diff_map, $deletion_ranges);
-
- foreach ($this->files_to_analyze as $file_path) {
- $file_reference_provider->clearExistingIssuesForFile($file_path);
- $file_reference_provider->clearExistingFileMapsForFile($file_path);
-
- $this->setMixedCountsForFile($file_path, [0, 0]);
-
- foreach ($file_references_to_class_members as $i => $_) {
- unset($file_references_to_class_members[$i][$file_path]);
- }
-
- foreach ($file_references_to_class_properties as $i => $_) {
- unset($file_references_to_class_properties[$i][$file_path]);
- }
-
- foreach ($file_references_to_method_returns as $i => $_) {
- unset($file_references_to_method_returns[$i][$file_path]);
- }
-
- foreach ($nonmethod_references_to_classes as $i => $_) {
- unset($nonmethod_references_to_classes[$i][$file_path]);
- }
-
- foreach ($references_to_mixed_member_names as $i => $_) {
- unset($references_to_mixed_member_names[$i][$file_path]);
- }
-
- foreach ($file_references_to_missing_class_members as $i => $_) {
- unset($file_references_to_missing_class_members[$i][$file_path]);
- }
- }
-
- foreach ($this->existing_issues as $file_path => $issues) {
- if (!isset($this->files_to_analyze[$file_path])) {
- unset($this->existing_issues[$file_path]);
-
- if ($this->file_provider->fileExists($file_path)) {
- IssueBuffer::addIssues([$file_path => array_values($issues)]);
- }
- }
- }
-
- $method_references_to_class_members = array_filter(
- $method_references_to_class_members
- );
-
- $method_dependencies = array_filter(
- $method_dependencies
- );
-
- $method_references_to_class_properties = array_filter(
- $method_references_to_class_properties
- );
-
- $method_references_to_method_returns = array_filter(
- $method_references_to_method_returns
- );
-
- $method_references_to_missing_class_members = array_filter(
- $method_references_to_missing_class_members
- );
-
- $file_references_to_class_members = array_filter(
- $file_references_to_class_members
- );
-
- $file_references_to_class_properties = array_filter(
- $file_references_to_class_properties
- );
-
- $file_references_to_method_returns = array_filter(
- $file_references_to_method_returns
- );
-
- $file_references_to_missing_class_members = array_filter(
- $file_references_to_missing_class_members
- );
-
- $references_to_mixed_member_names = array_filter(
- $references_to_mixed_member_names
- );
-
- $nonmethod_references_to_classes = array_filter(
- $nonmethod_references_to_classes
- );
-
- $method_references_to_classes = array_filter(
- $method_references_to_classes
- );
-
- $method_param_uses = array_filter(
- $method_param_uses
- );
-
- $file_reference_provider->setCallingMethodReferencesToClassMembers(
- $method_references_to_class_members
- );
-
- $file_reference_provider->setMethodDependencies(
- $method_dependencies
- );
-
- $file_reference_provider->setCallingMethodReferencesToClassProperties(
- $method_references_to_class_properties
- );
-
- $file_reference_provider->setCallingMethodReferencesToMethodReturns(
- $method_references_to_method_returns
- );
-
- $file_reference_provider->setFileReferencesToClassMembers(
- $file_references_to_class_members
- );
-
- $file_reference_provider->setFileReferencesToClassProperties(
- $file_references_to_class_properties
- );
-
- $file_reference_provider->setFileReferencesToMethodReturns(
- $file_references_to_method_returns
- );
-
- $file_reference_provider->setCallingMethodReferencesToMissingClassMembers(
- $method_references_to_missing_class_members
- );
-
- $file_reference_provider->setFileReferencesToMissingClassMembers(
- $file_references_to_missing_class_members
- );
-
- $file_reference_provider->setReferencesToMixedMemberNames(
- $references_to_mixed_member_names
- );
-
- $file_reference_provider->setCallingMethodReferencesToClasses(
- $method_references_to_classes
- );
-
- $file_reference_provider->setNonMethodReferencesToClasses(
- $nonmethod_references_to_classes
- );
-
- $file_reference_provider->setMethodParamUses(
- $method_param_uses
- );
- }
-
- /**
- * @param array<string, array<int, array{int, int, int, int}>> $diff_map
- * @param array<string, array<int, array{int, int}>> $deletion_ranges
- */
- public function shiftFileOffsets(array $diff_map, array $deletion_ranges): void
- {
- foreach ($this->existing_issues as $file_path => $file_issues) {
- if (!isset($this->analyzed_methods[$file_path])) {
- continue;
- }
-
- $file_diff_map = $diff_map[$file_path] ?? [];
- $file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
-
- if ($file_deletion_ranges) {
- foreach ($file_issues as $i => $issue_data) {
- foreach ($file_deletion_ranges as [$from, $to]) {
- if ($issue_data->from >= $from
- && $issue_data->from <= $to
- ) {
- unset($this->existing_issues[$file_path][$i]);
- break;
- }
- }
- }
- }
-
- if ($file_diff_map) {
- foreach ($file_issues as $issue_data) {
- foreach ($file_diff_map as [$from, $to, $file_offset, $line_offset]) {
- if ($issue_data->from >= $from
- && $issue_data->from <= $to
- ) {
- $issue_data->from += $file_offset;
- $issue_data->to += $file_offset;
- $issue_data->snippet_from += $file_offset;
- $issue_data->snippet_to += $file_offset;
- $issue_data->line_from += $line_offset;
- $issue_data->line_to += $line_offset;
- break;
- }
- }
- }
- }
- }
-
- foreach ($this->reference_map as $file_path => $reference_map) {
- if (!isset($this->analyzed_methods[$file_path])) {
- unset($this->reference_map[$file_path]);
- continue;
- }
-
- $file_diff_map = $diff_map[$file_path] ?? [];
- $file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
-
- if ($file_deletion_ranges) {
- foreach ($reference_map as $reference_from => $_) {
- foreach ($file_deletion_ranges as [$from, $to]) {
- if ($reference_from >= $from && $reference_from <= $to) {
- unset($this->reference_map[$file_path][$reference_from]);
- break;
- }
- }
- }
- }
-
- if ($file_diff_map) {
- foreach ($reference_map as $reference_from => [$reference_to, $tag]) {
- foreach ($file_diff_map as [$from, $to, $file_offset]) {
- if ($reference_from >= $from && $reference_from <= $to) {
- unset($this->reference_map[$file_path][$reference_from]);
- $this->reference_map[$file_path][$reference_from + $file_offset] = [
- $reference_to + $file_offset,
- $tag,
- ];
- break;
- }
- }
- }
- }
- }
-
- foreach ($this->type_map as $file_path => $type_map) {
- if (!isset($this->analyzed_methods[$file_path])) {
- unset($this->type_map[$file_path]);
- continue;
- }
-
- $file_diff_map = $diff_map[$file_path] ?? [];
- $file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
-
- if ($file_deletion_ranges) {
- foreach ($type_map as $type_from => $_) {
- foreach ($file_deletion_ranges as [$from, $to]) {
- if ($type_from >= $from && $type_from <= $to) {
- unset($this->type_map[$file_path][$type_from]);
- break;
- }
- }
- }
- }
-
- if ($file_diff_map) {
- foreach ($type_map as $type_from => [$type_to, $tag]) {
- foreach ($file_diff_map as [$from, $to, $file_offset]) {
- if ($type_from >= $from && $type_from <= $to) {
- unset($this->type_map[$file_path][$type_from]);
- $this->type_map[$file_path][$type_from + $file_offset] = [
- $type_to + $file_offset,
- $tag,
- ];
- break;
- }
- }
- }
- }
- }
-
- foreach ($this->argument_map as $file_path => $argument_map) {
- if (!isset($this->analyzed_methods[$file_path])) {
- unset($this->argument_map[$file_path]);
- continue;
- }
-
- $file_diff_map = $diff_map[$file_path] ?? [];
- $file_deletion_ranges = $deletion_ranges[$file_path] ?? [];
-
- if ($file_deletion_ranges) {
- foreach ($argument_map as $argument_from => $_) {
- foreach ($file_deletion_ranges as [$from, $to]) {
- if ($argument_from >= $from && $argument_from <= $to) {
- unset($argument_map[$argument_from]);
- break;
- }
- }
- }
- }
-
- if ($file_diff_map) {
- foreach ($argument_map as $argument_from => [$argument_to, $method_id, $argument_number]) {
- foreach ($file_diff_map as [$from, $to, $file_offset]) {
- if ($argument_from >= $from && $argument_from <= $to) {
- unset($this->argument_map[$file_path][$argument_from]);
- $this->argument_map[$file_path][$argument_from + $file_offset] = [
- $argument_to + $file_offset,
- $method_id,
- $argument_number,
- ];
- break;
- }
- }
- }
- }
- }
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getMixedMemberNames(): array
- {
- return $this->mixed_member_names;
- }
-
- public function addMixedMemberName(string $member_id, string $reference): void
- {
- $this->mixed_member_names[$member_id][$reference] = true;
- }
-
- public function hasMixedMemberName(string $member_id): bool
- {
- return isset($this->mixed_member_names[$member_id]);
- }
-
- /**
- * @param array<string, array<string, bool>> $names
- *
- */
- public function addMixedMemberNames(array $names): void
- {
- foreach ($names as $key => $name) {
- if (isset($this->mixed_member_names[$key])) {
- $this->mixed_member_names[$key] = array_merge(
- $this->mixed_member_names[$key],
- $name
- );
- } else {
- $this->mixed_member_names[$key] = $name;
- }
- }
- }
-
- /**
- * @return array{0:int, 1:int}
- */
- public function getMixedCountsForFile(string $file_path): array
- {
- if (!isset($this->mixed_counts[$file_path])) {
- $this->mixed_counts[$file_path] = [0, 0];
- }
-
- return $this->mixed_counts[$file_path];
- }
-
- /**
- * @param array{0:int, 1:int} $mixed_counts
- *
- */
- public function setMixedCountsForFile(string $file_path, array $mixed_counts): void
- {
- $this->mixed_counts[$file_path] = $mixed_counts;
- }
-
- public function incrementMixedCount(string $file_path): void
- {
- if (!$this->count_mixed) {
- return;
- }
-
- if (!isset($this->mixed_counts[$file_path])) {
- $this->mixed_counts[$file_path] = [0, 0];
- }
-
- ++$this->mixed_counts[$file_path][0];
- }
-
- public function decrementMixedCount(string $file_path): void
- {
- if (!$this->count_mixed) {
- return;
- }
-
- if (!isset($this->mixed_counts[$file_path])) {
- return;
- }
-
- if ($this->mixed_counts[$file_path][0] === 0) {
- return;
- }
-
- --$this->mixed_counts[$file_path][0];
- }
-
- public function incrementNonMixedCount(string $file_path): void
- {
- if (!$this->count_mixed) {
- return;
- }
-
- if (!isset($this->mixed_counts[$file_path])) {
- $this->mixed_counts[$file_path] = [0, 0];
- }
-
- ++$this->mixed_counts[$file_path][1];
- }
-
- /**
- * @return array<string, array{0: int, 1: int}>
- */
- public function getMixedCounts(): array
- {
- $all_deep_scanned_files = [];
-
- foreach ($this->files_to_analyze as $file_path => $_) {
- $all_deep_scanned_files[$file_path] = true;
- }
-
- return array_intersect_key($this->mixed_counts, $all_deep_scanned_files);
- }
-
- /**
- * @return array<string, float>
- */
- public function getFunctionTimings(): array
- {
- return $this->function_timings;
- }
-
- public function addFunctionTiming(string $function_id, float $time_per_node): void
- {
- $this->function_timings[$function_id] = $time_per_node;
- }
-
- public function addNodeType(
- string $file_path,
- PhpParser\Node $node,
- string $node_type,
- PhpParser\Node $parent_node = null
- ): void {
- if ($node_type === '') {
- throw new UnexpectedValueException('non-empty node_type expected');
- }
-
- $this->type_map[$file_path][(int)$node->getAttribute('startFilePos')] = [
- ($parent_node ? (int)$parent_node->getAttribute('endFilePos') : (int)$node->getAttribute('endFilePos')) + 1,
- $node_type,
- ];
- }
-
- public function addNodeArgument(
- string $file_path,
- int $start_position,
- int $end_position,
- string $reference,
- int $argument_number
- ): void {
- if ($reference === '') {
- throw new UnexpectedValueException('non-empty reference expected');
- }
-
- $this->argument_map[$file_path][$start_position] = [
- $end_position,
- $reference,
- $argument_number,
- ];
- }
-
- /**
- * @param string $reference The symbol name for the reference.
- * Prepend with an asterisk (*) to signify a reference that doesn't exist.
- */
- public function addNodeReference(string $file_path, PhpParser\Node $node, string $reference): void
- {
- if (!$reference) {
- throw new UnexpectedValueException('non-empty node_type expected');
- }
-
- $this->reference_map[$file_path][(int)$node->getAttribute('startFilePos')] = [
- (int)$node->getAttribute('endFilePos') + 1,
- $reference,
- ];
- }
-
- public function addOffsetReference(string $file_path, int $start, int $end, string $reference): void
- {
- if (!$reference) {
- throw new UnexpectedValueException('non-empty node_type expected');
- }
-
- $this->reference_map[$file_path][$start] = [
- $end,
- $reference,
- ];
- }
-
- /**
- * @return array{int, int}
- */
- public function getTotalTypeCoverage(Codebase $codebase): array
- {
- $mixed_count = 0;
- $nonmixed_count = 0;
-
- foreach ($codebase->file_reference_provider->getTypeCoverage() as $file_path => $counts) {
- if (!$this->config->reportTypeStatsForFile($file_path)) {
- continue;
- }
-
- [$path_mixed_count, $path_nonmixed_count] = $counts;
-
- if (isset($this->mixed_counts[$file_path])) {
- $mixed_count += $path_mixed_count;
- $nonmixed_count += $path_nonmixed_count;
- }
- }
-
- return [$mixed_count, $nonmixed_count];
- }
-
- public function getTypeInferenceSummary(Codebase $codebase): string
- {
- $all_deep_scanned_files = [];
-
- foreach ($this->files_to_analyze as $file_path => $_) {
- $all_deep_scanned_files[$file_path] = true;
-
- foreach ($this->file_storage_provider->get($file_path)->required_file_paths as $required_file_path) {
- $all_deep_scanned_files[$required_file_path] = true;
- }
- }
-
- [$mixed_count, $nonmixed_count] = $this->getTotalTypeCoverage($codebase);
-
- $total = $mixed_count + $nonmixed_count;
-
- $total_files = count($all_deep_scanned_files);
-
- $lines = [];
-
- if (!$total_files) {
- $lines[] = 'No files analyzed';
- }
-
- if (!$total) {
- $lines[] = 'Psalm was unable to infer types in the codebase';
- } else {
- $percentage = $nonmixed_count === $total ? '100' : number_format(100 * $nonmixed_count / $total, 4);
- $lines[] = 'Psalm was able to infer types for ' . $percentage . '%'
- . ' of the codebase';
- }
-
- return implode("\n", $lines);
- }
-
- public function getNonMixedStats(): string
- {
- $stats = '';
-
- $all_deep_scanned_files = [];
-
- foreach ($this->files_to_analyze as $file_path => $_) {
- $all_deep_scanned_files[$file_path] = true;
-
- if (!$this->config->reportTypeStatsForFile($file_path)) {
- continue;
- }
-
- foreach ($this->file_storage_provider->get($file_path)->required_file_paths as $required_file_path) {
- $all_deep_scanned_files[$required_file_path] = true;
- }
- }
-
- foreach ($all_deep_scanned_files as $file_path => $_) {
- if (isset($this->mixed_counts[$file_path])) {
- [$path_mixed_count, $path_nonmixed_count] = $this->mixed_counts[$file_path];
-
- if ($path_mixed_count + $path_nonmixed_count) {
- $stats .= number_format(100 * $path_nonmixed_count / ($path_mixed_count + $path_nonmixed_count), 3)
- . '% ' . $this->config->shortenFileName($file_path)
- . ' (' . $path_mixed_count . ' mixed)' . "\n";
- }
- }
- }
-
- return $stats;
- }
-
- public function disableMixedCounts(): void
- {
- $this->count_mixed = false;
- }
-
- public function enableMixedCounts(): void
- {
- $this->count_mixed = true;
- }
-
- public function updateFile(string $file_path, bool $dry_run): void
- {
- FileManipulationBuffer::add(
- $file_path,
- FunctionDocblockManipulator::getManipulationsForFile($file_path)
- );
-
- FileManipulationBuffer::add(
- $file_path,
- PropertyDocblockManipulator::getManipulationsForFile($file_path)
- );
-
- FileManipulationBuffer::add(
- $file_path,
- ClassDocblockManipulator::getManipulationsForFile($file_path)
- );
-
- $file_manipulations = FileManipulationBuffer::getManipulationsForFile($file_path);
-
- if (!$file_manipulations) {
- return;
- }
-
- usort(
- $file_manipulations,
- function (FileManipulation $a, FileManipulation $b): int {
- if ($b->end === $a->end) {
- if ($a->start === $b->start) {
- return $b->insertion_text > $a->insertion_text ? 1 : -1;
- }
-
- return $b->start > $a->start ? 1 : -1;
- }
-
- return $b->end > $a->end ? 1 : -1;
- }
- );
-
- $last_start = PHP_INT_MAX;
- $existing_contents = $this->file_provider->getContents($file_path);
-
- foreach ($file_manipulations as $manipulation) {
- if ($manipulation->start <= $last_start) {
- $existing_contents = $manipulation->transform($existing_contents);
- $last_start = $manipulation->start;
- }
- }
-
- if ($dry_run) {
- echo $file_path . ':' . "\n";
-
- $differ = new Differ(
- new StrictUnifiedDiffOutputBuilder([
- 'fromFile' => $file_path,
- 'toFile' => $file_path,
- ])
- );
-
- echo $differ->diff($this->file_provider->getContents($file_path), $existing_contents);
-
- return;
- }
-
- $this->progress->alterFileDone($file_path);
-
- $this->file_provider->setContents($file_path, $existing_contents);
- }
-
- /**
- * @return list<IssueData>
- */
- public function getExistingIssuesForFile(string $file_path, int $start, int $end, ?string $issue_type = null): array
- {
- if (!isset($this->existing_issues[$file_path])) {
- return [];
- }
-
- $applicable_issues = [];
-
- foreach ($this->existing_issues[$file_path] as $issue_data) {
- if ($issue_data->from >= $start && $issue_data->from <= $end) {
- if ($issue_type === null || $issue_type === $issue_data->type) {
- $applicable_issues[] = $issue_data;
- }
- }
- }
-
- return $applicable_issues;
- }
-
- public function removeExistingDataForFile(string $file_path, int $start, int $end, ?string $issue_type = null): void
- {
- if (isset($this->existing_issues[$file_path])) {
- foreach ($this->existing_issues[$file_path] as $i => $issue_data) {
- if ($issue_data->from >= $start && $issue_data->from <= $end) {
- if ($issue_type === null || $issue_type === $issue_data->type) {
- unset($this->existing_issues[$file_path][$i]);
- }
- }
- }
- }
-
- if (isset($this->type_map[$file_path])) {
- foreach ($this->type_map[$file_path] as $map_start => $_) {
- if ($map_start >= $start && $map_start <= $end) {
- unset($this->type_map[$file_path][$map_start]);
- }
- }
- }
-
- if (isset($this->reference_map[$file_path])) {
- foreach ($this->reference_map[$file_path] as $map_start => $_) {
- if ($map_start >= $start && $map_start <= $end) {
- unset($this->reference_map[$file_path][$map_start]);
- }
- }
- }
-
- if (isset($this->argument_map[$file_path])) {
- foreach ($this->argument_map[$file_path] as $map_start => $_) {
- if ($map_start >= $start && $map_start <= $end) {
- unset($this->argument_map[$file_path][$map_start]);
- }
- }
- }
- }
-
- /**
- * @return array<string, array<string, int>>
- */
- public function getAnalyzedMethods(): array
- {
- return $this->analyzed_methods;
- }
-
- /**
- * @return array<string, FileMapType>
- */
- public function getFileMaps(): array
- {
- $file_maps = [];
-
- foreach ($this->reference_map as $file_path => $reference_map) {
- $file_maps[$file_path] = [$reference_map, [], []];
- }
-
- foreach ($this->type_map as $file_path => $type_map) {
- if (isset($file_maps[$file_path])) {
- $file_maps[$file_path][1] = $type_map;
- } else {
- $file_maps[$file_path] = [[], $type_map, []];
- }
- }
-
- foreach ($this->argument_map as $file_path => $argument_map) {
- if (isset($file_maps[$file_path])) {
- $file_maps[$file_path][2] = $argument_map;
- } else {
- $file_maps[$file_path] = [[], [], $argument_map];
- }
- }
-
- return $file_maps;
- }
-
- /**
- * @return FileMapType
- */
- public function getMapsForFile(string $file_path): array
- {
- return [
- $this->reference_map[$file_path] ?? [],
- $this->type_map[$file_path] ?? [],
- $this->argument_map[$file_path] ?? [],
- ];
- }
-
- /**
- * @return array<string, array<int, Union>>
- */
- public function getPossibleMethodParamTypes(): array
- {
- return $this->possible_method_param_types;
- }
-
- public function addMutableClass(string $fqcln): void
- {
- $this->mutable_classes[strtolower($fqcln)] = true;
- }
-
- public function setAnalyzedMethod(string $file_path, string $method_id, bool $is_constructor = false): void
- {
- $this->analyzed_methods[$file_path][$method_id] = $is_constructor ? 2 : 1;
- }
-
- public function isMethodAlreadyAnalyzed(string $file_path, string $method_id, bool $is_constructor = false): bool
- {
- if ($is_constructor) {
- return isset($this->analyzed_methods[$file_path][$method_id])
- && $this->analyzed_methods[$file_path][$method_id] === 2;
- }
-
- return isset($this->analyzed_methods[$file_path][$method_id]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php
deleted file mode 100644
index d386344..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\Codebase;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TMixed;
-
-use function array_merge;
-use function array_values;
-use function preg_match;
-use function sprintf;
-use function str_replace;
-
-/**
- * @internal
- */
-final class ClassConstantByWildcardResolver
-{
- /**
- * @var Codebase
- */
- private $codebase;
-
- public function __construct(Codebase $codebase)
- {
- $this->codebase = $codebase;
- }
-
- /**
- * @return list<Atomic>|null
- */
- public function resolve(string $class_name, string $constant_pattern): ?array
- {
- if (!$this->codebase->classlike_storage_provider->has($class_name)) {
- return null;
- }
-
- $constant_regex_pattern = sprintf('#^%s$#', str_replace('*', '.*', $constant_pattern));
-
- $class_like_storage = $this->codebase->classlike_storage_provider->get($class_name);
- $matched_class_constant_types = [];
-
- foreach ($class_like_storage->constants as $constant => $class_constant_storage) {
- if (preg_match($constant_regex_pattern, $constant) === 0) {
- continue;
- }
-
- if (! $class_constant_storage->type) {
- $matched_class_constant_types[] = [new TMixed()];
- continue;
- }
-
- $matched_class_constant_types[] = $class_constant_storage->type->getAtomicTypes();
- }
-
- return array_values(array_merge([], ...$matched_class_constant_types));
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php
deleted file mode 100644
index 372d1da..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php
+++ /dev/null
@@ -1,2331 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use InvalidArgumentException;
-use PhpParser;
-use PhpParser\NodeTraverser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Exception\UnpopulatedClasslikeException;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\ConstantTypeResolver;
-use Psalm\Internal\FileManipulation\ClassDocblockManipulator;
-use Psalm\Internal\FileManipulation\CodeMigration;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\PhpVisitor\TraitFinder;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\StatementsProvider;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\PossiblyUnusedMethod;
-use Psalm\Issue\PossiblyUnusedParam;
-use Psalm\Issue\PossiblyUnusedProperty;
-use Psalm\Issue\PossiblyUnusedReturnValue;
-use Psalm\Issue\UnusedClass;
-use Psalm\Issue\UnusedConstructor;
-use Psalm\Issue\UnusedMethod;
-use Psalm\Issue\UnusedParam;
-use Psalm\Issue\UnusedProperty;
-use Psalm\Issue\UnusedReturnValue;
-use Psalm\IssueBuffer;
-use Psalm\Node\VirtualNode;
-use Psalm\Progress\Progress;
-use Psalm\Progress\VoidProgress;
-use Psalm\StatementsSource;
-use Psalm\Storage\ClassConstantStorage;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Union;
-use ReflectionClass;
-use ReflectionProperty;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_merge;
-use function array_pop;
-use function count;
-use function end;
-use function explode;
-use function get_declared_classes;
-use function get_declared_interfaces;
-use function implode;
-use function preg_match;
-use function preg_quote;
-use function preg_replace;
-use function strlen;
-use function strrpos;
-use function strtolower;
-use function substr;
-
-use const PHP_EOL;
-
-/**
- * @internal
- *
- * Handles information about classes, interfaces and traits
- */
-class ClassLikes
-{
- /**
- * @var ClassLikeStorageProvider
- */
- private $classlike_storage_provider;
-
- /**
- * @var FileReferenceProvider
- */
- public $file_reference_provider;
-
- /**
- * @var array<lowercase-string, bool>
- */
- private $existing_classlikes_lc = [];
-
- /**
- * @var array<lowercase-string, bool>
- */
- private $existing_classes_lc = [];
-
- /**
- * @var array<string, bool>
- */
- private $existing_classes = [];
-
- /**
- * @var array<lowercase-string, bool>
- */
- private $existing_interfaces_lc = [];
-
- /**
- * @var array<string, bool>
- */
- private $existing_interfaces = [];
-
- /**
- * @var array<lowercase-string, bool>
- */
- private $existing_traits_lc = [];
-
- /**
- * @var array<string, bool>
- */
- private $existing_traits = [];
-
- /**
- * @var array<lowercase-string, bool>
- */
- private $existing_enums_lc = [];
-
- /**
- * @var array<string, bool>
- */
- private $existing_enums = [];
-
- /**
- * @var array<lowercase-string, string>
- */
- private $classlike_aliases = [];
-
- /**
- * @var array<string, PhpParser\Node\Stmt\Trait_>
- */
- private $trait_nodes = [];
-
- /**
- * @var bool
- */
- public $collect_references = false;
-
- /**
- * @var bool
- */
- public $collect_locations = false;
-
- /**
- * @var StatementsProvider
- */
- private $statements_provider;
-
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var Scanner
- */
- private $scanner;
-
- public function __construct(
- Config $config,
- ClassLikeStorageProvider $storage_provider,
- FileReferenceProvider $file_reference_provider,
- StatementsProvider $statements_provider,
- Scanner $scanner
- ) {
- $this->config = $config;
- $this->classlike_storage_provider = $storage_provider;
- $this->file_reference_provider = $file_reference_provider;
- $this->statements_provider = $statements_provider;
- $this->scanner = $scanner;
-
- $this->collectPredefinedClassLikes();
- }
-
- private function collectPredefinedClassLikes(): void
- {
- /** @var array<int, string> */
- $predefined_classes = get_declared_classes();
-
- foreach ($predefined_classes as $predefined_class) {
- $predefined_class = preg_replace('/^\\\/', '', $predefined_class);
- /** @psalm-suppress ArgumentTypeCoercion */
- $reflection_class = new ReflectionClass($predefined_class);
-
- if (!$reflection_class->isUserDefined()) {
- $predefined_class_lc = strtolower($predefined_class);
- $this->existing_classlikes_lc[$predefined_class_lc] = true;
- $this->existing_classes_lc[$predefined_class_lc] = true;
- $this->existing_classes[$predefined_class] = true;
- }
- }
-
- /** @var array<int, string> */
- $predefined_interfaces = get_declared_interfaces();
-
- foreach ($predefined_interfaces as $predefined_interface) {
- $predefined_interface = preg_replace('/^\\\/', '', $predefined_interface);
- /** @psalm-suppress ArgumentTypeCoercion */
- $reflection_class = new ReflectionClass($predefined_interface);
-
- if (!$reflection_class->isUserDefined()) {
- $predefined_interface_lc = strtolower($predefined_interface);
- $this->existing_classlikes_lc[$predefined_interface_lc] = true;
- $this->existing_interfaces_lc[$predefined_interface_lc] = true;
- $this->existing_interfaces[$predefined_interface] = true;
- }
- }
- }
-
- public function addFullyQualifiedClassName(string $fq_class_name, ?string $file_path = null): void
- {
- $fq_class_name_lc = strtolower($fq_class_name);
- $this->existing_classlikes_lc[$fq_class_name_lc] = true;
- $this->existing_classes_lc[$fq_class_name_lc] = true;
- $this->existing_classes[$fq_class_name] = true;
-
- $this->existing_traits_lc[$fq_class_name_lc] = false;
- $this->existing_interfaces_lc[$fq_class_name_lc] = false;
- $this->existing_enums_lc[$fq_class_name_lc] = false;
-
- if ($file_path) {
- $this->scanner->setClassLikeFilePath($fq_class_name_lc, $file_path);
- }
- }
-
- public function addFullyQualifiedInterfaceName(string $fq_class_name, ?string $file_path = null): void
- {
- $fq_class_name_lc = strtolower($fq_class_name);
- $this->existing_classlikes_lc[$fq_class_name_lc] = true;
- $this->existing_interfaces_lc[$fq_class_name_lc] = true;
- $this->existing_interfaces[$fq_class_name] = true;
-
- $this->existing_classes_lc[$fq_class_name_lc] = false;
- $this->existing_traits_lc[$fq_class_name_lc] = false;
- $this->existing_enums_lc[$fq_class_name_lc] = false;
-
- if ($file_path) {
- $this->scanner->setClassLikeFilePath($fq_class_name_lc, $file_path);
- }
- }
-
- public function addFullyQualifiedTraitName(string $fq_class_name, ?string $file_path = null): void
- {
- $fq_class_name_lc = strtolower($fq_class_name);
- $this->existing_classlikes_lc[$fq_class_name_lc] = true;
- $this->existing_traits_lc[$fq_class_name_lc] = true;
- $this->existing_traits[$fq_class_name] = true;
-
- $this->existing_classes_lc[$fq_class_name_lc] = false;
- $this->existing_interfaces_lc[$fq_class_name_lc] = false;
- $this->existing_enums[$fq_class_name] = false;
-
- if ($file_path) {
- $this->scanner->setClassLikeFilePath($fq_class_name_lc, $file_path);
- }
- }
-
- public function addFullyQualifiedEnumName(string $fq_class_name, ?string $file_path = null): void
- {
- $fq_class_name_lc = strtolower($fq_class_name);
- $this->existing_classlikes_lc[$fq_class_name_lc] = true;
- $this->existing_enums_lc[$fq_class_name_lc] = true;
- $this->existing_enums[$fq_class_name] = true;
-
- $this->existing_traits_lc[$fq_class_name_lc] = false;
- $this->existing_classes_lc[$fq_class_name_lc] = false;
- $this->existing_interfaces_lc[$fq_class_name_lc] = false;
-
- if ($file_path) {
- $this->scanner->setClassLikeFilePath($fq_class_name_lc, $file_path);
- }
- }
-
- public function addFullyQualifiedClassLikeName(string $fq_class_name_lc, ?string $file_path = null): void
- {
- if ($file_path) {
- $this->scanner->setClassLikeFilePath($fq_class_name_lc, $file_path);
- }
- }
-
- /**
- * @return list<string>
- */
- public function getMatchingClassLikeNames(string $stub): array
- {
- $matching_classes = [];
-
- if ($stub[0] === '*') {
- $stub = substr($stub, 1);
- }
-
- $fully_qualified = false;
-
- if ($stub[0] === '\\') {
- $fully_qualified = true;
- $stub = substr($stub, 1);
- } else {
- // for any not-fully-qualified class name the bit we care about comes after a dash
- [, $stub] = explode('-', $stub);
- }
-
- $stub = preg_quote(strtolower($stub));
-
- if ($fully_qualified) {
- $stub = '^' . $stub;
- } else {
- $stub = '(^|\\\)' . $stub;
- }
-
- foreach ($this->existing_classes as $fq_classlike_name => $found) {
- if (!$found) {
- continue;
- }
-
- if (preg_match('@' . $stub . '.*@i', $fq_classlike_name)) {
- $matching_classes[] = $fq_classlike_name;
- }
- }
-
- foreach ($this->existing_interfaces as $fq_classlike_name => $found) {
- if (!$found) {
- continue;
- }
-
- if (preg_match('@' . $stub . '.*@i', $fq_classlike_name)) {
- $matching_classes[] = $fq_classlike_name;
- }
- }
-
- return $matching_classes;
- }
-
- public function hasFullyQualifiedClassName(
- string $fq_class_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- $fq_class_name_lc = strtolower($this->getUnAliasedName($fq_class_name));
-
- if ($code_location) {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClass(
- $calling_method_id,
- $fq_class_name_lc
- );
- } elseif (!$calling_fq_class_name || strtolower($calling_fq_class_name) !== $fq_class_name_lc) {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $code_location->file_path,
- $fq_class_name_lc
- );
-
- if ($calling_fq_class_name) {
- $class_storage = $this->classlike_storage_provider->get($calling_fq_class_name);
-
- if ($class_storage->location
- && $class_storage->location->file_path !== $code_location->file_path
- ) {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $class_storage->location->file_path,
- $fq_class_name_lc
- );
- }
- }
- }
- }
-
- if (!isset($this->existing_classes_lc[$fq_class_name_lc])
- || !$this->existing_classes_lc[$fq_class_name_lc]
- || !$this->classlike_storage_provider->has($fq_class_name_lc)
- ) {
- if ((
- !isset($this->existing_classes_lc[$fq_class_name_lc])
- || $this->existing_classes_lc[$fq_class_name_lc]
- )
- && !$this->classlike_storage_provider->has($fq_class_name_lc)
- ) {
- if (!isset($this->existing_classes_lc[$fq_class_name_lc])) {
- $this->existing_classes_lc[$fq_class_name_lc] = false;
-
- return false;
- }
-
- return $this->existing_classes_lc[$fq_class_name_lc];
- }
-
- return false;
- }
-
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClass(
- $code_location,
- strtolower($fq_class_name)
- );
- }
-
- return true;
- }
-
- public function hasFullyQualifiedInterfaceName(
- string $fq_class_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- $fq_class_name_lc = strtolower($this->getUnAliasedName($fq_class_name));
-
- if (!isset($this->existing_interfaces_lc[$fq_class_name_lc])
- || !$this->existing_interfaces_lc[$fq_class_name_lc]
- || !$this->classlike_storage_provider->has($fq_class_name_lc)
- ) {
- if ((
- !isset($this->existing_classes_lc[$fq_class_name_lc])
- || $this->existing_classes_lc[$fq_class_name_lc]
- )
- && !$this->classlike_storage_provider->has($fq_class_name_lc)
- ) {
- if (!isset($this->existing_interfaces_lc[$fq_class_name_lc])) {
- $this->existing_interfaces_lc[$fq_class_name_lc] = false;
-
- return false;
- }
-
- return $this->existing_interfaces_lc[$fq_class_name_lc];
- }
-
- return false;
- }
-
- if ($this->collect_references && $code_location) {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClass(
- $calling_method_id,
- $fq_class_name_lc
- );
- } else {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $code_location->file_path,
- $fq_class_name_lc
- );
-
- if ($calling_fq_class_name) {
- $class_storage = $this->classlike_storage_provider->get($calling_fq_class_name);
-
- if ($class_storage->location
- && $class_storage->location->file_path !== $code_location->file_path
- ) {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $class_storage->location->file_path,
- $fq_class_name_lc
- );
- }
- }
- }
- }
-
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClass(
- $code_location,
- strtolower($fq_class_name)
- );
- }
-
- return true;
- }
-
- public function hasFullyQualifiedEnumName(
- string $fq_class_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- $fq_class_name_lc = strtolower($this->getUnAliasedName($fq_class_name));
-
- if (!isset($this->existing_enums_lc[$fq_class_name_lc])
- || !$this->existing_enums_lc[$fq_class_name_lc]
- || !$this->classlike_storage_provider->has($fq_class_name_lc)
- ) {
- if ((
- !isset($this->existing_classes_lc[$fq_class_name_lc])
- || $this->existing_classes_lc[$fq_class_name_lc]
- )
- && !$this->classlike_storage_provider->has($fq_class_name_lc)
- ) {
- if (!isset($this->existing_enums_lc[$fq_class_name_lc])) {
- $this->existing_enums_lc[$fq_class_name_lc] = false;
-
- return false;
- }
-
- return $this->existing_enums_lc[$fq_class_name_lc];
- }
-
- return false;
- }
-
- if ($this->collect_references && $code_location) {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClass(
- $calling_method_id,
- $fq_class_name_lc
- );
- } else {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $code_location->file_path,
- $fq_class_name_lc
- );
-
- if ($calling_fq_class_name) {
- $class_storage = $this->classlike_storage_provider->get($calling_fq_class_name);
-
- if ($class_storage->location
- && $class_storage->location->file_path !== $code_location->file_path
- ) {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $class_storage->location->file_path,
- $fq_class_name_lc
- );
- }
- }
- }
- }
-
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClass(
- $code_location,
- strtolower($fq_class_name)
- );
- }
-
- return true;
- }
-
- public function hasFullyQualifiedTraitName(string $fq_class_name, ?CodeLocation $code_location = null): bool
- {
- $fq_class_name_lc = strtolower($this->getUnAliasedName($fq_class_name));
-
- if (!isset($this->existing_traits_lc[$fq_class_name_lc]) ||
- !$this->existing_traits_lc[$fq_class_name_lc]
- ) {
- return false;
- }
-
- if ($this->collect_references && $code_location) {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $code_location->file_path,
- $fq_class_name_lc
- );
- }
-
- return true;
- }
-
- /**
- * Check whether a class/interface exists
- */
- public function classOrInterfaceExists(
- string $fq_class_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- return $this->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
- || $this->interfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id);
- }
-
- /**
- * Check whether a class/interface exists
- */
- public function classOrInterfaceOrEnumExists(
- string $fq_class_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- return $this->classExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
- || $this->interfaceExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id)
- || $this->enumExists($fq_class_name, $code_location, $calling_fq_class_name, $calling_method_id);
- }
-
- /**
- * Determine whether or not a given class exists
- */
- public function classExists(
- string $fq_class_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- if (isset(ClassLikeAnalyzer::SPECIAL_TYPES[$fq_class_name])) {
- return false;
- }
-
- if ($fq_class_name === 'Generator') {
- return true;
- }
-
- return $this->hasFullyQualifiedClassName(
- $fq_class_name,
- $code_location,
- $calling_fq_class_name,
- $calling_method_id
- );
- }
-
- /**
- * Determine whether or not a class extends a parent
- *
- * @throws UnpopulatedClasslikeException when called on unpopulated class
- * @throws InvalidArgumentException when class does not exist
- */
- public function classExtends(string $fq_class_name, string $possible_parent, bool $from_api = false): bool
- {
- $unaliased_fq_class_name = $this->getUnAliasedName($fq_class_name);
- $unaliased_fq_class_name_lc = strtolower($unaliased_fq_class_name);
-
- if ($unaliased_fq_class_name_lc === 'generator') {
- return false;
- }
-
- $class_storage = $this->classlike_storage_provider->get($unaliased_fq_class_name);
-
- if ($from_api && !$class_storage->populated) {
- throw new UnpopulatedClasslikeException($fq_class_name);
- }
-
- return isset($class_storage->parent_classes[strtolower($possible_parent)]);
- }
-
- /**
- * Check whether a class implements an interface
- */
- public function classImplements(string $fq_class_name, string $interface): bool
- {
- $interface_id = strtolower($interface);
-
- $fq_class_name = strtolower($fq_class_name);
-
- if ($interface_id === 'callable' && $fq_class_name === 'closure') {
- return true;
- }
-
- if ($interface_id === 'traversable' && $fq_class_name === 'generator') {
- return true;
- }
-
- if ($interface_id === 'traversable' && $fq_class_name === 'iterator') {
- return true;
- }
-
- if (isset(ClassLikeAnalyzer::SPECIAL_TYPES[$interface_id])
- || isset(ClassLikeAnalyzer::SPECIAL_TYPES[$fq_class_name])
- ) {
- return false;
- }
-
- $fq_class_name = $this->getUnAliasedName($fq_class_name);
-
- if (!$this->classlike_storage_provider->has($fq_class_name)) {
- return false;
- }
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- if (isset($class_storage->class_implements[$interface_id])) {
- return true;
- }
-
- foreach ($class_storage->class_implements as $implementing_interface_lc => $_) {
- $aliased_interface_lc = strtolower(
- $this->getUnAliasedName($implementing_interface_lc)
- );
-
- if ($aliased_interface_lc === $interface_id) {
- return true;
- }
- }
-
- return false;
- }
-
- public function interfaceExists(
- string $fq_interface_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- if (isset(ClassLikeAnalyzer::SPECIAL_TYPES[strtolower($fq_interface_name)])) {
- return false;
- }
-
- return $this->hasFullyQualifiedInterfaceName(
- $fq_interface_name,
- $code_location,
- $calling_fq_class_name,
- $calling_method_id
- );
- }
-
- public function enumExists(
- string $fq_enum_name,
- ?CodeLocation $code_location = null,
- ?string $calling_fq_class_name = null,
- ?string $calling_method_id = null
- ): bool {
- if (isset(ClassLikeAnalyzer::SPECIAL_TYPES[strtolower($fq_enum_name)])) {
- return false;
- }
-
- return $this->hasFullyQualifiedEnumName(
- $fq_enum_name,
- $code_location,
- $calling_fq_class_name,
- $calling_method_id
- );
- }
-
- public function interfaceExtends(string $interface_name, string $possible_parent): bool
- {
- return isset($this->getParentInterfaces($interface_name)[strtolower($possible_parent)]);
- }
-
- /**
- * @return array<lowercase-string, string> all interfaces extended by $interface_name
- */
- public function getParentInterfaces(string $fq_interface_name): array
- {
- $fq_interface_name = strtolower($fq_interface_name);
-
- return $this->classlike_storage_provider->get($fq_interface_name)->parent_interfaces;
- }
-
- public function traitExists(string $fq_trait_name, ?CodeLocation $code_location = null): bool
- {
- return $this->hasFullyQualifiedTraitName($fq_trait_name, $code_location);
- }
-
- /**
- * Determine whether or not a class has the correct casing
- */
- public function classHasCorrectCasing(string $fq_class_name): bool
- {
- if ($fq_class_name === 'Generator') {
- return true;
- }
-
- if (isset($this->classlike_aliases[strtolower($fq_class_name)])) {
- return true;
- }
-
- return isset($this->existing_classes[$fq_class_name]);
- }
-
- public function interfaceHasCorrectCasing(string $fq_interface_name): bool
- {
- if (isset($this->classlike_aliases[strtolower($fq_interface_name)])) {
- return true;
- }
-
- return isset($this->existing_interfaces[$fq_interface_name]);
- }
-
- public function enumHasCorrectCasing(string $fq_enum_name): bool
- {
- if (isset($this->classlike_aliases[strtolower($fq_enum_name)])) {
- return true;
- }
-
- return isset($this->existing_enums[$fq_enum_name]);
- }
-
- public function traitHasCorrectCase(string $fq_trait_name): bool
- {
- if (isset($this->classlike_aliases[strtolower($fq_trait_name)])) {
- return true;
- }
-
- return isset($this->existing_traits[$fq_trait_name]);
- }
-
- /**
- * @param lowercase-string $fq_class_name
- */
- public function isUserDefined(string $fq_class_name): bool
- {
- return $this->classlike_storage_provider->get($fq_class_name)->user_defined;
- }
-
- public function getTraitNode(string $fq_trait_name): PhpParser\Node\Stmt\Trait_
- {
- $fq_trait_name_lc = strtolower($fq_trait_name);
-
- if (isset($this->trait_nodes[$fq_trait_name_lc])) {
- return $this->trait_nodes[$fq_trait_name_lc];
- }
-
- $storage = $this->classlike_storage_provider->get($fq_trait_name);
-
- if (!$storage->location) {
- throw new UnexpectedValueException('Storage should exist for ' . $fq_trait_name);
- }
-
- $file_statements = $this->statements_provider->getStatementsForFile($storage->location->file_path, '7.4');
-
- $trait_finder = new TraitFinder($fq_trait_name);
-
- $traverser = new NodeTraverser();
- $traverser->addVisitor(
- $trait_finder
- );
-
- $traverser->traverse($file_statements);
-
- $trait_node = $trait_finder->getNode();
-
- if ($trait_node) {
- $this->trait_nodes[$fq_trait_name_lc] = $trait_node;
-
- return $trait_node;
- }
-
- throw new UnexpectedValueException('Could not locate trait statement');
- }
-
- /**
- * @param lowercase-string $alias_name
- */
- public function addClassAlias(string $fq_class_name, string $alias_name): void
- {
- $this->classlike_aliases[$alias_name] = $fq_class_name;
- }
-
- public function getUnAliasedName(string $alias_name): string
- {
- $alias_name_lc = strtolower($alias_name);
- if ($this->existing_classlikes_lc[$alias_name_lc] ?? false) {
- return $alias_name;
- }
-
- $result = $this->classlike_aliases[$alias_name_lc] ?? $alias_name;
- if ($result === $alias_name) {
- return $result;
- }
-
- return $this->getUnAliasedName($result);
- }
-
- public function consolidateAnalyzedData(Methods $methods, ?Progress $progress, bool $find_unused_code): void
- {
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- $progress->debug('Checking class references' . PHP_EOL);
-
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- foreach ($this->existing_classlikes_lc as $fq_class_name_lc => $_) {
- try {
- $classlike_storage = $this->classlike_storage_provider->get($fq_class_name_lc);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if ($classlike_storage->location
- && $this->config->isInProjectDirs($classlike_storage->location->file_path)
- && !$classlike_storage->is_trait
- ) {
- if ($find_unused_code) {
- if (!$this->file_reference_provider->isClassReferenced($fq_class_name_lc)) {
- IssueBuffer::maybeAdd(
- new UnusedClass(
- 'Class ' . $classlike_storage->name . ' is never used',
- $classlike_storage->location,
- $classlike_storage->name
- ),
- $classlike_storage->suppressed_issues
- );
- } else {
- $this->checkMethodReferences($classlike_storage, $methods);
- $this->checkPropertyReferences($classlike_storage);
- }
- }
-
- $this->findPossibleMethodParamTypes($classlike_storage);
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MissingImmutableAnnotation'])
- && !isset($codebase->analyzer->mutable_classes[$fq_class_name_lc])
- && !$classlike_storage->external_mutation_free
- && $classlike_storage->properties
- && isset($classlike_storage->methods['__construct'])
- ) {
- $stmts = $codebase->getStatementsForFile(
- $classlike_storage->location->file_path
- );
-
- foreach ($stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\Namespace_) {
- foreach ($stmt->stmts as $namespace_stmt) {
- if ($namespace_stmt instanceof PhpParser\Node\Stmt\Class_
- && strtolower((string) $stmt->name . '\\' . (string) $namespace_stmt->name)
- === $fq_class_name_lc
- ) {
- self::makeImmutable(
- $namespace_stmt,
- $project_analyzer,
- $classlike_storage->location->file_path
- );
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Class_
- && strtolower((string) $stmt->name) === $fq_class_name_lc
- ) {
- self::makeImmutable(
- $stmt,
- $project_analyzer,
- $classlike_storage->location->file_path
- );
- }
- }
- }
- }
- }
- }
-
- public static function makeImmutable(
- PhpParser\Node\Stmt\Class_ $class_stmt,
- ProjectAnalyzer $project_analyzer,
- string $file_path
- ): void {
- $manipulator = ClassDocblockManipulator::getForClass(
- $project_analyzer,
- $file_path,
- $class_stmt
- );
-
- $manipulator->makeImmutable();
- }
-
- public function moveMethods(Methods $methods, ?Progress $progress = null): void
- {
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- if (!$codebase->methods_to_move) {
- return;
- }
-
- $progress->debug('Refactoring methods ' . PHP_EOL);
-
- $code_migrations = [];
-
- foreach ($codebase->methods_to_move as $source => $destination) {
- $source_parts = explode('::', $source);
-
- try {
- $source_method_storage = $methods->getStorage(
- new MethodIdentifier(...$source_parts)
- );
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- [$destination_fq_class_name, $destination_name] = explode('::', $destination);
-
- try {
- $classlike_storage = $this->classlike_storage_provider->get($destination_fq_class_name);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if ($classlike_storage->stmt_location
- && $this->config->isInProjectDirs($classlike_storage->stmt_location->file_path)
- && $source_method_storage->stmt_location
- && $source_method_storage->stmt_location->file_path
- && $source_method_storage->location
- ) {
- $new_class_bounds = $classlike_storage->stmt_location->getSnippetBounds();
- $old_method_bounds = $source_method_storage->stmt_location->getSnippetBounds();
-
- $old_method_name_bounds = $source_method_storage->location->getSelectionBounds();
-
- FileManipulationBuffer::add(
- $source_method_storage->stmt_location->file_path,
- [
- new FileManipulation(
- $old_method_name_bounds[0],
- $old_method_name_bounds[1],
- $destination_name
- ),
- ]
- );
-
- $selection = $classlike_storage->stmt_location->getSnippet();
-
- $insert_pos = strrpos($selection, "\n", -1);
-
- if (!$insert_pos) {
- $insert_pos = strlen($selection) - 1;
- } else {
- ++$insert_pos;
- }
-
- $code_migrations[] = new CodeMigration(
- $source_method_storage->stmt_location->file_path,
- $old_method_bounds[0],
- $old_method_bounds[1],
- $classlike_storage->stmt_location->file_path,
- $new_class_bounds[0] + $insert_pos
- );
- }
- }
-
- FileManipulationBuffer::addCodeMigrations($code_migrations);
- }
-
- public function moveProperties(Properties $properties, ?Progress $progress = null): void
- {
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- if (!$codebase->properties_to_move) {
- return;
- }
-
- $progress->debug('Refacting properties ' . PHP_EOL);
-
- $code_migrations = [];
-
- foreach ($codebase->properties_to_move as $source => $destination) {
- try {
- $source_property_storage = $properties->getStorage($source);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- [$source_fq_class_name] = explode('::$', $source);
- [$destination_fq_class_name, $destination_name] = explode('::$', $destination);
-
- $source_classlike_storage = $this->classlike_storage_provider->get($source_fq_class_name);
- $destination_classlike_storage = $this->classlike_storage_provider->get($destination_fq_class_name);
-
- if ($destination_classlike_storage->stmt_location
- && $this->config->isInProjectDirs($destination_classlike_storage->stmt_location->file_path)
- && $source_property_storage->stmt_location
- && $source_property_storage->stmt_location->file_path
- && $source_property_storage->location
- ) {
- if ($source_property_storage->type
- && $source_property_storage->type_location
- && $source_property_storage->type_location !== $source_property_storage->signature_type_location
- ) {
- $bounds = $source_property_storage->type_location->getSelectionBounds();
-
- $replace_type = TypeExpander::expandUnion(
- $codebase,
- $source_property_storage->type,
- $source_classlike_storage->name,
- $source_classlike_storage->name,
- $source_classlike_storage->parent_class
- );
-
- $this->airliftClassDefinedDocblockType(
- $replace_type,
- $destination_fq_class_name,
- $source_property_storage->stmt_location->file_path,
- $bounds[0],
- $bounds[1]
- );
- }
-
- $new_class_bounds = $destination_classlike_storage->stmt_location->getSnippetBounds();
- $old_property_bounds = $source_property_storage->stmt_location->getSnippetBounds();
-
- $old_property_name_bounds = $source_property_storage->location->getSelectionBounds();
-
- FileManipulationBuffer::add(
- $source_property_storage->stmt_location->file_path,
- [
- new FileManipulation(
- $old_property_name_bounds[0],
- $old_property_name_bounds[1],
- '$' . $destination_name
- ),
- ]
- );
-
- $selection = $destination_classlike_storage->stmt_location->getSnippet();
-
- $insert_pos = strrpos($selection, "\n", -1);
-
- if (!$insert_pos) {
- $insert_pos = strlen($selection) - 1;
- } else {
- ++$insert_pos;
- }
-
- $code_migrations[] = new CodeMigration(
- $source_property_storage->stmt_location->file_path,
- $old_property_bounds[0],
- $old_property_bounds[1],
- $destination_classlike_storage->stmt_location->file_path,
- $new_class_bounds[0] + $insert_pos
- );
- }
- }
-
- FileManipulationBuffer::addCodeMigrations($code_migrations);
- }
-
- public function moveClassConstants(?Progress $progress = null): void
- {
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- if (!$codebase->class_constants_to_move) {
- return;
- }
-
- $progress->debug('Refacting constants ' . PHP_EOL);
-
- $code_migrations = [];
-
- foreach ($codebase->class_constants_to_move as $source => $destination) {
- [$source_fq_class_name, $source_const_name] = explode('::', $source);
- [$destination_fq_class_name, $destination_name] = explode('::', $destination);
-
- $source_classlike_storage = $this->classlike_storage_provider->get($source_fq_class_name);
- $destination_classlike_storage = $this->classlike_storage_provider->get($destination_fq_class_name);
-
- $constant_storage = $source_classlike_storage->constants[$source_const_name];
-
- $source_const_stmt_location = $constant_storage->stmt_location;
- $source_const_location = $constant_storage->location;
-
- if (!$source_const_location || !$source_const_stmt_location) {
- continue;
- }
-
- if ($destination_classlike_storage->stmt_location
- && $this->config->isInProjectDirs($destination_classlike_storage->stmt_location->file_path)
- && $source_const_stmt_location->file_path
- ) {
- $new_class_bounds = $destination_classlike_storage->stmt_location->getSnippetBounds();
- $old_const_bounds = $source_const_stmt_location->getSnippetBounds();
-
- $old_const_name_bounds = $source_const_location->getSelectionBounds();
-
- FileManipulationBuffer::add(
- $source_const_stmt_location->file_path,
- [
- new FileManipulation(
- $old_const_name_bounds[0],
- $old_const_name_bounds[1],
- $destination_name
- ),
- ]
- );
-
- $selection = $destination_classlike_storage->stmt_location->getSnippet();
-
- $insert_pos = strrpos($selection, "\n", -1);
-
- if (!$insert_pos) {
- $insert_pos = strlen($selection) - 1;
- } else {
- ++$insert_pos;
- }
-
- $code_migrations[] = new CodeMigration(
- $source_const_stmt_location->file_path,
- $old_const_bounds[0],
- $old_const_bounds[1],
- $destination_classlike_storage->stmt_location->file_path,
- $new_class_bounds[0] + $insert_pos
- );
- }
- }
-
- FileManipulationBuffer::addCodeMigrations($code_migrations);
- }
-
- /**
- * @param lowercase-string|null $calling_method_id
- */
- public function handleClassLikeReferenceInMigration(
- Codebase $codebase,
- StatementsSource $source,
- PhpParser\Node $class_name_node,
- string $fq_class_name,
- ?string $calling_method_id,
- bool $force_change = false,
- bool $was_self = false
- ): bool {
- if ($class_name_node instanceof VirtualNode) {
- return false;
- }
- $calling_fq_class_name = $source->getFQCLN();
-
- // if we're inside a moved class static method
- if ($codebase->methods_to_move
- && $calling_fq_class_name
- && $calling_method_id
- && isset($codebase->methods_to_move[$calling_method_id])
- ) {
- $destination_class = explode('::', $codebase->methods_to_move[$calling_method_id])[0];
-
- $intended_fq_class_name = strtolower($calling_fq_class_name) === strtolower($fq_class_name)
- && isset($codebase->classes_to_move[strtolower($calling_fq_class_name)])
- ? $destination_class
- : $fq_class_name;
-
- $this->airliftClassLikeReference(
- $intended_fq_class_name,
- $destination_class,
- $source->getFilePath(),
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1,
- $class_name_node instanceof PhpParser\Node\Scalar\MagicConst\Class_,
- $was_self
- );
-
- return true;
- }
-
- // if we're outside a moved class, but we're changing all references to a class
- if (isset($codebase->class_transforms[strtolower($fq_class_name)])) {
- $new_fq_class_name = $codebase->class_transforms[strtolower($fq_class_name)];
- $file_manipulations = [];
-
- if ($class_name_node instanceof PhpParser\Node\Identifier) {
- $destination_parts = explode('\\', $new_fq_class_name);
-
- $destination_class_name = array_pop($destination_parts);
-
- $file_manipulations[] = new FileManipulation(
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1,
- $destination_class_name
- );
-
- FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
-
- return true;
- }
-
- $uses_flipped = $source->getAliasedClassesFlipped();
- $uses_flipped_replaceable = $source->getAliasedClassesFlippedReplaceable();
-
- $old_fq_class_name = strtolower($fq_class_name);
-
- $migrated_source_fqcln = $calling_fq_class_name;
-
- if ($calling_fq_class_name
- && isset($codebase->class_transforms[strtolower($calling_fq_class_name)])
- ) {
- $migrated_source_fqcln = $codebase->class_transforms[strtolower($calling_fq_class_name)];
- }
-
- $source_namespace = $source->getNamespace();
-
- if ($migrated_source_fqcln && $calling_fq_class_name !== $migrated_source_fqcln) {
- $new_source_parts = explode('\\', $migrated_source_fqcln, -1);
- $source_namespace = implode('\\', $new_source_parts);
- }
-
- if (isset($uses_flipped_replaceable[$old_fq_class_name])) {
- $alias = $uses_flipped_replaceable[$old_fq_class_name];
- unset($uses_flipped[$old_fq_class_name]);
- $old_class_name_parts = explode('\\', $old_fq_class_name);
- $old_class_name = end($old_class_name_parts);
- if ($old_class_name === strtolower($alias)) {
- $new_class_name_parts = explode('\\', $new_fq_class_name);
- $new_class_name = end($new_class_name_parts);
- $uses_flipped[strtolower($new_fq_class_name)] = $new_class_name;
- } else {
- $uses_flipped[strtolower($new_fq_class_name)] = $alias;
- }
- }
-
- $file_manipulations[] = new FileManipulation(
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1,
- Type::getStringFromFQCLN(
- $new_fq_class_name,
- $source_namespace,
- $uses_flipped,
- $migrated_source_fqcln,
- $was_self
- )
- . ($class_name_node instanceof PhpParser\Node\Scalar\MagicConst\Class_ ? '::class' : '')
- );
-
- FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
-
- return true;
- }
-
- // if we're inside a moved class (could be a method, could be a property/class const default)
- if ($codebase->classes_to_move
- && $calling_fq_class_name
- && isset($codebase->classes_to_move[strtolower($calling_fq_class_name)])
- ) {
- $destination_class = $codebase->classes_to_move[strtolower($calling_fq_class_name)];
-
- if ($class_name_node instanceof PhpParser\Node\Identifier) {
- $destination_parts = explode('\\', $destination_class);
-
- $destination_class_name = array_pop($destination_parts);
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1,
- $destination_class_name
- );
-
- FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
- } else {
- $this->airliftClassLikeReference(
- strtolower($calling_fq_class_name) === strtolower($fq_class_name)
- ? $destination_class
- : $fq_class_name,
- $destination_class,
- $source->getFilePath(),
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1,
- $class_name_node instanceof PhpParser\Node\Scalar\MagicConst\Class_
- );
- }
-
- return true;
- }
-
- if ($force_change) {
- if ($calling_fq_class_name) {
- $this->airliftClassLikeReference(
- $fq_class_name,
- $calling_fq_class_name,
- $source->getFilePath(),
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1
- );
- } else {
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- (int) $class_name_node->getAttribute('startFilePos'),
- (int) $class_name_node->getAttribute('endFilePos') + 1,
- Type::getStringFromFQCLN(
- $fq_class_name,
- $source->getNamespace(),
- $source->getAliasedClassesFlipped(),
- null
- )
- );
-
- FileManipulationBuffer::add($source->getFilePath(), $file_manipulations);
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * @param lowercase-string|null $calling_method_id
- */
- public function handleDocblockTypeInMigration(
- Codebase $codebase,
- StatementsSource $source,
- Union $type,
- CodeLocation $type_location,
- ?string $calling_method_id
- ): void {
- $calling_fq_class_name = $source->getFQCLN();
- $fq_class_name_lc = strtolower($calling_fq_class_name ?? '');
-
- $moved_type = false;
-
- // if we're inside a moved class static method
- if ($codebase->methods_to_move
- && $calling_fq_class_name
- && $calling_method_id
- && isset($codebase->methods_to_move[$calling_method_id])
- ) {
- $bounds = $type_location->getSelectionBounds();
-
- $destination_class = explode('::', $codebase->methods_to_move[$calling_method_id])[0];
-
- $this->airliftClassDefinedDocblockType(
- $type,
- $destination_class,
- $source->getFilePath(),
- $bounds[0],
- $bounds[1]
- );
-
- $moved_type = true;
- }
-
- // if we're outside a moved class, but we're changing all references to a class
- if (!$moved_type && $codebase->class_transforms) {
- $uses_flipped = $source->getAliasedClassesFlipped();
- $uses_flipped_replaceable = $source->getAliasedClassesFlippedReplaceable();
-
- $migrated_source_fqcln = $calling_fq_class_name;
-
- if ($calling_fq_class_name
- && isset($codebase->class_transforms[$fq_class_name_lc])
- ) {
- $migrated_source_fqcln = $codebase->class_transforms[$fq_class_name_lc];
- }
-
- $source_namespace = $source->getNamespace();
-
- if ($migrated_source_fqcln && $calling_fq_class_name !== $migrated_source_fqcln) {
- $new_source_parts = explode('\\', $migrated_source_fqcln, -1);
- $source_namespace = implode('\\', $new_source_parts);
- }
-
- foreach ($codebase->class_transforms as $old_fq_class_name => $new_fq_class_name) {
- if (isset($uses_flipped_replaceable[$old_fq_class_name])) {
- $alias = $uses_flipped_replaceable[$old_fq_class_name];
- unset($uses_flipped[$old_fq_class_name]);
- $old_class_name_parts = explode('\\', $old_fq_class_name);
- $old_class_name = end($old_class_name_parts);
- if ($old_class_name === strtolower($alias)) {
- $new_class_name_parts = explode('\\', $new_fq_class_name);
- $new_class_name = end($new_class_name_parts);
- $uses_flipped[strtolower($new_fq_class_name)] = $new_class_name;
- } else {
- $uses_flipped[strtolower($new_fq_class_name)] = $alias;
- }
- }
- }
-
- foreach ($codebase->class_transforms as $old_fq_class_name => $new_fq_class_name) {
- if ($type->containsClassLike($old_fq_class_name)) {
- $type = clone $type;
-
- $type->replaceClassLike($old_fq_class_name, $new_fq_class_name);
-
- $bounds = $type_location->getSelectionBounds();
-
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- $bounds[0],
- $bounds[1],
- $type->toNamespacedString(
- $source_namespace,
- $uses_flipped,
- $migrated_source_fqcln,
- false
- )
- );
-
- FileManipulationBuffer::add(
- $source->getFilePath(),
- $file_manipulations
- );
-
- $moved_type = true;
- }
- }
- }
-
- // if we're inside a moved class (could be a method, could be a property/class const default)
- if (!$moved_type
- && $codebase->classes_to_move
- && $calling_fq_class_name
- && isset($codebase->classes_to_move[$fq_class_name_lc])
- ) {
- $bounds = $type_location->getSelectionBounds();
-
- $destination_class = $codebase->classes_to_move[$fq_class_name_lc];
-
- if ($type->containsClassLike($fq_class_name_lc)) {
- $type = clone $type;
-
- $type->replaceClassLike($fq_class_name_lc, $destination_class);
- }
-
- $this->airliftClassDefinedDocblockType(
- $type,
- $destination_class,
- $source->getFilePath(),
- $bounds[0],
- $bounds[1]
- );
- }
- }
-
- public function airliftClassLikeReference(
- string $fq_class_name,
- string $destination_fq_class_name,
- string $source_file_path,
- int $source_start,
- int $source_end,
- bool $add_class_constant = false,
- bool $allow_self = false
- ): void {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- $destination_class_storage = $codebase->classlike_storage_provider->get($destination_fq_class_name);
-
- if (!$destination_class_storage->aliases) {
- throw new UnexpectedValueException('Aliases should not be null');
- }
-
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- $source_start,
- $source_end,
- Type::getStringFromFQCLN(
- $fq_class_name,
- $destination_class_storage->aliases->namespace,
- $destination_class_storage->aliases->uses_flipped,
- $destination_class_storage->name,
- $allow_self
- ) . ($add_class_constant ? '::class' : '')
- );
-
- FileManipulationBuffer::add(
- $source_file_path,
- $file_manipulations
- );
- }
-
- public function airliftClassDefinedDocblockType(
- Union $type,
- string $destination_fq_class_name,
- string $source_file_path,
- int $source_start,
- int $source_end
- ): void {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- $destination_class_storage = $codebase->classlike_storage_provider->get($destination_fq_class_name);
-
- if (!$destination_class_storage->aliases) {
- throw new UnexpectedValueException('Aliases should not be null');
- }
-
- $file_manipulations = [];
-
- $file_manipulations[] = new FileManipulation(
- $source_start,
- $source_end,
- $type->toNamespacedString(
- $destination_class_storage->aliases->namespace,
- $destination_class_storage->aliases->uses_flipped,
- $destination_class_storage->name,
- false
- )
- );
-
- FileManipulationBuffer::add(
- $source_file_path,
- $file_manipulations
- );
- }
-
- /**
- * @param ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PRIVATE
- * $visibility
- *
- * @return array<string, ClassConstantStorage>
- */
- public function getConstantsForClass(string $class_name, int $visibility): array
- {
- $class_name = strtolower($class_name);
-
- $storage = $this->classlike_storage_provider->get($class_name);
-
- if ($visibility === ReflectionProperty::IS_PUBLIC) {
- return array_filter(
- $storage->constants,
- function ($constant) {
- return $constant->type
- && $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- }
- );
- }
-
- if ($visibility === ReflectionProperty::IS_PROTECTED) {
- return array_filter(
- $storage->constants,
- function ($constant) {
- return $constant->type
- && ($constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
- || $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED);
- }
- );
- }
-
- return array_filter(
- $storage->constants,
- function ($constant) {
- return $constant->type !== null;
- }
- );
- }
-
- /**
- * @param ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PRIVATE
- * $visibility
- */
- public function getClassConstantType(
- string $class_name,
- string $constant_name,
- int $visibility,
- ?StatementsAnalyzer $statements_analyzer = null,
- array $visited_constant_ids = []
- ): ?Union {
- $class_name = strtolower($class_name);
-
- if (!$this->classlike_storage_provider->has($class_name)) {
- return null;
- }
-
- $storage = $this->classlike_storage_provider->get($class_name);
-
- if (isset($storage->constants[$constant_name])) {
- $constant_storage = $storage->constants[$constant_name];
-
- if ($visibility === ReflectionProperty::IS_PUBLIC
- && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC
- ) {
- return null;
- }
-
- if ($visibility === ReflectionProperty::IS_PROTECTED
- && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC
- && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PROTECTED
- ) {
- return null;
- }
-
- if ($constant_storage->unresolved_node) {
- $constant_storage->type = new Union([ConstantTypeResolver::resolve(
- $this,
- $constant_storage->unresolved_node,
- $statements_analyzer,
- $visited_constant_ids
- )]);
- }
-
- return $constant_storage->type;
- } elseif (isset($storage->enum_cases[$constant_name])) {
- return new Union([new TEnumCase($storage->name, $constant_name)]);
- }
- return null;
- }
-
- private function checkMethodReferences(ClassLikeStorage $classlike_storage, Methods $methods): void
- {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- foreach ($classlike_storage->appearing_method_ids as $method_name => $appearing_method_id) {
- $appearing_fq_classlike_name = $appearing_method_id->fq_class_name;
-
- if ($appearing_fq_classlike_name !== $classlike_storage->name) {
- continue;
- }
-
- $method_id = $appearing_method_id;
-
- $declaring_classlike_storage = $classlike_storage;
-
- if (isset($classlike_storage->methods[$method_name])) {
- $method_storage = $classlike_storage->methods[$method_name];
- } else {
- $declaring_method_id = $classlike_storage->declaring_method_ids[$method_name];
-
- $declaring_fq_classlike_name = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
-
- try {
- $declaring_classlike_storage = $this->classlike_storage_provider->get($declaring_fq_classlike_name);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $method_storage = $declaring_classlike_storage->methods[$declaring_method_name];
- $method_id = $declaring_method_id;
- }
-
- if ($method_storage->location
- && !$project_analyzer->canReportIssues($method_storage->location->file_path)
- && !$codebase->analyzer->canReportIssues($method_storage->location->file_path)
- ) {
- continue;
- }
-
- $method_referenced = $this->file_reference_provider->isClassMethodReferenced(
- strtolower((string) $method_id)
- );
-
- if (!$method_referenced
- && $method_storage->location
- ) {
- if ($method_name !== '__destruct'
- && $method_name !== '__clone'
- && $method_name !== '__invoke'
- && $method_name !== '__unset'
- && $method_name !== '__isset'
- && $method_name !== '__sleep'
- && $method_name !== '__wakeup'
- && $method_name !== '__serialize'
- && $method_name !== '__unserialize'
- && $method_name !== '__set_state'
- && $method_name !== '__debuginfo'
- && $method_name !== '__tostring' // can be called in array_unique)
- ) {
- $method_location = $method_storage->location;
-
- $method_id = $classlike_storage->name . '::' . $method_storage->cased_name;
-
- if ($method_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PRIVATE) {
- $has_parent_references = false;
-
- if ($codebase->classImplements($classlike_storage->name, 'Serializable')
- && ($method_name === 'serialize' || $method_name === 'unserialize')
- ) {
- continue;
- }
-
- $has_variable_calls = $codebase->analyzer->hasMixedMemberName($method_name)
- || $codebase->analyzer->hasMixedMemberName(strtolower($classlike_storage->name . '::'));
-
- if (isset($classlike_storage->overridden_method_ids[$method_name])) {
- foreach ($classlike_storage->overridden_method_ids[$method_name] as $parent_method_id) {
- $parent_method_storage = $methods->getStorage($parent_method_id);
-
- if ($parent_method_storage->location
- && !$project_analyzer->canReportIssues($parent_method_storage->location->file_path)
- ) {
- // here we just don’t know
- $has_parent_references = true;
- break;
- }
-
- $parent_method_referenced = $this->file_reference_provider->isClassMethodReferenced(
- strtolower((string) $parent_method_id)
- );
-
- if (!$parent_method_storage->abstract || $parent_method_referenced) {
- $has_parent_references = true;
- break;
- }
- }
- }
-
- foreach ($classlike_storage->parent_classes as $parent_method_fqcln) {
- if ($codebase->analyzer->hasMixedMemberName(
- strtolower($parent_method_fqcln) . '::'
- )) {
- $has_variable_calls = true;
- break;
- }
- }
-
- foreach ($classlike_storage->class_implements as $fq_interface_name_lc => $_) {
- try {
- $interface_storage = $this->classlike_storage_provider->get($fq_interface_name_lc);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if ($codebase->analyzer->hasMixedMemberName(
- $fq_interface_name_lc . '::'
- )) {
- $has_variable_calls = true;
- }
-
- if (isset($interface_storage->methods[$method_name])) {
- $interface_method_referenced = $this->file_reference_provider->isClassMethodReferenced(
- $fq_interface_name_lc . '::' . $method_name
- );
-
- if ($interface_method_referenced) {
- $has_parent_references = true;
- }
- }
- }
-
- if (!$has_parent_references) {
- $issue = new PossiblyUnusedMethod(
- 'Cannot find ' . ($has_variable_calls ? 'explicit' : 'any')
- . ' calls to method ' . $method_id
- . ($has_variable_calls ? ' (but did find some potential callers)' : ''),
- $method_storage->location,
- $method_id
- );
-
- if ($codebase->alter_code) {
- if ($method_storage->stmt_location
- && !$declaring_classlike_storage->is_trait
- && isset($project_analyzer->getIssuesToFix()['PossiblyUnusedMethod'])
- && !$has_variable_calls
- && !IssueBuffer::isSuppressed($issue, $method_storage->suppressed_issues)
- ) {
- FileManipulationBuffer::addForCodeLocation(
- $method_storage->stmt_location,
- '',
- true
- );
- }
- } elseif (IssueBuffer::accepts(
- $issue,
- $method_storage->suppressed_issues,
- $method_storage->stmt_location
- && !$declaring_classlike_storage->is_trait
- && !$has_variable_calls
- )) {
- // fall through
- }
- }
- } elseif (!isset($classlike_storage->declaring_method_ids['__call'])) {
- $has_variable_calls = $codebase->analyzer->hasMixedMemberName(
- strtolower($classlike_storage->name . '::')
- ) || $codebase->analyzer->hasMixedMemberName($method_name);
-
- if ($method_name === '__construct') {
- $issue = new UnusedConstructor(
- 'Cannot find ' . ($has_variable_calls ? 'explicit' : 'any')
- . ' calls to private constructor ' . $method_id
- . ($has_variable_calls ? ' (but did find some potential callers)' : ''),
- $method_location,
- $method_id
- );
- } else {
- $issue = new UnusedMethod(
- 'Cannot find ' . ($has_variable_calls ? 'explicit' : 'any')
- . ' calls to private method ' . $method_id
- . ($has_variable_calls ? ' (but did find some potential callers)' : ''),
- $method_location,
- $method_id
- );
- }
-
- if ($codebase->alter_code) {
- if ($method_storage->stmt_location
- && !$declaring_classlike_storage->is_trait
- && isset($project_analyzer->getIssuesToFix()['UnusedMethod'])
- && !$has_variable_calls
- && !IssueBuffer::isSuppressed($issue, $method_storage->suppressed_issues)
- ) {
- FileManipulationBuffer::addForCodeLocation(
- $method_storage->stmt_location,
- '',
- true
- );
- }
- } elseif (IssueBuffer::accepts(
- $issue,
- $method_storage->suppressed_issues,
- $method_storage->stmt_location
- && !$declaring_classlike_storage->is_trait
- && !$has_variable_calls
- )) {
- // fall through
- }
- }
- }
- } else {
- if ($method_storage->return_type
- && $method_storage->return_type_location
- && !$method_storage->return_type->isVoid()
- && !$method_storage->return_type->isNever()
- && $method_id->method_name !== '__tostring'
- && ($method_storage->is_static || !$method_storage->probably_fluent)
- ) {
- $method_return_referenced = $this->file_reference_provider->isMethodReturnReferenced(
- strtolower((string) $method_id)
- );
-
- if (!$method_return_referenced) {
- if ($method_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE) {
- IssueBuffer::maybeAdd(
- new UnusedReturnValue(
- 'The return value for this private method is never used',
- $method_storage->return_type_location
- ),
- $method_storage->suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new PossiblyUnusedReturnValue(
- 'The return value for this method is never used',
- $method_storage->return_type_location
- ),
- $method_storage->suppressed_issues
- );
- }
- }
- }
-
- if ($method_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PRIVATE
- && !$classlike_storage->is_interface
- ) {
- foreach ($method_storage->params as $offset => $param_storage) {
- if (empty($classlike_storage->overridden_method_ids[$method_name])
- && $param_storage->location
- && !$param_storage->promoted_property
- && !$this->file_reference_provider->isMethodParamUsed(
- strtolower((string) $method_id),
- $offset
- )
- ) {
- if ($method_storage->final) {
- IssueBuffer::maybeAdd(
- new UnusedParam(
- 'Param #' . ($offset + 1) . ' is never referenced in this method',
- $param_storage->location
- ),
- $method_storage->suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new PossiblyUnusedParam(
- 'Param #' . ($offset + 1) . ' is never referenced in this method',
- $param_storage->location
- ),
- $method_storage->suppressed_issues
- );
- }
- }
- }
- }
- }
- }
- }
-
- private function findPossibleMethodParamTypes(ClassLikeStorage $classlike_storage): void
- {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- foreach ($classlike_storage->appearing_method_ids as $method_name => $appearing_method_id) {
- $appearing_fq_classlike_name = $appearing_method_id->fq_class_name;
-
- if ($appearing_fq_classlike_name !== $classlike_storage->name) {
- continue;
- }
-
- $method_id = $appearing_method_id;
-
- $declaring_classlike_storage = $classlike_storage;
-
- if (isset($classlike_storage->methods[$method_name])) {
- $method_storage = $classlike_storage->methods[$method_name];
- } else {
- $declaring_method_id = $classlike_storage->declaring_method_ids[$method_name];
-
- $declaring_fq_classlike_name = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
-
- try {
- $declaring_classlike_storage = $this->classlike_storage_provider->get($declaring_fq_classlike_name);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $method_storage = $declaring_classlike_storage->methods[$declaring_method_name];
- $method_id = $declaring_method_id;
- }
-
- if ($method_storage->location
- && !$project_analyzer->canReportIssues($method_storage->location->file_path)
- && !$codebase->analyzer->canReportIssues($method_storage->location->file_path)
- ) {
- continue;
- }
-
- if ($declaring_classlike_storage->is_trait) {
- continue;
- }
-
- $method_id_lc = strtolower((string) $method_id);
-
- if (isset($codebase->analyzer->possible_method_param_types[$method_id_lc])) {
- if ($method_storage->location) {
- $possible_param_types
- = $codebase->analyzer->possible_method_param_types[$method_id_lc];
-
- if ($possible_param_types) {
- foreach ($possible_param_types as $offset => $possible_type) {
- if (!isset($method_storage->params[$offset])) {
- continue;
- }
-
- $param_name = $method_storage->params[$offset]->name;
-
- if ($possible_type->hasMixed() || $possible_type->isNull()) {
- continue;
- }
-
- if ($method_storage->params[$offset]->default_type) {
- if ($method_storage->params[$offset]->default_type instanceof Union) {
- $default_type = clone $method_storage->params[$offset]->default_type;
- } else {
- $default_type_atomic = ConstantTypeResolver::resolve(
- $codebase->classlikes,
- $method_storage->params[$offset]->default_type,
- null
- );
-
- $default_type = new Union([$default_type_atomic]);
- }
-
- $possible_type = Type::combineUnionTypes(
- $possible_type,
- $default_type
- );
- }
-
- if ($codebase->alter_code
- && isset($project_analyzer->getIssuesToFix()['MissingParamType'])
- ) {
- $function_analyzer = $project_analyzer->getFunctionLikeAnalyzer(
- $method_id,
- $method_storage->location->file_path
- );
-
- $has_variable_calls = $codebase->analyzer->hasMixedMemberName(
- $method_name
- )
- || $codebase->analyzer->hasMixedMemberName(
- strtolower($classlike_storage->name . '::')
- );
-
- if ($has_variable_calls) {
- $possible_type->from_docblock = true;
- }
-
- if ($function_analyzer) {
- $function_analyzer->addOrUpdateParamType(
- $project_analyzer,
- $param_name,
- $possible_type,
- $possible_type->from_docblock
- && $project_analyzer->only_replace_php_types_with_non_docblock_types
- );
- }
- } else {
- IssueBuffer::addFixableIssue('MissingParamType');
- }
- }
- }
- }
- }
- }
- }
-
- private function checkPropertyReferences(ClassLikeStorage $classlike_storage): void
- {
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
-
- foreach ($classlike_storage->properties as $property_name => $property_storage) {
- $referenced_property_name = strtolower($classlike_storage->name) . '::$' . $property_name;
- $property_referenced = $this->file_reference_provider->isClassPropertyReferenced(
- $referenced_property_name
- );
-
- $property_constructor_referenced = false;
- if ($property_referenced && $property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE) {
- $all_method_references = $this->file_reference_provider->getAllMethodReferencesToClassProperties();
-
- if (isset($all_method_references[$referenced_property_name])
- && count($all_method_references[$referenced_property_name]) === 1) {
- $constructor_name = strtolower($classlike_storage->name) . '::__construct';
- $property_references = $all_method_references[$referenced_property_name];
-
- $property_constructor_referenced = isset($property_references[$constructor_name])
- && !$property_storage->is_static;
- }
- }
-
- if ((!$property_referenced || $property_constructor_referenced)
- && $property_storage->location
- ) {
- $property_id = $classlike_storage->name . '::$' . $property_name;
-
- if ($property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
- || $property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED
- ) {
- $has_parent_references = isset($classlike_storage->overridden_property_ids[$property_name]);
-
- $has_variable_calls = $codebase->analyzer->hasMixedMemberName('$' . $property_name)
- || $codebase->analyzer->hasMixedMemberName(strtolower($classlike_storage->name) . '::$');
-
- foreach ($classlike_storage->parent_classes as $parent_method_fqcln) {
- if ($codebase->analyzer->hasMixedMemberName(
- strtolower($parent_method_fqcln) . '::$'
- )) {
- $has_variable_calls = true;
- break;
- }
- }
-
- foreach ($classlike_storage->class_implements as $fq_interface_name) {
- if ($codebase->analyzer->hasMixedMemberName(
- strtolower($fq_interface_name) . '::$'
- )) {
- $has_variable_calls = true;
- break;
- }
- }
-
- if (!$has_parent_references
- && ($property_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
- || !isset($classlike_storage->declaring_method_ids['__get']))
- ) {
- $issue = new PossiblyUnusedProperty(
- 'Cannot find ' . ($has_variable_calls ? 'explicit' : 'any')
- . ' references to property ' . $property_id
- . ($has_variable_calls ? ' (but did find some potential references)' : ''),
- $property_storage->location
- );
-
- if ($codebase->alter_code) {
- if ($property_storage->stmt_location
- && isset($project_analyzer->getIssuesToFix()['PossiblyUnusedProperty'])
- && !$has_variable_calls
- && !IssueBuffer::isSuppressed($issue, $classlike_storage->suppressed_issues)
- ) {
- FileManipulationBuffer::addForCodeLocation(
- $property_storage->stmt_location,
- '',
- true
- );
- }
- } elseif (IssueBuffer::accepts(
- $issue,
- $classlike_storage->suppressed_issues + $property_storage->suppressed_issues
- )) {
- // fall through
- }
- }
- } elseif (!isset($classlike_storage->declaring_method_ids['__get'])) {
- $has_variable_calls = $codebase->analyzer->hasMixedMemberName('$' . $property_name);
-
- $issue = new UnusedProperty(
- 'Cannot find ' . ($has_variable_calls ? 'explicit' : 'any')
- . ' references to private property ' . $property_id
- . ($has_variable_calls ? ' (but did find some potential references)' : ''),
- $property_storage->location
- );
-
- if ($codebase->alter_code) {
- if (!$property_constructor_referenced
- && $property_storage->stmt_location
- && isset($project_analyzer->getIssuesToFix()['UnusedProperty'])
- && !$has_variable_calls
- && !IssueBuffer::isSuppressed($issue, $classlike_storage->suppressed_issues)
- ) {
- FileManipulationBuffer::addForCodeLocation(
- $property_storage->stmt_location,
- '',
- true
- );
- }
- } elseif (IssueBuffer::accepts(
- $issue,
- $classlike_storage->suppressed_issues + $property_storage->suppressed_issues
- )) {
- // fall through
- }
- }
- }
- }
- }
-
- /**
- * @param lowercase-string $fq_classlike_name_lc
- */
- public function registerMissingClassLike(string $fq_classlike_name_lc): void
- {
- $this->existing_classlikes_lc[$fq_classlike_name_lc] = false;
- }
-
- /**
- * @param lowercase-string $fq_classlike_name_lc
- */
- public function isMissingClassLike(string $fq_classlike_name_lc): bool
- {
- return isset($this->existing_classlikes_lc[$fq_classlike_name_lc])
- && $this->existing_classlikes_lc[$fq_classlike_name_lc] === false;
- }
-
- /**
- * @param lowercase-string $fq_classlike_name_lc
- */
- public function doesClassLikeExist(string $fq_classlike_name_lc): bool
- {
- return isset($this->existing_classlikes_lc[$fq_classlike_name_lc])
- && $this->existing_classlikes_lc[$fq_classlike_name_lc];
- }
-
- public function forgetMissingClassLikes(): void
- {
- $this->existing_classlikes_lc = array_filter($this->existing_classlikes_lc);
- }
-
- public function removeClassLike(string $fq_class_name): void
- {
- $fq_class_name_lc = strtolower($fq_class_name);
-
- unset(
- $this->existing_classlikes_lc[$fq_class_name_lc],
- $this->existing_traits_lc[$fq_class_name_lc],
- $this->existing_traits[$fq_class_name],
- $this->existing_enums_lc[$fq_class_name_lc],
- $this->existing_enums[$fq_class_name],
- $this->existing_interfaces_lc[$fq_class_name_lc],
- $this->existing_interfaces[$fq_class_name],
- $this->existing_classes_lc[$fq_class_name_lc],
- $this->existing_classes[$fq_class_name],
- $this->trait_nodes[$fq_class_name_lc]
- );
-
- $this->scanner->removeClassLike($fq_class_name_lc);
- }
-
- /**
- * @return array{
- * array<lowercase-string, bool>,
- * array<lowercase-string, bool>,
- * array<lowercase-string, bool>,
- * array<string, bool>,
- * array<lowercase-string, bool>,
- * array<string, bool>,
- * array<lowercase-string, bool>,
- * array<string, bool>,
- * array<string, bool>,
- * }
- */
- public function getThreadData(): array
- {
- return [
- $this->existing_classlikes_lc,
- $this->existing_classes_lc,
- $this->existing_traits_lc,
- $this->existing_traits,
- $this->existing_enums_lc,
- $this->existing_enums,
- $this->existing_interfaces_lc,
- $this->existing_interfaces,
- $this->existing_classes,
- ];
- }
-
- /**
- * @param array{
- * 0: array<lowercase-string, bool>,
- * 1: array<lowercase-string, bool>,
- * 2: array<lowercase-string, bool>,
- * 3: array<string, bool>,
- * 4: array<lowercase-string, bool>,
- * 5: array<string, bool>,
- * 6: array<lowercase-string, bool>,
- * 7: array<string, bool>,
- * 8: array<string, bool>,
- * } $thread_data
- *
- */
- public function addThreadData(array $thread_data): void
- {
- [
- $existing_classlikes_lc,
- $existing_classes_lc,
- $existing_traits_lc,
- $existing_traits,
- $existing_enums_lc,
- $existing_enums,
- $existing_interfaces_lc,
- $existing_interfaces,
- $existing_classes
- ] = $thread_data;
-
- $this->existing_classlikes_lc = array_merge($existing_classlikes_lc, $this->existing_classlikes_lc);
- $this->existing_classes_lc = array_merge($existing_classes_lc, $this->existing_classes_lc);
- $this->existing_traits_lc = array_merge($existing_traits_lc, $this->existing_traits_lc);
- $this->existing_traits = array_merge($existing_traits, $this->existing_traits);
- $this->existing_enums_lc = array_merge($existing_enums_lc, $this->existing_enums_lc);
- $this->existing_enums = array_merge($existing_enums, $this->existing_enums);
- $this->existing_interfaces_lc = array_merge($existing_interfaces_lc, $this->existing_interfaces_lc);
- $this->existing_interfaces = array_merge($existing_interfaces, $this->existing_interfaces);
- $this->existing_classes = array_merge($existing_classes, $this->existing_classes);
- }
-
- public function getStorageFor(string $fq_class_name): ?ClassLikeStorage
- {
- $fq_class_name = $this->getUnAliasedName($fq_class_name);
-
- try {
- return $this->classlike_storage_provider->get($fq_class_name);
- } catch (InvalidArgumentException $e) {
- return null;
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php
deleted file mode 100644
index c73e67a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php
+++ /dev/null
@@ -1,363 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\Exception\CircularReferenceException;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Scanner\UnresolvedConstant\ArrayOffsetFetch;
-use Psalm\Internal\Scanner\UnresolvedConstant\ArraySpread;
-use Psalm\Internal\Scanner\UnresolvedConstant\ArrayValue;
-use Psalm\Internal\Scanner\UnresolvedConstant\ClassConstant;
-use Psalm\Internal\Scanner\UnresolvedConstant\Constant;
-use Psalm\Internal\Scanner\UnresolvedConstant\ScalarValue;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedAdditionOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBinaryOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseAnd;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseOr;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseXor;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedConcatOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedDivisionOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedMultiplicationOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedSubtractionOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedTernary;
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Union;
-use ReflectionProperty;
-
-use function ctype_digit;
-use function is_float;
-use function is_int;
-use function is_string;
-use function spl_object_id;
-
-/**
- * @internal
- */
-class ConstantTypeResolver
-{
- public static function resolve(
- ClassLikes $classlikes,
- UnresolvedConstantComponent $c,
- StatementsAnalyzer $statements_analyzer = null,
- array $visited_constant_ids = []
- ): Atomic {
- $c_id = spl_object_id($c);
-
- if (isset($visited_constant_ids[$c_id])) {
- throw new CircularReferenceException('Found a circular reference');
- }
-
- if ($c instanceof ScalarValue) {
- return self::getLiteralTypeFromScalarValue($c->value);
- }
-
- if ($c instanceof UnresolvedBinaryOp) {
- $left = self::resolve(
- $classlikes,
- $c->left,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
- $right = self::resolve(
- $classlikes,
- $c->right,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- if ($left instanceof TMixed || $right instanceof TMixed) {
- return new TMixed;
- }
-
- if ($c instanceof UnresolvedConcatOp) {
- if (($left instanceof TLiteralString
- || $left instanceof TLiteralFloat
- || $left instanceof TLiteralInt)
- && ($right instanceof TLiteralString
- || $right instanceof TLiteralFloat
- || $right instanceof TLiteralInt)
- ) {
- return new TLiteralString($left->value . $right->value);
- }
-
- return new TString();
- }
-
- if ($c instanceof UnresolvedAdditionOp
- || $c instanceof UnresolvedSubtractionOp
- || $c instanceof UnresolvedDivisionOp
- || $c instanceof UnresolvedMultiplicationOp
- || $c instanceof UnresolvedBitwiseOr
- || $c instanceof UnresolvedBitwiseXor
- || $c instanceof UnresolvedBitwiseAnd
- ) {
- if (($left instanceof TLiteralFloat || $left instanceof TLiteralInt)
- && ($right instanceof TLiteralFloat || $right instanceof TLiteralInt)
- ) {
- if ($c instanceof UnresolvedAdditionOp) {
- return self::getLiteralTypeFromScalarValue($left->value + $right->value);
- }
-
- if ($c instanceof UnresolvedSubtractionOp) {
- return self::getLiteralTypeFromScalarValue($left->value - $right->value);
- }
-
- if ($c instanceof UnresolvedDivisionOp) {
- return self::getLiteralTypeFromScalarValue($left->value / $right->value);
- }
-
- if ($c instanceof UnresolvedBitwiseOr) {
- return self::getLiteralTypeFromScalarValue($left->value | $right->value);
- }
-
- if ($c instanceof UnresolvedBitwiseXor) {
- return self::getLiteralTypeFromScalarValue($left->value ^ $right->value);
- }
-
- if ($c instanceof UnresolvedBitwiseAnd) {
- return self::getLiteralTypeFromScalarValue($left->value & $right->value);
- }
-
- return self::getLiteralTypeFromScalarValue($left->value * $right->value);
- }
-
- if ($left instanceof TKeyedArray && $right instanceof TKeyedArray) {
- $keyed_array = new TKeyedArray($left->properties + $right->properties);
- $keyed_array->sealed = true;
- return $keyed_array;
- }
-
- return new TMixed;
- }
-
- return new TMixed;
- }
-
- if ($c instanceof UnresolvedTernary) {
- $cond = self::resolve(
- $classlikes,
- $c->cond,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
- $if = $c->if ? self::resolve(
- $classlikes,
- $c->if,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- ) : null;
- $else = self::resolve(
- $classlikes,
- $c->else,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- if ($cond instanceof TLiteralFloat
- || $cond instanceof TLiteralInt
- || $cond instanceof TLiteralString
- ) {
- if ($cond->value) {
- return $if ?? $cond;
- }
- } elseif ($cond instanceof TFalse || $cond instanceof TNull) {
- return $else;
- } elseif ($cond instanceof TTrue) {
- return $if ?? $cond;
- }
- }
-
- if ($c instanceof ArrayValue) {
- $properties = [];
- $auto_key = 0;
-
- if (!$c->entries) {
- return new TArray([Type::getEmpty(), Type::getEmpty()]);
- }
-
- $is_list = true;
-
- foreach ($c->entries as $entry) {
- if ($entry instanceof ArraySpread) {
- $spread_array = self::resolve(
- $classlikes,
- $entry->array,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- if ($spread_array instanceof TArray && $spread_array->type_params[1]->isEmpty()) {
- continue;
- }
-
- if (!$spread_array instanceof TKeyedArray) {
- return new TArray([Type::getArrayKey(), Type::getMixed()]);
- }
-
- foreach ($spread_array->properties as $spread_array_type) {
- $properties[$auto_key++] = $spread_array_type;
- }
- continue;
- }
-
- if ($entry->key) {
- $key_type = self::resolve(
- $classlikes,
- $entry->key,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- if (!$key_type instanceof TLiteralInt
- || $key_type->value !== $auto_key
- ) {
- $is_list = false;
- }
- } else {
- $key_type = new TLiteralInt($auto_key);
- }
-
- if ($key_type instanceof TLiteralInt
- || $key_type instanceof TLiteralString
- ) {
- $key_value = $key_type->value;
- if ($key_type instanceof TLiteralInt) {
- $auto_key = $key_type->value + 1;
- } elseif (ctype_digit($key_type->value)) {
- $auto_key = ((int) $key_type->value) + 1;
- }
- } else {
- return new TArray([Type::getArrayKey(), Type::getMixed()]);
- }
-
- $value_type = new Union([self::resolve(
- $classlikes,
- $entry->value,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- )]);
-
- $properties[$key_value] = $value_type;
- }
-
- if (empty($properties)) {
- $resolved_type = new TArray([
- new Union([new TEmpty()]),
- new Union([new TEmpty()]),
- ]);
- } else {
- $resolved_type = new TKeyedArray($properties);
-
- $resolved_type->is_list = $is_list;
- $resolved_type->sealed = true;
- }
-
- return $resolved_type;
- }
-
- if ($c instanceof ClassConstant) {
- if ($c->name === 'class') {
- return new TLiteralClassString($c->fqcln);
- }
-
- $found_type = $classlikes->getClassConstantType(
- $c->fqcln,
- $c->name,
- ReflectionProperty::IS_PRIVATE,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- if ($found_type) {
- return $found_type->getSingleAtomic();
- }
- }
-
- if ($c instanceof ArrayOffsetFetch) {
- $var_type = self::resolve(
- $classlikes,
- $c->array,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- $offset_type = self::resolve(
- $classlikes,
- $c->offset,
- $statements_analyzer,
- $visited_constant_ids + [$c_id => true]
- );
-
- if ($var_type instanceof TKeyedArray
- && ($offset_type instanceof TLiteralInt
- || $offset_type instanceof TLiteralString)
- ) {
- $union = $var_type->properties[$offset_type->value] ?? null;
-
- if ($union && $union->isSingle()) {
- return $union->getSingleAtomic();
- }
- }
- }
-
- if ($c instanceof Constant) {
- if ($statements_analyzer) {
- $found_type = ConstFetchAnalyzer::getConstType(
- $statements_analyzer,
- $c->name,
- $c->is_fully_qualified,
- null
- );
-
- if ($found_type) {
- return $found_type->getSingleAtomic();
- }
- }
- }
-
- return new TMixed;
- }
-
- /**
- * @param string|int|float|bool|null $value
- */
- private static function getLiteralTypeFromScalarValue($value): Atomic
- {
- if (is_string($value)) {
- return new TLiteralString($value);
- }
-
- if (is_int($value)) {
- return new TLiteralInt($value);
- }
-
- if (is_float($value)) {
- return new TLiteralFloat($value);
- }
-
- if ($value === false) {
- return new TFalse;
- }
-
- if ($value === true) {
- return new TTrue;
- }
-
- return new TNull;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/DataFlowGraph.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/DataFlowGraph.php
deleted file mode 100644
index 988a814..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/DataFlowGraph.php
+++ /dev/null
@@ -1,159 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\Path;
-
-use function abs;
-use function array_keys;
-use function array_merge;
-use function array_reverse;
-use function array_sum;
-use function count;
-use function strlen;
-use function strpos;
-use function substr;
-
-abstract class DataFlowGraph
-{
- /** @var array<string, array<string, Path>> */
- protected $forward_edges = [];
-
- abstract public function addNode(DataFlowNode $node): void;
-
- /**
- * @param array<string> $added_taints
- * @param array<string> $removed_taints
- */
- public function addPath(
- DataFlowNode $from,
- DataFlowNode $to,
- string $path_type,
- ?array $added_taints = null,
- ?array $removed_taints = null
- ): void {
- $from_id = $from->id;
- $to_id = $to->id;
-
- if ($from_id === $to_id) {
- return;
- }
-
- $length = 0;
-
- if ($from->code_location
- && $to->code_location
- && $from->code_location->file_path === $to->code_location->file_path
- ) {
- $to_line = $to->code_location->raw_line_number;
- $from_line = $from->code_location->raw_line_number;
- $length = abs($to_line - $from_line);
- }
-
- $this->forward_edges[$from_id][$to_id] = new Path($path_type, $length, $added_taints, $removed_taints);
- }
-
- /**
- * @param array<string> $previous_path_types
- *
- * @psalm-pure
- */
- protected static function shouldIgnoreFetch(
- string $path_type,
- string $expression_type,
- array $previous_path_types
- ): bool {
- $el = strlen($expression_type);
-
- // arraykey-fetch requires a matching arraykey-assignment at the same level
- // otherwise the tainting is not valid
- if (strpos($path_type, $expression_type . '-fetch-') === 0 || $path_type === 'arraykey-fetch') {
- $fetch_nesting = 0;
-
- $previous_path_types = array_reverse($previous_path_types);
-
- foreach ($previous_path_types as $previous_path_type) {
- if ($previous_path_type === $expression_type . '-assignment') {
- if ($fetch_nesting === 0) {
- return false;
- }
-
- $fetch_nesting--;
- }
-
- if (strpos($previous_path_type, $expression_type . '-fetch') === 0) {
- $fetch_nesting++;
- }
-
- if (strpos($previous_path_type, $expression_type . '-assignment-') === 0) {
- if ($fetch_nesting > 0) {
- $fetch_nesting--;
- continue;
- }
-
- if (substr($previous_path_type, $el + 12) === substr($path_type, $el + 7)) {
- return false;
- }
-
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * @return array{int, int, int, float}
- */
- public function getEdgeStats(): array
- {
- $lengths = 0;
-
- $destination_counts = [];
- $origin_counts = [];
-
- foreach ($this->forward_edges as $from_id => $destinations) {
- foreach ($destinations as $to_id => $path) {
- if ($path->length === 0) {
- continue;
- }
-
- $lengths += $path->length;
-
- if (!isset($destination_counts[$to_id])) {
- $destination_counts[$to_id] = 0;
- }
-
- $destination_counts[$to_id]++;
-
- $origin_counts[$from_id] = true;
- }
- }
-
- $count = array_sum($destination_counts);
-
- if (!$count) {
- return [0, 0, 0, 0.0];
- }
-
- $mean = $lengths / $count;
-
- return [$count, count($origin_counts), count($destination_counts), $mean];
- }
-
- /**
- * @psalm-return list<list<string>>
- */
- public function summarizeEdges(): array
- {
- $edges = [];
-
- foreach ($this->forward_edges as $source => $destinations) {
- $edges[] = array_merge([$source], array_keys($destinations));
- }
-
- return $edges;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Functions.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Functions.php
deleted file mode 100644
index ca882a0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Functions.php
+++ /dev/null
@@ -1,603 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Exception;
-use PhpParser\Node\Arg;
-use PhpParser\Node\Expr\Closure as ClosureNode;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\Internal\Provider\FunctionExistenceProvider;
-use Psalm\Internal\Provider\FunctionParamsProvider;
-use Psalm\Internal\Provider\FunctionReturnTypeProvider;
-use Psalm\Internal\Type\Comparator\CallableTypeComparator;
-use Psalm\NodeTypeProvider;
-use Psalm\StatementsSource;
-use Psalm\Storage\FunctionStorage;
-use Psalm\Type\Atomic\TNamedObject;
-use UnexpectedValueException;
-
-use function array_shift;
-use function count;
-use function end;
-use function explode;
-use function implode;
-use function in_array;
-use function is_bool;
-use function rtrim;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class Functions
-{
- /**
- * @var FileStorageProvider
- */
- private $file_storage_provider;
-
- /**
- * @var array<lowercase-string, FunctionStorage>
- */
- private static $stubbed_functions;
-
- /** @var FunctionReturnTypeProvider */
- public $return_type_provider;
-
- /** @var FunctionExistenceProvider */
- public $existence_provider;
-
- /** @var FunctionParamsProvider */
- public $params_provider;
-
- /**
- * @var Reflection
- */
- private $reflection;
-
- public function __construct(FileStorageProvider $storage_provider, Reflection $reflection)
- {
- $this->file_storage_provider = $storage_provider;
- $this->reflection = $reflection;
- $this->return_type_provider = new FunctionReturnTypeProvider();
- $this->existence_provider = new FunctionExistenceProvider();
- $this->params_provider = new FunctionParamsProvider();
-
- self::$stubbed_functions = [];
- }
-
- /**
- * @param non-empty-lowercase-string $function_id
- */
- public function getStorage(
- ?StatementsAnalyzer $statements_analyzer,
- string $function_id,
- ?string $root_file_path = null,
- ?string $checked_file_path = null
- ): FunctionStorage {
- if ($function_id[0] === '\\') {
- $function_id = substr($function_id, 1);
- }
-
- if (isset(self::$stubbed_functions[$function_id])) {
- return self::$stubbed_functions[$function_id];
- }
-
- $file_storage = null;
-
- if ($statements_analyzer) {
- $root_file_path = $statements_analyzer->getRootFilePath();
- $checked_file_path = $statements_analyzer->getFilePath();
-
- $file_storage = $this->file_storage_provider->get($root_file_path);
-
- $function_analyzers = $statements_analyzer->getFunctionAnalyzers();
-
- if (isset($function_analyzers[$function_id])) {
- $function_id = $function_analyzers[$function_id]->getFunctionId();
-
- if (isset($file_storage->functions[$function_id])) {
- return $file_storage->functions[$function_id];
- }
- }
-
- // closures can be returned here
- if (isset($file_storage->functions[$function_id])) {
- return $file_storage->functions[$function_id];
- }
- }
-
- if (!$root_file_path || !$checked_file_path) {
- if ($this->reflection->hasFunction($function_id)) {
- return $this->reflection->getFunctionStorage($function_id);
- }
-
- throw new UnexpectedValueException(
- 'Expecting non-empty $root_file_path and $checked_file_path'
- );
- }
-
- if ($this->reflection->hasFunction($function_id)) {
- return $this->reflection->getFunctionStorage($function_id);
- }
-
- if (!isset($file_storage->declaring_function_ids[$function_id])) {
- if ($checked_file_path !== $root_file_path) {
- $file_storage = $this->file_storage_provider->get($checked_file_path);
-
- if (isset($file_storage->functions[$function_id])) {
- return $file_storage->functions[$function_id];
- }
- }
-
- throw new UnexpectedValueException(
- 'Expecting ' . $function_id . ' to have storage in ' . $checked_file_path
- );
- }
-
- $declaring_file_path = $file_storage->declaring_function_ids[$function_id];
-
- $declaring_file_storage = $this->file_storage_provider->get($declaring_file_path);
-
- if (!isset($declaring_file_storage->functions[$function_id])) {
- throw new UnexpectedValueException(
- 'Not expecting ' . $function_id . ' to not have storage in ' . $declaring_file_path
- );
- }
-
- return $declaring_file_storage->functions[$function_id];
- }
-
- public function addGlobalFunction(string $function_id, FunctionStorage $storage): void
- {
- self::$stubbed_functions[strtolower($function_id)] = $storage;
- }
-
- public function hasStubbedFunction(string $function_id): bool
- {
- return isset(self::$stubbed_functions[strtolower($function_id)]);
- }
-
- /**
- * @return array<string, FunctionStorage>
- */
- public function getAllStubbedFunctions(): array
- {
- return self::$stubbed_functions;
- }
-
- /**
- * @param lowercase-string $function_id
- */
- public function functionExists(
- StatementsAnalyzer $statements_analyzer,
- string $function_id
- ): bool {
- if ($this->existence_provider->has($function_id)) {
- $function_exists = $this->existence_provider->doesFunctionExist($statements_analyzer, $function_id);
-
- if ($function_exists !== null) {
- return $function_exists;
- }
- }
-
- $file_storage = $this->file_storage_provider->get($statements_analyzer->getRootFilePath());
-
- if (isset($file_storage->declaring_function_ids[$function_id])) {
- return true;
- }
-
- if ($this->reflection->hasFunction($function_id)) {
- return true;
- }
-
- if (isset(self::$stubbed_functions[$function_id])) {
- return true;
- }
-
- if (isset($statements_analyzer->getFunctionAnalyzers()[$function_id])) {
- return true;
- }
-
- $predefined_functions = $statements_analyzer->getCodebase()->config->getPredefinedFunctions();
-
- if (isset($predefined_functions[$function_id])) {
- /** @psalm-suppress ArgumentTypeCoercion */
- if ($this->reflection->registerFunction($function_id) === false) {
- return false;
- }
-
- return true;
- }
-
- return false;
- }
-
- /**
- * @param non-empty-string $function_name
- *
- * @return non-empty-string
- */
- public function getFullyQualifiedFunctionNameFromString(string $function_name, StatementsSource $source): string
- {
- if ($function_name[0] === '\\') {
- $function_name = substr($function_name, 1);
-
- if ($function_name === '') {
- throw new UnexpectedValueException('Malformed function name');
- }
-
- return $function_name;
- }
-
- $function_name_lcase = strtolower($function_name);
-
- $aliases = $source->getAliases();
-
- $imported_function_namespaces = $aliases->functions;
- $imported_namespaces = $aliases->uses;
-
- if (strpos($function_name, '\\') !== false) {
- $function_name_parts = explode('\\', $function_name);
- $first_namespace = array_shift($function_name_parts);
- $first_namespace_lcase = strtolower($first_namespace);
-
- if (isset($imported_namespaces[$first_namespace_lcase])) {
- return $imported_namespaces[$first_namespace_lcase] . '\\' . implode('\\', $function_name_parts);
- }
-
- if (isset($imported_function_namespaces[$first_namespace_lcase])) {
- return $imported_function_namespaces[$first_namespace_lcase] . '\\' .
- implode('\\', $function_name_parts);
- }
- } elseif (isset($imported_function_namespaces[$function_name_lcase])) {
- return $imported_function_namespaces[$function_name_lcase];
- }
-
- $namespace = $source->getNamespace();
-
- return ($namespace ? $namespace . '\\' : '') . $function_name;
- }
-
- /**
- * @return array<lowercase-string,FunctionStorage>
- */
- public function getMatchingFunctionNames(
- string $stub,
- int $offset,
- string $file_path,
- Codebase $codebase
- ): array {
- if ($stub[0] === '*') {
- $stub = substr($stub, 1);
- }
-
- $fully_qualified = false;
-
- if ($stub[0] === '\\') {
- $fully_qualified = true;
- $stub = substr($stub, 1);
- $stub_namespace = '';
- } else {
- // functions can reference either the current namespace or root-namespaced
- // equivalents. We therefore want to make both candidates.
- [$stub_namespace, $stub] = explode('-', $stub);
- }
-
- /** @var array<lowercase-string, FunctionStorage> */
- $matching_functions = [];
-
- $file_storage = $this->file_storage_provider->get($file_path);
-
- $current_namespace_aliases = null;
- foreach ($file_storage->namespace_aliases as $namespace_start => $namespace_aliases) {
- if ($namespace_start < $offset) {
- $current_namespace_aliases = $namespace_aliases;
- break;
- }
- }
-
- // We will search all functions for several patterns. This will
- // be for all used namespaces, the global namespace and matched
- // used functions.
- $match_function_patterns = [
- $stub . '*',
- ];
-
- if ($stub_namespace) {
- $match_function_patterns[] = $stub_namespace . '\\' . $stub . '*';
- }
-
- if ($current_namespace_aliases) {
- foreach ($current_namespace_aliases->functions as $alias_name => $function_name) {
- if (strpos($alias_name, $stub) === 0) {
- try {
- $match_function_patterns[] = $function_name;
- } catch (Exception $e) {
- }
- }
- }
-
- if (!$fully_qualified) {
- foreach ($current_namespace_aliases->uses as $namespace_name) {
- $match_function_patterns[] = $namespace_name . '\\' . $stub . '*';
- }
- }
- }
-
- $function_map = $file_storage->functions
- + $this->getAllStubbedFunctions()
- + $this->reflection->getFunctions()
- + $codebase->config->getPredefinedFunctions();
-
- foreach ($function_map as $function_name => $function) {
- foreach ($match_function_patterns as $pattern) {
- $pattern_lc = strtolower($pattern);
-
- if (substr($pattern, -1, 1) === '*') {
- if (strpos($function_name, rtrim($pattern_lc, '*')) !== 0) {
- continue;
- }
- } elseif ($function_name !== $pattern) {
- continue;
- }
- if (is_bool($function)) {
- /** @var callable-string $function_name */
- if ($this->reflection->registerFunction($function_name) === false) {
- continue;
- }
- $function = $this->reflection->getFunctionStorage($function_name);
- }
-
- if ($function->cased_name) {
- $cased_name_parts = explode('\\', $function->cased_name);
- $pattern_parts = explode('\\', $pattern);
-
- if (end($cased_name_parts)[0] !== end($pattern_parts)[0]) {
- continue;
- }
- }
-
- /** @var lowercase-string $function_name */
- $matching_functions[$function_name] = $function;
- }
- }
-
- return $matching_functions;
- }
-
- public static function isVariadic(Codebase $codebase, string $function_id, string $file_path): bool
- {
- $file_storage = $codebase->file_storage_provider->get($file_path);
-
- if (!isset($file_storage->declaring_function_ids[$function_id])) {
- return false;
- }
-
- $declaring_file_path = $file_storage->declaring_function_ids[$function_id];
-
- $file_storage = $declaring_file_path === $file_path
- ? $file_storage
- : $codebase->file_storage_provider->get($declaring_file_path);
-
- return isset($file_storage->functions[$function_id]) && $file_storage->functions[$function_id]->variadic;
- }
-
- /**
- * @param ?list<Arg> $args
- */
- public function isCallMapFunctionPure(
- Codebase $codebase,
- ?NodeTypeProvider $type_provider,
- string $function_id,
- ?array $args,
- bool &$must_use = true
- ): bool {
- $impure_functions = [
- // file io
- 'chdir', 'chgrp', 'chmod', 'chown', 'chroot', 'copy', 'file_get_contents', 'file_put_contents',
- 'opendir', 'readdir', 'closedir', 'rewinddir', 'scandir',
- 'fopen', 'fread', 'fwrite', 'fclose', 'touch', 'fpassthru', 'fputs', 'fscanf', 'fseek', 'flock',
- 'ftruncate', 'fprintf', 'symlink', 'mkdir', 'unlink', 'rename', 'rmdir', 'popen', 'pclose',
- 'fgetcsv', 'fputcsv', 'umask', 'finfo_open', 'finfo_close', 'finfo_file',
- 'stream_set_timeout', 'fgets', 'fflush', 'move_uploaded_file', 'file_exists', 'realpath', 'glob',
- 'is_readable', 'is_dir', 'is_file',
-
- // stream/socket io
- 'stream_context_set_option', 'socket_write', 'stream_set_blocking', 'socket_close',
- 'socket_set_option', 'stream_set_write_buffer', 'stream_socket_enable_crypto', 'stream_copy_to_stream',
- 'stream_wrapper_register',
-
- // meta calls
- 'call_user_func', 'call_user_func_array', 'define', 'create_function',
-
- // http
- 'header', 'header_remove', 'http_response_code', 'setcookie',
-
- // output buffer
- 'ob_start', 'ob_end_clean', 'ob_get_clean', 'readfile', 'printf', 'var_dump', 'phpinfo',
- 'ob_implicit_flush', 'vprintf',
-
- // mcrypt
- 'mcrypt_generic_init', 'mcrypt_generic_deinit', 'mcrypt_module_close',
-
- // internal optimisation
- 'opcache_compile_file', 'clearstatcache',
-
- // process-related
- 'pcntl_signal', 'pcntl_alarm', 'posix_kill', 'cli_set_process_title', 'pcntl_async_signals', 'proc_close',
- 'proc_nice', 'proc_open', 'proc_terminate',
-
- // curl
- 'curl_setopt', 'curl_close', 'curl_multi_add_handle', 'curl_multi_remove_handle',
- 'curl_multi_select', 'curl_multi_close', 'curl_setopt_array',
-
- // apc, apcu
- 'apc_store', 'apc_delete', 'apc_clear_cache', 'apc_add', 'apc_inc', 'apc_dec', 'apc_cas',
- 'apcu_store', 'apcu_delete', 'apcu_clear_cache', 'apcu_add', 'apcu_inc', 'apcu_dec', 'apcu_cas',
-
- // gz
- 'gzwrite', 'gzrewind', 'gzseek', 'gzclose',
-
- // newrelic
- 'newrelic_start_transaction', 'newrelic_name_transaction', 'newrelic_add_custom_parameter',
- 'newrelic_add_custom_tracer', 'newrelic_background_job', 'newrelic_end_transaction',
- 'newrelic_set_appname',
-
- // execution
- 'shell_exec', 'exec', 'system', 'passthru', 'pcntl_exec',
-
- // well-known functions
- 'libxml_use_internal_errors', 'libxml_disable_entity_loader', 'curl_exec',
- 'mt_srand', 'openssl_pkcs7_sign', 'openssl_sign',
- 'mt_rand', 'rand', 'random_int', 'random_bytes',
- 'wincache_ucache_delete', 'wincache_ucache_set', 'wincache_ucache_inc',
- 'class_alias',
- 'class_exists', // impure by virtue of triggering autoloader
-
- // php environment
- 'ini_set', 'sleep', 'usleep', 'register_shutdown_function',
- 'error_reporting', 'register_tick_function', 'unregister_tick_function',
- 'set_error_handler', 'user_error', 'trigger_error', 'restore_error_handler',
- 'date_default_timezone_set', 'assert_options', 'setlocale',
- 'set_exception_handler', 'set_time_limit', 'putenv', 'spl_autoload_register',
- 'spl_autoload_unregister', 'microtime', 'array_rand', 'set_include_path',
-
- // logging
- 'openlog', 'syslog', 'error_log', 'define_syslog_variables',
-
- // session
- 'session_id', 'session_decode', 'session_name', 'session_set_cookie_params',
- 'session_set_save_handler', 'session_regenerate_id', 'mb_internal_encoding',
- 'session_start', 'session_cache_limiter',
-
- // ldap
- 'ldap_set_option',
-
- // iterators
- 'rewind', 'iterator_apply', 'iterator_to_array',
-
- // mysqli
- 'mysqli_select_db', 'mysqli_dump_debug_info', 'mysqli_kill', 'mysqli_multi_query',
- 'mysqli_next_result', 'mysqli_options', 'mysqli_ping', 'mysqli_query', 'mysqli_report',
- 'mysqli_rollback', 'mysqli_savepoint', 'mysqli_set_charset', 'mysqli_ssl_set', 'mysqli_close',
-
- // script execution
- 'ignore_user_abort',
-
- // ftp
- 'ftp_close', 'ftp_pasv',
-
- // bcmath
- 'bcscale',
-
- // json
- 'json_last_error',
-
- // opcache
- 'opcache_compile_file', 'opcache_get_configuration', 'opcache_get_status',
- 'opcache_invalidate', 'opcache_is_script_cached', 'opcache_reset',
-
- //gettext
- 'bindtextdomain',
- ];
-
- if (in_array(strtolower($function_id), $impure_functions, true)) {
- return false;
- }
-
- if (strpos($function_id, 'image') === 0) {
- return false;
- }
-
- if (strpos($function_id, 'readline') === 0) {
- return false;
- }
-
- if (($function_id === 'var_export' || $function_id === 'print_r') && !isset($args[1])) {
- return false;
- }
-
- if ($function_id === 'assert') {
- $must_use = false;
- return true;
- }
-
- if ($function_id === 'func_num_args' || $function_id === 'func_get_args') {
- return true;
- }
-
- if ($function_id === 'count' && isset($args[0]) && $type_provider) {
- $count_type = $type_provider->getType($args[0]->value);
-
- if ($count_type) {
- foreach ($count_type->getAtomicTypes() as $atomic_count_type) {
- if ($atomic_count_type instanceof TNamedObject) {
- $count_method_id = new MethodIdentifier(
- $atomic_count_type->value,
- 'count'
- );
-
- try {
- return $codebase->methods->getStorage($count_method_id)->mutation_free;
- } catch (Exception $e) {
- // do nothing
- }
- }
- }
- }
- }
-
- $function_callable = InternalCallMapHandler::getCallableFromCallMapById(
- $codebase,
- $function_id,
- $args ?: [],
- null
- );
-
- if (!$function_callable->params
- || ($args !== null && count($args) === 0)
- || ($function_callable->return_type && $function_callable->return_type->isVoid())
- ) {
- return false;
- }
-
- $must_use = $function_id !== 'array_map'
- || (isset($args[0]) && !$args[0]->value instanceof ClosureNode);
-
- foreach ($function_callable->params as $i => $param) {
- if ($type_provider && $param->type && $param->type->hasCallableType() && isset($args[$i])) {
- $arg_type = $type_provider->getType($args[$i]->value);
-
- if ($arg_type) {
- foreach ($arg_type->getAtomicTypes() as $possible_callable) {
- $possible_callable = CallableTypeComparator::getCallableFromAtomic(
- $codebase,
- $possible_callable
- );
-
- if ($possible_callable && !$possible_callable->is_pure) {
- return false;
- }
- }
- }
- }
-
- if ($param->by_ref && isset($args[$i])) {
- $must_use = false;
- }
- }
-
- return true;
- }
-
- public static function clearCache(): void
- {
- self::$stubbed_functions = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/InternalCallMapHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/InternalCallMapHandler.php
deleted file mode 100644
index 8af4d0c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/InternalCallMapHandler.php
+++ /dev/null
@@ -1,437 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use PhpParser;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\NodeTypeProvider;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\TaintKind;
-use UnexpectedValueException;
-
-use function array_shift;
-use function assert;
-use function count;
-use function dirname;
-use function file_exists;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-use function version_compare;
-
-/**
- * @internal
- *
- * Gets values from the call map array, which stores data about native functions and methods
- */
-class InternalCallMapHandler
-{
- private const PHP_MAJOR_VERSION = 8;
- private const PHP_MINOR_VERSION = 1;
- private const LOWEST_AVAILABLE_DELTA = 71;
-
- /**
- * @var ?int
- */
- private static $loaded_php_major_version;
- /**
- * @var ?int
- */
- private static $loaded_php_minor_version;
-
- /**
- * @var array<lowercase-string, array<int|string,string>>|null
- */
- private static $call_map;
-
- /**
- * @var array<list<TCallable>>|null
- */
- private static $call_map_callables = [];
-
- /**
- * @var array<string, list<list<TaintKind::*>>>
- */
- private static $taint_sink_map = [];
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- */
- public static function getCallableFromCallMapById(
- Codebase $codebase,
- string $method_id,
- array $args,
- ?NodeDataProvider $nodes
- ): TCallable {
- $possible_callables = self::getCallablesFromCallMap($method_id);
-
- if ($possible_callables === null) {
- throw new UnexpectedValueException(
- 'Not expecting $function_param_options to be null for ' . $method_id
- );
- }
-
- return self::getMatchingCallableFromCallMapOptions(
- $codebase,
- $possible_callables,
- $args,
- $nodes,
- $method_id
- );
- }
-
- /**
- * @param array<int, TCallable> $callables
- * @param list<PhpParser\Node\Arg> $args
- *
- */
- public static function getMatchingCallableFromCallMapOptions(
- Codebase $codebase,
- array $callables,
- array $args,
- ?NodeTypeProvider $nodes,
- string $method_id
- ): TCallable {
- if (count($callables) === 1) {
- return $callables[0];
- }
-
- $matching_param_count_callable = null;
- $matching_coerced_param_count_callable = null;
-
- foreach ($callables as $possible_callable) {
- $possible_function_params = $possible_callable->params;
-
- assert($possible_function_params !== null);
-
- $all_args_match = true;
- $type_coerced = false;
-
- $last_param = count($possible_function_params)
- ? $possible_function_params[count($possible_function_params) - 1]
- : null;
-
- $mandatory_param_count = count($possible_function_params);
-
- foreach ($possible_function_params as $i => $possible_function_param) {
- if ($possible_function_param->is_optional) {
- $mandatory_param_count = $i;
- break;
- }
- }
-
- if ($mandatory_param_count > count($args) && !($last_param && $last_param->is_variadic)) {
- continue;
- }
-
- foreach ($args as $argument_offset => $arg) {
- if ($argument_offset >= count($possible_function_params)) {
- if (!$last_param || !$last_param->is_variadic) {
- $all_args_match = false;
- break;
- }
-
- $function_param = $last_param;
- } else {
- $function_param = $possible_function_params[$argument_offset];
- }
-
- $param_type = $function_param->type;
-
- if (!$param_type) {
- continue;
- }
-
- if (!$nodes
- || !($arg_type = $nodes->getType($arg->value))
- ) {
- continue;
- }
-
- if ($arg_type->hasMixed()) {
- continue;
- }
-
- if ($arg->unpack && !$function_param->is_variadic) {
- if ($arg_type->hasArray()) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_atomic_type = $arg_type->getAtomicTypes()['array'];
-
- if ($array_atomic_type instanceof TKeyedArray) {
- $arg_type = $array_atomic_type->getGenericValueType();
- } elseif ($array_atomic_type instanceof TList) {
- $arg_type = $array_atomic_type->type_param;
- } else {
- $arg_type = $array_atomic_type->type_params[1];
- }
- }
- }
-
- $arg_result = new TypeComparisonResult();
-
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $arg_type,
- $param_type,
- true,
- true,
- $arg_result
- ) || $arg_result->type_coerced) {
- if ($arg_result->type_coerced) {
- $type_coerced = true;
- }
-
- continue;
- }
-
- $all_args_match = false;
- break;
- }
-
- if (count($args) === count($possible_function_params)) {
- $matching_param_count_callable = $possible_callable;
- }
-
- if ($all_args_match && (!$type_coerced || $method_id === 'max' || $method_id === 'min')) {
- return $possible_callable;
- }
-
- if ($all_args_match) {
- $matching_coerced_param_count_callable = $possible_callable;
- }
- }
-
- if ($matching_coerced_param_count_callable) {
- return $matching_coerced_param_count_callable;
- }
-
- if ($matching_param_count_callable) {
- return $matching_param_count_callable;
- }
-
- // if we don't succeed in finding a match, set to the first possible and wait for issues below
- return $callables[0];
- }
-
- /**
- * @return list<TCallable>|null
- */
- public static function getCallablesFromCallMap(string $function_id): ?array
- {
- $call_map_key = strtolower($function_id);
-
- if (isset(self::$call_map_callables[$call_map_key])) {
- return self::$call_map_callables[$call_map_key];
- }
-
- $call_map = self::getCallMap();
-
- if (!isset($call_map[$call_map_key])) {
- return null;
- }
-
- $call_map_functions = [];
- $call_map_functions[] = $call_map[$call_map_key];
-
- for ($i = 1; $i < 10; ++$i) {
- if (!isset($call_map[$call_map_key . '\'' . $i])) {
- break;
- }
-
- $call_map_functions[] = $call_map[$call_map_key . '\'' . $i];
- }
-
- $possible_callables = [];
-
- foreach ($call_map_functions as $call_map_function_args) {
- $return_type_string = array_shift($call_map_function_args);
-
- if (!$return_type_string) {
- $return_type = Type::getMixed();
- } else {
- $return_type = Type::parseString($return_type_string);
- }
-
- $function_params = [];
-
- $arg_offset = 0;
-
- /** @var string $arg_name - key type changed with above array_shift */
- foreach ($call_map_function_args as $arg_name => $arg_type) {
- $by_reference = false;
- $optional = false;
- $variadic = false;
-
- if ($arg_name[0] === '&') {
- $arg_name = substr($arg_name, 1);
- $by_reference = true;
- }
-
- if (substr($arg_name, -1) === '=') {
- $arg_name = substr($arg_name, 0, -1);
- $optional = true;
- }
-
- if (strpos($arg_name, '...') === 0) {
- $arg_name = substr($arg_name, 3);
- $variadic = true;
- }
-
- $param_type = $arg_type
- ? Type::parseString($arg_type)
- : Type::getMixed();
-
- $out_type = null;
-
- if (strlen($arg_name) > 2 && $arg_name[0] === 'w' && $arg_name[1] === '_') {
- $out_type = $param_type;
- $param_type = Type::getMixed();
- }
-
- $function_param = new FunctionLikeParameter(
- $arg_name,
- $by_reference,
- $param_type,
- null,
- null,
- $optional,
- false,
- $variadic
- );
-
- if ($out_type) {
- $function_param->out_type = $out_type;
- }
-
- if ($arg_name === 'haystack') {
- $function_param->expect_variable = true;
- }
-
- if (isset(self::$taint_sink_map[$call_map_key][$arg_offset])) {
- $function_param->sinks = self::$taint_sink_map[$call_map_key][$arg_offset];
- }
-
- $function_param->signature_type = null;
-
- $function_params[] = $function_param;
-
- $arg_offset++;
- }
-
- $possible_callables[] = new TCallable('callable', $function_params, $return_type);
- }
-
- self::$call_map_callables[$call_map_key] = $possible_callables;
-
- return $possible_callables;
- }
-
- /**
- * Gets the method/function call map
- *
- * @return array<string, array<int|string, string>>
- */
- public static function getCallMap(): array
- {
- $codebase = ProjectAnalyzer::getInstance()->getCodebase();
- $analyzer_major_version = $codebase->php_major_version;
- $analyzer_minor_version = $codebase->php_minor_version;
-
- $analyzer_version = $analyzer_major_version . '.' . $analyzer_minor_version;
- $current_version = self::PHP_MAJOR_VERSION . '.' . self::PHP_MINOR_VERSION;
-
- $analyzer_version_int = (int) ($analyzer_major_version . $analyzer_minor_version);
- $current_version_int = (int) (self::PHP_MAJOR_VERSION . self::PHP_MINOR_VERSION);
-
- if (self::$call_map !== null
- && $analyzer_major_version === self::$loaded_php_major_version
- && $analyzer_minor_version === self::$loaded_php_minor_version
- ) {
- return self::$call_map;
- }
-
- /** @var array<string, array<int|string, string>> */
- $call_map = require(dirname(__DIR__, 4) . '/dictionaries/CallMap.php');
-
- self::$call_map = [];
-
- foreach ($call_map as $key => $value) {
- $cased_key = strtolower($key);
- self::$call_map[$cased_key] = $value;
- }
-
- /**
- * @var array<string, list<list<TaintKind::*>>>
- */
- $taint_map = require(dirname(__DIR__, 4) . '/dictionaries/InternalTaintSinkMap.php');
-
- foreach ($taint_map as $key => $value) {
- $cased_key = strtolower($key);
- self::$taint_sink_map[$cased_key] = $value;
- }
-
- if (version_compare($analyzer_version, $current_version, '<')) {
- // the following assumes both minor and major versions a single digits
- for ($i = $current_version_int; $i > $analyzer_version_int && $i >= self::LOWEST_AVAILABLE_DELTA; --$i) {
- $delta_file = dirname(__DIR__, 4) . '/dictionaries/CallMap_' . $i . '_delta.php';
- if (!file_exists($delta_file)) {
- continue;
- }
- /**
- * @var array{
- * added: array<string, array<int|string, string>>,
- * changed: array<string, array{
- * old: array<int|string, string>,
- * new: array<int|string, string>
- * }>,
- * removed: array<string, array<int|string, string>>
- * }
- */
- $diff_call_map = require($delta_file);
-
- foreach ($diff_call_map['added'] as $key => $_) {
- $cased_key = strtolower($key);
- unset(self::$call_map[$cased_key]);
- }
-
- foreach ($diff_call_map['removed'] as $key => $value) {
- $cased_key = strtolower($key);
- self::$call_map[$cased_key] = $value;
- }
-
- foreach ($diff_call_map['changed'] as $key => ['old' => $value]) {
- $cased_key = strtolower($key);
- self::$call_map[$cased_key] = $value;
- }
- }
- }
-
- self::$loaded_php_major_version = $analyzer_major_version;
- self::$loaded_php_minor_version = $analyzer_minor_version;
-
- return self::$call_map;
- }
-
- public static function inCallMap(string $key): bool
- {
- return isset(self::getCallMap()[strtolower($key)]);
- }
-
- public static function clearCache(): void
- {
- self::$call_map_callables = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php
deleted file mode 100644
index 6324b77..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php
+++ /dev/null
@@ -1,1255 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use InvalidArgumentException;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\SourceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\MethodExistenceProvider;
-use Psalm\Internal\Provider\MethodParamsProvider;
-use Psalm\Internal\Provider\MethodReturnTypeProvider;
-use Psalm\Internal\Provider\MethodVisibilityProvider;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\StatementsSource;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_pop;
-use function array_values;
-use function assert;
-use function count;
-use function explode;
-use function in_array;
-use function is_int;
-use function reset;
-use function strtolower;
-
-/**
- * @internal
- *
- * Handles information about class methods
- */
-class Methods
-{
- /**
- * @var ClassLikeStorageProvider
- */
- private $classlike_storage_provider;
-
- /**
- * @var bool
- */
- public $collect_locations = false;
-
- /**
- * @var FileReferenceProvider
- */
- public $file_reference_provider;
-
- /**
- * @var ClassLikes
- */
- private $classlikes;
-
- /** @var MethodReturnTypeProvider */
- public $return_type_provider;
-
- /** @var MethodParamsProvider */
- public $params_provider;
-
- /** @var MethodExistenceProvider */
- public $existence_provider;
-
- /** @var MethodVisibilityProvider */
- public $visibility_provider;
-
- public function __construct(
- ClassLikeStorageProvider $storage_provider,
- FileReferenceProvider $file_reference_provider,
- ClassLikes $classlikes
- ) {
- $this->classlike_storage_provider = $storage_provider;
- $this->file_reference_provider = $file_reference_provider;
- $this->classlikes = $classlikes;
- $this->return_type_provider = new MethodReturnTypeProvider();
- $this->existence_provider = new MethodExistenceProvider();
- $this->visibility_provider = new MethodVisibilityProvider();
- $this->params_provider = new MethodParamsProvider();
- }
-
- /**
- * Whether or not a given method exists
- *
- * If you pass true in $is_used argument the method return is considered used
- *
- * @param lowercase-string|null $calling_method_id
- */
- public function methodExists(
- MethodIdentifier $method_id,
- ?string $calling_method_id = null,
- ?CodeLocation $code_location = null,
- ?StatementsSource $source = null,
- ?string $source_file_path = null,
- bool $use_method_existence_provider = true,
- bool $is_used = false
- ): bool {
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- if ($use_method_existence_provider && $this->existence_provider->has($fq_class_name)) {
- $method_exists = $this->existence_provider->doesMethodExist(
- $fq_class_name,
- $method_name,
- $source,
- $code_location
- );
-
- if ($method_exists !== null) {
- return $method_exists;
- }
- }
-
- $old_method_id = null;
-
- $fq_class_name = strtolower($this->classlikes->getUnAliasedName($fq_class_name));
-
- try {
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
- } catch (InvalidArgumentException $e) {
- return false;
- }
-
- if ($class_storage->is_enum) {
- if ($method_name === 'cases') {
- return true;
- }
-
- if ($class_storage->enum_type
- && in_array($method_name, ['from', 'tryFrom'], true)
- ) {
- return true;
- }
- }
-
- $source_file_path = $source ? $source->getFilePath() : $source_file_path;
-
- $calling_class_name = $source ? $source->getFQCLN() : null;
-
- if (!$calling_class_name && $calling_method_id) {
- $calling_class_name = explode('::', $calling_method_id)[0];
- }
-
- if (isset($class_storage->declaring_method_ids[$method_name])) {
- $declaring_method_id = $class_storage->declaring_method_ids[$method_name];
-
- if ($calling_method_id === strtolower((string) $declaring_method_id)) {
- return true;
- }
-
- $declaring_fq_class_name = strtolower($declaring_method_id->fq_class_name);
-
- if ($declaring_fq_class_name !== strtolower((string) $calling_class_name)) {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClass(
- $calling_method_id,
- $declaring_fq_class_name
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $source_file_path,
- $declaring_fq_class_name
- );
- }
- }
-
- if ((string) $method_id !== (string) $declaring_method_id
- && $class_storage->user_defined
- && isset($class_storage->potential_declaring_method_ids[$method_name])
- ) {
- foreach ($class_storage->potential_declaring_method_ids[$method_name] as $potential_id => $_) {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClassMember(
- $calling_method_id,
- $potential_id,
- $is_used
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addFileReferenceToClassMember(
- $source_file_path,
- $potential_id,
- $is_used
- );
- }
- }
- } else {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClassMember(
- $calling_method_id,
- strtolower((string) $declaring_method_id),
- $is_used
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addFileReferenceToClassMember(
- $source_file_path,
- strtolower((string) $declaring_method_id),
- $is_used
- );
- }
- }
-
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClassMethod(
- $code_location,
- strtolower((string) $declaring_method_id)
- );
- }
-
- foreach ($class_storage->class_implements as $fq_interface_name) {
- $interface_method_id_lc = strtolower($fq_interface_name . '::' . $method_name);
-
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClassMethod(
- $code_location,
- $interface_method_id_lc
- );
- }
-
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClassMember(
- $calling_method_id,
- $interface_method_id_lc,
- $is_used
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addFileReferenceToClassMember(
- $source_file_path,
- $interface_method_id_lc,
- $is_used
- );
- }
- }
-
- $declaring_method_class = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
-
- $declaring_class_storage = $this->classlike_storage_provider->get($declaring_method_class);
-
- if (isset($declaring_class_storage->overridden_method_ids[$declaring_method_name])) {
- $overridden_method_ids = $declaring_class_storage->overridden_method_ids[$declaring_method_name];
-
- foreach ($overridden_method_ids as $overridden_method_id) {
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClassMethod(
- $code_location,
- strtolower((string) $overridden_method_id)
- );
- }
-
- if ($calling_method_id) {
- // also store failures in case the method is added later
- $this->file_reference_provider->addMethodReferenceToClassMember(
- $calling_method_id,
- strtolower((string) $overridden_method_id),
- $is_used
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addFileReferenceToClassMember(
- $source_file_path,
- strtolower((string) $overridden_method_id),
- $is_used
- );
- }
- }
- }
-
- return true;
- }
-
- if ($source_file_path && $fq_class_name !== strtolower((string) $calling_class_name)) {
- if ($calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClass(
- $calling_method_id,
- $fq_class_name
- );
- } else {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $source_file_path,
- $fq_class_name
- );
- }
- }
-
- if ($class_storage->abstract && isset($class_storage->overridden_method_ids[$method_name])) {
- return true;
- }
-
- // support checking oldstyle constructors
- if ($method_name === '__construct') {
- $method_name_parts = explode('\\', $fq_class_name);
- $old_constructor_name = array_pop($method_name_parts);
- $old_method_id = $fq_class_name . '::' . $old_constructor_name;
- }
-
- if (!$class_storage->user_defined
- && (InternalCallMapHandler::inCallMap((string) $method_id)
- || ($old_method_id && InternalCallMapHandler::inCallMap($old_method_id)))
- ) {
- return true;
- }
-
- foreach ($class_storage->parent_classes + $class_storage->used_traits as $potential_future_declaring_fqcln) {
- $potential_id = strtolower($potential_future_declaring_fqcln) . '::' . $method_name;
-
- if ($calling_method_id) {
- // also store failures in case the method is added later
- $this->file_reference_provider->addMethodReferenceToMissingClassMember(
- $calling_method_id,
- $potential_id
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addFileReferenceToMissingClassMember(
- $source_file_path,
- $potential_id
- );
- }
- }
-
- if ($calling_method_id) {
- // also store failures in case the method is added later
- $this->file_reference_provider->addMethodReferenceToMissingClassMember(
- $calling_method_id,
- strtolower((string) $method_id)
- );
- } elseif ($source_file_path) {
- $this->file_reference_provider->addFileReferenceToMissingClassMember(
- $source_file_path,
- strtolower((string) $method_id)
- );
- }
-
- return false;
- }
-
- /**
- * @param list<PhpParser\Node\Arg> $args
- *
- * @return list<FunctionLikeParameter>
- */
- public function getMethodParams(
- MethodIdentifier $method_id,
- ?StatementsSource $source = null,
- ?array $args = null,
- ?Context $context = null
- ): array {
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- if ($this->params_provider->has($fq_class_name)) {
- $method_params = $this->params_provider->getMethodParams(
- $fq_class_name,
- $method_name,
- $args,
- $source,
- $context
- );
-
- if ($method_params !== null) {
- return $method_params;
- }
- }
-
- $declaring_method_id = $this->getDeclaringMethodId($method_id);
-
- $callmap_id = $declaring_method_id ?? $method_id;
-
- // functions
- if (InternalCallMapHandler::inCallMap((string) $callmap_id)) {
- $class_storage = $this->classlike_storage_provider->get($callmap_id->fq_class_name);
-
- $declaring_method_name = $declaring_method_id->method_name ?? $method_name;
-
- if (!$class_storage->stubbed || empty($class_storage->methods[$declaring_method_name]->stubbed)) {
- $function_callables = InternalCallMapHandler::getCallablesFromCallMap((string) $callmap_id);
-
- if ($function_callables === null) {
- throw new UnexpectedValueException(
- 'Not expecting $function_callables to be null for ' . $callmap_id
- );
- }
-
- if (!$source || $args === null || count($function_callables) === 1) {
- assert($function_callables[0]->params !== null);
-
- return $function_callables[0]->params;
- }
-
- if ($context && $source instanceof StatementsAnalyzer) {
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- foreach ($args as $arg) {
- ExpressionAnalyzer::analyze(
- $source,
- $arg->value,
- $context
- );
- }
-
- $context->inside_call = $was_inside_call;
- }
-
- $matching_callable = InternalCallMapHandler::getMatchingCallableFromCallMapOptions(
- $source->getCodebase(),
- $function_callables,
- $args,
- $source->getNodeTypeProvider(),
- (string) $callmap_id
- );
-
- assert($matching_callable->params !== null);
-
- return $matching_callable->params;
- }
- }
-
- if ($declaring_method_id) {
- $storage = $this->getStorage($declaring_method_id);
-
- $params = $storage->params;
-
- if ($storage->has_docblock_param_types) {
- return $params;
- }
-
- $appearing_method_id = $this->getAppearingMethodId($declaring_method_id);
-
- if (!$appearing_method_id) {
- return $params;
- }
-
- $appearing_fq_class_name = $appearing_method_id->fq_class_name;
- $appearing_method_name = $appearing_method_id->method_name;
-
- $class_storage = $this->classlike_storage_provider->get($appearing_fq_class_name);
-
- if (!isset($class_storage->overridden_method_ids[$appearing_method_name])) {
- return $params;
- }
-
- if (!isset($class_storage->documenting_method_ids[$appearing_method_name])) {
- return $params;
- }
-
- $overridden_method_id = $class_storage->documenting_method_ids[$appearing_method_name];
-
- $overridden_storage = $this->getStorage($overridden_method_id);
-
- $overriding_fq_class_name = $overridden_method_id->fq_class_name;
-
- foreach ($params as $i => $param) {
- if (isset($overridden_storage->params[$i]->type)
- && $overridden_storage->params[$i]->has_docblock_type
- ) {
- $params[$i] = clone $param;
- /** @var Union $params[$i]->type */
- $params[$i]->type = clone $overridden_storage->params[$i]->type;
-
- if ($source) {
- $overridden_class_storage = $this->classlike_storage_provider->get($overriding_fq_class_name);
- $params[$i]->type = self::localizeType(
- $source->getCodebase(),
- $params[$i]->type,
- $appearing_fq_class_name,
- $overridden_class_storage->name
- );
- }
-
- if ($params[$i]->signature_type
- && $params[$i]->signature_type->isNullable()
- ) {
- $params[$i]->type->addType(new TNull);
- }
-
- $params[$i]->type_location = $overridden_storage->params[$i]->type_location;
- }
- }
-
- return $params;
- }
-
- throw new UnexpectedValueException('Cannot get method params for ' . $method_id);
- }
-
- public static function localizeType(
- Codebase $codebase,
- Union $type,
- string $appearing_fq_class_name,
- string $base_fq_class_name
- ): Union {
- $class_storage = $codebase->classlike_storage_provider->get($appearing_fq_class_name);
- $extends = $class_storage->template_extended_params;
-
- if (!$extends) {
- return $type;
- }
-
- $type = clone $type;
-
- foreach ($type->getAtomicTypes() as $key => $atomic_type) {
- if ($atomic_type instanceof TTemplateParam
- && ($atomic_type->defining_class === $base_fq_class_name
- || isset($extends[$atomic_type->defining_class]))
- ) {
- $types_to_add = self::getExtendedTemplatedTypes(
- $atomic_type,
- $extends
- );
-
- if ($types_to_add) {
- $type->removeType($key);
-
- foreach ($types_to_add as $extra_added_type) {
- $type->addType($extra_added_type);
- }
- }
- }
-
- if ($atomic_type instanceof TTemplateParamClass) {
- if ($atomic_type->defining_class === $base_fq_class_name) {
- if (isset($extends[$base_fq_class_name][$atomic_type->param_name])) {
- $extended_param = $extends[$base_fq_class_name][$atomic_type->param_name];
-
- $types = array_values($extended_param->getAtomicTypes());
-
- if (count($types) === 1 && $types[0] instanceof TNamedObject) {
- $atomic_type->as_type = $types[0];
- } else {
- $atomic_type->as_type = null;
- }
- }
- }
- }
-
- if ($atomic_type instanceof TArray
- || $atomic_type instanceof TIterable
- || $atomic_type instanceof TGenericObject
- ) {
- foreach ($atomic_type->type_params as &$type_param) {
- $type_param = self::localizeType(
- $codebase,
- $type_param,
- $appearing_fq_class_name,
- $base_fq_class_name
- );
- }
- }
-
- if ($atomic_type instanceof TList) {
- $atomic_type->type_param = self::localizeType(
- $codebase,
- $atomic_type->type_param,
- $appearing_fq_class_name,
- $base_fq_class_name
- );
- }
-
- if ($atomic_type instanceof TKeyedArray) {
- foreach ($atomic_type->properties as &$property_type) {
- $property_type = self::localizeType(
- $codebase,
- $property_type,
- $appearing_fq_class_name,
- $base_fq_class_name
- );
- }
- }
-
- if ($atomic_type instanceof TCallable
- || $atomic_type instanceof TClosure
- ) {
- if ($atomic_type->params) {
- foreach ($atomic_type->params as $param) {
- if ($param->type) {
- $param->type = self::localizeType(
- $codebase,
- $param->type,
- $appearing_fq_class_name,
- $base_fq_class_name
- );
- }
- }
- }
-
- if ($atomic_type->return_type) {
- $atomic_type->return_type = self::localizeType(
- $codebase,
- $atomic_type->return_type,
- $appearing_fq_class_name,
- $base_fq_class_name
- );
- }
- }
- }
-
- $type->bustCache();
-
- return $type;
- }
-
- /**
- * @param array<string, array<string, Union>> $extends
- * @return list<Atomic>
- */
- public static function getExtendedTemplatedTypes(
- TTemplateParam $atomic_type,
- array $extends
- ): array {
- $extra_added_types = [];
-
- if (isset($extends[$atomic_type->defining_class][$atomic_type->param_name])) {
- $extended_param = clone $extends[$atomic_type->defining_class][$atomic_type->param_name];
-
- foreach ($extended_param->getAtomicTypes() as $extended_atomic_type) {
- if ($extended_atomic_type instanceof TTemplateParam) {
- $extra_added_types = array_merge(
- $extra_added_types,
- self::getExtendedTemplatedTypes(
- $extended_atomic_type,
- $extends
- )
- );
- } else {
- $extra_added_types[] = $extended_atomic_type;
- }
- }
- } else {
- $extra_added_types[] = $atomic_type;
- }
-
- return $extra_added_types;
- }
-
- public function isVariadic(MethodIdentifier $method_id): bool
- {
- $declaring_method_id = $this->getDeclaringMethodId($method_id);
-
- if (!$declaring_method_id) {
- return false;
- }
-
- return $this->getStorage($declaring_method_id)->variadic;
- }
-
- /**
- * @param list<PhpParser\Node\Arg>|null $args
- *
- */
- public function getMethodReturnType(
- MethodIdentifier $method_id,
- ?string &$self_class,
- ?SourceAnalyzer $source_analyzer = null,
- ?array $args = null
- ): ?Union {
- $original_fq_class_name = $method_id->fq_class_name;
- $original_method_name = $method_id->method_name;
-
- $adjusted_fq_class_name = $this->classlikes->getUnAliasedName($original_fq_class_name);
-
- if ($adjusted_fq_class_name !== $original_fq_class_name) {
- $original_fq_class_name = strtolower($adjusted_fq_class_name);
- }
-
- $original_class_storage = $this->classlike_storage_provider->get($original_fq_class_name);
-
- if (isset($original_class_storage->pseudo_methods[$original_method_name])) {
- return $original_class_storage->pseudo_methods[$original_method_name]->return_type;
- }
-
- $declaring_method_id = $this->getDeclaringMethodId($method_id);
-
- if (!$declaring_method_id) {
- return null;
- }
-
- $appearing_method_id = $this->getAppearingMethodId($method_id);
-
- if (!$appearing_method_id) {
- $class_storage = $this->classlike_storage_provider->get($original_fq_class_name);
-
- if ($class_storage->abstract && isset($class_storage->overridden_method_ids[$original_method_name])) {
- $appearing_method_id = reset($class_storage->overridden_method_ids[$original_method_name]);
- } else {
- return null;
- }
- }
-
- $appearing_fq_class_name = $appearing_method_id->fq_class_name;
- $appearing_method_name = $appearing_method_id->method_name;
-
- $appearing_fq_class_storage = $this->classlike_storage_provider->get($appearing_fq_class_name);
-
- if ($appearing_fq_class_name === 'UnitEnum'
- && $original_class_storage->is_enum
- ) {
- if ($original_method_name === 'cases') {
- if ($original_class_storage->enum_cases === []) {
- return Type::getEmptyArray();
- }
- $types = [];
-
- foreach ($original_class_storage->enum_cases as $case_name => $_) {
- $types[] = new Union([new TEnumCase($original_fq_class_name, $case_name)]);
- }
-
- $list = new TKeyedArray($types);
- $list->is_list = true;
- $list->sealed = true;
- return new Union([$list]);
- }
- }
-
- if ($appearing_fq_class_name === 'BackedEnum'
- && $original_class_storage->is_enum
- && $original_class_storage->enum_type
- ) {
- if (($original_method_name === 'from'
- || $original_method_name === 'tryfrom'
- ) && $source_analyzer
- && isset($args[0])
- && ($first_arg_type = $source_analyzer->getNodeTypeProvider()->getType($args[0]->value))
- ) {
- $types = [];
- foreach ($original_class_storage->enum_cases as $case_name => $case_storage) {
- if (UnionTypeComparator::isContainedBy(
- $source_analyzer->getCodebase(),
- is_int($case_storage->value) ?
- Type::getInt(false, $case_storage->value) :
- Type::getString($case_storage->value),
- $first_arg_type
- )) {
- $types[] = new TEnumCase($original_fq_class_name, $case_name);
- }
- }
- if ($types) {
- if ($original_method_name === 'tryfrom') {
- $types[] = new TNull();
- }
- return new Union($types);
- }
- return $original_method_name === 'tryfrom' ? Type::getNull() : Type::getNever();
- }
- }
-
- if (!$appearing_fq_class_storage->user_defined
- && !$appearing_fq_class_storage->stubbed
- && InternalCallMapHandler::inCallMap((string) $appearing_method_id)
- ) {
- if ((string) $appearing_method_id === 'Closure::fromcallable'
- && isset($args[0])
- && $source_analyzer
- && ($first_arg_type = $source_analyzer->getNodeTypeProvider()->getType($args[0]->value))
- && $first_arg_type->isSingle()
- ) {
- foreach ($first_arg_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TCallable
- || $atomic_type instanceof TClosure
- ) {
- $callable_type = clone $atomic_type;
-
- return new Union([new TClosure(
- 'Closure',
- $callable_type->params,
- $callable_type->return_type
- )]);
- }
-
- if ($atomic_type instanceof TNamedObject
- && $this->methodExists(
- new MethodIdentifier($atomic_type->value, '__invoke')
- )
- ) {
- $invokable_storage = $this->getStorage(
- new MethodIdentifier($atomic_type->value, '__invoke')
- );
-
- return new Union([new TClosure(
- 'Closure',
- $invokable_storage->params,
- $invokable_storage->return_type
- )]);
- }
- }
- }
-
- $callmap_callables = InternalCallMapHandler::getCallablesFromCallMap((string) $appearing_method_id);
-
- if (!$callmap_callables || $callmap_callables[0]->return_type === null) {
- throw new UnexpectedValueException('Shouldn’t get here');
- }
-
- $return_type_candidate = $callmap_callables[0]->return_type;
-
- if ($return_type_candidate->isFalsable()) {
- $return_type_candidate->ignore_falsable_issues = true;
- }
-
- return $return_type_candidate;
- }
-
- $class_storage = $this->classlike_storage_provider->get($appearing_fq_class_name);
-
- $storage = $this->getStorage($declaring_method_id);
-
- $candidate_type = $storage->return_type;
-
- if ($candidate_type && $candidate_type->isVoid()) {
- return clone $candidate_type;
- }
-
- if (isset($class_storage->documenting_method_ids[$appearing_method_name])) {
- $overridden_method_id = $class_storage->documenting_method_ids[$appearing_method_name];
-
- // special override to allow inference of Iterator types
- if ($overridden_method_id->fq_class_name === 'Iterator'
- && $storage->return_type
- && $storage->return_type === $storage->signature_return_type
- ) {
- return clone $storage->return_type;
- }
-
- $overridden_storage = $this->getStorage($overridden_method_id);
-
- if ($overridden_storage->return_type) {
- if ($overridden_storage->return_type->isNull()) {
- return Type::getVoid();
- }
-
- if (!$candidate_type || !$source_analyzer) {
- $self_class = $overridden_method_id->fq_class_name;
-
- return clone $overridden_storage->return_type;
- }
-
- if ($candidate_type->getId() === $overridden_storage->return_type->getId()) {
- $self_class = $appearing_fq_class_storage->name;
-
- return clone $candidate_type;
- }
-
- $overridden_class_storage =
- $this->classlike_storage_provider->get($overridden_method_id->fq_class_name);
-
- $overridden_storage_return_type = TypeExpander::expandUnion(
- $source_analyzer->getCodebase(),
- clone $overridden_storage->return_type,
- $overridden_method_id->fq_class_name,
- $appearing_fq_class_name,
- $overridden_class_storage->parent_class,
- true,
- false,
- $storage->final
- );
-
- $old_contained_by_new = UnionTypeComparator::isContainedBy(
- $source_analyzer->getCodebase(),
- $candidate_type,
- $overridden_storage_return_type
- );
-
- $new_contained_by_old = UnionTypeComparator::isContainedBy(
- $source_analyzer->getCodebase(),
- $overridden_storage_return_type,
- $candidate_type
- );
-
- if ((!$old_contained_by_new && !$new_contained_by_old)
- || ($old_contained_by_new && $new_contained_by_old)
- ) {
- if ($old_contained_by_new) { //implicitly $new_contained_by_old as well
- $attempted_intersection = Type::intersectUnionTypes(
- $candidate_type,
- $overridden_storage->return_type,
- $source_analyzer->getCodebase()
- );
- } else {
- $attempted_intersection = Type::intersectUnionTypes(
- $overridden_storage->return_type,
- $candidate_type,
- $source_analyzer->getCodebase()
- );
- }
-
- if ($attempted_intersection) {
- $self_class = $overridden_method_id->fq_class_name;
-
- return $attempted_intersection;
- }
-
- $self_class = $appearing_fq_class_storage->name;
-
- return clone $candidate_type;
- }
-
- if ($old_contained_by_new) {
- $self_class = $appearing_fq_class_storage->name;
-
- return clone $candidate_type;
- }
-
- $self_class = $overridden_method_id->fq_class_name;
-
- return clone $overridden_storage->return_type;
- }
- }
-
- if ($candidate_type) {
- $self_class = $appearing_fq_class_storage->name;
-
- return clone $candidate_type;
- }
-
- if (!isset($class_storage->overridden_method_ids[$appearing_method_name])) {
- return null;
- }
-
- $candidate_type = null;
-
- foreach ($class_storage->overridden_method_ids[$appearing_method_name] as $overridden_method_id) {
- $overridden_storage = $this->getStorage($overridden_method_id);
-
- if ($overridden_storage->return_type) {
- if ($overridden_storage->return_type->isNull()) {
- if ($candidate_type && !$candidate_type->isVoid()) {
- return null;
- }
-
- $candidate_type = Type::getVoid();
- continue;
- }
-
- $fq_overridden_class = $overridden_method_id->fq_class_name;
-
- $overridden_class_storage =
- $this->classlike_storage_provider->get($fq_overridden_class);
-
- $overridden_return_type = clone $overridden_storage->return_type;
-
- $self_class = $overridden_class_storage->name;
-
- if ($candidate_type && $source_analyzer && !$candidate_type->isMixed()) {
- $old_contained_by_new = UnionTypeComparator::isContainedBy(
- $source_analyzer->getCodebase(),
- $candidate_type,
- $overridden_return_type
- );
-
- $new_contained_by_old = UnionTypeComparator::isContainedBy(
- $source_analyzer->getCodebase(),
- $overridden_return_type,
- $candidate_type
- );
-
- if ((!$old_contained_by_new && !$new_contained_by_old)
- || ($old_contained_by_new && $new_contained_by_old)
- ) {
- $attempted_intersection = Type::intersectUnionTypes(
- $candidate_type,
- $overridden_return_type,
- $source_analyzer->getCodebase()
- );
-
- if ($attempted_intersection) {
- $candidate_type = $attempted_intersection;
- continue;
- }
-
- return null;
- }
-
- if ($old_contained_by_new) {
- continue;
- }
- }
-
- $candidate_type = $overridden_return_type;
- }
- }
-
- return $candidate_type;
- }
-
- public function getMethodReturnsByRef(MethodIdentifier $method_id): bool
- {
- $method_id = $this->getDeclaringMethodId($method_id);
-
- if (!$method_id) {
- return false;
- }
-
- $fq_class_storage = $this->classlike_storage_provider->get($method_id->fq_class_name);
-
- if (!$fq_class_storage->user_defined && InternalCallMapHandler::inCallMap((string) $method_id)) {
- return false;
- }
-
- return $this->getStorage($method_id)->returns_by_ref;
- }
-
- /**
- * @param CodeLocation|null $defined_location
- *
- */
- public function getMethodReturnTypeLocation(
- MethodIdentifier $method_id,
- CodeLocation &$defined_location = null
- ): ?CodeLocation {
- $method_id = $this->getDeclaringMethodId($method_id);
-
- if ($method_id === null) {
- return null;
- }
-
- $storage = $this->getStorage($method_id);
-
- if (!$storage->return_type_location) {
- $overridden_method_ids = $this->getOverriddenMethodIds($method_id);
-
- foreach ($overridden_method_ids as $overridden_method_id) {
- $overridden_storage = $this->getStorage($overridden_method_id);
-
- if ($overridden_storage->return_type_location) {
- $defined_location = $overridden_storage->return_type_location;
- break;
- }
- }
- }
-
- return $storage->return_type_location;
- }
-
- /**
- * @param lowercase-string $method_name_lc
- * @param lowercase-string $declaring_method_name_lc
- *
- */
- public function setDeclaringMethodId(
- string $fq_class_name,
- string $method_name_lc,
- string $declaring_fq_class_name,
- string $declaring_method_name_lc
- ): void {
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- $class_storage->declaring_method_ids[$method_name_lc] = new MethodIdentifier(
- $declaring_fq_class_name,
- $declaring_method_name_lc
- );
- }
-
- /**
- * @param lowercase-string $method_name_lc
- * @param lowercase-string $appearing_method_name_lc
- *
- */
- public function setAppearingMethodId(
- string $fq_class_name,
- string $method_name_lc,
- string $appearing_fq_class_name,
- string $appearing_method_name_lc
- ): void {
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- $class_storage->appearing_method_ids[$method_name_lc] = new MethodIdentifier(
- $appearing_fq_class_name,
- $appearing_method_name_lc
- );
- }
-
- public function getDeclaringMethodId(
- MethodIdentifier $method_id
- ): ?MethodIdentifier {
- $fq_class_name = $this->classlikes->getUnAliasedName($method_id->fq_class_name);
-
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- $method_name = $method_id->method_name;
-
- if (isset($class_storage->declaring_method_ids[$method_name])) {
- return $class_storage->declaring_method_ids[$method_name];
- }
-
- if ($class_storage->abstract && isset($class_storage->overridden_method_ids[$method_name])) {
- return reset($class_storage->overridden_method_ids[$method_name]);
- }
-
- return null;
- }
-
- /**
- * Get the class this method appears in (vs is declared in, which could give a trait
- */
- public function getAppearingMethodId(
- MethodIdentifier $method_id
- ): ?MethodIdentifier {
- $fq_class_name = $this->classlikes->getUnAliasedName($method_id->fq_class_name);
-
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- $method_name = $method_id->method_name;
-
- return $class_storage->appearing_method_ids[$method_name] ?? null;
- }
-
- /**
- * @return array<string, MethodIdentifier>
- */
- public function getOverriddenMethodIds(MethodIdentifier $method_id): array
- {
- $class_storage = $this->classlike_storage_provider->get($method_id->fq_class_name);
- $method_name = $method_id->method_name;
-
- return $class_storage->overridden_method_ids[$method_name] ?? [];
- }
-
- public function getCasedMethodId(MethodIdentifier $original_method_id): string
- {
- $method_id = $this->getDeclaringMethodId($original_method_id);
-
- if ($method_id === null) {
- return (string) $original_method_id;
- }
-
- $fq_class_name = $method_id->fq_class_name;
- $new_method_name = $method_id->method_name;
-
- $old_fq_class_name = $original_method_id->fq_class_name;
- $old_method_name = $original_method_id->method_name;
-
- $storage = $this->getStorage($method_id);
-
- if ($old_method_name === $new_method_name
- && strtolower($old_fq_class_name) !== $old_fq_class_name
- ) {
- return $old_fq_class_name . '::' . $storage->cased_name;
- }
-
- return $fq_class_name . '::' . $storage->cased_name;
- }
-
- public function getUserMethodStorage(MethodIdentifier $method_id): ?MethodStorage
- {
- $declaring_method_id = $this->getDeclaringMethodId($method_id);
-
- if (!$declaring_method_id) {
- if (InternalCallMapHandler::inCallMap((string) $method_id)) {
- return null;
- }
-
- throw new UnexpectedValueException('$storage should not be null for ' . $method_id);
- }
-
- $storage = $this->getStorage($declaring_method_id);
-
- if (!$storage->location) {
- return null;
- }
-
- return $storage;
- }
-
- public function getClassLikeStorageForMethod(MethodIdentifier $method_id): ClassLikeStorage
- {
- $fq_class_name = $method_id->fq_class_name;
- $method_name = $method_id->method_name;
-
- if ($this->existence_provider->has($fq_class_name)) {
- if ($this->existence_provider->doesMethodExist(
- $fq_class_name,
- $method_name,
- null,
- null
- )) {
- return $this->classlike_storage_provider->get($fq_class_name);
- }
- }
-
- $declaring_method_id = $this->getDeclaringMethodId($method_id);
-
- if ($declaring_method_id === null) {
- if (InternalCallMapHandler::inCallMap((string) $method_id)) {
- $declaring_method_id = $method_id;
- } else {
- throw new UnexpectedValueException('$storage should not be null for ' . $method_id);
- }
- }
-
- $declaring_fq_class_name = $declaring_method_id->fq_class_name;
-
- return $this->classlike_storage_provider->get($declaring_fq_class_name);
- }
-
- public function getStorage(MethodIdentifier $method_id): MethodStorage
- {
- try {
- $class_storage = $this->classlike_storage_provider->get($method_id->fq_class_name);
- } catch (InvalidArgumentException $e) {
- throw new UnexpectedValueException($e->getMessage());
- }
-
- $method_name = $method_id->method_name;
-
- if (!isset($class_storage->methods[$method_name])) {
- throw new UnexpectedValueException(
- '$storage should not be null for ' . $method_id
- );
- }
-
- return $class_storage->methods[$method_name];
- }
-
- public function hasStorage(MethodIdentifier $method_id): bool
- {
- try {
- $class_storage = $this->classlike_storage_provider->get($method_id->fq_class_name);
- } catch (InvalidArgumentException $e) {
- return false;
- }
-
- $method_name = $method_id->method_name;
-
- if (!isset($class_storage->methods[$method_name])) {
- return false;
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php
deleted file mode 100644
index 9d83282..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php
+++ /dev/null
@@ -1,1224 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use InvalidArgumentException;
-use Psalm\Config;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\Issue\CircularReference;
-use Psalm\IssueBuffer;
-use Psalm\Progress\Progress;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_filter;
-use function array_intersect_key;
-use function array_keys;
-use function array_merge;
-use function count;
-use function in_array;
-use function reset;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- *
- * Populates file and class information so that analysis can work properly
- */
-class Populator
-{
- /**
- * @var ClassLikeStorageProvider
- */
- private $classlike_storage_provider;
-
- /**
- * @var FileStorageProvider
- */
- private $file_storage_provider;
-
- /**
- * @var array<lowercase-string, list<ClassLikeStorage>>
- */
- private $invalid_class_storages = [];
-
- /**
- * @var Progress
- */
- private $progress;
-
- /**
- * @var ClassLikes
- */
- private $classlikes;
-
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var FileReferenceProvider
- */
- private $file_reference_provider;
-
- public function __construct(
- Config $config,
- ClassLikeStorageProvider $classlike_storage_provider,
- FileStorageProvider $file_storage_provider,
- ClassLikes $classlikes,
- FileReferenceProvider $file_reference_provider,
- Progress $progress
- ) {
- $this->classlike_storage_provider = $classlike_storage_provider;
- $this->file_storage_provider = $file_storage_provider;
- $this->classlikes = $classlikes;
- $this->progress = $progress;
- $this->config = $config;
- $this->file_reference_provider = $file_reference_provider;
- }
-
- public function populateCodebase(): void
- {
- $this->progress->debug('ClassLikeStorage is populating' . "\n");
-
- foreach ($this->classlike_storage_provider->getNew() as $class_storage) {
- $this->populateClassLikeStorage($class_storage);
- }
-
- $this->progress->debug('ClassLikeStorage is populated' . "\n");
-
- $this->progress->debug('FileStorage is populating' . "\n");
-
- $all_file_storage = $this->file_storage_provider->getNew();
-
- foreach ($all_file_storage as $file_storage) {
- $this->populateFileStorage($file_storage);
- }
-
- foreach ($this->classlike_storage_provider->getNew() as $class_storage) {
- if ($this->config->allow_phpstorm_generics) {
- foreach ($class_storage->properties as $property_storage) {
- if ($property_storage->type) {
- $this->convertPhpStormGenericToPsalmGeneric($property_storage->type, true);
- }
- }
-
- foreach ($class_storage->methods as $method_storage) {
- if ($method_storage->return_type) {
- $this->convertPhpStormGenericToPsalmGeneric($method_storage->return_type);
- }
-
- foreach ($method_storage->params as $param_storage) {
- if ($param_storage->type) {
- $this->convertPhpStormGenericToPsalmGeneric($param_storage->type);
- }
- }
- }
- }
-
- foreach ($class_storage->dependent_classlikes as $dependent_classlike_lc => $_) {
- try {
- $dependee_storage = $this->classlike_storage_provider->get($dependent_classlike_lc);
- } catch (InvalidArgumentException $exception) {
- continue;
- }
-
- $class_storage->dependent_classlikes += $dependee_storage->dependent_classlikes;
- }
- }
-
- if ($this->config->allow_phpstorm_generics) {
- foreach ($all_file_storage as $file_storage) {
- foreach ($file_storage->functions as $function_storage) {
- if ($function_storage->return_type) {
- $this->convertPhpStormGenericToPsalmGeneric($function_storage->return_type);
- }
-
- foreach ($function_storage->params as $param_storage) {
- if ($param_storage->type) {
- $this->convertPhpStormGenericToPsalmGeneric($param_storage->type);
- }
- }
- }
- }
- }
-
- $this->progress->debug('FileStorage is populated' . "\n");
-
- ClassLikeStorageProvider::populated();
- FileStorageProvider::populated();
- }
-
- private function populateClassLikeStorage(ClassLikeStorage $storage, array $dependent_classlikes = []): void
- {
- if ($storage->populated) {
- return;
- }
-
- $fq_classlike_name_lc = strtolower($storage->name);
-
- if (isset($dependent_classlikes[$fq_classlike_name_lc])) {
- if ($storage->location && IssueBuffer::accepts(
- new CircularReference(
- 'Circular reference discovered when loading ' . $storage->name,
- $storage->location
- )
- )) {
- // fall through
- }
-
- return;
- }
-
- $storage_provider = $this->classlike_storage_provider;
-
- $dependent_classlikes[$fq_classlike_name_lc] = true;
-
- $this->populateDataFromTraits($storage, $storage_provider, $dependent_classlikes);
-
- if ($storage->parent_classes) {
- $this->populateDataFromParentClass($storage, $storage_provider, $dependent_classlikes);
- }
-
- if (!strpos($fq_classlike_name_lc, '\\')
- && !isset($storage->methods['__construct'])
- && isset($storage->methods[$fq_classlike_name_lc])
- && !$storage->is_interface
- && !$storage->is_trait
- ) {
- $storage->methods['__construct'] = $storage->methods[$fq_classlike_name_lc];
- }
-
- $this->populateInterfaceDataFromParentInterfaces($storage, $storage_provider, $dependent_classlikes);
-
- $this->populateDataFromImplementedInterfaces($storage, $storage_provider, $dependent_classlikes);
-
- if ($storage->location) {
- $file_path = $storage->location->file_path;
-
- foreach ($storage->parent_interfaces as $parent_interface_lc) {
- $this->file_reference_provider->addFileInheritanceToClass($file_path, $parent_interface_lc);
- }
-
- foreach ($storage->parent_classes as $parent_class_lc => $_) {
- $this->file_reference_provider->addFileInheritanceToClass($file_path, $parent_class_lc);
- }
-
- foreach ($storage->class_implements as $implemented_interface) {
- $this->file_reference_provider->addFileInheritanceToClass(
- $file_path,
- strtolower($implemented_interface)
- );
- }
-
- foreach ($storage->used_traits as $used_trait_lc => $_) {
- $this->file_reference_provider->addFileInheritanceToClass($file_path, $used_trait_lc);
- }
- }
-
- if ($storage->mutation_free || $storage->external_mutation_free) {
- foreach ($storage->methods as $method) {
- if (!$method->is_static && !$method->external_mutation_free) {
- $method->mutation_free = $storage->mutation_free;
- $method->external_mutation_free = $storage->external_mutation_free;
- $method->immutable = $storage->mutation_free;
- }
- }
-
- if ($storage->mutation_free) {
- foreach ($storage->properties as $property) {
- if (!$property->is_static) {
- $property->readonly = true;
- }
- }
- }
- }
-
- if ($storage->specialize_instance) {
- foreach ($storage->methods as $method) {
- if (!$method->is_static) {
- $method->specialize_call = true;
- }
- }
- }
-
- if (!$storage->is_interface && !$storage->is_trait) {
- foreach ($storage->methods as $method) {
- $method->internal = array_merge($storage->internal, $method->internal);
- }
-
- foreach ($storage->properties as $property) {
- $property->internal = array_merge($storage->internal, $property->internal);
- }
- }
-
- $this->populateOverriddenMethods($storage);
-
- $this->progress->debug('Have populated ' . $storage->name . "\n");
-
- $storage->populated = true;
-
- if (isset($this->invalid_class_storages[$fq_classlike_name_lc])) {
- foreach ($this->invalid_class_storages[$fq_classlike_name_lc] as $dependency) {
- $dependency->populated = false;
- $this->populateClassLikeStorage($dependency, $dependent_classlikes);
- }
-
- unset($this->invalid_class_storages[$fq_classlike_name_lc]);
- }
- }
-
- private function populateOverriddenMethods(
- ClassLikeStorage $storage
- ): void {
- $storage->documenting_method_ids = [];
-
- foreach ($storage->methods as $method_name => $method_storage) {
- if (isset($storage->overridden_method_ids[$method_name])) {
- $overridden_method_ids = $storage->overridden_method_ids[$method_name];
-
- $candidate_overridden_ids = null;
-
- $declaring_class_storages = [];
-
- foreach ($overridden_method_ids as $declaring_method_id) {
- $declaring_class = $declaring_method_id->fq_class_name;
- $declaring_class_storage
- = $declaring_class_storages[$declaring_class]
- = $this->classlike_storage_provider->get($declaring_class);
-
- if ($candidate_overridden_ids === null) {
- $candidate_overridden_ids
- = ($declaring_class_storage->overridden_method_ids[$method_name] ?? [])
- + [$declaring_method_id->fq_class_name => $declaring_method_id];
- } else {
- $candidate_overridden_ids = array_intersect_key(
- $candidate_overridden_ids,
- ($declaring_class_storage->overridden_method_ids[$method_name] ?? [])
- + [$declaring_method_id->fq_class_name => $declaring_method_id]
- );
- }
- }
-
- foreach ($overridden_method_ids as $declaring_method_id) {
- $declaring_class = $declaring_method_id->fq_class_name;
- $declaring_method_name = $declaring_method_id->method_name;
- $declaring_class_storage = $declaring_class_storages[$declaring_class];
-
- $declaring_method_storage = $declaring_class_storage->methods[$declaring_method_name];
-
- if (($declaring_method_storage->has_docblock_param_types
- || $declaring_method_storage->has_docblock_return_type)
- && !$method_storage->has_docblock_param_types
- && !$method_storage->has_docblock_return_type
- && $method_storage->inherited_return_type !== null
- ) {
- if (!isset($storage->documenting_method_ids[$method_name])
- || (string) $storage->documenting_method_ids[$method_name]
- === (string) $declaring_method_id
- ) {
- $storage->documenting_method_ids[$method_name] = $declaring_method_id;
- $method_storage->inherited_return_type = true;
- } else {
- if (in_array(
- $storage->documenting_method_ids[$method_name]->fq_class_name,
- $declaring_class_storage->parent_interfaces
- )) {
- $storage->documenting_method_ids[$method_name] = $declaring_method_id;
- $method_storage->inherited_return_type = true;
- } else {
- $documenting_class_storage = $declaring_class_storages
- [$storage->documenting_method_ids[$method_name]->fq_class_name];
-
- if (!in_array(
- $declaring_class,
- $documenting_class_storage->parent_interfaces
- ) && $documenting_class_storage->is_interface
- ) {
- unset($storage->documenting_method_ids[$method_name]);
- $method_storage->inherited_return_type = null;
- }
- }
- }
- }
-
- // tell the declaring class it's overridden downstream
- $declaring_method_storage->overridden_downstream = true;
- $declaring_method_storage->overridden_somewhere = true;
-
- if ($declaring_method_storage->mutation_free_inferred) {
- $declaring_method_storage->mutation_free = false;
- $declaring_method_storage->external_mutation_free = false;
- $declaring_method_storage->mutation_free_inferred = false;
- }
-
- if ($declaring_method_storage->throws
- && (!$method_storage->throws || $method_storage->inheritdoc)
- ) {
- $method_storage->throws += $declaring_method_storage->throws;
- }
- }
- }
- }
- }
-
- private function populateDataFromTraits(
- ClassLikeStorage $storage,
- ClassLikeStorageProvider $storage_provider,
- array $dependent_classlikes
- ): void {
- foreach ($storage->used_traits as $used_trait_lc => $_) {
- try {
- $used_trait_lc = strtolower(
- $this->classlikes->getUnAliasedName(
- $used_trait_lc
- )
- );
- $trait_storage = $storage_provider->get($used_trait_lc);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $this->populateClassLikeStorage($trait_storage, $dependent_classlikes);
-
- $this->inheritMethodsFromParent($storage, $trait_storage);
- $this->inheritPropertiesFromParent($storage, $trait_storage);
-
- if ($trait_storage->template_types) {
- $storage->template_extended_params[$trait_storage->name] = [];
-
- if (isset($storage->template_extended_offsets[$trait_storage->name])) {
- foreach ($storage->template_extended_offsets[$trait_storage->name] as $i => $type) {
- $trait_template_type_names = array_keys($trait_storage->template_types);
-
- $mapped_name = $trait_template_type_names[$i] ?? null;
-
- if ($mapped_name) {
- $storage->template_extended_params[$trait_storage->name][$mapped_name] = $type;
- }
- }
-
- if ($trait_storage->template_extended_params) {
- foreach ($trait_storage->template_extended_params as $t_storage_class => $type_map) {
- foreach ($type_map as $i => $type) {
- $storage->template_extended_params[$t_storage_class][$i] = self::extendType(
- $type,
- $storage
- );
- }
- }
- }
- } else {
- foreach ($trait_storage->template_types as $template_name => $template_type_map) {
- foreach ($template_type_map as $template_type) {
- $default_param = clone $template_type;
- $default_param->from_docblock = false;
- $storage->template_extended_params[$trait_storage->name][$template_name]
- = $default_param;
- }
- }
- }
- } elseif ($trait_storage->template_extended_params) {
- $storage->template_extended_params = array_merge(
- $storage->template_extended_params ?: [],
- $trait_storage->template_extended_params
- );
- }
-
- $storage->pseudo_property_get_types += $trait_storage->pseudo_property_get_types;
- $storage->pseudo_property_set_types += $trait_storage->pseudo_property_set_types;
-
- $storage->pseudo_methods += $trait_storage->pseudo_methods;
- $storage->declaring_pseudo_method_ids += $trait_storage->declaring_pseudo_method_ids;
- }
- }
-
- private static function extendType(
- Union $type,
- ClassLikeStorage $storage
- ): Union {
- $extended_types = [];
-
- foreach ($type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TTemplateParam) {
- $referenced_type
- = $storage->template_extended_params[$atomic_type->defining_class][$atomic_type->param_name]
- ?? null;
-
- if ($referenced_type) {
- foreach ($referenced_type->getAtomicTypes() as $atomic_referenced_type) {
- if (!$atomic_referenced_type instanceof TTemplateParam) {
- $extended_types[] = $atomic_referenced_type;
- } else {
- $extended_types[] = $atomic_type;
- }
- }
- } else {
- $extended_types[] = $atomic_type;
- }
- } else {
- $extended_types[] = $atomic_type;
- }
- }
-
- return new Union($extended_types);
- }
-
- private function populateDataFromParentClass(
- ClassLikeStorage $storage,
- ClassLikeStorageProvider $storage_provider,
- array $dependent_classlikes
- ): void {
- $parent_storage_class = reset($storage->parent_classes);
-
- $parent_storage_class = strtolower(
- $this->classlikes->getUnAliasedName(
- $parent_storage_class
- )
- );
-
- try {
- $parent_storage = $storage_provider->get($parent_storage_class);
- } catch (InvalidArgumentException $e) {
- $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n");
-
- $storage->invalid_dependencies[] = $parent_storage_class;
-
- $this->invalid_class_storages[$parent_storage_class][] = $storage;
-
- return;
- }
-
- $this->populateClassLikeStorage($parent_storage, $dependent_classlikes);
-
- $storage->parent_classes = array_merge($storage->parent_classes, $parent_storage->parent_classes);
-
- if ($parent_storage->template_types) {
- $storage->template_extended_params[$parent_storage->name] = [];
-
- if (isset($storage->template_extended_offsets[$parent_storage->name])) {
- foreach ($storage->template_extended_offsets[$parent_storage->name] as $i => $type) {
- $parent_template_type_names = array_keys($parent_storage->template_types);
-
- $mapped_name = $parent_template_type_names[$i] ?? null;
-
- if ($mapped_name) {
- $storage->template_extended_params[$parent_storage->name][$mapped_name] = $type;
- }
- }
-
- if ($parent_storage->template_extended_params) {
- foreach ($parent_storage->template_extended_params as $t_storage_class => $type_map) {
- foreach ($type_map as $i => $type) {
- $storage->template_extended_params[$t_storage_class][$i] = self::extendType(
- $type,
- $storage
- );
- }
- }
- }
- } else {
- foreach ($parent_storage->template_types as $template_name => $template_type_map) {
- foreach ($template_type_map as $template_type) {
- $default_param = clone $template_type;
- $default_param->from_docblock = false;
- $storage->template_extended_params[$parent_storage->name][$template_name]
- = $default_param;
- }
- }
-
- if ($parent_storage->template_extended_params) {
- $storage->template_extended_params = array_merge(
- $storage->template_extended_params,
- $parent_storage->template_extended_params
- );
- }
- }
- } elseif ($parent_storage->template_extended_params) {
- $storage->template_extended_params = array_merge(
- $storage->template_extended_params ?: [],
- $parent_storage->template_extended_params
- );
- }
-
- $this->inheritMethodsFromParent($storage, $parent_storage);
- $this->inheritPropertiesFromParent($storage, $parent_storage);
-
- $storage->class_implements = array_merge($storage->class_implements, $parent_storage->class_implements);
- $storage->invalid_dependencies = array_merge(
- $storage->invalid_dependencies,
- $parent_storage->invalid_dependencies
- );
-
- if ($parent_storage->has_visitor_issues) {
- $storage->has_visitor_issues = true;
- }
-
- $storage->constants = array_merge(
- array_filter(
- $parent_storage->constants,
- function ($constant) {
- return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
- || $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED;
- }
- ),
- $storage->constants
- );
-
- if ($parent_storage->preserve_constructor_signature) {
- $storage->preserve_constructor_signature = true;
- }
-
- if (($parent_storage->namedMixins || $parent_storage->templatedMixins)
- && (!$storage->namedMixins || !$storage->templatedMixins)) {
- $storage->mixin_declaring_fqcln = $parent_storage->mixin_declaring_fqcln;
-
- if (!$storage->namedMixins) {
- $storage->namedMixins = $parent_storage->namedMixins;
- }
-
- if (!$storage->templatedMixins) {
- $storage->templatedMixins = $parent_storage->templatedMixins;
- }
- }
-
- $storage->pseudo_property_get_types += $parent_storage->pseudo_property_get_types;
- $storage->pseudo_property_set_types += $parent_storage->pseudo_property_set_types;
-
- $parent_storage->dependent_classlikes[strtolower($storage->name)] = true;
-
- $storage->pseudo_methods += $parent_storage->pseudo_methods;
- $storage->declaring_pseudo_method_ids += $parent_storage->declaring_pseudo_method_ids;
- }
-
- private function populateInterfaceDataFromParentInterfaces(
- ClassLikeStorage $storage,
- ClassLikeStorageProvider $storage_provider,
- array $dependent_classlikes
- ): void {
- $parent_interfaces = [];
-
- foreach ($storage->direct_interface_parents as $parent_interface_lc => $_) {
- try {
- $parent_interface_lc = strtolower(
- $this->classlikes->getUnAliasedName(
- $parent_interface_lc
- )
- );
- $parent_interface_storage = $storage_provider->get($parent_interface_lc);
- } catch (InvalidArgumentException $e) {
- $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n");
-
- $storage->invalid_dependencies[] = $parent_interface_lc;
- continue;
- }
-
- $this->populateClassLikeStorage($parent_interface_storage, $dependent_classlikes);
-
- // copy over any constants
- $storage->constants = array_merge(
- array_filter(
- $parent_interface_storage->constants,
- function ($constant) {
- return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- }
- ),
- $storage->constants
- );
-
- $storage->invalid_dependencies = array_merge(
- $storage->invalid_dependencies,
- $parent_interface_storage->invalid_dependencies
- );
-
- if ($parent_interface_storage->template_types) {
- $storage->template_extended_params[$parent_interface_storage->name] = [];
-
- if (isset($storage->template_extended_offsets[$parent_interface_storage->name])) {
- foreach ($storage->template_extended_offsets[$parent_interface_storage->name] as $i => $type) {
- $parent_template_type_names = array_keys($parent_interface_storage->template_types);
-
- $mapped_name = $parent_template_type_names[$i] ?? null;
-
- if ($mapped_name) {
- $storage->template_extended_params[$parent_interface_storage->name][$mapped_name] = $type;
- }
- }
-
- if ($parent_interface_storage->template_extended_params) {
- foreach ($parent_interface_storage->template_extended_params as $t_storage_class => $type_map) {
- foreach ($type_map as $i => $type) {
- $storage->template_extended_params[$t_storage_class][$i] = self::extendType(
- $type,
- $storage
- );
- }
- }
- }
- } else {
- foreach ($parent_interface_storage->template_types as $template_name => $template_type_map) {
- foreach ($template_type_map as $template_type) {
- $default_param = clone $template_type;
- $default_param->from_docblock = false;
- $storage->template_extended_params[$parent_interface_storage->name][$template_name]
- = $default_param;
- }
- }
- }
- } elseif ($parent_interface_storage->template_extended_params) {
- $storage->template_extended_params = array_merge(
- $storage->template_extended_params ?: [],
- $parent_interface_storage->template_extended_params
- );
- }
-
- $parent_interfaces = array_merge($parent_interfaces, $parent_interface_storage->parent_interfaces);
-
- $this->inheritMethodsFromParent($storage, $parent_interface_storage);
-
- $storage->pseudo_methods += $parent_interface_storage->pseudo_methods;
- $storage->declaring_pseudo_method_ids += $parent_interface_storage->declaring_pseudo_method_ids;
- }
-
- $storage->parent_interfaces = array_merge($parent_interfaces, $storage->parent_interfaces);
-
- foreach ($storage->parent_interfaces as $parent_interface_lc => $_) {
- try {
- $parent_interface_lc = strtolower(
- $this->classlikes->getUnAliasedName(
- $parent_interface_lc
- )
- );
- $parent_interface_storage = $storage_provider->get($parent_interface_lc);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $parent_interface_storage->dependent_classlikes[strtolower($storage->name)] = true;
- }
- }
-
- private function populateDataFromImplementedInterfaces(
- ClassLikeStorage $storage,
- ClassLikeStorageProvider $storage_provider,
- array $dependent_classlikes
- ): void {
- $extra_interfaces = [];
-
- foreach ($storage->direct_class_interfaces as $implemented_interface_lc => $_) {
- try {
- $implemented_interface_lc = strtolower(
- $this->classlikes->getUnAliasedName(
- $implemented_interface_lc
- )
- );
- $implemented_interface_storage = $storage_provider->get($implemented_interface_lc);
- } catch (InvalidArgumentException $e) {
- $this->progress->debug('Populator could not find dependency (' . __LINE__ . ")\n");
-
- $storage->invalid_dependencies[] = $implemented_interface_lc;
- continue;
- }
-
- $this->populateClassLikeStorage($implemented_interface_storage, $dependent_classlikes);
-
- // copy over any constants
- $storage->constants = array_merge(
- array_filter(
- $implemented_interface_storage->constants,
- function ($constant) {
- return $constant->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- }
- ),
- $storage->constants
- );
-
- $storage->invalid_dependencies = array_merge(
- $storage->invalid_dependencies,
- $implemented_interface_storage->invalid_dependencies
- );
-
- if ($implemented_interface_storage->template_types) {
- $storage->template_extended_params[$implemented_interface_storage->name] = [];
-
- if (isset($storage->template_extended_offsets[$implemented_interface_storage->name])) {
- foreach ($storage->template_extended_offsets[$implemented_interface_storage->name] as $i => $type) {
- $parent_template_type_names = array_keys($implemented_interface_storage->template_types);
-
- $mapped_name = $parent_template_type_names[$i] ?? null;
-
- if ($mapped_name) {
- $storage->template_extended_params[$implemented_interface_storage->name][$mapped_name]
- = $type;
- }
- }
-
- if ($implemented_interface_storage->template_extended_params) {
- foreach ($implemented_interface_storage->template_extended_params as $e_i => $type_map) {
- foreach ($type_map as $i => $type) {
- $storage->template_extended_params[$e_i][$i] = self::extendType(
- $type,
- $storage
- );
- }
- }
- }
- } else {
- foreach ($implemented_interface_storage->template_types as $template_name => $template_type_map) {
- foreach ($template_type_map as $template_type) {
- $default_param = clone $template_type;
- $default_param->from_docblock = false;
- $storage->template_extended_params[$implemented_interface_storage->name][$template_name]
- = $default_param;
- }
- }
- }
- } elseif ($implemented_interface_storage->template_extended_params) {
- $storage->template_extended_params = array_merge(
- $storage->template_extended_params ?: [],
- $implemented_interface_storage->template_extended_params
- );
- }
-
- $extra_interfaces = array_merge($extra_interfaces, $implemented_interface_storage->parent_interfaces);
- }
-
- $storage->class_implements = array_merge($storage->class_implements, $extra_interfaces);
-
- $interface_method_implementers = [];
-
- foreach ($storage->class_implements as $implemented_interface_lc => $_) {
- try {
- $implemented_interface = strtolower(
- $this->classlikes->getUnAliasedName(
- $implemented_interface_lc
- )
- );
- $implemented_interface_storage = $storage_provider->get($implemented_interface);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $implemented_interface_storage->dependent_classlikes[strtolower($storage->name)] = true;
-
- foreach ($implemented_interface_storage->methods as $method_name => $method) {
- if ($method->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC) {
- $interface_method_implementers[$method_name][] = new MethodIdentifier(
- $implemented_interface_storage->name,
- $method_name
- );
- }
- }
- }
-
- foreach ($interface_method_implementers as $method_name => $interface_method_ids) {
- if (count($interface_method_ids) === 1) {
- if (isset($storage->methods[$method_name])) {
- $method_storage = $storage->methods[$method_name];
-
- if ($method_storage->signature_return_type
- && !$method_storage->signature_return_type->isVoid()
- && $method_storage->return_type === $method_storage->signature_return_type
- ) {
- $interface_fqcln = $interface_method_ids[0]->fq_class_name;
- $interface_storage = $storage_provider->get($interface_fqcln);
-
- if (isset($interface_storage->methods[$method_name])) {
- $interface_method_storage = $interface_storage->methods[$method_name];
-
- if ($interface_method_storage->throws
- && (!$method_storage->throws || $method_storage->inheritdoc)
- ) {
- $method_storage->throws += $interface_method_storage->throws;
- }
- }
- }
- }
- }
-
- foreach ($interface_method_ids as $interface_method_id) {
- $storage->overridden_method_ids[$method_name][$interface_method_id->fq_class_name]
- = $interface_method_id;
- }
- }
- }
-
- /**
- * @param array<string, bool> $dependent_file_paths
- */
- private function populateFileStorage(FileStorage $storage, array $dependent_file_paths = []): void
- {
- if ($storage->populated) {
- return;
- }
-
- $file_path_lc = strtolower($storage->file_path);
-
- if (isset($dependent_file_paths[$file_path_lc])) {
- return;
- }
-
- $dependent_file_paths[$file_path_lc] = true;
-
- $all_required_file_paths = $storage->required_file_paths;
-
- foreach ($storage->required_file_paths as $included_file_path => $_) {
- try {
- $included_file_storage = $this->file_storage_provider->get($included_file_path);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $this->populateFileStorage($included_file_storage, $dependent_file_paths);
-
- $all_required_file_paths = $all_required_file_paths + $included_file_storage->required_file_paths;
- }
-
- foreach ($all_required_file_paths as $included_file_path => $_) {
- try {
- $included_file_storage = $this->file_storage_provider->get($included_file_path);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $storage->declaring_function_ids = array_merge(
- $included_file_storage->declaring_function_ids,
- $storage->declaring_function_ids
- );
-
- $storage->declaring_constants = array_merge(
- $included_file_storage->declaring_constants,
- $storage->declaring_constants
- );
- }
-
- foreach ($storage->referenced_classlikes as $fq_class_name) {
- try {
- $classlike_storage = $this->classlike_storage_provider->get($fq_class_name);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if (!$classlike_storage->location) {
- continue;
- }
-
- try {
- $included_file_storage = $this->file_storage_provider->get($classlike_storage->location->file_path);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- foreach ($classlike_storage->used_traits as $used_trait) {
- try {
- $trait_storage = $this->classlike_storage_provider->get($used_trait);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if (!$trait_storage->location) {
- continue;
- }
-
- try {
- $included_trait_file_storage = $this->file_storage_provider->get(
- $trait_storage->location->file_path
- );
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $storage->declaring_function_ids = array_merge(
- $included_trait_file_storage->declaring_function_ids,
- $storage->declaring_function_ids
- );
- }
-
- $storage->declaring_function_ids = array_merge(
- $included_file_storage->declaring_function_ids,
- $storage->declaring_function_ids
- );
- }
-
- $storage->required_file_paths = $all_required_file_paths;
-
- foreach ($all_required_file_paths as $required_file_path) {
- try {
- $required_file_storage = $this->file_storage_provider->get($required_file_path);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $required_file_storage->required_by_file_paths += [$file_path_lc => $storage->file_path];
- }
-
- foreach ($storage->required_classes as $required_classlike) {
- try {
- $classlike_storage = $this->classlike_storage_provider->get($required_classlike);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- if (!$classlike_storage->location) {
- continue;
- }
-
- try {
- $required_file_storage = $this->file_storage_provider->get($classlike_storage->location->file_path);
- } catch (InvalidArgumentException $e) {
- continue;
- }
-
- $required_file_storage->required_by_file_paths += [$file_path_lc => $storage->file_path];
- }
-
- $storage->populated = true;
- }
-
- private function convertPhpStormGenericToPsalmGeneric(Union $candidate, bool $is_property = false): void
- {
- if (!$candidate->from_docblock) {
- //never convert a type that comes from a signature
- return;
- }
-
- $atomic_types = $candidate->getAtomicTypes();
-
- if (isset($atomic_types['array']) && count($atomic_types) > 1 && !isset($atomic_types['null'])) {
- $iterator_name = null;
- $generic_params = null;
- $iterator_key = null;
-
- try {
- foreach ($atomic_types as $type_key => $type) {
- if ($type instanceof TIterable
- || ($type instanceof TNamedObject
- && (!$type->from_docblock || $is_property)
- && (
- strtolower($type->value) === 'traversable'
- || $this->classlikes->interfaceExtends(
- $type->value,
- 'Traversable'
- )
- || $this->classlikes->classImplements(
- $type->value,
- 'Traversable'
- )
- ))
- ) {
- $iterator_name = $type->value;
- $iterator_key = $type_key;
- } elseif ($type instanceof TArray) {
- $generic_params = $type->type_params;
- }
- }
- } catch (InvalidArgumentException $e) {
- // ignore class-not-found issues
- }
-
- if ($iterator_name && $iterator_key && $generic_params) {
- if ($iterator_name === 'iterable') {
- $generic_iterator = new TIterable($generic_params);
- } else {
- if (strtolower($iterator_name) === 'generator') {
- $generic_params[] = Type::getMixed();
- $generic_params[] = Type::getMixed();
- }
- $generic_iterator = new TGenericObject($iterator_name, $generic_params);
- }
-
- $candidate->removeType('array');
- $candidate->removeType($iterator_key);
- $candidate->addType($generic_iterator);
- }
- }
- }
-
- protected function inheritMethodsFromParent(
- ClassLikeStorage $storage,
- ClassLikeStorage $parent_storage
- ): void {
- $fq_class_name = $storage->name;
- $fq_class_name_lc = strtolower($fq_class_name);
-
- if ($parent_storage->sealed_methods) {
- $storage->sealed_methods = true;
- }
-
- // register where they appear (can never be in a trait)
- foreach ($parent_storage->appearing_method_ids as $method_name_lc => $appearing_method_id) {
- $aliased_method_names = [$method_name_lc];
-
- if ($parent_storage->is_trait
- && $storage->trait_alias_map
- ) {
- $aliased_method_names = array_merge(
- $aliased_method_names,
- array_keys($storage->trait_alias_map, $method_name_lc, true)
- );
- }
-
- foreach ($aliased_method_names as $aliased_method_name) {
- if (isset($storage->appearing_method_ids[$aliased_method_name])) {
- continue;
- }
-
- $implemented_method_id = new MethodIdentifier(
- $fq_class_name,
- $aliased_method_name
- );
-
- $storage->appearing_method_ids[$aliased_method_name] =
- $parent_storage->is_trait ? $implemented_method_id : $appearing_method_id;
-
- $this_method_id = $fq_class_name_lc . '::' . $method_name_lc;
-
- if (isset($storage->methods[$aliased_method_name])) {
- $storage->potential_declaring_method_ids[$aliased_method_name] = [$this_method_id => true];
- } else {
- if (isset($parent_storage->potential_declaring_method_ids[$aliased_method_name])) {
- $storage->potential_declaring_method_ids[$aliased_method_name]
- = $parent_storage->potential_declaring_method_ids[$aliased_method_name];
- }
-
- $storage->potential_declaring_method_ids[$aliased_method_name][$this_method_id] = true;
-
- $parent_method_id = strtolower($parent_storage->name) . '::' . $method_name_lc;
- $storage->potential_declaring_method_ids[$aliased_method_name][$parent_method_id] = true;
- }
- }
- }
-
- // register where they're declared
- foreach ($parent_storage->inheritable_method_ids as $method_name_lc => $declaring_method_id) {
- if ($method_name_lc !== '__construct'
- || $parent_storage->preserve_constructor_signature
- ) {
- if ($parent_storage->is_trait) {
- $declaring_class = $declaring_method_id->fq_class_name;
- $declaring_class_storage = $this->classlike_storage_provider->get($declaring_class);
-
- if (isset($declaring_class_storage->methods[$method_name_lc])
- && $declaring_class_storage->methods[$method_name_lc]->abstract
- ) {
- $storage->overridden_method_ids[$method_name_lc][$declaring_method_id->fq_class_name]
- = $declaring_method_id;
- }
- } else {
- $storage->overridden_method_ids[$method_name_lc][$declaring_method_id->fq_class_name]
- = $declaring_method_id;
- }
-
- if (isset($parent_storage->overridden_method_ids[$method_name_lc])
- && isset($storage->overridden_method_ids[$method_name_lc])
- ) {
- $storage->overridden_method_ids[$method_name_lc]
- += $parent_storage->overridden_method_ids[$method_name_lc];
- }
- }
-
- $aliased_method_names = [$method_name_lc];
-
- if ($parent_storage->is_trait
- && $storage->trait_alias_map
- ) {
- $aliased_method_names = array_merge(
- $aliased_method_names,
- array_keys($storage->trait_alias_map, $method_name_lc, true)
- );
- }
-
- foreach ($aliased_method_names as $aliased_method_name) {
- if (isset($storage->declaring_method_ids[$aliased_method_name])) {
- $implementing_method_id = $storage->declaring_method_ids[$aliased_method_name];
-
- $implementing_class_storage = $this->classlike_storage_provider->get(
- $implementing_method_id->fq_class_name
- );
-
- if (!$implementing_class_storage->methods[$implementing_method_id->method_name]->abstract
- || !empty($storage->methods[$implementing_method_id->method_name]->abstract)
- ) {
- continue;
- }
- }
-
- $storage->declaring_method_ids[$aliased_method_name] = $declaring_method_id;
- $storage->inheritable_method_ids[$aliased_method_name] = $declaring_method_id;
- }
- }
- }
-
- private function inheritPropertiesFromParent(
- ClassLikeStorage $storage,
- ClassLikeStorage $parent_storage
- ): void {
- if ($parent_storage->sealed_properties) {
- $storage->sealed_properties = true;
- }
-
- // register where they appear (can never be in a trait)
- foreach ($parent_storage->appearing_property_ids as $property_name => $appearing_property_id) {
- if (isset($storage->appearing_property_ids[$property_name])) {
- continue;
- }
-
- if (!$parent_storage->is_trait
- && isset($parent_storage->properties[$property_name])
- && $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- continue;
- }
-
- $implemented_property_id = $storage->name . '::$' . $property_name;
-
- $storage->appearing_property_ids[$property_name] =
- $parent_storage->is_trait ? $implemented_property_id : $appearing_property_id;
- }
-
- // register where they're declared
- foreach ($parent_storage->declaring_property_ids as $property_name => $declaring_property_class) {
- if (isset($storage->declaring_property_ids[$property_name])) {
- continue;
- }
-
- if (!$parent_storage->is_trait
- && isset($parent_storage->properties[$property_name])
- && $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- continue;
- }
-
- $storage->declaring_property_ids[$property_name] = $declaring_property_class;
- }
-
- // register where they're declared
- foreach ($parent_storage->inheritable_property_ids as $property_name => $inheritable_property_id) {
- if (!$parent_storage->is_trait
- && isset($parent_storage->properties[$property_name])
- && $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- continue;
- }
-
- if (!$parent_storage->is_trait) {
- $storage->overridden_property_ids[$property_name][] = $inheritable_property_id;
- }
-
- $storage->inheritable_property_ids[$property_name] = $inheritable_property_id;
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Properties.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Properties.php
deleted file mode 100644
index 2a0137a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Properties.php
+++ /dev/null
@@ -1,356 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\PropertyExistenceProvider;
-use Psalm\Internal\Provider\PropertyTypeProvider;
-use Psalm\Internal\Provider\PropertyVisibilityProvider;
-use Psalm\StatementsSource;
-use Psalm\Storage\PropertyStorage;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function explode;
-use function preg_replace;
-use function strtolower;
-
-/**
- * @internal
- *
- * Handles information about class properties
- */
-class Properties
-{
- /**
- * @var ClassLikeStorageProvider
- */
- private $classlike_storage_provider;
-
- /** @var ClassLikes */
- private $classlikes;
-
- /**
- * @var bool
- */
- public $collect_locations = false;
-
- /**
- * @var FileReferenceProvider
- */
- public $file_reference_provider;
-
- /**
- * @var PropertyExistenceProvider
- */
- public $property_existence_provider;
-
- /**
- * @var PropertyTypeProvider
- */
- public $property_type_provider;
-
- /**
- * @var PropertyVisibilityProvider
- */
- public $property_visibility_provider;
-
-
- public function __construct(
- ClassLikeStorageProvider $storage_provider,
- FileReferenceProvider $file_reference_provider,
- ClassLikes $classlikes
- ) {
- $this->classlike_storage_provider = $storage_provider;
- $this->file_reference_provider = $file_reference_provider;
- $this->property_existence_provider = new PropertyExistenceProvider();
- $this->property_visibility_provider = new PropertyVisibilityProvider();
- $this->property_type_provider = new PropertyTypeProvider();
- $this->classlikes = $classlikes;
- }
-
- /**
- * Whether or not a given property exists
- *
- */
- public function propertyExists(
- string $property_id,
- bool $read_mode,
- ?StatementsSource $source = null,
- ?Context $context = null,
- ?CodeLocation $code_location = null
- ): bool {
- // remove trailing backslash if it exists
- $property_id = preg_replace('/^\\\\/', '', $property_id);
-
- [$fq_class_name, $property_name] = explode('::$', $property_id);
- $fq_class_name_lc = strtolower($fq_class_name);
-
- if ($this->property_existence_provider->has($fq_class_name)) {
- $property_exists = $this->property_existence_provider->doesPropertyExist(
- $fq_class_name,
- $property_name,
- $read_mode,
- $source,
- $context,
- $code_location
- );
-
- if ($property_exists !== null) {
- return $property_exists;
- }
- }
-
- $class_storage = $this->classlikes->getStorageFor($fq_class_name);
-
- if (!$class_storage) {
- return false;
- }
-
- if ($source
- && $context
- && $context->self !== $fq_class_name
- && !$context->collect_initializations
- && !$context->collect_mutations
- ) {
- if ($context->calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClass(
- $context->calling_method_id,
- $fq_class_name_lc
- );
- } else {
- $this->file_reference_provider->addNonMethodReferenceToClass(
- $source->getFilePath(),
- $fq_class_name_lc
- );
- }
- }
-
- if (isset($class_storage->declaring_property_ids[$property_name])) {
- $declaring_property_class = strtolower($class_storage->declaring_property_ids[$property_name]);
-
- if ($context && $context->calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToClassMember(
- $context->calling_method_id,
- $declaring_property_class . '::$' . $property_name,
- false
- );
-
- if ($read_mode) {
- $this->file_reference_provider->addMethodReferenceToClassProperty(
- $context->calling_method_id,
- $declaring_property_class . '::$' . $property_name
- );
- }
- } elseif ($source) {
- $this->file_reference_provider->addFileReferenceToClassMember(
- $source->getFilePath(),
- $declaring_property_class . '::$' . $property_name,
- false
- );
-
- if ($read_mode) {
- $this->file_reference_provider->addFileReferenceToClassProperty(
- $source->getFilePath(),
- $declaring_property_class . '::$' . $property_name
- );
- }
- }
-
- if ($this->collect_locations && $code_location) {
- $this->file_reference_provider->addCallingLocationForClassProperty(
- $code_location,
- $declaring_property_class . '::$' . $property_name
- );
- }
-
- return true;
- }
-
- if ($context && $context->calling_method_id) {
- $this->file_reference_provider->addMethodReferenceToMissingClassMember(
- $context->calling_method_id,
- $fq_class_name_lc . '::$' . $property_name
- );
- } elseif ($source) {
- $this->file_reference_provider->addFileReferenceToMissingClassMember(
- $source->getFilePath(),
- $fq_class_name_lc . '::$' . $property_name
- );
- }
-
- return false;
- }
-
- public function getDeclaringClassForProperty(
- string $property_id,
- bool $read_mode,
- ?StatementsSource $source = null
- ): ?string {
- [$fq_class_name, $property_name] = explode('::$', $property_id);
-
- if ($this->property_existence_provider->has($fq_class_name)) {
- if ($this->property_existence_provider->doesPropertyExist(
- $fq_class_name,
- $property_name,
- $read_mode,
- $source,
- null
- )) {
- return $fq_class_name;
- }
- }
-
- $class_storage = $this->classlikes->getStorageFor($fq_class_name);
-
- if ($class_storage && isset($class_storage->declaring_property_ids[$property_name])) {
- return $class_storage->declaring_property_ids[$property_name];
- }
-
- return null;
- }
-
- /**
- * Get the class this property appears in (vs is declared in, which could give a trait)
- */
- public function getAppearingClassForProperty(
- string $property_id,
- bool $read_mode,
- ?StatementsSource $source = null
- ): ?string {
- [$fq_class_name, $property_name] = explode('::$', $property_id);
-
- if ($this->property_existence_provider->has($fq_class_name)) {
- if ($this->property_existence_provider->doesPropertyExist(
- $fq_class_name,
- $property_name,
- $read_mode,
- $source,
- null
- )) {
- return $fq_class_name;
- }
- }
-
- $class_storage = $this->classlikes->getStorageFor($fq_class_name);
-
- if ($class_storage && isset($class_storage->appearing_property_ids[$property_name])) {
- $appearing_property_id = $class_storage->appearing_property_ids[$property_name];
-
- return explode('::$', $appearing_property_id)[0];
- }
-
- return null;
- }
-
- public function getStorage(string $property_id): PropertyStorage
- {
- // remove trailing backslash if it exists
- $property_id = preg_replace('/^\\\\/', '', $property_id);
-
- [$fq_class_name, $property_name] = explode('::$', $property_id);
-
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- if (isset($class_storage->declaring_property_ids[$property_name])) {
- $declaring_property_class = $class_storage->declaring_property_ids[$property_name];
- $declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class);
-
- if (isset($declaring_class_storage->properties[$property_name])) {
- return $declaring_class_storage->properties[$property_name];
- }
- }
-
- throw new UnexpectedValueException('Property ' . $property_id . ' should exist');
- }
-
- public function hasStorage(string $property_id): bool
- {
- // remove trailing backslash if it exists
- $property_id = preg_replace('/^\\\\/', '', $property_id);
-
- [$fq_class_name, $property_name] = explode('::$', $property_id);
-
- $class_storage = $this->classlike_storage_provider->get($fq_class_name);
-
- if (isset($class_storage->declaring_property_ids[$property_name])) {
- $declaring_property_class = $class_storage->declaring_property_ids[$property_name];
- $declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class);
-
- return isset($declaring_class_storage->properties[$property_name]);
- }
- return false;
- }
-
- public function getPropertyType(
- string $property_id,
- bool $property_set,
- ?StatementsSource $source = null,
- ?Context $context = null
- ): ?Union {
- // remove trailing backslash if it exists
- $property_id = preg_replace('/^\\\\/', '', $property_id);
-
- [$fq_class_name, $property_name] = explode('::$', $property_id);
-
- if ($this->property_type_provider->has($fq_class_name)) {
- $property_type = $this->property_type_provider->getPropertyType(
- $fq_class_name,
- $property_name,
- !$property_set,
- $source,
- $context
- );
-
- if ($property_type !== null) {
- return $property_type;
- }
- }
-
- $class_storage = $this->classlikes->getStorageFor($fq_class_name);
-
- if ($class_storage && isset($class_storage->declaring_property_ids[$property_name])) {
- $declaring_property_class = $class_storage->declaring_property_ids[$property_name];
- $declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class);
-
- if (isset($declaring_class_storage->properties[$property_name])) {
- $storage = $declaring_class_storage->properties[$property_name];
- } else {
- throw new UnexpectedValueException('Property ' . $property_id . ' should exist');
- }
- } else {
- throw new UnexpectedValueException('Property ' . $property_id . ' should exist');
- }
-
- if ($storage->type) {
- if ($property_set) {
- if (isset($class_storage->pseudo_property_set_types[$property_name])) {
- return $class_storage->pseudo_property_set_types[$property_name];
- }
- } else {
- if (isset($class_storage->pseudo_property_get_types[$property_name])) {
- return $class_storage->pseudo_property_get_types[$property_name];
- }
- }
-
- return $storage->type;
- }
-
- if (!isset($class_storage->overridden_property_ids[$property_name])) {
- return null;
- }
-
- foreach ($class_storage->overridden_property_ids[$property_name] as $overridden_property_id) {
- $overridden_storage = $this->getStorage($overridden_property_id);
-
- if ($overridden_storage->type) {
- return $overridden_storage->type;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/PropertyMap.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/PropertyMap.php
deleted file mode 100644
index 2b0029a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/PropertyMap.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use function dirname;
-use function strtolower;
-
-/**
- * @internal
- */
-class PropertyMap
-{
- /**
- * @var array<lowercase-string, array<string, string>>|null
- */
- private static $property_map;
-
- /**
- * Gets the method/function call map
- *
- * @return array<lowercase-string, array<string, string>>
- */
- public static function getPropertyMap(): array
- {
- if (self::$property_map !== null) {
- return self::$property_map;
- }
-
- /** @var array<string, array<string, string>> */
- $property_map = require(dirname(__DIR__, 4) . '/dictionaries/PropertyMap.php');
-
- self::$property_map = [];
-
- foreach ($property_map as $key => $value) {
- $cased_key = strtolower($key);
- self::$property_map[$cased_key] = $value;
- }
-
- return self::$property_map;
- }
-
- public static function inPropertyMap(string $class_name): bool
- {
- return isset(self::getPropertyMap()[strtolower($class_name)]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php
deleted file mode 100644
index 80a6ad2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ReferenceMapGenerator.php
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-
-class ReferenceMapGenerator
-{
- /**
- * @return array<string, string>
- */
- public static function getReferenceMap(
- ClassLikeStorageProvider $classlike_storage_provider,
- array $expected_references
- ): array {
- $reference_dictionary = [];
-
- foreach ($classlike_storage_provider->getAll() as $storage) {
- if (!$storage->location) {
- continue;
- }
-
- $fq_classlike_name = $storage->name;
-
- if (isset($expected_references[$fq_classlike_name])) {
- $reference_dictionary[$fq_classlike_name]
- = $storage->location->file_name
- . ':' . $storage->location->getLineNumber()
- . ':' . $storage->location->getColumn();
- }
-
- foreach ($storage->methods as $method_name => $method_storage) {
- if (!$method_storage->location) {
- continue;
- }
-
- if (isset($expected_references[$fq_classlike_name . '::' . $method_name . '()'])) {
- $reference_dictionary[$fq_classlike_name . '::' . $method_name . '()']
- = $method_storage->location->file_name
- . ':' . $method_storage->location->getLineNumber()
- . ':' . $method_storage->location->getColumn();
- }
- }
-
- foreach ($storage->properties as $property_name => $property_storage) {
- if (!$property_storage->location) {
- continue;
- }
-
- if (isset($expected_references[$fq_classlike_name . '::$' . $property_name])) {
- $reference_dictionary[$fq_classlike_name . '::$' . $property_name]
- = $property_storage->location->file_name
- . ':' . $property_storage->location->getLineNumber()
- . ':' . $property_storage->location->getColumn();
- }
- }
- }
-
- return $reference_dictionary;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Reflection.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Reflection.php
deleted file mode 100644
index 8a50103..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Reflection.php
+++ /dev/null
@@ -1,548 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Exception;
-use LogicException;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Storage\ClassConstantStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Storage\PropertyStorage;
-use Psalm\Type;
-use Psalm\Type\Union;
-use ReflectionClass;
-use ReflectionException;
-use ReflectionFunction;
-use ReflectionMethod;
-use ReflectionNamedType;
-use ReflectionParameter;
-use ReflectionType;
-use ReflectionUnionType;
-use UnexpectedValueException;
-
-use function array_map;
-use function array_merge;
-use function get_class;
-use function implode;
-use function strtolower;
-
-/**
- * @internal
- *
- * Handles information gleaned from class and function reflection
- */
-class Reflection
-{
- /**
- * @var ClassLikeStorageProvider
- */
- private $storage_provider;
-
- /**
- * @var Codebase
- */
- private $codebase;
-
- /**
- * @var array<string, FunctionStorage>
- */
- private static $builtin_functions = [];
-
- public function __construct(ClassLikeStorageProvider $storage_provider, Codebase $codebase)
- {
- $this->storage_provider = $storage_provider;
- $this->codebase = $codebase;
- self::$builtin_functions = [];
- }
-
- public function registerClass(ReflectionClass $reflected_class): void
- {
- $class_name = $reflected_class->name;
-
- if ($class_name === 'LibXMLError') {
- $class_name = 'libXMLError';
- }
-
- $class_name_lower = strtolower($class_name);
-
- try {
- $this->storage_provider->get($class_name_lower);
-
- return;
- } catch (Exception $e) {
- // this is fine
- }
-
- $reflected_parent_class = $reflected_class->getParentClass();
-
- $storage = $this->storage_provider->create($class_name);
- $storage->abstract = $reflected_class->isAbstract();
- $storage->is_interface = $reflected_class->isInterface();
-
- $storage->potential_declaring_method_ids['__construct'][$class_name_lower . '::__construct'] = true;
-
- if ($reflected_parent_class) {
- $parent_class_name = $reflected_parent_class->getName();
- $this->registerClass($reflected_parent_class);
- $parent_class_name_lc = strtolower($parent_class_name);
-
- $parent_storage = $this->storage_provider->get($parent_class_name_lc);
-
- $this->registerInheritedMethods($class_name_lower, $parent_class_name_lc);
- $this->registerInheritedProperties($class_name_lower, $parent_class_name_lc);
-
- $storage->class_implements = $parent_storage->class_implements;
-
- $storage->constants = $parent_storage->constants;
-
- $storage->parent_classes = array_merge(
- [$parent_class_name_lc => $parent_class_name],
- $parent_storage->parent_classes
- );
-
- $storage->used_traits = $parent_storage->used_traits;
- }
-
- $class_properties = $reflected_class->getProperties();
-
- $public_mapped_properties = PropertyMap::inPropertyMap($class_name)
- ? PropertyMap::getPropertyMap()[strtolower($class_name)]
- : [];
-
- foreach ($class_properties as $class_property) {
- $property_name = $class_property->getName();
- $storage->properties[$property_name] = new PropertyStorage();
-
- $storage->properties[$property_name]->type = Type::getMixed();
-
- if ($class_property->isStatic()) {
- $storage->properties[$property_name]->is_static = true;
- }
-
- if ($class_property->isPublic()) {
- $storage->properties[$property_name]->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- } elseif ($class_property->isProtected()) {
- $storage->properties[$property_name]->visibility = ClassLikeAnalyzer::VISIBILITY_PROTECTED;
- } elseif ($class_property->isPrivate()) {
- $storage->properties[$property_name]->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE;
- }
-
- $property_id = $class_property->class . '::$' . $property_name;
-
- $storage->declaring_property_ids[$property_name] = $class_property->class;
- $storage->appearing_property_ids[$property_name] = $property_id;
-
- if (!$class_property->isPrivate()) {
- $storage->inheritable_property_ids[$property_name] = $property_id;
- }
- }
-
- // have to do this separately as there can be new properties here
- foreach ($public_mapped_properties as $property_name => $type_string) {
- $property_id = $class_name . '::$' . $property_name;
-
- if (!isset($storage->properties[$property_name])) {
- $storage->properties[$property_name] = new PropertyStorage();
- $storage->properties[$property_name]->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
-
- $storage->declaring_property_ids[$property_name] = $class_name;
- $storage->appearing_property_ids[$property_name] = $property_id;
- $storage->inheritable_property_ids[$property_name] = $property_id;
- }
-
- $type = Type::parseString($type_string);
-
- if ($property_id === 'DateInterval::$days') {
- $type->ignore_falsable_issues = true;
- }
-
- $storage->properties[$property_name]->type = $type;
- }
-
- /** @var array<string, int|string|float|null|array> */
- $class_constants = $reflected_class->getConstants();
-
- foreach ($class_constants as $name => $value) {
- $storage->constants[$name] = new ClassConstantStorage(
- ClassLikeAnalyzer::getTypeFromValue($value),
- ClassLikeAnalyzer::VISIBILITY_PUBLIC,
- null
- );
- }
-
- if ($reflected_class->isInterface()) {
- $this->codebase->classlikes->addFullyQualifiedInterfaceName($class_name);
- } elseif ($reflected_class->isTrait()) {
- $this->codebase->classlikes->addFullyQualifiedTraitName($class_name);
- } else {
- $this->codebase->classlikes->addFullyQualifiedClassName($class_name);
- }
-
- $reflection_methods = $reflected_class->getMethods(
- (ReflectionMethod::IS_PUBLIC | ReflectionMethod::IS_PROTECTED)
- );
-
- if ($class_name_lower === 'generator') {
- $storage->template_types = [
- 'TKey' => ['Generator' => Type::getMixed()],
- 'TValue' => ['Generator' => Type::getMixed()],
- ];
- }
-
- $interfaces = $reflected_class->getInterfaces();
-
- foreach ($interfaces as $interface) {
- $interface_name = $interface->getName();
- $this->registerClass($interface);
-
- if ($reflected_class->isInterface()) {
- $storage->parent_interfaces[strtolower($interface_name)] = $interface_name;
- } else {
- $storage->class_implements[strtolower($interface_name)] = $interface_name;
- }
- }
-
- foreach ($reflection_methods as $reflection_method) {
- $method_reflection_class = $reflection_method->getDeclaringClass();
-
- $this->registerClass($method_reflection_class);
-
- $this->extractReflectionMethodInfo($reflection_method);
-
- if ($reflection_method->class !== $class_name
- && ($class_name !== 'SoapFault' || $reflection_method->name !== '__construct')
- ) {
- $reflection_method_name = strtolower($reflection_method->name);
- $reflection_method_class = $reflection_method->class;
-
- $this->codebase->methods->setDeclaringMethodId(
- $class_name,
- $reflection_method_name,
- $reflection_method_class,
- $reflection_method_name
- );
-
- $this->codebase->methods->setAppearingMethodId(
- $class_name,
- $reflection_method_name,
- $reflection_method_class,
- $reflection_method_name
- );
- }
- }
- }
-
- public function extractReflectionMethodInfo(ReflectionMethod $method): void
- {
- $method_name_lc = strtolower($method->getName());
-
- $fq_class_name = $method->class;
-
- $fq_class_name_lc = strtolower($fq_class_name);
-
- $class_storage = $this->storage_provider->get($fq_class_name_lc);
-
- if (isset($class_storage->methods[$method_name_lc])) {
- return;
- }
-
- $method_id = $method->class . '::' . $method_name_lc;
-
- $storage = $class_storage->methods[$method_name_lc] = new MethodStorage();
-
- $storage->cased_name = $method->name;
- $storage->defining_fqcln = $method->class;
-
- if ($method_name_lc === $fq_class_name_lc) {
- $this->codebase->methods->setDeclaringMethodId(
- $fq_class_name,
- '__construct',
- $fq_class_name,
- $method_name_lc
- );
- $this->codebase->methods->setAppearingMethodId(
- $fq_class_name,
- '__construct',
- $fq_class_name,
- $method_name_lc
- );
- }
-
- $declaring_class = $method->getDeclaringClass();
-
- $storage->is_static = $method->isStatic();
- $storage->abstract = $method->isAbstract();
- $storage->mutation_free = $storage->external_mutation_free
- = ($method_name_lc === '__construct' && $fq_class_name_lc === 'datetimezone');
-
- $class_storage->declaring_method_ids[$method_name_lc] = new MethodIdentifier(
- $declaring_class->name,
- $method_name_lc
- );
-
- $class_storage->inheritable_method_ids[$method_name_lc]
- = $class_storage->declaring_method_ids[$method_name_lc];
- $class_storage->appearing_method_ids[$method_name_lc]
- = $class_storage->declaring_method_ids[$method_name_lc];
- $class_storage->overridden_method_ids[$method_name_lc] = [];
-
- $storage->visibility = $method->isPrivate()
- ? ClassLikeAnalyzer::VISIBILITY_PRIVATE
- : ($method->isProtected() ? ClassLikeAnalyzer::VISIBILITY_PROTECTED : ClassLikeAnalyzer::VISIBILITY_PUBLIC);
-
- $callables = InternalCallMapHandler::getCallablesFromCallMap($method_id);
-
- if ($callables && $callables[0]->params !== null && $callables[0]->return_type !== null) {
- $storage->setParams([]);
-
- foreach ($callables[0]->params as $param) {
- if ($param->type) {
- $param->type->queueClassLikesForScanning($this->codebase);
- }
- }
-
- $storage->setParams($callables[0]->params);
-
- $storage->return_type = $callables[0]->return_type;
- $storage->return_type->queueClassLikesForScanning($this->codebase);
- } else {
- $params = $method->getParameters();
-
- $storage->setParams([]);
-
- foreach ($params as $param) {
- $param_array = $this->getReflectionParamData($param);
- $storage->addParam($param_array);
- }
- }
-
- $storage->required_param_count = 0;
-
- foreach ($storage->params as $i => $param) {
- if (!$param->is_optional && !$param->is_variadic) {
- $storage->required_param_count = $i + 1;
- }
- }
- }
-
- private function getReflectionParamData(ReflectionParameter $param): FunctionLikeParameter
- {
- $param_type = self::getPsalmTypeFromReflectionType($param->getType());
- $param_name = $param->getName();
-
- $is_optional = $param->isOptional();
-
- $parameter = new FunctionLikeParameter(
- $param_name,
- $param->isPassedByReference(),
- $param_type,
- null,
- null,
- $is_optional,
- $param_type->isNullable(),
- $param->isVariadic()
- );
-
- $parameter->signature_type = Type::getMixed();
-
- return $parameter;
- }
-
- /**
- * @param callable-string $function_id
- *
- * @return false|null
- */
- public function registerFunction(string $function_id): ?bool
- {
- try {
- $reflection_function = new ReflectionFunction($function_id);
-
- $callmap_callable = null;
-
- if (isset(self::$builtin_functions[$function_id])) {
- return null;
- }
-
- $storage = self::$builtin_functions[$function_id] = new FunctionStorage();
-
- if (InternalCallMapHandler::inCallMap($function_id)) {
- $callmap_callable = InternalCallMapHandler::getCallableFromCallMapById(
- $this->codebase,
- $function_id,
- [],
- null
- );
- }
-
- if ($callmap_callable !== null
- && $callmap_callable->params !== null
- && $callmap_callable->return_type !== null
- ) {
- $storage->setParams($callmap_callable->params);
- $storage->return_type = $callmap_callable->return_type;
- } else {
- $reflection_params = $reflection_function->getParameters();
-
- foreach ($reflection_params as $param) {
- $param_obj = $this->getReflectionParamData($param);
- $storage->addParam($param_obj);
- }
-
- if ($reflection_return_type = $reflection_function->getReturnType()) {
- $storage->return_type = self::getPsalmTypeFromReflectionType($reflection_return_type);
- }
- }
-
- $storage->pure = true;
-
- $storage->required_param_count = 0;
-
- foreach ($storage->params as $i => $param) {
- if (!$param->is_optional && !$param->is_variadic) {
- $storage->required_param_count = $i + 1;
- }
- }
-
- $storage->cased_name = $reflection_function->getName();
- } catch (ReflectionException $e) {
- return false;
- }
-
- return null;
- }
-
- public static function getPsalmTypeFromReflectionType(?ReflectionType $reflection_type = null): Union
- {
- if (!$reflection_type) {
- return Type::getMixed();
- }
-
- if ($reflection_type instanceof ReflectionNamedType) {
- $type = $reflection_type->getName();
- } elseif ($reflection_type instanceof ReflectionUnionType) {
- /** @psalm-suppress MixedArgument */
- $type = implode(
- '|',
- array_map(
- function (ReflectionNamedType $reflection) {
- return $reflection->getName();
- },
- $reflection_type->getTypes()
- )
- );
- } else {
- throw new LogicException('Unexpected reflection class ' . get_class($reflection_type) . ' found.');
- }
-
- if ($reflection_type->allowsNull()) {
- $type .= '|null';
- }
-
- return Type::parseString($type);
- }
-
- private function registerInheritedMethods(
- string $fq_class_name,
- string $parent_class
- ): void {
- $parent_storage = $this->storage_provider->get($parent_class);
- $storage = $this->storage_provider->get($fq_class_name);
-
- // register where they appear (can never be in a trait)
- foreach ($parent_storage->appearing_method_ids as $method_name => $appearing_method_id) {
- $storage->appearing_method_ids[$method_name] = $appearing_method_id;
- }
-
- // register where they're declared
- foreach ($parent_storage->inheritable_method_ids as $method_name => $declaring_method_id) {
- $storage->declaring_method_ids[$method_name] = $declaring_method_id;
- $storage->inheritable_method_ids[$method_name] = $declaring_method_id;
-
- $storage->overridden_method_ids[$method_name][$declaring_method_id->fq_class_name]
- = $declaring_method_id;
- }
- }
-
- /**
- * @param lowercase-string $fq_class_name
- * @param lowercase-string $parent_class
- *
- */
- private function registerInheritedProperties(
- string $fq_class_name,
- string $parent_class
- ): void {
- $parent_storage = $this->storage_provider->get($parent_class);
- $storage = $this->storage_provider->get($fq_class_name);
-
- // register where they appear (can never be in a trait)
- foreach ($parent_storage->appearing_property_ids as $property_name => $appearing_property_id) {
- if (!$parent_storage->is_trait
- && isset($parent_storage->properties[$property_name])
- && $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- continue;
- }
-
- $storage->appearing_property_ids[$property_name] = $appearing_property_id;
- }
-
- // register where they're declared
- foreach ($parent_storage->declaring_property_ids as $property_name => $declaring_property_class) {
- if (!$parent_storage->is_trait
- && isset($parent_storage->properties[$property_name])
- && $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- continue;
- }
-
- $storage->declaring_property_ids[$property_name] = strtolower($declaring_property_class);
- }
-
- // register where they're declared
- foreach ($parent_storage->inheritable_property_ids as $property_name => $inheritable_property_id) {
- if (!$parent_storage->is_trait
- && isset($parent_storage->properties[$property_name])
- && $parent_storage->properties[$property_name]->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE
- ) {
- continue;
- }
-
- $storage->inheritable_property_ids[$property_name] = $inheritable_property_id;
- }
- }
-
- public function hasFunction(string $function_id): bool
- {
- return isset(self::$builtin_functions[$function_id]);
- }
-
- public function getFunctionStorage(string $function_id): FunctionStorage
- {
- if (isset(self::$builtin_functions[$function_id])) {
- return self::$builtin_functions[$function_id];
- }
-
- throw new UnexpectedValueException('Expecting to have a function for ' . $function_id);
- }
-
- /**
- * @return array<string, FunctionStorage>
- */
- public function getFunctions(): array
- {
- return self::$builtin_functions;
- }
-
- public static function clearCache(): void
- {
- self::$builtin_functions = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php
deleted file mode 100644
index fb6e1bf..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php
+++ /dev/null
@@ -1,811 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Internal\Analyzer\IssueData;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\ErrorHandler;
-use Psalm\Internal\Fork\Pool;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\IssueBuffer;
-use Psalm\Progress\Progress;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Type;
-use ReflectionClass;
-use Throwable;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_merge;
-use function array_pop;
-use function ceil;
-use function count;
-use function error_reporting;
-use function explode;
-use function file_exists;
-use function min;
-use function realpath;
-use function strtolower;
-use function substr;
-
-use const DIRECTORY_SEPARATOR;
-use const PHP_EOL;
-
-/**
- * @psalm-type ThreadData = array{
- * array<string, string>,
- * array<string, string>,
- * array<string, string>,
- * array<string, bool>,
- * array<string, bool>,
- * array<string, string>,
- * array<string, bool>,
- * array<string, bool>,
- * array<string, bool>
- * }
- *
- * @psalm-type PoolData = array{
- * classlikes_data:array{
- * array<lowercase-string, bool>,
- * array<lowercase-string, bool>,
- * array<lowercase-string, bool>,
- * array<string, bool>,
- * array<lowercase-string, bool>,
- * array<string, bool>,
- * array<lowercase-string, bool>,
- * array<string, bool>,
- * array<string, bool>
- * },
- * scanner_data: ThreadData,
- * issues:array<string, list<IssueData>>,
- * changed_members:array<string, array<string, bool>>,
- * unchanged_signature_members:array<string, array<string, bool>>,
- * diff_map:array<string, array<int, array{int, int, int, int}>>,
- * deletion_ranges:array<string, array<int, array{int, int}>>,
- * errors:array<string, bool>,
- * classlike_storage:array<string, ClassLikeStorage>,
- * file_storage:array<lowercase-string, FileStorage>,
- * new_file_content_hashes: array<string, string>,
- * taint_data: ?TaintFlowGraph
- * }
- */
-
-/**
- * @internal
- *
- * Contains methods that aid in the scanning of Psalm's codebase
- */
-class Scanner
-{
- /**
- * @var Codebase
- */
- private $codebase;
-
- /**
- * @var array<string, string>
- */
- private $classlike_files = [];
-
- /**
- * @var array<string, bool>
- */
- private $deep_scanned_classlike_files = [];
-
- /**
- * @var array<string, string>
- */
- private $files_to_scan = [];
-
- /**
- * @var array<string, string>
- */
- private $classes_to_scan = [];
-
- /**
- * @var array<string, bool>
- */
- private $classes_to_deep_scan = [];
-
- /**
- * @var array<string, string>
- */
- private $files_to_deep_scan = [];
-
- /**
- * @var array<string, bool>
- */
- private $scanned_files = [];
-
- /**
- * @var array<string, bool>
- */
- private $store_scan_failure = [];
-
- /**
- * @var array<string, bool>
- */
- private $reflected_classlikes_lc = [];
-
- /**
- * @var Reflection
- */
- private $reflection;
-
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var Progress
- */
- private $progress;
-
- /**
- * @var FileStorageProvider
- */
- private $file_storage_provider;
-
- /**
- * @var FileProvider
- */
- private $file_provider;
-
- /**
- * @var FileReferenceProvider
- */
- private $file_reference_provider;
-
- /**
- * @var bool
- */
- private $is_forked = false;
-
- public function __construct(
- Codebase $codebase,
- Config $config,
- FileStorageProvider $file_storage_provider,
- FileProvider $file_provider,
- Reflection $reflection,
- FileReferenceProvider $file_reference_provider,
- Progress $progress
- ) {
- $this->codebase = $codebase;
- $this->reflection = $reflection;
- $this->file_provider = $file_provider;
- $this->progress = $progress;
- $this->file_storage_provider = $file_storage_provider;
- $this->config = $config;
- $this->file_reference_provider = $file_reference_provider;
- }
-
- /**
- * @param array<string, string> $files_to_scan
- *
- */
- public function addFilesToShallowScan(array $files_to_scan): void
- {
- $this->files_to_scan += $files_to_scan;
- }
-
- /**
- * @param array<string, string> $files_to_scan
- */
- public function addFilesToDeepScan(array $files_to_scan): void
- {
- $this->files_to_scan += $files_to_scan;
- $this->files_to_deep_scan += $files_to_scan;
- }
-
- public function addFileToShallowScan(string $file_path): void
- {
- $this->files_to_scan[$file_path] = $file_path;
- }
-
- public function addFileToDeepScan(string $file_path): void
- {
- $this->files_to_scan[$file_path] = $file_path;
- $this->files_to_deep_scan[$file_path] = $file_path;
- }
-
- public function removeFile(string $file_path): void
- {
- unset($this->scanned_files[$file_path]);
- }
-
- public function removeClassLike(string $fq_classlike_name_lc): void
- {
- unset(
- $this->classlike_files[$fq_classlike_name_lc],
- $this->deep_scanned_classlike_files[$fq_classlike_name_lc]
- );
- }
-
- public function setClassLikeFilePath(string $fq_classlike_name_lc, string $file_path): void
- {
- $this->classlike_files[$fq_classlike_name_lc] = $file_path;
- }
-
- public function getClassLikeFilePath(string $fq_classlike_name_lc): string
- {
- if (!isset($this->classlike_files[$fq_classlike_name_lc])) {
- throw new UnexpectedValueException('Could not find file for ' . $fq_classlike_name_lc);
- }
-
- return $this->classlike_files[$fq_classlike_name_lc];
- }
-
- /**
- * @param array<string, mixed> $phantom_classes
- */
- public function queueClassLikeForScanning(
- string $fq_classlike_name,
- bool $analyze_too = false,
- bool $store_failure = true,
- array $phantom_classes = []
- ): void {
- if ($fq_classlike_name[0] === '\\') {
- $fq_classlike_name = substr($fq_classlike_name, 1);
- }
-
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- if ($fq_classlike_name_lc === 'static') {
- return;
- }
-
- // avoid checking classes that we know will just end in failure
- if ($fq_classlike_name_lc === 'null' || substr($fq_classlike_name_lc, -5) === '\null') {
- return;
- }
-
- if (!isset($this->classlike_files[$fq_classlike_name_lc])
- || ($analyze_too && !isset($this->deep_scanned_classlike_files[$fq_classlike_name_lc]))
- ) {
- if (!isset($this->classes_to_scan[$fq_classlike_name_lc]) || $store_failure) {
- $this->classes_to_scan[$fq_classlike_name_lc] = $fq_classlike_name;
- }
-
- if ($analyze_too) {
- $this->classes_to_deep_scan[$fq_classlike_name_lc] = true;
- }
-
- $this->store_scan_failure[$fq_classlike_name] = $store_failure;
-
- if (PropertyMap::inPropertyMap($fq_classlike_name_lc)) {
- $public_mapped_properties = PropertyMap::getPropertyMap()[$fq_classlike_name_lc];
-
- foreach ($public_mapped_properties as $public_mapped_property) {
- $property_type = Type::parseString($public_mapped_property);
- $property_type->queueClassLikesForScanning(
- $this->codebase,
- null,
- $phantom_classes + [$fq_classlike_name_lc => true]
- );
- }
- }
- }
- }
-
- public function scanFiles(ClassLikes $classlikes, int $pool_size = 1): bool
- {
- $has_changes = false;
- while ($this->files_to_scan || $this->classes_to_scan) {
- if ($this->files_to_scan) {
- if ($this->scanFilePaths($pool_size)) {
- $has_changes = true;
- }
- } else {
- $this->convertClassesToFilePaths($classlikes);
- }
- }
-
- return $has_changes;
- }
-
- private function scanFilePaths(int $pool_size): bool
- {
- $filetype_scanners = $this->config->getFiletypeScanners();
- $files_to_scan = array_filter(
- $this->files_to_scan,
- function (string $file_path): bool {
- return $this->file_provider->fileExists($file_path)
- && (!isset($this->scanned_files[$file_path])
- || (isset($this->files_to_deep_scan[$file_path]) && !$this->scanned_files[$file_path]));
- }
- );
-
- $this->files_to_scan = [];
-
- if (!$files_to_scan) {
- return false;
- }
-
- $files_to_deep_scan = $this->files_to_deep_scan;
-
- $scanner_worker =
- function (int $_, string $file_path) use ($filetype_scanners, $files_to_deep_scan): void {
- $this->scanFile(
- $file_path,
- $filetype_scanners,
- isset($files_to_deep_scan[$file_path])
- );
- };
-
- if (!$this->is_forked && $pool_size > 1 && count($files_to_scan) > 512) {
- $pool_size = ceil(min($pool_size, count($files_to_scan) / 256));
- } else {
- $pool_size = 1;
- }
-
- if ($pool_size > 1) {
- $process_file_paths = [];
-
- $i = 0;
-
- foreach ($files_to_scan as $file_path) {
- $process_file_paths[$i % $pool_size][] = $file_path;
- ++$i;
- }
-
- $this->progress->debug('Forking process for scanning' . PHP_EOL);
-
- // Run scanning one file at a time, splitting the set of
- // files up among a given number of child processes.
- $pool = new Pool(
- $this->config,
- $process_file_paths,
- function (): void {
- $this->progress->debug('Initialising forked process for scanning' . PHP_EOL);
-
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
- $statements_provider = $codebase->statements_provider;
-
- $codebase->scanner->isForked();
- FileStorageProvider::deleteAll();
- ClassLikeStorageProvider::deleteAll();
-
- $statements_provider->resetDiffs();
-
- $this->progress->debug('Have initialised forked process for scanning' . PHP_EOL);
- },
- $scanner_worker,
- /**
- * @return PoolData
- */
- function () {
- $this->progress->debug('Collecting data from forked scanner process' . PHP_EOL);
-
- $project_analyzer = ProjectAnalyzer::getInstance();
- $codebase = $project_analyzer->getCodebase();
- $statements_provider = $codebase->statements_provider;
-
- return [
- 'classlikes_data' => $codebase->classlikes->getThreadData(),
- 'scanner_data' => $codebase->scanner->getThreadData(),
- 'issues' => IssueBuffer::getIssuesData(),
- 'changed_members' => $statements_provider->getChangedMembers(),
- 'unchanged_signature_members' => $statements_provider->getUnchangedSignatureMembers(),
- 'diff_map' => $statements_provider->getDiffMap(),
- 'deletion_ranges' => $statements_provider->getDeletionRanges(),
- 'errors' => $statements_provider->getErrors(),
- 'classlike_storage' => $codebase->classlike_storage_provider->getAll(),
- 'file_storage' => $codebase->file_storage_provider->getAll(),
- 'new_file_content_hashes' => $statements_provider->parser_cache_provider
- ? $statements_provider->parser_cache_provider->getNewFileContentHashes()
- : [],
- 'taint_data' => $codebase->taint_flow_graph,
- ];
- }
- );
-
- // Wait for all tasks to complete and collect the results.
- /**
- * @var array<int, PoolData>
- */
- $forked_pool_data = $pool->wait();
-
- foreach ($forked_pool_data as $pool_data) {
- IssueBuffer::addIssues($pool_data['issues']);
-
- $this->codebase->statements_provider->addChangedMembers(
- $pool_data['changed_members']
- );
- $this->codebase->statements_provider->addUnchangedSignatureMembers(
- $pool_data['unchanged_signature_members']
- );
- $this->codebase->statements_provider->addDiffMap(
- $pool_data['diff_map']
- );
- $this->codebase->statements_provider->addDeletionRanges(
- $pool_data['deletion_ranges']
- );
- $this->codebase->statements_provider->addErrors($pool_data['errors']);
-
- if ($this->codebase->taint_flow_graph && $pool_data['taint_data']) {
- $this->codebase->taint_flow_graph->addGraph($pool_data['taint_data']);
- }
-
- $this->codebase->file_storage_provider->addMore($pool_data['file_storage']);
- $this->codebase->classlike_storage_provider->addMore($pool_data['classlike_storage']);
-
- $this->codebase->classlikes->addThreadData($pool_data['classlikes_data']);
-
- $this->addThreadData($pool_data['scanner_data']);
-
- if ($this->codebase->statements_provider->parser_cache_provider) {
- $this->codebase->statements_provider->parser_cache_provider->addNewFileContentHashes(
- $pool_data['new_file_content_hashes']
- );
- }
- }
-
- if ($pool->didHaveError()) {
- exit(1);
- }
- } else {
- $i = 0;
-
- foreach ($files_to_scan as $file_path => $_) {
- $scanner_worker($i, $file_path);
- ++$i;
- }
- }
-
- if ($this->codebase->statements_provider->parser_cache_provider) {
- $this->codebase->statements_provider->parser_cache_provider->saveFileContentHashes();
- }
-
- foreach ($files_to_scan as $scanned_file) {
- if ($this->config->hasStubFile($scanned_file)) {
- $file_storage = $this->file_storage_provider->get($scanned_file);
-
- foreach ($file_storage->functions as $function_storage) {
- if ($function_storage->cased_name
- && !$this->codebase->functions->hasStubbedFunction($function_storage->cased_name)
- ) {
- $this->codebase->functions->addGlobalFunction(
- $function_storage->cased_name,
- $function_storage
- );
- }
- }
-
- foreach ($file_storage->constants as $name => $type) {
- $this->codebase->addGlobalConstantType($name, $type);
- }
- }
- }
-
- $this->file_reference_provider->addClassLikeFiles($this->classlike_files);
-
- return true;
- }
-
- private function convertClassesToFilePaths(ClassLikes $classlikes): void
- {
- $classes_to_scan = $this->classes_to_scan;
-
- $this->classes_to_scan = [];
-
- foreach ($classes_to_scan as $fq_classlike_name) {
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- if (isset($this->reflected_classlikes_lc[$fq_classlike_name_lc])) {
- continue;
- }
-
- if ($classlikes->isMissingClassLike($fq_classlike_name_lc)) {
- continue;
- }
-
- if (!isset($this->classlike_files[$fq_classlike_name_lc])) {
- if ($classlikes->doesClassLikeExist($fq_classlike_name_lc)) {
- if ($fq_classlike_name_lc === 'self') {
- continue;
- }
-
- $this->progress->debug('Using reflection to get metadata for ' . $fq_classlike_name . "\n");
-
- /** @psalm-suppress ArgumentTypeCoercion */
- $reflected_class = new ReflectionClass($fq_classlike_name);
- $this->reflection->registerClass($reflected_class);
- $this->reflected_classlikes_lc[$fq_classlike_name_lc] = true;
- } elseif ($this->fileExistsForClassLike($classlikes, $fq_classlike_name)) {
- $fq_classlike_name_lc = strtolower($classlikes->getUnAliasedName(
- $fq_classlike_name_lc
- ));
-
- // even though we've checked this above, calling the method invalidates it
- if (isset($this->classlike_files[$fq_classlike_name_lc])) {
- $file_path = $this->classlike_files[$fq_classlike_name_lc];
- $this->files_to_scan[$file_path] = $file_path;
- if (isset($this->classes_to_deep_scan[$fq_classlike_name_lc])) {
- unset($this->classes_to_deep_scan[$fq_classlike_name_lc]);
- $this->files_to_deep_scan[$file_path] = $file_path;
- }
- }
- } elseif ($this->store_scan_failure[$fq_classlike_name]) {
- $classlikes->registerMissingClassLike($fq_classlike_name_lc);
- }
- } elseif (isset($this->classes_to_deep_scan[$fq_classlike_name_lc])
- && !isset($this->deep_scanned_classlike_files[$fq_classlike_name_lc])
- ) {
- $file_path = $this->classlike_files[$fq_classlike_name_lc];
- $this->files_to_scan[$file_path] = $file_path;
- unset($this->classes_to_deep_scan[$fq_classlike_name_lc]);
- $this->files_to_deep_scan[$file_path] = $file_path;
- $this->deep_scanned_classlike_files[$fq_classlike_name_lc] = true;
- }
- }
- }
-
- /**
- * @param array<string, class-string<FileScanner>> $filetype_scanners
- */
- private function scanFile(
- string $file_path,
- array $filetype_scanners,
- bool $will_analyze = false
- ): void {
- $file_scanner = $this->getScannerForPath($file_path, $filetype_scanners, $will_analyze);
-
- if (isset($this->scanned_files[$file_path])
- && (!$will_analyze || $this->scanned_files[$file_path])
- ) {
- throw new UnexpectedValueException('Should not be rescanning ' . $file_path);
- }
-
- if (!$this->file_provider->fileExists($file_path) && $this->config->mustBeIgnored($file_path)) {
- // this should not happen, but might if the file was temporary
- return;
- }
-
- $file_contents = $this->file_provider->getContents($file_path);
-
- $from_cache = $this->file_storage_provider->has($file_path, $file_contents);
-
- if (!$from_cache) {
- $this->file_storage_provider->create($file_path);
- }
-
- $this->scanned_files[$file_path] = $will_analyze;
-
- $file_storage = $this->file_storage_provider->get($file_path);
-
- $file_scanner->scan(
- $this->codebase,
- $file_storage,
- $from_cache,
- $this->progress
- );
-
- if (!$from_cache) {
- if (!$file_storage->has_visitor_issues && $this->file_storage_provider->cache) {
- $this->file_storage_provider->cache->writeToCache($file_storage, $file_contents);
- }
- } else {
- $this->codebase->statements_provider->setUnchangedFile($file_path);
-
- foreach ($file_storage->required_file_paths as $required_file_path) {
- if ($will_analyze) {
- $this->addFileToDeepScan($required_file_path);
- } else {
- $this->addFileToShallowScan($required_file_path);
- }
- }
-
- foreach ($file_storage->classlikes_in_file as $fq_classlike_name) {
- $this->codebase->exhumeClassLikeStorage(strtolower($fq_classlike_name), $file_path);
- }
-
- foreach ($file_storage->required_classes as $fq_classlike_name) {
- $this->queueClassLikeForScanning($fq_classlike_name, $will_analyze, false);
- }
-
- foreach ($file_storage->required_interfaces as $fq_classlike_name) {
- $this->queueClassLikeForScanning($fq_classlike_name, false, false);
- }
-
- foreach ($file_storage->referenced_classlikes as $fq_classlike_name) {
- $this->queueClassLikeForScanning($fq_classlike_name, false, false);
- }
-
- if ($this->codebase->register_autoload_files) {
- foreach ($file_storage->functions as $function_storage) {
- if ($function_storage->cased_name
- && !$this->codebase->functions->hasStubbedFunction($function_storage->cased_name)
- ) {
- $this->codebase->functions->addGlobalFunction(
- $function_storage->cased_name,
- $function_storage
- );
- }
- }
-
- foreach ($file_storage->constants as $name => $type) {
- $this->codebase->addGlobalConstantType($name, $type);
- }
- }
-
- foreach ($file_storage->classlike_aliases as $aliased_name => $unaliased_name) {
- $this->codebase->classlikes->addClassAlias($unaliased_name, $aliased_name);
- }
- }
- }
-
- /**
- * @param array<string, class-string<FileScanner>> $filetype_scanners
- */
- private function getScannerForPath(
- string $file_path,
- array $filetype_scanners,
- bool $will_analyze = false
- ): FileScanner {
- $path_parts = explode(DIRECTORY_SEPARATOR, $file_path);
- $file_name_parts = explode('.', array_pop($path_parts));
- $extension = count($file_name_parts) > 1 ? array_pop($file_name_parts) : null;
-
- $file_name = $this->config->shortenFileName($file_path);
-
- if (isset($filetype_scanners[$extension])) {
- return new $filetype_scanners[$extension]($file_path, $file_name, $will_analyze);
- }
-
- return new FileScanner($file_path, $file_name, $will_analyze);
- }
-
- /**
- * @return array<string, bool>
- */
- public function getScannedFiles(): array
- {
- return $this->scanned_files;
- }
-
- /**
- * Checks whether a class exists, and if it does then records what file it's in
- * for later checking
- */
- private function fileExistsForClassLike(ClassLikes $classlikes, string $fq_class_name): bool
- {
- $fq_class_name_lc = strtolower($fq_class_name);
-
- if (isset($this->classlike_files[$fq_class_name_lc])) {
- return true;
- }
-
- if ($fq_class_name === 'self') {
- return false;
- }
-
- $composer_file_path = $this->config->getComposerFilePathForClassLike($fq_class_name);
-
- if ($composer_file_path && file_exists($composer_file_path)) {
- $this->progress->debug('Using composer to locate file for ' . $fq_class_name . "\n");
-
- $classlikes->addFullyQualifiedClassLikeName(
- $fq_class_name_lc,
- realpath($composer_file_path)
- );
-
- return true;
- }
-
- $reflected_class = ErrorHandler::runWithExceptionsSuppressed(
- function () use ($fq_class_name): ?ReflectionClass {
- $old_level = error_reporting();
- $this->progress->setErrorReporting();
-
- try {
- $this->progress->debug('Using reflection to locate file for ' . $fq_class_name . "\n");
-
- /** @psalm-suppress ArgumentTypeCoercion */
- return new ReflectionClass($fq_class_name);
- } catch (Throwable $e) {
- // do not cache any results here (as case-sensitive filenames can screw things up)
-
- return null;
- } finally {
- error_reporting($old_level);
- }
- }
- );
-
- if (null === $reflected_class) {
- return false;
- }
-
- $file_path = (string)$reflected_class->getFileName();
-
- // if the file was autoloaded but exists in evaled code only, return false
- if (!file_exists($file_path)) {
- return false;
- }
-
- $new_fq_class_name = $reflected_class->getName();
- $new_fq_class_name_lc = strtolower($new_fq_class_name);
-
- if ($new_fq_class_name_lc !== $fq_class_name_lc) {
- $classlikes->addClassAlias($new_fq_class_name, $fq_class_name_lc);
- $fq_class_name_lc = $new_fq_class_name_lc;
- }
-
- $fq_class_name = $new_fq_class_name;
- $classlikes->addFullyQualifiedClassLikeName($fq_class_name_lc);
-
- if ($reflected_class->isInterface()) {
- $classlikes->addFullyQualifiedInterfaceName($fq_class_name, $file_path);
- } elseif ($reflected_class->isTrait()) {
- $classlikes->addFullyQualifiedTraitName($fq_class_name, $file_path);
- } else {
- $classlikes->addFullyQualifiedClassName($fq_class_name, $file_path);
- }
-
- return true;
- }
-
- /**
- * @return ThreadData
- */
- public function getThreadData(): array
- {
- return [
- $this->files_to_scan,
- $this->files_to_deep_scan,
- $this->classes_to_scan,
- $this->classes_to_deep_scan,
- $this->store_scan_failure,
- $this->classlike_files,
- $this->deep_scanned_classlike_files,
- $this->scanned_files,
- $this->reflected_classlikes_lc,
- ];
- }
-
- /**
- * @param ThreadData $thread_data
- *
- */
- public function addThreadData(array $thread_data): void
- {
- [
- $files_to_scan,
- $files_to_deep_scan,
- $classes_to_scan,
- $classes_to_deep_scan,
- $store_scan_failure,
- $classlike_files,
- $deep_scanned_classlike_files,
- $scanned_files,
- $reflected_classlikes_lc
- ] = $thread_data;
-
- $this->files_to_scan = array_merge($files_to_scan, $this->files_to_scan);
- $this->files_to_deep_scan = array_merge($files_to_deep_scan, $this->files_to_deep_scan);
- $this->classes_to_scan = array_merge($classes_to_scan, $this->classes_to_scan);
- $this->classes_to_deep_scan = array_merge($classes_to_deep_scan, $this->classes_to_deep_scan);
- $this->store_scan_failure = array_merge($store_scan_failure, $this->store_scan_failure);
- $this->classlike_files = array_merge($classlike_files, $this->classlike_files);
- $this->deep_scanned_classlike_files = array_merge(
- $deep_scanned_classlike_files,
- $this->deep_scanned_classlike_files
- );
- $this->scanned_files = array_merge($scanned_files, $this->scanned_files);
- $this->reflected_classlikes_lc = array_merge($reflected_classlikes_lc, $this->reflected_classlikes_lc);
- }
-
- public function isForked(): void
- {
- $this->is_forked = true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/TaintFlowGraph.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/TaintFlowGraph.php
deleted file mode 100644
index 2812bb6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/TaintFlowGraph.php
+++ /dev/null
@@ -1,524 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\CodeLocation;
-use Psalm\Config;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\TaintSink;
-use Psalm\Internal\DataFlow\TaintSource;
-use Psalm\Issue\TaintedCallable;
-use Psalm\Issue\TaintedCookie;
-use Psalm\Issue\TaintedCustom;
-use Psalm\Issue\TaintedEval;
-use Psalm\Issue\TaintedFile;
-use Psalm\Issue\TaintedHeader;
-use Psalm\Issue\TaintedHtml;
-use Psalm\Issue\TaintedInclude;
-use Psalm\Issue\TaintedLdap;
-use Psalm\Issue\TaintedSSRF;
-use Psalm\Issue\TaintedShell;
-use Psalm\Issue\TaintedSql;
-use Psalm\Issue\TaintedSystemSecret;
-use Psalm\Issue\TaintedTextWithQuotes;
-use Psalm\Issue\TaintedUnserialize;
-use Psalm\Issue\TaintedUserSecret;
-use Psalm\IssueBuffer;
-use Psalm\Type\TaintKind;
-
-use function array_diff;
-use function array_filter;
-use function array_intersect;
-use function array_merge;
-use function array_unique;
-use function count;
-use function end;
-use function implode;
-use function json_encode;
-use function ksort;
-use function sort;
-use function strlen;
-use function substr;
-
-class TaintFlowGraph extends DataFlowGraph
-{
- /** @var array<string, TaintSource> */
- private $sources = [];
-
- /** @var array<string, DataFlowNode> */
- private $nodes = [];
-
- /** @var array<string, TaintSink> */
- private $sinks = [];
-
- /** @var array<string, array<string, true>> */
- private $specialized_calls = [];
-
- /** @var array<string, array<string, true>> */
- private $specializations = [];
-
- public function addNode(DataFlowNode $node): void
- {
- $this->nodes[$node->id] = $node;
-
- if ($node->unspecialized_id && $node->specialization_key) {
- $this->specialized_calls[$node->specialization_key][$node->unspecialized_id] = true;
- $this->specializations[$node->unspecialized_id][$node->specialization_key] = true;
- }
- }
-
- public function addSource(TaintSource $node): void
- {
- $this->sources[$node->id] = $node;
- }
-
- public function addSink(TaintSink $node): void
- {
- $this->sinks[$node->id] = $node;
- // in the rare case the sink is the _next_ node, this is necessary
- $this->nodes[$node->id] = $node;
- }
-
- public function addGraph(self $taint): void
- {
- $this->sources += $taint->sources;
- $this->sinks += $taint->sinks;
- $this->nodes += $taint->nodes;
- $this->specialized_calls += $taint->specialized_calls;
-
- foreach ($taint->forward_edges as $key => $map) {
- if (!isset($this->forward_edges[$key])) {
- $this->forward_edges[$key] = $map;
- } else {
- $this->forward_edges[$key] += $map;
- }
- }
-
- foreach ($taint->specializations as $key => $map) {
- if (!isset($this->specializations[$key])) {
- $this->specializations[$key] = $map;
- } else {
- $this->specializations[$key] += $map;
- }
- }
- }
-
- public function getPredecessorPath(DataFlowNode $source): string
- {
- $location_summary = '';
-
- if ($source->code_location) {
- $location_summary = $source->code_location->getShortSummary();
- }
-
- $source_descriptor = $source->label . ($location_summary ? ' (' . $location_summary . ')' : '');
-
- $previous_source = $source->previous;
-
- if ($previous_source) {
- if ($previous_source === $source) {
- return '';
- }
-
- if ($source->code_location
- && $previous_source->code_location
- && $previous_source->code_location->getHash() === $source->code_location->getHash()
- && $previous_source->previous
- ) {
- return $this->getPredecessorPath($previous_source->previous) . ' -> ' . $source_descriptor;
- }
-
- return $this->getPredecessorPath($previous_source) . ' -> ' . $source_descriptor;
- }
-
- return $source_descriptor;
- }
-
- public function getSuccessorPath(DataFlowNode $sink): string
- {
- $location_summary = '';
-
- if ($sink->code_location) {
- $location_summary = $sink->code_location->getShortSummary();
- }
-
- $sink_descriptor = $sink->label . ($location_summary ? ' (' . $location_summary . ')' : '');
-
- $next_sink = $sink->previous;
-
- if ($next_sink) {
- if ($next_sink === $sink) {
- return '';
- }
-
- if ($sink->code_location
- && $next_sink->code_location
- && $next_sink->code_location->getHash() === $sink->code_location->getHash()
- && $next_sink->previous
- ) {
- return $sink_descriptor . ' -> ' . $this->getSuccessorPath($next_sink->previous);
- }
-
- return $sink_descriptor . ' -> ' . $this->getSuccessorPath($next_sink);
- }
-
- return $sink_descriptor;
- }
-
- /**
- * @return list<array{location: ?CodeLocation, label: string, entry_path_type: string}>
- */
- public function getIssueTrace(DataFlowNode $source): array
- {
- $previous_source = $source->previous;
-
- $node = [
- 'location' => $source->code_location,
- 'label' => $source->label,
- 'entry_path_type' => end($source->path_types) ?: ''
- ];
-
- if ($previous_source) {
- if ($previous_source === $source) {
- return [];
- }
-
- return array_merge($this->getIssueTrace($previous_source), [$node]);
- }
-
- return [$node];
- }
-
- public function connectSinksAndSources(): void
- {
- $visited_source_ids = [];
-
- $sources = $this->sources;
- $sinks = $this->sinks;
-
- ksort($this->specializations);
- ksort($this->forward_edges);
-
- // reprocess resolved descendants up to a maximum nesting level of 40
- for ($i = 0; count($sinks) && count($sources) && $i < 40; $i++) {
- $new_sources = [];
-
- ksort($sources);
-
- foreach ($sources as $source) {
- $source_taints = $source->taints;
- sort($source_taints);
-
- $visited_source_ids[$source->id][implode(',', $source_taints)] = true;
-
- $generated_sources = $this->getSpecializedSources($source);
-
- foreach ($generated_sources as $generated_source) {
- $new_sources = array_merge(
- $new_sources,
- $this->getChildNodes(
- $generated_source,
- $source_taints,
- $sinks,
- $visited_source_ids
- )
- );
- }
- }
-
- $sources = $new_sources;
- }
- }
-
- /**
- * @param array<string> $source_taints
- * @param array<DataFlowNode> $sinks
- * @return array<string, DataFlowNode>
- */
- private function getChildNodes(
- DataFlowNode $generated_source,
- array $source_taints,
- array $sinks,
- array $visited_source_ids
- ): array {
- $new_sources = [];
-
- $config = Config::getInstance();
-
- $project_analyzer = ProjectAnalyzer::getInstance();
-
- foreach ($this->forward_edges[$generated_source->id] as $to_id => $path) {
- $path_type = $path->type;
- $added_taints = $path->unescaped_taints ?: [];
- $removed_taints = $path->escaped_taints ?: [];
-
- if (!isset($this->nodes[$to_id])) {
- continue;
- }
-
- $destination_node = $this->nodes[$to_id];
-
- $new_taints = array_unique(
- array_diff(
- array_merge($source_taints, $added_taints),
- $removed_taints
- )
- );
-
- sort($new_taints);
-
- if (isset($visited_source_ids[$to_id][implode(',', $new_taints)])) {
- continue;
- }
-
- if (self::shouldIgnoreFetch($path_type, 'arraykey', $generated_source->path_types)) {
- continue;
- }
-
- if (self::shouldIgnoreFetch($path_type, 'arrayvalue', $generated_source->path_types)) {
- continue;
- }
-
- if (self::shouldIgnoreFetch($path_type, 'property', $generated_source->path_types)) {
- continue;
- }
-
- if ($generated_source->code_location
- && $project_analyzer->canReportIssues($generated_source->code_location->file_path)
- && !$config->reportIssueInFile('TaintedInput', $generated_source->code_location->file_path)
- ) {
- continue;
- }
-
- if (isset($sinks[$to_id])) {
- $matching_taints = array_intersect($sinks[$to_id]->taints, $new_taints);
-
- if ($matching_taints && $generated_source->code_location) {
- if ($sinks[$to_id]->code_location
- && $config->reportIssueInFile('TaintedInput', $sinks[$to_id]->code_location->file_path)
- ) {
- $issue_location = $sinks[$to_id]->code_location;
- } else {
- $issue_location = $generated_source->code_location;
- }
-
- $issue_trace = $this->getIssueTrace($generated_source);
- $path = $this->getPredecessorPath($generated_source)
- . ' -> ' . $this->getSuccessorPath($sinks[$to_id]);
-
- foreach ($matching_taints as $matching_taint) {
- switch ($matching_taint) {
- case TaintKind::INPUT_CALLABLE:
- $issue = new TaintedCallable(
- 'Detected tainted text',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_UNSERIALIZE:
- $issue = new TaintedUnserialize(
- 'Detected tainted code passed to unserialize or similar',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_INCLUDE:
- $issue = new TaintedInclude(
- 'Detected tainted code passed to include or similar',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_EVAL:
- $issue = new TaintedEval(
- 'Detected tainted code passed to eval or similar',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_SQL:
- $issue = new TaintedSql(
- 'Detected tainted SQL',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_HTML:
- $issue = new TaintedHtml(
- 'Detected tainted HTML',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_HAS_QUOTES:
- $issue = new TaintedTextWithQuotes(
- 'Detected tainted text with possible quotes',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_SHELL:
- $issue = new TaintedShell(
- 'Detected tainted shell code',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::USER_SECRET:
- $issue = new TaintedUserSecret(
- 'Detected tainted user secret leaking',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::SYSTEM_SECRET:
- $issue = new TaintedSystemSecret(
- 'Detected tainted system secret leaking',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_SSRF:
- $issue = new TaintedSSRF(
- 'Detected tainted network request',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_LDAP:
- $issue = new TaintedLdap(
- 'Detected tainted LDAP request',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_COOKIE:
- $issue = new TaintedCookie(
- 'Detected tainted cookie',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_FILE:
- $issue = new TaintedFile(
- 'Detected tainted file handling',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- case TaintKind::INPUT_HEADER:
- $issue = new TaintedHeader(
- 'Detected tainted header',
- $issue_location,
- $issue_trace,
- $path
- );
- break;
-
- default:
- $issue = new TaintedCustom(
- 'Detected tainted ' . $matching_taint,
- $issue_location,
- $issue_trace,
- $path
- );
- }
-
- IssueBuffer::maybeAdd($issue);
- }
- }
- }
-
- $new_destination = clone $destination_node;
- $new_destination->previous = $generated_source;
- $new_destination->taints = $new_taints;
- $new_destination->specialized_calls = $generated_source->specialized_calls;
- $new_destination->path_types = array_merge($generated_source->path_types, [$path_type]);
-
- $key = $to_id .
- ' ' . json_encode($new_destination->specialized_calls) .
- ' ' . json_encode($new_destination->taints);
- $new_sources[$key] = $new_destination;
- }
-
- return $new_sources;
- }
-
- /** @return array<int, DataFlowNode> */
- private function getSpecializedSources(DataFlowNode $source): array
- {
- $generated_sources = [];
-
- if (isset($this->forward_edges[$source->id])) {
- return [$source];
- }
-
- if ($source->specialization_key && isset($this->specialized_calls[$source->specialization_key])) {
- $generated_source = clone $source;
-
- $generated_source->id = substr($source->id, 0, -strlen($source->specialization_key) - 1);
-
- $generated_source->specialized_calls[$source->specialization_key][$generated_source->id] = true;
-
- $generated_sources[] = $generated_source;
- } elseif (isset($this->specializations[$source->id])) {
- foreach ($this->specializations[$source->id] as $specialization => $_) {
- if (!$source->specialized_calls || isset($source->specialized_calls[$specialization])) {
- $new_source = clone $source;
-
- $new_source->id = $source->id . '-' . $specialization;
-
- unset($new_source->specialized_calls[$specialization]);
-
- $generated_sources[] = $new_source;
- }
- }
- } else {
- foreach ($source->specialized_calls as $key => $map) {
- if (isset($map[$source->id]) && isset($this->forward_edges[$source->id . '-' . $key])) {
- $new_source = clone $source;
-
- $new_source->id = $source->id . '-' . $key;
-
- $generated_sources[] = $new_source;
- }
- }
- }
-
- return array_filter(
- $generated_sources,
- function ($new_source): bool {
- return isset($this->forward_edges[$new_source->id]);
- }
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/VariableUseGraph.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/VariableUseGraph.php
deleted file mode 100644
index f0bca35..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/VariableUseGraph.php
+++ /dev/null
@@ -1,224 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Codebase;
-
-use Psalm\CodeLocation;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Internal\DataFlow\Path;
-
-use function abs;
-use function array_merge;
-use function count;
-
-class VariableUseGraph extends DataFlowGraph
-{
- /** @var array<string, array<string, true>> */
- protected $backward_edges = [];
-
- /** @var array<string, DataFlowNode> */
- private $nodes = [];
-
- /** @var array<string, list<CodeLocation>> */
- private $origin_locations_by_id = [];
-
- public function addNode(DataFlowNode $node): void
- {
- $this->nodes[$node->id] = $node;
- }
-
- /**
- * @param array<string> $added_taints
- * @param array<string> $removed_taints
- */
- public function addPath(
- DataFlowNode $from,
- DataFlowNode $to,
- string $path_type,
- ?array $added_taints = null,
- ?array $removed_taints = null
- ): void {
- $from_id = $from->id;
- $to_id = $to->id;
-
- if ($from_id === $to_id) {
- return;
- }
-
- $length = 0;
-
- if ($from->code_location
- && $to->code_location
- && $from->code_location->file_path === $to->code_location->file_path
- ) {
- $to_line = $to->code_location->raw_line_number;
- $from_line = $from->code_location->raw_line_number;
- $length = abs($to_line - $from_line);
- }
-
- $this->backward_edges[$to_id][$from_id] = true;
- $this->forward_edges[$from_id][$to_id] = new Path($path_type, $length);
- }
-
- public function isVariableUsed(DataFlowNode $assignment_node): bool
- {
- $visited_source_ids = [];
-
- $sources = [$assignment_node];
-
- for ($i = 0; count($sources) && $i < 200; $i++) {
- $new_child_nodes = [];
-
- foreach ($sources as $source) {
- $visited_source_ids[$source->id] = true;
-
- $child_nodes = $this->getChildNodes(
- $source,
- $visited_source_ids
- );
-
- if ($child_nodes === null) {
- return true;
- }
-
- $new_child_nodes = array_merge(
- $new_child_nodes,
- $child_nodes
- );
- }
-
- $sources = $new_child_nodes;
- }
-
- return false;
- }
-
- /**
- * @return list<CodeLocation>
- */
- public function getOriginLocations(DataFlowNode $assignment_node): array
- {
- if (isset($this->origin_locations_by_id[$assignment_node->id])) {
- return $this->origin_locations_by_id[$assignment_node->id];
- }
-
- $visited_child_ids = [];
-
- $origin_locations = [];
-
- $child_nodes = [$assignment_node];
-
- for ($i = 0; count($child_nodes) && $i < 200; $i++) {
- $new_parent_nodes = [];
-
- foreach ($child_nodes as $child_node) {
- $visited_child_ids[$child_node->id] = true;
-
- $parent_nodes = $this->getParentNodes(
- $child_node,
- $visited_child_ids
- );
-
- if (!$parent_nodes) {
- if ($child_node->code_location) {
- $origin_locations[] = $child_node->code_location;
- }
-
- continue;
- }
-
- $new_parent_nodes = array_merge(
- $new_parent_nodes,
- $parent_nodes
- );
- }
-
- $child_nodes = $new_parent_nodes;
- }
-
- $this->origin_locations_by_id[$assignment_node->id] = $origin_locations;
-
- return $origin_locations;
- }
-
- /**
- * @param array<string, bool> $visited_source_ids
- * @return array<string, DataFlowNode>|null
- */
- private function getChildNodes(
- DataFlowNode $generated_source,
- array $visited_source_ids
- ): ?array {
- $new_child_nodes = [];
-
- if (!isset($this->forward_edges[$generated_source->id])) {
- return [];
- }
-
- foreach ($this->forward_edges[$generated_source->id] as $to_id => $path) {
- $path_type = $path->type;
-
- if ($path->type === 'variable-use'
- || $path->type === 'closure-use'
- || $path->type === 'global-use'
- || $path->type === 'use-inside-instance-property'
- || $path->type === 'use-inside-static-property'
- || $path->type === 'use-inside-call'
- || $path->type === 'use-inside-conditional'
- || $path->type === 'use-inside-isset'
- || $path->type === 'arg'
- ) {
- return null;
- }
-
- if (isset($visited_source_ids[$to_id])) {
- continue;
- }
-
- if (self::shouldIgnoreFetch($path_type, 'arraykey', $generated_source->path_types)) {
- continue;
- }
-
- if (self::shouldIgnoreFetch($path_type, 'arrayvalue', $generated_source->path_types)) {
- continue;
- }
-
- if (self::shouldIgnoreFetch($path_type, 'property', $generated_source->path_types)) {
- continue;
- }
-
- $new_destination = new DataFlowNode($to_id, $to_id, null);
- $new_destination->path_types = array_merge($generated_source->path_types, [$path_type]);
-
- $new_child_nodes[$to_id] = $new_destination;
- }
-
- return $new_child_nodes;
- }
-
- /**
- * @param array<string, bool> $visited_source_ids
- * @return list<DataFlowNode>
- */
- private function getParentNodes(
- DataFlowNode $destination,
- array $visited_source_ids
- ): array {
- $new_parent_nodes = [];
-
- if (!isset($this->backward_edges[$destination->id])) {
- return [];
- }
-
- foreach ($this->backward_edges[$destination->id] as $from_id => $_) {
- if (isset($visited_source_ids[$from_id])) {
- continue;
- }
-
- if (isset($this->nodes[$from_id])) {
- $new_parent_nodes[] = $this->nodes[$from_id];
- }
- }
-
- return $new_parent_nodes;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Composer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Composer.php
deleted file mode 100644
index 52ac935..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Composer.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use function basename;
-use function getenv;
-use function pathinfo;
-use function substr;
-use function trim;
-
-use const PATHINFO_EXTENSION;
-
-/**
- * @internal
- */
-final class Composer
-{
- /**
- * Retrieve the path to composer.json file.
- *
- * @see https://github.com/composer/composer/blob/5df1797d20c6ab1eb606dc0f0d76a16ba57ddb7f/src/Composer/Factory.php#L233
- */
- public static function getJsonFilePath(string $root): string
- {
- $file_name = getenv('COMPOSER') ?: 'composer.json';
- $file_name = basename(trim($file_name));
-
- return $root . '/' . $file_name;
- }
-
- /**
- * Retrieve the path to composer.lock file.
- *
- * @see https://github.com/composer/composer/blob/5df1797d20c6ab1eb606dc0f0d76a16ba57ddb7f/src/Composer/Factory.php#L238
- */
- public static function getLockFilePath(string $root): string
- {
- $composer_json_path = self::getJsonFilePath($root);
- return "json" === pathinfo($composer_json_path, PATHINFO_EXTENSION)
- ? substr($composer_json_path, 0, -4).'lock'
- : $composer_json_path . '.lock';
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/DataFlowNode.php b/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/DataFlowNode.php
deleted file mode 100644
index 39f4564..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/DataFlowNode.php
+++ /dev/null
@@ -1,137 +0,0 @@
-<?php
-
-namespace Psalm\Internal\DataFlow;
-
-use Psalm\CodeLocation;
-
-use function strtolower;
-
-/**
- * @psalm-consistent-constructor
- */
-class DataFlowNode
-{
- /** @var string */
- public $id;
-
- /** @var ?string */
- public $unspecialized_id;
-
- /** @var string */
- public $label;
-
- /** @var ?CodeLocation */
- public $code_location;
-
- /** @var ?string */
- public $specialization_key;
-
- /** @var array<string> */
- public $taints;
-
- /** @var ?self */
- public $previous;
-
- /** @var list<string> */
- public $path_types = [];
-
- /**
- * @var array<string, array<string, true>>
- */
- public $specialized_calls = [];
-
- /**
- * @param array<string> $taints
- */
- public function __construct(
- string $id,
- string $label,
- ?CodeLocation $code_location,
- ?string $specialization_key = null,
- array $taints = []
- ) {
- $this->id = $id;
-
- if ($specialization_key) {
- $this->unspecialized_id = $id;
- $this->id .= '-' . $specialization_key;
- }
-
- $this->label = $label;
- $this->code_location = $code_location;
- $this->specialization_key = $specialization_key;
- $this->taints = $taints;
- }
-
- /**
- * @return static
- */
- final public static function getForMethodArgument(
- string $method_id,
- string $cased_method_id,
- int $argument_offset,
- ?CodeLocation $arg_location,
- ?CodeLocation $code_location = null
- ): self {
- $arg_id = strtolower($method_id) . '#' . ($argument_offset + 1);
-
- $label = $cased_method_id . '#' . ($argument_offset + 1);
-
- $specialization_key = null;
-
- if ($code_location) {
- $specialization_key = strtolower($code_location->file_name) . ':' . $code_location->raw_file_start;
- }
-
- return new static(
- $arg_id,
- $label,
- $arg_location,
- $specialization_key
- );
- }
-
- /**
- * @return static
- */
- final public static function getForAssignment(
- string $var_id,
- CodeLocation $assignment_location,
- ?string $specialization_key = null
- ): self {
- $id = $var_id
- . '-' . $assignment_location->file_name
- . ':' . $assignment_location->raw_file_start
- . '-' . $assignment_location->raw_file_end;
-
- return new static($id, $var_id, $assignment_location, $specialization_key);
- }
-
- /**
- * @return static
- */
- final public static function getForMethodReturn(
- string $method_id,
- string $cased_method_id,
- ?CodeLocation $code_location,
- ?CodeLocation $function_location = null
- ): self {
- $specialization_key = null;
-
- if ($function_location) {
- $specialization_key = strtolower($function_location->file_name) . ':' . $function_location->raw_file_start;
- }
-
- return new static(
- strtolower($method_id),
- $cased_method_id,
- $code_location,
- $specialization_key
- );
- }
-
- public function __toString(): string
- {
- return $this->id;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/Path.php b/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/Path.php
deleted file mode 100644
index 05d120b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/Path.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-namespace Psalm\Internal\DataFlow;
-
-/**
- * @psalm-immutable
- */
-class Path
-{
- public $type;
-
- public $unescaped_taints;
-
- public $escaped_taints;
-
- public $length;
-
- /**
- * @param ?array<string> $unescaped_taints
- * @param ?array<string> $escaped_taints
- */
- public function __construct(
- string $type,
- int $length,
- ?array $unescaped_taints = null,
- ?array $escaped_taints = null
- ) {
- $this->type = $type;
- $this->length = $length;
- $this->unescaped_taints = $unescaped_taints;
- $this->escaped_taints = $escaped_taints;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSink.php b/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSink.php
deleted file mode 100644
index 408f13c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSink.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Psalm\Internal\DataFlow;
-
-class TaintSink extends DataFlowNode
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSource.php b/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSource.php
deleted file mode 100644
index c3b9acc..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/DataFlow/TaintSource.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Psalm\Internal\DataFlow;
-
-class TaintSource extends DataFlowNode
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/AstDiffer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Diff/AstDiffer.php
deleted file mode 100644
index b058845..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/AstDiffer.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Diff;
-
-use Closure;
-use Exception;
-use PhpParser\Node\Stmt;
-
-use function array_reverse;
-use function count;
-
-/**
- * Borrows from https://github.com/nikic/PHP-Parser/blob/master/lib/PhpParser/Internal/Differ.php
- *
- * Implements the Myers diff algorithm.
- *
- * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
- * Algorithmica 1.1 (1986): 251-266.
- *
- * @internal
- */
-class AstDiffer
-{
- /**
- * @param Closure(Stmt, Stmt, string, string, bool=): bool $is_equal
- * @param array<int, Stmt> $a
- * @param array<int, Stmt> $b
- *
- * @return array{0:non-empty-list<array<int, int>>, 1: int, 2: int, 3: array<int, bool>}
- */
- protected static function calculateTrace(
- Closure $is_equal,
- array $a,
- array $b,
- string $a_code,
- string $b_code
- ): array {
- $n = count($a);
- $m = count($b);
- $max = $n + $m;
- $v = [1 => 0];
- $bc = [];
- $trace = [];
- for ($d = 0; $d <= $max; ++$d) {
- $trace[] = $v;
- for ($k = -$d; $k <= $d; $k += 2) {
- if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
- $x = $v[$k + 1];
- } else {
- $x = $v[$k - 1] + 1;
- }
-
- $y = $x - $k;
-
- $body_change = false;
-
- while ($x < $n && $y < $m && ($is_equal)($a[$x], $b[$y], $a_code, $b_code, $body_change)) {
- $bc[$x] = $body_change;
- ++$x;
- ++$y;
-
- $body_change = false;
- }
-
- $v[$k] = $x;
- if ($x >= $n && $y >= $m) {
- return [$trace, $x, $y, $bc];
- }
- }
- }
- throw new Exception('Should not happen');
- }
-
- /**
- * @param array<int, array<int, int>> $trace
- * @param array<int, Stmt> $a
- * @param array<int, Stmt> $b
- * @param array<int, bool> $bc
- *
- * @return list<DiffElem>
- *
- * @psalm-pure
- */
- protected static function extractDiff(array $trace, int $x, int $y, array $a, array $b, array $bc): array
- {
- $result = [];
- for ($d = count($trace) - 1; $d >= 0; --$d) {
- $v = $trace[$d];
- $k = $x - $y;
-
- if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
- $prevK = $k + 1;
- } else {
- $prevK = $k - 1;
- }
-
- $prevX = $v[$prevK];
- $prevY = $prevX - $prevK;
-
- while ($x > $prevX && $y > $prevY) {
- $result[] = new DiffElem(
- $bc[$x - 1] ? DiffElem::TYPE_KEEP_SIGNATURE : DiffElem::TYPE_KEEP,
- $a[$x - 1],
- $b[$y - 1]
- );
- --$x;
- --$y;
- }
-
- if ($d === 0) {
- break;
- }
-
- while ($x > $prevX) {
- $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x - 1], null);
- --$x;
- }
-
- while ($y > $prevY) {
- $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y - 1]);
- --$y;
- }
- }
-
- return array_reverse($result);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/ClassStatementsDiffer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Diff/ClassStatementsDiffer.php
deleted file mode 100644
index fab907d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/ClassStatementsDiffer.php
+++ /dev/null
@@ -1,251 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Diff;
-
-use PhpParser;
-
-use function count;
-use function get_class;
-use function strpos;
-use function strtolower;
-use function substr;
-use function trim;
-
-/**
- * @internal
- */
-class ClassStatementsDiffer extends AstDiffer
-{
- /**
- * Calculate diff (edit script) from $a to $b.
- *
- * @param array<int, PhpParser\Node\Stmt> $a
- * @param array<int, PhpParser\Node\Stmt> $b
- *
- * @return array{
- * 0: list<string>,
- * 1: list<string>,
- * 2: list<string>,
- * 3: array<int, array{int, int, int, int}>,
- * 4: list<array{int, int}>
- * }
- */
- public static function diff(string $name, array $a, array $b, string $a_code, string $b_code): array
- {
- $diff_map = [];
-
- [$trace, $x, $y, $bc] = self::calculateTrace(
- function (
- PhpParser\Node\Stmt $a,
- PhpParser\Node\Stmt $b,
- string $a_code,
- string $b_code,
- bool &$body_change = false
- ) use (&$diff_map): bool {
- if (get_class($a) !== get_class($b)) {
- return false;
- }
-
- $a_start = (int)$a->getAttribute('startFilePos');
- $a_end = (int)$a->getAttribute('endFilePos');
-
- $b_start = (int)$b->getAttribute('startFilePos');
- $b_end = (int)$b->getAttribute('endFilePos');
-
- $a_comments_end = $a_start;
- $b_comments_end = $b_start;
-
- /** @var list<PhpParser\Comment> */
- $a_comments = $a->getComments();
- /** @var list<PhpParser\Comment> */
- $b_comments = $b->getComments();
-
- $signature_change = false;
- $body_change = false;
-
- if ($a_comments) {
- if (!$b_comments) {
- $signature_change = true;
- }
-
- $a_start = $a_comments[0]->getStartFilePos();
- }
-
- if ($b_comments) {
- if (!$a_comments) {
- $signature_change = true;
- }
-
- $b_start = $b_comments[0]->getStartFilePos();
- }
-
- $a_size = $a_end - $a_start;
- $b_size = $b_end - $b_start;
-
- if ($a_size === $b_size
- && substr($a_code, $a_start, $a_size) === substr($b_code, $b_start, $b_size)
- ) {
- $start_diff = $b_start - $a_start;
- $line_diff = $b->getLine() - $a->getLine();
-
- /** @psalm-suppress MixedArrayAssignment */
- $diff_map[] = [$a_start, $a_end, $start_diff, $line_diff];
-
- return true;
- }
-
- if (!$signature_change
- && substr($a_code, $a_start, $a_comments_end - $a_start)
- !== substr($b_code, $b_start, $b_comments_end - $b_start)
- ) {
- $signature_change = true;
- }
-
- if ($a instanceof PhpParser\Node\Stmt\ClassMethod && $b instanceof PhpParser\Node\Stmt\ClassMethod) {
- if ((string) $a->name !== (string) $b->name) {
- return false;
- }
-
- if ($a->stmts) {
- $first_stmt = $a->stmts[0];
- $a_stmts_start = (int) $first_stmt->getAttribute('startFilePos');
-
- if ($a_stmt_comments = $first_stmt->getComments()) {
- $a_stmts_start = $a_stmt_comments[0]->getStartFilePos();
- }
- } else {
- $a_stmts_start = $a_end;
- }
-
- if ($b->stmts) {
- $first_stmt = $b->stmts[0];
- $b_stmts_start = (int) $first_stmt->getAttribute('startFilePos');
-
- if ($b_stmt_comments = $first_stmt->getComments()) {
- $b_stmts_start = $b_stmt_comments[0]->getStartFilePos();
- }
- } else {
- $b_stmts_start = $b_end;
- }
-
- $a_body_size = $a_end - $a_stmts_start;
- $b_body_size = $b_end - $b_stmts_start;
-
- $body_change = $a_body_size !== $b_body_size
- || substr($a_code, $a_stmts_start, $a_end - $a_stmts_start)
- !== substr($b_code, $b_stmts_start, $b_end - $b_stmts_start);
-
- if (!$signature_change) {
- $a_signature = substr($a_code, $a_start, $a_stmts_start - $a_start);
- $b_signature = substr($b_code, $b_start, $b_stmts_start - $b_start);
-
- if ($a_signature !== $b_signature) {
- $a_signature = trim($a_signature);
- $b_signature = trim($b_signature);
-
- if (strpos($a_signature, $b_signature) === false
- && strpos($b_signature, $a_signature) === false
- ) {
- $signature_change = true;
- }
- }
- }
- } elseif ($a instanceof PhpParser\Node\Stmt\Property && $b instanceof PhpParser\Node\Stmt\Property) {
- if (count($a->props) !== 1 || count($b->props) !== 1) {
- return false;
- }
-
- if ((string) $a->props[0]->name !== (string) $b->props[0]->name || $a->flags !== $b->flags) {
- return false;
- }
-
- $body_change = substr($a_code, $a_comments_end, $a_end - $a_comments_end)
- !== substr($b_code, $b_comments_end, $b_end - $b_comments_end);
- } else {
- $signature_change = true;
- }
-
- if (!$signature_change && !$body_change) {
- /** @psalm-suppress MixedArrayAssignment */
- $diff_map[] = [$a_start, $a_end, $b_start - $a_start, $b->getLine() - $a->getLine()];
- }
-
- return !$signature_change;
- },
- $a,
- $b,
- $a_code,
- $b_code
- );
-
- $diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
-
- $keep = [];
- $keep_signature = [];
- $add_or_delete = [];
- $deletion_ranges = [];
-
- $name_lc = strtolower($name);
- foreach ($diff as $diff_elem) {
- if ($diff_elem->type === DiffElem::TYPE_KEEP) {
- if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
- $keep[] = $name_lc . '::' . strtolower((string) $diff_elem->old->name);
- } elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
- foreach ($diff_elem->old->props as $prop) {
- $keep[] = $name_lc . '::$' . $prop->name;
- }
- } elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassConst) {
- foreach ($diff_elem->old->consts as $const) {
- $keep[] = $name_lc . '::' . $const->name;
- }
- } elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\TraitUse) {
- foreach ($diff_elem->old->traits as $trait) {
- $keep[] = $name_lc . '&' . strtolower((string) $trait->getAttribute('resolvedName'));
- }
- }
- } elseif ($diff_elem->type === DiffElem::TYPE_KEEP_SIGNATURE) {
- if ($diff_elem->old instanceof PhpParser\Node\Stmt\ClassMethod) {
- $keep_signature[] = $name_lc . '::' . strtolower((string) $diff_elem->old->name);
- } elseif ($diff_elem->old instanceof PhpParser\Node\Stmt\Property) {
- foreach ($diff_elem->old->props as $prop) {
- $keep_signature[] = $name_lc . '::$' . $prop->name;
- }
- }
- } elseif ($diff_elem->type === DiffElem::TYPE_REMOVE || $diff_elem->type === DiffElem::TYPE_ADD) {
- /** @var PhpParser\Node */
- $affected_elem = $diff_elem->type === DiffElem::TYPE_REMOVE ? $diff_elem->old : $diff_elem->new;
- if ($affected_elem instanceof PhpParser\Node\Stmt\ClassMethod) {
- $add_or_delete[] = $name_lc . '::' . strtolower((string) $affected_elem->name);
- } elseif ($affected_elem instanceof PhpParser\Node\Stmt\Property) {
- foreach ($affected_elem->props as $prop) {
- $add_or_delete[] = $name_lc . '::$' . $prop->name;
- }
- } elseif ($affected_elem instanceof PhpParser\Node\Stmt\ClassConst) {
- foreach ($affected_elem->consts as $const) {
- $add_or_delete[] = $name_lc . '::' . $const->name;
- }
- } elseif ($affected_elem instanceof PhpParser\Node\Stmt\TraitUse) {
- foreach ($affected_elem->traits as $trait) {
- $add_or_delete[] = $name_lc . '&' . strtolower((string) $trait->getAttribute('resolvedName'));
- }
- }
-
- if ($diff_elem->type === DiffElem::TYPE_REMOVE) {
- if ($doc = $affected_elem->getDocComment()) {
- $start = $doc->getStartFilePos();
- } else {
- $start = (int)$affected_elem->getAttribute('startFilePos');
- }
-
- $deletion_ranges[] = [
- $start,
- (int)$affected_elem->getAttribute('endFilePos')
- ];
- }
- }
- }
-
- /** @var array<int, array{int, int, int, int}> $diff_map */
- return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/DiffElem.php b/vendor/vimeo/psalm/src/Psalm/Internal/Diff/DiffElem.php
deleted file mode 100644
index d8ac0d2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/DiffElem.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Diff;
-
-/**
- * @internal
- *
- * @psalm-immutable
- */
-class DiffElem
-{
- public const TYPE_KEEP = 0;
- public const TYPE_REMOVE = 1;
- public const TYPE_ADD = 2;
- public const TYPE_REPLACE = 3;
- public const TYPE_KEEP_SIGNATURE = 4;
-
- /** @var int One of the TYPE_* constants */
- public $type;
- /** @var mixed Is null for add operations */
- public $old;
- /** @var mixed Is null for remove operations */
- public $new;
-
- /**
- * @param mixed $old
- * @param mixed $new
- */
- public function __construct(int $type, $old, $new)
- {
- $this->type = $type;
- $this->old = $old;
- $this->new = $new;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileDiffer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileDiffer.php
deleted file mode 100644
index c50d446..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileDiffer.php
+++ /dev/null
@@ -1,319 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Diff;
-
-use Exception;
-
-use function array_reverse;
-use function count;
-use function explode;
-use function min;
-use function strlen;
-use function substr;
-
-/**
- * Borrows from https://github.com/nikic/PHP-Parser/blob/master/lib/PhpParser/Internal/Differ.php
- *
- * Implements the Myers diff algorithm.
- *
- * Myers, Eugene W. "An O (ND) difference algorithm and its variations."
- * Algorithmica 1.1 (1986): 251-266.
- *
- * @internal
- */
-class FileDiffer
-{
- /**
- * @param list<string> $a
- * @param list<string> $b
- *
- * @return array{0:non-empty-list<array<int, int>>, 1: int, 2: int}
- *
- * @psalm-pure
- */
- private static function calculateTrace(
- array $a,
- array $b
- ): array {
- $n = count($a);
- $m = count($b);
- $max = $n + $m;
- $v = [1 => 0];
- $trace = [];
- for ($d = 0; $d <= $max; ++$d) {
- $trace[] = $v;
- for ($k = -$d; $k <= $d; $k += 2) {
- if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
- $x = $v[$k + 1];
- } else {
- $x = $v[$k - 1] + 1;
- }
-
- $y = $x - $k;
-
- while ($x < $n && $y < $m && $a[$x] === $b[$y]) {
- ++$x;
- ++$y;
- }
-
- $v[$k] = $x;
- if ($x >= $n && $y >= $m) {
- return [$trace, $x, $y];
- }
- }
- }
- throw new Exception('Should not happen');
- }
-
- /**
- * @param list<array<int, int>> $trace
- * @param list<string> $a
- * @param list<string> $b
- *
- * @return list<DiffElem>
- *
- * @psalm-pure
- */
- private static function extractDiff(array $trace, int $x, int $y, array $a, array $b): array
- {
- $result = [];
- for ($d = count($trace) - 1; $d >= 0; --$d) {
- $v = $trace[$d];
- $k = $x - $y;
-
- if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) {
- $prevK = $k + 1;
- } else {
- $prevK = $k - 1;
- }
-
- $prevX = $v[$prevK];
- $prevY = $prevX - $prevK;
-
- while ($x > $prevX && $y > $prevY) {
- $result[] = new DiffElem(
- DiffElem::TYPE_KEEP,
- $a[$x - 1],
- $b[$y - 1]
- );
- --$x;
- --$y;
- }
-
- if ($d === 0) {
- break;
- }
-
- while ($x > $prevX) {
- $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $a[$x - 1], null);
- --$x;
- }
-
- while ($y > $prevY) {
- $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $b[$y - 1]);
- --$y;
- }
- }
-
- return array_reverse($result);
- }
-
- /**
- * @return array<int, array{0: int, 1: int, 2: int, 3: int, 4: int, 5: string}>
- *
- * @psalm-pure
- */
- public static function getDiff(string $a_code, string $b_code): array
- {
- $a = explode("\n", $a_code);
- $b = explode("\n", $b_code);
- [$trace, $x, $y] = self::calculateTrace($a, $b);
-
- $diff = self::coalesceReplacements(self::extractDiff($trace, $x, $y, $a, $b));
-
- $a_offset = 0;
-
- $b_offset = 0;
-
- $last_diff_type = null;
-
- /** @var array{0:int, 1:int, 2:int, 3:int, 4:int, 5:string}|null */
- $last_change = null;
-
- $changes = [];
- $i = 0;
- $line_diff = 0;
-
- foreach ($diff as $diff_elem) {
- $diff_type = $diff_elem->type;
-
- if ($diff_type !== $last_diff_type) {
- $last_change = null;
- }
-
- if ($diff_type === DiffElem::TYPE_REMOVE) {
- /** @var string $diff_elem->old */
- $diff_text = $diff_elem->old . "\n";
-
- $text_length = strlen($diff_text);
-
- --$line_diff;
-
- if ($last_change === null) {
- ++$i;
- $last_change = [
- $a_offset,
- $a_offset + $text_length,
- $b_offset,
- $b_offset,
- $line_diff,
- '',
- ];
- $changes[$i - 1] = $last_change;
- } else {
- $last_change[1] += $text_length;
- $last_change[4] = $line_diff;
- $changes[$i - 1] = $last_change;
- }
-
- $a_offset += $text_length;
- } elseif ($diff_type === DiffElem::TYPE_ADD) {
- /** @var string $diff_elem->new */
- $diff_text = $diff_elem->new . "\n";
-
- $text_length = strlen($diff_text);
-
- ++$line_diff;
-
- if ($last_change === null) {
- ++$i;
- $last_change = [
- $a_offset,
- $a_offset,
- $b_offset,
- $b_offset + $text_length,
- $line_diff,
- $diff_text,
- ];
- $changes[$i - 1] = $last_change;
- } else {
- $last_change[3] += $text_length;
- $last_change[4] = $line_diff;
- $last_change[5] .= $diff_text;
-
- $changes[$i - 1] = $last_change;
- }
-
- $b_offset += $text_length;
- } elseif ($diff_type === DiffElem::TYPE_REPLACE) {
- /** @var string $diff_elem->old */
- $old_diff_text = $diff_elem->old . "\n";
-
- /** @var string $diff_elem->new */
- $new_diff_text = $diff_elem->new . "\n";
-
- $old_text_length = strlen($old_diff_text);
- $new_text_length = strlen($new_diff_text);
-
- $max_same_count = min($old_text_length, $new_text_length);
-
- for ($j = 0; $j < $max_same_count; ++$j) {
- if ($old_diff_text[$j] !== $new_diff_text[$j]) {
- break;
- }
-
- ++$a_offset;
- ++$b_offset;
- --$old_text_length;
- --$new_text_length;
- }
-
- $new_diff_text = substr($new_diff_text, $j);
-
- if ($last_change === null || $j) {
- ++$i;
- $last_change = [
- $a_offset,
- $a_offset + $old_text_length,
- $b_offset,
- $b_offset + $new_text_length,
- $line_diff,
- $new_diff_text,
- ];
- $changes[$i - 1] = $last_change;
- } else {
- $last_change[1] += $old_text_length;
- $last_change[3] += $new_text_length;
- $last_change[5] .= $new_diff_text;
- $changes[$i - 1] = $last_change;
- }
-
- $a_offset += $old_text_length;
- $b_offset += $new_text_length;
- } else {
- /** @psalm-suppress MixedArgument */
- $same_text_length = strlen($diff_elem->new) + 1;
-
- $a_offset += $same_text_length;
- $b_offset += $same_text_length;
- }
-
- $last_diff_type = $diff_elem->type;
- }
-
- return $changes;
- }
-
- /**
- * Coalesce equal-length sequences of remove+add into a replace operation.
- *
- * @param DiffElem[] $diff
- *
- * @return list<DiffElem>
- *
- * @psalm-pure
- */
- private static function coalesceReplacements(array $diff): array
- {
- $newDiff = [];
- $c = count($diff);
- for ($i = 0; $i < $c; ++$i) {
- $diffType = $diff[$i]->type;
- if ($diffType !== DiffElem::TYPE_REMOVE) {
- $newDiff[] = $diff[$i];
- continue;
- }
-
- $j = $i;
- while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) {
- ++$j;
- }
-
- $k = $j;
- while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) {
- ++$k;
- }
-
- if ($j - $i === $k - $j) {
- $len = $j - $i;
- for ($n = 0; $n < $len; ++$n) {
- $newDiff[] = new DiffElem(
- DiffElem::TYPE_REPLACE,
- $diff[$i + $n]->old,
- $diff[$j + $n]->new
- );
- }
- } else {
- for (; $i < $k; ++$i) {
- $newDiff[] = $diff[$i];
- }
- }
-
- $i = $k - 1;
- }
-
- return $newDiff;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileStatementsDiffer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileStatementsDiffer.php
deleted file mode 100644
index 9580d7f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/FileStatementsDiffer.php
+++ /dev/null
@@ -1,172 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Diff;
-
-use PhpParser;
-
-use function array_merge;
-use function end;
-use function get_class;
-use function substr;
-
-/**
- * @internal
- */
-class FileStatementsDiffer extends AstDiffer
-{
- /**
- * Calculate diff (edit script) from $a to $b.
- * @param list<PhpParser\Node\Stmt> $a
- * @param list<PhpParser\Node\Stmt> $b
- *
- * @return array{
- * 0: list<string>,
- * 1: list<string>,
- * 2: list<string>,
- * 3: list<array{int, int, int, int}>,
- * 4: list<array{int, int}>
- * }
- */
- public static function diff(array $a, array $b, string $a_code, string $b_code): array
- {
- [$trace, $x, $y, $bc] = self::calculateTrace(
- function (
- PhpParser\Node\Stmt $a,
- PhpParser\Node\Stmt $b,
- string $a_code,
- string $b_code,
- bool &$body_change = false
- ): bool {
- if (get_class($a) !== get_class($b)) {
- return false;
- }
-
- if (($a instanceof PhpParser\Node\Stmt\Namespace_ && $b instanceof PhpParser\Node\Stmt\Namespace_)
- || ($a instanceof PhpParser\Node\Stmt\Class_ && $b instanceof PhpParser\Node\Stmt\Class_)
- || ($a instanceof PhpParser\Node\Stmt\Interface_ && $b instanceof PhpParser\Node\Stmt\Interface_)
- || ($a instanceof PhpParser\Node\Stmt\Trait_ && $b instanceof PhpParser\Node\Stmt\Trait_)
- ) {
- return (string)$a->name === (string)$b->name;
- }
-
- if (($a instanceof PhpParser\Node\Stmt\Use_
- && $b instanceof PhpParser\Node\Stmt\Use_)
- || ($a instanceof PhpParser\Node\Stmt\GroupUse
- && $b instanceof PhpParser\Node\Stmt\GroupUse)
- ) {
- $a_start = (int)$a->getAttribute('startFilePos');
- $a_end = (int)$a->getAttribute('endFilePos');
-
- $b_start = (int)$b->getAttribute('startFilePos');
- $b_end = (int)$b->getAttribute('endFilePos');
-
- $a_size = $a_end - $a_start;
- $b_size = $b_end - $b_start;
-
- if (substr($a_code, $a_start, $a_size) === substr($b_code, $b_start, $b_size)) {
- return true;
- }
- }
-
- return false;
- },
- $a,
- $b,
- $a_code,
- $b_code
- );
-
- $diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
-
- $keep = [];
- $keep_signature = [];
- $add_or_delete = [];
- $diff_map = [];
- $deletion_ranges = [];
-
- foreach ($diff as $diff_elem) {
- if ($diff_elem->type === DiffElem::TYPE_KEEP) {
- if ($diff_elem->old instanceof PhpParser\Node\Stmt\Namespace_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Namespace_
- ) {
- $namespace_keep = NamespaceStatementsDiffer::diff(
- (string) $diff_elem->old->name,
- $diff_elem->old->stmts,
- $diff_elem->new->stmts,
- $a_code,
- $b_code
- );
-
- $keep = array_merge($keep, $namespace_keep[0]);
- $keep_signature = array_merge($keep_signature, $namespace_keep[1]);
- $add_or_delete = array_merge($add_or_delete, $namespace_keep[2]);
- $diff_map = array_merge($diff_map, $namespace_keep[3]);
- $deletion_ranges = array_merge($deletion_ranges, $namespace_keep[4]);
- } elseif (($diff_elem->old instanceof PhpParser\Node\Stmt\Class_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Class_)
- || ($diff_elem->old instanceof PhpParser\Node\Stmt\Interface_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Interface_)
- || ($diff_elem->old instanceof PhpParser\Node\Stmt\Trait_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Trait_)
- ) {
- $class_keep = ClassStatementsDiffer::diff(
- (string) $diff_elem->old->name,
- $diff_elem->old->stmts,
- $diff_elem->new->stmts,
- $a_code,
- $b_code
- );
-
- $keep = array_merge($keep, $class_keep[0]);
- $keep_signature = array_merge($keep_signature, $class_keep[1]);
- $add_or_delete = array_merge($add_or_delete, $class_keep[2]);
- $diff_map = array_merge($diff_map, $class_keep[3]);
- $deletion_ranges = array_merge($deletion_ranges, $class_keep[4]);
- }
- } elseif ($diff_elem->type === DiffElem::TYPE_REMOVE) {
- if ($diff_elem->old instanceof PhpParser\Node\Stmt\Use_
- || $diff_elem->old instanceof PhpParser\Node\Stmt\GroupUse
- ) {
- foreach ($diff_elem->old->uses as $use) {
- if ($use->alias) {
- $add_or_delete[] = 'use:' . (string) $use->alias;
- } else {
- $name_parts = $use->name->parts;
-
- $add_or_delete[] = 'use:' . end($name_parts);
- }
- }
- } elseif ($diff_elem->old instanceof PhpParser\Node
- && !$diff_elem->old instanceof PhpParser\Node\Stmt\Namespace_
- ) {
- if ($doc = $diff_elem->old->getDocComment()) {
- $start = $doc->getStartFilePos();
- } else {
- $start = (int)$diff_elem->old->getAttribute('startFilePos');
- }
-
- $deletion_ranges[] = [
- $start,
- (int)$diff_elem->old->getAttribute('endFilePos')
- ];
- }
- } elseif ($diff_elem->type === DiffElem::TYPE_ADD) {
- if ($diff_elem->new instanceof PhpParser\Node\Stmt\Use_
- || $diff_elem->new instanceof PhpParser\Node\Stmt\GroupUse
- ) {
- foreach ($diff_elem->new->uses as $use) {
- if ($use->alias) {
- $add_or_delete[] = 'use:' . (string) $use->alias;
- } else {
- $name_parts = $use->name->parts;
-
- $add_or_delete[] = 'use:' . end($name_parts);
- }
- }
- }
- }
- }
-
- return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php
deleted file mode 100644
index 5ac1e8a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Diff/NamespaceStatementsDiffer.php
+++ /dev/null
@@ -1,144 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Diff;
-
-use PhpParser;
-
-use function array_merge;
-use function end;
-use function get_class;
-use function substr;
-
-/**
- * @internal
- */
-class NamespaceStatementsDiffer extends AstDiffer
-{
- /**
- * Calculate diff (edit script) from $a to $b.
- * @param array<int, PhpParser\Node\Stmt> $a
- * @param array<int, PhpParser\Node\Stmt> $b
- *
- * @return array{
- * 0: list<string>,
- * 1: list<string>,
- * 2: list<string>,
- * 3: list<array{int, int, int, int}>,
- * 4: list<array{int, int}>
- * }
- */
- public static function diff(string $name, array $a, array $b, string $a_code, string $b_code): array
- {
- [$trace, $x, $y, $bc] = self::calculateTrace(
- function (
- PhpParser\Node\Stmt $a,
- PhpParser\Node\Stmt $b,
- string $a_code,
- string $b_code,
- bool &$body_change = false
- ): bool {
- if (get_class($a) !== get_class($b)) {
- return false;
- }
-
- if (($a instanceof PhpParser\Node\Stmt\Class_ && $b instanceof PhpParser\Node\Stmt\Class_)
- || ($a instanceof PhpParser\Node\Stmt\Interface_ && $b instanceof PhpParser\Node\Stmt\Interface_)
- || ($a instanceof PhpParser\Node\Stmt\Trait_ && $b instanceof PhpParser\Node\Stmt\Trait_)
- ) {
- // @todo add check for comments comparison
-
- return (string)$a->name === (string)$b->name;
- }
-
- if (($a instanceof PhpParser\Node\Stmt\Use_
- && $b instanceof PhpParser\Node\Stmt\Use_)
- || ($a instanceof PhpParser\Node\Stmt\GroupUse
- && $b instanceof PhpParser\Node\Stmt\GroupUse)
- ) {
- $a_start = (int)$a->getAttribute('startFilePos');
- $a_end = (int)$a->getAttribute('endFilePos');
-
- $b_start = (int)$b->getAttribute('startFilePos');
- $b_end = (int)$b->getAttribute('endFilePos');
-
- $a_size = $a_end - $a_start;
- $b_size = $b_end - $b_start;
-
- if (substr($a_code, $a_start, $a_size) === substr($b_code, $b_start, $b_size)) {
- return true;
- }
- }
-
- return false;
- },
- $a,
- $b,
- $a_code,
- $b_code
- );
-
- $diff = self::extractDiff($trace, $x, $y, $a, $b, $bc);
-
- $keep = [];
- $keep_signature = [];
- $add_or_delete = [];
- $diff_map = [];
- $deletion_ranges = [];
-
- foreach ($diff as $diff_elem) {
- if ($diff_elem->type === DiffElem::TYPE_KEEP) {
- if (($diff_elem->old instanceof PhpParser\Node\Stmt\Class_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Class_)
- || ($diff_elem->old instanceof PhpParser\Node\Stmt\Interface_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Interface_)
- || ($diff_elem->old instanceof PhpParser\Node\Stmt\Trait_
- && $diff_elem->new instanceof PhpParser\Node\Stmt\Trait_)
- ) {
- $class_keep = ClassStatementsDiffer::diff(
- ($name ? $name . '\\' : '') . $diff_elem->old->name,
- $diff_elem->old->stmts,
- $diff_elem->new->stmts,
- $a_code,
- $b_code
- );
-
- $keep = array_merge($keep, $class_keep[0]);
- $keep_signature = array_merge($keep_signature, $class_keep[1]);
- $add_or_delete = array_merge($add_or_delete, $class_keep[2]);
- $diff_map = array_merge($diff_map, $class_keep[3]);
- $deletion_ranges = array_merge($deletion_ranges, $class_keep[4]);
- }
- } elseif ($diff_elem->type === DiffElem::TYPE_REMOVE) {
- if ($diff_elem->old instanceof PhpParser\Node\Stmt\Use_
- || $diff_elem->old instanceof PhpParser\Node\Stmt\GroupUse
- ) {
- foreach ($diff_elem->old->uses as $use) {
- if ($use->alias) {
- $add_or_delete[] = 'use:' . (string) $use->alias;
- } else {
- $name_parts = $use->name->parts;
-
- $add_or_delete[] = 'use:' . end($name_parts);
- }
- }
- }
- } elseif ($diff_elem->type === DiffElem::TYPE_ADD) {
- if ($diff_elem->new instanceof PhpParser\Node\Stmt\Use_
- || $diff_elem->new instanceof PhpParser\Node\Stmt\GroupUse
- ) {
- foreach ($diff_elem->new->uses as $use) {
- if ($use->alias) {
- $add_or_delete[] = 'use:' . (string) $use->alias;
- } else {
- $name_parts = $use->name->parts;
-
- $add_or_delete[] = 'use:' . end($name_parts);
- }
- }
- }
- }
- }
-
- return [$keep, $keep_signature, $add_or_delete, $diff_map, $deletion_ranges];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/ErrorHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/ErrorHandler.php
deleted file mode 100644
index 802f472..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/ErrorHandler.php
+++ /dev/null
@@ -1,90 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use RuntimeException;
-use Throwable;
-
-use function defined;
-use function error_reporting;
-use function fwrite;
-use function ini_set;
-use function set_error_handler;
-use function set_exception_handler;
-
-use const E_ALL;
-use const E_STRICT;
-use const STDERR;
-
-final class ErrorHandler
-{
- /** @var bool */
- private static $exceptions_enabled = true;
-
- public static function install(): void
- {
- self::setErrorReporting();
- self::installErrorHandler();
- self::installExceptionHandler();
- }
-
- /**
- * @template T
- * @param callable():T $f
- * @return T
- */
- public static function runWithExceptionsSuppressed(callable $f)
- {
- try {
- self::$exceptions_enabled = false;
- return $f();
- } finally {
- self::$exceptions_enabled = true;
- }
- }
-
- /** @psalm-suppress UnusedConstructor added to prevent instantiations */
- private function __construct()
- {
- }
-
- private static function setErrorReporting(): void
- {
- error_reporting(E_ALL | E_STRICT);
- ini_set('display_errors', '1');
- }
-
- private static function installErrorHandler(): void
- {
- set_error_handler(static function (
- int $error_code,
- string $error_message,
- string $error_filename = 'unknown',
- int $error_line = -1
- ): bool {
- if (ErrorHandler::$exceptions_enabled && ($error_code & error_reporting())) {
- throw new RuntimeException(
- 'PHP Error: ' . $error_message . ' in ' . $error_filename . ':' . $error_line,
- $error_code
- );
- }
- // let PHP handle suppressed errors how it sees fit
- return false;
- });
- }
-
- private static function installExceptionHandler(): void
- {
- /**
- * If there is an uncaught exception,
- * then print more of the backtrace than is done by default to stderr,
- * then exit with a non-zero exit code to indicate failure.
- */
- set_exception_handler(static function (Throwable $throwable): void {
- fwrite(STDERR, "Uncaught $throwable\n");
- $version = defined('PSALM_VERSION') ? PSALM_VERSION : '(unknown version)';
- fwrite(STDERR, "(Psalm $version crashed due to an uncaught Throwable)\n");
- exit(1);
- });
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php b/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php
deleted file mode 100644
index c7d2265..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php
+++ /dev/null
@@ -1,621 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use Psalm\Plugin\EventHandler\AddTaintsInterface;
-use Psalm\Plugin\EventHandler\AfterAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterClassLikeAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterClassLikeExistenceCheckInterface;
-use Psalm\Plugin\EventHandler\AfterClassLikeVisitInterface;
-use Psalm\Plugin\EventHandler\AfterCodebasePopulatedInterface;
-use Psalm\Plugin\EventHandler\AfterEveryFunctionCallAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterExpressionAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterFileAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterFunctionCallAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterFunctionLikeAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterMethodCallAnalysisInterface;
-use Psalm\Plugin\EventHandler\AfterStatementAnalysisInterface;
-use Psalm\Plugin\EventHandler\BeforeFileAnalysisInterface;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Plugin\EventHandler\Event\AfterAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterClassLikeAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterClassLikeExistenceCheckEvent;
-use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
-use Psalm\Plugin\EventHandler\Event\AfterCodebasePopulatedEvent;
-use Psalm\Plugin\EventHandler\Event\AfterEveryFunctionCallAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterExpressionAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterFileAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterFunctionCallAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterFunctionLikeAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\BeforeFileAnalysisEvent;
-use Psalm\Plugin\EventHandler\Event\StringInterpreterEvent;
-use Psalm\Plugin\EventHandler\RemoveTaintsInterface;
-use Psalm\Plugin\EventHandler\StringInterpreterInterface;
-use Psalm\Plugin\Hook\AfterAnalysisInterface as LegacyAfterAnalysisInterface;
-use Psalm\Plugin\Hook\AfterClassLikeAnalysisInterface as LegacyAfterClassLikeAnalysisInterface;
-use Psalm\Plugin\Hook\AfterClassLikeExistenceCheckInterface as LegacyAfterClassLikeExistenceCheckInterface;
-use Psalm\Plugin\Hook\AfterClassLikeVisitInterface as LegacyAfterClassLikeVisitInterface;
-use Psalm\Plugin\Hook\AfterCodebasePopulatedInterface as LegacyAfterCodebasePopulatedInterface;
-use Psalm\Plugin\Hook\AfterEveryFunctionCallAnalysisInterface as LegacyAfterEveryFunctionCallAnalysisInterface;
-use Psalm\Plugin\Hook\AfterExpressionAnalysisInterface as LegacyAfterExpressionAnalysisInterface;
-use Psalm\Plugin\Hook\AfterFileAnalysisInterface as LegacyAfterFileAnalysisInterface;
-use Psalm\Plugin\Hook\AfterFunctionCallAnalysisInterface as LegacyAfterFunctionCallAnalysisInterface;
-use Psalm\Plugin\Hook\AfterFunctionLikeAnalysisInterface as LegacyAfterFunctionLikeAnalysisInterface;
-use Psalm\Plugin\Hook\AfterMethodCallAnalysisInterface as LegacyAfterMethodCallAnalysisInterface;
-use Psalm\Plugin\Hook\AfterStatementAnalysisInterface as LegacyAfterStatementAnalysisInterface;
-use Psalm\Plugin\Hook\BeforeFileAnalysisInterface as LegacyBeforeFileAnalysisInterface;
-use Psalm\Plugin\Hook\StringInterpreterInterface as LegacyStringInterpreterInterface;
-use Psalm\Type\Atomic\TLiteralString;
-
-use function array_merge;
-use function count;
-use function is_subclass_of;
-
-class EventDispatcher
-{
- /**
- * Static methods to be called after method checks have completed
- *
- * @var list<class-string<AfterMethodCallAnalysisInterface>>
- */
- private $after_method_checks = [];
- /** @var list<class-string<LegacyAfterMethodCallAnalysisInterface>> */
- private $legacy_after_method_checks = [];
-
- /**
- * Static methods to be called after project function checks have completed
- *
- * Called after function calls to functions defined in the project.
- *
- * Allows influencing the return type and adding of modifications.
- *
- * @var list<class-string<AfterFunctionCallAnalysisInterface>>
- */
- public $after_function_checks = [];
- /** @var list<class-string<LegacyAfterFunctionCallAnalysisInterface>> */
- public $legacy_after_function_checks = [];
-
- /**
- * Static methods to be called after every function call
- *
- * Called after each function call, including php internal functions.
- *
- * Cannot change the call or influence its return type
- *
- * @var list<class-string<AfterEveryFunctionCallAnalysisInterface>>
- */
- public $after_every_function_checks = [];
- /** @var list<class-string<LegacyAfterEveryFunctionCallAnalysisInterface>> */
- public $legacy_after_every_function_checks = [];
-
- /**
- * Static methods to be called after expression checks have completed
- *
- * @var list<class-string<AfterExpressionAnalysisInterface>>
- */
- public $after_expression_checks = [];
- /** @var list<class-string<LegacyAfterExpressionAnalysisInterface>> */
- public $legacy_after_expression_checks = [];
-
- /**
- * Static methods to be called after statement checks have completed
- *
- * @var list<class-string<AfterStatementAnalysisInterface>>
- */
- public $after_statement_checks = [];
- /** @var list<class-string<LegacyAfterStatementAnalysisInterface>> */
- public $legacy_after_statement_checks = [];
-
- /**
- * Static methods to be called after method checks have completed
- *
- * @var list<class-string<StringInterpreterInterface>>
- */
- public $string_interpreters = [];
- /** @var list<class-string<LegacyStringInterpreterInterface>> */
- public $legacy_string_interpreters = [];
-
- /**
- * Static methods to be called after classlike exists checks have completed
- *
- * @var list<class-string<AfterClassLikeExistenceCheckInterface>>
- */
- public $after_classlike_exists_checks = [];
- /** @var list<class-string<LegacyAfterClassLikeExistenceCheckInterface>> */
- public $legacy_after_classlike_exists_checks = [];
-
- /**
- * Static methods to be called after classlike checks have completed
- *
- * @var list<class-string<AfterClassLikeAnalysisInterface>>
- */
- public $after_classlike_checks = [];
- /** @var list<class-string<LegacyAfterClassLikeAnalysisInterface>> */
- public $legacy_after_classlike_checks = [];
-
- /**
- * Static methods to be called after classlikes have been scanned
- *
- * @var list<class-string<AfterClassLikeVisitInterface>>
- */
- private $after_visit_classlikes = [];
- /** @var list<class-string<LegacyAfterClassLikeVisitInterface>> */
- private $legacy_after_visit_classlikes = [];
-
- /**
- * Static methods to be called after codebase has been populated
- *
- * @var list<class-string<AfterCodebasePopulatedInterface>>
- */
- public $after_codebase_populated = [];
- /** @var list<class-string<LegacyAfterCodebasePopulatedInterface>> */
- public $legacy_after_codebase_populated = [];
-
- /**
- * Static methods to be called after codebase has been populated
- *
- * @var list<class-string<AfterAnalysisInterface>>
- */
- public $after_analysis = [];
- /** @var list<class-string<LegacyAfterAnalysisInterface>> */
- public $legacy_after_analysis = [];
-
- /**
- * Static methods to be called after a file has been analyzed
- *
- * @var list<class-string<AfterFileAnalysisInterface>>
- */
- public $after_file_checks = [];
- /** @var list<class-string<LegacyAfterFileAnalysisInterface>> */
- public $legacy_after_file_checks = [];
-
- /**
- * Static methods to be called before a file is analyzed
- *
- * @var list<class-string<BeforeFileAnalysisInterface>>
- */
- public $before_file_checks = [];
- /** @var list<class-string<LegacyBeforeFileAnalysisInterface>> */
- public $legacy_before_file_checks = [];
-
- /**
- * Static methods to be called after functionlike checks have completed
- *
- * @var list<class-string<AfterFunctionLikeAnalysisInterface>>
- */
- public $after_functionlike_checks = [];
- /** @var list<class-string<LegacyAfterFunctionLikeAnalysisInterface>> */
- public $legacy_after_functionlike_checks = [];
-
- /**
- * Static methods to be called to see if taints should be added
- *
- * @var list<class-string<AddTaintsInterface>>
- */
- public $add_taints_checks = [];
-
- /**
- * Static methods to be called to see if taints should be removed
- *
- * @var list<class-string<RemoveTaintsInterface>>
- */
- public $remove_taints_checks = [];
-
- /**
- * @param class-string $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyAfterMethodCallAnalysisInterface::class)) {
- $this->legacy_after_method_checks[] = $class;
- } elseif (is_subclass_of($class, AfterMethodCallAnalysisInterface::class)) {
- $this->after_method_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterFunctionCallAnalysisInterface::class)) {
- $this->legacy_after_function_checks[] = $class;
- } elseif (is_subclass_of($class, AfterFunctionCallAnalysisInterface::class)) {
- $this->after_function_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterEveryFunctionCallAnalysisInterface::class)) {
- $this->legacy_after_every_function_checks[] = $class;
- } elseif (is_subclass_of($class, AfterEveryFunctionCallAnalysisInterface::class)) {
- $this->after_every_function_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterExpressionAnalysisInterface::class)) {
- $this->legacy_after_expression_checks[] = $class;
- } elseif (is_subclass_of($class, AfterExpressionAnalysisInterface::class)) {
- $this->after_expression_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterStatementAnalysisInterface::class)) {
- $this->legacy_after_statement_checks[] = $class;
- } elseif (is_subclass_of($class, AfterStatementAnalysisInterface::class)) {
- $this->after_statement_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyStringInterpreterInterface::class)) {
- $this->legacy_string_interpreters[] = $class;
- } elseif (is_subclass_of($class, StringInterpreterInterface::class)) {
- $this->string_interpreters[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterClassLikeExistenceCheckInterface::class)) {
- $this->legacy_after_classlike_exists_checks[] = $class;
- } elseif (is_subclass_of($class, AfterClassLikeExistenceCheckInterface::class)) {
- $this->after_classlike_exists_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterClassLikeAnalysisInterface::class)) {
- $this->legacy_after_classlike_checks[] = $class;
- } elseif (is_subclass_of($class, AfterClassLikeAnalysisInterface::class)) {
- $this->after_classlike_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterClassLikeVisitInterface::class)) {
- $this->legacy_after_visit_classlikes[] = $class;
- } elseif (is_subclass_of($class, AfterClassLikeVisitInterface::class)) {
- $this->after_visit_classlikes[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterCodebasePopulatedInterface::class)) {
- $this->legacy_after_codebase_populated[] = $class;
- } elseif (is_subclass_of($class, AfterCodebasePopulatedInterface::class)) {
- $this->after_codebase_populated[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterAnalysisInterface::class)) {
- $this->legacy_after_analysis[] = $class;
- } elseif (is_subclass_of($class, AfterAnalysisInterface::class)) {
- $this->after_analysis[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterFileAnalysisInterface::class)) {
- $this->legacy_after_file_checks[] = $class;
- } elseif (is_subclass_of($class, AfterFileAnalysisInterface::class)) {
- $this->after_file_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyBeforeFileAnalysisInterface::class)) {
- $this->legacy_before_file_checks[] = $class;
- } elseif (is_subclass_of($class, BeforeFileAnalysisInterface::class)) {
- $this->before_file_checks[] = $class;
- }
-
- if (is_subclass_of($class, LegacyAfterFunctionLikeAnalysisInterface::class)) {
- $this->legacy_after_functionlike_checks[] = $class;
- } elseif (is_subclass_of($class, AfterFunctionLikeAnalysisInterface::class)) {
- $this->after_functionlike_checks[] = $class;
- }
-
- if (is_subclass_of($class, AddTaintsInterface::class)) {
- $this->add_taints_checks[] = $class;
- }
-
- if (is_subclass_of($class, RemoveTaintsInterface::class)) {
- $this->remove_taints_checks[] = $class;
- }
- }
-
- public function hasAfterMethodCallAnalysisHandlers(): bool
- {
- return count($this->after_method_checks) || count($this->legacy_after_method_checks);
- }
-
- public function dispatchAfterMethodCallAnalysis(AfterMethodCallAnalysisEvent $event): void
- {
- foreach ($this->after_method_checks as $handler) {
- $handler::afterMethodCallAnalysis($event);
- }
-
- foreach ($this->legacy_after_method_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- $return_type_candidate = $event->getReturnTypeCandidate();
- $handler::afterMethodCallAnalysis(
- $event->getExpr(),
- $event->getMethodId(),
- $event->getAppearingMethodId(),
- $event->getDeclaringMethodId(),
- $event->getContext(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements,
- $return_type_candidate
- );
- $event->setFileReplacements($file_replacements);
- $event->setReturnTypeCandidate($return_type_candidate);
- }
- }
-
- public function dispatchAfterFunctionCallAnalysis(AfterFunctionCallAnalysisEvent $event): void
- {
- foreach ($this->after_function_checks as $handler) {
- $handler::afterFunctionCallAnalysis($event);
- }
-
- foreach ($this->legacy_after_function_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- $handler::afterFunctionCallAnalysis(
- $event->getExpr(),
- $event->getFunctionId(),
- $event->getContext(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $event->getReturnTypeCandidate(),
- $file_replacements
- );
- $event->setFileReplacements($file_replacements);
- }
- }
-
- public function dispatchAfterEveryFunctionCallAnalysis(AfterEveryFunctionCallAnalysisEvent $event): void
- {
- foreach ($this->after_every_function_checks as $handler) {
- $handler::afterEveryFunctionCallAnalysis($event);
- }
-
- foreach ($this->legacy_after_every_function_checks as $handler) {
- $handler::afterEveryFunctionCallAnalysis(
- $event->getExpr(),
- $event->getFunctionId(),
- $event->getContext(),
- $event->getStatementsSource(),
- $event->getCodebase()
- );
- }
- }
-
- public function dispatchAfterExpressionAnalysis(AfterExpressionAnalysisEvent $event): ?bool
- {
- foreach ($this->after_expression_checks as $handler) {
- if ($handler::afterExpressionAnalysis($event) === false) {
- return false;
- }
- }
-
- foreach ($this->legacy_after_expression_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- if ($handler::afterExpressionAnalysis(
- $event->getExpr(),
- $event->getContext(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements
- ) === false) {
- return false;
- }
- $event->setFileReplacements($file_replacements);
- }
-
- return null;
- }
-
- public function dispatchAfterStatementAnalysis(AfterStatementAnalysisEvent $event): ?bool
- {
- foreach ($this->after_statement_checks as $handler) {
- if ($handler::afterStatementAnalysis($event) === false) {
- return false;
- }
- }
-
- foreach ($this->legacy_after_statement_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- if ($handler::afterStatementAnalysis(
- $event->getStmt(),
- $event->getContext(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements
- ) === false) {
- return false;
- }
- $event->setFileReplacements($file_replacements);
- }
-
- return null;
- }
-
- public function dispatchStringInterpreter(StringInterpreterEvent $event): ?TLiteralString
- {
- foreach ($this->string_interpreters as $handler) {
- if ($type = $handler::getTypeFromValue($event)) {
- return $type;
- }
- }
-
- foreach ($this->legacy_string_interpreters as $handler) {
- if ($type = $handler::getTypeFromValue($event->getValue())) {
- return $type;
- }
- }
-
- return null;
- }
-
- public function dispatchAfterClassLikeExistenceCheck(AfterClassLikeExistenceCheckEvent $event): void
- {
- foreach ($this->after_classlike_exists_checks as $handler) {
- $handler::afterClassLikeExistenceCheck($event);
- }
-
- foreach ($this->legacy_after_classlike_exists_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- $handler::afterClassLikeExistenceCheck(
- $event->getFqClassName(),
- $event->getCodeLocation(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements
- );
- $event->setFileReplacements($file_replacements);
- }
- }
-
- public function dispatchAfterClassLikeAnalysis(AfterClassLikeAnalysisEvent $event): ?bool
- {
- foreach ($this->after_classlike_checks as $handler) {
- if ($handler::afterStatementAnalysis($event) === false) {
- return false;
- }
- }
-
- foreach ($this->legacy_after_classlike_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- if ($handler::afterStatementAnalysis(
- $event->getStmt(),
- $event->getClasslikeStorage(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements
- ) === false) {
- return false;
- }
- $event->setFileReplacements($file_replacements);
- }
-
- return null;
- }
-
- public function hasAfterClassLikeVisitHandlers(): bool
- {
- return count($this->after_visit_classlikes) || count($this->legacy_after_visit_classlikes);
- }
-
- public function dispatchAfterClassLikeVisit(AfterClassLikeVisitEvent $event): void
- {
- foreach ($this->after_visit_classlikes as $handler) {
- $handler::afterClassLikeVisit($event);
- }
-
- foreach ($this->legacy_after_visit_classlikes as $handler) {
- $file_replacements = $event->getFileReplacements();
- $handler::afterClassLikeVisit(
- $event->getStmt(),
- $event->getStorage(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements
- );
- $event->setFileReplacements($file_replacements);
- }
- }
-
- public function dispatchAfterCodebasePopulated(AfterCodebasePopulatedEvent $event): void
- {
- foreach ($this->after_codebase_populated as $handler) {
- $handler::afterCodebasePopulated($event);
- }
-
- foreach ($this->legacy_after_codebase_populated as $handler) {
- $handler::afterCodebasePopulated(
- $event->getCodebase()
- );
- }
- }
-
- public function dispatchAfterAnalysis(AfterAnalysisEvent $event): void
- {
- foreach ($this->after_analysis as $handler) {
- $handler::afterAnalysis($event);
- }
-
- foreach ($this->legacy_after_analysis as $handler) {
- /** @psalm-suppress MixedArgumentTypeCoercion due to Psalm bug */
- $handler::afterAnalysis(
- $event->getCodebase(),
- $event->getIssues(),
- $event->getBuildInfo(),
- $event->getSourceControlInfo()
- );
- }
- }
-
- public function dispatchAfterFileAnalysis(AfterFileAnalysisEvent $event): void
- {
- foreach ($this->after_file_checks as $handler) {
- $handler::afterAnalyzeFile($event);
- }
-
- foreach ($this->legacy_after_file_checks as $handler) {
- $handler::afterAnalyzeFile(
- $event->getStatementsSource(),
- $event->getFileContext(),
- $event->getFileStorage(),
- $event->getCodebase()
- );
- }
- }
-
- public function dispatchBeforeFileAnalysis(BeforeFileAnalysisEvent $event): void
- {
- foreach ($this->before_file_checks as $handler) {
- $handler::beforeAnalyzeFile($event);
- }
-
- foreach ($this->legacy_before_file_checks as $handler) {
- $handler::beforeAnalyzeFile(
- $event->getStatementsSource(),
- $event->getFileContext(),
- $event->getFileStorage(),
- $event->getCodebase()
- );
- }
- }
-
- public function dispatchAfterFunctionLikeAnalysis(AfterFunctionLikeAnalysisEvent $event): ?bool
- {
- foreach ($this->after_functionlike_checks as $handler) {
- if ($handler::afterStatementAnalysis($event) === false) {
- return false;
- }
- }
-
- foreach ($this->legacy_after_functionlike_checks as $handler) {
- $file_replacements = $event->getFileReplacements();
- if ($handler::afterStatementAnalysis(
- $event->getStmt(),
- $event->getFunctionlikeStorage(),
- $event->getStatementsSource(),
- $event->getCodebase(),
- $file_replacements
- ) === false) {
- return false;
- }
- $event->setFileReplacements($file_replacements);
- }
-
- return null;
- }
-
- /**
- * @return list<string>
- */
- public function dispatchAddTaints(AddRemoveTaintsEvent $event): array
- {
- $added_taints = [];
-
- foreach ($this->add_taints_checks as $handler) {
- $added_taints = array_merge($added_taints, $handler::addTaints($event));
- }
-
- return $added_taints;
- }
-
- /**
- * @return list<string>
- */
- public function dispatchRemoveTaints(AddRemoveTaintsEvent $event): array
- {
- $removed_taints = [];
-
- foreach ($this->remove_taints_checks as $handler) {
- $removed_taints = array_merge($removed_taints, $handler::removeTaints($event));
- }
-
- return $removed_taints;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php b/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php
deleted file mode 100644
index 858e7f4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/BuildInfoCollector.php
+++ /dev/null
@@ -1,321 +0,0 @@
-<?php
-
-namespace Psalm\Internal\ExecutionEnvironment;
-
-use Psalm\SourceControl\Git\CommitInfo;
-use Psalm\SourceControl\Git\GitInfo;
-
-use function explode;
-use function file_get_contents;
-use function json_decode;
-use function str_replace;
-use function strpos;
-use function strtotime;
-
-/**
- * Environment variables collector for CI environment.
- *
- * @author Kitamura Satoshi <with.no.parachute@gmail.com>
- */
-class BuildInfoCollector
-{
- /**
- * Environment variables.
- *
- * Overwritten through collection process.
- *
- * @var array
- */
- protected $env;
-
- /**
- * Read environment variables.
- *
- * @var array
- */
- protected $readEnv = [];
-
- public function __construct(array $env)
- {
- $this->env = $env;
- }
-
- // API
-
- /**
- * Collect environment variables.
- */
- public function collect(): array
- {
- $this->readEnv = [];
-
- $this
- ->fillTravisCi()
- ->fillCircleCi()
- ->fillAppVeyor()
- ->fillJenkins()
- ->fillScrutinizer()
- ->fillGithubActions();
-
- return $this->readEnv;
- }
-
- // internal method
-
- /**
- * Fill Travis CI environment variables.
- *
- * "TRAVIS", "TRAVIS_JOB_ID" must be set.
- *
- * @return $this
- *
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- */
- protected function fillTravisCi(): self
- {
- if (isset($this->env['TRAVIS']) && $this->env['TRAVIS'] && isset($this->env['TRAVIS_JOB_ID'])) {
- $this->readEnv['CI_JOB_ID'] = $this->env['TRAVIS_JOB_ID'];
- $this->env['CI_NAME'] = 'travis-ci';
-
- // backup
- $this->readEnv['TRAVIS'] = $this->env['TRAVIS'];
- $this->readEnv['TRAVIS_JOB_ID'] = $this->env['TRAVIS_JOB_ID'];
- $this->readEnv['CI_NAME'] = $this->env['CI_NAME'];
- $this->readEnv['TRAVIS_TAG'] = $this->env['TRAVIS_TAG'] ?? '';
-
- $repo_slug = (string) $this->env['TRAVIS_REPO_SLUG'];
-
- if ($repo_slug) {
- $slug_parts = explode('/', $repo_slug);
- $this->readEnv['CI_REPO_OWNER'] = $slug_parts[0];
- $this->readEnv['CI_REPO_NAME'] = $slug_parts[1];
- }
-
- $pr_slug = (string) ($this->env['TRAVIS_PULL_REQUEST_SLUG'] ?? '');
-
- if ($pr_slug) {
- $slug_parts = explode('/', $pr_slug);
-
- $this->readEnv['CI_PR_REPO_OWNER'] = $slug_parts[0];
- $this->readEnv['CI_PR_REPO_NAME'] = $slug_parts[1];
- }
-
- $this->readEnv['CI_PR_NUMBER'] = $this->env['TRAVIS_PULL_REQUEST'];
- $this->readEnv['CI_BRANCH'] = $this->env['TRAVIS_BRANCH'];
- }
-
- return $this;
- }
-
- /**
- * Fill CircleCI environment variables.
- *
- * "CIRCLECI", "CIRCLE_BUILD_NUM" must be set.
- *
- * @return $this
- */
- protected function fillCircleCi(): self
- {
- if (isset($this->env['CIRCLECI']) && $this->env['CIRCLECI'] && isset($this->env['CIRCLE_BUILD_NUM'])) {
- $this->env['CI_BUILD_NUMBER'] = $this->env['CIRCLE_BUILD_NUM'];
- $this->env['CI_NAME'] = 'circleci';
-
- // backup
- $this->readEnv['CIRCLECI'] = $this->env['CIRCLECI'];
- $this->readEnv['CIRCLE_BUILD_NUM'] = $this->env['CIRCLE_BUILD_NUM'];
- $this->readEnv['CI_NAME'] = $this->env['CI_NAME'];
-
- $this->readEnv['CI_PR_REPO_OWNER'] = $this->env['CIRCLE_PR_USERNAME'] ?? null;
- $this->readEnv['CI_PR_REPO_NAME'] = $this->env['CIRCLE_PR_REPONAME'] ?? null;
-
- $this->readEnv['CI_REPO_OWNER'] = $this->env['CIRCLE_PROJECT_USERNAME'] ?? null;
- $this->readEnv['CI_REPO_NAME'] = $this->env['CIRCLE_PROJECT_REPONAME'] ?? null;
-
- $this->readEnv['CI_PR_NUMBER'] = $this->env['CIRCLE_PR_NUMBER'] ?? null;
-
- $this->readEnv['CI_BRANCH'] = $this->env['CIRCLE_BRANCH'] ?? null;
- }
-
- return $this;
- }
-
- /**
- * Fill AppVeyor environment variables.
- *
- * "APPVEYOR", "APPVEYOR_BUILD_NUMBER" must be set.
- *
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- *
- * @return $this
- */
- protected function fillAppVeyor(): self
- {
- if (isset($this->env['APPVEYOR']) && $this->env['APPVEYOR'] && isset($this->env['APPVEYOR_BUILD_NUMBER'])) {
- $this->readEnv['CI_BUILD_NUMBER'] = $this->env['APPVEYOR_BUILD_NUMBER'];
- $this->readEnv['CI_JOB_ID'] = $this->env['APPVEYOR_JOB_NUMBER'];
- $this->readEnv['CI_PR_NUMBER'] = $this->env['APPVEYOR_PULL_REQUEST_NUMBER'] ?? '';
- $this->env['CI_NAME'] = 'AppVeyor';
-
- // backup
- $this->readEnv['APPVEYOR'] = $this->env['APPVEYOR'];
- $this->readEnv['APPVEYOR_BUILD_NUMBER'] = $this->env['APPVEYOR_BUILD_NUMBER'];
- $this->readEnv['APPVEYOR_JOB_NUMBER'] = $this->env['APPVEYOR_JOB_NUMBER'];
- $this->readEnv['APPVEYOR_REPO_BRANCH'] = $this->env['APPVEYOR_REPO_BRANCH'];
- $this->readEnv['CI_NAME'] = $this->env['CI_NAME'];
-
- $repo_slug = (string) ($this->env['APPVEYOR_REPO_NAME'] ?? '');
-
- if ($repo_slug) {
- $slug_parts = explode('/', $repo_slug);
-
- $this->readEnv['CI_REPO_OWNER'] = $slug_parts[0];
- $this->readEnv['CI_REPO_NAME'] = $slug_parts[1];
- }
-
- $pr_slug = (string) ($this->env['APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME'] ?? '');
-
- if ($pr_slug) {
- $slug_parts = explode('/', $pr_slug);
-
- $this->readEnv['CI_PR_REPO_OWNER'] = $slug_parts[0];
- $this->readEnv['CI_PR_REPO_NAME'] = $slug_parts[1];
- }
-
- $this->readEnv['CI_BRANCH'] = $this->env['APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH']
- ?? $this->env['APPVEYOR_REPO_BRANCH'];
- }
-
- return $this;
- }
-
- /**
- * Fill Jenkins environment variables.
- *
- * "JENKINS_URL", "BUILD_NUMBER" must be set.
- *
- * @return $this
- */
- protected function fillJenkins(): self
- {
- if (isset($this->env['JENKINS_URL']) && isset($this->env['BUILD_NUMBER'])) {
- $this->readEnv['CI_BUILD_NUMBER'] = $this->env['BUILD_NUMBER'];
- $this->readEnv['CI_BUILD_URL'] = $this->env['JENKINS_URL'];
- $this->env['CI_NAME'] = 'jenkins';
-
- // backup
- $this->readEnv['BUILD_NUMBER'] = $this->env['BUILD_NUMBER'];
- $this->readEnv['JENKINS_URL'] = $this->env['JENKINS_URL'];
- $this->readEnv['CI_NAME'] = $this->env['CI_NAME'];
- }
-
- return $this;
- }
-
- /**
- * Fill Scrutinizer environment variables.
- *
- * "JENKINS_URL", "BUILD_NUMBER" must be set.
- *
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- *
- * @return $this
- */
- protected function fillScrutinizer(): self
- {
- if (isset($this->env['SCRUTINIZER']) && $this->env['SCRUTINIZER']) {
- $this->readEnv['CI_JOB_ID'] = $this->env['SCRUTINIZER_INSPECTION_UUID'];
- $this->readEnv['CI_BRANCH'] = $this->env['SCRUTINIZER_BRANCH'];
- $this->readEnv['CI_PR_NUMBER'] = $this->env['SCRUTINIZER_PR_NUMBER'] ?? '';
-
- // backup
- $this->readEnv['CI_NAME'] = 'Scrutinizer';
-
- $repo_slug = (string) ($this->env['SCRUTINIZER_PROJECT'] ?? '');
-
- if ($repo_slug) {
- $slug_parts = explode('/', $repo_slug);
-
- if ($this->readEnv['CI_PR_NUMBER']) {
- $this->readEnv['CI_PR_REPO_OWNER'] = $slug_parts[1];
- $this->readEnv['CI_PR_REPO_NAME'] = $slug_parts[2];
- } else {
- $this->readEnv['CI_REPO_OWNER'] = $slug_parts[1];
- $this->readEnv['CI_REPO_NAME'] = $slug_parts[2];
- }
- }
- }
-
- return $this;
- }
-
- /**
- * Fill Github Actions environment variables.
- *
- * @return $this
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- */
- protected function fillGithubActions(): BuildInfoCollector
- {
- if (isset($this->env['GITHUB_ACTIONS'])) {
- $this->env['CI_NAME'] = 'github-actions';
- $this->env['CI_JOB_ID'] = $this->env['GITHUB_ACTIONS'];
-
- $githubRef = (string) $this->env['GITHUB_REF'];
- if (strpos($githubRef, 'refs/heads/') !== false) {
- $githubRef = str_replace('refs/heads/', '', $githubRef);
- } elseif (strpos($githubRef, 'refs/tags/') !== false) {
- $githubRef = str_replace('refs/tags/', '', $githubRef);
- }
-
- $this->env['CI_BRANCH'] = $githubRef;
-
- $this->readEnv['GITHUB_ACTIONS'] = $this->env['GITHUB_ACTIONS'];
- $this->readEnv['GITHUB_REF'] = $this->env['GITHUB_REF'];
- $this->readEnv['CI_NAME'] = $this->env['CI_NAME'];
- $this->readEnv['CI_BRANCH'] = $this->env['CI_BRANCH'];
-
- $slug_parts = explode('/', (string) $this->env['GITHUB_REPOSITORY']);
-
- $this->readEnv['CI_REPO_OWNER'] = $slug_parts[0];
- $this->readEnv['CI_REPO_NAME'] = $slug_parts[1];
-
- if (isset($this->env['GITHUB_EVENT_PATH'])) {
- $event_json = file_get_contents((string) $this->env['GITHUB_EVENT_PATH']);
- /** @var array */
- $event_data = json_decode($event_json, true);
-
- if (isset($event_data['head_commit'])) {
- /**
- * @var array{
- * id: string,
- * author: array{name: string, email: string},
- * committer: array{name: string, email: string},
- * message: string,
- * timestamp: string
- * }
- */
- $head_commit_data = $event_data['head_commit'];
- $gitinfo = new GitInfo(
- $githubRef,
- (new CommitInfo())
- ->setId($head_commit_data['id'])
- ->setAuthorName($head_commit_data['author']['name'])
- ->setAuthorEmail($head_commit_data['author']['email'])
- ->setCommitterName($head_commit_data['committer']['name'])
- ->setCommitterEmail($head_commit_data['committer']['email'])
- ->setMessage($head_commit_data['message'])
- ->setDate(strtotime($head_commit_data['timestamp'])),
- []
- );
-
- $this->readEnv['git'] = $gitinfo->toArray();
- }
-
- if ($this->env['GITHUB_EVENT_PATH'] === 'pull_request') {
- $this->readEnv['CI_PR_NUMBER'] = $event_data['number'];
- }
- }
- }
- return $this;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php b/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php
deleted file mode 100644
index e782128..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/GitInfoCollector.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-namespace Psalm\Internal\ExecutionEnvironment;
-
-use Psalm\SourceControl\Git\CommitInfo;
-use Psalm\SourceControl\Git\GitInfo;
-use Psalm\SourceControl\Git\RemoteInfo;
-use RuntimeException;
-
-use function array_keys;
-use function array_unique;
-use function count;
-use function explode;
-use function range;
-use function strpos;
-use function trim;
-
-/**
- * Git repository info collector.
- *
- * @author Kitamura Satoshi <with.no.parachute@gmail.com>
- */
-class GitInfoCollector
-{
- /**
- * Git command.
- *
- * @var SystemCommandExecutor
- */
- protected $executor;
-
- /**
- * Constructor.
- */
- public function __construct()
- {
- $this->executor = new SystemCommandExecutor();
- }
-
- // API
-
- /**
- * Collect git repository info.
- */
- public function collect(): GitInfo
- {
- $branch = $this->collectBranch();
- $commit = $this->collectCommit();
- $remotes = $this->collectRemotes();
-
- return new GitInfo($branch, $commit, $remotes);
- }
-
- /**
- * Collect branch name.
- *
- * @throws RuntimeException
- */
- protected function collectBranch(): string
- {
- $branchesResult = $this->executor->execute('git branch');
-
- foreach ($branchesResult as $result) {
- if (strpos($result, '* ') === 0) {
- $exploded = explode('* ', $result, 2);
-
- return $exploded[1];
- }
- }
-
- throw new RuntimeException();
- }
-
- /**
- * Collect commit info.
- *
- * @throws RuntimeException
- */
- protected function collectCommit(): CommitInfo
- {
- $commitResult = $this->executor->execute('git log -1 --pretty=format:%H%n%aN%n%ae%n%cN%n%ce%n%s%n%at');
-
- if (count($commitResult) !== 7 || array_keys($commitResult) !== range(0, 6)) {
- throw new RuntimeException();
- }
-
- $commit = new CommitInfo();
-
- return $commit
- ->setId(trim($commitResult[0]))
- ->setAuthorName(trim($commitResult[1]))
- ->setAuthorEmail(trim($commitResult[2]))
- ->setCommitterName(trim($commitResult[3]))
- ->setCommitterEmail(trim($commitResult[4]))
- ->setMessage($commitResult[5])
- ->setDate((int) $commitResult[6]);
- }
-
- /**
- * Collect remotes info.
- *
- * @throws RuntimeException
- *
- * @return list<RemoteInfo>
- */
- protected function collectRemotes(): array
- {
- $remotesResult = $this->executor->execute('git remote -v');
-
- if (count($remotesResult) === 0) {
- throw new RuntimeException();
- }
-
- // parse command result
- $results = [];
-
- foreach ($remotesResult as $result) {
- if (strpos($result, ' ') !== false) {
- [$remote] = explode(' ', $result, 2);
-
- $results[] = $remote;
- }
- }
-
- // filter
- $results = array_unique($results);
-
- // create Remote instances
- $remotes = [];
-
- foreach ($results as $result) {
- if (strpos($result, "\t") !== false) {
- [$name, $url] = explode("\t", $result, 2);
-
- $remote = new RemoteInfo();
- $remotes[] = $remote->setName(trim($name))->setUrl(trim($url));
- }
- }
-
- return $remotes;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/SystemCommandExecutor.php b/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/SystemCommandExecutor.php
deleted file mode 100644
index 97113ea..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/ExecutionEnvironment/SystemCommandExecutor.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-namespace Psalm\Internal\ExecutionEnvironment;
-
-use RuntimeException;
-
-use function exec;
-use function function_exists;
-use function sprintf;
-
-/**
- * @author Kitamura Satoshi <with.no.parachute@gmail.com>
- * @author Dariusz Rumiński <dariusz.ruminski@gmail.com>
- *
- * @internal
- */
-final class SystemCommandExecutor
-{
- /**
- * Execute command.
- *
- *
- * @throws RuntimeException
- *
- * @return string[]
- */
- public function execute(string $command): array
- {
- if (!function_exists('exec')) {
- throw new RuntimeException(sprintf('exec does not exist, failed to execute command: %s', $command));
- }
-
- exec($command, $result, $returnValue);
-
- if ($returnValue === 0) {
- /** @var string[] */
- return $result;
- }
-
- throw new RuntimeException(sprintf('Failed to execute command: %s', $command), $returnValue);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/ClassDocblockManipulator.php b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/ClassDocblockManipulator.php
deleted file mode 100644
index f953ad9..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/ClassDocblockManipulator.php
+++ /dev/null
@@ -1,141 +0,0 @@
-<?php
-
-namespace Psalm\Internal\FileManipulation;
-
-use PhpParser\Node\Stmt\Class_;
-use Psalm\DocComment;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Scanner\ParsedDocblock;
-
-use function ltrim;
-use function str_replace;
-use function strlen;
-use function strrpos;
-use function substr;
-
-/**
- * @internal
- */
-class ClassDocblockManipulator
-{
- /**
- * @var array<string, array<int, self>>
- */
- private static $manipulators = [];
-
- /** @var Class_ */
- private $stmt;
-
- /** @var int */
- private $docblock_start;
-
- /** @var int */
- private $docblock_end;
-
- /** @var bool */
- private $immutable = false;
-
- /** @var string */
- private $indentation;
-
- public static function getForClass(
- ProjectAnalyzer $project_analyzer,
- string $file_path,
- Class_ $stmt
- ): self {
- if (isset(self::$manipulators[$file_path][$stmt->getLine()])) {
- return self::$manipulators[$file_path][$stmt->getLine()];
- }
-
- $manipulator
- = self::$manipulators[$file_path][$stmt->getLine()]
- = new self($project_analyzer, $stmt, $file_path);
-
- return $manipulator;
- }
-
- private function __construct(
- ProjectAnalyzer $project_analyzer,
- Class_ $stmt,
- string $file_path
- ) {
- $this->stmt = $stmt;
- $docblock = $stmt->getDocComment();
- $this->docblock_start = $docblock ? $docblock->getStartFilePos() : (int)$stmt->getAttribute('startFilePos');
- $this->docblock_end = (int)$stmt->getAttribute('startFilePos');
-
- $codebase = $project_analyzer->getCodebase();
-
- $file_contents = $codebase->getFileContents($file_path);
-
- $preceding_newline_pos = (int) strrpos($file_contents, "\n", $this->docblock_end - strlen($file_contents));
-
- $first_line = substr($file_contents, $preceding_newline_pos + 1, $this->docblock_end - $preceding_newline_pos);
-
- $this->indentation = str_replace(ltrim($first_line), '', $first_line);
- }
-
- public function makeImmutable(): void
- {
- $this->immutable = true;
- }
-
- /**
- * Gets a new docblock given the existing docblock, if one exists, and the updated return types
- * and/or parameters
- *
- */
- private function getDocblock(): string
- {
- $docblock = $this->stmt->getDocComment();
-
- if ($docblock) {
- $parsed_docblock = DocComment::parsePreservingLength($docblock);
- } else {
- $parsed_docblock = new ParsedDocblock('', []);
- }
-
- $modified_docblock = false;
-
- if ($this->immutable) {
- $modified_docblock = true;
- $parsed_docblock->tags['psalm-immutable'] = [''];
- }
-
- if (!$modified_docblock) {
- return (string)$docblock . "\n" . $this->indentation;
- }
-
- return $parsed_docblock->render($this->indentation);
- }
-
- /**
- * @return array<int, FileManipulation>
- */
- public static function getManipulationsForFile(string $file_path): array
- {
- if (!isset(self::$manipulators[$file_path])) {
- return [];
- }
-
- $file_manipulations = [];
-
- foreach (self::$manipulators[$file_path] as $manipulator) {
- if ($manipulator->immutable) {
- $file_manipulations[$manipulator->docblock_start] = new FileManipulation(
- $manipulator->docblock_start,
- $manipulator->docblock_end,
- $manipulator->getDocblock()
- );
- }
- }
-
- return $file_manipulations;
- }
-
- public static function clearCache(): void
- {
- self::$manipulators = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/CodeMigration.php b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/CodeMigration.php
deleted file mode 100644
index 086af29..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/CodeMigration.php
+++ /dev/null
@@ -1,38 +0,0 @@
-<?php
-
-namespace Psalm\Internal\FileManipulation;
-
-/**
- * @psalm-immutable
- */
-class CodeMigration
-{
- /** @var string */
- public $source_file_path;
-
- /** @var int */
- public $source_start;
-
- /** @var int */
- public $source_end;
-
- /** @var string */
- public $destination_file_path;
-
- /** @var int */
- public $destination_start;
-
- public function __construct(
- string $source_file_path,
- int $source_start,
- int $source_end,
- string $destination_file_path,
- int $destination_start
- ) {
- $this->source_file_path = $source_file_path;
- $this->source_start = $source_start;
- $this->source_end = $source_end;
- $this->destination_file_path = $destination_file_path;
- $this->destination_start = $destination_start;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php
deleted file mode 100644
index 1ed9c6c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FileManipulationBuffer.php
+++ /dev/null
@@ -1,245 +0,0 @@
-<?php
-
-namespace Psalm\Internal\FileManipulation;
-
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Provider\FileProvider;
-
-use function array_merge;
-use function preg_match;
-use function strlen;
-use function strpos;
-use function strrpos;
-use function substr;
-use function substr_replace;
-
-/**
- * @internal
- */
-class FileManipulationBuffer
-{
- /** @var array<string, FileManipulation[]> */
- private static $file_manipulations = [];
-
- /** @var CodeMigration[] */
- private static $code_migrations = [];
-
- /**
- * @param FileManipulation[] $file_manipulations
- *
- */
- public static function add(string $file_path, array $file_manipulations): void
- {
- if (!isset(self::$file_manipulations[$file_path])) {
- self::$file_manipulations[$file_path] = [];
- }
-
- foreach ($file_manipulations as $file_manipulation) {
- self::$file_manipulations[$file_path][$file_manipulation->getKey()] = $file_manipulation;
- }
- }
-
- /** @param CodeMigration[] $code_migrations */
- public static function addCodeMigrations(array $code_migrations): void
- {
- self::$code_migrations = array_merge(self::$code_migrations, $code_migrations);
- }
-
- /**
- * @return array{int, int}
- */
- private static function getCodeOffsets(
- string $source_file_path,
- int $source_start,
- int $source_end
- ): array {
- if (!isset(self::$file_manipulations[$source_file_path])) {
- return [0, 0];
- }
-
- $start_offset = 0;
- $middle_offset = 0;
-
- foreach (self::$file_manipulations[$source_file_path] as $fm) {
- $offset = strlen($fm->insertion_text) - $fm->end + $fm->start;
-
- if ($fm->end < $source_start) {
- $start_offset += $offset;
- $middle_offset += $offset;
- } elseif ($fm->start > $source_start
- && $fm->end < $source_end
- ) {
- $middle_offset += $offset;
- }
- }
-
- return [$start_offset, $middle_offset];
- }
-
- public static function addForCodeLocation(
- CodeLocation $code_location,
- string $replacement_text,
- bool $swallow_newlines = false
- ): void {
- $bounds = $code_location->getSnippetBounds();
-
- if ($swallow_newlines) {
- $project_analyzer = ProjectAnalyzer::getInstance();
-
- $codebase = $project_analyzer->getCodebase();
-
- $file_contents = $codebase->getFileContents($code_location->file_path);
-
- if (($file_contents[$bounds[0] - 1] ?? null) === "\n"
- && ($file_contents[$bounds[0] - 2] ?? null) === "\n"
- ) {
- $bounds[0] -= 2;
- }
- }
-
- self::add(
- $code_location->file_path,
- [
- new FileManipulation(
- $bounds[0],
- $bounds[1],
- $replacement_text
- ),
- ]
- );
- }
-
- public static function addVarAnnotationToRemove(DocblockTypeLocation $code_location): void
- {
- $bounds = $code_location->getSelectionBounds();
-
- $project_analyzer = ProjectAnalyzer::getInstance();
-
- $codebase = $project_analyzer->getCodebase();
-
- $file_contents = $codebase->getFileContents($code_location->file_path);
-
- $comment_start = strrpos($file_contents, '/**', $bounds[0] - strlen($file_contents));
-
- if ($comment_start === false) {
- return;
- }
-
- $comment_end = strpos($file_contents, '*/', $bounds[1]);
-
- if ($comment_end === false) {
- return;
- }
-
- $comment_end += 2;
-
- $comment_text = substr($file_contents, $comment_start, $comment_end - $comment_start);
-
- $var_type_comment_start = $bounds[0] - $comment_start;
- $var_type_comment_end = $bounds[1] - $comment_start;
-
- $var_start = strrpos($comment_text, '@var', $var_type_comment_start - strlen($comment_text));
- $var_end = strpos($comment_text, "\n", $var_type_comment_end);
-
- if ($var_start && $var_end) {
- $var_start = strrpos($comment_text, "\n", $var_start - strlen($comment_text)) ?: $var_start;
- $comment_text = substr_replace($comment_text, '', $var_start, $var_end - $var_start);
- if (preg_match('@^/\*\*\n(\s*\*\s*\n)*\s*\*?\*/$@', $comment_text)) {
- $comment_text = '';
- }
- } else {
- $comment_text = '';
- }
-
- self::add(
- $code_location->file_path,
- [
- new FileManipulation(
- $comment_start,
- $comment_end,
- $comment_text,
- false,
- $comment_text === ''
- ),
- ]
- );
- }
-
- /**
- * @return FileManipulation[]
- */
- public static function getManipulationsForFile(string $file_path): array
- {
- return self::$file_manipulations[$file_path] ?? [];
- }
-
- /**
- * @return array<string, FileManipulation[]>
- */
- public static function getMigrationManipulations(FileProvider $file_provider): array
- {
- $code_migration_manipulations = [];
-
- foreach (self::$code_migrations as $code_migration) {
- [$start_offset, $middle_offset] = self::getCodeOffsets(
- $code_migration->source_file_path,
- $code_migration->source_start,
- $code_migration->source_end
- );
-
- if (!isset($code_migration_manipulations[$code_migration->source_file_path])) {
- $code_migration_manipulations[$code_migration->source_file_path] = [];
- }
-
- if (!isset($code_migration_manipulations[$code_migration->destination_file_path])) {
- $code_migration_manipulations[$code_migration->destination_file_path] = [];
- }
-
- $delete_file_manipulation = new FileManipulation(
- $code_migration->source_start + $start_offset,
- $code_migration->source_end + $middle_offset,
- ''
- );
-
- $code_migration_manipulations[$code_migration->source_file_path][] = $delete_file_manipulation;
-
- [$destination_start_offset] = self::getCodeOffsets(
- $code_migration->destination_file_path,
- $code_migration->destination_start,
- $code_migration->destination_start
- );
-
- $manipulation = new FileManipulation(
- $code_migration->destination_start + $destination_start_offset,
- $code_migration->destination_start + $destination_start_offset,
- "\n" . substr(
- $file_provider->getContents($code_migration->source_file_path),
- $delete_file_manipulation->start,
- $delete_file_manipulation->end - $delete_file_manipulation->start
- ) . "\n"
- );
-
- $code_migration_manipulations[$code_migration->destination_file_path][$manipulation->getKey()]
- = $manipulation;
- }
-
- return $code_migration_manipulations;
- }
-
- /**
- * @return array<string, FileManipulation[]>
- */
- public static function getAll(): array
- {
- return self::$file_manipulations;
- }
-
- public static function clearCache(): void
- {
- self::$file_manipulations = [];
- self::$code_migrations = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php
deleted file mode 100644
index ca65233..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php
+++ /dev/null
@@ -1,579 +0,0 @@
-<?php
-
-namespace Psalm\Internal\FileManipulation;
-
-use PhpParser;
-use PhpParser\Node\Expr\ArrowFunction;
-use PhpParser\Node\Expr\Closure;
-use PhpParser\Node\FunctionLike;
-use PhpParser\Node\Stmt\ClassMethod;
-use PhpParser\Node\Stmt\Function_;
-use Psalm\DocComment;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Scanner\ParsedDocblock;
-
-use function array_key_exists;
-use function array_merge;
-use function array_reduce;
-use function count;
-use function is_string;
-use function ltrim;
-use function preg_match;
-use function reset;
-use function str_replace;
-use function str_split;
-use function strlen;
-use function strpos;
-use function strrpos;
-use function substr;
-
-/**
- * @internal
- */
-class FunctionDocblockManipulator
-{
- /**
- * Manipulators ordered by line number
- *
- * @var array<string, array<int, FunctionDocblockManipulator>>
- */
- private static $manipulators = [];
-
- /** @var Closure|Function_|ClassMethod|ArrowFunction */
- private $stmt;
-
- /** @var int */
- private $docblock_start;
-
- /** @var int */
- private $docblock_end;
-
- /** @var int */
- private $return_typehint_area_start;
-
- /** @var null|int */
- private $return_typehint_colon_start;
-
- /** @var null|int */
- private $return_typehint_start;
-
- /** @var null|int */
- private $return_typehint_end;
-
- /** @var null|string */
- private $new_php_return_type;
-
- /** @var bool */
- private $return_type_is_php_compatible = false;
-
- /** @var null|string */
- private $new_phpdoc_return_type;
-
- /** @var null|string */
- private $new_psalm_return_type;
-
- /** @var array<string, string> */
- private $new_php_param_types = [];
-
- /** @var array<string, string> */
- private $new_phpdoc_param_types = [];
-
- /** @var array<string, string> */
- private $new_psalm_param_types = [];
-
- /** @var string */
- private $indentation;
-
- /** @var string|null */
- private $return_type_description;
-
- /** @var array<string, int> */
- private $param_offsets = [];
-
- /** @var array<string, array{int, int}> */
- private $param_typehint_offsets = [];
-
- /** @var bool */
- private $is_pure = false;
-
- /** @var list<string> */
- private $throwsExceptions = [];
-
- /**
- * @param Closure|Function_|ClassMethod|ArrowFunction $stmt
- */
- public static function getForFunction(
- ProjectAnalyzer $project_analyzer,
- string $file_path,
- FunctionLike $stmt
- ): FunctionDocblockManipulator {
- if (isset(self::$manipulators[$file_path][$stmt->getLine()])) {
- return self::$manipulators[$file_path][$stmt->getLine()];
- }
-
- $manipulator
- = self::$manipulators[$file_path][$stmt->getLine()]
- = new self($file_path, $stmt, $project_analyzer);
-
- return $manipulator;
- }
-
- /**
- * @param Closure|Function_|ClassMethod|ArrowFunction $stmt
- */
- private function __construct(string $file_path, FunctionLike $stmt, ProjectAnalyzer $project_analyzer)
- {
- $this->stmt = $stmt;
- $docblock = $stmt->getDocComment();
- $this->docblock_start = $docblock ? $docblock->getStartFilePos() : (int)$stmt->getAttribute('startFilePos');
- $this->docblock_end = $function_start = (int)$stmt->getAttribute('startFilePos');
- $function_end = (int)$stmt->getAttribute('endFilePos');
-
- $attributes = $stmt->getAttrGroups();
- foreach ($attributes as $attribute) {
- // if we have attribute groups, we need to consider that the function starts after them
- if ((int) $attribute->getAttribute('endFilePos') > $function_start) {
- $function_start = (int) $attribute->getAttribute('endFilePos');
- }
- }
-
- foreach ($stmt->params as $param) {
- if ($param->var instanceof PhpParser\Node\Expr\Variable
- && is_string($param->var->name)
- ) {
- $this->param_offsets[$param->var->name] = (int) $param->getAttribute('startFilePos');
-
- if ($param->type) {
- $this->param_typehint_offsets[$param->var->name] = [
- (int) $param->type->getAttribute('startFilePos'),
- (int) $param->type->getAttribute('endFilePos')
- ];
- }
- }
- }
-
- $codebase = $project_analyzer->getCodebase();
-
- $file_contents = $codebase->getFileContents($file_path);
-
- $last_arg_position = $stmt->params
- ? (int) $stmt->params[count($stmt->params) - 1]->getAttribute('endFilePos') + 1
- : null;
-
- if ($stmt instanceof Closure && $stmt->uses) {
- $last_arg_position = (int) $stmt->uses[count($stmt->uses) - 1]->getAttribute('endFilePos') + 1;
- }
-
- $end_bracket_position = (int) strpos($file_contents, ')', $last_arg_position ?: $function_start);
-
- $this->return_typehint_area_start = $end_bracket_position + 1;
-
- $function_code = substr($file_contents, $function_start, $function_end);
-
- $function_code_after_bracket = substr($function_code, $end_bracket_position + 1 - $function_start);
-
- // do a little parsing here
- $chars = str_split($function_code_after_bracket);
-
- $in_single_line_comment = $in_multi_line_comment = false;
-
- for ($i = 0, $iMax = count($chars); $i < $iMax; ++$i) {
- $char = $chars[$i];
-
- switch ($char) {
- case "\n":
- $in_single_line_comment = false;
- continue 2;
-
- case ':':
- if ($in_multi_line_comment || $in_single_line_comment) {
- continue 2;
- }
-
- $this->return_typehint_colon_start = $i + $end_bracket_position + 1;
-
- continue 2;
-
- case '/':
- if ($in_multi_line_comment || $in_single_line_comment) {
- continue 2;
- }
-
- if ($chars[$i + 1] === '*') {
- $in_multi_line_comment = true;
- ++$i;
- }
-
- if ($chars[$i + 1] === '/') {
- $in_single_line_comment = true;
- ++$i;
- }
-
- continue 2;
-
- case '*':
- if ($in_single_line_comment) {
- continue 2;
- }
-
- if ($chars[$i + 1] === '/') {
- $in_multi_line_comment = false;
- ++$i;
- }
-
- continue 2;
-
- case '{':
- if ($in_multi_line_comment || $in_single_line_comment) {
- continue 2;
- }
-
- break 2;
-
- case '=':
- if ($in_multi_line_comment || $in_single_line_comment) {
- continue 2;
- }
- break 2;
-
- case '?':
- if ($in_multi_line_comment || $in_single_line_comment) {
- continue 2;
- }
-
- $this->return_typehint_start = $i + $end_bracket_position + 1;
- break;
- }
-
- if ($in_multi_line_comment || $in_single_line_comment) {
- continue;
- }
-
- if ($chars[$i] === '\\' || preg_match('/\w/', $char)) {
- if ($this->return_typehint_start === null) {
- $this->return_typehint_start = $i + $end_bracket_position + 1;
- }
-
- if ($chars[$i + 1] !== '\\' && !preg_match('/[\w]/', $chars[$i + 1])) {
- $this->return_typehint_end = $i + $end_bracket_position + 2;
- break;
- }
- }
- }
-
- $preceding_newline_pos = strrpos($file_contents, "\n", $this->docblock_end - strlen($file_contents));
-
- if ($preceding_newline_pos === false) {
- $this->indentation = '';
-
- return;
- }
-
- $first_line = substr($file_contents, $preceding_newline_pos + 1, $this->docblock_end - $preceding_newline_pos);
-
- $this->indentation = str_replace(ltrim($first_line), '', $first_line);
- }
-
- /**
- * Sets the new return type
- *
- */
- public function setReturnType(
- ?string $php_type,
- string $new_type,
- string $phpdoc_type,
- bool $is_php_compatible,
- ?string $description
- ): void {
- $new_type = str_replace(['<mixed, mixed>', '<array-key, mixed>'], '', $new_type);
-
- $this->new_php_return_type = $php_type;
- $this->new_phpdoc_return_type = $phpdoc_type;
- $this->new_psalm_return_type = $new_type;
- $this->return_type_is_php_compatible = $is_php_compatible;
- $this->return_type_description = $description;
- }
-
- /**
- * Sets a new param type
- */
- public function setParamType(
- string $param_name,
- ?string $php_type,
- string $new_type,
- string $phpdoc_type
- ): void {
- $new_type = str_replace(['<mixed, mixed>', '<array-key, mixed>', '<empty, empty>'], '', $new_type);
-
- if ($php_type === 'static') {
- $php_type = '';
- }
- if ($php_type) {
- $this->new_php_param_types[$param_name] = $php_type;
- }
-
- if ($php_type !== $phpdoc_type) {
- $this->new_phpdoc_param_types[$param_name] = $phpdoc_type;
- }
- if ($php_type !== $new_type && $phpdoc_type !== $new_type) {
- $this->new_psalm_param_types[$param_name] = $new_type;
- }
- }
-
- /**
- * Gets a new docblock given the existing docblock, if one exists, and the updated return types
- * and/or parameters
- *
- */
- private function getDocblock(): string
- {
- $docblock = $this->stmt->getDocComment();
-
- if ($docblock) {
- $parsed_docblock = DocComment::parsePreservingLength($docblock);
- } else {
- $parsed_docblock = new ParsedDocblock('', []);
- }
-
- $modified_docblock = false;
-
- foreach ($this->new_phpdoc_param_types as $param_name => $phpdoc_type) {
- $found_in_params = false;
- $new_param_block = $phpdoc_type . ' ' . '$' . $param_name;
-
- if (isset($parsed_docblock->tags['param'])) {
- foreach ($parsed_docblock->tags['param'] as &$param_block) {
- $doc_parts = CommentAnalyzer::splitDocLine($param_block);
-
- if (($doc_parts[1] ?? null) === '$' . $param_name) {
- if ($param_block !== $new_param_block) {
- $modified_docblock = true;
- }
-
- $param_block = $new_param_block;
- $found_in_params = true;
- break;
- }
- }
- }
-
- if (!$found_in_params) {
- $modified_docblock = true;
- $parsed_docblock->tags['param'][] = $new_param_block;
- }
- }
-
- foreach ($this->new_psalm_param_types as $param_name => $psalm_type) {
- $found_in_params = false;
- $new_param_block = $psalm_type . ' ' . '$' . $param_name;
-
- if (isset($parsed_docblock->tags['psalm-param'])) {
- foreach ($parsed_docblock->tags['psalm-param'] as &$param_block) {
- $doc_parts = CommentAnalyzer::splitDocLine($param_block);
-
- if (($doc_parts[1] ?? null) === '$' . $param_name) {
- if ($param_block !== $new_param_block) {
- $modified_docblock = true;
- }
-
- $param_block = $new_param_block;
- $found_in_params = true;
- break;
- }
- }
- }
-
- if (!$found_in_params) {
- $modified_docblock = true;
- $parsed_docblock->tags['psalm-param'][] = $new_param_block;
- }
- }
-
- $old_phpdoc_return_type = null;
- if (isset($parsed_docblock->tags['return'])) {
- $old_phpdoc_return_type = reset($parsed_docblock->tags['return']);
- }
-
- if ($this->is_pure) {
- $modified_docblock = true;
- $parsed_docblock->tags['psalm-pure'] = [''];
- }
- if (count($this->throwsExceptions) > 0) {
- $modified_docblock = true;
- $inferredThrowsClause = array_reduce(
- $this->throwsExceptions,
- function (string $throwsClause, string $exception) {
- return $throwsClause === '' ? $exception : $throwsClause.'|'.$exception;
- },
- ''
- );
- if (array_key_exists('throws', $parsed_docblock->tags)) {
- $parsed_docblock->tags['throws'][] = $inferredThrowsClause;
- } else {
- $parsed_docblock->tags['throws'] = [$inferredThrowsClause];
- }
- }
-
-
- if ($this->new_phpdoc_return_type && $this->new_phpdoc_return_type !== $old_phpdoc_return_type) {
- $modified_docblock = true;
- if ($this->new_phpdoc_return_type !== $this->new_php_return_type || $this->return_type_description) {
- //only add the type if it's different than signature or if there's a description
- $parsed_docblock->tags['return'] = [
- $this->new_phpdoc_return_type
- . ($this->return_type_description ? (' ' . $this->return_type_description) : ''),
- ];
- } else {
- unset($parsed_docblock->tags['return']);
- }
- }
-
- $old_psalm_return_type = null;
- if (isset($parsed_docblock->tags['psalm-return'])) {
- $old_psalm_return_type = reset($parsed_docblock->tags['psalm-return']);
- }
-
- if ($this->new_psalm_return_type
- && $this->new_phpdoc_return_type !== $this->new_psalm_return_type
- && $this->new_psalm_return_type !== $old_psalm_return_type
- ) {
- $modified_docblock = true;
- $parsed_docblock->tags['psalm-return'] = [$this->new_psalm_return_type];
- }
-
- if (!$parsed_docblock->tags && !$parsed_docblock->description) {
- return '';
- }
-
- if (!$modified_docblock) {
- return (string)$docblock . "\n" . $this->indentation;
- }
-
- return $parsed_docblock->render($this->indentation);
- }
-
- /**
- * @return array<int, FileManipulation>
- */
- public static function getManipulationsForFile(string $file_path): array
- {
- if (!isset(self::$manipulators[$file_path])) {
- return [];
- }
-
- $file_manipulations = [];
-
- foreach (self::$manipulators[$file_path] as $manipulator) {
- if ($manipulator->new_php_return_type) {
- if ($manipulator->return_typehint_start && $manipulator->return_typehint_end) {
- $file_manipulations[$manipulator->return_typehint_start] = new FileManipulation(
- $manipulator->return_typehint_start,
- $manipulator->return_typehint_end,
- $manipulator->new_php_return_type
- );
- } else {
- $file_manipulations[$manipulator->return_typehint_area_start] = new FileManipulation(
- $manipulator->return_typehint_area_start,
- $manipulator->return_typehint_area_start,
- ': ' . $manipulator->new_php_return_type
- );
- }
- } elseif ($manipulator->new_php_return_type === ''
- && $manipulator->return_typehint_colon_start
- && $manipulator->new_phpdoc_return_type
- && $manipulator->return_typehint_start
- && $manipulator->return_typehint_end
- ) {
- $file_manipulations[$manipulator->return_typehint_start] = new FileManipulation(
- $manipulator->return_typehint_colon_start,
- $manipulator->return_typehint_end,
- ''
- );
- }
-
- if (!$manipulator->new_php_return_type
- || !$manipulator->return_type_is_php_compatible
- || $manipulator->docblock_start !== $manipulator->docblock_end
- || $manipulator->is_pure
- ) {
- $file_manipulations[$manipulator->docblock_start] = new FileManipulation(
- $manipulator->docblock_start,
- $manipulator->docblock_end,
- $manipulator->getDocblock()
- );
- }
-
- foreach ($manipulator->new_php_param_types as $param_name => $new_php_param_type) {
- if (!isset($manipulator->param_offsets[$param_name])) {
- continue;
- }
-
- $param_offset = $manipulator->param_offsets[$param_name];
-
- $typehint_offsets = $manipulator->param_typehint_offsets[$param_name] ?? null;
-
- if ($new_php_param_type) {
- if ($typehint_offsets) {
- $file_manipulations[$typehint_offsets[0]] = new FileManipulation(
- $typehint_offsets[0],
- $typehint_offsets[1],
- $new_php_param_type
- );
- } else {
- $file_manipulations[$param_offset] = new FileManipulation(
- $param_offset,
- $param_offset,
- $new_php_param_type . ' '
- );
- }
- } elseif ($new_php_param_type === ''
- && $typehint_offsets
- ) {
- $file_manipulations[$typehint_offsets[0]] = new FileManipulation(
- $typehint_offsets[0],
- $param_offset,
- ''
- );
- }
- }
- }
-
- return $file_manipulations;
- }
-
- public function makePure(): void
- {
- $this->is_pure = true;
- }
-
- /**
- * @param list<string> $exceptions
- */
- public function addThrowsDocblock(array $exceptions): void
- {
- $this->throwsExceptions = $exceptions;
- }
-
- public static function clearCache(): void
- {
- self::$manipulators = [];
- }
-
- /**
- * @param array<string, array<int, FunctionDocblockManipulator>> $manipulators
- */
- public static function addManipulators(array $manipulators): void
- {
- self::$manipulators = array_merge($manipulators, self::$manipulators);
- }
-
- /**
- * @return array<string, array<int, FunctionDocblockManipulator>>
- */
- public static function getManipulators(): array
- {
- return self::$manipulators;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php
deleted file mode 100644
index 0ee9654..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/PropertyDocblockManipulator.php
+++ /dev/null
@@ -1,281 +0,0 @@
-<?php
-
-namespace Psalm\Internal\FileManipulation;
-
-use PhpParser\Node\Stmt\Property;
-use Psalm\Config;
-use Psalm\DocComment;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use UnexpectedValueException;
-
-use function array_shift;
-use function count;
-use function ltrim;
-use function str_replace;
-use function strlen;
-use function strrpos;
-use function substr;
-use function substr_count;
-
-/**
- * @internal
- */
-class PropertyDocblockManipulator
-{
- /**
- * @var array<string, array<int, self>>
- */
- private static $manipulators = [];
-
- /** @var Property */
- private $stmt;
-
- /** @var int */
- private $docblock_start;
-
- /** @var int */
- private $docblock_end;
-
- /** @var null|int */
- private $typehint_start;
-
- /** @var int */
- private $typehint_area_start;
-
- /** @var null|int */
- private $typehint_end;
-
- /** @var null|string */
- private $new_php_type;
-
- /** @var bool */
- private $type_is_php_compatible = false;
-
- /** @var null|string */
- private $new_phpdoc_type;
-
- /** @var null|string */
- private $new_psalm_type;
-
- /** @var string */
- private $indentation;
-
- /** @var bool */
- private $add_newline = false;
-
- /** @var string|null */
- private $type_description;
-
- public static function getForProperty(
- ProjectAnalyzer $project_analyzer,
- string $file_path,
- Property $stmt
- ): self {
- if (isset(self::$manipulators[$file_path][$stmt->getLine()])) {
- return self::$manipulators[$file_path][$stmt->getLine()];
- }
-
- $manipulator
- = self::$manipulators[$file_path][$stmt->getLine()]
- = new self($project_analyzer, $stmt, $file_path);
-
- return $manipulator;
- }
-
- private function __construct(
- ProjectAnalyzer $project_analyzer,
- Property $stmt,
- string $file_path
- ) {
- $this->stmt = $stmt;
- $docblock = $stmt->getDocComment();
- $this->docblock_start = $docblock ? $docblock->getStartFilePos() : (int)$stmt->getAttribute('startFilePos');
- $this->docblock_end = (int)$stmt->getAttribute('startFilePos');
-
- $codebase = $project_analyzer->getCodebase();
-
- $file_contents = $codebase->getFileContents($file_path);
-
- if (count($stmt->props) > 1) {
- $config = Config::getInstance();
- if ($config->isInProjectDirs($file_path)) {
- throw new UnexpectedValueException('Cannot replace multiple inline properties in ' . $file_path);
- }
-
- $this->indentation = '';
-
- return;
- }
-
- $prop = $stmt->props[0];
-
- if ($stmt->type) {
- $this->typehint_start = (int)$stmt->type->getAttribute('startFilePos');
- $this->typehint_end = (int)$stmt->type->getAttribute('endFilePos');
- }
-
- $this->typehint_area_start = (int)$prop->getAttribute('startFilePos') - 1;
-
- $preceding_newline_pos = strrpos($file_contents, "\n", $this->docblock_end - strlen($file_contents));
-
- if ($preceding_newline_pos === false) {
- $this->indentation = '';
-
- return;
- }
-
- if (!$docblock) {
- $preceding_semicolon_pos = strrpos($file_contents, ";", $preceding_newline_pos - strlen($file_contents));
-
- if ($preceding_semicolon_pos) {
- $preceding_space = substr(
- $file_contents,
- $preceding_semicolon_pos + 1,
- $preceding_newline_pos - $preceding_semicolon_pos - 1
- );
-
- if (!substr_count($preceding_space, "\n")) {
- $this->add_newline = true;
- }
- }
- }
-
- $first_line = substr($file_contents, $preceding_newline_pos + 1, $this->docblock_end - $preceding_newline_pos);
-
- $this->indentation = str_replace(ltrim($first_line), '', $first_line);
- }
-
- public function setType(
- ?string $php_type,
- string $new_type,
- string $phpdoc_type,
- bool $is_php_compatible,
- ?string $description = null
- ): void {
- $new_type = str_replace(['<mixed, mixed>', '<array-key, mixed>', '<empty, empty>'], '', $new_type);
-
- $this->new_php_type = $php_type;
- $this->new_phpdoc_type = $phpdoc_type;
- $this->new_psalm_type = $new_type;
- $this->type_is_php_compatible = $is_php_compatible;
- $this->type_description = $description;
- }
-
- /**
- * Gets a new docblock given the existing docblock, if one exists, and the updated return types
- * and/or parameters
- *
- */
- private function getDocblock(): string
- {
- $docblock = $this->stmt->getDocComment();
-
- if ($docblock) {
- $parsed_docblock = DocComment::parsePreservingLength($docblock);
- } else {
- $parsed_docblock = new ParsedDocblock('', []);
- }
-
- $modified_docblock = false;
-
- $old_phpdoc_type = null;
- if (isset($parsed_docblock->tags['var'])) {
- $old_phpdoc_type = array_shift($parsed_docblock->tags['var']);
- }
-
- if ($this->new_phpdoc_type
- && $this->new_phpdoc_type !== $old_phpdoc_type
- ) {
- $modified_docblock = true;
- $parsed_docblock->tags['var'] = [
- $this->new_phpdoc_type
- . ($this->type_description ? (' ' . $this->type_description) : ''),
- ];
- }
-
- $old_psalm_type = null;
- if (isset($parsed_docblock->tags['psalm-var'])) {
- $old_psalm_type = array_shift($parsed_docblock->tags['psalm-var']);
- }
-
- if ($this->new_psalm_type
- && $this->new_phpdoc_type !== $this->new_psalm_type
- && $this->new_psalm_type !== $old_psalm_type
- ) {
- $modified_docblock = true;
- $parsed_docblock->tags['psalm-var'] = [$this->new_psalm_type];
- }
-
- if (!$parsed_docblock->tags && !$parsed_docblock->description) {
- return '';
- }
-
- if (!$modified_docblock) {
- return (string)$docblock . "\n" . $this->indentation;
- }
-
- return $parsed_docblock->render($this->indentation);
- }
-
- /**
- * @return array<int, FileManipulation>
- */
- public static function getManipulationsForFile(string $file_path): array
- {
- if (!isset(self::$manipulators[$file_path])) {
- return [];
- }
-
- $file_manipulations = [];
-
- foreach (self::$manipulators[$file_path] as $manipulator) {
- if ($manipulator->new_php_type) {
- if ($manipulator->typehint_start && $manipulator->typehint_end) {
- $file_manipulations[$manipulator->typehint_start] = new FileManipulation(
- $manipulator->typehint_start,
- $manipulator->typehint_end,
- $manipulator->new_php_type
- );
- } else {
- $file_manipulations[$manipulator->typehint_area_start] = new FileManipulation(
- $manipulator->typehint_area_start,
- $manipulator->typehint_area_start,
- ' ' . $manipulator->new_php_type
- );
- }
- } elseif ($manipulator->new_php_type === ''
- && $manipulator->new_phpdoc_type
- && $manipulator->typehint_start
- && $manipulator->typehint_end
- ) {
- $file_manipulations[$manipulator->typehint_start] = new FileManipulation(
- $manipulator->typehint_start,
- $manipulator->typehint_end,
- ''
- );
- }
-
- if (!$manipulator->new_php_type
- || !$manipulator->type_is_php_compatible
- || $manipulator->docblock_start !== $manipulator->docblock_end
- ) {
- $file_manipulations[$manipulator->docblock_start] = new FileManipulation(
- $manipulator->docblock_start
- - ($manipulator->add_newline ? strlen($manipulator->indentation) : 0),
- $manipulator->docblock_end,
- ($manipulator->add_newline ? "\n" . $manipulator->indentation : '')
- . $manipulator->getDocblock()
- );
- }
- }
-
- return $file_manipulations;
- }
-
- public static function clearCache(): void
- {
- self::$manipulators = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkMessage.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkMessage.php
deleted file mode 100644
index bd1e146..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkMessage.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Fork;
-
-interface ForkMessage
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php
deleted file mode 100644
index edda8ca..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessDoneMessage.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Fork;
-
-/**
- * @psalm-immutable
- */
-class ForkProcessDoneMessage implements ForkMessage
-{
- /** @var mixed */
- public $data;
-
- /**
- * @param mixed $data
- */
- public function __construct($data)
- {
- $this->data = $data;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php
deleted file mode 100644
index 0794c9d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkProcessErrorMessage.php
+++ /dev/null
@@ -1,17 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Fork;
-
-/**
- * @psalm-immutable
- */
-class ForkProcessErrorMessage implements ForkMessage
-{
- /** @var string */
- public $message;
-
- public function __construct(string $message)
- {
- $this->message = $message;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php
deleted file mode 100644
index 7c43220..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/ForkTaskDoneMessage.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Fork;
-
-/**
- * @psalm-immutable
- */
-class ForkTaskDoneMessage implements ForkMessage
-{
- /** @var mixed */
- public $data;
-
- /**
- * @param mixed $data
- */
- public function __construct($data)
- {
- $this->data = $data;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php
deleted file mode 100644
index 3d0ad04..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/Pool.php
+++ /dev/null
@@ -1,492 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Fork;
-
-use Closure;
-use Exception;
-use Psalm\Config;
-use Throwable;
-
-use function array_fill_keys;
-use function array_keys;
-use function array_map;
-use function array_pop;
-use function array_values;
-use function assert;
-use function base64_decode;
-use function base64_encode;
-use function count;
-use function error_get_last;
-use function error_log;
-use function explode;
-use function extension_loaded;
-use function fclose;
-use function feof;
-use function fread;
-use function fwrite;
-use function get_class;
-use function gettype;
-use function igbinary_serialize;
-use function igbinary_unserialize;
-use function in_array;
-use function ini_get;
-use function pcntl_fork;
-use function pcntl_waitpid;
-use function pcntl_wexitstatus;
-use function pcntl_wifsignaled;
-use function pcntl_wtermsig;
-use function posix_get_last_error;
-use function posix_kill;
-use function posix_strerror;
-use function serialize;
-use function stream_select;
-use function stream_set_blocking;
-use function stream_socket_pair;
-use function stripos;
-use function strlen;
-use function strpos;
-use function substr;
-use function unserialize;
-use function usleep;
-use function version_compare;
-
-use const PHP_EOL;
-use const PHP_OS;
-use const PHP_VERSION;
-use const SIGALRM;
-use const SIGTERM;
-use const STREAM_IPPROTO_IP;
-use const STREAM_PF_UNIX;
-use const STREAM_SOCK_STREAM;
-
-/**
- * Adapted with relatively few changes from
- * https://github.com/etsy/phan/blob/1ccbe7a43a6151ca7c0759d6c53e2c3686994e53/src/Phan/ForkPool.php
- *
- * Authors: https://github.com/morria, https://github.com/TysonAndre
- *
- * Fork off to n-processes and divide up tasks between
- * each process.
- */
-class Pool
-{
- private const EXIT_SUCCESS = 0;
- private const EXIT_FAILURE = 1;
-
- /** @var Config */
- private $config;
-
- /** @var int[] */
- private $child_pid_list = [];
-
- /** @var resource[] */
- private $read_streams = [];
-
- /** @var bool */
- private $did_have_error = false;
-
- /** @var ?Closure(mixed): void */
- private $task_done_closure;
-
- public const MAC_PCRE_MESSAGE = 'Mac users: pcre.jit is set to 1 in your PHP config.' . PHP_EOL
- . 'The pcre jit is known to cause segfaults in PHP 7.3 on Macs, and Psalm' . PHP_EOL
- . 'will not execute in threaded mode to avoid indecipherable errors.' . PHP_EOL
- . 'Consider adding pcre.jit=0 to your PHP config, or upgrade to PHP 7.4.' . PHP_EOL
- . 'Relevant info: https://bugs.php.net/bug.php?id=77260';
-
- /**
- * @param Config $config
- * @param array<int, array<int, mixed>> $process_task_data_iterator
- * An array of task data items to be divided up among the
- * workers. The size of this is the number of forked processes.
- * @param Closure $startup_closure
- * A closure to execute upon starting a child
- * @param Closure(int, mixed):mixed $task_closure
- * A method to execute on each task data.
- * This closure must return an array (to be gathered).
- * @param Closure():mixed $shutdown_closure
- * A closure to execute upon shutting down a child
- * @param Closure(mixed $data):void $task_done_closure
- * A closure to execute when a task is done
- *
- * @psalm-suppress MixedAssignment
- */
- public function __construct(
- Config $config,
- array $process_task_data_iterator,
- Closure $startup_closure,
- Closure $task_closure,
- Closure $shutdown_closure,
- ?Closure $task_done_closure = null
- ) {
- $pool_size = count($process_task_data_iterator);
- $this->task_done_closure = $task_done_closure;
- $this->config = $config;
-
- assert(
- $pool_size > 1,
- 'The pool size must be >= 2 to use the fork pool.'
- );
-
- if (!extension_loaded('pcntl') || !extension_loaded('posix')) {
- echo
- 'The pcntl & posix extensions must be loaded in order for Psalm to be able to use multiple processes.'
- . PHP_EOL;
- exit(1);
- }
-
- $disabled_functions = array_map('trim', explode(',', ini_get('disable_functions')));
- if (in_array('pcntl_fork', $disabled_functions)) {
- echo "pcntl_fork() is disabled by php configuration (disable_functions directive).\n"
- . "Please enable it or run Psalm single-threaded with --threads=1 cli switch.\n";
- exit(1);
- }
-
- if (ini_get('pcre.jit') === '1'
- && PHP_OS === 'Darwin'
- && version_compare(PHP_VERSION, '7.3.0') >= 0
- && version_compare(PHP_VERSION, '7.4.0') < 0
- ) {
- die(
- self::MAC_PCRE_MESSAGE . PHP_EOL
- );
- }
-
- // We'll keep track of if this is the parent process
- // so that we can tell who will be doing the waiting
- $is_parent = false;
-
- $sockets = [];
-
- // Fork as many times as requested to get the given
- // pool size
- for ($proc_id = 0; $proc_id < $pool_size; ++$proc_id) {
- // Create an IPC socket pair.
- $sockets = stream_socket_pair(STREAM_PF_UNIX, STREAM_SOCK_STREAM, STREAM_IPPROTO_IP);
- if (!$sockets) {
- error_log('unable to create stream socket pair');
- exit(self::EXIT_FAILURE);
- }
-
- // Fork
- if (($pid = pcntl_fork()) < 0) {
- error_log(posix_strerror(posix_get_last_error()));
- exit(self::EXIT_FAILURE);
- }
-
- // Parent
- if ($pid > 0) {
- $is_parent = true;
- $this->child_pid_list[] = $pid;
- $this->read_streams[] = self::streamForParent($sockets);
- continue;
- }
-
- // Child
- if ($pid === 0) {
- $is_parent = false;
- break;
- }
- }
-
- // If we're the parent, return
- if ($is_parent) {
- return;
- }
-
- // Get the write stream for the child.
- $write_stream = self::streamForChild($sockets);
-
- // Execute anything the children wanted to execute upon
- // starting up
- $startup_closure();
-
- // Get the work for this process
- $task_data_iterator = array_values($process_task_data_iterator)[$proc_id];
-
- $task_done_buffer = '';
-
- try {
- foreach ($task_data_iterator as $i => $task_data) {
- $task_result = $task_closure($i, $task_data);
-
- $task_done_message = new ForkTaskDoneMessage($task_result);
- if ($this->config->use_igbinary) {
- $encoded_message = base64_encode(igbinary_serialize($task_done_message));
- } else {
- $encoded_message = base64_encode(serialize($task_done_message));
- }
- $serialized_message = $task_done_buffer . $encoded_message . "\n";
-
- if (strlen($serialized_message) > 200) {
- $bytes_written = @fwrite($write_stream, $serialized_message);
-
- if (strlen($serialized_message) !== $bytes_written) {
- $task_done_buffer = substr($serialized_message, $bytes_written);
- } else {
- $task_done_buffer = '';
- }
- } else {
- $task_done_buffer = $serialized_message;
- }
- }
-
- // Execute each child's shutdown closure before
- // exiting the process
- $results = $shutdown_closure();
-
- // Serialize this child's produced results and send them to the parent.
- $process_done_message = new ForkProcessDoneMessage($results ?: []);
- } catch (Throwable $t) {
- // This can happen when developing Psalm from source without running `composer update`,
- // or because of rare bugs in Psalm.
- $process_done_message = new ForkProcessErrorMessage(
- get_class($t) . ' ' . $t->getMessage() . "\n" .
- "Emitted in " . $t->getFile() . ":" . $t->getLine() . "\n" .
- "Stack trace in the forked worker:\n" .
- $t->getTraceAsString()
- );
- }
-
- if ($this->config->use_igbinary) {
- $encoded_message = base64_encode(igbinary_serialize($process_done_message));
- } else {
- $encoded_message = base64_encode(serialize($process_done_message));
- }
- $serialized_message = $task_done_buffer . $encoded_message . "\n";
-
- $bytes_to_write = strlen($serialized_message);
- $bytes_written = 0;
-
- while ($bytes_written < $bytes_to_write && !feof($write_stream)) {
- // attempt to write the remaining unsent part
- $bytes_written += @fwrite($write_stream, substr($serialized_message, $bytes_written));
-
- if ($bytes_written < $bytes_to_write) {
- // wait a bit
- usleep(500000);
- }
- }
-
- fclose($write_stream);
-
- // Children exit after completing their work
- exit(self::EXIT_SUCCESS);
- }
-
- /**
- * Prepare the socket pair to be used in a parent process and
- * return the stream the parent will use to read results.
- *
- * @param resource[] $sockets the socket pair for IPC
- *
- * @return resource
- */
- private static function streamForParent(array $sockets)
- {
- [$for_read, $for_write] = $sockets;
-
- // The parent will not use the write channel, so it
- // must be closed to prevent deadlock.
- fclose($for_write);
-
- // stream_select will be used to read multiple streams, so these
- // must be set to non-blocking mode.
- if (!stream_set_blocking($for_read, false)) {
- error_log('unable to set read stream to non-blocking');
- exit(self::EXIT_FAILURE);
- }
-
- return $for_read;
- }
-
- /**
- * Prepare the socket pair to be used in a child process and return
- * the stream the child will use to write results.
- *
- * @param resource[] $sockets the socket pair for IPC
- *
- * @return resource
- */
- private static function streamForChild(array $sockets)
- {
- [$for_read, $for_write] = $sockets;
-
- // The while will not use the read channel, so it must
- // be closed to prevent deadlock.
- fclose($for_read);
-
- return $for_write;
- }
-
- /**
- * Read the results that each child process has serialized on their write streams.
- * The results are returned in an array, one for each worker. The order of the results
- * is not maintained.
- *
- *
- * @psalm-suppress MixedAssignment
- *
- * @return list<mixed>
- */
- private function readResultsFromChildren(): array
- {
- // Create an array of all active streams, indexed by
- // resource id.
- $streams = [];
- foreach ($this->read_streams as $stream) {
- $streams[(int)$stream] = $stream;
- }
-
- // Create an array for the content received on each stream,
- // indexed by resource id.
- $content = array_fill_keys(array_keys($streams), '');
-
- $terminationMessages = [];
-
- // Read the data off of all the stream.
- while (count($streams) > 0) {
- $needs_read = array_values($streams);
- $needs_write = null;
- $needs_except = null;
-
- // Wait for data on at least one stream.
- $num = @stream_select($needs_read, $needs_write, $needs_except, null /* no timeout */);
- if ($num === false) {
- $err = error_get_last();
-
- // stream_select returns false when the `select` system call is interrupted by an incoming signal
- if (isset($err['message']) && stripos($err['message'], 'interrupted system call') === false) {
- error_log('unable to select on read stream');
- exit(self::EXIT_FAILURE);
- }
-
- continue;
- }
-
- // For each stream that was ready, read the content.
- foreach ($needs_read as $file) {
- $buffer = fread($file, 1024);
- if ($buffer !== false) {
- $content[(int)$file] .= $buffer;
- }
-
- if (strpos($buffer, "\n") !== false) {
- $serialized_messages = explode("\n", $content[(int)$file]);
- $content[(int)$file] = array_pop($serialized_messages);
-
- foreach ($serialized_messages as $serialized_message) {
- if ($this->config->use_igbinary) {
- $message = igbinary_unserialize(base64_decode($serialized_message, true));
- } else {
- $message = unserialize(base64_decode($serialized_message, true));
- }
-
- if ($message instanceof ForkProcessDoneMessage) {
- $terminationMessages[] = $message->data;
- } elseif ($message instanceof ForkTaskDoneMessage) {
- if ($this->task_done_closure !== null) {
- ($this->task_done_closure)($message->data);
- }
- } elseif ($message instanceof ForkProcessErrorMessage) {
- // Kill all children
- foreach ($this->child_pid_list as $child_pid) {
- /**
- * SIGTERM does not exist on windows
- * @psalm-suppress UnusedPsalmSuppress
- * @psalm-suppress UndefinedConstant
- * @psalm-suppress MixedArgument
- */
- posix_kill($child_pid, SIGTERM);
- }
- throw new Exception($message->message);
- } else {
- error_log('Child should return ForkMessage - response type=' . gettype($message));
- $this->did_have_error = true;
- }
- }
- }
-
- // If the stream has closed, stop trying to select on it.
- if (feof($file)) {
- if ($content[(int)$file] !== '') {
- error_log('Child did not send full message before closing the connection');
- $this->did_have_error = true;
- }
-
- fclose($file);
- unset($streams[(int)$file]);
- }
- }
- }
-
- return $terminationMessages;
- }
-
- /**
- * Wait for all child processes to complete
- *
- * @return list<mixed>
- */
- public function wait(): array
- {
- $ignore_return_code = false;
- try {
- // Read all the streams from child processes into an array.
- $content = $this->readResultsFromChildren();
- } catch (Throwable $e) {
- // If children were killed because one of them threw an exception we don't care about return codes.
- $ignore_return_code = true;
- // PHP guarantees finally is run even after throwing
- throw $e;
- } finally {
- // Wait for all children to return
- foreach ($this->child_pid_list as $child_pid) {
- $process_lookup = posix_kill($child_pid, 0);
-
- $status = 0;
-
- if ($process_lookup) {
- /**
- * SIGALRM does not exist on windows
- * @psalm-suppress UnusedPsalmSuppress
- * @psalm-suppress UndefinedConstant
- * @psalm-suppress MixedArgument
- */
- posix_kill($child_pid, SIGALRM);
-
- if (pcntl_waitpid($child_pid, $status) < 0) {
- error_log(posix_strerror(posix_get_last_error()));
- }
- }
-
- // Check to see if the child died a graceful death
- if (!$ignore_return_code && pcntl_wifsignaled($status)) {
- $return_code = pcntl_wexitstatus($status);
- $term_sig = pcntl_wtermsig($status);
-
- /**
- * SIGALRM does not exist on windows
- * @psalm-suppress UnusedPsalmSuppress
- * @psalm-suppress UndefinedConstant
- */
- if ($term_sig !== SIGALRM) {
- $this->did_have_error = true;
- error_log("Child terminated with return code $return_code and signal $term_sig");
- }
- }
- }
- }
-
- return $content;
- }
-
- /**
- * Returns true if this had an error, e.g. due to memory limits or due to a child process crashing.
- *
- */
- public function didHaveError(): bool
- {
- return $this->did_have_error;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php
deleted file mode 100644
index 0ca2e1a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Fork;
-
-use Composer\XdebugHandler\XdebugHandler;
-
-use function array_filter;
-use function extension_loaded;
-use function file_get_contents;
-use function file_put_contents;
-use function implode;
-use function preg_replace;
-
-/**
- * @internal
- */
-class PsalmRestarter extends XdebugHandler
-{
- /**
- * @var bool
- */
- private $required = false;
-
- /**
- * @var string[]
- */
- private $disabledExtensions = [];
-
- public function disableExtension(string $disabledExtension): void
- {
- $this->disabledExtensions[] = $disabledExtension;
- }
-
- /**
- * No type hint to allow xdebug-handler v1 and v2 usage
- * @param bool $default
- */
- protected function requiresRestart($default): bool
- {
- $this->required = (bool) array_filter(
- $this->disabledExtensions,
- function (string $extension): bool {
- return extension_loaded($extension);
- }
- );
-
- return $default || $this->required;
- }
-
- /**
- * No type hint to allow xdebug-handler v1 and v2 usage
- * @param string|string[] $command
- */
- protected function restart($command): void
- {
- if ($this->required && $this->tmpIni) {
- $regex = '/^\s*(extension\s*=.*(' . implode('|', $this->disabledExtensions) . ').*)$/mi';
- $content = file_get_contents($this->tmpIni);
-
- $content = preg_replace($regex, ';$1', $content);
-
- file_put_contents($this->tmpIni, $content);
- }
-
- /** @psalm-suppress PossiblyInvalidArgument */
- parent::restart($command);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/IncludeCollector.php b/vendor/vimeo/psalm/src/Psalm/Internal/IncludeCollector.php
deleted file mode 100644
index 6cd2477..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/IncludeCollector.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use function array_diff;
-use function array_merge;
-use function array_unique;
-use function array_values;
-use function get_included_files;
-use function preg_grep;
-
-use const PREG_GREP_INVERT;
-
-/**
- * Include collector
- *
- * Used to execute code that may cause file inclusions, and report what files have been included
- * NOTE: dependencies of this class should be kept at minimum, as it's used before autoloader is
- * registered.
- */
-final class IncludeCollector
-{
- /** @var list<string> */
- private $included_files = [];
-
- /**
- * @template T
- * @param callable():T $f
- * @return T
- */
- public function runAndCollect(callable $f)
- {
- $before = get_included_files();
- $ret = $f();
- $after = get_included_files();
-
- $included = array_diff($after, $before);
-
- $this->included_files = array_values(array_unique(array_merge($this->included_files, $included)));
-
- return $ret;
- }
-
- /** @return list<string> */
- public function getIncludedFiles(): array
- {
- return $this->included_files;
- }
-
- /** @return list<string> */
- public function getFilteredIncludedFiles(): array
- {
- return array_values(preg_grep('@^phar://@', $this->getIncludedFiles(), PREG_GREP_INVERT));
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Json/Json.php b/vendor/vimeo/psalm/src/Psalm/Internal/Json/Json.php
deleted file mode 100644
index 2e2445f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Json/Json.php
+++ /dev/null
@@ -1,46 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Json;
-
-use RuntimeException;
-
-use function json_encode;
-use function json_last_error_msg;
-
-use const JSON_PRETTY_PRINT;
-use const JSON_UNESCAPED_SLASHES;
-use const JSON_UNESCAPED_UNICODE;
-
-/**
- * Provides ability of pretty printed JSON output.
- */
-class Json
-{
- public const PRETTY = JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE;
-
- /**
- * @var int
- */
- public const DEFAULT = 0;
-
- /**
- * @param mixed $data
- *
- *
- * @psalm-pure
- */
- public static function encode($data, ?int $options = null): string
- {
- if ($options === null) {
- $options = self::DEFAULT;
- }
-
- $result = json_encode($data, $options);
- if ($result === false) {
- /** @psalm-suppress ImpureFunctionCall */
- throw new RuntimeException('Cannot create JSON string: '.json_last_error_msg());
- }
-
- return $result;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php
deleted file mode 100644
index 26cd646..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php
+++ /dev/null
@@ -1,79 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer\Client;
-
-use Amp\Promise;
-use Generator;
-use JsonMapper;
-use LanguageServerProtocol\Diagnostic;
-use LanguageServerProtocol\TextDocumentIdentifier;
-use LanguageServerProtocol\TextDocumentItem;
-use Psalm\Internal\LanguageServer\ClientHandler;
-
-use function Amp\call;
-
-/**
- * Provides method handlers for all textDocument/* methods
- */
-class TextDocument
-{
- /**
- * @var ClientHandler
- */
- private $handler;
-
- /**
- * @var JsonMapper
- */
- private $mapper;
-
- public function __construct(ClientHandler $handler, JsonMapper $mapper)
- {
- $this->handler = $handler;
- $this->mapper = $mapper;
- }
-
- /**
- * Diagnostics notification are sent from the server to the client to signal results of validation runs.
- *
- * @param Diagnostic[] $diagnostics
- */
- public function publishDiagnostics(string $uri, array $diagnostics): void
- {
- $this->handler->notify('textDocument/publishDiagnostics', [
- 'uri' => $uri,
- 'diagnostics' => $diagnostics,
- ]);
- }
-
- /**
- * The content request is sent from a server to a client
- * to request the current content of a text document identified by the URI
- *
- * @param TextDocumentIdentifier $textDocument The document to get the content for
- *
- * @return Promise<TextDocumentItem> The document's current content
- */
- public function xcontent(TextDocumentIdentifier $textDocument): Promise
- {
- return call(
- /**
- * @return Generator<int, Promise<object>, object, TextDocumentItem>
- */
- function () use ($textDocument) {
- /** @var Promise<object> */
- $promise = $this->handler->request(
- 'textDocument/xcontent',
- ['textDocument' => $textDocument]
- );
-
- $result = yield $promise;
-
- /** @var TextDocumentItem */
- return $this->mapper->map($result, new TextDocumentItem);
- }
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php
deleted file mode 100644
index 8c70156..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use AdvancedJsonRpc\Notification;
-use AdvancedJsonRpc\Request;
-use AdvancedJsonRpc\Response;
-use AdvancedJsonRpc\SuccessResponse;
-use Amp\Deferred;
-use Amp\Promise;
-use Generator;
-
-use function Amp\call;
-use function error_log;
-
-/**
- * @internal
- */
-class ClientHandler
-{
- /**
- * @var ProtocolReader
- */
- public $protocolReader;
-
- /**
- * @var ProtocolWriter
- */
- public $protocolWriter;
-
- /**
- * @var IdGenerator
- */
- public $idGenerator;
-
- public function __construct(ProtocolReader $protocolReader, ProtocolWriter $protocolWriter)
- {
- $this->protocolReader = $protocolReader;
- $this->protocolWriter = $protocolWriter;
- $this->idGenerator = new IdGenerator;
- }
-
- /**
- * Sends a request to the client and returns a promise that is resolved with the result or rejected with the error
- *
- * @param string $method The method to call
- * @param array|object $params The method parameters
- *
- * @return Promise<mixed> Resolved with the result of the request or rejected with an error
- */
- public function request(string $method, $params): Promise
- {
- $id = $this->idGenerator->generate();
-
- return call(
- /**
- * @return Generator<int, Promise, mixed, Promise<mixed>>
- */
- function () use ($id, $method, $params): Generator {
- yield $this->protocolWriter->write(
- new Message(
- new Request($id, $method, (object) $params)
- )
- );
-
- $deferred = new Deferred();
-
- $listener =
- function (Message $msg) use ($id, $deferred, &$listener): void {
- error_log('request handler');
- /**
- * @psalm-suppress UndefinedPropertyFetch
- * @psalm-suppress MixedArgument
- */
- if ($msg->body
- && Response::isResponse($msg->body)
- && $msg->body->id === $id
- ) {
- // Received a response
- $this->protocolReader->removeListener('message', $listener);
- if (SuccessResponse::isSuccessResponse($msg->body)) {
- $deferred->resolve($msg->body->result);
- } else {
- $deferred->fail($msg->body->error);
- }
- }
- };
- $this->protocolReader->on('message', $listener);
-
- return $deferred->promise();
- }
- );
- }
-
- /**
- * Sends a notification to the client
- *
- * @param string $method The method to call
- * @param array|object $params The method parameters
- */
- public function notify(string $method, $params): void
- {
- $this->protocolWriter->write(
- new Message(
- new Notification($method, (object)$params)
- )
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterInterface.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterInterface.php
deleted file mode 100644
index 9f8f80b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterInterface.php
+++ /dev/null
@@ -1,71 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-/**
- * Event Emitter Interface
- *
- * Anything that accepts listeners and emits events should implement this
- * interface.
- *
- * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-interface EmitterInterface
-{
- /**
- * Subscribe to an event.
- */
- public function on(string $eventName, callable $callBack, int $priority = 100): void;
-
- /**
- * Emits an event.
- *
- * This method will return true if 0 or more listeners were successfully
- * handled. false is returned if one of the events broke the event chain.
- *
- * If the continueCallBack is specified, this callback will be called every
- * time before the next event handler is called.
- *
- * If the continueCallback returns false, event propagation stops. This
- * allows you to use the eventEmitter as a means for listeners to implement
- * functionality in your application, and break the event loop as soon as
- * some condition is fulfilled.
- *
- * Note that returning false from an event subscriber breaks propagation
- * and returns false, but if the continue-callback stops propagation, this
- * is still considered a 'successful' operation and returns true.
- *
- * Lastly, if there are 5 event handlers for an event. The continueCallback
- * will be called at most 4 times.
- *
- * @param list<mixed> $arguments
- */
- public function emit(
- string $eventName,
- array $arguments = [],
- ?callable $continueCallBack = null
- ): void;
-
- /**
- * Returns the list of listeners for an event.
- *
- * The list is returned as an array, and the list of events are sorted by
- * their priority.
- *
- * @return callable[]
- */
- public function listeners(string $eventName): array;
-
- /**
- * Removes a specific listener from an event.
- *
- * If the listener could not be found, this method will return false. If it
- * was removed it will return true.
- * @psalm-suppress PossiblyUnusedReturnValue
- */
- public function removeListener(string $eventName, callable $listener): bool;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterTrait.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterTrait.php
deleted file mode 100644
index 50535d0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/EmitterTrait.php
+++ /dev/null
@@ -1,157 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use function array_multisort;
-use function call_user_func_array;
-use function count;
-
-use const SORT_NUMERIC;
-
-/**
- * Event Emitter Trait
- *
- * This trait contains all the basic functions to implement an
- * EventEmitterInterface.
- *
- * Using the trait + interface allows you to add EventEmitter capabilities
- * without having to change your base-class.
- *
- * @copyright Copyright (C) fruux GmbH (https://fruux.com/)
- * @author Evert Pot (http://evertpot.com/)
- * @license http://sabre.io/license/ Modified BSD License
- */
-trait EmitterTrait
-{
- /**
- * The list of listeners
- *
- * @var array<string, array{0: bool, 1: int[], 2: callable[]}>
- */
- protected $listeners = [];
-
- /**
- * Subscribe to an event.
- */
- public function on(string $eventName, callable $callBack, int $priority = 100): void
- {
- if (!isset($this->listeners[$eventName])) {
- $this->listeners[$eventName] = [
- true, // If there's only one item, it's sorted
- [$priority],
- [$callBack],
- ];
- } else {
- $this->listeners[$eventName][0] = false; // marked as unsorted
- $this->listeners[$eventName][1][] = $priority;
- $this->listeners[$eventName][2][] = $callBack;
- }
- }
-
- /**
- * Emits an event.
- *
- * This method will return true if 0 or more listeners were successfully
- * handled. false is returned if one of the events broke the event chain.
- *
- * If the continueCallBack is specified, this callback will be called every
- * time before the next event handler is called.
- *
- * If the continueCallback returns false, event propagation stops. This
- * allows you to use the eventEmitter as a means for listeners to implement
- * functionality in your application, and break the event loop as soon as
- * some condition is fulfilled.
- *
- * Note that returning false from an event subscriber breaks propagation
- * and returns false, but if the continue-callback stops propagation, this
- * is still considered a 'successful' operation and returns true.
- *
- * Lastly, if there are 5 event handlers for an event. The continueCallback
- * will be called at most 4 times.
- *
- * @param list<mixed> $arguments
- */
- public function emit(
- string $eventName,
- array $arguments = [],
- ?callable $continueCallBack = null
- ): void {
- if ($continueCallBack === null) {
- foreach ($this->listeners($eventName) as $listener) {
- /** @psalm-suppress MixedAssignment */
- $result = call_user_func_array($listener, $arguments);
- if ($result === false) {
- return;
- }
- }
- } else {
- $listeners = $this->listeners($eventName);
- $counter = count($listeners);
-
- foreach ($listeners as $listener) {
- --$counter;
- /** @psalm-suppress MixedAssignment */
- $result = call_user_func_array($listener, $arguments);
- if ($result === false) {
- return;
- }
-
- if ($counter > 0) {
- if (!$continueCallBack()) {
- break;
- }
- }
- }
- }
- }
-
- /**
- * Returns the list of listeners for an event.
- *
- * The list is returned as an array, and the list of events are sorted by
- * their priority.
- *
- * @return callable[]
- */
- public function listeners(string $eventName): array
- {
- if (!isset($this->listeners[$eventName])) {
- return [];
- }
-
- // The list is not sorted
- if (!$this->listeners[$eventName][0]) {
- // Sorting
- array_multisort($this->listeners[$eventName][1], SORT_NUMERIC, $this->listeners[$eventName][2]);
-
- // Marking the listeners as sorted
- $this->listeners[$eventName][0] = true;
- }
-
- return $this->listeners[$eventName][2];
- }
-
- /**
- * Removes a specific listener from an event.
- *
- * If the listener could not be found, this method will return false. If it
- * was removed it will return true.
- */
- public function removeListener(string $eventName, callable $listener): bool
- {
- if (!isset($this->listeners[$eventName])) {
- return false;
- }
- foreach ($this->listeners[$eventName][2] as $index => $check) {
- if ($check === $listener) {
- unset($this->listeners[$eventName][1][$index], $this->listeners[$eventName][2][$index]);
-
- return true;
- }
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/IdGenerator.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/IdGenerator.php
deleted file mode 100644
index a7eee0f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/IdGenerator.php
+++ /dev/null
@@ -1,25 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-/**
- * Generates unique, incremental IDs for use as request IDs
- */
-class IdGenerator
-{
- /**
- * @var int
- */
- public $counter = 1;
-
- /**
- * Returns a unique ID
- *
- */
- public function generate(): int
- {
- return $this->counter++;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php
deleted file mode 100644
index 9c2ef14..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use JsonMapper;
-use Psalm\Internal\LanguageServer\Client\TextDocument as ClientTextDocument;
-
-/**
- * @internal
- */
-class LanguageClient
-{
- /**
- * Handles textDocument/* methods
- *
- * @var ClientTextDocument
- */
- public $textDocument;
-
- /**
- * The client handler
- *
- * @var ClientHandler
- */
- private $handler;
-
- public function __construct(ProtocolReader $reader, ProtocolWriter $writer)
- {
- $this->handler = new ClientHandler($reader, $writer);
- $mapper = new JsonMapper;
-
- $this->textDocument = new ClientTextDocument($this->handler, $mapper);
- }
-
- /**
- * Send a log message to the client.
- *
- * @param string $message The message to send to the client.
- * @psalm-param 1|2|3|4 $type
- * @param int $type The log type:
- * - 1 = Error
- * - 2 = Warning
- * - 3 = Info
- * - 4 = Log
- */
- public function logMessage(string $message, int $type = 4, string $method = 'window/logMessage'): void
- {
- // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage
-
- if ($type < 1 || $type > 4) {
- $type = 4;
- }
-
- $this->handler->notify(
- $method,
- [
- 'type' => $type,
- 'message' => $message
- ]
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php
deleted file mode 100644
index 30c48be..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php
+++ /dev/null
@@ -1,575 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use AdvancedJsonRpc\Dispatcher;
-use AdvancedJsonRpc\Error;
-use AdvancedJsonRpc\ErrorCode;
-use AdvancedJsonRpc\ErrorResponse;
-use AdvancedJsonRpc\Request;
-use AdvancedJsonRpc\Response;
-use AdvancedJsonRpc\SuccessResponse;
-use Amp\Promise;
-use Amp\Success;
-use Generator;
-use InvalidArgumentException;
-use LanguageServerProtocol\ClientCapabilities;
-use LanguageServerProtocol\CompletionOptions;
-use LanguageServerProtocol\Diagnostic;
-use LanguageServerProtocol\DiagnosticSeverity;
-use LanguageServerProtocol\InitializeResult;
-use LanguageServerProtocol\Position;
-use LanguageServerProtocol\Range;
-use LanguageServerProtocol\SaveOptions;
-use LanguageServerProtocol\ServerCapabilities;
-use LanguageServerProtocol\SignatureHelpOptions;
-use LanguageServerProtocol\TextDocumentSyncKind;
-use LanguageServerProtocol\TextDocumentSyncOptions;
-use Psalm\Config;
-use Psalm\Internal\Analyzer\IssueData;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\LanguageServer\Server\TextDocument as ServerTextDocument;
-use Psalm\Internal\LanguageServer\Server\Workspace as ServerWorkspace;
-use Psalm\IssueBuffer;
-use Throwable;
-
-use function Amp\asyncCoroutine;
-use function Amp\call;
-use function array_combine;
-use function array_keys;
-use function array_map;
-use function array_shift;
-use function array_unshift;
-use function explode;
-use function implode;
-use function max;
-use function parse_url;
-use function rawurlencode;
-use function realpath;
-use function str_replace;
-use function strpos;
-use function substr;
-use function trim;
-use function urldecode;
-
-/**
- * @internal
- */
-class LanguageServer extends Dispatcher
-{
- /**
- * Handles textDocument/* method calls
- *
- * @var ?ServerTextDocument
- */
- public $textDocument;
-
- /**
- * Handles workspace/* method calls
- *
- * @var ?ServerWorkspace
- */
- public $workspace;
-
- /**
- * @var ProtocolReader
- */
- protected $protocolReader;
-
- /**
- * @var ProtocolWriter
- */
- protected $protocolWriter;
-
- /**
- * @var LanguageClient
- */
- public $client;
-
- /**
- * @var ProjectAnalyzer
- */
- protected $project_analyzer;
-
- /**
- * @var array<string, string>
- */
- protected $onsave_paths_to_analyze = [];
-
- /**
- * @var array<string, string>
- */
- protected $onchange_paths_to_analyze = [];
-
- /**
- * @var array<string, list<IssueData>>
- */
- protected $current_issues = [];
-
- public function __construct(
- ProtocolReader $reader,
- ProtocolWriter $writer,
- ProjectAnalyzer $project_analyzer
- ) {
- parent::__construct($this, '/');
- $this->project_analyzer = $project_analyzer;
-
- $this->protocolWriter = $writer;
-
- $this->protocolReader = $reader;
- $this->protocolReader->on(
- 'close',
- function (): void {
- $this->shutdown();
- $this->exit();
- }
- );
- $this->protocolReader->on(
- 'message',
- asyncCoroutine(
- /**
- * @return Generator<int, Promise, mixed, void>
- */
- function (Message $msg): Generator {
- if (!$msg->body) {
- return;
- }
-
- // Ignore responses, this is the handler for requests and notifications
- if (Response::isResponse($msg->body)) {
- return;
- }
-
- $result = null;
- $error = null;
- try {
- // Invoke the method handler to get a result
- /**
- * @var Promise
- */
- $dispatched = $this->dispatch($msg->body);
- /** @psalm-suppress MixedAssignment */
- $result = yield $dispatched;
- } catch (Error $e) {
- // If a ResponseError is thrown, send it back in the Response
- $error = $e;
- } catch (Throwable $e) {
- // If an unexpected error occurred, send back an INTERNAL_ERROR error response
- $error = new Error(
- (string) $e,
- ErrorCode::INTERNAL_ERROR,
- null,
- $e
- );
- }
- // Only send a Response for a Request
- // Notifications do not send Responses
- /**
- * @psalm-suppress UndefinedPropertyFetch
- * @psalm-suppress MixedArgument
- */
- if (Request::isRequest($msg->body)) {
- if ($error !== null) {
- $responseBody = new ErrorResponse($msg->body->id, $error);
- } else {
- $responseBody = new SuccessResponse($msg->body->id, $result);
- }
- yield $this->protocolWriter->write(new Message($responseBody));
- }
- }
- )
- );
-
- $this->protocolReader->on(
- 'readMessageGroup',
- function (): void {
- $this->doAnalysis();
- }
- );
-
- $this->client = new LanguageClient($reader, $writer);
-
- $this->verboseLog("Language server has started.");
- }
-
- /**
- * The initialize request is sent as the first request from the client to the server.
- *
- * @param ClientCapabilities $capabilities The capabilities provided by the client (editor)
- * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open.
- * @param int|null $processId The process Id of the parent process that started the server.
- * Is null if the process has not been started by another process. If the parent process is
- * not alive then the server should exit (see exit notification) its process.
- * @psalm-return Promise<InitializeResult>
- * @psalm-suppress PossiblyUnusedMethod
- */
- public function initialize(
- ClientCapabilities $capabilities,
- ?string $rootPath = null,
- ?int $processId = null
- ): Promise {
- return call(
- /** @return Generator<int, true, mixed, InitializeResult> */
- function () {
- $this->verboseLog("Initializing...");
- $this->clientStatus('initializing');
-
- // Eventually, this might block on something. Leave it as a generator.
- /** @psalm-suppress TypeDoesNotContainType */
- if (false) {
- yield true;
- }
-
- $this->verboseLog("Initializing: Getting code base...");
- $this->clientStatus('initializing', 'getting code base');
- $codebase = $this->project_analyzer->getCodebase();
-
- $this->verboseLog("Initializing: Scanning files...");
- $this->clientStatus('initializing', 'scanning files');
- $codebase->scanFiles($this->project_analyzer->threads);
-
- $this->verboseLog("Initializing: Registering stub files...");
- $this->clientStatus('initializing', 'registering stub files');
- $codebase->config->visitStubFiles($codebase);
-
- if ($this->textDocument === null) {
- $this->textDocument = new ServerTextDocument(
- $this,
- $codebase,
- $this->project_analyzer
- );
- }
-
- if ($this->workspace === null) {
- $this->workspace = new ServerWorkspace(
- $this,
- $codebase,
- $this->project_analyzer
- );
- }
-
- $serverCapabilities = new ServerCapabilities();
-
- $textDocumentSyncOptions = new TextDocumentSyncOptions();
-
- $textDocumentSyncOptions->openClose = true;
-
- $saveOptions = new SaveOptions();
- $saveOptions->includeText = true;
- $textDocumentSyncOptions->save = $saveOptions;
-
- if ($this->project_analyzer->onchange_line_limit === 0) {
- $textDocumentSyncOptions->change = TextDocumentSyncKind::NONE;
- } else {
- $textDocumentSyncOptions->change = TextDocumentSyncKind::FULL;
- }
-
- $serverCapabilities->textDocumentSync = $textDocumentSyncOptions;
-
- // Support "Find all symbols"
- $serverCapabilities->documentSymbolProvider = false;
- // Support "Find all symbols in workspace"
- $serverCapabilities->workspaceSymbolProvider = false;
- // Support "Go to definition"
- $serverCapabilities->definitionProvider = true;
- // Support "Find all references"
- $serverCapabilities->referencesProvider = false;
- // Support "Hover"
- $serverCapabilities->hoverProvider = true;
- // Support "Completion"
- $serverCapabilities->codeActionProvider = true;
- // Support "Code Actions"
-
- if ($this->project_analyzer->provide_completion) {
- $serverCapabilities->completionProvider = new CompletionOptions();
- $serverCapabilities->completionProvider->resolveProvider = false;
- $serverCapabilities->completionProvider->triggerCharacters = ['$', '>', ':',"[", "(", ",", " "];
- }
-
- $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions(['(', ',']);
-
- // Support global references
- $serverCapabilities->xworkspaceReferencesProvider = false;
- $serverCapabilities->xdefinitionProvider = false;
- $serverCapabilities->dependenciesProvider = false;
-
- $this->verboseLog("Initializing: Complete.");
- $this->clientStatus('initialized');
- return new InitializeResult($serverCapabilities);
- }
- );
- }
-
- /**
- * @psalm-suppress PossiblyUnusedMethod
- *
- */
- public function initialized(): void
- {
- $this->clientStatus('running');
- }
-
- public function queueTemporaryFileAnalysis(string $file_path, string $uri): void
- {
- $this->onchange_paths_to_analyze[$file_path] = $uri;
- }
-
- public function queueFileAnalysis(string $file_path, string $uri): void
- {
- $this->onsave_paths_to_analyze[$file_path] = $uri;
- }
-
- public function doAnalysis(): void
- {
- $this->clientStatus('analyzing');
-
- try {
- $codebase = $this->project_analyzer->getCodebase();
-
- $all_files_to_analyze = $this->onchange_paths_to_analyze + $this->onsave_paths_to_analyze;
-
- if (!$all_files_to_analyze) {
- return;
- }
-
- if ($this->onsave_paths_to_analyze) {
- $codebase->reloadFiles($this->project_analyzer, array_keys($this->onsave_paths_to_analyze));
- }
-
- if ($this->onchange_paths_to_analyze) {
- $codebase->reloadFiles($this->project_analyzer, array_keys($this->onchange_paths_to_analyze));
- }
-
- $all_file_paths_to_analyze = array_keys($all_files_to_analyze);
- $codebase->analyzer->addFilesToAnalyze(
- array_combine($all_file_paths_to_analyze, $all_file_paths_to_analyze)
- );
- $codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false);
-
- $this->emitIssues($all_files_to_analyze);
-
- $this->onchange_paths_to_analyze = [];
- $this->onsave_paths_to_analyze = [];
- } finally {
- // we are done, so set the status back to running
- $this->clientStatus('running');
- }
- }
-
- /**
- * @param array<string, string> $uris
- *
- */
- public function emitIssues(array $uris): void
- {
- $data = IssueBuffer::clear();
- $this->current_issues = $data;
-
- foreach ($uris as $file_path => $uri) {
- $diagnostics = array_map(
- function (IssueData $issue_data): Diagnostic {
- //$check_name = $issue->check_name;
- $description = $issue_data->message;
- $severity = $issue_data->severity;
-
- $start_line = max($issue_data->line_from, 1);
- $end_line = $issue_data->line_to;
- $start_column = $issue_data->column_from;
- $end_column = $issue_data->column_to;
- // Language server has 0 based lines and columns, phan has 1-based lines and columns.
- $range = new Range(
- new Position($start_line - 1, $start_column - 1),
- new Position($end_line - 1, $end_column - 1)
- );
- switch ($severity) {
- case Config::REPORT_INFO:
- $diagnostic_severity = DiagnosticSeverity::WARNING;
- break;
- case Config::REPORT_ERROR:
- default:
- $diagnostic_severity = DiagnosticSeverity::ERROR;
- break;
- }
- $diagnostic = new Diagnostic(
- $description,
- $range,
- null,
- $diagnostic_severity,
- 'Psalm'
- );
-
- //$code = 'PS' . \str_pad((string) $issue_data->shortcode, 3, "0", \STR_PAD_LEFT);
- $code = $issue_data->link;
-
- if ($this->project_analyzer->language_server_use_extended_diagnostic_codes) {
- // Added in VSCode 1.43.0 and will be part of the LSP 3.16.0 standard.
- // Since this new functionality is not backwards compatible, we use a
- // configuration option so the end user must opt in to it using the cli argument.
- // https://github.com/microsoft/vscode/blob/1.43.0/src/vs/vscode.d.ts#L4688-L4699
-
- /** @psalm-suppress InvalidPropertyAssignmentValue */
- $diagnostic->code = [
- "value" => $code,
- "target" => $issue_data->link,
- ];
- } else {
- // the Diagnostic constructor only takes `int` for the code, but the property can be
- // `int` or `string`, so we set the property directly because we want to use a `string`
- /** @psalm-suppress InvalidPropertyAssignmentValue */
- $diagnostic->code = $code;
- }
-
- return $diagnostic;
- },
- $data[$file_path] ?? []
- );
-
- $this->client->textDocument->publishDiagnostics($uri, $diagnostics);
- }
- }
-
- /**
- * The shutdown request is sent from the client to the server. It asks the server to shut down,
- * but to not exit (otherwise the response might not be delivered correctly to the client).
- * There is a separate exit notification that asks the server to exit.
- * @psalm-suppress PossiblyUnusedReturnValue
- */
- public function shutdown(): Promise
- {
- $this->clientStatus('closing');
- $this->verboseLog("Shutting down...");
- $codebase = $this->project_analyzer->getCodebase();
- $scanned_files = $codebase->scanner->getScannedFiles();
- $codebase->file_reference_provider->updateReferenceCache(
- $codebase,
- $scanned_files
- );
- $this->clientStatus('closed');
- return new Success(null);
- }
-
- /**
- * A notification to ask the server to exit its process.
- *
- */
- public function exit(): void
- {
- exit(0);
- }
-
-
- /**
- * Send log message to the client
- *
- * @param string $message The log message to send to the client.
- * @psalm-param 1|2|3|4 $type
- * @param int $type The log type:
- * - 1 = Error
- * - 2 = Warning
- * - 3 = Info
- * - 4 = Log
- */
- public function verboseLog(string $message, int $type = 4): void
- {
- if ($this->project_analyzer->language_server_verbose) {
- try {
- $this->client->logMessage(
- '[Psalm ' .PSALM_VERSION. ' - PHP Language Server] ' . $message,
- $type
- );
- } catch (Throwable $err) {
- // do nothing
- }
- }
- new Success(null);
- }
-
- /**
- * Send status message to client. This is the same as sending a log message,
- * except this is meant for parsing by the client to present status updates in a UI.
- *
- * @param string $status The log message to send to the client. Should not contain colons `:`.
- * @param string|null $additional_info This is additional info that the client
- * can use as part of the display message.
- */
- private function clientStatus(string $status, ?string $additional_info = null): void
- {
- try {
- // here we send a notification to the client using the telemetry notification method
- $this->client->logMessage(
- $status . (!empty($additional_info) ? ': ' . $additional_info : ''),
- 3,
- 'telemetry/event'
- );
- } catch (Throwable $err) {
- // do nothing
- }
- new Success(null);
- }
-
- /**
- * Transforms an absolute file path into a URI as used by the language server protocol.
- *
- * @psalm-pure
- */
- public static function pathToUri(string $filepath): string
- {
- $filepath = trim(str_replace('\\', '/', $filepath), '/');
- $parts = explode('/', $filepath);
- // Don't %-encode the colon after a Windows drive letter
- $first = array_shift($parts);
- if (substr($first, -1) !== ':') {
- $first = rawurlencode($first);
- }
- $parts = array_map('rawurlencode', $parts);
- array_unshift($parts, $first);
- $filepath = implode('/', $parts);
-
- return 'file:///' . $filepath;
- }
-
- /**
- * Transforms URI into file path
- *
- *
- */
- public static function uriToPath(string $uri): string
- {
- $fragments = parse_url($uri);
- if ($fragments === false
- || !isset($fragments['scheme'])
- || $fragments['scheme'] !== 'file'
- || !isset($fragments['path'])
- ) {
- throw new InvalidArgumentException("Not a valid file URI: $uri");
- }
-
- $filepath = urldecode($fragments['path']);
-
- if (strpos($filepath, ':') !== false) {
- if ($filepath[0] === '/') {
- $filepath = substr($filepath, 1);
- }
- $filepath = str_replace('/', '\\', $filepath);
- }
-
- $realpath = realpath($filepath);
- if ($realpath !== false) {
- return $realpath;
- }
-
- return $filepath;
- }
-
- /**
- * Get the value of current_issues
- *
- * @return array<string, list<IssueData>>
- */
- public function getCurrentIssues(): array
- {
- return $this->current_issues;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php
deleted file mode 100644
index be33625..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use AdvancedJsonRpc\Message as MessageBody;
-
-use function array_pop;
-use function explode;
-use function strlen;
-
-/**
- * @internal
- */
-class Message
-{
- /**
- * @var ?MessageBody
- */
- public $body;
-
- /**
- * @var string[]
- */
- public $headers;
-
- /**
- * Parses a message
- *
- *
- * @psalm-suppress UnusedMethod
- */
- public static function parse(string $msg): Message
- {
- $obj = new self;
- $parts = explode("\r\n", $msg);
- $obj->body = MessageBody::parse(array_pop($parts));
- foreach ($parts as $line) {
- if ($line) {
- $pair = explode(': ', $line);
- $obj->headers[$pair[0]] = $pair[1];
- }
- }
-
- return $obj;
- }
-
- /**
- * @param string[] $headers
- */
- public function __construct(?MessageBody $body = null, array $headers = [])
- {
- $this->body = $body;
- if (!isset($headers['Content-Type'])) {
- $headers['Content-Type'] = 'application/vscode-jsonrpc; charset=utf8';
- }
- $this->headers = $headers;
- }
-
- public function __toString(): string
- {
- $body = (string)$this->body;
- $contentLength = strlen($body);
- $this->headers['Content-Length'] = (string) $contentLength;
- $headers = '';
- foreach ($this->headers as $name => $value) {
- $headers .= "$name: $value\r\n";
- }
-
- return $headers . "\r\n" . $body;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolReader.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolReader.php
deleted file mode 100644
index dbc23d2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolReader.php
+++ /dev/null
@@ -1,15 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-/**
- * Must emit a "message" event with a Message object as parameter
- * when a message comes in
- *
- * Must emit a "close" event when the stream closes
- */
-interface ProtocolReader extends EmitterInterface
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php
deleted file mode 100644
index 68073df..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use AdvancedJsonRpc\Message as MessageBody;
-use Amp\ByteStream\ResourceInputStream;
-use Amp\Promise;
-use Exception;
-use Generator;
-
-use function Amp\asyncCall;
-use function explode;
-use function strlen;
-use function substr;
-use function trim;
-
-/**
- * Source: https://github.com/felixfbecker/php-language-server/tree/master/src/ProtocolStreamReader.php
- */
-class ProtocolStreamReader implements ProtocolReader
-{
- use EmitterTrait;
-
- private const PARSE_HEADERS = 1;
- private const PARSE_BODY = 2;
-
- /**
- * This is checked by ProtocolStreamReader so that it will stop reading from streams in the forked process.
- * There could be buffered bytes in stdin/over TCP, those would be processed by TCP if it were not for this check.
- *
- * @var bool
- */
- private $is_accepting_new_requests = true;
- /** @var int */
- private $parsing_mode = self::PARSE_HEADERS;
- /** @var string */
- private $buffer = '';
- /** @var string[] */
- private $headers = [];
- /** @var ?int */
- private $content_length;
- /** @var bool */
- private $did_emit_close = false;
-
- /**
- * @param resource $input
- */
- public function __construct($input)
- {
- $input = new ResourceInputStream($input);
- asyncCall(
- /**
- * @return Generator<int, Promise<?string>, ?string, void>
- */
- function () use ($input): Generator {
- while ($this->is_accepting_new_requests) {
- $read_promise = $input->read();
-
- $chunk = yield $read_promise;
-
- if ($chunk === null) {
- break;
- }
-
- if ($this->readMessages($chunk) > 0) {
- $this->emit('readMessageGroup');
- }
- }
-
- $this->emitClose();
- }
- );
-
- $this->on(
- 'close',
- static function () use ($input): void {
- $input->close();
- }
- );
- }
-
- private function readMessages(string $buffer): int
- {
- $emitted_messages = 0;
- $i = 0;
- while (($buffer[$i] ?? '') !== '') {
- $this->buffer .= $buffer[$i++];
- switch ($this->parsing_mode) {
- case self::PARSE_HEADERS:
- if ($this->buffer === "\r\n") {
- $this->parsing_mode = self::PARSE_BODY;
- $this->content_length = (int) ($this->headers['Content-Length'] ?? 0);
- $this->buffer = '';
- } elseif (substr($this->buffer, -2) === "\r\n") {
- $parts = explode(':', $this->buffer);
- $this->headers[$parts[0]] = trim($parts[1]);
- $this->buffer = '';
- }
- break;
- case self::PARSE_BODY:
- if (strlen($this->buffer) === $this->content_length) {
- if (!$this->is_accepting_new_requests) {
- // If we fork, don't read any bytes in the input buffer from the worker process.
- $this->emitClose();
-
- return $emitted_messages;
- }
- // MessageBody::parse can throw an Error, maybe log an error?
- try {
- $msg = new Message(MessageBody::parse($this->buffer), $this->headers);
- } catch (Exception $_) {
- $msg = null;
- }
- if ($msg) {
- ++$emitted_messages;
- $this->emit('message', [$msg]);
- /**
- * @psalm-suppress DocblockTypeContradiction
- */
- if (!$this->is_accepting_new_requests) {
- // If we fork, don't read any bytes in the input buffer from the worker process.
- $this->emitClose();
-
- return $emitted_messages;
- }
- }
- $this->parsing_mode = self::PARSE_HEADERS;
- $this->headers = [];
- $this->buffer = '';
- }
- break;
- }
- }
-
- return $emitted_messages;
- }
-
- private function emitClose(): void
- {
- if ($this->did_emit_close) {
- return;
- }
- $this->did_emit_close = true;
- $this->emit('close');
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php
deleted file mode 100644
index cedd7c7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamWriter.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use Amp\ByteStream\ResourceOutputStream;
-use Amp\Promise;
-
-/**
- * @internal
- */
-class ProtocolStreamWriter implements ProtocolWriter
-{
- /**
- * @var ResourceOutputStream
- */
- private $output;
-
- /**
- * @param resource $output
- */
- public function __construct($output)
- {
- $this->output = new ResourceOutputStream($output);
- }
-
- /**
- * {@inheritdoc}
- */
- public function write(Message $msg): Promise
- {
- return $this->output->write((string)$msg);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolWriter.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolWriter.php
deleted file mode 100644
index 6d5ce01..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolWriter.php
+++ /dev/null
@@ -1,18 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer;
-
-use Amp\Promise;
-
-interface ProtocolWriter
-{
- /**
- * Sends a Message to the client
- *
- *
- * @return Promise Resolved when the message has been fully written out to the output stream
- */
- public function write(Message $msg): Promise;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php
deleted file mode 100644
index 46cbabe..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php
+++ /dev/null
@@ -1,410 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer\Server;
-
-use Amp\Promise;
-use Amp\Success;
-use LanguageServerProtocol\CompletionList;
-use LanguageServerProtocol\Hover;
-use LanguageServerProtocol\Location;
-use LanguageServerProtocol\MarkupContent;
-use LanguageServerProtocol\MarkupKind;
-use LanguageServerProtocol\Position;
-use LanguageServerProtocol\Range;
-use LanguageServerProtocol\SignatureHelp;
-use LanguageServerProtocol\TextDocumentContentChangeEvent;
-use LanguageServerProtocol\TextDocumentIdentifier;
-use LanguageServerProtocol\TextDocumentItem;
-use LanguageServerProtocol\TextEdit;
-use LanguageServerProtocol\VersionedTextDocumentIdentifier;
-use LanguageServerProtocol\WorkspaceEdit;
-use Psalm\Codebase;
-use Psalm\Exception\UnanalyzedFileException;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\LanguageServer\LanguageServer;
-use UnexpectedValueException;
-
-use function array_values;
-use function count;
-use function error_log;
-use function preg_match;
-use function substr_count;
-
-/**
- * Provides method handlers for all textDocument/* methods
- */
-class TextDocument
-{
- /**
- * @var LanguageServer
- */
- protected $server;
-
- /**
- * @var Codebase
- */
- protected $codebase;
-
- /**
- * @var ProjectAnalyzer
- */
- protected $project_analyzer;
-
- public function __construct(
- LanguageServer $server,
- Codebase $codebase,
- ProjectAnalyzer $project_analyzer
- ) {
- $this->server = $server;
- $this->codebase = $codebase;
- $this->project_analyzer = $project_analyzer;
- }
-
- /**
- * The document open notification is sent from the client to the server to signal newly opened text documents. The
- * document’s content is now managed by the client and the server must not try to read the document’s content using
- * the document’s Uri. Open in this sense means it is managed by the client. It doesn’t necessarily mean that its
- * content is presented in an editor. An open notification must not be sent more than once without a corresponding
- * close notification send before. This means open and close notification must be balanced and the max open count
- * for a particular textDocument is one. Note that a server’s ability to fulfill requests is independent of whether
- * a text document is open or closed.
- *
- * @param TextDocumentItem $textDocument the document that was opened
- */
- public function didOpen(TextDocumentItem $textDocument): void
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- if (!$this->codebase->config->isInProjectDirs($file_path)) {
- return;
- }
-
- $this->codebase->file_provider->openFile($file_path);
-
- $this->server->queueFileAnalysis($file_path, $textDocument->uri);
- }
-
- /**
- * The document save notification is sent from the client to the server when the document was saved in the client
- *
- * @param TextDocumentItem $textDocument the document that was opened
- * @param ?string $text the content when saved
- */
- public function didSave(TextDocumentItem $textDocument, ?string $text): void
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- if (!$this->codebase->config->isInProjectDirs($file_path)) {
- return;
- }
-
- // reopen file
- $this->codebase->removeTemporaryFileChanges($file_path);
- $this->codebase->file_provider->setOpenContents($file_path, (string) $text);
-
- $this->server->queueFileAnalysis($file_path, $textDocument->uri);
- }
-
- /**
- * The document change notification is sent from the client to the server to signal changes to a text document.
- *
- * @param VersionedTextDocumentIdentifier $textDocument
- * @param TextDocumentContentChangeEvent[] $contentChanges
- */
- public function didChange(VersionedTextDocumentIdentifier $textDocument, array $contentChanges): void
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- if (!$this->codebase->config->isInProjectDirs($file_path)) {
- return;
- }
-
- if (count($contentChanges) === 1 && $contentChanges[0]->range === null) {
- $new_content = $contentChanges[0]->text;
- } else {
- throw new UnexpectedValueException('Not expecting partial diff');
- }
-
- if ($this->project_analyzer->onchange_line_limit !== null) {
- if (substr_count($new_content, "\n") > $this->project_analyzer->onchange_line_limit) {
- return;
- }
- }
-
- $this->codebase->addTemporaryFileChanges($file_path, $new_content);
- $this->server->queueTemporaryFileAnalysis($file_path, $textDocument->uri);
- }
-
- /**
- * The document close notification is sent from the client to the server when the document got closed in the client.
- * The document’s master now exists where the document’s Uri points to (e.g. if the document’s Uri is a file Uri the
- * master now exists on disk). As with the open notification the close notification is about managing the document’s
- * content. Receiving a close notification doesn’t mean that the document was open in an editor before. A close
- * notification requires a previous open notification to be sent. Note that a server’s ability to fulfill requests
- * is independent of whether a text document is open or closed.
- *
- * @param TextDocumentIdentifier $textDocument The document that was closed
- *
- */
- public function didClose(TextDocumentIdentifier $textDocument): void
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- $this->codebase->file_provider->closeFile($file_path);
- $this->server->client->textDocument->publishDiagnostics($textDocument->uri, []);
- }
-
- /**
- * The goto definition request is sent from the client to the server to resolve the definition location of a symbol
- * at a given text document position.
- *
- * @param TextDocumentIdentifier $textDocument The text document
- * @param Position $position The position inside the text document
- * @psalm-return Promise<Location>|Promise<null>
- */
- public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- try {
- $reference_location = $this->codebase->getReferenceAtPosition($file_path, $position);
- } catch (UnanalyzedFileException $e) {
- $this->codebase->file_provider->openFile($file_path);
- $this->server->queueFileAnalysis($file_path, $textDocument->uri);
-
- return new Success(null);
- }
-
- if ($reference_location === null) {
- return new Success(null);
- }
-
- [$reference] = $reference_location;
-
- $code_location = $this->codebase->getSymbolLocation($file_path, $reference);
-
- if (!$code_location) {
- return new Success(null);
- }
-
- return new Success(
- new Location(
- LanguageServer::pathToUri($code_location->file_path),
- new Range(
- new Position($code_location->getLineNumber() - 1, $code_location->getColumn() - 1),
- new Position($code_location->getEndLineNumber() - 1, $code_location->getEndColumn() - 1)
- )
- )
- );
- }
-
- /**
- * The hover request is sent from the client to the server to request
- * hover information at a given text document position.
- *
- * @param TextDocumentIdentifier $textDocument The text document
- * @param Position $position The position inside the text document
- * @psalm-return Promise<Hover>|Promise<null>
- */
- public function hover(TextDocumentIdentifier $textDocument, Position $position): Promise
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- try {
- $reference_location = $this->codebase->getReferenceAtPosition($file_path, $position);
- } catch (UnanalyzedFileException $e) {
- $this->codebase->file_provider->openFile($file_path);
- $this->server->queueFileAnalysis($file_path, $textDocument->uri);
-
- return new Success(null);
- }
-
- if ($reference_location === null) {
- return new Success(null);
- }
-
- [$reference, $range] = $reference_location;
-
- $symbol_information = $this->codebase->getSymbolInformation($file_path, $reference);
-
- if ($symbol_information === null) {
- return new Success(null);
- }
-
- $content = "```php\n" . $symbol_information['type'] . "\n```";
- if (isset($symbol_information['description'])) {
- $content .= "\n---\n" . $symbol_information['description'];
- }
- $contents = new MarkupContent(
- MarkupKind::MARKDOWN,
- $content
- );
-
- return new Success(new Hover($contents, $range));
- }
-
- /**
- * The Completion request is sent from the client to the server to compute completion items at a given cursor
- * position. Completion items are presented in the IntelliSense user interface. If computing full completion items
- * is expensive, servers can additionally provide a handler for the completion item resolve request
- * ('completionItem/resolve'). This request is sent when a completion item is selected in the user interface. A
- * typically use case is for example: the 'textDocument/completion' request doesn't fill in the documentation
- * property for returned completion items since it is expensive to compute. When the item is selected in the user
- * interface then a 'completionItem/resolve' request is sent with the selected completion item as a param. The
- * returned completion item should have the documentation property filled in.
- *
- * @param TextDocumentIdentifier $textDocument The text document
- * @param Position $position The position
- * @psalm-return Promise<array<empty, empty>>|Promise<CompletionList>
- */
- public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
- if (!$this->codebase->config->isInProjectDirs($file_path)) {
- return new Success([]);
- }
-
- try {
- $completion_data = $this->codebase->getCompletionDataAtPosition($file_path, $position);
- } catch (UnanalyzedFileException $e) {
- $this->codebase->file_provider->openFile($file_path);
- $this->server->queueFileAnalysis($file_path, $textDocument->uri);
-
- return new Success([]);
- }
-
- try {
- $type_context = $this->codebase->getTypeContextAtPosition($file_path, $position);
- } catch (UnexpectedValueException $e) {
- error_log('completion errored at ' . $position->line . ':' . $position->character.
- ', Reason: '.$e->getMessage());
- return new Success([]);
- }
-
- if (!$completion_data && !$type_context) {
- error_log('completion not found at ' . $position->line . ':' . $position->character);
- return new Success([]);
- }
-
- if ($completion_data) {
- [$recent_type, $gap, $offset] = $completion_data;
-
- if ($gap === '->' || $gap === '::') {
- $completion_items = $this->codebase->getCompletionItemsForClassishThing($recent_type, $gap);
- } elseif ($gap === '[') {
- $completion_items = $this->codebase->getCompletionItemsForArrayKeys($recent_type);
- } else {
- $completion_items = $this->codebase->getCompletionItemsForPartialSymbol(
- $recent_type,
- $offset,
- $file_path
- );
- }
- } else {
- $completion_items = $this->codebase->getCompletionItemsForType($type_context);
- }
-
- return new Success(new CompletionList($completion_items, false));
- }
-
- /**
- * The signature help request is sent from the client to the server to request signature
- * information at a given cursor position.
- */
- public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
-
- try {
- $argument_location = $this->codebase->getFunctionArgumentAtPosition($file_path, $position);
- } catch (UnanalyzedFileException $e) {
- $this->codebase->file_provider->openFile($file_path);
- $this->server->queueFileAnalysis($file_path, $textDocument->uri);
-
- return new Success(new SignatureHelp());
- }
-
- if ($argument_location === null) {
- return new Success(new SignatureHelp());
- }
-
- $signature_information = $this->codebase->getSignatureInformation($argument_location[0], $file_path);
-
- if (!$signature_information) {
- return new Success(new SignatureHelp());
- }
-
- return new Success(new SignatureHelp([
- $signature_information,
- ], 0, $argument_location[1]));
- }
-
- /**
- * The code action request is sent from the client to the server to compute commands
- * for a given text document and range. These commands are typically code fixes to
- * either fix problems or to beautify/refactor code.
- *
- */
- public function codeAction(TextDocumentIdentifier $textDocument, Range $range): Promise
- {
- $file_path = LanguageServer::uriToPath($textDocument->uri);
- if (!$this->codebase->file_provider->isOpen($file_path)) {
- return new Success(null);
- }
-
- $issues = $this->server->getCurrentIssues();
-
- if (empty($issues[$file_path])) {
- return new Success(null);
- }
-
- $file_contents = $this->codebase->getFileContents($file_path);
-
- $offsetStart = $range->start->toOffset($file_contents);
- $offsetEnd = $range->end->toOffset($file_contents);
-
- $fixers = [];
- foreach ($issues[$file_path] as $issue) {
- if ($offsetStart === $issue->from && $offsetEnd === $issue->to) {
- $snippetRange = new Range(
- new Position($issue->line_from-1),
- new Position($issue->line_to)
- );
-
- $indentation = '';
- if (preg_match('/^(\s*)/', $issue->snippet, $matches)) {
- $indentation = $matches[1] ?? '';
- }
-
- $edit = new WorkspaceEdit([
- $textDocument->uri => [
- new TextEdit(
- $snippetRange,
- "{$indentation}/**\n".
- "{$indentation} * @psalm-suppress {$issue->type}\n".
- "{$indentation} */\n".
- "{$issue->snippet}\n"
- )
- ]
- ]);
-
- //Suppress Ability
- $fixers["suppress.{$issue->type}"] = [
- 'title' => "Suppress {$issue->type} for this line",
- 'kind' => 'quickfix',
- 'edit' => $edit
- ];
- }
- }
-
- if (empty($fixers)) {
- return new Success(null);
- }
-
- return new Success(
- array_values($fixers)
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php
deleted file mode 100644
index c1bc2bd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php
+++ /dev/null
@@ -1,78 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\LanguageServer\Server;
-
-use LanguageServerProtocol\FileChangeType;
-use LanguageServerProtocol\FileEvent;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\LanguageServer\LanguageServer;
-
-/**
- * Provides method handlers for all workspace/* methods
- */
-class Workspace
-{
- /**
- * @var LanguageServer
- */
- protected $server;
-
- /**
- * @var Codebase
- */
- protected $codebase;
-
- /**
- * @var ProjectAnalyzer
- */
- protected $project_analyzer;
-
- public function __construct(
- LanguageServer $server,
- Codebase $codebase,
- ProjectAnalyzer $project_analyzer
- ) {
- $this->server = $server;
- $this->codebase = $codebase;
- $this->project_analyzer = $project_analyzer;
- }
-
- /**
- * The watched files notification is sent from the client to the server when the client
- * detects changes to files and folders watched by the language client (note although
- * the name suggest that only file events are sent it is about file system events
- * which include folders as well). It is recommended that servers register for these
- * file system events using the registration mechanism. In former implementations clients
- * pushed file events without the server actively asking for it.
- *
- * @param FileEvent[] $changes
- * @psalm-suppress PossiblyUnusedMethod
- */
- public function didChangeWatchedFiles(array $changes): void
- {
- foreach ($changes as $change) {
- $file_path = LanguageServer::uriToPath($change->uri);
-
- if ($change->type === FileChangeType::DELETED) {
- $this->codebase->invalidateInformationForFile($file_path);
- continue;
- }
-
- if (!$this->codebase->config->isInProjectDirs($file_path)) {
- continue;
- }
-
- if ($this->project_analyzer->onchange_line_limit === 0) {
- continue;
- }
-
- //If the file is currently open then dont analyse it because its tracked by the client
- if (!$this->codebase->file_provider->isOpen($file_path)) {
- $this->server->queueFileAnalysis($file_path, $change->uri);
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/MethodIdentifier.php b/vendor/vimeo/psalm/src/Psalm/Internal/MethodIdentifier.php
deleted file mode 100644
index cf4c81b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/MethodIdentifier.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use InvalidArgumentException;
-
-use function explode;
-use function is_string;
-use function preg_replace;
-use function strpos;
-use function strtolower;
-
-/**
- * @psalm-immutable
- */
-class MethodIdentifier
-{
- public $fq_class_name;
- public $method_name;
-
- /**
- * @param lowercase-string $method_name
- */
- public function __construct(string $fq_class_name, string $method_name)
- {
- $this->fq_class_name = $fq_class_name;
- $this->method_name = $method_name;
- }
-
- /**
- * Takes any valid reference to a method id and converts
- * it into a MethodIdentifier
- *
- * @param string|MethodIdentifier $method_id
- *
- * @psalm-pure
- */
- public static function wrap($method_id): self
- {
- return is_string($method_id) ? static::fromMethodIdReference($method_id) : $method_id;
- }
-
- /**
- * @psalm-pure
- */
- public static function isValidMethodIdReference(string $method_id): bool
- {
- return strpos($method_id, '::') !== false;
- }
-
- /**
- * @psalm-pure
- */
- public static function fromMethodIdReference(string $method_id): self
- {
- if (!static::isValidMethodIdReference($method_id)) {
- throw new InvalidArgumentException('Invalid method id reference provided: ' . $method_id);
- }
- // remove trailing backslash if it exists
- $method_id = preg_replace('/^\\\\/', '', $method_id);
- $method_id_parts = explode('::', $method_id);
- return new self($method_id_parts[0], strtolower($method_id_parts[1]));
- }
-
- /** @return non-empty-string */
- public function __toString(): string
- {
- return $this->fq_class_name . '::' . $this->method_name;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpTraverser/CustomTraverser.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpTraverser/CustomTraverser.php
deleted file mode 100644
index 2c96c6b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpTraverser/CustomTraverser.php
+++ /dev/null
@@ -1,174 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\PhpTraverser;
-
-use LogicException;
-use PhpParser\Node;
-use PhpParser\NodeTraverser;
-
-use function array_pop;
-use function array_splice;
-use function gettype;
-use function is_array;
-
-/**
- * @internal
- */
-class CustomTraverser extends NodeTraverser
-{
- public function __construct()
- {
- $this->stopTraversal = false;
- }
-
- /**
- * Recursively traverse a node.
- *
- * @param Node $node node to traverse
- *
- * @return Node Result of traversal (may be original node or new one)
- */
- protected function traverseNode(Node $node): Node
- {
- foreach ($node->getSubNodeNames() as $name) {
- $subNode = &$node->$name;
-
- if (is_array($subNode)) {
- $subNode = $this->traverseArray($subNode);
- if ($this->stopTraversal) {
- break;
- }
- } elseif ($subNode instanceof Node) {
- $traverseChildren = true;
- foreach ($this->visitors as $visitor) {
- $return = $visitor->enterNode($subNode, $traverseChildren);
- if (null !== $return) {
- if ($return instanceof Node) {
- $subNode = $return;
- } elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
- $traverseChildren = false;
- } elseif (self::STOP_TRAVERSAL === $return) {
- $this->stopTraversal = true;
- break 2;
- } else {
- throw new LogicException(
- 'enterNode() returned invalid value of type ' . gettype($return)
- );
- }
- }
- }
-
- if ($traverseChildren) {
- $subNode = $this->traverseNode($subNode);
- if ($this->stopTraversal) {
- break;
- }
- }
-
- foreach ($this->visitors as $visitor) {
- $return = $visitor->leaveNode($subNode);
- if (null !== $return) {
- if ($return instanceof Node) {
- $subNode = $return;
- } elseif (self::STOP_TRAVERSAL === $return) {
- $this->stopTraversal = true;
- break 2;
- } elseif (is_array($return)) {
- throw new LogicException(
- 'leaveNode() may only return an array ' .
- 'if the parent structure is an array'
- );
- } else {
- throw new LogicException(
- 'leaveNode() returned invalid value of type ' . gettype($return)
- );
- }
- }
- }
- }
- }
-
- return $node;
- }
-
- /**
- * Recursively traverse array (usually of nodes).
- *
- * @param array $nodes Array to traverse
- *
- * @return array Result of traversal (may be original array or changed one)
- */
- protected function traverseArray(array $nodes): array
- {
- $doNodes = [];
-
- foreach ($nodes as $i => &$node) {
- if ($node instanceof Node) {
- $traverseChildren = true;
- foreach ($this->visitors as $visitor) {
- $return = $visitor->enterNode($node, $traverseChildren);
- if (null !== $return) {
- if ($return instanceof Node) {
- $node = $return;
- } elseif (self::DONT_TRAVERSE_CHILDREN === $return) {
- $traverseChildren = false;
- } elseif (self::STOP_TRAVERSAL === $return) {
- $this->stopTraversal = true;
- break 2;
- } else {
- throw new LogicException(
- 'enterNode() returned invalid value of type ' . gettype($return)
- );
- }
- }
- }
-
- if ($traverseChildren) {
- $node = $this->traverseNode($node);
- if ($this->stopTraversal) {
- break;
- }
- }
-
- foreach ($this->visitors as $visitor) {
- $return = $visitor->leaveNode($node);
- if (null !== $return) {
- if ($return instanceof Node) {
- $node = $return;
- } elseif (is_array($return)) {
- $doNodes[] = [$i, $return];
- break;
- } elseif (self::REMOVE_NODE === $return) {
- $doNodes[] = [$i, []];
- break;
- } elseif (self::STOP_TRAVERSAL === $return) {
- $this->stopTraversal = true;
- break 2;
- } elseif (false === $return) {
- throw new LogicException(
- 'bool(false) return from leaveNode() no longer supported. ' .
- 'Return NodeTraverser::REMOVE_NODE instead'
- );
- } else {
- throw new LogicException(
- 'leaveNode() returned invalid value of type ' . gettype($return)
- );
- }
- }
- }
- } elseif (is_array($node)) {
- throw new LogicException('Invalid node structure: Contains nested arrays');
- }
- }
-
- if (!empty($doNodes)) {
- while (list($i, $replace) = array_pop($doNodes)) {
- array_splice($nodes, $i, 1, $replace);
- }
- }
-
- return $nodes;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php
deleted file mode 100644
index a3fd78e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/AssignmentMapVisitor.php
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-
-/**
- * @internal
- *
- * This produces a graph of probably assignments inside a loop
- *
- * With this map we can calculate how many times the loop analysis must
- * be run before all variables have the correct types
- */
-class AssignmentMapVisitor extends PhpParser\NodeVisitorAbstract
-{
- /**
- * @var array<string, array<string, bool>>
- */
- protected $assignment_map = [];
-
- /**
- * @var string|null
- */
- protected $this_class_name;
-
- public function __construct(?string $this_class_name)
- {
- $this->this_class_name = $this_class_name;
- }
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- if ($node instanceof PhpParser\Node\Expr\Assign) {
- $right_var_id = ExpressionIdentifier::getRootVarId($node->expr, $this->this_class_name);
-
- if ($node->var instanceof PhpParser\Node\Expr\List_
- || $node->var instanceof PhpParser\Node\Expr\Array_
- ) {
- foreach ($node->var->items as $assign_item) {
- if ($assign_item) {
- $left_var_id = ExpressionIdentifier::getRootVarId($assign_item->value, $this->this_class_name);
-
- if ($left_var_id) {
- $this->assignment_map[$left_var_id][$right_var_id ?: 'isset'] = true;
- }
- }
- }
- } else {
- $left_var_id = ExpressionIdentifier::getRootVarId($node->var, $this->this_class_name);
-
- if ($left_var_id) {
- $this->assignment_map[$left_var_id][$right_var_id ?: 'isset'] = true;
- }
- }
-
- return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN;
- }
-
- if ($node instanceof PhpParser\Node\Expr\PostInc
- || $node instanceof PhpParser\Node\Expr\PostDec
- || $node instanceof PhpParser\Node\Expr\PreInc
- || $node instanceof PhpParser\Node\Expr\PreDec
- || $node instanceof PhpParser\Node\Expr\AssignOp
- ) {
- $var_id = ExpressionIdentifier::getRootVarId($node->var, $this->this_class_name);
-
- if ($var_id) {
- $this->assignment_map[$var_id][$var_id] = true;
- }
-
- return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN;
- }
-
- if ($node instanceof PhpParser\Node\Expr\FuncCall
- || $node instanceof PhpParser\Node\Expr\MethodCall
- || $node instanceof PhpParser\Node\Expr\StaticCall
- ) {
- if (!$node->isFirstClassCallable()) {
- foreach ($node->getArgs() as $arg) {
- $arg_var_id = ExpressionIdentifier::getRootVarId($arg->value, $this->this_class_name);
-
- if ($arg_var_id) {
- $this->assignment_map[$arg_var_id][$arg_var_id] = true;
- }
- }
- }
-
- if ($node instanceof PhpParser\Node\Expr\MethodCall) {
- $var_id = ExpressionIdentifier::getRootVarId($node->var, $this->this_class_name);
-
- if ($var_id) {
- $this->assignment_map[$var_id]['isset'] = true;
- }
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\Unset_) {
- foreach ($node->vars as $arg) {
- $arg_var_id = ExpressionIdentifier::getRootVarId($arg, $this->this_class_name);
-
- if ($arg_var_id) {
- $this->assignment_map[$arg_var_id][$arg_var_id] = true;
- }
- }
- }
-
- return null;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAssignmentMap(): array
- {
- return $this->assignment_map;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php
deleted file mode 100644
index a57eeab..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CheckTrivialExprVisitor.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-
-/**
- * @internal
- */
-class CheckTrivialExprVisitor extends PhpParser\NodeVisitorAbstract
-{
- /**
- * @var array<int, PhpParser\Node\Expr>
- */
- protected $non_trivial_expr = [];
-
- private function checkNonTrivialExpr(PhpParser\Node\Expr $node): bool
- {
- if ($node instanceof PhpParser\Node\Expr\ArrayDimFetch
- || $node instanceof PhpParser\Node\Expr\Closure
- || $node instanceof PhpParser\Node\Expr\ClosureUse
- || $node instanceof PhpParser\Node\Expr\Eval_
- || $node instanceof PhpParser\Node\Expr\Exit_
- || $node instanceof PhpParser\Node\Expr\Include_
- || $node instanceof PhpParser\Node\Expr\FuncCall
- || $node instanceof PhpParser\Node\Expr\MethodCall
- || $node instanceof PhpParser\Node\Expr\ArrowFunction
- || $node instanceof PhpParser\Node\Expr\ShellExec
- || $node instanceof PhpParser\Node\Expr\StaticCall
- || $node instanceof PhpParser\Node\Expr\Yield_
- || $node instanceof PhpParser\Node\Expr\YieldFrom
- || $node instanceof PhpParser\Node\Expr\New_
- || $node instanceof PhpParser\Node\Expr\Cast\String_
- ) {
- if (($node instanceof PhpParser\Node\Expr\FuncCall
- || $node instanceof PhpParser\Node\Expr\MethodCall
- || $node instanceof PhpParser\Node\Expr\StaticCall)
- && $node->getAttribute('pure', false)
- ) {
- return false;
- }
-
- if ($node instanceof PhpParser\Node\Expr\New_ && $node->getAttribute('external_mutation_free', false)) {
- return false;
- }
-
- return true;
- }
-
- return false;
- }
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- if ($node instanceof PhpParser\Node\Expr) {
- // Check for Non-Trivial Expression first
- if ($this->checkNonTrivialExpr($node)) {
- $this->non_trivial_expr[] = $node;
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- if ($node instanceof PhpParser\Node\Expr\ClassConstFetch
- || $node instanceof PhpParser\Node\Expr\ConstFetch
- || $node instanceof PhpParser\Node\Expr\Error
- || $node instanceof PhpParser\Node\Expr\PropertyFetch
- || $node instanceof PhpParser\Node\Expr\StaticPropertyFetch) {
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
- }
- return null;
- }
-
- /**
- * @return array<int, PhpParser\Node\Expr>
- */
- public function getNonTrivialExpr(): array
- {
- return $this->non_trivial_expr;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CloningVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CloningVisitor.php
deleted file mode 100644
index befd1d1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/CloningVisitor.php
+++ /dev/null
@@ -1,40 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser\Comment;
-use PhpParser\Node;
-use PhpParser\NodeVisitorAbstract;
-
-use function array_map;
-
-/**
- * Visitor cloning all nodes and linking to the original nodes using an attribute.
- *
- * This visitor is required to perform format-preserving pretty prints.
- */
-class CloningVisitor extends NodeVisitorAbstract
-{
- public function enterNode(Node $node): Node
- {
- $node = clone $node;
- if ($cs = $node->getComments()) {
- $node->setAttribute(
- 'comments',
- array_map(
- /**
- * @return Comment
- */
- function (Comment $c): Comment {
- return clone $c;
- },
- $cs
- )
- );
- }
-
- return $node;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php
deleted file mode 100644
index c368f37..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ConditionCloningVisitor.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser\Node;
-use PhpParser\Node\Expr;
-use PhpParser\NodeVisitorAbstract;
-use Psalm\Internal\Provider\NodeDataProvider;
-
-class ConditionCloningVisitor extends NodeVisitorAbstract
-{
- private $type_provider;
-
- public function __construct(NodeDataProvider $old_type_provider)
- {
- $this->type_provider = $old_type_provider;
- }
-
- /**
- * @return Node\Expr
- */
- public function enterNode(Node $node): Node
- {
- /** @var Expr $node */
- $origNode = $node;
-
- $node = clone $node;
-
- $node_type = $this->type_provider->getType($origNode);
-
- if ($node_type) {
- $this->type_provider->setType($node, clone $node_type);
- }
-
- return $node;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCleanerVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCleanerVisitor.php
deleted file mode 100644
index f951578..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCleanerVisitor.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-use Psalm\Internal\Provider\NodeDataProvider;
-
-/**
- * @internal
- */
-class NodeCleanerVisitor extends PhpParser\NodeVisitorAbstract
-{
- private $type_provider;
-
- public function __construct(NodeDataProvider $type_provider)
- {
- $this->type_provider = $type_provider;
- }
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- if ($node instanceof PhpParser\Node\Expr) {
- $this->type_provider->clearNodeOfTypeAndAssertions($node);
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCounterVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCounterVisitor.php
deleted file mode 100644
index dd71903..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/NodeCounterVisitor.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-
-/**
- * @internal
- */
-class NodeCounterVisitor extends PhpParser\NodeVisitorAbstract
-{
- /** @var int */
- public $count = 0;
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- $this->count++;
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php
deleted file mode 100644
index 7e3de32..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/OffsetShifterVisitor.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-
-/**
- * Shifts all nodes in a given AST by a set amount
- */
-class OffsetShifterVisitor extends PhpParser\NodeVisitorAbstract
-{
- /** @var int */
- private $file_offset;
-
- /** @var int */
- private $line_offset;
-
- /** @var array<int, int> */
- private $extra_offsets;
-
- /**
- * @param array<int, int> $extra_offsets
- */
- public function __construct(int $offset, int $line_offset, array $extra_offsets)
- {
- $this->file_offset = $offset;
- $this->line_offset = $line_offset;
- $this->extra_offsets = $extra_offsets;
- }
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- /** @var array{startFilePos: int, endFilePos: int, startLine: int} */
- $attrs = $node->getAttributes();
-
- if ($cs = $node->getComments()) {
- $new_comments = [];
-
- foreach ($cs as $c) {
- if ($c instanceof PhpParser\Comment\Doc) {
- $new_comments[] = new PhpParser\Comment\Doc(
- $c->getText(),
- $c->getStartLine() + $this->line_offset,
- $c->getStartFilePos() + $this->file_offset + ($this->extra_offsets[$c->getStartFilePos()] ?? 0)
- );
- } else {
- $new_comments[] = new PhpParser\Comment(
- $c->getText(),
- $c->getStartLine() + $this->line_offset,
- $c->getStartFilePos() + $this->file_offset + ($this->extra_offsets[$c->getStartFilePos()] ?? 0)
- );
- }
- }
-
- $node->setAttribute('comments', $new_comments);
- }
-
- $node->setAttribute(
- 'startFilePos',
- $attrs['startFilePos'] + $this->file_offset + ($this->extra_offsets[$attrs['startFilePos']] ?? 0)
- );
- $node->setAttribute(
- 'endFilePos',
- $attrs['endFilePos'] + $this->file_offset + ($this->extra_offsets[$attrs['endFilePos']] ?? 0)
- );
- $node->setAttribute('startLine', $attrs['startLine'] + $this->line_offset);
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php
deleted file mode 100644
index 32fe8e3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ParamReplacementVisitor.php
+++ /dev/null
@@ -1,122 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-use Psalm\FileManipulation;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Scanner\DocblockParser;
-
-use function rtrim;
-use function str_replace;
-use function strlen;
-
-/**
- * @internal
- */
-class ParamReplacementVisitor extends PhpParser\NodeVisitorAbstract
-{
- /** @var string */
- private $old_name;
-
- /** @var string */
- private $new_name;
-
- /** @var list<FileManipulation> */
- private $replacements = [];
-
- /** @var bool */
- private $new_name_replaced = false;
-
- /** @var bool */
- private $new_new_name_used = false;
-
- public function __construct(string $old_name, string $new_name)
- {
- $this->old_name = $old_name;
- $this->new_name = $new_name;
- }
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- if ($node instanceof PhpParser\Node\Expr\Variable) {
- if ($node->name === $this->old_name) {
- $this->replacements[] = new FileManipulation(
- (int) $node->getAttribute('startFilePos') + 1,
- (int) $node->getAttribute('endFilePos') + 1,
- $this->new_name
- );
- } elseif ($node->name === $this->new_name) {
- if ($this->new_new_name_used) {
- $this->replacements = [];
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- $this->replacements[] = new FileManipulation(
- (int) $node->getAttribute('startFilePos') + 1,
- (int) $node->getAttribute('endFilePos') + 1,
- $this->new_name . '_new'
- );
-
- $this->new_name_replaced = true;
- } elseif ($node->name === $this->new_name . '_new') {
- if ($this->new_name_replaced) {
- $this->replacements = [];
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- $this->new_new_name_used = true;
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\ClassMethod
- && ($docblock = $node->getDocComment())
- ) {
- $parsed_docblock = DocblockParser::parse(
- $docblock->getText(),
- $docblock->getStartFilePos()
- );
-
- $replaced = false;
-
- foreach ($parsed_docblock->tags as $tag_name => $tags) {
- foreach ($tags as $i => $tag) {
- if ($tag_name === 'param'
- || $tag_name === 'psalm-param'
- || $tag_name === 'phpstan-param'
- || $tag_name === 'phan-param'
- ) {
- $parts = CommentAnalyzer::splitDocLine($tag);
-
- if (($parts[1] ?? '') === '$' . $this->old_name) {
- $parsed_docblock->tags[$tag_name][$i] = str_replace(
- '$' . $this->old_name,
- '$' . $this->new_name,
- $tag
- );
- $replaced = true;
- }
- }
- }
- }
-
- if ($replaced) {
- $this->replacements[] = new FileManipulation(
- $docblock->getStartFilePos(),
- $docblock->getStartFilePos() + strlen($docblock->getText()),
- rtrim($parsed_docblock->render($parsed_docblock->first_line_padding)),
- false,
- false
- );
- }
- }
-
- return null;
- }
-
- /**
- * @return list<FileManipulation>
- */
- public function getReplacements(): array
- {
- return $this->replacements;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php
deleted file mode 100644
index 9734be2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/PartialParserVisitor.php
+++ /dev/null
@@ -1,409 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-use PhpParser\ErrorHandler\Collecting;
-use Psalm\Internal\PhpVisitor\OffsetShifterVisitor;
-
-use function count;
-use function preg_match_all;
-use function preg_replace;
-use function reset;
-use function str_repeat;
-use function strlen;
-use function strpos;
-use function strrpos;
-use function substr;
-use function substr_count;
-use function substr_replace;
-use function token_get_all;
-
-use const PREG_OFFSET_CAPTURE;
-use const PREG_SET_ORDER;
-
-/**
- * Given a list of file diffs, this scans an AST to find the sections it can replace, and parses
- * just those methods.
- */
-class PartialParserVisitor extends PhpParser\NodeVisitorAbstract
-{
- /** @var array<int, array{int, int, int, int, int}> */
- private $offset_map;
-
- /** @var bool */
- private $must_rescan = false;
-
- /** @var int */
- private $non_method_changes;
-
- /** @var string */
- private $a_file_contents;
-
- /** @var string */
- private $b_file_contents;
-
- /** @var int */
- private $a_file_contents_length;
-
- /** @var PhpParser\Parser */
- private $parser;
-
- /** @var PhpParser\ErrorHandler\Collecting */
- private $error_handler;
-
- /** @param array<int, array{int, int, int, int, int}> $offset_map */
- public function __construct(
- PhpParser\Parser $parser,
- PhpParser\ErrorHandler\Collecting $error_handler,
- array $offset_map,
- string $a_file_contents,
- string $b_file_contents
- ) {
- $this->parser = $parser;
- $this->error_handler = $error_handler;
- $this->offset_map = $offset_map;
- $this->a_file_contents = $a_file_contents;
- $this->a_file_contents_length = strlen($a_file_contents);
- $this->b_file_contents = $b_file_contents;
- $this->non_method_changes = count($offset_map);
- }
-
- /**
- * @return null|int|PhpParser\Node
- */
- public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true)
- {
- /** @var array{startFilePos: int, endFilePos: int, startLine: int} */
- $attrs = $node->getAttributes();
-
- if ($cs = $node->getComments()) {
- $stmt_start_pos = $cs[0]->getStartFilePos();
- } else {
- $stmt_start_pos = $attrs['startFilePos'];
- }
-
- $stmt_end_pos = $attrs['endFilePos'];
-
- $start_offset = 0;
- $end_offset = 0;
-
- $line_offset = 0;
-
- foreach ($this->offset_map as [$a_s, $a_e, $b_s, $b_e, $line_diff]) {
- if ($a_s > $stmt_end_pos) {
- break;
- }
-
- $end_offset = $b_e - $a_e;
-
- if ($a_s < $stmt_start_pos) {
- $start_offset = $b_s - $a_s;
- }
-
- if ($a_e < $stmt_start_pos) {
- $start_offset = $end_offset;
-
- $line_offset = $line_diff;
-
- continue;
- }
-
- if ($node instanceof PhpParser\Node\Stmt\ClassMethod
- || $node instanceof PhpParser\Node\Stmt\Namespace_
- || $node instanceof PhpParser\Node\Stmt\ClassLike
- ) {
- if ($node instanceof PhpParser\Node\Stmt\ClassMethod) {
- if ($a_s >= $stmt_start_pos && $a_e <= $stmt_end_pos) {
- foreach ($this->offset_map as [$a_s2, $a_e2, $b_s2, $b_e2]) {
- if ($a_s2 > $stmt_end_pos) {
- break;
- }
-
- // we have a diff that goes outside the bounds that we care about
- if ($a_e2 > $stmt_end_pos) {
- $this->must_rescan = true;
-
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- $end_offset = $b_e2 - $a_e2;
-
- if ($a_s2 < $stmt_start_pos) {
- $start_offset = $b_s2 - $a_s2;
- }
-
- if ($a_e2 < $stmt_start_pos) {
- $start_offset = $end_offset;
-
- $line_offset = $line_diff;
-
- continue;
- }
-
- if ($a_s2 >= $stmt_start_pos && $a_e2 <= $stmt_end_pos) {
- --$this->non_method_changes;
- }
- }
-
- $stmt_start_pos += $start_offset;
- $stmt_end_pos += $end_offset;
-
- $current_line = substr_count(substr($this->b_file_contents, 0, $stmt_start_pos), "\n");
-
- $method_contents = substr(
- $this->b_file_contents,
- $stmt_start_pos,
- $stmt_end_pos - $stmt_start_pos + 1
- );
-
- if (!$method_contents) {
- $this->must_rescan = true;
-
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- $error_handler = new Collecting();
-
- $fake_class = '<?php class _ {' . $method_contents . '}';
-
- $extra_characters = [];
-
- // To avoid a parser error during completion we replace
- //
- // Foo::
- // if (...) {}
- //
- // with
- //
- // Foo::;
- // if (...) {}
- //
- // When we insert the extra semicolon we have to keep track of the places
- // we inserted it, and then shift the AST node offsets accordingly after parsing
- // is complete.
- //
- // If anyone's unlucky enough to have a static method named "if" with a newline
- // before the method name e.g.
- //
- // Foo::
- // if(...);
- //
- // This transformation will break that.
- preg_match_all(
- '/(->|::)(\n\s*(if|list)\s*\()/',
- $fake_class,
- $matches,
- PREG_OFFSET_CAPTURE | PREG_SET_ORDER
- );
-
- foreach ($matches as $match) {
- $fake_class = substr_replace(
- $fake_class,
- $match[1][0] . ';' . $match[2][0],
- $match[0][1],
- strlen($match[0][0])
- );
-
- $extra_characters[] = $match[2][1];
- }
-
- $replacement_stmts = $this->parser->parse(
- $fake_class,
- $error_handler
- ) ?: [];
-
- if (!$replacement_stmts
- || !$replacement_stmts[0] instanceof PhpParser\Node\Stmt\ClassLike
- || count($replacement_stmts[0]->stmts) !== 1
- ) {
- $hacky_class_fix = self::balanceBrackets($fake_class);
-
- if ($replacement_stmts
- && $replacement_stmts[0] instanceof PhpParser\Node\Stmt\ClassLike
- && count($replacement_stmts[0]->stmts) !== 1
- ) {
- $this->must_rescan = true;
-
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- // changes "): {" to ") {"
- $hacky_class_fix = preg_replace('/(\)[\s]*):([\s]*\{)/', '$1 $2', $hacky_class_fix);
-
- if ($hacky_class_fix !== $fake_class) {
- $replacement_stmts = $this->parser->parse(
- $hacky_class_fix,
- $error_handler
- ) ?: [];
- }
-
- if (!$replacement_stmts
- || !$replacement_stmts[0] instanceof PhpParser\Node\Stmt\ClassLike
- || count($replacement_stmts[0]->stmts) > 1
- ) {
- $this->must_rescan = true;
-
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
- }
-
- $replacement_stmts = $replacement_stmts[0]->stmts;
-
- $extra_offsets = [];
-
- foreach ($extra_characters as $extra_offset) {
- $l = strlen($fake_class);
-
- for ($i = $extra_offset; $i < $l; $i++) {
- if (isset($extra_offsets[$i])) {
- $extra_offsets[$i]--;
- } else {
- $extra_offsets[$i] = -1;
- }
- }
- }
-
- $renumbering_traverser = new PhpParser\NodeTraverser;
- $position_shifter = new OffsetShifterVisitor(
- $stmt_start_pos - 15,
- $current_line,
- $extra_offsets
- );
- $renumbering_traverser->addVisitor($position_shifter);
- $replacement_stmts = $renumbering_traverser->traverse($replacement_stmts);
-
- if ($error_handler->hasErrors()) {
- foreach ($error_handler->getErrors() as $error) {
- if ($error->hasColumnInfo()) {
- /** @var array{startFilePos: int, endFilePos: int} */
- $error_attrs = $error->getAttributes();
- $error = new PhpParser\Error(
- $error->getRawMessage(),
- [
- 'startFilePos' => $stmt_start_pos + $error_attrs['startFilePos'] - 15,
- 'endFilePos' => $stmt_start_pos + $error_attrs['endFilePos'] - 15,
- 'startLine' => $error->getStartLine() + $current_line + $line_offset,
- ]
- );
- }
-
- $this->error_handler->handleError($error);
- }
- }
-
- $error_handler->clearErrors();
-
- $traverseChildren = false;
-
- return reset($replacement_stmts);
- }
-
- $this->must_rescan = true;
-
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- if ($node->stmts) {
- /** @var int */
- $stmt_inner_start_pos = $node->stmts[0]->getAttribute('startFilePos');
-
- /** @var int */
- $stmt_inner_end_pos = $node->stmts[count($node->stmts) - 1]->getAttribute('endFilePos');
-
- if ($node instanceof PhpParser\Node\Stmt\ClassLike) {
- /** @psalm-suppress PossiblyFalseOperand */
- $stmt_inner_start_pos = strrpos(
- $this->a_file_contents,
- '{',
- $stmt_inner_start_pos - $this->a_file_contents_length
- ) + 1;
-
- if ($stmt_inner_end_pos < $this->a_file_contents_length) {
- $stmt_inner_end_pos = strpos($this->a_file_contents, '}', $stmt_inner_end_pos + 1);
- }
- }
-
- if ($a_s > $stmt_inner_start_pos && $a_e < $stmt_inner_end_pos) {
- continue;
- }
- }
- }
-
- $this->must_rescan = true;
-
- return PhpParser\NodeTraverser::STOP_TRAVERSAL;
- }
-
- if ($start_offset !== 0 || $end_offset !== 0 || $line_offset !== 0) {
- if ($start_offset !== 0) {
- if ($cs) {
- $new_comments = [];
-
- foreach ($cs as $c) {
- if ($c instanceof PhpParser\Comment\Doc) {
- $new_comments[] = new PhpParser\Comment\Doc(
- $c->getText(),
- $c->getStartLine() + $line_offset,
- $c->getStartFilePos() + $start_offset
- );
- } else {
- $new_comments[] = new PhpParser\Comment(
- $c->getText(),
- $c->getStartLine() + $line_offset,
- $c->getStartFilePos() + $start_offset
- );
- }
- }
-
- $node->setAttribute('comments', $new_comments);
-
- $node->setAttribute('startFilePos', $attrs['startFilePos'] + $start_offset);
- } else {
- $node->setAttribute('startFilePos', $stmt_start_pos + $start_offset);
- }
- }
-
- if ($end_offset !== 0) {
- $node->setAttribute('endFilePos', $stmt_end_pos + $end_offset);
- }
-
- if ($line_offset !== 0) {
- $node->setAttribute('startLine', $attrs['startLine'] + $line_offset);
- }
-
- return $node;
- }
-
- return null;
- }
-
- public function mustRescan(): bool
- {
- return $this->must_rescan || $this->non_method_changes;
- }
-
- /**
- * @psalm-pure
- */
- private static function balanceBrackets(string $fake_class): string
- {
- $tokens = token_get_all($fake_class);
-
- $brace_count = 0;
-
- foreach ($tokens as $token) {
- if ($token === '{') {
- ++$brace_count;
- } elseif ($token === '}') {
- --$brace_count;
- }
- }
-
- if ($brace_count > 0) {
- $fake_class .= str_repeat('}', $brace_count);
- }
-
- return $fake_class;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php
deleted file mode 100644
index 9caf12d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/AttributeResolver.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\Storage\AttributeArg;
-use Psalm\Storage\AttributeStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Type;
-
-use function strtolower;
-
-class AttributeResolver
-{
- public static function resolve(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- PhpParser\Node\Attribute $stmt,
- ?string $fq_classlike_name
- ): AttributeStorage {
- if ($stmt->name instanceof PhpParser\Node\Name\FullyQualified) {
- $fq_type_string = (string)$stmt->name;
- } else {
- $fq_type_string = ClassLikeAnalyzer::getFQCLNFromNameObject($stmt->name, $aliases);
- }
-
- $codebase->scanner->queueClassLikeForScanning($fq_type_string);
- $file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
-
- $args = [];
-
- foreach ($stmt->args as $arg_node) {
- $key = $arg_node->name->name ?? null;
-
- $const_type = SimpleTypeInferer::infer(
- $codebase,
- new NodeDataProvider(),
- $arg_node->value,
- $aliases,
- null,
- [],
- $fq_classlike_name
- );
-
- if (!$const_type) {
- $const_type = ExpressionResolver::getUnresolvedClassConstExpr(
- $arg_node->value,
- $aliases,
- $fq_classlike_name
- );
- }
-
- if (!$const_type) {
- $const_type = Type::getMixed();
- }
-
- $args[] = new AttributeArg(
- $key,
- $const_type,
- new CodeLocation($file_scanner, $arg_node->value)
- );
- }
-
- return new AttributeStorage(
- $fq_type_string,
- $args,
- new CodeLocation($file_scanner, $stmt),
- new CodeLocation($file_scanner, $stmt->name)
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php
deleted file mode 100644
index 4572df3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php
+++ /dev/null
@@ -1,562 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use Exception;
-use PhpParser\Comment\Doc;
-use PhpParser\Node;
-use PhpParser\Node\Stmt\ClassMethod;
-use PhpParser\Node\Stmt\Class_;
-use Psalm\Aliases;
-use Psalm\DocComment;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Provider\StatementsProvider;
-use Psalm\Internal\Scanner\ClassLikeDocblockComment;
-use Psalm\Internal\Scanner\DocblockParser;
-use Psalm\Internal\Type\ParseTree\MethodParamTree;
-use Psalm\Internal\Type\ParseTree\MethodTree;
-use Psalm\Internal\Type\ParseTree\MethodWithReturnTypeTree;
-use Psalm\Internal\Type\ParseTreeCreator;
-use Psalm\Internal\Type\TypeParser;
-use Psalm\Internal\Type\TypeTokenizer;
-
-use function array_key_first;
-use function array_shift;
-use function count;
-use function explode;
-use function implode;
-use function in_array;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function reset;
-use function str_replace;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-use function substr_count;
-use function trim;
-
-use const PREG_OFFSET_CAPTURE;
-
-/**
- * @internal
- */
-class ClassLikeDocblockParser
-{
- /**
- * @throws DocblockParseException if there was a problem parsing the docblock
- */
- public static function parse(
- Node $node,
- Doc $comment,
- Aliases $aliases
- ): ClassLikeDocblockComment {
- $parsed_docblock = DocComment::parsePreservingLength($comment);
- $codebase = ProjectAnalyzer::getInstance()->getCodebase();
-
- $info = new ClassLikeDocblockComment();
-
- $templates = [];
- if (isset($parsed_docblock->combined_tags['template'])) {
- foreach ($parsed_docblock->combined_tags['template'] as $offset => $template_line) {
- $template_type = preg_split('/[\s]+/', preg_replace('@^[ \t]*\*@m', '', $template_line));
-
- $template_name = array_shift($template_type);
-
- if (!$template_name) {
- throw new IncorrectDocblockException('Empty @template tag');
- }
-
- $source_prefix = 'none';
- if (isset($parsed_docblock->tags['psalm-template'][$offset])) {
- $source_prefix = 'psalm';
- } elseif (isset($parsed_docblock->tags['phpstan-template'][$offset])) {
- $source_prefix = 'phpstan';
- }
-
- if (count($template_type) > 1
- && in_array(strtolower($template_type[0]), ['as', 'super', 'of'], true)
- ) {
- $template_modifier = strtolower(array_shift($template_type));
- $templates[$template_name][$source_prefix] = [
- $template_name,
- $template_modifier,
- implode(' ', $template_type),
- false,
- $offset - $comment->getStartFilePos()
- ];
- } else {
- $templates[$template_name][$source_prefix] = [
- $template_name,
- null,
- null,
- false,
- $offset - $comment->getStartFilePos()
- ];
- }
- }
- }
-
- if (isset($parsed_docblock->combined_tags['template-covariant'])) {
- foreach ($parsed_docblock->combined_tags['template-covariant'] as $offset => $template_line) {
- $template_type = preg_split('/[\s]+/', preg_replace('@^[ \t]*\*@m', '', $template_line));
-
- $template_name = array_shift($template_type);
-
- if (!$template_name) {
- throw new IncorrectDocblockException('Empty @template-covariant tag');
- }
-
- $source_prefix = 'none';
- if (isset($parsed_docblock->tags['psalm-template-covariant'][$offset])) {
- $source_prefix = 'psalm';
- } elseif (isset($parsed_docblock->tags['phpstan-template-covariant'][$offset])) {
- $source_prefix = 'phpstan';
- }
-
- if (count($template_type) > 1
- && in_array(strtolower($template_type[0]), ['as', 'super', 'of'], true)
- ) {
- $template_modifier = strtolower(array_shift($template_type));
- $templates[$template_name][$source_prefix] = [
- $template_name,
- $template_modifier,
- implode(' ', $template_type),
- true,
- $offset - $comment->getStartFilePos()
- ];
- } else {
- $templates[$template_name][$source_prefix] = [
- $template_name,
- null,
- null,
- true,
- $offset - $comment->getStartFilePos()
- ];
- }
- }
- }
-
- foreach ($templates as $template_entries) {
- foreach (['psalm', 'phpstan', 'none'] as $source_prefix) {
- if (isset($template_entries[$source_prefix])) {
- $info->templates[] = $template_entries[$source_prefix];
- break;
- }
- }
- }
-
- if (isset($parsed_docblock->combined_tags['extends'])) {
- foreach ($parsed_docblock->combined_tags['extends'] as $template_line) {
- $doc_line_parts = CommentAnalyzer::splitDocLine($template_line);
- $doc_line_parts[0] = CommentAnalyzer::sanitizeDocblockType($doc_line_parts[0]);
- $info->template_extends[] = $doc_line_parts[0];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-require-extends'])
- && count($extension_requirements = $parsed_docblock->tags['psalm-require-extends']) > 0) {
- $info->extension_requirement = trim(preg_replace(
- '@^[ \t]*\*@m',
- '',
- $extension_requirements[array_key_first($extension_requirements)]
- ));
- }
-
- if (isset($parsed_docblock->tags['psalm-require-implements'])) {
- foreach ($parsed_docblock->tags['psalm-require-implements'] as $implementation_requirement) {
- $info->implementation_requirements[] = trim(preg_replace(
- '@^[ \t]*\*@m',
- '',
- $implementation_requirement
- ));
- }
- }
-
- if (isset($parsed_docblock->combined_tags['implements'])) {
- foreach ($parsed_docblock->combined_tags['implements'] as $template_line) {
- $doc_line_parts = CommentAnalyzer::splitDocLine($template_line);
- $doc_line_parts[0] = CommentAnalyzer::sanitizeDocblockType($doc_line_parts[0]);
- $info->template_implements[] = $doc_line_parts[0];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-yield'])) {
- $yield = reset($parsed_docblock->tags['psalm-yield']);
-
- $info->yield = trim(preg_replace('@^[ \t]*\*@m', '', $yield));
- }
-
- if (isset($parsed_docblock->tags['deprecated'])) {
- $info->deprecated = true;
- }
-
- if (isset($parsed_docblock->tags['internal'])) {
- $info->internal = true;
- }
-
- if (isset($parsed_docblock->tags['final'])) {
- $info->final = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-consistent-constructor'])) {
- $info->consistent_constructor = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-consistent-templates'])) {
- $info->consistent_templates = true;
- }
-
- if (count($info->psalm_internal = DocblockParser::handlePsalmInternal($parsed_docblock)) !== 0) {
- $info->internal = true;
- }
-
- if (isset($parsed_docblock->tags['mixin'])) {
- foreach ($parsed_docblock->tags['mixin'] as $rawMixin) {
- $mixin = trim($rawMixin);
- $doc_line_parts = CommentAnalyzer::splitDocLine($mixin);
- $mixin = $doc_line_parts[0];
-
- if ($mixin) {
- $info->mixins[] = $mixin;
- } else {
- throw new DocblockParseException('@mixin annotation used without specifying class');
- }
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-seal-properties'])) {
- $info->sealed_properties = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-seal-methods'])) {
- $info->sealed_methods = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-immutable'])
- || isset($parsed_docblock->tags['psalm-mutation-free'])
- ) {
- $info->mutation_free = true;
- $info->external_mutation_free = true;
- $info->taint_specialize = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-external-mutation-free'])) {
- $info->external_mutation_free = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-specialize'])) {
- $info->taint_specialize = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-override-property-visibility'])) {
- $info->override_property_visibility = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-override-method-visibility'])) {
- $info->override_method_visibility = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-suppress'])) {
- foreach ($parsed_docblock->tags['psalm-suppress'] as $offset => $suppress_entry) {
- foreach (DocComment::parseSuppressList($suppress_entry) as $issue_offset => $suppressed_issue) {
- $info->suppressed_issues[$issue_offset + $offset] = $suppressed_issue;
- }
- }
- }
-
- $imported_types = ($parsed_docblock->tags['phpstan-import-type'] ?? []) +
- ($parsed_docblock->tags['psalm-import-type'] ?? []);
-
- foreach ($imported_types as $offset => $imported_type_entry) {
- $info->imported_types[] = [
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- 'start_offset' => $offset,
- 'end_offset' => $offset + strlen($imported_type_entry),
- 'parts' => CommentAnalyzer::splitDocLine($imported_type_entry)
- ];
- }
-
- if (isset($parsed_docblock->combined_tags['method'])) {
- foreach ($parsed_docblock->combined_tags['method'] as $offset => $method_entry) {
- $method_entry = preg_replace('/[ \t]+/', ' ', trim($method_entry));
-
- $docblock_lines = [];
-
- $is_static = false;
-
- $has_return = false;
-
- if (!preg_match('/^([a-z_A-Z][a-z_0-9A-Z]+) *\(/', $method_entry, $matches)) {
- $doc_line_parts = CommentAnalyzer::splitDocLine($method_entry);
-
- if ($doc_line_parts[0] === 'static' && !strpos($doc_line_parts[1], '(')) {
- $is_static = true;
- array_shift($doc_line_parts);
- }
-
- if (count($doc_line_parts) > 1) {
- $docblock_lines[] = '@return ' . array_shift($doc_line_parts);
- $has_return = true;
-
- $method_entry = implode(' ', $doc_line_parts);
- }
- }
-
- $method_entry = trim(preg_replace('/\/\/.*/', '', $method_entry));
-
- $method_entry = preg_replace(
- '/array\(([0-9a-zA-Z_\'\" ]+,)*([0-9a-zA-Z_\'\" ]+)\)/',
- '[]',
- $method_entry
- );
-
- $end_of_method_regex = '/(?<!array\()\) ?(\: ?(\??[\\\\a-zA-Z0-9_]+))?/';
-
- if (preg_match($end_of_method_regex, $method_entry, $matches, PREG_OFFSET_CAPTURE)) {
- $method_entry = substr($method_entry, 0, $matches[0][1] + strlen($matches[0][0]));
- }
-
- $method_entry = str_replace([', ', '( '], [',', '('], $method_entry);
- $method_entry = preg_replace('/ (?!(\$|\.\.\.|&))/', '', trim($method_entry));
-
- // replace array bracket contents
- $method_entry = preg_replace('/\[([0-9a-zA-Z_\'\" ]+,)*([0-9a-zA-Z_\'\" ]+)\]/', '[]', $method_entry);
-
- if (!$method_entry) {
- throw new DocblockParseException('No @method entry specified');
- }
-
- try {
- $parse_tree_creator = new ParseTreeCreator(
- TypeTokenizer::getFullyQualifiedTokens(
- $method_entry,
- $aliases,
- null
- )
- );
-
- $method_tree = $parse_tree_creator->create();
- } catch (TypeParseTreeException $e) {
- throw new DocblockParseException($method_entry . ' is not a valid method');
- }
-
- if (!$method_tree instanceof MethodWithReturnTypeTree
- && !$method_tree instanceof MethodTree) {
- throw new DocblockParseException($method_entry . ' is not a valid method');
- }
-
- if ($method_tree instanceof MethodWithReturnTypeTree) {
- if (!$has_return) {
- $docblock_lines[] = '@return ' . TypeParser::getTypeFromTree(
- $method_tree->children[1],
- $codebase
- )->toNamespacedString($aliases->namespace, $aliases->uses, null, false);
- }
-
- $method_tree = $method_tree->children[0];
- }
-
- if (!$method_tree instanceof MethodTree) {
- throw new DocblockParseException($method_entry . ' is not a valid method');
- }
-
- $args = [];
-
- foreach ($method_tree->children as $method_tree_child) {
- if (!$method_tree_child instanceof MethodParamTree) {
- throw new DocblockParseException($method_entry . ' is not a valid method');
- }
-
- $args[] = ($method_tree_child->byref ? '&' : '')
- . ($method_tree_child->variadic ? '...' : '')
- . $method_tree_child->name
- . ($method_tree_child->default != '' ? ' = ' . $method_tree_child->default : '');
-
-
- if ($method_tree_child->children) {
- try {
- $param_type = TypeParser::getTypeFromTree($method_tree_child->children[0], $codebase);
- } catch (Exception $e) {
- throw new DocblockParseException(
- 'Badly-formatted @method string ' . $method_entry . ' - ' . $e
- );
- }
-
- $param_type_string = $param_type->toNamespacedString('\\', [], null, false);
- $docblock_lines[] = '@param ' . $param_type_string . ' '
- . ($method_tree_child->variadic ? '...' : '')
- . $method_tree_child->name;
- }
- }
-
- $function_string = 'function ' . $method_tree->value . '(' . implode(', ', $args) . ')';
-
- if ($is_static) {
- $function_string = 'static ' . $function_string;
- }
-
- $function_docblock = $docblock_lines ? "/**\n * " . implode("\n * ", $docblock_lines) . "\n*/\n" : "";
-
- $php_string = '<?php class A { ' . $function_docblock . ' public ' . $function_string . '{} }';
-
- try {
- $has_errors = false;
-
- $statements = StatementsProvider::parseStatements(
- $php_string,
- $codebase->php_major_version . '.' . $codebase->php_minor_version,
- $has_errors
- );
- } catch (Exception $e) {
- throw new DocblockParseException('Badly-formatted @method string ' . $method_entry);
- }
-
- if (!$statements
- || !$statements[0] instanceof Class_
- || !isset($statements[0]->stmts[0])
- || !$statements[0]->stmts[0] instanceof ClassMethod
- ) {
- throw new DocblockParseException('Badly-formatted @method string ' . $method_entry);
- }
-
- /** @var Doc */
- $node_doc_comment = $node->getDocComment();
-
- $method_offset = self::getMethodOffset($comment, $method_entry);
-
- $statements[0]->stmts[0]->setAttribute('startLine', $node_doc_comment->getStartLine() + $method_offset);
- $statements[0]->stmts[0]->setAttribute('startFilePos', $node_doc_comment->getStartFilePos());
- $statements[0]->stmts[0]->setAttribute('endFilePos', $node->getAttribute('startFilePos'));
-
- if ($doc_comment = $statements[0]->stmts[0]->getDocComment()) {
- $statements[0]->stmts[0]->setDocComment(
- new Doc(
- $doc_comment->getText(),
- $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- $node_doc_comment->getStartFilePos()
- )
- );
- }
-
- $info->methods[] = $statements[0]->stmts[0];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-stub-override'])) {
- $info->stub_override = true;
- }
-
- if ($parsed_docblock->description) {
- $info->description = $parsed_docblock->description;
- }
-
- self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property');
- self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'psalm-property');
- self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property-read');
- self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'psalm-property-read');
- self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property-write');
- self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'psalm-property-write');
-
- return $info;
- }
-
- /**
- * @param array<string, array<int, string>> $specials
- * @param 'property'|'psalm-property'|'property-read'|
- * 'psalm-property-read'|'property-write'|'psalm-property-write' $property_tag
- *
- * @throws DocblockParseException
- *
- */
- protected static function addMagicPropertyToInfo(
- Doc $comment,
- ClassLikeDocblockComment $info,
- array $specials,
- string $property_tag
- ): void {
- $magic_property_comments = $specials[$property_tag] ?? [];
-
- foreach ($magic_property_comments as $offset => $property) {
- $line_parts = CommentAnalyzer::splitDocLine($property);
-
- if (count($line_parts) === 1 && isset($line_parts[0][0]) && $line_parts[0][0] === '$') {
- continue;
- }
-
- if (count($line_parts) > 1) {
- if (preg_match('/^&?\$[A-Za-z0-9_]+,?$/', $line_parts[1])
- && $line_parts[0][0] !== '{'
- ) {
- $line_parts[1] = str_replace('&', '', $line_parts[1]);
-
- $line_parts[1] = preg_replace('/,$/', '', $line_parts[1]);
-
- $end = $offset + strlen($line_parts[0]);
-
- $line_parts[0] = str_replace("\n", '', preg_replace('@^[ \t]*\*@m', '', $line_parts[0]));
-
- if ($line_parts[0] === ''
- || ($line_parts[0][0] === '$'
- && !preg_match('/^\$this(\||$)/', $line_parts[0]))
- ) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- $name = trim($line_parts[1]);
-
- if (!preg_match('/^\$([a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*)$/', $name)) {
- throw new DocblockParseException('Badly-formatted @property name');
- }
-
- $info->properties[] = [
- 'name' => $name,
- 'type' => $line_parts[0],
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- 'tag' => $property_tag,
- 'start' => $offset,
- 'end' => $end,
- ];
- }
- } else {
- throw new DocblockParseException('Badly-formatted @property');
- }
- }
- }
-
- private static function getMethodOffset(Doc $comment, string $method_entry): int
- {
- $lines = explode("\n", $comment->getText());
- $method_offset = 0;
-
- foreach ($lines as $i => $line) {
- if (strpos($line, $method_entry) !== false) {
- $method_offset = $i;
- break;
- }
- }
-
- return $method_offset;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php
deleted file mode 100644
index 91fbfbf..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php
+++ /dev/null
@@ -1,1861 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use Exception;
-use InvalidArgumentException;
-use LogicException;
-use PhpParser;
-use PhpParser\Node\Expr\BinaryOp\Concat;
-use PhpParser\Node\Identifier;
-use PhpParser\Node\Name;
-use PhpParser\Node\NullableType;
-use PhpParser\Node\UnionType;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\DocComment;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Exception\InvalidClasslikeOverrideException;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Analyzer\ClassAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\Codebase\PropertyMap;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Scanner\ClassLikeDocblockComment;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\Internal\Type\TypeAlias;
-use Psalm\Internal\Type\TypeAlias\ClassTypeAlias;
-use Psalm\Internal\Type\TypeAlias\InlineTypeAlias;
-use Psalm\Internal\Type\TypeAlias\LinkableTypeAlias;
-use Psalm\Internal\Type\TypeParser;
-use Psalm\Internal\Type\TypeTokenizer;
-use Psalm\Issue\DuplicateClass;
-use Psalm\Issue\DuplicateConstant;
-use Psalm\Issue\DuplicateEnumCase;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\InvalidEnumBackingType;
-use Psalm\Issue\InvalidTypeImport;
-use Psalm\Issue\MissingDocblockType;
-use Psalm\Issue\ParseError;
-use Psalm\IssueBuffer;
-use Psalm\Storage\AttributeStorage;
-use Psalm\Storage\ClassConstantStorage;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\EnumCaseStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Storage\PropertyStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use RuntimeException;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_map;
-use function array_merge;
-use function array_pop;
-use function array_shift;
-use function array_values;
-use function assert;
-use function count;
-use function get_class;
-use function implode;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function str_replace;
-use function strtolower;
-use function trim;
-use function usort;
-
-use const PREG_SPLIT_DELIM_CAPTURE;
-use const PREG_SPLIT_NO_EMPTY;
-
-class ClassLikeNodeScanner
-{
- /**
- * @var FileScanner
- */
- private $file_scanner;
-
- /**
- * @var Codebase
- */
- private $codebase;
-
- /**
- * @var string
- */
- private $file_path;
-
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var FileStorage
- */
- private $file_storage;
-
- /**
- * @var array<string, InlineTypeAlias>
- */
- private $classlike_type_aliases = [];
-
- /**
- * @var array<string, array<string, Union>>
- */
- public $class_template_types = [];
-
- /**
- * @var PhpParser\Node\Name|null
- */
- private $namespace_name;
-
- /**
- * @var Aliases
- */
- private $aliases;
-
- /**
- * @var ?ClassLikeStorage
- */
- public $storage;
-
- /**
- * @var array<string, TypeAlias>
- */
- public $type_aliases = [];
-
- public function __construct(
- Codebase $codebase,
- FileStorage $file_storage,
- FileScanner $file_scanner,
- Aliases $aliases,
- ?PhpParser\Node\Name $namespace_name
- ) {
- $this->codebase = $codebase;
- $this->file_storage = $file_storage;
- $this->file_scanner = $file_scanner;
- $this->file_path = $file_storage->file_path;
- $this->aliases = $aliases;
- $this->config = Config::getInstance();
- $this->namespace_name = $namespace_name;
- }
-
- /**
- * @return false|null
- */
- public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool
- {
- $class_location = new CodeLocation($this->file_scanner, $node);
- $name_location = null;
-
- $storage = null;
-
- $class_name = null;
-
- $is_classlike_overridden = false;
-
- if ($node->name === null) {
- if (!$node instanceof PhpParser\Node\Stmt\Class_) {
- throw new LogicException('Anonymous classes are always classes');
- }
-
- $fq_classlike_name = ClassAnalyzer::getAnonymousClassName($node, $this->file_path);
- } else {
- $name_location = new CodeLocation($this->file_scanner, $node->name);
-
- $fq_classlike_name =
- ($this->aliases->namespace ? $this->aliases->namespace . '\\' : '') . $node->name->name;
- assert($fq_classlike_name !== "");
-
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- $class_name = $node->name->name;
-
- if ($this->codebase->classlike_storage_provider->has($fq_classlike_name_lc)) {
- $duplicate_storage = $this->codebase->classlike_storage_provider->get($fq_classlike_name_lc);
-
- if (!$this->codebase->register_stub_files) {
- if (!$duplicate_storage->stmt_location
- || $duplicate_storage->stmt_location->file_path !== $this->file_path
- || $class_location->getHash() !== $duplicate_storage->stmt_location->getHash()
- ) {
- if (IssueBuffer::accepts(
- new DuplicateClass(
- 'Class ' . $fq_classlike_name . ' has already been defined'
- . ($duplicate_storage->location
- ? ' in ' . $duplicate_storage->location->file_path
- : ''),
- $name_location
- )
- )) {
- }
-
- $this->file_storage->has_visitor_issues = true;
-
- $duplicate_storage->has_visitor_issues = true;
-
- return false;
- }
- } elseif (!$duplicate_storage->location
- || $duplicate_storage->location->file_path !== $this->file_path
- || $class_location->getHash() !== $duplicate_storage->location->getHash()
- ) {
- $is_classlike_overridden = true;
- // we're overwriting some methods
- $storage = $this->storage = $duplicate_storage;
- $this->codebase->classlike_storage_provider->makeNew(strtolower($fq_classlike_name));
- $storage->populated = false;
- $storage->class_implements = []; // we do this because reflection reports
- $storage->parent_interfaces = [];
- $storage->stubbed = true;
- $storage->aliases = $this->aliases;
-
- foreach ($storage->dependent_classlikes as $dependent_name_lc => $_) {
- try {
- $dependent_storage = $this->codebase->classlike_storage_provider->get($dependent_name_lc);
- } catch (InvalidArgumentException $exception) {
- continue;
- }
- $dependent_storage->populated = false;
- $this->codebase->classlike_storage_provider->makeNew($dependent_name_lc);
- }
- }
- }
- }
-
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- $this->file_storage->classlikes_in_file[$fq_classlike_name_lc] = $fq_classlike_name;
-
- if (!$storage) {
- $this->storage = $storage = $this->codebase->classlike_storage_provider->create($fq_classlike_name);
- }
-
- if ($class_name
- && isset($this->aliases->uses[strtolower($class_name)])
- && $this->aliases->uses[strtolower($class_name)] !== $fq_classlike_name
- ) {
- IssueBuffer::maybeAdd(
- new ParseError(
- 'Class name ' . $class_name . ' clashes with a use statement alias',
- $name_location ?? $class_location
- )
- );
-
- $storage->has_visitor_issues = true;
- $this->file_storage->has_visitor_issues = true;
- }
-
- $storage->stmt_location = $class_location;
- $storage->location = $name_location;
- if ($this->namespace_name) {
- $storage->namespace_name_location = new CodeLocation($this->file_scanner, $this->namespace_name);
- }
- $storage->user_defined = !$this->codebase->register_stub_files;
- $storage->stubbed = $this->codebase->register_stub_files;
- $storage->aliases = $this->aliases;
-
- if ($node instanceof PhpParser\Node\Stmt\Class_) {
- $storage->abstract = $node->isAbstract();
- $storage->final = $node->isFinal();
-
- $this->codebase->classlikes->addFullyQualifiedClassName($fq_classlike_name, $this->file_path);
-
- if ($node->extends) {
- $parent_fqcln = ClassLikeAnalyzer::getFQCLNFromNameObject($node->extends, $this->aliases);
- $parent_fqcln = $this->codebase->classlikes->getUnAliasedName($parent_fqcln);
- $this->codebase->scanner->queueClassLikeForScanning(
- $parent_fqcln,
- $this->file_scanner->will_analyze
- );
- $parent_fqcln_lc = strtolower($parent_fqcln);
- $storage->parent_class = $parent_fqcln;
- $storage->parent_classes[$parent_fqcln_lc] = $parent_fqcln;
- $this->file_storage->required_classes[strtolower($parent_fqcln)] = $parent_fqcln;
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\Interface_) {
- $storage->is_interface = true;
- $this->codebase->classlikes->addFullyQualifiedInterfaceName($fq_classlike_name, $this->file_path);
-
- foreach ($node->extends as $interface) {
- $interface_fqcln = ClassLikeAnalyzer::getFQCLNFromNameObject($interface, $this->aliases);
- $interface_fqcln = $this->codebase->classlikes->getUnAliasedName($interface_fqcln);
- $interface_fqcln_lc = strtolower($interface_fqcln);
- $this->codebase->scanner->queueClassLikeForScanning($interface_fqcln);
- $storage->parent_interfaces[$interface_fqcln_lc] = $interface_fqcln;
- $storage->direct_interface_parents[$interface_fqcln_lc] = $interface_fqcln;
- $this->file_storage->required_interfaces[$interface_fqcln_lc] = $interface_fqcln;
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\Trait_) {
- $storage->is_trait = true;
- $this->codebase->classlikes->addFullyQualifiedTraitName($fq_classlike_name, $this->file_path);
- } elseif ($node instanceof PhpParser\Node\Stmt\Enum_) {
- $storage->is_enum = true;
-
- if ($node->scalarType) {
- if ($node->scalarType->name === 'string' || $node->scalarType->name === 'int') {
- $storage->enum_type = $node->scalarType->name;
- } else {
- IssueBuffer::maybeAdd(
- new InvalidEnumBackingType(
- 'Enums cannot be backed by ' . $node->scalarType->name . ', string or int expected',
- new CodeLocation($this->file_scanner, $node->scalarType),
- $fq_classlike_name
- )
- );
- $this->file_storage->has_visitor_issues = true;
- $storage->has_visitor_issues = true;
- }
- $storage->class_implements['backedenum'] = 'BackedEnum';
- $storage->direct_class_interfaces['backedenum'] = 'BackedEnum';
- $this->file_storage->required_interfaces['backedenum'] = 'BackedEnum';
- $this->codebase->scanner->queueClassLikeForScanning('BackedEnum');
- $storage->declaring_method_ids['from'] = new MethodIdentifier('BackedEnum', 'from');
- $storage->appearing_method_ids['from'] = $storage->declaring_method_ids['from'];
- $storage->declaring_method_ids['tryfrom'] = new MethodIdentifier(
- 'BackedEnum',
- 'tryfrom'
- );
- $storage->appearing_method_ids['tryfrom'] = $storage->declaring_method_ids['tryfrom'];
- }
-
- $this->codebase->scanner->queueClassLikeForScanning('UnitEnum');
- $storage->class_implements['unitenum'] = 'UnitEnum';
- $storage->direct_class_interfaces['unitenum'] = 'UnitEnum';
- $this->file_storage->required_interfaces['unitenum'] = 'UnitEnum';
- $storage->final = true;
-
- $storage->declaring_method_ids['cases'] = new MethodIdentifier(
- 'UnitEnum',
- 'cases'
- );
- $storage->appearing_method_ids['cases'] = $storage->declaring_method_ids['cases'];
-
- $this->codebase->classlikes->addFullyQualifiedEnumName($fq_classlike_name, $this->file_path);
- } else {
- throw new UnexpectedValueException('Unknown classlike type');
- }
-
- if ($node instanceof PhpParser\Node\Stmt\Class_ || $node instanceof PhpParser\Node\Stmt\Enum_) {
- foreach ($node->implements as $interface) {
- $interface_fqcln = ClassLikeAnalyzer::getFQCLNFromNameObject($interface, $this->aliases);
- $interface_fqcln_lc = strtolower($interface_fqcln);
- $this->codebase->scanner->queueClassLikeForScanning($interface_fqcln);
- $storage->class_implements[$interface_fqcln_lc] = $interface_fqcln;
- $storage->direct_class_interfaces[$interface_fqcln_lc] = $interface_fqcln;
- $this->file_storage->required_interfaces[$interface_fqcln_lc] = $interface_fqcln;
- }
- }
-
- $docblock_info = null;
- $doc_comment = $node->getDocComment();
- if ($doc_comment) {
- try {
- $docblock_info = ClassLikeDocblockParser::parse(
- $node,
- $doc_comment,
- $this->aliases
- );
-
- $this->type_aliases += $this->getImportedTypeAliases($docblock_info, $fq_classlike_name);
- } catch (DocblockParseException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $fq_classlike_name,
- $name_location ?? $class_location
- );
- }
- }
-
- foreach ($node->getComments() as $comment) {
- if (!$comment instanceof PhpParser\Comment\Doc) {
- continue;
- }
-
- try {
- $type_aliases = self::getTypeAliasesFromComment(
- $comment,
- $this->aliases,
- $this->type_aliases,
- $fq_classlike_name
- );
-
- foreach ($type_aliases as $type_alias) {
- // finds issues, if there are any
- TypeParser::parseTokens($type_alias->replacement_tokens);
- }
-
- $this->type_aliases += $type_aliases;
-
- if ($type_aliases) {
- $this->classlike_type_aliases = $type_aliases;
- }
- } catch (DocblockParseException | TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
- }
- }
-
- if ($docblock_info) {
- if ($docblock_info->stub_override && !$is_classlike_overridden) {
- throw new InvalidClasslikeOverrideException(
- 'Class/interface/trait ' . $fq_classlike_name . ' is marked as stub override,'
- . ' but no original counterpart found'
- );
- }
-
- if ($docblock_info->templates) {
- $storage->template_types = [];
-
- usort(
- $docblock_info->templates,
- function (array $l, array $r): int {
- return $l[4] > $r[4] ? 1 : -1;
- }
- );
-
- foreach ($docblock_info->templates as $i => $template_map) {
- $template_name = $template_map[0];
-
- if ($template_map[1] !== null && $template_map[2] !== null) {
- if (trim($template_map[2])) {
- try {
- $template_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $template_map[2],
- $this->aliases,
- $storage->template_types,
- $this->type_aliases
- ),
- null,
- $storage->template_types,
- $this->type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $fq_classlike_name,
- $name_location ?? $class_location
- );
-
- continue;
- }
-
- $storage->template_types[$template_name] = [
- $fq_classlike_name => $template_type,
- ];
- } else {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Template missing as type',
- $name_location ?? $class_location
- );
- }
- } else {
- /** @psalm-suppress PropertyTypeCoercion due to a Psalm bug */
- $storage->template_types[$template_name][$fq_classlike_name] = Type::getMixed();
- }
-
- $storage->template_covariants[$i] = $template_map[3];
- }
-
- $this->class_template_types = $storage->template_types;
- }
-
- foreach ($docblock_info->template_extends as $extended_class_name) {
- $this->extendTemplatedType($storage, $node, $extended_class_name);
- }
-
- foreach ($docblock_info->template_implements as $implemented_class_name) {
- $this->implementTemplatedType($storage, $node, $implemented_class_name);
- }
-
- if ($docblock_info->yield) {
- try {
- $yield_type_tokens = TypeTokenizer::getFullyQualifiedTokens(
- $docblock_info->yield,
- $this->aliases,
- $storage->template_types,
- $this->type_aliases
- );
-
- $yield_type = TypeParser::parseTokens(
- $yield_type_tokens,
- null,
- $storage->template_types ?: [],
- $this->type_aliases
- );
- $yield_type->setFromDocblock();
- $yield_type->queueClassLikesForScanning(
- $this->codebase,
- $this->file_storage,
- $storage->template_types ?: []
- );
-
- $storage->yield = $yield_type;
- } catch (TypeParseTreeException $e) {
- // do nothing
- }
- }
-
- if ($docblock_info->extension_requirement !== null) {
- $storage->extension_requirement = (string) TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $docblock_info->extension_requirement,
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases
- ),
- null,
- $this->class_template_types,
- $this->type_aliases
- );
- }
-
- foreach ($docblock_info->implementation_requirements as $implementation_requirement) {
- $storage->implementation_requirements[] = (string) TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $implementation_requirement,
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases
- ),
- null,
- $this->class_template_types,
- $this->type_aliases
- );
- }
-
- $storage->sealed_properties = $docblock_info->sealed_properties;
- $storage->sealed_methods = $docblock_info->sealed_methods;
-
- if ($docblock_info->properties) {
- foreach ($docblock_info->properties as $property) {
- $pseudo_property_type_tokens = TypeTokenizer::getFullyQualifiedTokens(
- $property['type'],
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases
- );
-
- try {
- $pseudo_property_type = TypeParser::parseTokens(
- $pseudo_property_type_tokens,
- null,
- $this->class_template_types,
- $this->type_aliases
- );
- $pseudo_property_type->setFromDocblock();
- $pseudo_property_type->queueClassLikesForScanning(
- $this->codebase,
- $this->file_storage,
- $storage->template_types ?: []
- );
-
- if ($property['tag'] !== 'property-read' && $property['tag'] !== 'psalm-property-read') {
- $storage->pseudo_property_set_types[$property['name']] = $pseudo_property_type;
- }
-
- if ($property['tag'] !== 'property-write' && $property['tag'] !== 'psalm-property-write') {
- $storage->pseudo_property_get_types[$property['name']] = $pseudo_property_type;
- }
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $fq_classlike_name,
- $name_location ?? $class_location
- );
- }
- }
-
- $storage->sealed_properties = true;
- }
-
- foreach ($docblock_info->methods as $method) {
- $functionlike_node_scanner = new FunctionLikeNodeScanner(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $this->type_aliases,
- $this->storage,
- []
- );
-
- /** @var MethodStorage */
- $pseudo_method_storage = $functionlike_node_scanner->start($method, true);
- $lc_method_name = strtolower($method->name->name);
-
- if ($pseudo_method_storage->is_static) {
- $storage->pseudo_static_methods[$lc_method_name] = $pseudo_method_storage;
- } else {
- $storage->pseudo_methods[$lc_method_name] = $pseudo_method_storage;
- $storage->declaring_pseudo_method_ids[$lc_method_name] = new MethodIdentifier(
- $fq_classlike_name,
- $lc_method_name
- );
- }
-
- $storage->sealed_methods = true;
- }
-
-
- $storage->deprecated = $docblock_info->deprecated;
-
- if (count($docblock_info->psalm_internal) !== 0) {
- $storage->internal = $docblock_info->psalm_internal;
- } elseif ($docblock_info->internal && $this->aliases->namespace) {
- $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($this->aliases->namespace)];
- }
-
- if ($docblock_info->final && !$storage->final) {
- $storage->final = true;
- $storage->final_from_docblock = true;
- }
-
- $storage->preserve_constructor_signature = $docblock_info->consistent_constructor;
-
- if ($storage->preserve_constructor_signature) {
- $has_constructor = false;
-
- foreach ($node->stmts as $stmt) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
- && $stmt->name->name === '__construct'
- ) {
- $has_constructor = true;
- break;
- }
- }
-
- if (!$has_constructor) {
- self::registerEmptyConstructor($storage);
- }
- }
-
- $storage->enforce_template_inheritance = $docblock_info->consistent_templates;
-
- foreach ($docblock_info->mixins as $key => $mixin) {
- $mixin_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $mixin,
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases,
- $fq_classlike_name
- ),
- null,
- $this->class_template_types,
- $this->type_aliases
- );
-
- $mixin_type->queueClassLikesForScanning(
- $this->codebase,
- $this->file_storage,
- $storage->template_types ?: []
- );
-
- $mixin_type->setFromDocblock();
-
- if ($mixin_type->isSingle()) {
- $mixin_type = $mixin_type->getSingleAtomic();
-
- if ($mixin_type instanceof TNamedObject) {
- $storage->namedMixins[] = $mixin_type;
- }
-
- if ($mixin_type instanceof TTemplateParam) {
- $storage->templatedMixins[] = $mixin_type;
- }
- }
-
- if ($key === 0) {
- $storage->mixin_declaring_fqcln = $storage->name;
- }
- }
-
- $storage->mutation_free = $docblock_info->mutation_free;
- $storage->external_mutation_free = $docblock_info->external_mutation_free;
- $storage->specialize_instance = $docblock_info->taint_specialize;
-
- $storage->override_property_visibility = $docblock_info->override_property_visibility;
- $storage->override_method_visibility = $docblock_info->override_method_visibility;
-
- $storage->suppressed_issues = $docblock_info->suppressed_issues;
-
- if ($docblock_info->description) {
- $storage->description = $docblock_info->description;
- }
- }
-
- foreach ($node->stmts as $node_stmt) {
- if ($node_stmt instanceof PhpParser\Node\Stmt\ClassConst) {
- $this->visitClassConstDeclaration($node_stmt, $storage, $fq_classlike_name);
- } elseif ($node_stmt instanceof PhpParser\Node\Stmt\EnumCase
- && $node instanceof PhpParser\Node\Stmt\Enum_
- ) {
- $this->visitEnumDeclaration($node_stmt, $storage, $fq_classlike_name);
- }
- }
-
- foreach ($node->stmts as $node_stmt) {
- if ($node_stmt instanceof PhpParser\Node\Stmt\Property) {
- $this->visitPropertyDeclaration($node_stmt, $this->config, $storage, $fq_classlike_name);
- }
- }
-
- foreach ($node->attrGroups as $attr_group) {
- foreach ($attr_group->attrs as $attr) {
- $attribute = AttributeResolver::resolve(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $attr,
- $this->storage->name ?? null
- );
-
- if ($attribute->fq_class_name === 'Psalm\\Deprecated'
- || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
- ) {
- $storage->deprecated = true;
- }
-
- if ($attribute->fq_class_name === 'Psalm\\Internal' && !$storage->internal) {
- $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)];
- }
-
- if ($attribute->fq_class_name === 'Psalm\\Immutable'
- || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Immutable'
- ) {
- $storage->mutation_free = true;
- $storage->external_mutation_free = true;
- }
-
- if ($attribute->fq_class_name === 'Psalm\\ExternalMutationFree') {
- $storage->external_mutation_free = true;
- }
-
- $storage->attributes[] = $attribute;
- }
- }
-
- return null;
- }
-
- public function finish(PhpParser\Node\Stmt\ClassLike $node): ClassLikeStorage
- {
- if (!$this->storage) {
- throw new UnexpectedValueException(
- 'Storage should exist in ' . $this->file_path . ' at ' . $node->getLine()
- );
- }
-
- $classlike_storage = $this->storage;
-
- $fq_classlike_name = $classlike_storage->name;
-
- if (PropertyMap::inPropertyMap($fq_classlike_name)) {
- $mapped_properties = PropertyMap::getPropertyMap()[strtolower($fq_classlike_name)];
-
- foreach ($mapped_properties as $property_name => $public_mapped_property) {
- $property_type = Type::parseString($public_mapped_property);
-
- $property_type->queueClassLikesForScanning($this->codebase, $this->file_storage);
-
- if (!isset($classlike_storage->properties[$property_name])) {
- $classlike_storage->properties[$property_name] = new PropertyStorage();
- }
-
- $classlike_storage->properties[$property_name]->type = $property_type;
-
- $property_id = $fq_classlike_name . '::$' . $property_name;
-
- $classlike_storage->declaring_property_ids[$property_name] = $fq_classlike_name;
- $classlike_storage->appearing_property_ids[$property_name] = $property_id;
- }
- }
-
- $converted_aliases = array_map(
- function (InlineTypeAlias $t): ?ClassTypeAlias {
- try {
- $union = TypeParser::parseTokens(
- $t->replacement_tokens,
- null,
- [],
- $this->type_aliases
- );
-
- $union->setFromDocblock();
-
- return new ClassTypeAlias(
- array_values($union->getAtomicTypes())
- );
- } catch (Exception $e) {
- return null;
- }
- },
- $this->classlike_type_aliases
- );
-
- foreach ($converted_aliases as $key => $type) {
- if (!$type) {
- $classlike_storage->docblock_issues[] = new InvalidDocblock(
- '@psalm-type ' . $key . ' contains invalid references',
- new CodeLocation($this->file_scanner, $node, null, true)
- );
- }
- }
-
- $classlike_storage->type_aliases = array_filter($converted_aliases);
-
- return $classlike_storage;
- }
-
- public function handleTraitUse(PhpParser\Node\Stmt\TraitUse $node): void
- {
- $storage = $this->storage;
-
- if (!$storage) {
- throw new UnexpectedValueException('bad');
- }
-
- $method_map = $storage->trait_alias_map ?: [];
- $visibility_map = $storage->trait_visibility_map ?: [];
- $final_map = $storage->trait_final_map ?: [];
-
- foreach ($node->adaptations as $adaptation) {
- if ($adaptation instanceof PhpParser\Node\Stmt\TraitUseAdaptation\Alias) {
- $old_name = strtolower($adaptation->method->name);
- $new_name = $old_name;
-
- if ($adaptation->newName) {
- $new_name = strtolower($adaptation->newName->name);
-
- if ($new_name !== $old_name) {
- $method_map[$new_name] = $old_name;
- }
- }
-
- if ($adaptation->newModifier) {
- switch ($adaptation->newModifier) {
- case 1:
- $visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- break;
-
- case 2:
- $visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PROTECTED;
- break;
-
- case 4:
- $visibility_map[$new_name] = ClassLikeAnalyzer::VISIBILITY_PRIVATE;
- break;
-
- case 32:
- $final_map[$new_name] = true;
- break;
- }
- }
- }
- }
-
- $storage->trait_alias_map = $method_map;
- $storage->trait_visibility_map = $visibility_map;
- $storage->trait_final_map = $final_map;
-
- foreach ($node->traits as $trait) {
- $trait_fqcln = ClassLikeAnalyzer::getFQCLNFromNameObject($trait, $this->aliases);
- $this->codebase->scanner->queueClassLikeForScanning($trait_fqcln, $this->file_scanner->will_analyze);
- $storage->used_traits[strtolower($trait_fqcln)] = $trait_fqcln;
- $this->file_storage->required_classes[strtolower($trait_fqcln)] = $trait_fqcln;
- }
-
- if ($node_comment = $node->getDocComment()) {
- $comments = DocComment::parsePreservingLength($node_comment);
-
- if (isset($comments->combined_tags['use'])) {
- foreach ($comments->combined_tags['use'] as $template_line) {
- $this->useTemplatedType(
- $storage,
- $node,
- trim(preg_replace('@^[ \t]*\*@m', '', $template_line))
- );
- }
- }
-
- if (isset($comments->tags['template-extends'])
- || isset($comments->tags['extends'])
- || isset($comments->tags['template-implements'])
- || isset($comments->tags['implements'])
- ) {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'You must use @use or @template-use to parameterize traits',
- new CodeLocation($this->file_scanner, $node, null, true)
- );
- }
- }
- }
-
- private function extendTemplatedType(
- ClassLikeStorage $storage,
- PhpParser\Node\Stmt\ClassLike $node,
- string $extended_class_name
- ): void {
- if (trim($extended_class_name) === '') {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Extended class cannot be empty in docblock for ' . $storage->name,
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- try {
- $extended_union_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $extended_class_name,
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases
- ),
- null,
- $this->class_template_types,
- $this->type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $storage->name,
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- if (!$extended_union_type->isSingle()) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-extends cannot be a union type',
- new CodeLocation($this->file_scanner, $node, null, true)
- );
- }
-
- $extended_union_type->setFromDocblock();
-
- $extended_union_type->queueClassLikesForScanning(
- $this->codebase,
- $this->file_storage,
- $storage->template_types ?: []
- );
-
- foreach ($extended_union_type->getAtomicTypes() as $atomic_type) {
- if (!$atomic_type instanceof TGenericObject) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-extends has invalid class ' . $atomic_type->getId(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $generic_class_lc = strtolower($atomic_type->value);
-
- if (!isset($storage->parent_classes[$generic_class_lc])
- && !isset($storage->parent_interfaces[$generic_class_lc])
- ) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-extends must include the name of an extended class,'
- . ' got ' . $atomic_type->getId(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
- }
-
- $extended_type_parameters = [];
-
- $storage->template_extended_count = count($atomic_type->type_params);
-
- foreach ($atomic_type->type_params as $type_param) {
- $extended_type_parameters[] = $type_param;
- }
-
- $storage->template_extended_offsets[$atomic_type->value] = $extended_type_parameters;
- }
- }
-
- private function implementTemplatedType(
- ClassLikeStorage $storage,
- PhpParser\Node\Stmt\ClassLike $node,
- string $implemented_class_name
- ): void {
- if (trim($implemented_class_name) === '') {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Extended class cannot be empty in docblock for ' . $storage->name,
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- try {
- $implemented_union_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $implemented_class_name,
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases
- ),
- null,
- $this->class_template_types,
- $this->type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $storage->name,
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- if (!$implemented_union_type->isSingle()) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-implements cannot be a union type',
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $implemented_union_type->setFromDocblock();
-
- $implemented_union_type->queueClassLikesForScanning(
- $this->codebase,
- $this->file_storage,
- $storage->template_types ?: []
- );
-
- foreach ($implemented_union_type->getAtomicTypes() as $atomic_type) {
- if (!$atomic_type instanceof TGenericObject) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-implements has invalid class ' . $atomic_type->getId(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $generic_class_lc = strtolower($atomic_type->value);
-
- if (!isset($storage->class_implements[$generic_class_lc])) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-implements must include the name of an implemented class,'
- . ' got ' . $atomic_type->getId(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $implemented_type_parameters = [];
-
- $storage->template_type_implements_count[$generic_class_lc] = count($atomic_type->type_params);
-
- foreach ($atomic_type->type_params as $type_param) {
- $implemented_type_parameters[] = $type_param;
- }
-
- $storage->template_extended_offsets[$atomic_type->value] = $implemented_type_parameters;
- }
- }
-
- private function useTemplatedType(
- ClassLikeStorage $storage,
- PhpParser\Node\Stmt\TraitUse $node,
- string $used_class_name
- ): void {
- if (trim($used_class_name) === '') {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Extended class cannot be empty in docblock for ' . $storage->name,
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- try {
- $used_union_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $used_class_name,
- $this->aliases,
- $this->class_template_types,
- $this->type_aliases
- ),
- null,
- $this->class_template_types,
- $this->type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $storage->name,
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- if (!$used_union_type->isSingle()) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-use cannot be a union type',
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $used_union_type->setFromDocblock();
-
- $used_union_type->queueClassLikesForScanning(
- $this->codebase,
- $this->file_storage,
- $storage->template_types ?: []
- );
-
- foreach ($used_union_type->getAtomicTypes() as $atomic_type) {
- if (!$atomic_type instanceof TGenericObject) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-use has invalid class ' . $atomic_type->getId(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $generic_class_lc = strtolower($atomic_type->value);
-
- if (!isset($storage->used_traits[$generic_class_lc])) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@template-use must include the name of an used class,'
- . ' got ' . $atomic_type->getId(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
-
- return;
- }
-
- $used_type_parameters = [];
-
- $storage->template_type_uses_count[$generic_class_lc] = count($atomic_type->type_params);
-
- foreach ($atomic_type->type_params as $type_param) {
- $used_type_parameters[] = $type_param;
- }
-
- $storage->template_extended_offsets[$atomic_type->value] = $used_type_parameters;
- }
- }
-
- private static function registerEmptyConstructor(ClassLikeStorage $class_storage): void
- {
- $method_name_lc = '__construct';
-
- if (isset($class_storage->methods[$method_name_lc])) {
- return;
- }
-
- $storage = $class_storage->methods['__construct'] = new MethodStorage();
-
- $storage->cased_name = '__construct';
- $storage->defining_fqcln = $class_storage->name;
-
- $storage->mutation_free = $storage->external_mutation_free = true;
- $storage->mutation_free_inferred = true;
-
- $class_storage->declaring_method_ids['__construct'] = new MethodIdentifier(
- $class_storage->name,
- '__construct'
- );
-
- $class_storage->inheritable_method_ids['__construct']
- = $class_storage->declaring_method_ids['__construct'];
- $class_storage->appearing_method_ids['__construct']
- = $class_storage->declaring_method_ids['__construct'];
- $class_storage->overridden_method_ids['__construct'] = [];
-
- $storage->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- }
-
- private function visitClassConstDeclaration(
- PhpParser\Node\Stmt\ClassConst $stmt,
- ClassLikeStorage $storage,
- string $fq_classlike_name
- ): void {
- $existing_constants = $storage->constants;
-
- $comment = $stmt->getDocComment();
- $deprecated = false;
- $description = null;
- $config = $this->config;
-
- if ($comment && $comment->getText() && ($config->use_docblock_types || $config->use_docblock_property_types)) {
- $comments = DocComment::parsePreservingLength($comment);
-
- if (isset($comments->tags['deprecated'])) {
- $deprecated = true;
- }
-
- $description = $comments->description;
- }
-
- foreach ($stmt->consts as $const) {
- $const_type = SimpleTypeInferer::infer(
- $this->codebase,
- new NodeDataProvider(),
- $const->value,
- $this->aliases,
- null,
- $existing_constants,
- $fq_classlike_name
- );
-
- if (isset($storage->constants[$const->name->name])
- || isset($storage->enum_cases[$const->name->name])
- ) {
- if (IssueBuffer::accepts(new DuplicateConstant(
- 'Constant names should be unique',
- new CodeLocation($this->file_scanner, $const),
- $fq_classlike_name
- ))) {
- // fall through
- }
- continue;
- }
-
- $storage->constants[$const->name->name] = $constant_storage = new ClassConstantStorage(
- $const_type,
- $stmt->isProtected()
- ? ClassLikeAnalyzer::VISIBILITY_PROTECTED
- : ($stmt->isPrivate()
- ? ClassLikeAnalyzer::VISIBILITY_PRIVATE
- : ClassLikeAnalyzer::VISIBILITY_PUBLIC),
- new CodeLocation(
- $this->file_scanner,
- $const->name
- )
- );
-
- $constant_storage->stmt_location = new CodeLocation(
- $this->file_scanner,
- $const
- );
-
- if ($const_type
- && $const->value instanceof Concat
- && $const_type->isSingle()
- && get_class($const_type->getSingleAtomic()) === TString::class
- ) {
- // Prefer unresolved type over inferred string from concat, so that it can later be resolved to literal.
- $const_type = null;
- }
-
- if ($const_type) {
- $existing_constants[$const->name->name] = new ClassConstantStorage(
- $const_type,
- $stmt->isProtected()
- ? ClassLikeAnalyzer::VISIBILITY_PROTECTED
- : ($stmt->isPrivate()
- ? ClassLikeAnalyzer::VISIBILITY_PRIVATE
- : ClassLikeAnalyzer::VISIBILITY_PUBLIC),
- null
- );
- } else {
- $unresolved_const_expr = ExpressionResolver::getUnresolvedClassConstExpr(
- $const->value,
- $this->aliases,
- $fq_classlike_name,
- $storage->parent_class
- );
-
- if ($unresolved_const_expr) {
- $constant_storage->unresolved_node = $unresolved_const_expr;
- } else {
- $constant_storage->type = Type::getMixed();
- }
- }
-
- if ($deprecated) {
- $constant_storage->deprecated = true;
- }
-
- $constant_storage->description = $description;
-
- foreach ($stmt->attrGroups as $attr_group) {
- foreach ($attr_group->attrs as $attr) {
- $constant_storage->attributes[] = AttributeResolver::resolve(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $attr,
- $this->storage->name ?? null
- );
- }
- }
- }
- }
-
- private function visitEnumDeclaration(
- PhpParser\Node\Stmt\EnumCase $stmt,
- ClassLikeStorage $storage,
- string $fq_classlike_name
- ): void {
- if (isset($storage->constants[$stmt->name->name])) {
- if (IssueBuffer::accepts(new DuplicateConstant(
- 'Constant names should be unique',
- new CodeLocation($this->file_scanner, $stmt),
- $fq_classlike_name
- ))) {
- // fall through
- }
- return;
- }
-
- $enum_value = null;
-
- if ($stmt->expr !== null) {
- $case_type = SimpleTypeInferer::infer(
- $this->codebase,
- new NodeDataProvider(),
- $stmt->expr,
- $this->aliases,
- $this->file_scanner,
- null, // enum case value expressions cannot reference constants
- $fq_classlike_name
- );
-
- if ($case_type) {
- if ($case_type->isSingleIntLiteral()) {
- $enum_value = $case_type->getSingleIntLiteral()->value;
- } elseif ($case_type->isSingleStringLiteral()) {
- $enum_value = $case_type->getSingleStringLiteral()->value;
- } else {
- throw new RuntimeException(
- 'Unexpected: case value for ' . $stmt->name->name . ' is ' . $case_type->getId()
- );
- }
- } else {
- throw new RuntimeException('Failed to infer case value for ' . $stmt->name->name);
- }
- }
-
- $case_location = new CodeLocation($this->file_scanner, $stmt);
-
- if (!isset($storage->enum_cases[$stmt->name->name])) {
- $case = new EnumCaseStorage(
- $enum_value,
- $case_location
- );
-
- $attrs = $this->getAttributeStorageFromStatement(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $stmt,
- $this->storage->name ?? null
- );
-
- foreach ($attrs as $attribute) {
- if ($attribute->fq_class_name === 'Psalm\\Deprecated'
- || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
- ) {
- $case->deprecated = true;
- break;
- }
- }
-
- $comment = $stmt->getDocComment();
- if ($comment) {
- $comments = DocComment::parsePreservingLength($comment);
-
- if (isset($comments->tags['deprecated'])) {
- $case->deprecated = true;
- }
- }
- $storage->enum_cases[$stmt->name->name] = $case;
- } else {
- if (IssueBuffer::accepts(
- new DuplicateEnumCase(
- 'Enum case names should be unique',
- $case_location,
- $fq_classlike_name
- )
- )) {
- }
- }
- }
-
- /**
- * @param PhpParser\Node\Stmt\Property|PhpParser\Node\Stmt\EnumCase $stmt
- * @return list<AttributeStorage>
- */
- private function getAttributeStorageFromStatement(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- PhpParser\Node\Stmt $stmt,
- ?string $fq_classlike_name
- ): array {
- $storages = [];
- foreach ($stmt->attrGroups as $attr_group) {
- foreach ($attr_group->attrs as $attr) {
- $storages[] = AttributeResolver::resolve(
- $codebase,
- $file_scanner,
- $file_storage,
- $aliases,
- $attr,
- $fq_classlike_name
- );
- }
- }
- return $storages;
- }
-
- /**
- * @param non-empty-string $fq_classlike_name
- */
- private function visitPropertyDeclaration(
- PhpParser\Node\Stmt\Property $stmt,
- Config $config,
- ClassLikeStorage $storage,
- string $fq_classlike_name
- ): void {
- $comment = $stmt->getDocComment();
- $var_comment = null;
-
- $property_is_initialized = false;
-
- $existing_constants = $storage->constants;
-
- if ($comment && $comment->getText() && ($config->use_docblock_types || $config->use_docblock_property_types)) {
- if (preg_match('/[ \t\*]+@psalm-suppress[ \t]+PropertyNotSetInConstructor/', (string)$comment)) {
- $property_is_initialized = true;
- }
-
- if (preg_match('/[ \t\*]+@property[ \t]+/', (string)$comment)) {
- $storage->docblock_issues[] = new InvalidDocblock(
- '@property is valid only in docblocks for class',
- new CodeLocation($this->file_scanner, $stmt, null, true)
- );
- }
-
- try {
- $var_comments = CommentAnalyzer::getTypeFromComment(
- $comment,
- $this->file_scanner,
- $this->aliases,
- !$stmt->isStatic() ? $this->class_template_types : [],
- $this->type_aliases
- );
-
- $var_comment = array_pop($var_comments);
- } catch (IncorrectDocblockException $e) {
- $storage->docblock_issues[] = new MissingDocblockType(
- $e->getMessage(),
- new CodeLocation($this->file_scanner, $stmt, null, true)
- );
- } catch (DocblockParseException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($this->file_scanner, $stmt, null, true)
- );
- }
- }
-
- $signature_type = null;
- $signature_type_location = null;
-
- if ($stmt->type) {
- $parser_property_type = $stmt->type;
- if ($parser_property_type instanceof PhpParser\Node\IntersectionType) {
- throw new UnexpectedValueException('Intersection types not yet supported');
- }
- /** @var Identifier|Name|NullableType|UnionType $parser_property_type */
-
- $signature_type = TypeHintResolver::resolve(
- $parser_property_type,
- $this->codebase->scanner,
- $this->file_storage,
- $this->storage,
- $this->aliases,
- $this->codebase->php_major_version,
- $this->codebase->php_minor_version
- );
-
- $signature_type_location = new CodeLocation(
- $this->file_scanner,
- $parser_property_type,
- null,
- false,
- CodeLocation::FUNCTION_RETURN_TYPE
- );
- }
-
- $doc_var_group_type = $var_comment->type ?? null;
-
- if ($doc_var_group_type) {
- $doc_var_group_type->queueClassLikesForScanning($this->codebase, $this->file_storage);
- $doc_var_group_type->setFromDocblock();
- }
-
- foreach ($stmt->props as $property) {
- $doc_var_location = null;
-
- $property_storage = $storage->properties[$property->name->name] = new PropertyStorage();
- $property_storage->is_static = $stmt->isStatic();
- $property_storage->type = $signature_type;
- $property_storage->signature_type = $signature_type;
- $property_storage->signature_type_location = $signature_type_location;
- $property_storage->type_location = $signature_type_location;
- $property_storage->location = new CodeLocation($this->file_scanner, $property->name);
- $property_storage->stmt_location = new CodeLocation($this->file_scanner, $stmt);
- $property_storage->has_default = (bool)$property->default;
- $property_storage->deprecated = $var_comment ? $var_comment->deprecated : false;
- $property_storage->suppressed_issues = $var_comment ? $var_comment->suppressed_issues : [];
- $property_storage->internal = $var_comment ? $var_comment->psalm_internal : [];
- if (count($property_storage->internal) === 0 && $var_comment && $var_comment->internal) {
- $property_storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)];
- }
- $property_storage->readonly = $stmt->isReadonly() || ($var_comment && $var_comment->readonly);
- $property_storage->allow_private_mutation = $var_comment ? $var_comment->allow_private_mutation : false;
- $property_storage->description = $var_comment ? $var_comment->description : null;
-
- if (!$signature_type && !$doc_var_group_type) {
- if ($property->default) {
- $property_storage->suggested_type = SimpleTypeInferer::infer(
- $this->codebase,
- new NodeDataProvider(),
- $property->default,
- $this->aliases,
- null,
- $existing_constants,
- $fq_classlike_name
- );
- }
-
- $property_storage->type = null;
- } else {
- if ($var_comment
- && $var_comment->type_start
- && $var_comment->type_end
- && $var_comment->line_number
- ) {
- $doc_var_location = new DocblockTypeLocation(
- $this->file_scanner,
- $var_comment->type_start,
- $var_comment->type_end,
- $var_comment->line_number
- );
- }
-
- if ($doc_var_group_type) {
- $property_storage->type = count($stmt->props) === 1
- ? $doc_var_group_type
- : clone $doc_var_group_type;
- }
- }
-
- if ($property_storage->type
- && $property_storage->type !== $property_storage->signature_type
- ) {
- if (!$property_storage->signature_type) {
- $property_storage->type_location = $doc_var_location;
- }
-
- if ($property_storage->signature_type) {
- $all_typehint_types_match = true;
- $signature_atomic_types = $property_storage->signature_type->getAtomicTypes();
-
- foreach ($property_storage->type->getAtomicTypes() as $key => $type) {
- if (isset($signature_atomic_types[$key])) {
- $type->from_docblock = false;
- } else {
- $all_typehint_types_match = false;
- }
- }
-
- if ($all_typehint_types_match) {
- $property_storage->type->from_docblock = false;
- }
-
- if ($property_storage->signature_type->isNullable()
- && !$property_storage->type->isNullable()
- ) {
- $property_storage->type->addType(new TNull());
- }
- }
-
- $property_storage->type->queueClassLikesForScanning($this->codebase, $this->file_storage);
- }
-
- if ($stmt->isPublic()) {
- $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- } elseif ($stmt->isProtected()) {
- $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PROTECTED;
- } elseif ($stmt->isPrivate()) {
- $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE;
- }
-
- $property_id = $fq_classlike_name . '::$' . $property->name->name;
-
- $storage->declaring_property_ids[$property->name->name] = $fq_classlike_name;
- $storage->appearing_property_ids[$property->name->name] = $property_id;
-
- if ($property_is_initialized) {
- $storage->initialized_properties[$property->name->name] = true;
- }
-
- if (!$stmt->isPrivate()) {
- $storage->inheritable_property_ids[$property->name->name] = $property_id;
- }
-
- $attrs = $this->getAttributeStorageFromStatement(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $stmt,
- $this->storage->name ?? null
- );
-
- foreach ($attrs as $attribute) {
- if ($attribute->fq_class_name === 'Psalm\\Deprecated'
- || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
- ) {
- $property_storage->deprecated = true;
- }
-
- if ($attribute->fq_class_name === 'Psalm\\Internal' && !$property_storage->internal) {
- $property_storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)];
- }
-
- if ($attribute->fq_class_name === 'Psalm\\Readonly') {
- $property_storage->readonly = true;
- }
-
- $property_storage->attributes[] = $attribute;
- }
- }
- }
-
- /**
- * @param ClassLikeDocblockComment $comment
- * @param string $fq_classlike_name
- *
- * @return array<string, LinkableTypeAlias>
- */
- private function getImportedTypeAliases(ClassLikeDocblockComment $comment, string $fq_classlike_name): array
- {
- /** @var array<string, LinkableTypeAlias> $results */
- $results = [];
-
- foreach ($comment->imported_types as $import_type_entry) {
- $imported_type_data = $import_type_entry['parts'];
- $location = new DocblockTypeLocation(
- $this->file_scanner,
- $import_type_entry['start_offset'],
- $import_type_entry['end_offset'],
- $import_type_entry['line_number']
- );
- // There are two valid forms:
- // @psalm-import Thing from Something
- // @psalm-import Thing from Something as Alias
- // but there could be leftovers after that
- if (count($imported_type_data) < 3) {
- $this->file_storage->docblock_issues[] = new InvalidTypeImport(
- 'Invalid import in docblock for ' . $fq_classlike_name
- . ', expecting "<TypeName> from <ClassName>",'
- . ' got "' . implode(' ', $imported_type_data) . '" instead.',
- $location
- );
- continue;
- }
-
- if ($imported_type_data[1] === 'from'
- && !empty($imported_type_data[0])
- && !empty($imported_type_data[2])
- ) {
- $type_alias_name = $as_alias_name = $imported_type_data[0];
- $declaring_classlike_name = $imported_type_data[2];
- } else {
- $this->file_storage->docblock_issues[] = new InvalidTypeImport(
- 'Invalid import in docblock for ' . $fq_classlike_name
- . ', expecting "<TypeName> from <ClassName>", got "'
- . implode(
- ' ',
- [$imported_type_data[0], $imported_type_data[1], $imported_type_data[2]]
- ) . '" instead.',
- $location
- );
- continue;
- }
-
- if (count($imported_type_data) >= 4 && $imported_type_data[3] === 'as') {
- // long form
- if (empty($imported_type_data[4])) {
- $this->file_storage->docblock_issues[] = new InvalidTypeImport(
- 'Invalid import in docblock for ' . $fq_classlike_name
- . ', expecting "as <TypeName>", got "'
- . $imported_type_data[3] . ' ' . ($imported_type_data[4] ?? '') . '" instead.',
- $location
- );
- continue;
- }
-
- $as_alias_name = $imported_type_data[4];
- }
-
- $declaring_fq_classlike_name = Type::getFQCLNFromString(
- $declaring_classlike_name,
- $this->aliases
- );
-
- $this->codebase->scanner->queueClassLikeForScanning($declaring_fq_classlike_name);
- $this->file_storage->referenced_classlikes[strtolower($declaring_fq_classlike_name)]
- = $declaring_fq_classlike_name;
-
- $results[$as_alias_name] = new LinkableTypeAlias(
- $declaring_fq_classlike_name,
- $type_alias_name,
- $import_type_entry['line_number'],
- $import_type_entry['start_offset'],
- $import_type_entry['end_offset']
- );
- }
-
- return $results;
- }
-
- /**
- * @param array<string, TypeAlias> $type_aliases
- *
- * @return array<string, InlineTypeAlias>
- *
- * @throws DocblockParseException if there was a problem parsing the docblock
- */
- public static function getTypeAliasesFromComment(
- PhpParser\Comment\Doc $comment,
- Aliases $aliases,
- ?array $type_aliases,
- ?string $self_fqcln
- ): array {
- $parsed_docblock = DocComment::parsePreservingLength($comment);
-
- if (!isset($parsed_docblock->tags['psalm-type']) && !isset($parsed_docblock->tags['phpstan-type'])) {
- return [];
- }
-
- $type_alias_comment_lines = array_merge(
- $parsed_docblock->tags['phpstan-type'] ?? [],
- $parsed_docblock->tags['psalm-type'] ?? []
- );
-
- return self::getTypeAliasesFromCommentLines(
- $type_alias_comment_lines,
- $aliases,
- $type_aliases,
- $self_fqcln
- );
- }
-
- /**
- * @param array<string> $type_alias_comment_lines
- * @param array<string, TypeAlias> $type_aliases
- *
- * @return array<string, InlineTypeAlias>
- *
- * @throws DocblockParseException if there was a problem parsing the docblock
- */
- private static function getTypeAliasesFromCommentLines(
- array $type_alias_comment_lines,
- Aliases $aliases,
- ?array $type_aliases,
- ?string $self_fqcln
- ): array {
- $type_alias_tokens = [];
-
- foreach ($type_alias_comment_lines as $var_line) {
- $var_line = trim($var_line);
-
- if (!$var_line) {
- continue;
- }
-
- $var_line = preg_replace('/[ \t]+/', ' ', preg_replace('@^[ \t]*\*@m', '', $var_line));
- $var_line = preg_replace('/,\n\s+\}/', '}', $var_line);
- $var_line = str_replace("\n", '', $var_line);
-
- $var_line_parts = preg_split('/( |=)/', $var_line, -1, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
-
- if (!$var_line_parts) {
- continue;
- }
-
- $type_alias = array_shift($var_line_parts);
-
- if (!isset($var_line_parts[0])) {
- continue;
- }
-
- if ($var_line_parts[0] === ' ') {
- array_shift($var_line_parts);
- }
-
- if ($var_line_parts[0] === '=') {
- array_shift($var_line_parts);
- }
-
- if (!isset($var_line_parts[0])) {
- continue;
- }
-
- if ($var_line_parts[0] === ' ') {
- array_shift($var_line_parts);
- }
-
- $type_string = str_replace("\n", '', implode('', $var_line_parts));
-
- $type_string = preg_replace('/>[^>^\}]*$/', '>', $type_string);
- $type_string = preg_replace('/\}[^>^\}]*$/', '}', $type_string);
-
- try {
- $type_tokens = TypeTokenizer::getFullyQualifiedTokens(
- $type_string,
- $aliases,
- null,
- $type_alias_tokens + $type_aliases,
- $self_fqcln
- );
- } catch (TypeParseTreeException $e) {
- throw new DocblockParseException($type_string . ' is not a valid type');
- }
-
- $type_alias_tokens[$type_alias] = new InlineTypeAlias($type_tokens);
- }
-
- return $type_alias_tokens;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php
deleted file mode 100644
index 3cf9d02..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php
+++ /dev/null
@@ -1,450 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use PhpParser;
-use PhpParser\ConstExprEvaluationException;
-use PhpParser\ConstExprEvaluator;
-use PhpParser\Node\Expr;
-use PhpParser\Node\Expr\ConstFetch;
-use Psalm\Aliases;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Scanner\UnresolvedConstant\ArrayOffsetFetch;
-use Psalm\Internal\Scanner\UnresolvedConstant\ArraySpread;
-use Psalm\Internal\Scanner\UnresolvedConstant\ArrayValue;
-use Psalm\Internal\Scanner\UnresolvedConstant\ClassConstant;
-use Psalm\Internal\Scanner\UnresolvedConstant\Constant;
-use Psalm\Internal\Scanner\UnresolvedConstant\KeyValuePair;
-use Psalm\Internal\Scanner\UnresolvedConstant\ScalarValue;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedAdditionOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseAnd;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseOr;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBitwiseXor;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedConcatOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedDivisionOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedMultiplicationOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedSubtractionOp;
-use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedTernary;
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-use ReflectionClass;
-use ReflectionFunction;
-
-use function assert;
-use function class_exists;
-use function function_exists;
-use function implode;
-use function interface_exists;
-use function strtolower;
-
-class ExpressionResolver
-{
- public static function getUnresolvedClassConstExpr(
- PhpParser\Node\Expr $stmt,
- Aliases $aliases,
- ?string $fq_classlike_name,
- ?string $parent_fq_class_name = null
- ): ?UnresolvedConstantComponent {
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp) {
- $left = self::getUnresolvedClassConstExpr(
- $stmt->left,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- $right = self::getUnresolvedClassConstExpr(
- $stmt->right,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if (!$left || !$right) {
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Plus) {
- return new UnresolvedAdditionOp($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Minus) {
- return new UnresolvedSubtractionOp($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Mul) {
- return new UnresolvedMultiplicationOp($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Div) {
- return new UnresolvedDivisionOp($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\Concat) {
- return new UnresolvedConcatOp($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseOr) {
- return new UnresolvedBitwiseOr($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseXor) {
- return new UnresolvedBitwiseXor($left, $right);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\BinaryOp\BitwiseAnd) {
- return new UnresolvedBitwiseAnd($left, $right);
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Ternary) {
- $cond = self::getUnresolvedClassConstExpr(
- $stmt->cond,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- $if = null;
-
- if ($stmt->if) {
- $if = self::getUnresolvedClassConstExpr(
- $stmt->if,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if ($if === null) {
- $if = false;
- }
- }
-
- $else = self::getUnresolvedClassConstExpr(
- $stmt->else,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if ($cond && $else && $if !== false) {
- return new UnresolvedTernary($cond, $if, $else);
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ConstFetch) {
- $part0_lc = strtolower($stmt->name->parts[0]);
- if ($part0_lc === 'false') {
- return new ScalarValue(false);
- }
-
- if ($part0_lc === 'true') {
- return new ScalarValue(true);
- }
-
- if ($part0_lc === 'null') {
- return new ScalarValue(null);
- }
-
- if ($part0_lc === '__namespace__') {
- return new ScalarValue($aliases->namespace);
- }
-
- return new Constant(
- implode('\\', $stmt->name->parts),
- $stmt->name instanceof PhpParser\Node\Name\FullyQualified
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\MagicConst\Namespace_) {
- return new ScalarValue($aliases->namespace);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ArrayDimFetch && $stmt->dim) {
- $left = self::getUnresolvedClassConstExpr(
- $stmt->var,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- $right = self::getUnresolvedClassConstExpr(
- $stmt->dim,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if ($left && $right) {
- return new ArrayOffsetFetch($left, $right);
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\ClassConstFetch) {
- if ($stmt->class instanceof PhpParser\Node\Name
- && $stmt->name instanceof PhpParser\Node\Identifier
- && $fq_classlike_name
- && $stmt->class->parts !== ['static']
- && ($stmt->class->parts !== ['parent'] || $parent_fq_class_name !== null)
- ) {
- if ($stmt->class->parts === ['self']) {
- $const_fq_class_name = $fq_classlike_name;
- } else {
- if ($stmt->class->parts === ['parent']) {
- assert($parent_fq_class_name !== null);
- $const_fq_class_name = $parent_fq_class_name;
- } else {
- $const_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject(
- $stmt->class,
- $aliases
- );
- }
- }
-
- return new ClassConstant($const_fq_class_name, $stmt->name->name);
- }
-
- return null;
- }
-
- if ($stmt instanceof PhpParser\Node\Scalar\String_
- || $stmt instanceof PhpParser\Node\Scalar\LNumber
- || $stmt instanceof PhpParser\Node\Scalar\DNumber
- ) {
- return new ScalarValue($stmt->value);
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\UnaryPlus) {
- $right = self::getUnresolvedClassConstExpr(
- $stmt->expr,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if (!$right) {
- return null;
- }
-
- return new UnresolvedAdditionOp(
- new ScalarValue(0),
- $right
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\UnaryMinus) {
- $right = self::getUnresolvedClassConstExpr(
- $stmt->expr,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if (!$right) {
- return null;
- }
-
- return new UnresolvedSubtractionOp(
- new ScalarValue(0),
- $right
- );
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Array_) {
- $items = [];
-
- foreach ($stmt->items as $item) {
- if ($item === null) {
- return null;
- }
-
- if ($item->key) {
- $item_key_type = self::getUnresolvedClassConstExpr(
- $item->key,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if (!$item_key_type) {
- return null;
- }
- } else {
- $item_key_type = null;
- }
-
- $item_value_type = self::getUnresolvedClassConstExpr(
- $item->value,
- $aliases,
- $fq_classlike_name,
- $parent_fq_class_name
- );
-
- if (!$item_value_type) {
- return null;
- }
-
- if ($item->unpack) {
- $items[] = new ArraySpread($item_value_type);
- } else {
- $items[] = new KeyValuePair($item_key_type, $item_value_type);
- }
- }
-
- return new ArrayValue($items);
- }
-
- return null;
- }
-
- public static function enterConditional(
- Codebase $codebase,
- string $file_path,
- PhpParser\Node\Expr $expr
- ): ?bool {
- if ($expr instanceof PhpParser\Node\Expr\BooleanNot) {
- $enter_negated = self::enterConditional($codebase, $file_path, $expr->expr);
-
- return $enter_negated === null ? null : !$enter_negated;
- }
-
- if ($expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanAnd) {
- $enter_conditional_left = self::enterConditional($codebase, $file_path, $expr->left);
- $enter_conditional_right = self::enterConditional($codebase, $file_path, $expr->right);
-
- return $enter_conditional_left !== false && $enter_conditional_right !== false;
- }
-
- if ($expr instanceof PhpParser\Node\Expr\BinaryOp\BooleanOr) {
- $enter_conditional_left = self::enterConditional($codebase, $file_path, $expr->left);
- $enter_conditional_right = self::enterConditional($codebase, $file_path, $expr->right);
-
- return $enter_conditional_left !== false || $enter_conditional_right !== false;
- }
-
- if ($codebase->register_autoload_files) {
- if ((
- $expr instanceof PhpParser\Node\Expr\BinaryOp\GreaterOrEqual
- || $expr instanceof PhpParser\Node\Expr\BinaryOp\Greater
- || $expr instanceof PhpParser\Node\Expr\BinaryOp\SmallerOrEqual
- || $expr instanceof PhpParser\Node\Expr\BinaryOp\Smaller
- ) && (
- (
- $expr->left instanceof PhpParser\Node\Expr\ConstFetch
- && $expr->left->name->parts === ['PHP_VERSION_ID']
- && $expr->right instanceof PhpParser\Node\Scalar\LNumber
- ) || (
- $expr->right instanceof PhpParser\Node\Expr\ConstFetch
- && $expr->right->name->parts === ['PHP_VERSION_ID']
- && $expr->left instanceof PhpParser\Node\Scalar\LNumber
- )
- )
- ) {
- $php_version_id = $codebase->php_major_version * 10000 + $codebase->php_minor_version * 100;
- $evaluator = new ConstExprEvaluator(function (Expr $expr) use ($php_version_id) {
- if ($expr instanceof ConstFetch && $expr->name->parts === ['PHP_VERSION_ID']) {
- return $php_version_id;
- }
- throw new ConstExprEvaluationException('unexpected');
- });
- try {
- return (bool) $evaluator->evaluateSilently($expr);
- } catch (ConstExprEvaluationException $e) {
- return null;
- }
- }
- }
-
- if (!$expr instanceof PhpParser\Node\Expr\FuncCall) {
- return null;
- }
-
- return self::functionEvaluatesToTrue($codebase, $file_path, $expr);
- }
-
- private static function functionEvaluatesToTrue(
- Codebase $codebase,
- string $file_path,
- PhpParser\Node\Expr\FuncCall $function
- ): ?bool {
- if (!$function->name instanceof PhpParser\Node\Name) {
- return null;
- }
-
- if ($function->name->parts === ['function_exists']
- && isset($function->getArgs()[0])
- && $function->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_
- && function_exists($function->getArgs()[0]->value->value)
- ) {
- $reflection_function = new ReflectionFunction($function->getArgs()[0]->value->value);
-
- if ($reflection_function->isInternal()) {
- return true;
- }
-
- return false;
- }
-
- if ($function->name->parts === ['class_exists']
- && isset($function->getArgs()[0])
- ) {
- $string_value = null;
-
- if ($function->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_) {
- $string_value = $function->getArgs()[0]->value->value;
- } elseif ($function->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $function->getArgs()[0]->value->class instanceof PhpParser\Node\Name
- && $function->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
- && strtolower($function->getArgs()[0]->value->name->name) === 'class'
- ) {
- $string_value = (string) $function->getArgs()[0]->value->class->getAttribute('resolvedName');
- }
-
- if ($string_value && class_exists($string_value)) {
- $reflection_class = new ReflectionClass($string_value);
-
- if ($reflection_class->getFileName() !== $file_path) {
- $codebase->scanner->queueClassLikeForScanning(
- $string_value
- );
-
- return true;
- }
- }
-
- return false;
- }
-
- if ($function->name->parts === ['interface_exists']
- && isset($function->getArgs()[0])
- ) {
- $string_value = null;
-
- if ($function->getArgs()[0]->value instanceof PhpParser\Node\Scalar\String_) {
- $string_value = $function->getArgs()[0]->value->value;
- } elseif ($function->getArgs()[0]->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $function->getArgs()[0]->value->class instanceof PhpParser\Node\Name
- && $function->getArgs()[0]->value->name instanceof PhpParser\Node\Identifier
- && strtolower($function->getArgs()[0]->value->name->name) === 'class'
- ) {
- $string_value = (string) $function->getArgs()[0]->value->class->getAttribute('resolvedName');
- }
-
- if ($string_value && interface_exists($string_value)) {
- $reflection_class = new ReflectionClass($string_value);
-
- if ($reflection_class->getFileName() !== $file_path) {
- $codebase->scanner->queueClassLikeForScanning(
- $string_value
- );
-
- return true;
- }
- }
-
- return false;
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php
deleted file mode 100644
index 2a6dbf7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionScanner.php
+++ /dev/null
@@ -1,332 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Exception\FileIncludeException;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ConstFetchAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\IncludeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\Storage\FileStorage;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Type;
-
-use function assert;
-use function defined;
-use function dirname;
-use function explode;
-use function implode;
-use function in_array;
-use function preg_match;
-use function strpos;
-use function strtolower;
-use function substr;
-
-use const DIRECTORY_SEPARATOR;
-
-class ExpressionScanner
-{
- public static function scan(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- PhpParser\Node\Expr $node,
- ?FunctionLikeStorage $functionlike_storage,
- ?int $skip_if_descendants
- ): void {
- if ($node instanceof PhpParser\Node\Expr\Include_ && !$skip_if_descendants) {
- self::visitInclude(
- $codebase,
- $file_storage,
- $node,
- $file_scanner->will_analyze
- );
- } elseif ($node instanceof PhpParser\Node\Expr\Yield_ || $node instanceof PhpParser\Node\Expr\YieldFrom) {
- if ($functionlike_storage) {
- $functionlike_storage->has_yield = true;
- }
- } elseif ($node instanceof PhpParser\Node\Expr\Cast\Object_) {
- $codebase->scanner->queueClassLikeForScanning('stdClass', false, false);
- $file_storage->referenced_classlikes['stdclass'] = 'stdClass';
- } elseif (($node instanceof PhpParser\Node\Expr\New_
- || $node instanceof PhpParser\Node\Expr\Instanceof_
- || $node instanceof PhpParser\Node\Expr\StaticPropertyFetch
- || $node instanceof PhpParser\Node\Expr\ClassConstFetch
- || $node instanceof PhpParser\Node\Expr\StaticCall)
- && $node->class instanceof PhpParser\Node\Name
- ) {
- $fq_classlike_name = ClassLikeAnalyzer::getFQCLNFromNameObject($node->class, $aliases);
-
- if (!in_array(strtolower($fq_classlike_name), ['self', 'static', 'parent'], true)) {
- $codebase->scanner->queueClassLikeForScanning(
- $fq_classlike_name,
- false,
- !($node instanceof PhpParser\Node\Expr\ClassConstFetch)
- || !($node->name instanceof PhpParser\Node\Identifier)
- || strtolower($node->name->name) !== 'class'
- );
- $file_storage->referenced_classlikes[strtolower($fq_classlike_name)] = $fq_classlike_name;
- }
- } elseif ($node instanceof PhpParser\Node\Expr\FuncCall && $node->name instanceof PhpParser\Node\Name) {
- $function_id = implode('\\', $node->name->parts);
-
- if (InternalCallMapHandler::inCallMap($function_id)) {
- self::registerClassMapFunctionCall(
- $codebase,
- $file_storage,
- $file_scanner,
- $aliases,
- $function_id,
- $node,
- $functionlike_storage,
- $skip_if_descendants
- );
- }
- }
- }
-
- private static function registerClassMapFunctionCall(
- Codebase $codebase,
- FileStorage $file_storage,
- FileScanner $file_scanner,
- Aliases $aliases,
- string $function_id,
- PhpParser\Node\Expr\FuncCall $node,
- ?FunctionLikeStorage $functionlike_storage,
- ?int $skip_if_descendants
- ): void {
- $callables = InternalCallMapHandler::getCallablesFromCallMap($function_id);
-
- if ($callables) {
- foreach ($callables as $callable) {
- assert($callable->params !== null);
-
- foreach ($callable->params as $function_param) {
- if ($function_param->type) {
- $function_param->type->queueClassLikesForScanning(
- $codebase,
- $file_storage
- );
- }
- }
-
- if ($callable->return_type && !$callable->return_type->hasMixed()) {
- $callable->return_type->queueClassLikesForScanning($codebase, $file_storage);
- }
- }
- }
-
- if ($node->isFirstClassCallable()) {
- return;
- }
-
- if ($function_id === 'define') {
- $first_arg_value = isset($node->getArgs()[0]) ? $node->getArgs()[0]->value : null;
- $second_arg_value = isset($node->getArgs()[1]) ? $node->getArgs()[1]->value : null;
- if ($first_arg_value && $second_arg_value) {
- $type_provider = new NodeDataProvider();
- $const_name = ConstFetchAnalyzer::getConstName(
- $first_arg_value,
- $type_provider,
- $codebase,
- $aliases
- );
-
- if ($const_name !== null) {
- $const_type = SimpleTypeInferer::infer(
- $codebase,
- $type_provider,
- $second_arg_value,
- $aliases
- ) ?? Type::getMixed();
-
- $config = Config::getInstance();
-
- if ($functionlike_storage && !$config->hoist_constants) {
- $functionlike_storage->defined_constants[$const_name] = $const_type;
- } else {
- $file_storage->constants[$const_name] = $const_type;
- $file_storage->declaring_constants[$const_name] = $file_storage->file_path;
- }
-
- if (($codebase->register_stub_files || $codebase->register_autoload_files)
- && (!defined($const_name) || $const_type->isMixed())
- ) {
- $codebase->addGlobalConstantType($const_name, $const_type);
- }
- }
- }
- }
-
- $mapping_function_ids = [];
-
- if (($function_id === 'array_map' && isset($node->getArgs()[0]))
- || ($function_id === 'array_filter' && isset($node->getArgs()[1]))
- ) {
- $node_arg_value = $function_id === 'array_map' ? $node->getArgs()[0]->value : $node->getArgs()[1]->value;
-
- if ($node_arg_value instanceof PhpParser\Node\Scalar\String_
- || $node_arg_value instanceof PhpParser\Node\Expr\Array_
- || $node_arg_value instanceof PhpParser\Node\Expr\BinaryOp\Concat
- ) {
- $mapping_function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
- $file_scanner,
- $node_arg_value
- );
- }
-
- foreach ($mapping_function_ids as $potential_method_id) {
- if (strpos($potential_method_id, '::') === false) {
- continue;
- }
-
- [$callable_fqcln] = explode('::', $potential_method_id);
-
- if (!in_array(strtolower($callable_fqcln), ['self', 'parent', 'static'], true)) {
- $codebase->scanner->queueClassLikeForScanning(
- $callable_fqcln
- );
- }
- }
- }
-
- if ($function_id === 'func_get_arg'
- || $function_id === 'func_get_args'
- || $function_id === 'func_num_args'
- ) {
- if ($functionlike_storage) {
- $functionlike_storage->variadic = true;
- }
- }
-
- if ($function_id === 'is_a' || $function_id === 'is_subclass_of') {
- $second_arg = $node->getArgs()[1]->value ?? null;
-
- if ($second_arg instanceof PhpParser\Node\Scalar\String_) {
- $codebase->scanner->queueClassLikeForScanning(
- $second_arg->value
- );
- }
- }
-
- if ($function_id === 'class_alias' && !$skip_if_descendants) {
- $first_arg = $node->getArgs()[0]->value ?? null;
- $second_arg = $node->getArgs()[1]->value ?? null;
-
- if ($first_arg instanceof PhpParser\Node\Scalar\String_) {
- $first_arg_value = $first_arg->value;
- } elseif ($first_arg instanceof PhpParser\Node\Expr\ClassConstFetch
- && $first_arg->class instanceof PhpParser\Node\Name
- && $first_arg->name instanceof PhpParser\Node\Identifier
- && strtolower($first_arg->name->name) === 'class'
- ) {
- /** @var string */
- $first_arg_value = $first_arg->class->getAttribute('resolvedName');
- } else {
- $first_arg_value = null;
- }
-
- if ($second_arg instanceof PhpParser\Node\Scalar\String_) {
- $second_arg_value = $second_arg->value;
- } elseif ($second_arg instanceof PhpParser\Node\Expr\ClassConstFetch
- && $second_arg->class instanceof PhpParser\Node\Name
- && $second_arg->name instanceof PhpParser\Node\Identifier
- && strtolower($second_arg->name->name) === 'class'
- ) {
- /** @var string */
- $second_arg_value = $second_arg->class->getAttribute('resolvedName');
- } else {
- $second_arg_value = null;
- }
-
- if ($first_arg_value !== null && $second_arg_value !== null) {
- if ($first_arg_value[0] === '\\') {
- $first_arg_value = substr($first_arg_value, 1);
- }
-
- if ($second_arg_value[0] === '\\') {
- $second_arg_value = substr($second_arg_value, 1);
- }
-
- $second_arg_value = strtolower($second_arg_value);
-
- $codebase->classlikes->addClassAlias(
- $first_arg_value,
- $second_arg_value
- );
-
- $file_storage->classlike_aliases[$second_arg_value] = $first_arg_value;
- }
- }
- }
-
- public static function visitInclude(
- Codebase $codebase,
- FileStorage $file_storage,
- PhpParser\Node\Expr\Include_ $stmt,
- bool $scan_deep
- ): void {
- $config = Config::getInstance();
-
- if (!$config->allow_includes) {
- throw new FileIncludeException(
- 'File includes are not allowed per your Psalm config - check the allowFileIncludes flag.'
- );
- }
-
- if ($stmt->expr instanceof PhpParser\Node\Scalar\String_) {
- $path_to_file = $stmt->expr->value;
-
- // attempts to resolve using get_include_path dirs
- $include_path = IncludeAnalyzer::resolveIncludePath($path_to_file, dirname($file_storage->file_path));
- $path_to_file = $include_path ?: $path_to_file;
-
- if (DIRECTORY_SEPARATOR === '/') {
- $is_path_relative = $path_to_file[0] !== DIRECTORY_SEPARATOR;
- } else {
- $is_path_relative = !preg_match('~^[A-Z]:\\\\~i', $path_to_file);
- }
-
- if ($is_path_relative) {
- $path_to_file = $config->base_dir . DIRECTORY_SEPARATOR . $path_to_file;
- }
- } else {
- $path_to_file = IncludeAnalyzer::getPathTo(
- $stmt->expr,
- null,
- null,
- $file_storage->file_path,
- $config
- );
- }
-
- if ($path_to_file) {
- $path_to_file = IncludeAnalyzer::normalizeFilePath($path_to_file);
-
- if ($file_storage->file_path === $path_to_file) {
- return;
- }
-
- if ($codebase->fileExists($path_to_file)) {
- if ($scan_deep) {
- $codebase->scanner->addFileToDeepScan($path_to_file);
- } else {
- $codebase->scanner->addFileToShallowScan($path_to_file);
- }
-
- $file_storage->required_file_paths[strtolower($path_to_file)] = $path_to_file;
-
- return;
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php
deleted file mode 100644
index 341d0f6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockParser.php
+++ /dev/null
@@ -1,697 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\DocComment;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Scanner\DocblockParser;
-use Psalm\Internal\Scanner\FunctionDocblockComment;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\IssueBuffer;
-
-use function array_shift;
-use function array_unique;
-use function count;
-use function explode;
-use function implode;
-use function in_array;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function reset;
-use function str_replace;
-use function stripos;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-use function substr_count;
-use function trim;
-
-class FunctionLikeDocblockParser
-{
- /**
- * @throws DocblockParseException if there was a problem parsing the docblock
- */
- public static function parse(
- PhpParser\Comment\Doc $comment,
- CodeLocation $code_location,
- string $cased_function_id
- ): FunctionDocblockComment {
- $parsed_docblock = DocComment::parsePreservingLength($comment);
-
- $comment_text = $comment->getText();
-
- $info = new FunctionDocblockComment();
-
- self::checkDuplicatedTags($parsed_docblock);
- self::checkUnexpectedTags($parsed_docblock, $info, $comment);
-
- if (isset($parsed_docblock->combined_tags['return'])) {
- self::extractReturnType(
- $comment,
- $parsed_docblock->combined_tags['return'],
- $info,
- $code_location,
- $cased_function_id
- );
- }
-
- if (isset($parsed_docblock->combined_tags['param'])) {
- foreach ($parsed_docblock->combined_tags['param'] as $offset => $param) {
- $line_parts = CommentAnalyzer::splitDocLine($param);
-
- if (count($line_parts) === 1 && isset($line_parts[0][0]) && $line_parts[0][0] === '$') {
- continue;
- }
-
- if (count($line_parts) > 1) {
- if (preg_match('/^&?(\.\.\.)?&?\$[A-Za-z0-9_]+,?$/', $line_parts[1])
- && ($line_parts[0] === '' || $line_parts[0][0] !== '{')
- ) {
- $line_parts[1] = str_replace('&', '', $line_parts[1]);
-
- $line_parts[1] = preg_replace('/,$/', '', $line_parts[1]);
-
- $end = $offset + strlen($line_parts[0]);
-
- $line_parts[0] = CommentAnalyzer::sanitizeDocblockType($line_parts[0]);
-
- if ($line_parts[0] === ''
- || ($line_parts[0][0] === '$'
- && !preg_match('/^\$this(\||$)/', $line_parts[0]))
- ) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- $info_param = [
- 'name' => trim($line_parts[1]),
- 'type' => $line_parts[0],
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment_text,
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- 'start' => $offset,
- 'end' => $end,
- ];
-
- if (isset($line_parts[1]) && isset($line_parts[2])) {
- $description = substr($param, strlen($line_parts[0]) + strlen($line_parts[1]) + 2);
- $info_param['description'] = trim($description);
- // Handle multiline description.
- $info_param['description'] = preg_replace(
- '/\\n \\*\\s+/um',
- ' ',
- $info_param['description']
- );
- }
-
- $info->params[] = $info_param;
- }
- } else {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Badly-formatted @param in docblock for ' . $cased_function_id,
- $code_location
- )
- );
- }
- }
- }
-
- if (isset($parsed_docblock->combined_tags['param-out'])) {
- foreach ($parsed_docblock->combined_tags['param-out'] as $offset => $param) {
- $line_parts = CommentAnalyzer::splitDocLine($param);
-
- if (count($line_parts) === 1 && isset($line_parts[0][0]) && $line_parts[0][0] === '$') {
- continue;
- }
-
- if (count($line_parts) > 1) {
- if (!preg_match('/\[[^\]]+\]/', $line_parts[0])
- && preg_match('/^(\.\.\.)?&?\$[A-Za-z0-9_]+,?$/', $line_parts[1])
- && $line_parts[0][0] !== '{'
- ) {
- if ($line_parts[1][0] === '&') {
- $line_parts[1] = substr($line_parts[1], 1);
- }
-
- $line_parts[0] = str_replace("\n", '', preg_replace('@^[ \t]*\*@m', '', $line_parts[0]));
-
- if ($line_parts[0] === ''
- || ($line_parts[0][0] === '$'
- && !preg_match('/^\$this(\||$)/', $line_parts[0]))
- ) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- $line_parts[1] = preg_replace('/,$/', '', $line_parts[1]);
-
- $info->params_out[] = [
- 'name' => trim($line_parts[1]),
- 'type' => str_replace("\n", '', $line_parts[0]),
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment_text,
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- ];
- }
- } else {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Badly-formatted @param in docblock for ' . $cased_function_id,
- $code_location
- )
- );
- }
- }
- }
-
- foreach (['psalm-self-out', 'psalm-this-out'] as $alias) {
- if (isset($parsed_docblock->tags[$alias])) {
- foreach ($parsed_docblock->tags[$alias] as $offset => $param) {
- $line_parts = CommentAnalyzer::splitDocLine($param);
-
- if (count($line_parts) > 0) {
- $line_parts[0] = str_replace("\n", '', preg_replace('@^[ \t]*\*@m', '', $line_parts[0]));
-
- $info->self_out = [
- 'type' => str_replace("\n", '', $line_parts[0]),
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment_text,
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- ];
- }
- }
- break;
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-flow'])) {
- foreach ($parsed_docblock->tags['psalm-flow'] as $param) {
- $info->flows[] = trim($param);
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-if-this-is'])) {
- foreach ($parsed_docblock->tags['psalm-if-this-is'] as $offset => $param) {
- $line_parts = CommentAnalyzer::splitDocLine($param);
-
- $line_parts[0] = str_replace("\n", '', preg_replace('@^[ \t]*\*@m', '', $line_parts[0]));
-
- $info->if_this_is = [
- 'type' => str_replace("\n", '', $line_parts[0]),
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- ];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-sink'])) {
- foreach ($parsed_docblock->tags['psalm-taint-sink'] as $param) {
- $param_parts = preg_split('/\s+/', trim($param));
-
- if (count($param_parts) >= 2) {
- $info->taint_sink_params[] = ['name' => $param_parts[1], 'taint' => $param_parts[0]];
- }
- }
- }
-
- // support for MediaWiki taint plugin
- if (isset($parsed_docblock->tags['param-taint'])) {
- foreach ($parsed_docblock->tags['param-taint'] as $param) {
- $param_parts = preg_split('/\s+/', trim($param));
-
- if (count($param_parts) === 2) {
- $taint_type = $param_parts[1];
-
- if (strpos($taint_type, 'exec_') === 0) {
- $taint_type = substr($taint_type, 5);
-
- if ($taint_type === 'tainted') {
- $taint_type = 'input';
- }
-
- if ($taint_type === 'misc') {
- $taint_type = 'text';
- }
-
- $info->taint_sink_params[] = ['name' => $param_parts[0], 'taint' => $taint_type];
- }
- }
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-source'])) {
- foreach ($parsed_docblock->tags['psalm-taint-source'] as $param) {
- $param_parts = preg_split('/\s+/', trim($param));
-
- if ($param_parts[0]) {
- $info->taint_source_types[] = $param_parts[0];
- }
- }
- } elseif (isset($parsed_docblock->tags['return-taint'])) {
- // support for MediaWiki taint plugin
- foreach ($parsed_docblock->tags['return-taint'] as $param) {
- $param_parts = preg_split('/\s+/', trim($param));
-
- if ($param_parts[0]) {
- if ($param_parts[0] === 'tainted') {
- $param_parts[0] = 'input';
- }
-
- if ($param_parts[0] === 'misc') {
- $param_parts[0] = 'text';
- }
-
- if ($param_parts[0] !== 'none') {
- $info->taint_source_types[] = $param_parts[0];
- }
- }
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-unescape'])) {
- foreach ($parsed_docblock->tags['psalm-taint-unescape'] as $param) {
- $param = trim($param);
- $info->added_taints[] = $param;
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-escape'])) {
- foreach ($parsed_docblock->tags['psalm-taint-escape'] as $param) {
- $param = trim($param);
- if ($param[0] === '(') {
- $line_parts = CommentAnalyzer::splitDocLine($param);
-
- $info->removed_taints[] = CommentAnalyzer::sanitizeDocblockType($line_parts[0]);
- } else {
- $info->removed_taints[] = explode(' ', $param)[0];
- }
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-assert-untainted'])) {
- foreach ($parsed_docblock->tags['psalm-assert-untainted'] as $param) {
- $param = trim($param);
-
- $info->assert_untainted_params[] = ['name' => $param];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-taint-specialize'])) {
- $info->specialize_call = true;
- }
-
- if (isset($parsed_docblock->tags['global'])) {
- foreach ($parsed_docblock->tags['global'] as $offset => $global) {
- $line_parts = CommentAnalyzer::splitDocLine($global);
-
- if (count($line_parts) === 1 && isset($line_parts[0][0]) && $line_parts[0][0] === '$') {
- continue;
- }
-
- if (count($line_parts) > 1) {
- if (!preg_match('/\[[^\]]+\]/', $line_parts[0])
- && preg_match('/^(\.\.\.)?&?\$[A-Za-z0-9_]+,?$/', $line_parts[1])
- && $line_parts[0][0] !== '{'
- ) {
- if ($line_parts[1][0] === '&') {
- $line_parts[1] = substr($line_parts[1], 1);
- }
-
- if ($line_parts[0][0] === '$' && !preg_match('/^\$this(\||$)/', $line_parts[0])) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- $line_parts[1] = preg_replace('/,$/', '', $line_parts[1]);
-
- $info->globals[] = [
- 'name' => $line_parts[1],
- 'type' => $line_parts[0],
- 'line_number' => $comment->getStartLine() + substr_count(
- $comment_text,
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- ),
- ];
- }
- } else {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Badly-formatted @param in docblock for ' . $cased_function_id,
- $code_location
- )
- );
- }
- }
- }
-
- if (isset($parsed_docblock->tags['since'])) {
- $since = trim(reset($parsed_docblock->tags['since']));
- if (preg_match('/^[4578]\.\d(\.\d+)?$/', $since)) {
- $since_parts = explode('.', $since);
-
- $info->since_php_major_version = (int)$since_parts[0];
- $info->since_php_minor_version = (int)$since_parts[1];
- }
- }
-
- if (isset($parsed_docblock->tags['deprecated'])) {
- $info->deprecated = true;
- }
-
- if (isset($parsed_docblock->tags['internal'])) {
- $info->internal = true;
- }
-
- if (count($info->psalm_internal = DocblockParser::handlePsalmInternal($parsed_docblock)) !== 0) {
- $info->internal = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-suppress'])) {
- foreach ($parsed_docblock->tags['psalm-suppress'] as $offset => $suppress_entry) {
- foreach (DocComment::parseSuppressList($suppress_entry) as $issue_offset => $suppressed_issue) {
- $info->suppressed_issues[$issue_offset + $offset] = $suppressed_issue;
- }
- }
- }
-
- if (isset($parsed_docblock->tags['throws'])) {
- foreach ($parsed_docblock->tags['throws'] as $offset => $throws_entry) {
- $throws_class = preg_split('/[\s]+/', $throws_entry)[0];
-
- if (!$throws_class) {
- throw new IncorrectDocblockException('Unexpectedly empty @throws');
- }
-
- $info->throws[] = [
- $throws_class,
- $offset,
- $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- )
- ];
- }
- }
-
- if (stripos($parsed_docblock->description, '@inheritdoc') !== false
- || isset($parsed_docblock->tags['inheritdoc'])
- || isset($parsed_docblock->tags['inheritDoc'])
- ) {
- $info->inheritdoc = true;
- }
-
- $templates = [];
- if (isset($parsed_docblock->combined_tags['template'])) {
- foreach ($parsed_docblock->combined_tags['template'] as $offset => $template_line) {
- $template_type = preg_split('/[\s]+/', preg_replace('@^[ \t]*\*@m', '', $template_line));
-
- $template_name = array_shift($template_type);
-
- if (!$template_name) {
- throw new IncorrectDocblockException('Empty @template tag');
- }
-
- $source_prefix = 'none';
- if (isset($parsed_docblock->tags['psalm-template'][$offset])) {
- $source_prefix = 'psalm';
- } elseif (isset($parsed_docblock->tags['phpstan-template'][$offset])) {
- $source_prefix = 'phpstan';
- }
-
- if (count($template_type) > 1
- && in_array(strtolower($template_type[0]), ['as', 'super', 'of'], true)
- ) {
- $template_modifier = strtolower(array_shift($template_type));
- $templates[$template_name][$source_prefix] = [
- $template_name,
- $template_modifier,
- implode(' ', $template_type),
- false
- ];
- } else {
- $templates[$template_name][$source_prefix] = [$template_name, null, null, false];
- }
- }
- }
-
- foreach ($templates as $template_entries) {
- foreach (['psalm', 'phpstan', 'none'] as $source_prefix) {
- if (isset($template_entries[$source_prefix])) {
- $info->templates[] = $template_entries[$source_prefix];
- break;
- }
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-assert'])) {
- foreach ($parsed_docblock->tags['psalm-assert'] as $assertion) {
- $line_parts = self::sanitizeAssertionLineParts(CommentAnalyzer::splitDocLine($assertion));
-
- $info->assertions[] = [
- 'type' => $line_parts[0],
- 'param_name' => $line_parts[1][0] === '$' ? substr($line_parts[1], 1) : $line_parts[1],
- ];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-assert-if-true'])) {
- foreach ($parsed_docblock->tags['psalm-assert-if-true'] as $assertion) {
- $line_parts = self::sanitizeAssertionLineParts(CommentAnalyzer::splitDocLine($assertion));
-
- $info->if_true_assertions[] = [
- 'type' => $line_parts[0],
- 'param_name' => $line_parts[1][0] === '$' ? substr($line_parts[1], 1) : $line_parts[1],
- ];
- }
- }
-
- if (isset($parsed_docblock->tags['psalm-assert-if-false'])) {
- foreach ($parsed_docblock->tags['psalm-assert-if-false'] as $assertion) {
- $line_parts = self::sanitizeAssertionLineParts(CommentAnalyzer::splitDocLine($assertion));
-
- $info->if_false_assertions[] = [
- 'type' => $line_parts[0],
- 'param_name' => $line_parts[1][0] === '$' ? substr($line_parts[1], 1) : $line_parts[1],
- ];
- }
- }
-
- $info->variadic = isset($parsed_docblock->tags['psalm-variadic']);
- $info->pure = isset($parsed_docblock->tags['psalm-pure'])
- || isset($parsed_docblock->tags['pure']);
-
- if (isset($parsed_docblock->tags['psalm-mutation-free'])) {
- $info->mutation_free = true;
- }
-
- if (isset($parsed_docblock->tags['psalm-external-mutation-free'])) {
- $info->external_mutation_free = true;
- }
-
- if (isset($parsed_docblock->tags['no-named-arguments'])) {
- $info->no_named_args = true;
- }
-
- $info->ignore_nullable_return = isset($parsed_docblock->tags['psalm-ignore-nullable-return']);
- $info->ignore_falsable_return = isset($parsed_docblock->tags['psalm-ignore-falsable-return']);
- $info->stub_override = isset($parsed_docblock->tags['psalm-stub-override']);
-
- if (!empty($parsed_docblock->description)) {
- $info->description = $parsed_docblock->description;
- }
-
- return $info;
- }
-
- /**
- * @psalm-pure
- * @param list<string> $line_parts
- * @return array{string, string} $line_parts
- */
- private static function sanitizeAssertionLineParts(array $line_parts): array
- {
- if (count($line_parts) < 2 || strpos($line_parts[1], '$') === false) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- $line_parts[0] = CommentAnalyzer::sanitizeDocblockType($line_parts[0]);
-
- if ($line_parts[1][0] === '$') {
- $param_name_parts = explode('->', $line_parts[1]);
-
- foreach ($param_name_parts as $i => $param_name_part) {
- if (substr($param_name_part, -2) === '()') {
- $param_name_parts[$i] = strtolower($param_name_part);
- }
- }
-
- $line_parts[1] = implode('->', $param_name_parts);
- }
-
- return $line_parts;
- }
-
- /**
- * @param array<int, string> $return_specials
- */
- private static function extractReturnType(
- PhpParser\Comment\Doc $comment,
- array $return_specials,
- FunctionDocblockComment $info,
- CodeLocation $code_location,
- string $cased_function_id
- ): void {
- foreach ($return_specials as $offset => $return_block) {
- $return_lines = explode("\n", $return_block);
-
- if (trim($return_lines[0]) === '') {
- return;
- }
-
- $return_block = trim($return_block);
-
- if ($return_block === '') {
- return;
- }
-
- $line_parts = CommentAnalyzer::splitDocLine($return_block);
-
- if ($line_parts[0][0] !== '{') {
- if ($line_parts[0][0] === '$' && !preg_match('/^\$this(\||$)/', $line_parts[0])) {
- throw new IncorrectDocblockException('Misplaced variable');
- }
-
- $end = $offset + strlen($line_parts[0]);
-
- $line_parts[0] = CommentAnalyzer::sanitizeDocblockType($line_parts[0]);
-
- $info->return_type = array_shift($line_parts);
- $info->return_type_description = $line_parts ? implode(' ', $line_parts) : null;
-
- $info->return_type_line_number
- = $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- );
- $info->return_type_start = $offset;
- $info->return_type_end = $end;
- } else {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- 'Badly-formatted @param in docblock for ' . $cased_function_id,
- $code_location
- )
- );
- }
-
- break;
- }
- }
-
- /**
- * @throws DocblockParseException if a duplicate is found
- */
- private static function checkDuplicatedTags(ParsedDocblock $parsed_docblock): void
- {
- if (count($parsed_docblock->tags['return'] ?? []) > 1
- || count($parsed_docblock->tags['psalm-return'] ?? []) > 1
- || count($parsed_docblock->tags['phpstan-return'] ?? []) > 1
- ) {
- throw new DocblockParseException('Found duplicated @return or prefixed @return tag');
- }
-
- self::checkDuplicatedParams($parsed_docblock->tags['param'] ?? []);
- self::checkDuplicatedParams($parsed_docblock->tags['psalm-param'] ?? []);
- self::checkDuplicatedParams($parsed_docblock->tags['phpstan-param'] ?? []);
- }
-
- /**
- * @param array<int, string> $param
- *
- *
- * @throws DocblockParseException if a duplicate is found
- */
- private static function checkDuplicatedParams(array $param): void
- {
- $list_names = self::extractAllParamNames($param);
-
- if (count($list_names) !== count(array_unique($list_names))) {
- throw new DocblockParseException('Found duplicated @param or prefixed @param tag');
- }
- }
-
- /**
- * @param array<int, string> $lines
- *
- * @return list<string>
- *
- * @psalm-pure
- */
- private static function extractAllParamNames(array $lines): array
- {
- $names = [];
-
- foreach ($lines as $line) {
- $split_by_dollar = explode('$', $line, 2);
- if (count($split_by_dollar) > 1) {
- $split_by_space = explode(' ', $split_by_dollar[1], 2);
- $names[] = $split_by_space[0];
- }
- }
-
- return $names;
- }
-
- private static function checkUnexpectedTags(
- ParsedDocblock $parsed_docblock,
- FunctionDocblockComment $info,
- PhpParser\Comment\Doc $comment
- ): void {
- if (isset($parsed_docblock->tags['psalm-import-type'])) {
- foreach ($parsed_docblock->tags['psalm-import-type'] as $offset => $_) {
- $info->unexpected_tags['psalm-import-type']['lines'][] = self::docblockLineNumber($comment, $offset);
- }
- }
-
- if (isset($parsed_docblock->combined_tags['var'])) {
- $info->unexpected_tags['var'] = ['lines' => [], 'suggested_replacement' => 'param'];
- foreach ($parsed_docblock->combined_tags['var'] as $offset => $_) {
- $info->unexpected_tags['var']['lines'][] = self::docblockLineNumber($comment, $offset);
- }
- }
- }
-
- private static function docblockLineNumber(PhpParser\Comment\Doc $comment, int $offset): int
- {
- return $comment->getStartLine() + substr_count(
- $comment->getText(),
- "\n",
- 0,
- $offset - $comment->getStartFilePos()
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php
deleted file mode 100644
index d78bff9..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php
+++ /dev/null
@@ -1,1454 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Exception\InvalidMethodOverrideException;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\Internal\Scanner\FunctionDocblockComment;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeAlias;
-use Psalm\Internal\Type\TypeParser;
-use Psalm\Internal\Type\TypeTokenizer;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\PossiblyInvalidDocblockTag;
-use Psalm\Storage\Assertion;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TAssertionFalsy;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\TaintKindGroup;
-use Psalm\Type\Union;
-
-use function array_filter;
-use function array_map;
-use function array_merge;
-use function array_values;
-use function count;
-use function explode;
-use function in_array;
-use function preg_match;
-use function preg_replace;
-use function preg_split;
-use function str_replace;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-use function trim;
-
-class FunctionLikeDocblockScanner
-{
- /**
- * @param array<string, non-empty-array<string, Union>> $existing_function_template_types
- * @param array<string, TypeAlias> $type_aliases
- */
- public static function addDocblockInfo(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- array $type_aliases,
- ?ClassLikeStorage $classlike_storage,
- array $existing_function_template_types,
- FunctionLikeStorage $storage,
- PhpParser\Node\FunctionLike $stmt,
- FunctionDocblockComment $docblock_info,
- bool $is_functionlike_override,
- bool $fake_method,
- string $cased_function_id
- ): void {
- self::handleUnexpectedTags($docblock_info, $storage, $stmt, $file_scanner, $cased_function_id);
-
- $config = Config::getInstance();
-
- if ($docblock_info->mutation_free) {
- $storage->mutation_free = true;
-
- if ($storage instanceof MethodStorage) {
- $storage->external_mutation_free = true;
- $storage->mutation_free_inferred = false;
- }
- }
-
- if ($storage instanceof MethodStorage && $docblock_info->external_mutation_free) {
- $storage->external_mutation_free = true;
- }
-
- if ($docblock_info->deprecated) {
- $storage->deprecated = true;
- }
-
- if (count($docblock_info->psalm_internal) !== 0) {
- $storage->internal = $docblock_info->psalm_internal;
- } elseif ($docblock_info->internal && $aliases->namespace) {
- $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($aliases->namespace)];
- }
-
- if (($storage->internal || ($classlike_storage && $classlike_storage->internal))
- && !$config->allow_internal_named_arg_calls
- ) {
- $storage->allow_named_arg_calls = false;
- } elseif ($docblock_info->no_named_args) {
- $storage->allow_named_arg_calls = false;
- }
-
- if ($docblock_info->variadic) {
- $storage->variadic = true;
- }
-
- if ($docblock_info->pure) {
- $storage->pure = true;
- $storage->specialize_call = true;
- $storage->mutation_free = true;
- if ($storage instanceof MethodStorage) {
- $storage->external_mutation_free = true;
- }
- }
-
- if ($docblock_info->specialize_call) {
- $storage->specialize_call = true;
- }
-
- // we make sure we only add ignore flag for internal stubs if the config is set to true
- if ($docblock_info->ignore_nullable_return
- && $storage->return_type
- && ($codebase->config->ignore_internal_nullable_issues
- || !in_array($file_storage->file_path, $codebase->config->internal_stubs)
- )
- ) {
- $storage->return_type->ignore_nullable_issues = true;
- }
-
- // we make sure we only add ignore flag for internal stubs if the config is set to true
- if ($docblock_info->ignore_falsable_return
- && $storage->return_type
- && ($codebase->config->ignore_internal_falsable_issues
- || !in_array($file_storage->file_path, $codebase->config->internal_stubs)
- )
- ) {
- $storage->return_type->ignore_falsable_issues = true;
- }
-
- if ($docblock_info->stub_override && !$is_functionlike_override) {
- throw new InvalidMethodOverrideException(
- 'Method ' . $cased_function_id . ' is marked as stub override,'
- . ' but no original counterpart found'
- );
- }
-
- $storage->suppressed_issues = $docblock_info->suppressed_issues;
-
- foreach ($docblock_info->throws as [$throw, $offset, $line]) {
- $throw_location = new DocblockTypeLocation(
- $file_scanner,
- $offset,
- $offset + strlen($throw),
- $line
- );
-
- $class_names = array_filter(array_map('trim', explode('|', $throw)));
-
- foreach ($class_names as $throw_class) {
- if ($throw_class !== 'self' && $throw_class !== 'static' && $throw_class !== 'parent') {
- $exception_fqcln = Type::getFQCLNFromString(
- $throw_class,
- $aliases
- );
- } else {
- $exception_fqcln = $throw_class;
- }
-
- $codebase->scanner->queueClassLikeForScanning($exception_fqcln);
- $file_storage->referenced_classlikes[strtolower($exception_fqcln)] = $exception_fqcln;
- $storage->throws[$exception_fqcln] = true;
- $storage->throw_locations[$exception_fqcln] = $throw_location;
- }
- }
-
- if (!$config->use_docblock_types) {
- return;
- }
-
- if ($storage instanceof MethodStorage && $docblock_info->inheritdoc) {
- $storage->inheritdoc = true;
- }
-
- $template_types = $classlike_storage && $classlike_storage->template_types
- ? $classlike_storage->template_types
- : null;
-
- $function_template_types = $existing_function_template_types;
- $class_template_types = $classlike_storage ? ($classlike_storage->template_types ?: []) : [];
-
- if ($docblock_info->templates) {
- $function_template_types = self::handleTemplates(
- $storage,
- $docblock_info,
- $aliases,
- $template_types,
- $type_aliases,
- $file_scanner,
- $stmt,
- $cased_function_id
- );
- }
-
- self::handleAssertions(
- $docblock_info,
- $storage,
- $codebase,
- $file_scanner,
- $file_storage,
- $aliases,
- $stmt,
- $class_template_types,
- $function_template_types,
- $type_aliases,
- $classlike_storage
- );
-
- foreach ($docblock_info->globals as $global) {
- try {
- $storage->global_types[$global['name']] = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $global['type'],
- $aliases,
- null,
- $type_aliases
- ),
- null
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $cased_function_id,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
-
- continue;
- }
- }
-
- if ($docblock_info->params) {
- self::improveParamsFromDocblock(
- $codebase,
- $file_scanner,
- $file_storage,
- $aliases,
- $type_aliases,
- $classlike_storage,
- $storage,
- $function_template_types,
- $class_template_types,
- $docblock_info->params,
- $stmt,
- $fake_method,
- $classlike_storage && !$classlike_storage->is_trait ? $classlike_storage->name : null
- );
- }
-
- if ($storage instanceof MethodStorage) {
- $storage->has_docblock_param_types = (bool) array_filter(
- $storage->params,
- function (FunctionLikeParameter $p): bool {
- return $p->type !== null && $p->has_docblock_type;
- }
- );
- }
-
- foreach ($docblock_info->params_out as $docblock_param_out) {
- self::handleParamOut(
- $docblock_param_out,
- $aliases,
- $function_template_types,
- $class_template_types,
- $type_aliases,
- $cased_function_id,
- $file_scanner,
- $stmt,
- $storage,
- $codebase,
- $file_storage
- );
- }
-
- if ($docblock_info->self_out
- && $storage instanceof MethodStorage) {
- $out_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $docblock_info->self_out['type'],
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases,
- $classlike_storage ? $classlike_storage->name : null
- ),
- null,
- $function_template_types + $class_template_types,
- $type_aliases
- );
- $storage->self_out_type = $out_type;
- }
-
- if ($docblock_info->if_this_is
- && $storage instanceof MethodStorage) {
- $out_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $docblock_info->if_this_is['type'],
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases,
- $classlike_storage ? $classlike_storage->name : null
- ),
- null,
- $function_template_types + $class_template_types,
- $type_aliases
- );
- $storage->if_this_is_type = $out_type;
- }
-
- foreach ($docblock_info->taint_sink_params as $taint_sink_param) {
- $param_name = substr($taint_sink_param['name'], 1);
-
- foreach ($storage->params as $param_storage) {
- if ($param_storage->name === $param_name) {
- $param_storage->sinks[] = $taint_sink_param['taint'];
- }
- }
- }
-
- foreach ($docblock_info->taint_source_types as $taint_source_type) {
- if ($taint_source_type === 'input') {
- $storage->taint_source_types = array_merge(
- $storage->taint_source_types,
- TaintKindGroup::ALL_INPUT
- );
- } else {
- $storage->taint_source_types[] = $taint_source_type;
- }
- }
-
- $storage->added_taints = $docblock_info->added_taints;
-
- foreach ($docblock_info->removed_taints as $removed_taint) {
- if ($removed_taint[0] === '(') {
- self::handleRemovedTaint(
- $codebase,
- $stmt,
- $aliases,
- $removed_taint,
- $function_template_types,
- $class_template_types,
- $type_aliases,
- $storage,
- $classlike_storage,
- $cased_function_id,
- $file_storage,
- $file_scanner
- );
- } else {
- $storage->removed_taints[] = $removed_taint;
- }
- }
-
- self::handleTaintFlow($docblock_info, $storage);
-
- foreach ($docblock_info->assert_untainted_params as $untainted_assert_param) {
- $param_name = substr($untainted_assert_param['name'], 1);
-
- foreach ($storage->params as $param_storage) {
- if ($param_storage->name === $param_name) {
- $param_storage->assert_untainted = true;
- }
- }
- }
-
- if ($docblock_info->return_type !== null) {
- self::handleReturn(
- $codebase,
- $docblock_info,
- $docblock_info->return_type,
- $fake_method,
- $file_scanner,
- $storage,
- $stmt,
- $aliases,
- $function_template_types,
- $class_template_types,
- $type_aliases,
- $classlike_storage,
- $cased_function_id,
- $file_storage
- );
- }
-
- if ($docblock_info->description) {
- $storage->description = $docblock_info->description;
- }
- }
-
- /**
- * @param array<string, array<string, Union>> $template_types
- * @param array<string, TypeAlias>|null $type_aliases
- * @param array<string, array<string, Union>> $function_template_types
- *
- * @return array{
- * array<int, array{0: string, 1: int, 2?: string}>,
- * array<string, array<string, Union>>
- * }
- */
- private static function getConditionalSanitizedTypeTokens(
- string $docblock_return_type,
- Aliases $aliases,
- array $template_types,
- ?array $type_aliases,
- FunctionLikeStorage $storage,
- ?ClassLikeStorage $classlike_storage,
- string $cased_function_id,
- array $function_template_types
- ): array {
- $fixed_type_tokens = TypeTokenizer::getFullyQualifiedTokens(
- $docblock_return_type,
- $aliases,
- $template_types,
- $type_aliases,
- $classlike_storage && !$classlike_storage->is_trait ? $classlike_storage->name : null
- );
-
- $param_type_mapping = [];
-
- // This checks for param references in the return type tokens
- // If found, the param is replaced with a generated template param
- foreach ($fixed_type_tokens as $i => $type_token) {
- $token_body = $type_token[0];
- $template_function_id = 'fn-' . strtolower($cased_function_id);
-
- if ($token_body[0] === '$') {
- foreach ($storage->params as $j => $param_storage) {
- if ('$' . $param_storage->name === $token_body) {
- if (!isset($param_type_mapping[$token_body])) {
- $template_name = 'TGeneratedFromParam' . $j;
-
- $template_as_type = $param_storage->type
- ? clone $param_storage->type
- : Type::getMixed();
-
- $storage->template_types[$template_name] = [
- $template_function_id => $template_as_type,
- ];
-
- $function_template_types[$template_name]
- = $storage->template_types[$template_name];
-
- $param_type_mapping[$token_body] = $template_name;
-
- $param_storage->type = new Union([
- new TTemplateParam(
- $template_name,
- $template_as_type,
- $template_function_id
- )
- ]);
- }
-
- // spaces are allowed before $foo in get(string $foo) magic method
- // definitions, but we want to remove them in this instance
- if (isset($fixed_type_tokens[$i - 1])
- && $fixed_type_tokens[$i - 1][0][0] === ' '
- ) {
- unset($fixed_type_tokens[$i - 1]);
- }
-
- $fixed_type_tokens[$i][0] = $param_type_mapping[$token_body];
-
- continue 2;
- }
- }
- }
-
- if ($token_body === 'func_num_args()') {
- $template_name = 'TFunctionArgCount';
-
- $storage->template_types[$template_name] = [
- $template_function_id => Type::getInt(),
- ];
-
- $function_template_types[$template_name]
- = $storage->template_types[$template_name];
-
- $fixed_type_tokens[$i][0] = $template_name;
- }
-
- if ($token_body === 'PHP_MAJOR_VERSION') {
- $template_name = 'TPhpMajorVersion';
-
- $storage->template_types[$template_name] = [
- $template_function_id => Type::getInt(),
- ];
-
- $function_template_types[$template_name]
- = $storage->template_types[$template_name];
-
- $fixed_type_tokens[$i][0] = $template_name;
- }
-
- if ($token_body === 'PHP_VERSION_ID') {
- $template_name = 'TPhpVersionId';
-
- $storage->template_types[$template_name] = [
- $template_function_id => Type::getInt()
- ];
-
- $function_template_types[$template_name]
- = $storage->template_types[$template_name];
-
- $fixed_type_tokens[$i][0] = $template_name;
- }
- }
-
- return [$fixed_type_tokens, $function_template_types];
- }
-
- /**
- * @param array<string, array<string, Union>> $class_template_types
- * @param array<string, array<string, Union>> $function_template_types
- * @param array<string, TypeAlias> $type_aliases
- * @return non-empty-list<string>|null
- */
- private static function getAssertionParts(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- PhpParser\Node\FunctionLike $stmt,
- FunctionLikeStorage $storage,
- string $assertion_type,
- array $class_template_types,
- array $function_template_types,
- array $type_aliases,
- ?string $self_fqcln
- ): ?array {
- $prefix = '';
-
- if ($assertion_type[0] === '!') {
- $prefix = '!';
- $assertion_type = substr($assertion_type, 1);
- }
-
- if ($assertion_type[0] === '~') {
- $prefix .= '~';
- $assertion_type = substr($assertion_type, 1);
- }
-
- if ($assertion_type[0] === '=') {
- $prefix .= '=';
- $assertion_type = substr($assertion_type, 1);
- }
-
- $class_template_types = !$stmt instanceof PhpParser\Node\Stmt\ClassMethod || !$stmt->isStatic()
- ? $class_template_types
- : [];
-
- try {
- $namespaced_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $assertion_type,
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases,
- $self_fqcln,
- null,
- true
- )
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Invalid @psalm-assert union type ' . $e,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
-
- return null;
- }
-
-
- if ($prefix && count($namespaced_type->getAtomicTypes()) > 1) {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Docblock assertions cannot contain | characters together with ' . $prefix,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
-
- return null;
- }
-
- $namespaced_type->queueClassLikesForScanning(
- $codebase,
- $file_storage,
- $function_template_types + $class_template_types
- );
-
- $assertion_type_parts = [];
-
- foreach ($namespaced_type->getAtomicTypes() as $namespaced_type_part) {
- if ($namespaced_type_part instanceof TAssertionFalsy
- || $namespaced_type_part instanceof TClassConstant
- || ($namespaced_type_part instanceof TList
- && $namespaced_type_part->type_param->isMixed())
- || ($namespaced_type_part instanceof TArray
- && $namespaced_type_part->type_params[0]->isArrayKey()
- && $namespaced_type_part->type_params[1]->isMixed())
- || ($namespaced_type_part instanceof TIterable
- && $namespaced_type_part->type_params[0]->isMixed()
- && $namespaced_type_part->type_params[1]->isMixed())
- ) {
- $assertion_type_parts[] = $prefix . $namespaced_type_part->getAssertionString();
- } else {
- $assertion_type_parts[] = $prefix . $namespaced_type_part->getId();
- }
- }
-
- return $assertion_type_parts;
- }
-
- /**
- * @param array<string, array<string, Union>> $class_template_types
- * @param array<string, non-empty-array<string, Union>> $function_template_types
- * @param array<string, TypeAlias> $type_aliases
- * @param array<
- * int,
- * array{
- * type:string,
- * name:string,
- * line_number:int,
- * start:int,
- * end:int,
- * description?:string
- * }
- * > $docblock_params
- */
- private static function improveParamsFromDocblock(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- array $type_aliases,
- ?ClassLikeStorage $classlike_storage,
- FunctionLikeStorage $storage,
- array &$function_template_types,
- array $class_template_types,
- array $docblock_params,
- PhpParser\Node\FunctionLike $function,
- bool $fake_method,
- ?string $fq_classlike_name
- ): void {
- $base = $classlike_storage ? $classlike_storage->name . '::' : '';
-
- $cased_method_id = $base . $storage->cased_name;
-
- $unused_docblock_params = [];
-
- $class_template_types = !$function instanceof PhpParser\Node\Stmt\ClassMethod || !$function->isStatic()
- ? $class_template_types
- : [];
-
- foreach ($docblock_params as $docblock_param) {
- $param_name = $docblock_param['name'];
- $docblock_param_variadic = false;
-
- if (strpos($param_name, '...') === 0) {
- $docblock_param_variadic = true;
- $param_name = substr($param_name, 3);
- }
-
- $param_name = substr($param_name, 1);
-
- $storage_param = null;
-
- foreach ($storage->params as $function_signature_param) {
- if ($function_signature_param->name === $param_name) {
- $storage_param = $function_signature_param;
- break;
- }
- }
-
- if (!$fake_method) {
- $docblock_type_location = new DocblockTypeLocation(
- $file_scanner,
- $docblock_param['start'],
- $docblock_param['end'],
- $docblock_param['line_number']
- );
- } else {
- $docblock_type_location = new CodeLocation(
- $file_scanner,
- $function,
- null,
- false,
- CodeLocation::FUNCTION_PHPDOC_METHOD,
- null
- );
- }
-
- if ($storage_param === null) {
- $param_location = new CodeLocation(
- $file_scanner,
- $function,
- null,
- true,
- CodeLocation::FUNCTION_PARAM_VAR
- );
-
- $param_location->setCommentLine($docblock_param['line_number']);
- $unused_docblock_params[$param_name] = $param_location;
-
- if (!$docblock_param_variadic || $storage->params || $file_scanner->will_analyze) {
- continue;
- }
-
- $storage_param = new FunctionLikeParameter(
- $param_name,
- false,
- null,
- null,
- null,
- false,
- false,
- true,
- null
- );
-
- $storage->addParam($storage_param);
- }
-
- try {
- $new_param_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $docblock_param['type'],
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases,
- $fq_classlike_name
- ),
- null,
- $function_template_types + $class_template_types,
- $type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $cased_method_id,
- $docblock_type_location
- );
-
- continue;
- }
-
- $storage_param->has_docblock_type = true;
- $new_param_type->setFromDocblock();
-
- $new_param_type->queueClassLikesForScanning(
- $codebase,
- $file_storage,
- $storage->template_types ?: []
- );
-
- if ($storage->template_types) {
- foreach ($storage->template_types as $t => $type_map) {
- foreach ($type_map as $obj => $type) {
- if ($type->isMixed() && $docblock_param['type'] === 'class-string<' . $t . '>') {
- $storage->template_types[$t][$obj] = Type::getObject();
-
- if (isset($function_template_types[$t])) {
- $function_template_types[$t][$obj] = $storage->template_types[$t][$obj];
- }
- }
- }
- }
- }
-
- if (!$docblock_param_variadic && $storage_param->is_variadic && $new_param_type->hasArray()) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_type = $new_param_type->getAtomicTypes()['array'];
-
- if ($array_type instanceof TKeyedArray) {
- $new_param_type = $array_type->getGenericValueType();
- } elseif ($array_type instanceof TList) {
- $new_param_type = $array_type->type_param;
- } else {
- $new_param_type = $array_type->type_params[1];
- }
- }
-
- $existing_param_type_nullable = $storage_param->is_nullable;
-
- if (isset($docblock_param['description'])) {
- $storage_param->description = $docblock_param['description'];
- }
-
- if (!$storage_param->type || $storage_param->type->hasMixed() || $storage->template_types) {
- if ($existing_param_type_nullable
- && !$new_param_type->isNullable()
- && !$new_param_type->hasTemplate()
- ) {
- $new_param_type->addType(new TNull());
- }
-
- $config = Config::getInstance();
-
- if ($config->add_param_default_to_docblock_type
- && $storage_param->default_type instanceof Union
- && !$storage_param->default_type->hasMixed()
- && (!$storage_param->type || !$storage_param->type->hasMixed())
- ) {
- $new_param_type = Type::combineUnionTypes($new_param_type, $storage_param->default_type);
- }
-
- $storage_param->type = $new_param_type;
- $storage_param->type_location = $docblock_type_location;
- continue;
- }
-
- $storage_param_atomic_types = $storage_param->type->getAtomicTypes();
-
- $all_typehint_types_match = true;
-
- foreach ($new_param_type->getAtomicTypes() as $key => $type) {
- if (isset($storage_param_atomic_types[$key])) {
- $type->from_docblock = false;
-
- if ($storage_param_atomic_types[$key] instanceof TArray
- && $type instanceof TArray
- && $type->type_params[0]->hasArrayKey()
- ) {
- $type->type_params[0]->from_docblock = false;
- }
- } else {
- $all_typehint_types_match = false;
- }
- }
-
- if ($all_typehint_types_match) {
- $new_param_type->from_docblock = false;
- }
-
- if ($existing_param_type_nullable && !$new_param_type->isNullable()) {
- $new_param_type->addType(new TNull());
- }
-
- $storage_param->type = $new_param_type;
- $storage_param->type_location = $docblock_type_location;
- }
-
- $params_without_docblock_type = array_filter(
- $storage->params,
- function (FunctionLikeParameter $p): bool {
- return !$p->has_docblock_type && (!$p->type || $p->type->hasArray());
- }
- );
-
- if ($params_without_docblock_type) {
- $storage->unused_docblock_params = $unused_docblock_params;
- }
- }
-
- /**
- * @param array<string, TypeAlias> $type_aliases
- * @param array<string, non-empty-array<string, Union>> $function_template_types
- * @param array<string, non-empty-array<string, Union>> $class_template_types
- */
- private static function handleReturn(
- Codebase $codebase,
- FunctionDocblockComment $docblock_info,
- string $docblock_return_type,
- bool $fake_method,
- FileScanner $file_scanner,
- FunctionLikeStorage $storage,
- PhpParser\Node\FunctionLike $stmt,
- Aliases $aliases,
- array $function_template_types,
- array $class_template_types,
- array $type_aliases,
- ?ClassLikeStorage $classlike_storage,
- string $cased_function_id,
- FileStorage $file_storage
- ): void {
- if (!$fake_method
- && $docblock_info->return_type_line_number
- && $docblock_info->return_type_start
- && $docblock_info->return_type_end
- ) {
- $storage->return_type_location = new DocblockTypeLocation(
- $file_scanner,
- $docblock_info->return_type_start,
- $docblock_info->return_type_end,
- $docblock_info->return_type_line_number
- );
- } else {
- $storage->return_type_location = new CodeLocation(
- $file_scanner,
- $stmt,
- null,
- false,
- !$fake_method
- ? CodeLocation::FUNCTION_PHPDOC_RETURN_TYPE
- : CodeLocation::FUNCTION_PHPDOC_METHOD,
- $docblock_info->return_type
- );
- }
-
- try {
- [$fixed_type_tokens, $function_template_types] = self::getConditionalSanitizedTypeTokens(
- $docblock_return_type,
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases,
- $storage,
- $classlike_storage,
- $cased_function_id,
- $function_template_types
- );
-
- $storage->return_type = TypeParser::parseTokens(
- array_values($fixed_type_tokens),
- null,
- $function_template_types + $class_template_types,
- $type_aliases
- );
-
- $storage->return_type->setFromDocblock();
-
- if ($storage instanceof MethodStorage) {
- $storage->has_docblock_return_type = true;
- }
-
- if ($storage->signature_return_type) {
- $all_typehint_types_match = true;
- $signature_return_atomic_types = $storage->signature_return_type->getAtomicTypes();
-
- foreach ($storage->return_type->getAtomicTypes() as $key => $type) {
- if (isset($signature_return_atomic_types[$key])) {
- $type->from_docblock = false;
- } else {
- $all_typehint_types_match = false;
- }
- }
-
- if ($all_typehint_types_match) {
- $storage->return_type->from_docblock = false;
-
- if ($storage instanceof MethodStorage) {
- $storage->has_docblock_return_type = true;
- }
- }
-
- // if the signature type contains null, we add null into the final return type too
- if ($storage->signature_return_type->isNullable()
- && !$storage->return_type->isNullable()
- && !$storage->return_type->hasTemplate()
- && !$storage->return_type->hasConditional()
- ) {
- //don't add null to final type if signature type don't match the docblock type
- // however, we can't check for object types at this point (#6931), so we'll assume it's ok
- if ($storage->return_type->hasObjectType() ||
- UnionTypeComparator::isContainedBy(
- $codebase,
- $storage->return_type,
- $storage->signature_return_type
- )
- ) {
- $storage->return_type->addType(new TNull());
- }
- }
- }
-
- $storage->return_type->queueClassLikesForScanning($codebase, $file_storage);
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $cased_function_id,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
- }
-
- // we make sure we only add ignore flag for internal stubs if the config is set to true
- if ($docblock_info->ignore_nullable_return
- && $storage->return_type
- && ($codebase->config->ignore_internal_nullable_issues
- || !in_array($file_storage->file_path, $codebase->config->internal_stubs)
- )
- ) {
- $storage->return_type->ignore_nullable_issues = true;
- }
-
- // we make sure we only add ignore flag for internal stubs if the config is set to true
- if ($docblock_info->ignore_falsable_return
- && $storage->return_type
- && ($codebase->config->ignore_internal_falsable_issues
- || !in_array($file_storage->file_path, $codebase->config->internal_stubs)
- )
- ) {
- $storage->return_type->ignore_falsable_issues = true;
- }
-
- if ($stmt->returnsByRef() && $storage->return_type) {
- $storage->return_type->by_ref = true;
- }
-
- if ($docblock_info->return_type_line_number && !$fake_method) {
- $storage->return_type_location->setCommentLine($docblock_info->return_type_line_number);
- }
-
- $storage->return_type_description = $docblock_info->return_type_description;
- }
-
- private static function handleTaintFlow(
- FunctionDocblockComment $docblock_info,
- FunctionLikeStorage $storage
- ): void {
- if ($docblock_info->flows) {
- foreach ($docblock_info->flows as $flow) {
- $path_type = 'arg';
-
- $fancy_path_regex = '/-\(([a-z\-]+)\)->/';
-
- if (preg_match($fancy_path_regex, $flow, $matches)) {
- if (isset($matches[1])) {
- $path_type = $matches[1];
- }
-
- $flow = preg_replace($fancy_path_regex, '->', $flow);
- }
-
- $flow_parts = explode('->', $flow);
-
- if (isset($flow_parts[1]) && trim($flow_parts[1]) === 'return') {
- $source_param_string = trim($flow_parts[0]);
-
- if ($source_param_string[0] === '(' && substr($source_param_string, -1) === ')') {
- $source_params = preg_split('/, ?/', substr($source_param_string, 1, -1));
-
- foreach ($source_params as $source_param) {
- $source_param = substr($source_param, 1);
-
- foreach ($storage->params as $i => $param_storage) {
- if ($param_storage->name === $source_param) {
- $storage->return_source_params[$i] = $path_type;
- }
- }
- }
- }
- }
-
- if (isset($flow_parts[0]) && strpos(trim($flow_parts[0]), 'proxy') === 0) {
- $proxy_call = trim(substr($flow_parts[0], strlen('proxy')));
- [$fully_qualified_name, $source_param_string] = explode('(', $proxy_call, 2);
-
- if (!empty($fully_qualified_name) && !empty($source_param_string)) {
- $source_params = preg_split('/, ?/', substr($source_param_string, 0, -1)) ?: [];
- $call_params = [];
- foreach ($source_params as $source_param) {
- $source_param = substr($source_param, 1);
-
- foreach ($storage->params as $i => $param_storage) {
- if ($param_storage->name === $source_param) {
- $call_params[] = $i;
- }
- }
- }
-
- if ($storage->proxy_calls === null) {
- $storage->proxy_calls = [];
- }
-
- $storage->proxy_calls[] = [
- 'fqn' => $fully_qualified_name,
- 'params' => $call_params,
- 'return' => isset($flow_parts[1]) && trim($flow_parts[1]) === 'return'
- ];
- }
- }
- }
- }
- }
-
- /**
- * @param array<string, TypeAlias> $type_aliases
- * @param array<string, non-empty-array<string, Union>> $function_template_types
- * @param array<string, non-empty-array<string, Union>> $class_template_types
- */
- private static function handleRemovedTaint(
- Codebase $codebase,
- PhpParser\Node\FunctionLike $stmt,
- Aliases $aliases,
- string $removed_taint,
- array $function_template_types,
- array $class_template_types,
- array $type_aliases,
- FunctionLikeStorage $storage,
- ?ClassLikeStorage $classlike_storage,
- string $cased_function_id,
- FileStorage $file_storage,
- FileScanner $file_scanner
- ): void {
- try {
- [$fixed_type_tokens, $function_template_types] = self::getConditionalSanitizedTypeTokens(
- $removed_taint,
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases,
- $storage,
- $classlike_storage,
- $cased_function_id,
- $function_template_types
- );
-
- $removed_taint = TypeParser::parseTokens(
- array_values($fixed_type_tokens),
- null,
- $function_template_types + $class_template_types,
- $type_aliases
- );
-
- $removed_taint->queueClassLikesForScanning($codebase, $file_storage);
-
- $removed_taint_single = $removed_taint->getSingleAtomic();
-
- if (!$removed_taint_single instanceof TConditional) {
- throw new TypeParseTreeException('Escaped taint must be a conditional');
- }
-
- $storage->conditionally_removed_taints[] = $removed_taint;
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $cased_function_id,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
- }
- }
-
- /**
- * @param array<string, TypeAlias> $type_aliases
- * @param array<string, non-empty-array<string, Union>> $function_template_types
- * @param array<string, non-empty-array<string, Union>> $class_template_types
- */
- private static function handleAssertions(
- FunctionDocblockComment $docblock_info,
- FunctionLikeStorage $storage,
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- PhpParser\Node\FunctionLike $stmt,
- array $class_template_types,
- array $function_template_types,
- array $type_aliases,
- ?ClassLikeStorage $classlike_storage
- ): void {
- if ($docblock_info->assertions) {
- $storage->assertions = [];
-
- foreach ($docblock_info->assertions as $assertion) {
- $assertion_type_parts = self::getAssertionParts(
- $codebase,
- $file_scanner,
- $file_storage,
- $aliases,
- $stmt,
- $storage,
- $assertion['type'],
- $class_template_types,
- $function_template_types,
- $type_aliases,
- $classlike_storage && !$classlike_storage->is_trait ? $classlike_storage->name : null
- );
-
- if (!$assertion_type_parts) {
- continue;
- }
-
- foreach ($storage->params as $i => $param) {
- if ($param->name === $assertion['param_name']) {
- $storage->assertions[] = new Assertion(
- $i,
- [$assertion_type_parts]
- );
- continue 2;
- }
-
- if (strpos($assertion['param_name'], $param->name.'->') === 0) {
- $storage->assertions[] = new Assertion(
- str_replace($param->name, (string) $i, $assertion['param_name']),
- [$assertion_type_parts]
- );
- continue 2;
- }
- }
-
- $storage->assertions[] = new Assertion(
- (strpos($assertion['param_name'], '$') === false ? '$' : '') . $assertion['param_name'],
- [$assertion_type_parts]
- );
- }
- }
-
- if ($docblock_info->if_true_assertions) {
- $storage->if_true_assertions = [];
-
- foreach ($docblock_info->if_true_assertions as $assertion) {
- $assertion_type_parts = self::getAssertionParts(
- $codebase,
- $file_scanner,
- $file_storage,
- $aliases,
- $stmt,
- $storage,
- $assertion['type'],
- $class_template_types,
- $function_template_types,
- $type_aliases,
- $classlike_storage && !$classlike_storage->is_trait ? $classlike_storage->name : null
- );
-
- if (!$assertion_type_parts) {
- continue;
- }
-
- foreach ($storage->params as $i => $param) {
- if ($param->name === $assertion['param_name']) {
- $storage->if_true_assertions[] = new Assertion(
- $i,
- [$assertion_type_parts]
- );
- continue 2;
- }
-
- if (strpos($assertion['param_name'], $param->name.'->') === 0) {
- $storage->if_true_assertions[] = new Assertion(
- str_replace($param->name, (string) $i, $assertion['param_name']),
- [$assertion_type_parts]
- );
- continue 2;
- }
- }
-
- $storage->if_true_assertions[] = new Assertion(
- (strpos($assertion['param_name'], '$') === false ? '$' : '') . $assertion['param_name'],
- [$assertion_type_parts]
- );
- }
- }
-
- if ($docblock_info->if_false_assertions) {
- $storage->if_false_assertions = [];
-
- foreach ($docblock_info->if_false_assertions as $assertion) {
- $assertion_type_parts = self::getAssertionParts(
- $codebase,
- $file_scanner,
- $file_storage,
- $aliases,
- $stmt,
- $storage,
- $assertion['type'],
- $class_template_types,
- $function_template_types,
- $type_aliases,
- $classlike_storage && !$classlike_storage->is_trait ? $classlike_storage->name : null
- );
-
- if (!$assertion_type_parts) {
- continue;
- }
-
- foreach ($storage->params as $i => $param) {
- if ($param->name === $assertion['param_name']) {
- $storage->if_false_assertions[] = new Assertion(
- $i,
- [$assertion_type_parts]
- );
- continue 2;
- }
-
- if (strpos($assertion['param_name'], $param->name.'->') === 0) {
- $storage->if_false_assertions[] = new Assertion(
- str_replace($param->name, (string) $i, $assertion['param_name']),
- [$assertion_type_parts]
- );
- continue 2;
- }
- }
-
- $storage->if_false_assertions[] = new Assertion(
- (strpos($assertion['param_name'], '$') === false ? '$' : '') . $assertion['param_name'],
- [$assertion_type_parts]
- );
- }
- }
- }
-
- /**
- * @param array<string, TypeAlias> $type_aliases
- * @param array<string, array<string, Union>> $function_template_types
- * @param array<string, non-empty-array<string, Union>> $class_template_types
- * @param array{name:string, type:string, line_number: int} $docblock_param_out
- */
- private static function handleParamOut(
- array $docblock_param_out,
- Aliases $aliases,
- array $function_template_types,
- array $class_template_types,
- array $type_aliases,
- string $cased_function_id,
- FileScanner $file_scanner,
- PhpParser\Node\FunctionLike $stmt,
- FunctionLikeStorage $storage,
- Codebase $codebase,
- FileStorage $file_storage
- ): void {
- $param_name = substr($docblock_param_out['name'], 1);
-
- try {
- $out_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $docblock_param_out['type'],
- $aliases,
- $function_template_types + $class_template_types,
- $type_aliases
- ),
- null,
- $function_template_types + $class_template_types,
- $type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $cased_function_id,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
-
- return;
- }
-
- $out_type->queueClassLikesForScanning(
- $codebase,
- $file_storage,
- $storage->template_types ?: []
- );
-
- foreach ($storage->params as $param_storage) {
- if ($param_storage->name === $param_name) {
- $param_storage->out_type = $out_type;
- }
- }
- }
-
- /**
- * @param ?array<string, non-empty-array<string, Union>> $template_types
- * @param array<string, TypeAlias> $type_aliases
- * @return array<string, non-empty-array<string, Union>>
- */
- private static function handleTemplates(
- FunctionLikeStorage $storage,
- FunctionDocblockComment $docblock_info,
- Aliases $aliases,
- ?array $template_types,
- array $type_aliases,
- FileScanner $file_scanner,
- PhpParser\Node\FunctionLike $stmt,
- string $cased_function_id
- ): array {
- $storage->template_types = [];
-
- foreach ($docblock_info->templates as $template_map) {
- $template_name = $template_map[0];
-
- if ($template_map[1] !== null && $template_map[2] !== null) {
- if (trim($template_map[2])) {
- try {
- $template_type = TypeParser::parseTokens(
- TypeTokenizer::getFullyQualifiedTokens(
- $template_map[2],
- $aliases,
- $storage->template_types + ($template_types ?: []),
- $type_aliases
- ),
- null,
- $storage->template_types + ($template_types ?: []),
- $type_aliases
- );
- } catch (TypeParseTreeException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Template ' . $template_name . ' has invalid as type - ' . $e->getMessage(),
- new CodeLocation($file_scanner, $stmt, null, true)
- );
-
- $template_type = Type::getMixed();
- }
- } else {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Template ' . $template_name . ' missing as type',
- new CodeLocation($file_scanner, $stmt, null, true)
- );
-
- $template_type = Type::getMixed();
- }
- } else {
- $template_type = Type::getMixed();
- }
-
- if (isset($template_types[$template_name])) {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Duplicate template param ' . $template_name . ' in docblock for '
- . $cased_function_id,
- new CodeLocation($file_scanner, $stmt, null, true)
- );
- } else {
- $storage->template_types[$template_name] = [
- 'fn-' . strtolower($cased_function_id) => $template_type,
- ];
- }
- }
-
- return array_merge($template_types ?: [], $storage->template_types);
- }
-
- private static function handleUnexpectedTags(
- FunctionDocblockComment $docblock_info,
- FunctionLikeStorage $storage,
- PhpParser\Node\FunctionLike $stmt,
- FileScanner $file_scanner,
- string $cased_function_id
- ): void {
- foreach ($docblock_info->unexpected_tags as $tag => $details) {
- foreach ($details['lines'] as $line) {
- $tag_location = new CodeLocation($file_scanner, $stmt, null, true);
- $tag_location->setCommentLine($line);
-
- $message = 'Docblock tag @' . $tag . ' is not recognized in the function docblock '
- . 'for ' . $cased_function_id;
-
- if (isset($details['suggested_replacement'])) {
- $message .= ', did you mean to use @' . $details['suggested_replacement'] . '?';
- }
-
- $storage->docblock_issues[] = new PossiblyInvalidDocblockTag($message, $tag_location);
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php
deleted file mode 100644
index cc10139..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeNodeScanner.php
+++ /dev/null
@@ -1,1151 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use LogicException;
-use PhpParser;
-use PhpParser\Node\Identifier;
-use PhpParser\Node\Name;
-use PhpParser\Node\NullableType;
-use PhpParser\Node\Stmt\Class_;
-use PhpParser\Node\UnionType;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Config;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\IncorrectDocblockException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\NamespaceAnalyzer;
-use Psalm\Internal\Analyzer\ScopeAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\Internal\Type\TypeAlias;
-use Psalm\Issue\DuplicateFunction;
-use Psalm\Issue\DuplicateMethod;
-use Psalm\Issue\DuplicateParam;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\MissingDocblockType;
-use Psalm\Issue\ParseError;
-use Psalm\IssueBuffer;
-use Psalm\Storage\Assertion;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Storage\FunctionStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Storage\PropertyStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-use ReflectionFunction;
-use UnexpectedValueException;
-
-use function array_keys;
-use function array_merge;
-use function array_pop;
-use function array_search;
-use function count;
-use function end;
-use function explode;
-use function implode;
-use function in_array;
-use function is_string;
-use function spl_object_id;
-use function strpos;
-use function strtolower;
-
-class FunctionLikeNodeScanner
-{
- /**
- * @var FileScanner
- */
- private $file_scanner;
-
- /**
- * @var Codebase
- */
- private $codebase;
-
- /**
- * @var string
- */
- private $file_path;
-
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var FileStorage
- */
- private $file_storage;
-
- /**
- * @var ?ClassLikeStorage
- */
- private $classlike_storage;
-
- /**
- * @var array<string, non-empty-array<string, Union>>
- */
- private $existing_function_template_types;
-
- /**
- * @var Aliases
- */
- private $aliases;
-
- /**
- * @var array<string, TypeAlias>
- */
- private $type_aliases;
-
- /**
- * @var ?FunctionLikeStorage
- */
- public $storage;
-
- /**
- * @param array<string, non-empty-array<string, Union>> $existing_function_template_types
- * @param array<string, TypeAlias> $type_aliases
- */
- public function __construct(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage,
- Aliases $aliases,
- array $type_aliases,
- ?ClassLikeStorage $classlike_storage,
- array $existing_function_template_types
- ) {
- $this->codebase = $codebase;
- $this->file_storage = $file_storage;
- $this->file_scanner = $file_scanner;
- $this->file_path = $file_storage->file_path;
- $this->aliases = $aliases;
- $this->type_aliases = $type_aliases;
- $this->config = Config::getInstance();
- $this->classlike_storage = $classlike_storage;
- $this->existing_function_template_types = $existing_function_template_types;
- }
-
- /**
- * @param bool $fake_method in the case of @method annotations we do something a little strange
- *
- * @return FunctionStorage|MethodStorage|false
- */
- public function start(PhpParser\Node\FunctionLike $stmt, bool $fake_method = false)
- {
- if ($stmt instanceof PhpParser\Node\Expr\Closure
- || $stmt instanceof PhpParser\Node\Expr\ArrowFunction
- ) {
- $this->codebase->scanner->queueClassLikeForScanning('Closure');
- }
-
- $functionlike_info = $this->createStorageForFunctionLike($stmt, $fake_method);
-
- if ($functionlike_info === false) {
- return false;
- }
-
- [
- $cased_function_id,
- $storage,
- $function_id,
- $fq_classlike_name,
- $method_name_lc,
- $classlike_storage,
- $is_functionlike_override,
- $method_id,
- $is_dupe
- ] = $functionlike_info;
-
- if ($is_dupe) {
- return $storage;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
- $storage->cased_name = $stmt->name->name;
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Function_) {
- $storage->cased_name =
- ($this->aliases->namespace ? $this->aliases->namespace . '\\' : '') . $stmt->name->name;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod || $stmt instanceof PhpParser\Node\Stmt\Function_) {
- $storage->location = new CodeLocation($this->file_scanner, $stmt->name, null, true);
- } else {
- $storage->location = new CodeLocation($this->file_scanner, $stmt, null, true);
- }
-
- $storage->stmt_location = new CodeLocation($this->file_scanner, $stmt);
-
- $required_param_count = 0;
- $i = 0;
- $has_optional_param = false;
-
- $existing_params = [];
- $storage->setParams([]);
-
- foreach ($stmt->getParams() as $param) {
- if ($param->var instanceof PhpParser\Node\Expr\Error) {
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Param' . ($i + 1) . ' of ' . $cased_function_id . ' has invalid syntax',
- new CodeLocation($this->file_scanner, $param, null, true)
- );
-
- ++$i;
-
- continue;
- }
-
- $param_storage = $this->getTranslatedFunctionParam($param, $stmt, $fake_method, $fq_classlike_name);
-
- foreach ($param->attrGroups as $attr_group) {
- foreach ($attr_group->attrs as $attr) {
- $param_storage->attributes[] = AttributeResolver::resolve(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $attr,
- $this->classlike_storage->name ?? null
- );
- }
- }
-
- if ($param_storage->name === 'haystack'
- && in_array($this->file_path, $this->codebase->config->internal_stubs)
- ) {
- $param_storage->expect_variable = true;
- }
-
- if (isset($existing_params['$' . $param_storage->name])) {
- $storage->docblock_issues[] = new DuplicateParam(
- 'Duplicate param $' . $param_storage->name . ' in docblock for ' . $cased_function_id,
- new CodeLocation($this->file_scanner, $param, null, true)
- );
-
- ++$i;
-
- continue;
- }
-
- $existing_params['$' . $param_storage->name] = $i;
- $storage->addParam($param_storage, (bool)$param->type);
-
- if (!$param_storage->is_optional && !$param_storage->is_variadic) {
- $required_param_count = $i + 1;
-
- if (!$param->variadic
- && $has_optional_param
- ) {
- foreach ($storage->params as $param) {
- $param->is_optional = false;
- }
- }
- } else {
- $has_optional_param = true;
- }
-
- ++$i;
- }
-
- $storage->required_param_count = $required_param_count;
-
- if ($stmt instanceof PhpParser\Node\Stmt\Function_
- || $stmt instanceof PhpParser\Node\Stmt\ClassMethod
- ) {
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
- && $storage instanceof MethodStorage
- && $classlike_storage
- && !$classlike_storage->mutation_free
- && $stmt->stmts
- && count($stmt->stmts) === 1
- && !count($stmt->params)
- && $stmt->stmts[0] instanceof PhpParser\Node\Stmt\Return_
- && $stmt->stmts[0]->expr instanceof PhpParser\Node\Expr\PropertyFetch
- && $stmt->stmts[0]->expr->var instanceof PhpParser\Node\Expr\Variable
- && $stmt->stmts[0]->expr->var->name === 'this'
- && $stmt->stmts[0]->expr->name instanceof PhpParser\Node\Identifier
- ) {
- $property_name = $stmt->stmts[0]->expr->name->name;
-
- if (isset($classlike_storage->properties[$property_name])
- && $classlike_storage->properties[$property_name]->type
- ) {
- $storage->mutation_free = true;
- $storage->external_mutation_free = true;
- $storage->mutation_free_inferred = !$stmt->isFinal() && !$classlike_storage->final;
-
- $classlike_storage->properties[$property_name]->getter_method = strtolower($stmt->name->name);
- }
- } elseif (strpos($stmt->name->name, 'assert') === 0
- && $stmt->stmts
- ) {
- $var_assertions = [];
-
- foreach ($stmt->stmts as $function_stmt) {
- if ($function_stmt instanceof PhpParser\Node\Stmt\If_) {
- $final_actions = ScopeAnalyzer::getControlActions(
- $function_stmt->stmts,
- null,
- $this->config->exit_functions,
- [],
- false
- );
-
- if ($final_actions !== [ScopeAnalyzer::ACTION_END]) {
- $var_assertions = [];
- break;
- }
-
- $cond_id = spl_object_id($function_stmt->cond);
-
- $if_clauses = FormulaGenerator::getFormula(
- $cond_id,
- $cond_id,
- $function_stmt->cond,
- $this->classlike_storage->name ?? null,
- $this->file_scanner,
- null
- );
-
- try {
- $negated_formula = Algebra::negateFormula($if_clauses);
- } catch (ComplicatedExpressionException $e) {
- $var_assertions = [];
- break;
- }
-
- $rules = Algebra::getTruthsFromFormula($negated_formula);
-
- if (!$rules) {
- $var_assertions = [];
- break;
- }
-
- foreach ($rules as $var_id => $rule) {
- foreach ($rule as $rule_part) {
- if (count($rule_part) > 1) {
- continue 2;
- }
- }
-
- if (isset($existing_params[$var_id])) {
- $param_offset = $existing_params[$var_id];
-
- $var_assertions[] = new Assertion(
- $param_offset,
- $rule
- );
- } elseif (strpos($var_id, '$this->') === 0) {
- $var_assertions[] = new Assertion(
- $var_id,
- $rule
- );
- }
- }
- } else {
- $var_assertions = [];
- break;
- }
- }
-
- $storage->assertions = $var_assertions;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
- && $stmt->stmts
- && $storage instanceof MethodStorage
- ) {
- $last_stmt = end($stmt->stmts);
-
- if ($last_stmt instanceof PhpParser\Node\Stmt\Return_
- && $last_stmt->expr instanceof PhpParser\Node\Expr\Variable
- && $last_stmt->expr->name === 'this'
- ) {
- $storage->probably_fluent = true;
- }
- }
- }
-
- if (!$this->file_scanner->will_analyze
- && ($stmt instanceof PhpParser\Node\Stmt\Function_
- || $stmt instanceof PhpParser\Node\Stmt\ClassMethod
- || $stmt instanceof PhpParser\Node\Expr\Closure)
- && $stmt->stmts
- ) {
- // pick up func_get_args that would otherwise be missed
- foreach ($stmt->stmts as $function_stmt) {
- if ($function_stmt instanceof PhpParser\Node\Stmt\Expression
- && $function_stmt->expr instanceof PhpParser\Node\Expr\Assign
- && $function_stmt->expr->expr instanceof PhpParser\Node\Expr\FuncCall
- && $function_stmt->expr->expr->name instanceof PhpParser\Node\Name
- ) {
- $inner_function_id = implode('\\', $function_stmt->expr->expr->name->parts);
-
- if ($inner_function_id === 'func_get_arg'
- || $inner_function_id === 'func_get_args'
- || $inner_function_id === 'func_num_args'
- ) {
- $storage->variadic = true;
- }
- } elseif ($function_stmt instanceof PhpParser\Node\Stmt\If_
- && $function_stmt->cond instanceof PhpParser\Node\Expr\BinaryOp
- && $function_stmt->cond->left instanceof PhpParser\Node\Expr\BinaryOp\Equal
- && $function_stmt->cond->left->left instanceof PhpParser\Node\Expr\FuncCall
- && $function_stmt->cond->left->left->name instanceof PhpParser\Node\Name
- ) {
- $inner_function_id = implode('\\', $function_stmt->cond->left->left->name->parts);
-
- if ($inner_function_id === 'func_get_arg'
- || $inner_function_id === 'func_get_args'
- || $inner_function_id === 'func_num_args'
- ) {
- $storage->variadic = true;
- }
- }
- }
- }
-
- $parser_return_type = $stmt->getReturnType();
-
- if ($parser_return_type) {
- $original_type = $parser_return_type;
- if ($original_type instanceof PhpParser\Node\IntersectionType) {
- throw new UnexpectedValueException('Intersection types not yet supported');
- }
- /** @var Identifier|Name|NullableType|UnionType $original_type */
-
- $storage->return_type = TypeHintResolver::resolve(
- $original_type,
- $this->codebase->scanner,
- $this->file_storage,
- $this->classlike_storage,
- $this->aliases,
- $this->codebase->php_major_version,
- $this->codebase->php_minor_version
- );
-
- $storage->return_type_location = new CodeLocation(
- $this->file_scanner,
- $original_type
- );
-
- if ($stmt->returnsByRef()) {
- $storage->return_type->by_ref = true;
- }
-
- $storage->signature_return_type = $storage->return_type;
- $storage->signature_return_type_location = $storage->return_type_location;
- }
-
- if ($stmt->returnsByRef()) {
- $storage->returns_by_ref = true;
- }
-
- $doc_comment = $stmt->getDocComment();
-
-
- if ($classlike_storage && !$classlike_storage->is_trait) {
- $storage->internal = array_merge($classlike_storage->internal, $storage->internal);
- }
-
- if ($doc_comment) {
- try {
- $code_location = new CodeLocation($this->file_scanner, $stmt, null, true);
- $docblock_info = FunctionLikeDocblockParser::parse($doc_comment, $code_location, $cased_function_id);
- } catch (IncorrectDocblockException $e) {
- $storage->docblock_issues[] = new MissingDocblockType(
- $e->getMessage() . ' in docblock for ' . $cased_function_id,
- new CodeLocation($this->file_scanner, $stmt, null, true)
- );
-
- $docblock_info = null;
- } catch (DocblockParseException $e) {
- $storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage() . ' in docblock for ' . $cased_function_id,
- new CodeLocation($this->file_scanner, $stmt, null, true)
- );
-
- $docblock_info = null;
- }
-
- if ($docblock_info) {
- if ($docblock_info->since_php_major_version && !$this->aliases->namespace) {
- if ($docblock_info->since_php_major_version > $this->codebase->php_major_version) {
- return false;
- }
-
- if ($docblock_info->since_php_major_version === $this->codebase->php_major_version
- && $docblock_info->since_php_minor_version > $this->codebase->php_minor_version
- ) {
- return false;
- }
- }
-
- if ($stmt instanceof PhpParser\Node\Expr\Closure
- || $stmt instanceof PhpParser\Node\Expr\ArrowFunction
- ) {
- if ($docblock_info->templates !== []) {
- $docblock_info->templates = [];
- $storage->docblock_issues[] = new InvalidDocblock(
- 'Templated closures are not supported',
- new CodeLocation($this->file_scanner, $stmt, null, true)
- );
- }
- }
-
- FunctionLikeDocblockScanner::addDocblockInfo(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $this->type_aliases,
- $this->classlike_storage,
- $this->existing_function_template_types,
- $storage,
- $stmt,
- $docblock_info,
- $is_functionlike_override,
- $fake_method,
- $cased_function_id
- );
- }
- }
-
- // register the functionlike once the @since check has been completed
- if ($stmt instanceof PhpParser\Node\Stmt\Function_
- && $function_id
- && $storage instanceof FunctionStorage
- ) {
- if ($this->codebase->register_stub_files
- || ($this->codebase->register_autoload_files
- && !$this->codebase->functions->hasStubbedFunction($function_id))
- ) {
- $this->codebase->functions->addGlobalFunction($function_id, $storage);
- }
-
- $this->file_storage->functions[$function_id] = $storage;
- $this->file_storage->declaring_function_ids[$function_id] = strtolower($this->file_path);
- } elseif ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
- && $classlike_storage
- && $storage instanceof MethodStorage
- && $method_name_lc
- && !$fake_method
- && $method_id
- ) {
- $classlike_storage->methods[$method_name_lc] = $storage;
-
- $classlike_storage->declaring_method_ids[$method_name_lc]
- = $classlike_storage->appearing_method_ids[$method_name_lc]
- = $method_id;
-
- if (!$stmt->isPrivate() || $method_name_lc === '__construct' || $classlike_storage->is_trait) {
- $classlike_storage->inheritable_method_ids[$method_name_lc] = $method_id;
- }
-
- if (!isset($classlike_storage->overridden_method_ids[$method_name_lc])) {
- $classlike_storage->overridden_method_ids[$method_name_lc] = [];
- }
-
- if ($storage->final && $method_name_lc === '__construct') {
- // a bit of a hack, but makes sure that `new static` works for these classes
- $classlike_storage->preserve_constructor_signature = true;
- }
- } elseif (($stmt instanceof PhpParser\Node\Expr\Closure
- || $stmt instanceof PhpParser\Node\Expr\ArrowFunction)
- && $function_id
- && $storage instanceof FunctionStorage
- ) {
- $this->file_storage->functions[$function_id] = $storage;
- }
-
- if ($classlike_storage && $method_name_lc === '__construct') {
- foreach ($stmt->getParams() as $param) {
- if (!$param->flags || !$param->var instanceof PhpParser\Node\Expr\Variable) {
- continue;
- }
-
- $param_storage = null;
-
- foreach ($storage->params as $param_storage) {
- if ($param_storage->name === $param->var->name) {
- break;
- }
- }
-
- if (!$param_storage) {
- continue;
- }
-
- if (isset($classlike_storage->properties[$param_storage->name]) && $param_storage->location) {
- IssueBuffer::maybeAdd(
- new ParseError(
- 'Promoted property ' . $param_storage->name . ' clashes with an existing property',
- $param_storage->location
- )
- );
-
- $storage->has_visitor_issues = true;
- $this->file_storage->has_visitor_issues = true;
- continue;
- }
-
- $doc_comment = $param->getDocComment();
- $var_comment_type = null;
- $var_comment_readonly = false;
- $var_comment_allow_private_mutation = false;
- if ($doc_comment) {
- $var_comments = CommentAnalyzer::getTypeFromComment(
- $doc_comment,
- $this->file_scanner,
- $this->aliases,
- $this->existing_function_template_types ?: [],
- $this->type_aliases
- );
-
- $var_comment = array_pop($var_comments);
-
- if ($var_comment !== null) {
- $var_comment_type = $var_comment->type;
- $var_comment_readonly = $var_comment->readonly;
- $var_comment_allow_private_mutation = $var_comment->allow_private_mutation;
- }
- }
-
- //both way to document type were used
- if ($param_storage->type && $param_storage->type->from_docblock && $var_comment_type) {
- if (IssueBuffer::accepts(
- new InvalidDocblock(
- 'Param ' . $param_storage->name . ' of ' . $cased_function_id .
- ' should be documented as a param or a property, not both',
- new CodeLocation($this->file_scanner, $param, null, true)
- )
- )) {
- return false;
- }
- }
-
- //no docblock type was provided for param but we have one for property
- if ($var_comment_type) {
- $param_storage->type = $var_comment_type;
- }
-
- $property_storage = $classlike_storage->properties[$param_storage->name] = new PropertyStorage();
- $property_storage->is_static = false;
- $property_storage->type = $param_storage->type;
- $property_storage->signature_type = $param_storage->signature_type;
- $property_storage->signature_type_location = $param_storage->signature_type_location;
- $property_storage->type_location = $param_storage->type_location;
- $property_storage->location = $param_storage->location;
- $property_storage->stmt_location = new CodeLocation($this->file_scanner, $param);
- $property_storage->has_default = (bool)$param->default;
- $param_type_readonly = (bool)($param->flags & PhpParser\Node\Stmt\Class_::MODIFIER_READONLY);
- $property_storage->readonly = $param_type_readonly ?: $var_comment_readonly;
- $property_storage->allow_private_mutation = $var_comment_allow_private_mutation;
- $param_storage->promoted_property = true;
- $property_storage->is_promoted = true;
-
- $property_id = $fq_classlike_name . '::$' . $param_storage->name;
-
- switch ($param->flags & Class_::VISIBILITY_MODIFIER_MASK) {
- case Class_::MODIFIER_PUBLIC:
- $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- $classlike_storage->inheritable_property_ids[$param_storage->name] = $property_id;
- break;
-
- case Class_::MODIFIER_PROTECTED:
- $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PROTECTED;
- $classlike_storage->inheritable_property_ids[$param_storage->name] = $property_id;
- break;
-
- case Class_::MODIFIER_PRIVATE:
- $property_storage->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE;
- break;
- }
-
- $fq_classlike_name = $classlike_storage->name;
-
- $property_id = $fq_classlike_name . '::$' . $param_storage->name;
-
- $classlike_storage->declaring_property_ids[$param_storage->name] = $fq_classlike_name;
- $classlike_storage->appearing_property_ids[$param_storage->name] = $property_id;
- $classlike_storage->initialized_properties[$param_storage->name] = true;
- }
-
- if ($stmt instanceof PhpParser\Node\Stmt\ClassMethod
- && $storage instanceof MethodStorage
- && $storage->params
- && $this->config->infer_property_types_from_constructor
- ) {
- $this->inferPropertyTypeFromConstructor($stmt, $storage, $classlike_storage);
- }
- }
-
- foreach ($stmt->getAttrGroups() as $attr_group) {
- foreach ($attr_group->attrs as $attr) {
- $attribute = AttributeResolver::resolve(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $attr,
- $this->classlike_storage->name ?? null
- );
-
- if ($attribute->fq_class_name === 'Psalm\\Pure'
- || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Pure'
- ) {
- $storage->specialize_call = true;
- $storage->mutation_free = true;
- if ($storage instanceof MethodStorage) {
- $storage->external_mutation_free = true;
- }
- }
-
- if ($attribute->fq_class_name === 'Psalm\\Deprecated'
- || $attribute->fq_class_name === 'JetBrains\\PhpStorm\\Deprecated'
- ) {
- $storage->deprecated = true;
- }
-
- if ($attribute->fq_class_name === 'Psalm\\Internal' && !$storage->internal && $fq_classlike_name) {
- $storage->internal = [NamespaceAnalyzer::getNameSpaceRoot($fq_classlike_name)];
- }
-
- if ($attribute->fq_class_name === 'Psalm\\ExternalMutationFree'
- && $storage instanceof MethodStorage
- ) {
- $storage->external_mutation_free = true;
- }
-
- if ($attribute->fq_class_name === 'JetBrains\\PhpStorm\\NoReturn') {
- $storage->return_type = new Union([new TNever()]);
- }
-
- $storage->attributes[] = $attribute;
- }
- }
-
- return $storage;
- }
-
- private function inferPropertyTypeFromConstructor(
- PhpParser\Node\Stmt\ClassMethod $stmt,
- MethodStorage $storage,
- ClassLikeStorage $classlike_storage
- ): void {
- if (!$stmt->stmts) {
- return;
- }
-
- $assigned_properties = [];
-
- foreach ($stmt->stmts as $function_stmt) {
- if ($function_stmt instanceof PhpParser\Node\Stmt\Expression
- && $function_stmt->expr instanceof PhpParser\Node\Expr\Assign
- && $function_stmt->expr->var instanceof PhpParser\Node\Expr\PropertyFetch
- && $function_stmt->expr->var->var instanceof PhpParser\Node\Expr\Variable
- && $function_stmt->expr->var->var->name === 'this'
- && $function_stmt->expr->var->name instanceof PhpParser\Node\Identifier
- && ($property_name = $function_stmt->expr->var->name->name)
- && isset($classlike_storage->properties[$property_name])
- && $function_stmt->expr->expr instanceof PhpParser\Node\Expr\Variable
- && is_string($function_stmt->expr->expr->name)
- && ($param_name = $function_stmt->expr->expr->name)
- && isset($storage->param_lookup[$param_name])
- ) {
- if ($classlike_storage->properties[$property_name]->type
- || !$storage->param_lookup[$param_name]
- ) {
- continue;
- }
-
- $param_index = array_search($param_name, array_keys($storage->param_lookup), true);
-
- if ($param_index === false || !isset($storage->params[$param_index]->type)) {
- continue;
- }
-
- $param_type = $storage->params[$param_index]->type;
-
- $assigned_properties[$property_name] =
- $storage->params[$param_index]->is_variadic
- ? new Union([
- new TArray([
- Type::getInt(),
- $param_type,
- ]),
- ])
- : $param_type;
- } else {
- $assigned_properties = [];
- break;
- }
- }
-
- if (!$assigned_properties) {
- return;
- }
-
- $storage->external_mutation_free = true;
-
- foreach ($assigned_properties as $property_name => $property_type) {
- $classlike_storage->properties[$property_name]->type = clone $property_type;
- }
- }
-
- private function getTranslatedFunctionParam(
- PhpParser\Node\Param $param,
- PhpParser\Node\FunctionLike $stmt,
- bool $fake_method,
- ?string $fq_classlike_name
- ): FunctionLikeParameter {
- $param_type = null;
-
- $is_nullable = $param->default instanceof PhpParser\Node\Expr\ConstFetch &&
- strtolower($param->default->name->parts[0]) === 'null';
-
- $param_typehint = $param->type;
-
- if ($param_typehint) {
- if ($param_typehint instanceof PhpParser\Node\IntersectionType) {
- throw new UnexpectedValueException('Intersection types not yet supported');
- }
- /** @var Identifier|Name|NullableType|UnionType $param_typehint */
-
- $param_type = TypeHintResolver::resolve(
- $param_typehint,
- $this->codebase->scanner,
- $this->file_storage,
- $this->classlike_storage,
- $this->aliases,
- $this->codebase->php_major_version,
- $this->codebase->php_minor_version
- );
-
- if ($is_nullable) {
- $param_type->addType(new TNull);
- } else {
- $is_nullable = $param_type->isNullable();
- }
- }
-
- $is_optional = $param->default !== null;
-
- if ($param->var instanceof PhpParser\Node\Expr\Error || !is_string($param->var->name)) {
- throw new UnexpectedValueException('Not expecting param name to be non-string');
- }
-
- $default_type = null;
-
- if ($param->default) {
- $default_type = SimpleTypeInferer::infer(
- $this->codebase,
- new NodeDataProvider(),
- $param->default,
- $this->aliases,
- null,
- null,
- $fq_classlike_name
- );
-
- if (!$default_type) {
- $default_type = ExpressionResolver::getUnresolvedClassConstExpr(
- $param->default,
- $this->aliases,
- $fq_classlike_name
- );
- }
- }
-
- return new FunctionLikeParameter(
- $param->var->name,
- $param->byRef,
- $param_type,
- new CodeLocation(
- $this->file_scanner,
- $fake_method ? $stmt : $param->var,
- null,
- false,
- !$fake_method
- ? CodeLocation::FUNCTION_PARAM_VAR
- : CodeLocation::FUNCTION_PHPDOC_METHOD
- ),
- $param_typehint
- ? new CodeLocation(
- $this->file_scanner,
- $fake_method ? $stmt : $param,
- null,
- false,
- CodeLocation::FUNCTION_PARAM_TYPE
- )
- : null,
- $is_optional,
- $is_nullable,
- $param->variadic,
- $default_type
- );
- }
-
- /**
- * @return array{
- * string,
- * FunctionStorage|MethodStorage,
- * null|string,
- * null|string,
- * null|lowercase-string,
- * ClassLikeStorage|null,
- * bool,
- * MethodIdentifier|null,
- * bool
- * }|false
- */
- private function createStorageForFunctionLike(
- PhpParser\Node\FunctionLike $stmt,
- bool $fake_method
- ) {
- $classlike_storage = null;
- $fq_classlike_name = null;
- $is_functionlike_override = false;
-
- $function_id = null;
- $method_name_lc = null;
- $method_id = null;
-
- if ($fake_method && $stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
- $cased_function_id = '@method ' . $stmt->name->name;
-
- $storage = $this->storage = new MethodStorage();
- $storage->defining_fqcln = '';
- $storage->is_static = $stmt->isStatic();
- $storage->final = $this->classlike_storage && $this->classlike_storage->final;
- $storage->final_from_docblock = $this->classlike_storage && $this->classlike_storage->final_from_docblock;
- } elseif ($stmt instanceof PhpParser\Node\Stmt\Function_) {
- $cased_function_id =
- ($this->aliases->namespace ? $this->aliases->namespace . '\\' : '') . $stmt->name->name;
- $function_id = strtolower($cased_function_id);
-
- $storage = $this->storage = new FunctionStorage();
-
- if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
- if (isset($this->file_storage->functions[$function_id])
- && ($this->codebase->register_stub_files
- || !$this->codebase->functions->hasStubbedFunction($function_id))
- ) {
- $this->codebase->functions->addGlobalFunction(
- $function_id,
- $this->file_storage->functions[$function_id]
- );
-
- $storage = $this->storage = $this->file_storage->functions[$function_id];
-
- return [$function_id, $storage, null, null, null, null, false, null, true];
- }
- } else {
- if (isset($this->file_storage->functions[$function_id])) {
- $duplicate_function_storage = $this->file_storage->functions[$function_id];
-
- if ($duplicate_function_storage->location
- && $duplicate_function_storage->location->getLineNumber() === $stmt->getLine()
- ) {
- $storage = $this->storage = $this->file_storage->functions[$function_id];
-
- return [$function_id, $storage, null, null, null, null, false, null, true];
- }
-
- IssueBuffer::maybeAdd(
- new DuplicateFunction(
- 'Method ' . $function_id . ' has already been defined'
- . ($duplicate_function_storage->location
- ? ' in ' . $duplicate_function_storage->location->file_path
- : ''),
- new CodeLocation($this->file_scanner, $stmt, null, true)
- )
- );
-
- $this->file_storage->has_visitor_issues = true;
-
- $duplicate_function_storage->has_visitor_issues = true;
-
- $storage = $this->storage = $this->file_storage->functions[$function_id];
-
- return [$function_id, $storage, null, null, null, null, false, null, true];
- }
-
- if (isset($this->config->getPredefinedFunctions()[$function_id])) {
- /** @psalm-suppress ArgumentTypeCoercion */
- $reflection_function = new ReflectionFunction($function_id);
-
- if ($reflection_function->getFileName() !== $this->file_path) {
- IssueBuffer::maybeAdd(
- new DuplicateFunction(
- 'Method ' . $function_id . ' has already been defined as a core function',
- new CodeLocation($this->file_scanner, $stmt, null, true)
- )
- );
- }
- }
- }
- } elseif ($stmt instanceof PhpParser\Node\Stmt\ClassMethod) {
- if (!$this->classlike_storage) {
- throw new LogicException('$this->classlike_storage should not be null');
- }
-
- $fq_classlike_name = $this->classlike_storage->name;
-
- $method_name_lc = strtolower($stmt->name->name);
-
- $function_id = $fq_classlike_name . '::' . $method_name_lc;
- $cased_function_id = $fq_classlike_name . '::' . $stmt->name->name;
-
- $classlike_storage = $this->classlike_storage;
-
- $storage = null;
-
- if (isset($classlike_storage->methods[$method_name_lc])) {
- if (!$this->codebase->register_stub_files) {
- $duplicate_method_storage = $classlike_storage->methods[$method_name_lc];
-
- IssueBuffer::maybeAdd(
- new DuplicateMethod(
- 'Method ' . $function_id . ' has already been defined'
- . ($duplicate_method_storage->location
- ? ' in ' . $duplicate_method_storage->location->file_path
- : ''),
- new CodeLocation($this->file_scanner, $stmt, null, true)
- )
- );
-
- $this->file_storage->has_visitor_issues = true;
-
- $duplicate_method_storage->has_visitor_issues = true;
-
- return false;
- }
-
- // skip methods based on @since docblock tag
- $doc_comment = $stmt->getDocComment();
-
- if ($doc_comment) {
- $docblock_info = null;
- try {
- $code_location = new CodeLocation($this->file_scanner, $stmt, null, true);
- $docblock_info = FunctionLikeDocblockParser::parse(
- $doc_comment,
- $code_location,
- $cased_function_id
- );
- } catch (IncorrectDocblockException|DocblockParseException $e) {
- }
- if ($docblock_info) {
- if ($docblock_info->since_php_major_version && !$this->aliases->namespace) {
- if ($docblock_info->since_php_major_version > $this->codebase->php_major_version) {
- return false;
- }
- if ($docblock_info->since_php_major_version === $this->codebase->php_major_version
- && $docblock_info->since_php_minor_version > $this->codebase->php_minor_version
- ) {
- return false;
- }
- }
- }
- }
-
- $is_functionlike_override = true;
- $storage = $this->storage = $classlike_storage->methods[$method_name_lc];
- }
-
- if (!$storage) {
- $storage = $this->storage = new MethodStorage();
- }
-
- $storage->stubbed = $this->codebase->register_stub_files;
- $storage->defining_fqcln = $fq_classlike_name;
-
- $class_name_parts = explode('\\', $fq_classlike_name);
- $class_name = array_pop($class_name_parts);
-
- if ($method_name_lc === strtolower($class_name)
- && !isset($classlike_storage->methods['__construct'])
- && strpos($fq_classlike_name, '\\') === false
- && $this->codebase->php_major_version < 8
- ) {
- $this->codebase->methods->setDeclaringMethodId(
- $fq_classlike_name,
- '__construct',
- $fq_classlike_name,
- $method_name_lc
- );
-
- $this->codebase->methods->setAppearingMethodId(
- $fq_classlike_name,
- '__construct',
- $fq_classlike_name,
- $method_name_lc
- );
- }
-
- $method_id = new MethodIdentifier(
- $fq_classlike_name,
- $method_name_lc
- );
-
- $storage->is_static = $stmt->isStatic();
- $storage->abstract = $stmt->isAbstract();
-
- $storage->final = $classlike_storage->final || $stmt->isFinal();
- $storage->final_from_docblock = $classlike_storage->final_from_docblock;
-
- if ($stmt->isPrivate()) {
- $storage->visibility = ClassLikeAnalyzer::VISIBILITY_PRIVATE;
- } elseif ($stmt->isProtected()) {
- $storage->visibility = ClassLikeAnalyzer::VISIBILITY_PROTECTED;
- } else {
- $storage->visibility = ClassLikeAnalyzer::VISIBILITY_PUBLIC;
- }
- } elseif ($stmt instanceof PhpParser\Node\Expr\Closure
- || $stmt instanceof PhpParser\Node\Expr\ArrowFunction
- ) {
- $function_id = $cased_function_id = strtolower($this->file_path)
- . ':' . $stmt->getLine()
- . ':' . (int)$stmt->getAttribute('startFilePos') . ':-:closure';
-
- $storage = $this->storage = $this->file_storage->functions[$function_id] = new FunctionStorage();
-
- if ($stmt instanceof PhpParser\Node\Expr\Closure) {
- foreach ($stmt->uses as $closure_use) {
- if ($closure_use->byRef && is_string($closure_use->var->name)) {
- $storage->byref_uses[$closure_use->var->name] = true;
- }
- }
- }
- } else {
- throw new UnexpectedValueException('Unrecognized functionlike');
- }
-
- return [
- $cased_function_id,
- $storage,
- $function_id,
- $fq_classlike_name,
- $method_name_lc,
- $classlike_storage,
- $is_functionlike_override,
- $method_id,
- false
- ];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php
deleted file mode 100644
index 3dfb085..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/TypeHintResolver.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor\Reflector;
-
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Codebase\Scanner as CodebaseScanner;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Storage\FileStorage;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function implode;
-use function strtolower;
-
-class TypeHintResolver
-{
- /**
- * @param PhpParser\Node\Identifier|PhpParser\Node\Name|PhpParser\Node\NullableType|PhpParser\Node\UnionType $hint
- */
- public static function resolve(
- PhpParser\NodeAbstract $hint,
- CodebaseScanner $scanner,
- FileStorage $file_storage,
- ?ClassLikeStorage $classlike_storage,
- Aliases $aliases,
- int $php_major_version,
- int $php_minor_version
- ): Union {
- if ($hint instanceof PhpParser\Node\UnionType) {
- $type = null;
-
- if (!$hint->types) {
- throw new UnexpectedValueException('bad');
- }
-
- foreach ($hint->types as $atomic_typehint) {
- $resolved_type = self::resolve(
- $atomic_typehint,
- $scanner,
- $file_storage,
- $classlike_storage,
- $aliases,
- $php_major_version,
- $php_minor_version
- );
-
- $type = Type::combineUnionTypes($resolved_type, $type);
- }
-
- return $type;
- }
-
- $is_nullable = false;
-
- if ($hint instanceof PhpParser\Node\NullableType) {
- $is_nullable = true;
- $hint = $hint->type;
- }
-
- $type_string = null;
-
- if ($hint instanceof PhpParser\Node\Identifier) {
- $fq_type_string = $hint->name;
- } elseif ($hint instanceof PhpParser\Node\Name\FullyQualified) {
- $fq_type_string = (string)$hint;
-
- $scanner->queueClassLikeForScanning($fq_type_string);
- $file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
- } else {
- $lower_hint = strtolower($hint->parts[0]);
-
- if ($classlike_storage
- && ($lower_hint === 'self' || $lower_hint === 'static')
- && !$classlike_storage->is_trait
- ) {
- $fq_type_string = $classlike_storage->name;
-
- if ($lower_hint === 'static') {
- $fq_type_string .= '&static';
- }
- } else {
- $type_string = implode('\\', $hint->parts);
- $fq_type_string = ClassLikeAnalyzer::getFQCLNFromNameObject($hint, $aliases);
-
- $scanner->queueClassLikeForScanning($fq_type_string);
- $file_storage->referenced_classlikes[strtolower($fq_type_string)] = $fq_type_string;
- }
- }
-
- $type = Type::parseString(
- $fq_type_string,
- [$php_major_version, $php_minor_version],
- []
- );
-
- if ($type_string) {
- $atomic_type = $type->getSingleAtomic();
- $atomic_type->text = $type_string;
- }
-
- if ($is_nullable) {
- $type->addType(new TNull);
- }
-
- return $type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php
deleted file mode 100644
index e99259c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php
+++ /dev/null
@@ -1,650 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use LogicException;
-use PhpParser;
-use Psalm\Aliases;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Exception\CodeException;
-use Psalm\Exception\DocblockParseException;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\FileSource;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\CommentAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\SimpleTypeInferer;
-use Psalm\Internal\EventDispatcher;
-use Psalm\Internal\PhpVisitor\Reflector\ClassLikeNodeScanner;
-use Psalm\Internal\PhpVisitor\Reflector\ExpressionResolver;
-use Psalm\Internal\PhpVisitor\Reflector\ExpressionScanner;
-use Psalm\Internal\PhpVisitor\Reflector\FunctionLikeNodeScanner;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Scanner\FileScanner;
-use Psalm\Internal\Scanner\PhpStormMetaScanner;
-use Psalm\Internal\Type\TypeAlias;
-use Psalm\Internal\Type\TypeParser;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\TaintedInput;
-use Psalm\Plugin\EventHandler\Event\AfterClassLikeVisitEvent;
-use Psalm\Storage\FileStorage;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type;
-use UnexpectedValueException;
-
-use function array_pop;
-use function end;
-use function explode;
-use function get_class;
-use function implode;
-use function in_array;
-use function is_string;
-use function reset;
-use function spl_object_id;
-use function strpos;
-use function strtolower;
-
-use const PHP_VERSION_ID;
-
-/**
- * @internal
- */
-class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSource
-{
- /**
- * @var Aliases
- */
- private $aliases;
-
- /**
- * @var FileScanner
- */
- private $file_scanner;
-
- /**
- * @var Codebase
- */
- private $codebase;
-
- /**
- * @var string
- */
- private $file_path;
-
- /**
- * @var bool
- */
- private $scan_deep;
-
- /**
- * @var FileStorage
- */
- private $file_storage;
-
- /**
- * @var array<FunctionLikeNodeScanner>
- */
- private $functionlike_node_scanners = [];
-
- /**
- * @var array<ClassLikeNodeScanner>
- */
- private $classlike_node_scanners = [];
-
- /**
- * @var PhpParser\Node\Name|null
- */
- private $namespace_name;
-
- /**
- * @var PhpParser\Node\Expr|null
- */
- private $exists_cond_expr;
-
- /**
- * @var ?int
- */
- private $skip_if_descendants;
-
- /**
- * @var array<string, TypeAlias>
- */
- private $type_aliases = [];
-
- /**
- * @var array<int, bool>
- */
- private $bad_classes = [];
- /**
- * @var EventDispatcher
- */
- private $eventDispatcher;
-
- public function __construct(
- Codebase $codebase,
- FileScanner $file_scanner,
- FileStorage $file_storage
- ) {
- $this->codebase = $codebase;
- $this->file_scanner = $file_scanner;
- $this->file_path = $file_scanner->file_path;
- $this->scan_deep = $file_scanner->will_analyze;
- $this->file_storage = $file_storage;
- $this->aliases = $this->file_storage->aliases = new Aliases();
- $this->eventDispatcher = $this->codebase->config->eventDispatcher;
- }
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- foreach ($node->getComments() as $comment) {
- if ($comment instanceof PhpParser\Comment\Doc && !$node instanceof PhpParser\Node\Stmt\ClassLike) {
- try {
- $type_aliases = ClassLikeNodeScanner::getTypeAliasesFromComment(
- $comment,
- $this->aliases,
- $this->type_aliases,
- null
- );
-
- foreach ($type_aliases as $type_alias) {
- // finds issues, if there are any
- TypeParser::parseTokens($type_alias->replacement_tokens);
- }
-
- $this->type_aliases += $type_aliases;
- } catch (DocblockParseException | TypeParseTreeException $e) {
- $this->file_storage->docblock_issues[] = new InvalidDocblock(
- $e->getMessage(),
- new CodeLocation($this->file_scanner, $node, null, true)
- );
- }
- }
- }
-
- if ($node instanceof PhpParser\Node\Stmt\Namespace_) {
- $this->handleNamespace($node);
- } elseif ($node instanceof PhpParser\Node\Stmt\Use_) {
- $this->handleUse($node);
- } elseif ($node instanceof PhpParser\Node\Stmt\GroupUse) {
- $this->handleGroupUse($node);
- } elseif ($node instanceof PhpParser\Node\Stmt\ClassLike) {
- if ($this->skip_if_descendants) {
- return null;
- }
-
- $classlike_node_scanner = new ClassLikeNodeScanner(
- $this->codebase,
- $this->file_storage,
- $this->file_scanner,
- $this->aliases,
- $this->namespace_name
- );
-
- $this->classlike_node_scanners[] = $classlike_node_scanner;
-
- if ($classlike_node_scanner->start($node) === false) {
- $this->bad_classes[spl_object_id($node)] = true;
- return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN;
- }
-
- $this->type_aliases += $classlike_node_scanner->type_aliases;
- } elseif ($node instanceof PhpParser\Node\Stmt\TryCatch) {
- foreach ($node->catches as $catch) {
- foreach ($catch->types as $catch_type) {
- $catch_fqcln = ClassLikeAnalyzer::getFQCLNFromNameObject($catch_type, $this->aliases);
-
- if (!in_array(strtolower($catch_fqcln), ['self', 'static', 'parent'], true)) {
- $this->codebase->scanner->queueClassLikeForScanning($catch_fqcln);
- $this->file_storage->referenced_classlikes[strtolower($catch_fqcln)] = $catch_fqcln;
- }
- }
- }
- } elseif ($node instanceof PhpParser\Node\FunctionLike) {
- if ($node instanceof PhpParser\Node\Stmt\Function_
- || $node instanceof PhpParser\Node\Stmt\ClassMethod
- ) {
- if ($this->skip_if_descendants) {
- return null;
- }
- }
-
- $classlike_storage = null;
-
- if ($this->classlike_node_scanners) {
- $classlike_node_scanner = end($this->classlike_node_scanners);
- $classlike_storage = $classlike_node_scanner->storage;
- }
-
- $functionlike_types = [];
-
- foreach ($this->functionlike_node_scanners as $functionlike_node_scanner) {
- $functionlike_storage = $functionlike_node_scanner->storage;
- $functionlike_types += $functionlike_storage->template_types ?? [];
- }
-
- $functionlike_node_scanner = new FunctionLikeNodeScanner(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $this->type_aliases,
- $classlike_storage,
- $functionlike_types
- );
-
- $functionlike_node_scanner->start($node);
-
- $this->functionlike_node_scanners[] = $functionlike_node_scanner;
-
- if ($classlike_storage
- && $this->codebase->php_major_version >= 8
- && $node instanceof PhpParser\Node\Stmt\ClassMethod
- && strtolower($node->name->name) === '__tostring'
- ) {
- if ($classlike_storage->is_interface) {
- $classlike_storage->parent_interfaces['stringable'] = 'Stringable';
- } else {
- $classlike_storage->class_implements['stringable'] = 'Stringable';
- }
-
- if (PHP_VERSION_ID >= 80000) {
- $this->codebase->scanner->queueClassLikeForScanning('Stringable');
- }
- }
-
- if (!$this->scan_deep) {
- return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN;
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\Global_) {
- $functionlike_node_scanner = end($this->functionlike_node_scanners);
-
- if ($functionlike_node_scanner && $functionlike_node_scanner->storage) {
- foreach ($node->vars as $var) {
- if ($var instanceof PhpParser\Node\Expr\Variable) {
- if (is_string($var->name) && $var->name !== 'argv' && $var->name !== 'argc') {
- $var_id = '$' . $var->name;
-
- $functionlike_node_scanner->storage->global_variables[$var_id] = true;
- }
- }
- }
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\TraitUse) {
- if ($this->skip_if_descendants) {
- return null;
- }
-
- if (!$this->classlike_node_scanners) {
- throw new LogicException('$this->classlike_node_scanners should not be empty');
- }
-
- $classlike_node_scanner = end($this->classlike_node_scanners);
-
- $classlike_node_scanner->handleTraitUse($node);
- } elseif ($node instanceof PhpParser\Node\Stmt\Const_) {
- foreach ($node->consts as $const) {
- $const_type = SimpleTypeInferer::infer(
- $this->codebase,
- new NodeDataProvider(),
- $const->value,
- $this->aliases
- ) ?? Type::getMixed();
-
- $fq_const_name = Type::getFQCLNFromString($const->name->name, $this->aliases);
-
- if ($this->codebase->register_stub_files || $this->codebase->register_autoload_files) {
- $this->codebase->addGlobalConstantType($fq_const_name, $const_type);
- }
-
- $this->file_storage->constants[$fq_const_name] = $const_type;
- $this->file_storage->declaring_constants[$fq_const_name] = $this->file_path;
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\If_ && !$this->skip_if_descendants) {
- if (!$this->functionlike_node_scanners) {
- $this->exists_cond_expr = $node->cond;
-
- if (ExpressionResolver::enterConditional(
- $this->codebase,
- $this->file_path,
- $this->exists_cond_expr
- ) === false
- ) {
- // the else node should terminate the agreement
- $this->skip_if_descendants = $node->else ? $node->else->getLine() : $node->getLine();
- }
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\Else_) {
- if ($this->skip_if_descendants === $node->getLine()) {
- $this->skip_if_descendants = null;
- $this->exists_cond_expr = null;
- } elseif (!$this->skip_if_descendants) {
- if ($this->exists_cond_expr
- && ExpressionResolver::enterConditional(
- $this->codebase,
- $this->file_path,
- $this->exists_cond_expr
- ) === true
- ) {
- $this->skip_if_descendants = $node->getLine();
- }
- }
- } elseif ($node instanceof PhpParser\Node\Expr) {
- $functionlike_storage = null;
-
- if ($this->functionlike_node_scanners) {
- $functionlike_node_scanner = end($this->functionlike_node_scanners);
- $functionlike_storage = $functionlike_node_scanner->storage;
- }
-
- ExpressionScanner::scan(
- $this->codebase,
- $this->file_scanner,
- $this->file_storage,
- $this->aliases,
- $node,
- $functionlike_storage,
- $this->skip_if_descendants
- );
- }
-
- if ($doc_comment = $node->getDocComment()) {
- $var_comments = [];
-
- $template_types = [];
-
- if ($this->classlike_node_scanners) {
- $classlike_node_scanner = end($this->classlike_node_scanners);
- $classlike_storage = $classlike_node_scanner->storage;
- $template_types = $classlike_storage->template_types ?? [];
- }
-
- foreach ($this->functionlike_node_scanners as $functionlike_node_scanner) {
- $functionlike_storage = $functionlike_node_scanner->storage;
- $template_types += $functionlike_storage->template_types ?? [];
- }
-
- try {
- $var_comments = CommentAnalyzer::getTypeFromComment(
- $doc_comment,
- $this->file_scanner,
- $this->aliases,
- $template_types,
- $this->type_aliases
- );
- } catch (DocblockParseException $e) {
- // do nothing
- }
-
- foreach ($var_comments as $var_comment) {
- if (!$var_comment->type) {
- continue;
- }
-
- $var_type = $var_comment->type;
- $var_type->queueClassLikesForScanning($this->codebase, $this->file_storage);
- }
- }
-
- if ($node instanceof PhpParser\Node\Expr\Assign
- || $node instanceof PhpParser\Node\Expr\AssignOp
- || $node instanceof PhpParser\Node\Expr\AssignRef
- ) {
- if ($node->var instanceof PhpParser\Node\Expr\PropertyFetch
- && $node->var->var instanceof PhpParser\Node\Expr\Variable
- && $node->var->var->name === 'this'
- && $node->var->name instanceof PhpParser\Node\Identifier
- ) {
- if ($this->functionlike_node_scanners) {
- $functionlike_node_scanner = end($this->functionlike_node_scanners);
- $functionlike_storage = $functionlike_node_scanner->storage;
-
- if ($functionlike_storage instanceof MethodStorage) {
- $functionlike_storage->this_property_mutations[$node->var->name->name] = true;
- }
- }
- }
- }
-
- return null;
- }
-
- private function handleNamespace(PhpParser\Node\Stmt\Namespace_ $node): void
- {
- $this->file_storage->aliases = $this->aliases;
-
- $this->namespace_name = $node->name;
-
- $this->aliases = new Aliases(
- $node->name ? implode('\\', $node->name->parts) : '',
- $this->aliases->uses,
- $this->aliases->functions,
- $this->aliases->constants,
- $this->aliases->uses_flipped,
- $this->aliases->functions_flipped,
- $this->aliases->constants_flipped
- );
-
- $this->file_storage->namespace_aliases[(int) $node->getAttribute('startFilePos')] = $this->aliases;
-
- if ($node->stmts) {
- $this->aliases->namespace_first_stmt_start = (int) $node->stmts[0]->getAttribute('startFilePos');
- }
- }
-
- private function handleUse(PhpParser\Node\Stmt\Use_ $node): void
- {
- foreach ($node->uses as $use) {
- $use_path = implode('\\', $use->name->parts);
-
- $use_alias = $use->alias->name ?? $use->name->getLast();
-
- switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $node->type) {
- case PhpParser\Node\Stmt\Use_::TYPE_FUNCTION:
- $this->aliases->functions[strtolower($use_alias)] = $use_path;
- $this->aliases->functions_flipped[strtolower($use_path)] = $use_alias;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_CONSTANT:
- $this->aliases->constants[$use_alias] = $use_path;
- $this->aliases->constants_flipped[$use_path] = $use_alias;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_NORMAL:
- $this->aliases->uses[strtolower($use_alias)] = $use_path;
- $this->aliases->uses_flipped[strtolower($use_path)] = $use_alias;
- break;
- }
- }
-
- if (!$this->aliases->uses_start) {
- $this->aliases->uses_start = (int) $node->getAttribute('startFilePos');
- }
-
- $this->aliases->uses_end = (int) $node->getAttribute('endFilePos') + 1;
- }
-
- private function handleGroupUse(PhpParser\Node\Stmt\GroupUse $node): void
- {
- $use_prefix = implode('\\', $node->prefix->parts);
-
- foreach ($node->uses as $use) {
- $use_path = $use_prefix . '\\' . implode('\\', $use->name->parts);
- $use_alias = $use->alias->name ?? $use->name->getLast();
-
- switch ($use->type !== PhpParser\Node\Stmt\Use_::TYPE_UNKNOWN ? $use->type : $node->type) {
- case PhpParser\Node\Stmt\Use_::TYPE_FUNCTION:
- $this->aliases->functions[strtolower($use_alias)] = $use_path;
- $this->aliases->functions_flipped[strtolower($use_path)] = $use_alias;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_CONSTANT:
- $this->aliases->constants[$use_alias] = $use_path;
- $this->aliases->constants_flipped[$use_path] = $use_alias;
- break;
-
- case PhpParser\Node\Stmt\Use_::TYPE_NORMAL:
- $this->aliases->uses[strtolower($use_alias)] = $use_path;
- $this->aliases->uses_flipped[strtolower($use_path)] = $use_alias;
- break;
- }
- }
-
- if (!$this->aliases->uses_start) {
- $this->aliases->uses_start = (int) $node->getAttribute('startFilePos');
- }
-
- $this->aliases->uses_end = (int) $node->getAttribute('endFilePos') + 1;
- }
-
- /**
- * @return null
- */
- public function leaveNode(PhpParser\Node $node)
- {
- if ($node instanceof PhpParser\Node\Stmt\Namespace_) {
- if (!$this->file_storage->aliases) {
- throw new UnexpectedValueException('File storage liases should not be null');
- }
-
- $this->aliases = $this->file_storage->aliases;
-
- if ($this->codebase->register_stub_files
- && $node->name
- && $node->name->parts === ['PHPSTORM_META']
- ) {
- foreach ($node->stmts as $meta_stmt) {
- if ($meta_stmt instanceof PhpParser\Node\Stmt\Expression
- && $meta_stmt->expr instanceof PhpParser\Node\Expr\FuncCall
- && $meta_stmt->expr->name instanceof PhpParser\Node\Name
- && $meta_stmt->expr->name->parts === ['override']
- ) {
- PhpStormMetaScanner::handleOverride($meta_stmt->expr->getArgs(), $this->codebase);
- }
- }
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\ClassLike) {
- if ($this->skip_if_descendants) {
- return null;
- }
-
- if (isset($this->bad_classes[spl_object_id($node)])) {
- return null;
- }
-
- if (!$this->classlike_node_scanners) {
- throw new UnexpectedValueException('$this->classlike_node_scanners cannot be empty');
- }
-
- $classlike_node_scanner = array_pop($this->classlike_node_scanners);
-
- $classlike_storage = $classlike_node_scanner->finish($node);
-
- if ($classlike_storage->has_visitor_issues) {
- $this->file_storage->has_visitor_issues = true;
- }
-
- $event = new AfterClassLikeVisitEvent(
- $node,
- $classlike_storage,
- $this,
- $this->codebase,
- []
- );
-
- $this->eventDispatcher->dispatchAfterClassLikeVisit($event);
-
- if (!$this->file_storage->has_visitor_issues) {
- $this->codebase->cacheClassLikeStorage($classlike_storage, $this->file_path);
- }
- } elseif ($node instanceof PhpParser\Node\FunctionLike) {
- if ($this->skip_if_descendants) {
- return null;
- }
-
- if (!$this->functionlike_node_scanners) {
- if ($this->file_storage->has_visitor_issues) {
- return null;
- }
-
- throw new UnexpectedValueException(
- 'There should be function storages for line ' . $this->file_path . ':' . $node->getLine()
- );
- }
-
- $functionlike_node_scanner = array_pop($this->functionlike_node_scanners);
-
- if ($functionlike_node_scanner->storage) {
- foreach ($functionlike_node_scanner->storage->docblock_issues as $docblock_issue) {
- if (strpos($docblock_issue->code_location->file_path, 'CoreGenericFunctions.phpstub')
- || strpos($docblock_issue->code_location->file_path, 'CoreGenericClasses.phpstub')
- || strpos($this->file_path, 'CoreGenericIterators.phpstub')
- ) {
- $e = reset($functionlike_node_scanner->storage->docblock_issues);
-
- $fqcn_parts = explode('\\', get_class($e));
- $issue_type = array_pop($fqcn_parts);
-
- $message = $e instanceof TaintedInput
- ? $e->getJourneyMessage()
- : $e->message;
-
- throw new CodeException(
- 'Error with core stub file docblocks: '
- . $issue_type
- . ' - ' . $e->getShortLocationWithPrevious()
- . ':' . $e->code_location->getColumn()
- . ' - ' . $message
- );
- }
- }
-
- if ($functionlike_node_scanner->storage->has_visitor_issues) {
- $this->file_storage->has_visitor_issues = true;
- }
- }
- } elseif ($node instanceof PhpParser\Node\Stmt\If_ && $node->getLine() === $this->skip_if_descendants) {
- $this->exists_cond_expr = null;
- $this->skip_if_descendants = null;
- } elseif ($node instanceof PhpParser\Node\Stmt\Else_ && $node->getLine() === $this->skip_if_descendants) {
- $this->exists_cond_expr = null;
- $this->skip_if_descendants = null;
- }
-
- return null;
- }
-
- public function getFilePath(): string
- {
- return $this->file_path;
- }
-
- public function getFileName(): string
- {
- return $this->file_scanner->getFileName();
- }
-
- public function getRootFilePath(): string
- {
- return $this->file_scanner->getRootFilePath();
- }
-
- public function getRootFileName(): string
- {
- return $this->file_scanner->getRootFileName();
- }
-
- public function getAliases(): Aliases
- {
- return $this->aliases;
- }
-
- /**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint
- */
- public function afterTraverse(array $nodes)
- {
- $this->file_storage->type_aliases = $this->type_aliases;
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php
deleted file mode 100644
index e0ed99d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ShortClosureVisitor.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-
-use function is_string;
-
-/**
- * @internal
- */
-class ShortClosureVisitor extends PhpParser\NodeVisitorAbstract
-{
- /**
- * @var array<string, bool>
- */
- protected $used_variables = [];
-
- public function enterNode(PhpParser\Node $node): ?int
- {
- if ($node instanceof PhpParser\Node\Expr\Variable && is_string($node->name)) {
- $this->used_variables['$' . $node->name] = true;
- }
-
- return null;
- }
-
- /**
- * @return array<string, bool>
- */
- public function getUsedVariables(): array
- {
- return $this->used_variables;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php
deleted file mode 100644
index 38b14e5..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/SimpleNameResolver.php
+++ /dev/null
@@ -1,252 +0,0 @@
-<?php
-
-// based on PhpParser's builtin one
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-use PhpParser\ErrorHandler;
-use PhpParser\NameContext;
-use PhpParser\Node;
-use PhpParser\Node\Expr;
-use PhpParser\Node\Name;
-use PhpParser\Node\NullableType;
-use PhpParser\Node\Stmt;
-use PhpParser\NodeVisitorAbstract;
-
-/**
- * @internal
- */
-class SimpleNameResolver extends NodeVisitorAbstract
-{
- /** @var NameContext Naming context */
- private $nameContext;
-
- /** @var int|null */
- private $start_change;
-
- /** @var int|null */
- private $end_change;
-
- /**
- * @param ErrorHandler $errorHandler Error handler
- * @param null|array<int, array{int, int, int, int, int, string}> $offset_map
- */
- public function __construct(ErrorHandler $errorHandler, ?array $offset_map = null)
- {
- if ($offset_map) {
- foreach ($offset_map as [, , $b_s, $b_e]) {
- if ($this->start_change === null) {
- $this->start_change = $b_s;
- }
-
- $this->end_change = $b_e;
- }
- }
-
- $this->nameContext = new NameContext($errorHandler);
- }
-
- public function beforeTraverse(array $nodes): ?array
- {
- $this->nameContext->startNamespace();
-
- return null;
- }
-
- public function enterNode(Node $node): ?int
- {
- if ($node instanceof Stmt\Namespace_) {
- $this->nameContext->startNamespace($node->name);
- } elseif ($node instanceof Stmt\Use_) {
- foreach ($node->uses as $use) {
- $this->addAlias($use, $node->type);
- }
- } elseif ($node instanceof Stmt\GroupUse) {
- foreach ($node->uses as $use) {
- $this->addAlias($use, $node->type, $node->prefix);
- }
- } elseif ($node instanceof Stmt\Class_) {
- if (null !== $node->extends) {
- $node->extends = $this->resolveClassName($node->extends);
- }
- foreach ($node->implements as &$interface) {
- $interface = $this->resolveClassName($interface);
- }
- $this->resolveAttrGroups($node);
- if (null !== $node->name) {
- $this->addNamespacedName($node);
- }
- }
-
- if ($node instanceof Stmt\ClassMethod
- && $this->start_change
- && $this->end_change
- ) {
- /** @var array{startFilePos: int, endFilePos: int} */
- $attrs = $node->getAttributes();
-
- if ($cs = $node->getComments()) {
- $attrs['startFilePos'] = $cs[0]->getStartFilePos();
- }
-
- if ($attrs['endFilePos'] < $this->start_change
- || $attrs['startFilePos'] > $this->end_change
- ) {
- return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN;
- }
- }
-
- if ($node instanceof Stmt\ClassMethod
- || $node instanceof Expr\Closure
- ) {
- $this->resolveSignature($node);
- } elseif ($node instanceof Expr\StaticCall
- || $node instanceof Expr\StaticPropertyFetch
- || $node instanceof Expr\ClassConstFetch
- || $node instanceof Expr\New_
- || $node instanceof Expr\Instanceof_
- ) {
- if ($node->class instanceof Name) {
- $node->class = $this->resolveClassName($node->class);
- }
- } elseif ($node instanceof Stmt\Catch_) {
- foreach ($node->types as &$type) {
- $type = $this->resolveClassName($type);
- }
- } elseif ($node instanceof Expr\FuncCall) {
- if ($node->name instanceof Name) {
- $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_FUNCTION);
- }
- } elseif ($node instanceof Expr\ConstFetch) {
- $node->name = $this->resolveName($node->name, Stmt\Use_::TYPE_CONSTANT);
- } elseif ($node instanceof Stmt\Trait_) {
- $this->resolveTrait($node);
- } elseif ($node instanceof Stmt\TraitUse) {
- foreach ($node->traits as &$trait) {
- $trait = $this->resolveClassName($trait);
- }
-
- foreach ($node->adaptations as $adaptation) {
- if (null !== $adaptation->trait) {
- $adaptation->trait = $this->resolveClassName($adaptation->trait);
- }
-
- if ($adaptation instanceof Stmt\TraitUseAdaptation\Precedence) {
- foreach ($adaptation->insteadof as &$insteadof) {
- $insteadof = $this->resolveClassName($insteadof);
- }
- }
- }
- }
-
- return null;
- }
-
- private function addAlias(Stmt\UseUse $use, int $type, ?Name $prefix = null): void
- {
- // Add prefix for group uses
- /** @var Name $name */
- $name = $prefix ? Name::concat($prefix, $use->name) : $use->name;
- // Type is determined either by individual element or whole use declaration
- $type |= $use->type;
-
- $this->nameContext->addAlias(
- $name,
- (string) $use->getAlias(),
- $type,
- $use->getAttributes()
- );
- }
-
- /**
- * @param Stmt\Function_|Stmt\ClassMethod|Expr\Closure $node
- */
- private function resolveSignature(PhpParser\NodeAbstract $node): void
- {
- foreach ($node->params as $param) {
- $param->type = $this->resolveType($param->type);
- }
- $node->returnType = $this->resolveType($node->returnType);
- }
-
- /**
- * @template T of Node|null
- * @param T $node
- * @return ($node is NullableType ? NullableType : ($node is Name ? Name : T))
- * @psalm-suppress LessSpecificReturnType
- */
- private function resolveType(?Node $node): ?Node
- {
- if ($node instanceof NullableType) {
- $node->type = $this->resolveType($node->type);
-
- return $node;
- }
- if ($node instanceof Name) {
- return $this->resolveClassName($node);
- }
-
- return $node;
- }
-
- /**
- * Resolve name, according to name resolver options.
- *
- * CAVE: Attribute values are of type `string`, this is
- * different to PhpParser's `NameResolver` using objects.
- *
- * @param Name $name Function or constant name to resolve
- * @param Stmt\Use_::TYPE_* $type One of Stmt\Use_::TYPE_*
- *
- * @return Name Resolved name, or original name with attribute
- */
- protected function resolveName(Name $name, int $type): Name
- {
- $resolvedName = $this->nameContext->getResolvedName($name, $type);
- if (null !== $resolvedName) {
- $name->setAttribute('resolvedName', $resolvedName->toString());
- } else {
- $namespaceName = Name\FullyQualified::concat(
- $this->nameContext->getNamespace(),
- $name,
- $name->getAttributes()
- );
- if ($namespaceName instanceof Name) {
- $name->setAttribute('namespacedName', $namespaceName->toString());
- }
- }
- return $name;
- }
-
- protected function resolveClassName(Name $name): Name
- {
- return $this->resolveName($name, Stmt\Use_::TYPE_NORMAL);
- }
-
- protected function addNamespacedName(Stmt\Class_ $node): void
- {
- $node->setAttribute('namespacedName', Name::concat(
- $this->nameContext->getNamespace(),
- (string)$node->name
- ));
- }
-
- protected function resolveAttrGroups(Stmt\Class_ $node): void
- {
- foreach ($node->attrGroups as $attrGroup) {
- foreach ($attrGroup->attrs as $attr) {
- $attr->name = $this->resolveClassName($attr->name);
- }
- }
- }
-
- protected function resolveTrait(Stmt\Trait_ $node): void
- {
- $resolvedName = Name::concat($this->nameContext->getNamespace(), (string) $node->name);
-
- if (null !== $resolvedName) {
- $node->setAttribute('resolvedName', $resolvedName->toString());
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TraitFinder.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TraitFinder.php
deleted file mode 100644
index 93a0522..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TraitFinder.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser;
-use ReflectionClass;
-use Throwable;
-
-use function count;
-use function end;
-use function explode;
-use function trait_exists;
-
-/**
- * Given a list of file diffs, this scans an AST to find the sections it can replace, and parses
- * just those methods.
- */
-class TraitFinder extends PhpParser\NodeVisitorAbstract
-{
- /** @var list<PhpParser\Node\Stmt\Trait_> */
- private $matching_trait_nodes = [];
-
- private $fq_trait_name;
-
- public function __construct(string $fq_trait_name)
- {
- $this->fq_trait_name = $fq_trait_name;
- }
-
- public function enterNode(PhpParser\Node $node, bool &$traverseChildren = true): ?int
- {
- if ($node instanceof PhpParser\Node\Stmt\Trait_) {
- /** @var ?string */
- $resolved_name = $node->getAttribute('resolvedName');
-
- if ($resolved_name === null) {
- // compare ends of names, a temporary hack because PHPParser caches
- // may not have that attribute
-
- $fq_trait_name_parts = explode('\\', $this->fq_trait_name);
-
- /** @psalm-suppress PossiblyNullPropertyFetch */
- if ($node->name->name === end($fq_trait_name_parts)) {
- $this->matching_trait_nodes[] = $node;
- }
- } elseif ($resolved_name === $this->fq_trait_name) {
- $this->matching_trait_nodes[] = $node;
- }
- }
-
- if ($node instanceof PhpParser\Node\Stmt\ClassLike
- || $node instanceof PhpParser\Node\FunctionLike
- ) {
- return PhpParser\NodeTraverser::DONT_TRAVERSE_CHILDREN;
- }
-
- return null;
- }
-
- public function getNode(): ?PhpParser\Node\Stmt\Trait_
- {
- if (!count($this->matching_trait_nodes)) {
- return null;
- }
-
- if (count($this->matching_trait_nodes) === 1 || !trait_exists($this->fq_trait_name)) {
- return $this->matching_trait_nodes[0];
- }
-
- try {
- $reflection_trait = new ReflectionClass($this->fq_trait_name);
- } catch (Throwable $t) {
- return null;
- }
-
- foreach ($this->matching_trait_nodes as $node) {
- if ($node->getLine() === $reflection_trait->getStartLine()) {
- return $node;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php
deleted file mode 100644
index 05704e1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/TypeMappingVisitor.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\PhpVisitor;
-
-use PhpParser\Node;
-use PhpParser\NodeVisitorAbstract;
-use Psalm\Internal\Provider\NodeDataProvider;
-
-class TypeMappingVisitor extends NodeVisitorAbstract
-{
- private $fake_type_provider;
- private $real_type_provider;
-
- public function __construct(
- NodeDataProvider $fake_type_provider,
- NodeDataProvider $real_type_provider
- ) {
- $this->fake_type_provider = $fake_type_provider;
- $this->real_type_provider = $real_type_provider;
- }
-
- /**
- * @phpcsSuppress SlevomatCodingStandard.TypeHints.ReturnTypeHint.MissingAnyTypeHint
- */
- public function enterNode(Node $node)
- {
- $origNode = $node;
-
- /** @psalm-suppress ArgumentTypeCoercion */
- $node_type = $this->fake_type_provider->getType($origNode);
-
- if ($node_type) {
- /** @psalm-suppress ArgumentTypeCoercion */
- $this->real_type_provider->setType($origNode, clone $node_type);
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/DisableCommand.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/DisableCommand.php
deleted file mode 100644
index ae05fb4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/DisableCommand.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager\Command;
-
-use InvalidArgumentException;
-use Psalm\Internal\PluginManager\PluginListFactory;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Style\SymfonyStyle;
-use UnexpectedValueException;
-
-use function assert;
-use function getcwd;
-use function is_string;
-
-use const DIRECTORY_SEPARATOR;
-
-/**
- * @internal
- */
-class DisableCommand extends Command
-{
- /** @var PluginListFactory */
- private $plugin_list_factory;
-
- public function __construct(PluginListFactory $plugin_list_factory)
- {
- $this->plugin_list_factory = $plugin_list_factory;
- parent::__construct();
- }
-
- protected function configure(): void
- {
- $this
- ->setName('disable')
- ->setDescription('Disables a named plugin')
- ->addArgument(
- 'pluginName',
- InputArgument::REQUIRED,
- 'Plugin name (fully qualified class name or composer package name)'
- )
- ->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
- ->addUsage('vendor/plugin-package-name [-c path/to/psalm.xml]');
- $this->addUsage('\'Plugin\Class\Name\' [-c path/to/psalm.xml]');
- }
-
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- $io = new SymfonyStyle($input, $output);
-
- $current_dir = (string) getcwd() . DIRECTORY_SEPARATOR;
-
- $config_file_path = $input->getOption('config');
- if ($config_file_path !== null && !is_string($config_file_path)) {
- throw new UnexpectedValueException('Config file path should be a string');
- }
-
- $plugin_list = ($this->plugin_list_factory)($current_dir, $config_file_path);
-
- $plugin_name = $input->getArgument('pluginName');
-
- assert(is_string($plugin_name));
-
- try {
- $plugin_class = $plugin_list->resolvePluginClass($plugin_name);
- } catch (InvalidArgumentException $e) {
- $io->error('Unknown plugin class ' . $plugin_name);
-
- return 2;
- }
-
- if (!$plugin_list->isEnabled($plugin_class)) {
- $io->note('Plugin already disabled');
-
- return 3;
- }
-
- $plugin_list->disable($plugin_class);
- $io->success('Plugin disabled');
-
- return 0;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/EnableCommand.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/EnableCommand.php
deleted file mode 100644
index d868ca1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/EnableCommand.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager\Command;
-
-use InvalidArgumentException;
-use Psalm\Internal\PluginManager\PluginListFactory;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Style\SymfonyStyle;
-use UnexpectedValueException;
-
-use function assert;
-use function getcwd;
-use function is_string;
-
-use const DIRECTORY_SEPARATOR;
-
-/**
- * @internal
- */
-class EnableCommand extends Command
-{
- /** @var PluginListFactory */
- private $plugin_list_factory;
-
- public function __construct(PluginListFactory $plugin_list_factory)
- {
- $this->plugin_list_factory = $plugin_list_factory;
- parent::__construct();
- }
-
- protected function configure(): void
- {
- $this
- ->setName('enable')
- ->setDescription('Enables a named plugin')
- ->addArgument(
- 'pluginName',
- InputArgument::REQUIRED,
- 'Plugin name (fully qualified class name or composer package name)'
- )
- ->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
- ->addUsage('vendor/plugin-package-name [-c path/to/psalm.xml]');
- $this->addUsage('\'Plugin\Class\Name\' [-c path/to/psalm.xml]');
- }
-
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- $io = new SymfonyStyle($input, $output);
-
- $current_dir = (string) getcwd() . DIRECTORY_SEPARATOR;
-
- $config_file_path = $input->getOption('config');
- if ($config_file_path !== null && !is_string($config_file_path)) {
- throw new UnexpectedValueException('Config file path should be a string');
- }
-
- $plugin_list = ($this->plugin_list_factory)($current_dir, $config_file_path);
-
- $plugin_name = $input->getArgument('pluginName');
-
- assert(is_string($plugin_name));
-
- try {
- $plugin_class = $plugin_list->resolvePluginClass($plugin_name);
- } catch (InvalidArgumentException $e) {
- $io->error('Unknown plugin class ' . $plugin_name);
-
- return 2;
- }
-
- if ($plugin_list->isEnabled($plugin_class)) {
- $io->note('Plugin already enabled');
-
- return 3;
- }
-
- $plugin_list->enable($plugin_class);
- $io->success('Plugin enabled');
-
- return 0;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/ShowCommand.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/ShowCommand.php
deleted file mode 100644
index 1ef6165..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/Command/ShowCommand.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager\Command;
-
-use Psalm\Internal\PluginManager\PluginListFactory;
-use Symfony\Component\Console\Command\Command;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Style\SymfonyStyle;
-use UnexpectedValueException;
-
-use function array_keys;
-use function array_map;
-use function array_values;
-use function count;
-use function getcwd;
-use function is_string;
-
-use const DIRECTORY_SEPARATOR;
-
-/**
- * @internal
- */
-class ShowCommand extends Command
-{
- /** @var PluginListFactory */
- private $plugin_list_factory;
-
- public function __construct(PluginListFactory $plugin_list_factory)
- {
- $this->plugin_list_factory = $plugin_list_factory;
- parent::__construct();
- }
-
- protected function configure(): void
- {
- $this
- ->setName('show')
- ->setDescription('Lists enabled and available plugins')
- ->addOption('config', 'c', InputOption::VALUE_REQUIRED, 'Path to Psalm config file')
- ->addUsage('[-c path/to/psalm.xml]');
- }
-
- protected function execute(InputInterface $input, OutputInterface $output): int
- {
- $io = new SymfonyStyle($input, $output);
- $current_dir = (string) getcwd() . DIRECTORY_SEPARATOR;
-
- $config_file_path = $input->getOption('config');
- if ($config_file_path !== null && !is_string($config_file_path)) {
- throw new UnexpectedValueException('Config file path should be a string');
- }
-
- $plugin_list = ($this->plugin_list_factory)($current_dir, $config_file_path);
-
- $enabled = $plugin_list->getEnabled();
- $available = $plugin_list->getAvailable();
-
- $formatRow =
- /**
- * @return array{0: null|string, 1: string}
- */
- function (string $class, ?string $package): array {
- return [$package, $class];
- };
-
- $io->section('Enabled');
- if (count($enabled)) {
- $io->table(
- ['Package', 'Class'],
- array_map(
- $formatRow,
- array_keys($enabled),
- array_values($enabled)
- )
- );
- } else {
- $io->note('No plugins enabled');
- }
-
- $io->section('Available');
- if (count($available)) {
- $io->table(
- ['Package', 'Class'],
- array_map(
- $formatRow,
- array_keys($available),
- array_values($available)
- )
- );
- } else {
- $io->note('No plugins available');
- }
-
- return 0;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ComposerLock.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ComposerLock.php
deleted file mode 100644
index fd5d251..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ComposerLock.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager;
-
-use RuntimeException;
-
-use function array_merge;
-use function file_get_contents;
-use function is_array;
-use function is_string;
-use function json_decode;
-use function json_last_error;
-use function json_last_error_msg;
-
-class ComposerLock
-{
- /** @var string[] */
- private $file_names;
-
- /** @param string[] $file_names */
- public function __construct(array $file_names)
- {
- $this->file_names = $file_names;
- }
-
- /**
- * @param mixed $package
- *
- * @psalm-assert-if-true array{name: string, extra: array{psalm: array{pluginClass: string}}} $package
- *
- * @psalm-pure
- */
- public function isPlugin($package): bool
- {
- return is_array($package)
- && isset($package['name'], $package['extra']['psalm']['pluginClass'])
- && is_string($package['name'])
- && is_array($package['extra'])
- && is_array($package['extra']['psalm'])
- && is_string($package['extra']['psalm']['pluginClass']);
- }
-
- /**
- * @return array<string,string> [packageName => pluginClass, ...]
- */
- public function getPlugins(): array
- {
- $pluginPackages = $this->getAllPluginPackages();
- $ret = [];
- foreach ($pluginPackages as $package) {
- $ret[$package['name']] = $package['extra']['psalm']['pluginClass'];
- }
-
- return $ret;
- }
-
- private function read(string $file_name): array
- {
- $contents = json_decode(file_get_contents($file_name), true);
-
- if ($error = json_last_error()) {
- throw new RuntimeException(json_last_error_msg(), $error);
- }
-
- if (!is_array($contents)) {
- throw new RuntimeException('Malformed ' . $file_name . ', expecting JSON-encoded object');
- }
-
- return $contents;
- }
-
- /**
- * @return list<array{name:string,extra:array{psalm:array{pluginClass:string}}}>
- */
- private function getAllPluginPackages(): array
- {
- $packages = $this->getAllPackages();
- $ret = [];
- /** @psalm-suppress MixedAssignment */
- foreach ($packages as $package) {
- if ($this->isPlugin($package)) {
- $ret[] = $package;
- }
- }
-
- return $ret;
- }
-
- private function getAllPackages(): array
- {
- $packages = [];
- foreach ($this->file_names as $file_name) {
- $composer_lock_contents = $this->read($file_name);
- if (!isset($composer_lock_contents['packages']) || !is_array($composer_lock_contents['packages'])) {
- throw new RuntimeException('packages section is missing or not an array');
- }
- if (!isset($composer_lock_contents['packages-dev']) || !is_array($composer_lock_contents['packages-dev'])) {
- throw new RuntimeException('packages-dev section is missing or not an array');
- }
- $packages = array_merge(
- $packages,
- array_merge(
- $composer_lock_contents['packages'],
- $composer_lock_contents['packages-dev']
- )
- );
- }
-
- return $packages;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ConfigFile.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ConfigFile.php
deleted file mode 100644
index a945dd2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/ConfigFile.php
+++ /dev/null
@@ -1,152 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager;
-
-use DOMDocument;
-use DomElement;
-use Psalm\Config;
-use RuntimeException;
-
-use function assert;
-use function file_get_contents;
-use function file_put_contents;
-use function sprintf;
-use function strpos;
-use function substr;
-
-class ConfigFile
-{
- /** @var string */
- private $path;
-
- /** @var string */
- private $current_dir;
-
- /** @var string|null */
- private $psalm_header;
-
- /** @var int|null */
- private $psalm_tag_end_pos;
-
- public function __construct(string $current_dir, ?string $explicit_path)
- {
- $this->current_dir = $current_dir;
-
- if ($explicit_path) {
- $this->path = $explicit_path;
- } else {
- $path = Config::locateConfigFile($current_dir);
- if (!$path) {
- throw new RuntimeException('Cannot find Psalm config');
- }
- $this->path = $path;
- }
- }
-
- public function getConfig(): Config
- {
- return Config::loadFromXMLFile($this->path, $this->current_dir);
- }
-
- public function removePlugin(string $plugin_class): void
- {
- $config_xml = $this->readXml();
- /** @var DomElement */
- $psalm_root = $config_xml->getElementsByTagName('psalm')[0];
- $plugins_elements = $psalm_root->getElementsByTagName('plugins');
- if (!$plugins_elements->length) {
- // no plugins, nothing to remove
- return;
- }
-
- /** @var DomElement */
- $plugins_element = $plugins_elements->item(0);
-
- $plugin_elements = $plugins_element->getElementsByTagName('pluginClass');
-
- foreach ($plugin_elements as $plugin_element) {
- if ($plugin_element->getAttribute('class') === $plugin_class) {
- $plugins_element->removeChild($plugin_element);
- break;
- }
- }
-
- if (!$plugin_elements->length) {
- // avoid breaking old psalm binaries, whose schema did not allow empty plugins
- $psalm_root->removeChild($plugins_element);
- }
-
- $this->saveXml($config_xml);
- }
-
- public function addPlugin(string $plugin_class): void
- {
- $config_xml = $this->readXml();
- /** @var DomElement */
- $psalm_root = $config_xml->getElementsByTagName('psalm')->item(0);
- $plugins_elements = $psalm_root->getElementsByTagName('plugins');
- if (!$plugins_elements->length) {
- $plugins_element = $config_xml->createElement('plugins');
- if ($plugins_element) {
- $psalm_root->appendChild($plugins_element);
- }
- } else {
- /** @var DomElement */
- $plugins_element = $plugins_elements->item(0);
- }
-
- $plugin_class_element = $config_xml->createElement('pluginClass');
- if ($plugin_class_element) {
- $plugin_class_element->setAttribute('xmlns', Config::CONFIG_NAMESPACE);
- $plugin_class_element->setAttribute('class', $plugin_class);
- if ($plugins_element) {
- $plugins_element->appendChild($plugin_class_element);
- }
- }
-
- $this->saveXml($config_xml);
- }
-
- private function readXml(): DOMDocument
- {
- $doc = new DOMDocument();
-
- $file_contents = file_get_contents($this->path);
-
- if (($tag_start = strpos($file_contents, '<psalm')) !== false) {
- $tag_end = strpos($file_contents, '>', $tag_start + 1);
-
- if ($tag_end !== false) {
- $this->psalm_tag_end_pos = $tag_end;
- $this->psalm_header = substr($file_contents, 0, $tag_end);
- }
- }
-
- assert($file_contents !== '');
- $doc->loadXML($file_contents);
-
- return $doc;
- }
-
- private function saveXml(DOMDocument $config_xml): void
- {
- $new_file_contents = $config_xml->saveXML($config_xml);
-
- if (($tag_start = strpos($new_file_contents, '<psalm')) !== false) {
- $tag_end = strpos($new_file_contents, '>', $tag_start + 1);
-
- if ($tag_end !== false
- && ($new_file_contents[$tag_end - 1] !== '/')
- && $this->psalm_tag_end_pos
- && $this->psalm_header
- ) {
- $new_file_contents = $this->psalm_header . substr($new_file_contents, $tag_end);
- }
- }
-
- $result = file_put_contents($this->path, $new_file_contents);
- if ($result === false) {
- throw new RuntimeException(sprintf('Unable to save xml to %s', $this->path));
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginList.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginList.php
deleted file mode 100644
index 7f5d905..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginList.php
+++ /dev/null
@@ -1,120 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager;
-
-use InvalidArgumentException;
-use RuntimeException;
-
-use function array_diff_key;
-use function array_flip;
-use function array_key_exists;
-use function array_search;
-use function strpos;
-
-class PluginList
-{
- /** @var null|ConfigFile */
- private $config_file;
-
- /** @var ComposerLock */
- private $composer_lock;
-
- /** @var ?array<string,string> [pluginClass => packageName] */
- private $all_plugins;
-
- /** @var ?array<string,?string> [pluginClass => ?packageName] */
- private $enabled_plugins;
-
- public function __construct(?ConfigFile $config_file, ComposerLock $composer_lock)
- {
- $this->config_file = $config_file;
- $this->composer_lock = $composer_lock;
- }
-
- /**
- * @return array<string,?string> [pluginClass => ?packageName, ...]
- */
- public function getEnabled(): array
- {
- if (!$this->enabled_plugins) {
- $this->enabled_plugins = [];
- if ($this->config_file) {
- foreach ($this->config_file->getConfig()->getPluginClasses() as $plugin_entry) {
- $plugin_class = $plugin_entry['class'];
- $this->enabled_plugins[$plugin_class] = $this->findPluginPackage($plugin_class);
- }
- }
- }
-
- return $this->enabled_plugins;
- }
-
- /**
- * @return array<string,?string> [pluginCLass => ?packageName]
- */
- public function getAvailable(): array
- {
- return array_diff_key($this->getAll(), $this->getEnabled());
- }
-
- /**
- * @return array<string,string> [pluginClass => packageName]
- */
- public function getAll(): array
- {
- if (null === $this->all_plugins) {
- $this->all_plugins = array_flip($this->composer_lock->getPlugins());
- }
-
- return $this->all_plugins;
- }
-
- public function resolvePluginClass(string $class_or_package): string
- {
- if (false === strpos($class_or_package, '/')) {
- return $class_or_package; // must be a class then
- }
-
- // pluginClass => ?pluginPackage
- $plugin_classes = $this->getAll();
-
- $class = array_search($class_or_package, $plugin_classes, true);
-
- if (false === $class) {
- throw new InvalidArgumentException('Unknown plugin: ' . $class_or_package);
- }
-
- return $class;
- }
-
- public function findPluginPackage(string $class): ?string
- {
- // pluginClass => ?pluginPackage
- $plugin_classes = $this->getAll();
-
- return $plugin_classes[$class] ?? null;
- }
-
- public function isEnabled(string $class): bool
- {
- return array_key_exists($class, $this->getEnabled());
- }
-
- public function enable(string $class): void
- {
- if (!$this->config_file) {
- throw new RuntimeException('Cannot find Psalm config');
- }
-
- $this->config_file->addPlugin($class);
- }
-
- public function disable(string $class): void
- {
- if (!$this->config_file) {
- throw new RuntimeException('Cannot find Psalm config');
- }
-
- $this->config_file->removePlugin($class);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginListFactory.php b/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginListFactory.php
deleted file mode 100644
index 0683365..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/PluginManager/PluginListFactory.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-namespace Psalm\Internal\PluginManager;
-
-use Psalm\Internal\Composer;
-use RuntimeException;
-
-use function array_filter;
-use function json_encode;
-use function rtrim;
-use function urlencode;
-
-use const DIRECTORY_SEPARATOR;
-
-class PluginListFactory
-{
- /** @var string */
- private $project_root;
-
- /** @var string */
- private $psalm_root;
-
- public function __construct(string $project_root, string $psalm_root)
- {
- $this->project_root = $project_root;
- $this->psalm_root = $psalm_root;
- }
-
- public function __invoke(string $current_dir, ?string $config_file_path = null): PluginList
- {
- try {
- $config_file = new ConfigFile($current_dir, $config_file_path);
- } catch (RuntimeException $exception) {
- $config_file = null;
- }
- $composer_lock = new ComposerLock($this->findLockFiles());
-
- return new PluginList($config_file, $composer_lock);
- }
-
- /** @return non-empty-array<int,string> */
- private function findLockFiles(): array
- {
- // use cases
- // 1. plugins are installed into project vendors - composer.lock is PROJECT_ROOT/composer.lock
- // 2. plugins are installed into separate composer environment (either global or bamarni-bin)
- // - composer.lock is PSALM_ROOT/../../../composer.lock
- // 3. plugins are installed into psalm vendors - composer.lock is PSALM_ROOT/composer.lock
- // 4. none of the above - use stub (empty virtual composer.lock)
-
- if ($this->psalm_root === $this->project_root) {
- // managing plugins for psalm itself
- $composer_lock_filenames = [
- Composer::getLockFilePath(rtrim($this->psalm_root, DIRECTORY_SEPARATOR)),
- ];
- } else {
- $composer_lock_filenames = [
- Composer::getLockFilePath(rtrim($this->project_root, DIRECTORY_SEPARATOR)),
- Composer::getLockFilePath(rtrim($this->psalm_root, DIRECTORY_SEPARATOR) . '/../../..'),
- Composer::getLockFilePath(rtrim($this->psalm_root, DIRECTORY_SEPARATOR)),
- ];
- }
-
- $composer_lock_filenames = array_filter($composer_lock_filenames, 'is_readable');
-
- if (empty($composer_lock_filenames)) {
- $stub_composer_lock = (object)[
- 'packages' => [],
- 'packages-dev' => [],
- ];
- $composer_lock_filenames[] = 'data:application/json,' . urlencode(json_encode($stub_composer_lock));
- }
-
- return $composer_lock_filenames;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php
deleted file mode 100644
index d3328a5..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/AddRemoveTaints/HtmlFunctionTainter.php
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\AddRemoveTaints;
-
-use PhpParser;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\AddTaintsInterface;
-use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent;
-use Psalm\Plugin\EventHandler\RemoveTaintsInterface;
-
-use function count;
-use function strtolower;
-
-use const ENT_QUOTES;
-
-class HtmlFunctionTainter implements AddTaintsInterface, RemoveTaintsInterface
-{
- /**
- * Called to see what taints should be added
- *
- * @return list<string>
- */
- public static function addTaints(AddRemoveTaintsEvent $event): array
- {
- $item = $event->getExpr();
- $statements_analyzer = $event->getStatementsSource();
-
- if (!$statements_analyzer instanceof StatementsAnalyzer
- || !$item instanceof PhpParser\Node\Expr\FuncCall
- || $item->isFirstClassCallable()
- || !$item->name instanceof PhpParser\Node\Name
- || count($item->name->parts) !== 1
- || count($item->getArgs()) === 0
- ) {
- return [];
- }
-
- $function_id = strtolower($item->name->parts[0]);
-
- if ($function_id === 'html_entity_decode'
- || $function_id === 'htmlspecialchars_decode'
- ) {
- $second_arg = $item->getArgs()[1]->value ?? null;
-
- if ($second_arg === null) {
- return ['html'];
- }
-
- $second_arg_value = $statements_analyzer->node_data->getType($second_arg);
-
- if (!$second_arg_value || !$second_arg_value->isSingleIntLiteral()) {
- return ['html'];
- }
-
- $second_arg_value = $second_arg_value->getSingleIntLiteral()->value;
-
- if (($second_arg_value & ENT_QUOTES) === ENT_QUOTES) {
- return ['html', 'has_quotes'];
- }
-
- return ['html'];
- }
-
- return [];
- }
-
- /**
- * Called to see what taints should be removed
- *
- * @return list<string>
- */
- public static function removeTaints(AddRemoveTaintsEvent $event): array
- {
- $item = $event->getExpr();
- $statements_analyzer = $event->getStatementsSource();
-
- if (!$statements_analyzer instanceof StatementsAnalyzer
- || !$item instanceof PhpParser\Node\Expr\FuncCall
- || $item->isFirstClassCallable()
- || !$item->name instanceof PhpParser\Node\Name
- || count($item->name->parts) !== 1
- || count($item->getArgs()) === 0
- ) {
- return [];
- }
-
- $function_id = strtolower($item->name->parts[0]);
-
- if ($function_id === 'htmlentities'
- || $function_id === 'htmlspecialchars'
- ) {
- $second_arg = $item->getArgs()[1]->value ?? null;
-
- if ($second_arg === null) {
- return ['html'];
- }
-
- $second_arg_value = $statements_analyzer->node_data->getType($second_arg);
-
- if (!$second_arg_value || !$second_arg_value->isSingleIntLiteral()) {
- return ['html'];
- }
-
- $second_arg_value = $second_arg_value->getSingleIntLiteral()->value;
-
- if (($second_arg_value & ENT_QUOTES) === ENT_QUOTES) {
- return ['html', 'has_quotes'];
- }
-
- return ['html'];
- }
-
- return [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php
deleted file mode 100644
index dae227f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageCacheProvider.php
+++ /dev/null
@@ -1,182 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Psalm\Config;
-use Psalm\Storage\ClassLikeStorage;
-use UnexpectedValueException;
-
-use function array_merge;
-use function dirname;
-use function file_exists;
-use function file_get_contents;
-use function file_put_contents;
-use function filemtime;
-use function get_class;
-use function hash;
-use function igbinary_serialize;
-use function igbinary_unserialize;
-use function is_dir;
-use function is_null;
-use function mkdir;
-use function serialize;
-use function strtolower;
-use function unlink;
-use function unserialize;
-
-use const DIRECTORY_SEPARATOR;
-use const PHP_VERSION_ID;
-
-/**
- * @internal
- */
-class ClassLikeStorageCacheProvider
-{
- /**
- * @var Config
- */
- private $config;
-
- /**
- * @var string
- */
- private $modified_timestamps = '';
-
- private const CLASS_CACHE_DIRECTORY = 'class_cache';
-
- public function __construct(Config $config)
- {
- $this->config = $config;
-
- $storage_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Storage' . DIRECTORY_SEPARATOR;
-
- $dependent_files = [
- $storage_dir . 'FileStorage.php',
- $storage_dir . 'FunctionLikeStorage.php',
- $storage_dir . 'ClassLikeStorage.php',
- $storage_dir . 'MethodStorage.php',
- ];
-
- if ($config->eventDispatcher->hasAfterClassLikeVisitHandlers()) {
- $dependent_files = array_merge($dependent_files, $config->plugin_paths);
- }
-
- foreach ($dependent_files as $dependent_file_path) {
- if (!file_exists($dependent_file_path)) {
- throw new UnexpectedValueException($dependent_file_path . ' must exist');
- }
-
- $this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
- }
-
- $this->modified_timestamps .= $this->config->computeHash();
- }
-
- public function writeToCache(ClassLikeStorage $storage, string $file_path, string $file_contents): void
- {
- $fq_classlike_name_lc = strtolower($storage->name);
-
- $storage->hash = $this->getCacheHash($file_path, $file_contents);
-
- // check if we have it in cache already
- $cached_value = $this->loadFromCache($fq_classlike_name_lc, $file_path);
- if (!is_null($cached_value) && $cached_value->hash === $storage->hash) {
- return;
- }
-
- $cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path, true);
- if ($this->config->use_igbinary) {
- file_put_contents($cache_location, igbinary_serialize($storage));
- } else {
- file_put_contents($cache_location, serialize($storage));
- }
- }
-
- public function getLatestFromCache(
- string $fq_classlike_name_lc,
- ?string $file_path,
- ?string $file_contents
- ): ClassLikeStorage {
- $cached_value = $this->loadFromCache($fq_classlike_name_lc, $file_path);
-
- if (!$cached_value) {
- throw new UnexpectedValueException($fq_classlike_name_lc . ' should be in cache');
- }
-
- $cache_hash = $this->getCacheHash($file_path, $file_contents);
-
- /** @psalm-suppress TypeDoesNotContainType */
- if (@get_class($cached_value) === '__PHP_Incomplete_Class'
- || $cache_hash !== $cached_value->hash
- ) {
- unlink($this->getCacheLocationForClass($fq_classlike_name_lc, $file_path));
-
- throw new UnexpectedValueException($fq_classlike_name_lc . ' should not be outdated');
- }
-
- return $cached_value;
- }
-
- private function getCacheHash(?string $_unused_file_path, ?string $file_contents): string
- {
- $data = $file_contents ? $file_contents : $this->modified_timestamps;
- return PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data);
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- private function loadFromCache(string $fq_classlike_name_lc, ?string $file_path): ?ClassLikeStorage
- {
- $cache_location = $this->getCacheLocationForClass($fq_classlike_name_lc, $file_path);
-
- if (file_exists($cache_location)) {
- if ($this->config->use_igbinary) {
- $storage = igbinary_unserialize((string)file_get_contents($cache_location));
-
- if ($storage instanceof ClassLikeStorage) {
- return $storage;
- }
-
- return null;
- }
-
- $storage = unserialize((string)file_get_contents($cache_location));
-
- if ($storage instanceof ClassLikeStorage) {
- return $storage;
- }
-
- return null;
- }
-
- return null;
- }
-
- private function getCacheLocationForClass(
- string $fq_classlike_name_lc,
- ?string $file_path,
- bool $create_directory = false
- ): string {
- $root_cache_directory = $this->config->getCacheDirectory();
-
- if (!$root_cache_directory) {
- throw new UnexpectedValueException('No cache directory defined');
- }
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::CLASS_CACHE_DIRECTORY;
-
- if ($create_directory && !is_dir($parser_cache_directory)) {
- mkdir($parser_cache_directory, 0777, true);
- }
-
- $data = $file_path ? strtolower($file_path) . ' ' : '';
- $data .= $fq_classlike_name_lc;
- $file_path_sha = PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data);
-
- return $parser_cache_directory
- . DIRECTORY_SEPARATOR
- . $file_path_sha
- . ($this->config->use_igbinary ? '-igbinary' : '');
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php
deleted file mode 100644
index bedf9b6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use InvalidArgumentException;
-use LogicException;
-use Psalm\Storage\ClassLikeStorage;
-
-use function array_merge;
-use function strtolower;
-
-/**
- * @internal
- */
-class ClassLikeStorageProvider
-{
- /**
- * Storing this statically is much faster (at least in PHP 7.2.1)
- *
- * @var array<string, ClassLikeStorage>
- */
- private static $storage = [];
-
- /**
- * @var array<string, ClassLikeStorage>
- */
- private static $new_storage = [];
-
- /**
- * @var ?ClassLikeStorageCacheProvider
- */
- public $cache;
-
- public function __construct(?ClassLikeStorageCacheProvider $cache = null)
- {
- $this->cache = $cache;
- }
-
- /**
- * @throws InvalidArgumentException when class does not exist
- */
- public function get(string $fq_classlike_name): ClassLikeStorage
- {
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
- if (!isset(self::$storage[$fq_classlike_name_lc])) {
- throw new InvalidArgumentException('Could not get class storage for ' . $fq_classlike_name_lc);
- }
-
- return self::$storage[$fq_classlike_name_lc];
- }
-
- public function has(string $fq_classlike_name): bool
- {
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- return isset(self::$storage[$fq_classlike_name_lc]);
- }
-
- public function exhume(string $fq_classlike_name, string $file_path, string $file_contents): ClassLikeStorage
- {
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- if (isset(self::$storage[$fq_classlike_name_lc])) {
- return self::$storage[$fq_classlike_name_lc];
- }
-
- if (!$this->cache) {
- throw new LogicException('Cannot exhume when there’s no cache');
- }
-
- $cached_value = $this->cache->getLatestFromCache($fq_classlike_name_lc, $file_path, $file_contents);
-
- self::$storage[$fq_classlike_name_lc] = $cached_value;
- self::$new_storage[$fq_classlike_name_lc] = $cached_value;
-
- return $cached_value;
- }
-
- /**
- * @return array<string, ClassLikeStorage>
- */
- public function getAll(): array
- {
- return self::$storage;
- }
-
- /**
- * @return array<string, ClassLikeStorage>
- */
- public function getNew(): array
- {
- return self::$new_storage;
- }
-
- /**
- * @param array<string, ClassLikeStorage> $more
- *
- */
- public function addMore(array $more): void
- {
- self::$new_storage = array_merge(self::$new_storage, $more);
- self::$storage = array_merge(self::$storage, $more);
- }
-
- public function makeNew(string $fq_classlike_name_lc): void
- {
- self::$new_storage[$fq_classlike_name_lc] = self::$storage[$fq_classlike_name_lc];
- }
-
- public function create(string $fq_classlike_name): ClassLikeStorage
- {
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- $storage = new ClassLikeStorage($fq_classlike_name);
- self::$storage[$fq_classlike_name_lc] = $storage;
- self::$new_storage[$fq_classlike_name_lc] = $storage;
-
- return $storage;
- }
-
- public function remove(string $fq_classlike_name): void
- {
- $fq_classlike_name_lc = strtolower($fq_classlike_name);
-
- unset(self::$storage[$fq_classlike_name_lc]);
- }
-
- public static function deleteAll(): void
- {
- self::$storage = [];
- self::$new_storage = [];
- }
-
- public static function populated(): void
- {
- self::$new_storage = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php
deleted file mode 100644
index a94071a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php
+++ /dev/null
@@ -1,75 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use function microtime;
-use function strpos;
-
-class FakeFileProvider extends FileProvider
-{
- /**
- * @var array<string, string>
- */
- public $fake_files = [];
-
- /**
- * @var array<string, int>
- */
- public $fake_file_times = [];
-
- public function fileExists(string $file_path): bool
- {
- return isset($this->fake_files[$file_path]) || parent::fileExists($file_path);
- }
-
- public function getContents(string $file_path, bool $go_to_source = false): string
- {
- if (!$go_to_source && isset($this->temp_files[$file_path])) {
- return $this->temp_files[$file_path];
- }
-
- return $this->fake_files[$file_path] ?? parent::getContents($file_path);
- }
-
- public function setContents(string $file_path, string $file_contents): void
- {
- $this->fake_files[$file_path] = $file_contents;
- }
-
- public function setOpenContents(string $file_path, string $file_contents): void
- {
- if (isset($this->fake_files[$file_path])) {
- $this->fake_files[$file_path] = $file_contents;
- }
- }
-
- public function getModifiedTime(string $file_path): int
- {
- return $this->fake_file_times[$file_path] ?? parent::getModifiedTime($file_path);
- }
-
- public function registerFile(string $file_path, string $file_contents): void
- {
- $this->fake_files[$file_path] = $file_contents;
- $this->fake_file_times[$file_path] = (int)microtime(true);
- }
-
- /**
- * @param array<string> $file_extensions
- * @param null|callable(string):bool $filter
- *
- * @return list<string>
- */
- public function getFilesInDir(string $dir_path, array $file_extensions, callable $filter = null): array
- {
- $file_paths = parent::getFilesInDir($dir_path, $file_extensions, $filter);
-
- foreach ($this->fake_files as $file_path => $_) {
- if (strpos($file_path, $dir_path) === 0) {
- $file_paths[] = $file_path;
- }
- }
-
- return $file_paths;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php
deleted file mode 100644
index 66d5aad..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php
+++ /dev/null
@@ -1,163 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use FilesystemIterator;
-use RecursiveCallbackFilterIterator;
-use RecursiveDirectoryIterator;
-use RecursiveIterator;
-use RecursiveIteratorIterator;
-use UnexpectedValueException;
-
-use function file_exists;
-use function file_get_contents;
-use function file_put_contents;
-use function filemtime;
-use function in_array;
-use function is_dir;
-
-use const DIRECTORY_SEPARATOR;
-
-class FileProvider
-{
- /**
- * @var array<string, string>
- */
- protected $temp_files = [];
-
- /**
- * @var array<string, string>
- */
- protected static $open_files = [];
-
- public function getContents(string $file_path, bool $go_to_source = false): string
- {
- if (!$go_to_source && isset($this->temp_files[$file_path])) {
- return $this->temp_files[$file_path];
- }
-
- if (isset(self::$open_files[$file_path])) {
- return self::$open_files[$file_path];
- }
-
- if (!file_exists($file_path)) {
- throw new UnexpectedValueException('File ' . $file_path . ' should exist to get contents');
- }
-
- if (is_dir($file_path)) {
- throw new UnexpectedValueException('File ' . $file_path . ' is a directory');
- }
-
- $file_contents = (string) file_get_contents($file_path);
-
- self::$open_files[$file_path] = $file_contents;
-
- return $file_contents;
- }
-
- public function setContents(string $file_path, string $file_contents): void
- {
- if (isset(self::$open_files[$file_path])) {
- self::$open_files[$file_path] = $file_contents;
- }
-
- if (isset($this->temp_files[$file_path])) {
- $this->temp_files[$file_path] = $file_contents;
- }
-
- file_put_contents($file_path, $file_contents);
- }
-
- public function setOpenContents(string $file_path, string $file_contents): void
- {
- if (isset(self::$open_files[$file_path])) {
- self::$open_files[$file_path] = $file_contents;
- }
- }
-
- public function getModifiedTime(string $file_path): int
- {
- if (!file_exists($file_path)) {
- throw new UnexpectedValueException('File should exist to get modified time');
- }
-
- return (int) filemtime($file_path);
- }
-
- public function addTemporaryFileChanges(string $file_path, string $new_content): void
- {
- $this->temp_files[$file_path] = $new_content;
- }
-
- public function removeTemporaryFileChanges(string $file_path): void
- {
- unset($this->temp_files[$file_path]);
- }
-
- public function openFile(string $file_path): void
- {
- self::$open_files[$file_path] = $this->getContents($file_path, true);
- }
-
- public function isOpen(string $file_path): bool
- {
- return isset($this->temp_files[$file_path]) || isset(self::$open_files[$file_path]);
- }
-
- public function closeFile(string $file_path): void
- {
- unset($this->temp_files[$file_path], self::$open_files[$file_path]);
- }
-
- public function fileExists(string $file_path): bool
- {
- return file_exists($file_path);
- }
-
- /**
- * @param array<string> $file_extensions
- * @param null|callable(string):bool $filter
- *
- * @return list<string>
- */
- public function getFilesInDir(string $dir_path, array $file_extensions, callable $filter = null): array
- {
- $file_paths = [];
-
- $iterator = new RecursiveDirectoryIterator(
- $dir_path,
- FilesystemIterator::CURRENT_AS_PATHNAME | FilesystemIterator::SKIP_DOTS
- );
-
- if ($filter !== null) {
- $iterator = new RecursiveCallbackFilterIterator(
- $iterator,
- /** @param mixed $_ */
- function (string $current, $_, RecursiveIterator $iterator) use ($filter): bool {
- if ($iterator->hasChildren()) {
- $path = $current . DIRECTORY_SEPARATOR;
- } else {
- $path = $current;
- }
-
- return $filter($path);
- }
- );
- }
-
- /** @var RecursiveDirectoryIterator */
- $iterator = new RecursiveIteratorIterator($iterator);
- $iterator->rewind();
-
- while ($iterator->valid()) {
- $extension = $iterator->getExtension();
- if (in_array($extension, $file_extensions, true)) {
- $file_paths[] = (string)$iterator->getRealPath();
- }
-
- $iterator->next();
- }
-
- return $file_paths;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php
deleted file mode 100644
index 616a1c2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceCacheProvider.php
+++ /dev/null
@@ -1,1007 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Psalm\Config;
-use Psalm\Internal\Codebase\Analyzer;
-use UnexpectedValueException;
-
-use function file_exists;
-use function file_get_contents;
-use function file_put_contents;
-use function igbinary_serialize;
-use function igbinary_unserialize;
-use function is_array;
-use function is_readable;
-use function mkdir;
-use function serialize;
-use function unserialize;
-
-use const DIRECTORY_SEPARATOR;
-
-/**
- * @psalm-import-type FileMapType from Analyzer
- *
- * Used to determine which files reference other files, necessary for using the --diff
- * option from the command line.
- */
-class FileReferenceCacheProvider
-{
- private const REFERENCE_CACHE_NAME = 'references';
- private const CLASSLIKE_FILE_CACHE_NAME = 'classlike_files';
- private const NONMETHOD_CLASS_REFERENCE_CACHE_NAME = 'file_class_references';
- private const METHOD_CLASS_REFERENCE_CACHE_NAME = 'method_class_references';
- private const ANALYZED_METHODS_CACHE_NAME = 'analyzed_methods';
- private const CLASS_METHOD_CACHE_NAME = 'class_method_references';
- private const METHOD_DEPENDENCIES_CACHE_NAME = 'class_method_dependencies';
- private const CLASS_PROPERTY_CACHE_NAME = 'class_property_references';
- private const CLASS_METHOD_RETURN_CACHE_NAME = 'class_method_return_references';
- private const FILE_METHOD_RETURN_CACHE_NAME = 'file_method_return_references';
- private const FILE_CLASS_MEMBER_CACHE_NAME = 'file_class_member_references';
- private const FILE_CLASS_PROPERTY_CACHE_NAME = 'file_class_property_references';
- private const ISSUES_CACHE_NAME = 'issues';
- private const FILE_MAPS_CACHE_NAME = 'file_maps';
- private const TYPE_COVERAGE_CACHE_NAME = 'type_coverage';
- private const CONFIG_HASH_CACHE_NAME = 'config';
- private const METHOD_MISSING_MEMBER_CACHE_NAME = 'method_missing_member';
- private const FILE_MISSING_MEMBER_CACHE_NAME = 'file_missing_member';
- private const UNKNOWN_MEMBER_CACHE_NAME = 'unknown_member_references';
- private const METHOD_PARAM_USE_CACHE_NAME = 'method_param_uses';
-
- /**
- * @var Config
- */
- protected $config;
-
- public function __construct(Config $config)
- {
- $this->config = $config;
- }
-
- public function hasConfigChanged(): bool
- {
- $new_hash = $this->config->computeHash();
- $has_changed = $new_hash !== $this->getConfigHashCache();
- $this->setConfigHashCache($new_hash);
- return $has_changed;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedFileReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::REFERENCE_CACHE_NAME;
-
- if (!is_readable($reference_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $reference_cache = igbinary_unserialize((string) file_get_contents($reference_cache_location));
- } else {
- $reference_cache = unserialize((string) file_get_contents($reference_cache_location));
- }
-
- if (!is_array($reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedClassLikeFiles(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASSLIKE_FILE_CACHE_NAME;
-
- if (!is_readable($reference_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $reference_cache = igbinary_unserialize((string) file_get_contents($reference_cache_location));
- } else {
- $reference_cache = unserialize((string) file_get_contents($reference_cache_location));
- }
-
- if (!is_array($reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedNonMethodClassReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::NONMETHOD_CLASS_REFERENCE_CACHE_NAME;
-
- if (!is_readable($reference_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $reference_cache = igbinary_unserialize((string) file_get_contents($reference_cache_location));
- } else {
- $reference_cache = unserialize((string) file_get_contents($reference_cache_location));
- }
-
- if (!is_array($reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodClassReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_CLASS_REFERENCE_CACHE_NAME;
-
- if (!is_readable($reference_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $reference_cache = igbinary_unserialize((string) file_get_contents($reference_cache_location));
- } else {
- $reference_cache = unserialize((string) file_get_contents($reference_cache_location));
- }
-
- if (!is_array($reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodMemberReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_METHOD_CACHE_NAME;
-
- if (!is_readable($class_member_cache_location)) {
- return null;
- }
-
- $class_member_reference_cache = (string) file_get_contents($class_member_cache_location);
- if ($this->config->use_igbinary) {
- $class_member_reference_cache = igbinary_unserialize($class_member_reference_cache);
- } else {
- $class_member_reference_cache = unserialize($class_member_reference_cache);
- }
-
- if (!is_array($class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodDependencies(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $method_dependencies_cache_location
- = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_DEPENDENCIES_CACHE_NAME;
-
- if (!is_readable($method_dependencies_cache_location)) {
- return null;
- }
-
- $method_dependencies_cache = (string) file_get_contents($method_dependencies_cache_location);
- if ($this->config->use_igbinary) {
- $method_dependencies_cache = igbinary_unserialize($method_dependencies_cache);
- } else {
- $method_dependencies_cache = unserialize($method_dependencies_cache);
- }
-
- if (!is_array($method_dependencies_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $method_dependencies_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodPropertyReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_PROPERTY_CACHE_NAME;
-
- if (!is_readable($class_member_cache_location)) {
- return null;
- }
-
- $class_member_reference_cache = (string) file_get_contents($class_member_cache_location);
- if ($this->config->use_igbinary) {
- $class_member_reference_cache = igbinary_unserialize($class_member_reference_cache);
- } else {
- $class_member_reference_cache = unserialize($class_member_reference_cache);
- }
-
- if (!is_array($class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodMethodReturnReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_METHOD_RETURN_CACHE_NAME;
-
- if (!is_readable($class_member_cache_location)) {
- return null;
- }
-
- $class_member_reference_cache = (string) file_get_contents($class_member_cache_location);
- if ($this->config->use_igbinary) {
- $class_member_reference_cache = igbinary_unserialize($class_member_reference_cache);
- } else {
- $class_member_reference_cache = unserialize($class_member_reference_cache);
- }
-
- if (!is_array($class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodMissingMemberReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_MISSING_MEMBER_CACHE_NAME;
-
- if (!is_readable($class_member_cache_location)) {
- return null;
- }
-
- $class_member_reference_cache = (string) file_get_contents($class_member_cache_location);
- if ($this->config->use_igbinary) {
- $class_member_reference_cache = igbinary_unserialize($class_member_reference_cache);
- } else {
- $class_member_reference_cache = unserialize($class_member_reference_cache);
- }
-
- if (!is_array($class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedFileMemberReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $file_class_member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_CLASS_MEMBER_CACHE_NAME;
-
- if (!is_readable($file_class_member_cache_location)) {
- return null;
- }
-
- $file_class_member_reference_cache = (string) file_get_contents($file_class_member_cache_location);
- if ($this->config->use_igbinary) {
- $file_class_member_reference_cache = igbinary_unserialize($file_class_member_reference_cache);
- } else {
- $file_class_member_reference_cache = unserialize($file_class_member_reference_cache);
- }
-
- if (!is_array($file_class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $file_class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedFilePropertyReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $file_class_member_cache_location = $cache_directory
- . DIRECTORY_SEPARATOR
- . self::FILE_CLASS_PROPERTY_CACHE_NAME;
-
- if (!is_readable($file_class_member_cache_location)) {
- return null;
- }
-
- $file_class_member_reference_cache = (string) file_get_contents($file_class_member_cache_location);
- if ($this->config->use_igbinary) {
- $file_class_member_reference_cache = igbinary_unserialize($file_class_member_reference_cache);
- } else {
- $file_class_member_reference_cache = unserialize($file_class_member_reference_cache);
- }
-
- if (!is_array($file_class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $file_class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedFileMethodReturnReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $file_class_member_cache_location = $cache_directory
- . DIRECTORY_SEPARATOR
- . self::FILE_METHOD_RETURN_CACHE_NAME;
-
- if (!is_readable($file_class_member_cache_location)) {
- return null;
- }
-
- $file_class_member_reference_cache = (string) file_get_contents($file_class_member_cache_location);
- if ($this->config->use_igbinary) {
- $file_class_member_reference_cache = igbinary_unserialize($file_class_member_reference_cache);
- } else {
- $file_class_member_reference_cache = unserialize($file_class_member_reference_cache);
- }
-
- if (!is_array($file_class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $file_class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedFileMissingMemberReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $file_class_member_cache_location
- = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MISSING_MEMBER_CACHE_NAME;
-
- if (!is_readable($file_class_member_cache_location)) {
- return null;
- }
-
- $file_class_member_reference_cache = (string) file_get_contents($file_class_member_cache_location);
- if ($this->config->use_igbinary) {
- $file_class_member_reference_cache = igbinary_unserialize($file_class_member_reference_cache);
- } else {
- $file_class_member_reference_cache = unserialize($file_class_member_reference_cache);
- }
-
- if (!is_array($file_class_member_reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $file_class_member_reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMixedMemberNameReferences(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::UNKNOWN_MEMBER_CACHE_NAME;
-
- if (!is_readable($reference_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $reference_cache = igbinary_unserialize((string) file_get_contents($reference_cache_location));
- } else {
- $reference_cache = unserialize((string) file_get_contents($reference_cache_location));
- }
-
- if (!is_array($reference_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedMethodParamUses(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_PARAM_USE_CACHE_NAME;
-
- if (!is_readable($reference_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $reference_cache = igbinary_unserialize((string) file_get_contents($reference_cache_location));
- } else {
- $reference_cache = unserialize((string) file_get_contents($reference_cache_location));
- }
-
- if (!is_array($reference_cache)) {
- throw new UnexpectedValueException('The method param use cache must be an array');
- }
-
- return $reference_cache;
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- public function getCachedIssues(): ?array
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return null;
- }
-
- $issues_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::ISSUES_CACHE_NAME;
-
- if (!is_readable($issues_cache_location)) {
- return null;
- }
-
- if ($this->config->use_igbinary) {
- $issues_cache = igbinary_unserialize((string) file_get_contents($issues_cache_location));
- } else {
- $issues_cache = unserialize((string) file_get_contents($issues_cache_location));
- }
-
- if (!is_array($issues_cache)) {
- throw new UnexpectedValueException('The reference cache must be an array');
- }
-
- return $issues_cache;
- }
-
- public function setCachedFileReferences(array $file_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::REFERENCE_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($reference_cache_location, igbinary_serialize($file_references));
- } else {
- file_put_contents($reference_cache_location, serialize($file_references));
- }
- }
-
- public function setCachedClassLikeFiles(array $file_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASSLIKE_FILE_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($reference_cache_location, igbinary_serialize($file_references));
- } else {
- file_put_contents($reference_cache_location, serialize($file_references));
- }
- }
-
- public function setCachedNonMethodClassReferences(array $file_class_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::NONMETHOD_CLASS_REFERENCE_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($reference_cache_location, igbinary_serialize($file_class_references));
- } else {
- file_put_contents($reference_cache_location, serialize($file_class_references));
- }
- }
-
- public function setCachedMethodClassReferences(array $method_class_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_CLASS_REFERENCE_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($reference_cache_location, igbinary_serialize($method_class_references));
- } else {
- file_put_contents($reference_cache_location, serialize($method_class_references));
- }
- }
-
- public function setCachedMethodMemberReferences(array $member_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_METHOD_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($member_references));
- } else {
- file_put_contents($member_cache_location, serialize($member_references));
- }
- }
-
- public function setCachedMethodDependencies(array $member_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_DEPENDENCIES_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($member_references));
- } else {
- file_put_contents($member_cache_location, serialize($member_references));
- }
- }
-
- public function setCachedMethodPropertyReferences(array $property_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_PROPERTY_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($property_references));
- } else {
- file_put_contents($member_cache_location, serialize($property_references));
- }
- }
-
- public function setCachedMethodMethodReturnReferences(array $method_return_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CLASS_METHOD_RETURN_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($method_return_references));
- } else {
- file_put_contents($member_cache_location, serialize($method_return_references));
- }
- }
-
- public function setCachedMethodMissingMemberReferences(array $member_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_MISSING_MEMBER_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($member_references));
- } else {
- file_put_contents($member_cache_location, serialize($member_references));
- }
- }
-
- public function setCachedFileMemberReferences(array $member_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_CLASS_MEMBER_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($member_references));
- } else {
- file_put_contents($member_cache_location, serialize($member_references));
- }
- }
-
- public function setCachedFilePropertyReferences(array $property_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_CLASS_PROPERTY_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($property_references));
- } else {
- file_put_contents($member_cache_location, serialize($property_references));
- }
- }
-
- public function setCachedFileMethodReturnReferences(array $method_return_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_METHOD_RETURN_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($method_return_references));
- } else {
- file_put_contents($member_cache_location, serialize($method_return_references));
- }
- }
-
- public function setCachedFileMissingMemberReferences(array $member_references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $member_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MISSING_MEMBER_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($member_cache_location, igbinary_serialize($member_references));
- } else {
- file_put_contents($member_cache_location, serialize($member_references));
- }
- }
-
- public function setCachedMixedMemberNameReferences(array $references): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::UNKNOWN_MEMBER_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($reference_cache_location, igbinary_serialize($references));
- } else {
- file_put_contents($reference_cache_location, serialize($references));
- }
- }
-
- public function setCachedMethodParamUses(array $uses): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $reference_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::METHOD_PARAM_USE_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($reference_cache_location, igbinary_serialize($uses));
- } else {
- file_put_contents($reference_cache_location, serialize($uses));
- }
- }
-
- public function setCachedIssues(array $issues): void
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $issues_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::ISSUES_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($issues_cache_location, igbinary_serialize($issues));
- } else {
- file_put_contents($issues_cache_location, serialize($issues));
- }
- }
-
- /**
- * @return array<string, array<string, int>>|false
- */
- public function getAnalyzedMethodCache()
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- $analyzed_methods_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::ANALYZED_METHODS_CACHE_NAME;
-
- if ($cache_directory
- && file_exists($analyzed_methods_cache_location)
- ) {
- if ($this->config->use_igbinary) {
- /** @var array<string, array<string, int>> */
- return igbinary_unserialize(file_get_contents($analyzed_methods_cache_location));
- } else {
- /** @var array<string, array<string, int>> */
- return unserialize(file_get_contents($analyzed_methods_cache_location));
- }
- }
-
- return false;
- }
-
- /**
- * @param array<string, array<string, int>> $analyzed_methods
- */
- public function setAnalyzedMethodCache(array $analyzed_methods): void
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if ($cache_directory) {
- $analyzed_methods_cache_location = $cache_directory
- . DIRECTORY_SEPARATOR
- . self::ANALYZED_METHODS_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($analyzed_methods_cache_location, igbinary_serialize($analyzed_methods));
- } else {
- file_put_contents($analyzed_methods_cache_location, serialize($analyzed_methods));
- }
- }
- }
-
- /**
- * @return array<string, FileMapType>|false
- */
- public function getFileMapCache()
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- $file_maps_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MAPS_CACHE_NAME;
-
- if ($cache_directory
- && file_exists($file_maps_cache_location)
- ) {
- if ($this->config->use_igbinary) {
- /**
- * @var array<string, FileMapType>
- */
- $file_maps_cache = igbinary_unserialize(file_get_contents($file_maps_cache_location));
- } else {
- /**
- * @var array<string, FileMapType>
- */
- $file_maps_cache = unserialize(file_get_contents($file_maps_cache_location));
- }
-
- return $file_maps_cache;
- }
-
- return false;
- }
-
- /**
- * @param array<string, FileMapType> $file_maps
- */
- public function setFileMapCache(array $file_maps): void
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if ($cache_directory) {
- $file_maps_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::FILE_MAPS_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($file_maps_cache_location, igbinary_serialize($file_maps));
- } else {
- file_put_contents($file_maps_cache_location, serialize($file_maps));
- }
- }
- }
-
- /**
- * @return array<string, array{int, int}>|false
- */
- public function getTypeCoverage()
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- $type_coverage_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::TYPE_COVERAGE_CACHE_NAME;
-
- if ($cache_directory
- && file_exists($type_coverage_cache_location)
- ) {
- if ($this->config->use_igbinary) {
- /** @var array<string, array{int, int}> */
- $type_coverage_cache = igbinary_unserialize(file_get_contents($type_coverage_cache_location));
- } else {
- /** @var array<string, array{int, int}> */
- $type_coverage_cache = unserialize(file_get_contents($type_coverage_cache_location));
- }
-
- return $type_coverage_cache;
- }
-
- return false;
- }
-
- /**
- * @param array<string, array{int, int}> $mixed_counts
- */
- public function setTypeCoverage(array $mixed_counts): void
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if ($cache_directory) {
- $type_coverage_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::TYPE_COVERAGE_CACHE_NAME;
-
- if ($this->config->use_igbinary) {
- file_put_contents($type_coverage_cache_location, igbinary_serialize($mixed_counts));
- } else {
- file_put_contents($type_coverage_cache_location, serialize($mixed_counts));
- }
- }
- }
-
- /**
- * @return string|false
- */
- public function getConfigHashCache()
- {
- $cache_directory = $this->config->getCacheDirectory();
-
- $config_hash_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CONFIG_HASH_CACHE_NAME;
-
- if ($cache_directory
- && file_exists($config_hash_cache_location)
- ) {
- return file_get_contents($config_hash_cache_location);
- }
-
- return false;
- }
-
- public function setConfigHashCache(string $hash): void
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if ($cache_directory) {
- if (!file_exists($cache_directory)) {
- mkdir($cache_directory, 0777, true);
- }
-
- $config_hash_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::CONFIG_HASH_CACHE_NAME;
-
- file_put_contents(
- $config_hash_cache_location,
- $hash
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceProvider.php
deleted file mode 100644
index 431726c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileReferenceProvider.php
+++ /dev/null
@@ -1,1320 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\IssueData;
-use Psalm\Internal\Codebase\Analyzer;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_keys;
-use function array_merge;
-use function array_unique;
-use function explode;
-use function file_exists;
-
-/**
- * @psalm-import-type FileMapType from Analyzer
- *
- * Used to determine which files reference other files, necessary for using the --diff
- * option from the command line.
- */
-class FileReferenceProvider
-{
- /**
- * @var bool
- */
- private $loaded_from_cache = false;
-
- /**
- * A lookup table used for getting all the references to a class not inside a method
- * indexed by file
- *
- * @var array<string, array<string,bool>>
- */
- private static $nonmethod_references_to_classes = [];
-
- /**
- * A lookup table used for getting all the methods that reference a class
- *
- * @var array<string, array<string,bool>>
- */
- private static $method_references_to_classes = [];
-
- /**
- * A lookup table used for getting all the files that reference a class member
- *
- * @var array<string, array<string,bool>>
- */
- private static $file_references_to_class_members = [];
-
- /**
- * A lookup table used for getting all the files that reference a class property
- *
- * @var array<string, array<string,bool>>
- */
- private static $file_references_to_class_properties = [];
-
- /**
- * A lookup table used for getting all the files that reference a method's return value
- *
- * @var array<string, array<string,bool>>
- */
- private static $file_references_to_method_returns = [];
-
- /**
- * A lookup table used for getting all the files that reference a missing class member
- *
- * @var array<string, array<string,bool>>
- */
- private static $file_references_to_missing_class_members = [];
-
- /**
- * @var array<string, array<string, true>>
- */
- private static $files_inheriting_classes = [];
-
- /**
- * A list of all files deleted since the last successful run
- *
- * @var array<int, string>|null
- */
- private static $deleted_files;
-
- /**
- * A lookup table used for getting all the files referenced by a file
- *
- * @var array<string, array{a:array<int, string>, i:array<int, string>}>
- */
- private static $file_references = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private static $method_references_to_class_members = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private static $method_dependencies = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private static $method_references_to_class_properties = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private static $method_references_to_method_returns = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private static $method_references_to_missing_class_members = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private static $references_to_mixed_member_names = [];
-
- /**
- * @var array<string, array<int, CodeLocation>>
- */
- private static $class_method_locations = [];
-
- /**
- * @var array<string, array<int, CodeLocation>>
- */
- private static $class_property_locations = [];
-
- /**
- * @var array<string, array<int, CodeLocation>>
- */
- private static $class_locations = [];
-
- /**
- * @var array<string, string>
- */
- private static $classlike_files = [];
-
- /**
- * @var array<string, array<string, int>>
- */
- private static $analyzed_methods = [];
-
- /**
- * @var array<string, array<int, IssueData>>
- */
- private static $issues = [];
-
- /**
- * @var array<string, FileMapType>
- */
- private static $file_maps = [];
-
- /**
- * @var array<string, array{int, int}>
- */
- private static $mixed_counts = [];
-
- /**
- * @var array<string, array<int, array<string, bool>>>
- */
- private static $method_param_uses = [];
-
- /**
- * @var ?FileReferenceCacheProvider
- */
- public $cache;
-
- public function __construct(?FileReferenceCacheProvider $cache = null)
- {
- $this->cache = $cache;
- }
-
- /**
- * @return array<int, string>
- */
- public function getDeletedReferencedFiles(): array
- {
- if (self::$deleted_files === null) {
- self::$deleted_files = array_filter(
- array_keys(self::$file_references),
- function (string $file_name): bool {
- return !file_exists($file_name);
- }
- );
- }
-
- return self::$deleted_files;
- }
-
- /**
- * @param lowercase-string $fq_class_name_lc
- */
- public function addNonMethodReferenceToClass(string $source_file, string $fq_class_name_lc): void
- {
- self::$nonmethod_references_to_classes[$fq_class_name_lc][$source_file] = true;
- }
-
- /**
- * @return array<string, array<string,bool>>
- */
- public function getAllNonMethodReferencesToClasses(): array
- {
- return self::$nonmethod_references_to_classes;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addNonMethodReferencesToClasses(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$nonmethod_references_to_classes[$key])) {
- self::$nonmethod_references_to_classes[$key] = array_merge(
- $reference,
- self::$nonmethod_references_to_classes[$key]
- );
- } else {
- self::$nonmethod_references_to_classes[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, string> $map
- */
- public function addClassLikeFiles(array $map): void
- {
- self::$classlike_files += $map;
- }
-
- public function addFileReferenceToClassMember(
- string $source_file,
- string $referenced_member_id,
- bool $inside_return
- ): void {
- self::$file_references_to_class_members[$referenced_member_id][$source_file] = true;
-
- if ($inside_return) {
- self::$file_references_to_method_returns[$referenced_member_id][$source_file] = true;
- }
- }
-
- public function addFileReferenceToClassProperty(string $source_file, string $referenced_property_id): void
- {
- self::$file_references_to_class_properties[$referenced_property_id][$source_file] = true;
- }
-
- public function addFileReferenceToMissingClassMember(string $source_file, string $referenced_member_id): void
- {
- self::$file_references_to_missing_class_members[$referenced_member_id][$source_file] = true;
- }
-
- /**
- * @return array<string, array<string,bool>>
- */
- public function getAllFileReferencesToClassMembers(): array
- {
- return self::$file_references_to_class_members;
- }
-
- /**
- * @return array<string, array<string,bool>>
- */
- public function getAllFileReferencesToClassProperties(): array
- {
- return self::$file_references_to_class_properties;
- }
-
- /**
- * @return array<string, array<string,bool>>
- */
- public function getAllFileReferencesToMethodReturns(): array
- {
- return self::$file_references_to_method_returns;
- }
-
- /**
- * @return array<string, array<string,bool>>
- */
- public function getAllFileReferencesToMissingClassMembers(): array
- {
- return self::$file_references_to_missing_class_members;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addFileReferencesToClassMembers(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$file_references_to_class_members[$key])) {
- self::$file_references_to_class_members[$key] = array_merge(
- $reference,
- self::$file_references_to_class_members[$key]
- );
- } else {
- self::$file_references_to_class_members[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addFileReferencesToClassProperties(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$file_references_to_class_properties[$key])) {
- self::$file_references_to_class_properties[$key] = array_merge(
- $reference,
- self::$file_references_to_class_properties[$key]
- );
- } else {
- self::$file_references_to_class_properties[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addFileReferencesToMethodReturns(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$file_references_to_method_returns[$key])) {
- self::$file_references_to_method_returns[$key] = array_merge(
- $reference,
- self::$file_references_to_method_returns[$key]
- );
- } else {
- self::$file_references_to_method_returns[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addFileReferencesToMissingClassMembers(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$file_references_to_missing_class_members[$key])) {
- self::$file_references_to_missing_class_members[$key] = array_merge(
- $reference,
- self::$file_references_to_missing_class_members[$key]
- );
- } else {
- self::$file_references_to_missing_class_members[$key] = $reference;
- }
- }
- }
-
- public function addFileInheritanceToClass(string $source_file, string $fq_class_name_lc): void
- {
- self::$files_inheriting_classes[$fq_class_name_lc][$source_file] = true;
- }
-
- public function addMethodParamUse(string $method_id, int $offset, string $referencing_method_id): void
- {
- self::$method_param_uses[$method_id][$offset][$referencing_method_id] = true;
- }
-
- /**
- * @return array<int, string>
- */
- private function calculateFilesReferencingFile(Codebase $codebase, string $file): array
- {
- $referenced_files = [];
-
- $file_classes = ClassLikeAnalyzer::getClassesForFile($codebase, $file);
-
- foreach ($file_classes as $file_class_lc => $_) {
- if (isset(self::$nonmethod_references_to_classes[$file_class_lc])) {
- $new_files = array_keys(self::$nonmethod_references_to_classes[$file_class_lc]);
-
- $referenced_files = array_merge(
- $referenced_files,
- $new_files
- );
- }
-
- if (isset(self::$method_references_to_classes[$file_class_lc])) {
- $new_referencing_methods = array_keys(self::$method_references_to_classes[$file_class_lc]);
-
- foreach ($new_referencing_methods as $new_referencing_method_id) {
- $fq_class_name_lc = explode('::', $new_referencing_method_id)[0];
-
- try {
- $referenced_files[] = $codebase->scanner->getClassLikeFilePath($fq_class_name_lc);
- } catch (UnexpectedValueException $e) {
- if (isset(self::$classlike_files[$fq_class_name_lc])) {
- $referenced_files[] = self::$classlike_files[$fq_class_name_lc];
- }
- }
- }
- }
- }
-
- return array_unique($referenced_files);
- }
-
- /**
- * @return array<int, string>
- */
- private function calculateFilesInheritingFile(Codebase $codebase, string $file): array
- {
- $referenced_files = [];
-
- $file_classes = ClassLikeAnalyzer::getClassesForFile($codebase, $file);
-
- foreach ($file_classes as $file_class_lc => $_) {
- if (isset(self::$files_inheriting_classes[$file_class_lc])) {
- $referenced_files = array_merge(
- $referenced_files,
- array_keys(self::$files_inheriting_classes[$file_class_lc])
- );
- }
- }
-
- return array_unique($referenced_files);
- }
-
- public function removeDeletedFilesFromReferences(): void
- {
- $deleted_files = $this->getDeletedReferencedFiles();
-
- if ($deleted_files) {
- foreach ($deleted_files as $file) {
- unset(self::$file_references[$file]);
- }
-
- if ($this->cache) {
- $this->cache->setCachedFileReferences(self::$file_references);
- }
- }
- }
-
- /**
- * @return array<int, string>
- */
- public function getFilesReferencingFile(string $file): array
- {
- return self::$file_references[$file]['a'] ?? [];
- }
-
- /**
- * @return array<int, string>
- */
- public function getFilesInheritingFromFile(string $file): array
- {
- return self::$file_references[$file]['i'] ?? [];
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAllMethodReferencesToClassMembers(): array
- {
- return self::$method_references_to_class_members;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAllMethodDependencies(): array
- {
- return self::$method_dependencies;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAllMethodReferencesToClassProperties(): array
- {
- return self::$method_references_to_class_properties;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAllMethodReferencesToMethodReturns(): array
- {
- return self::$method_references_to_method_returns;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAllMethodReferencesToClasses(): array
- {
- return self::$method_references_to_classes;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getAllMethodReferencesToMissingClassMembers(): array
- {
- return self::$method_references_to_missing_class_members;
- }
-
- /**
- * @return array<string, array<string,bool>>
- */
- public function getAllReferencesToMixedMemberNames(): array
- {
- return self::$references_to_mixed_member_names;
- }
-
- /**
- * @return array<string, array<int, array<string, bool>>>
- */
- public function getAllMethodParamUses(): array
- {
- return self::$method_param_uses;
- }
-
- /**
- * @psalm-suppress MixedPropertyTypeCoercion
- */
- public function loadReferenceCache(bool $force_reload = true): bool
- {
- if ($this->cache && (!$this->loaded_from_cache || $force_reload)) {
- $this->loaded_from_cache = true;
-
- $file_references = $this->cache->getCachedFileReferences();
-
- if ($file_references === null) {
- return false;
- }
-
- self::$file_references = $file_references;
-
- $nonmethod_references_to_classes = $this->cache->getCachedNonMethodClassReferences();
-
- if ($nonmethod_references_to_classes === null) {
- return false;
- }
-
- self::$nonmethod_references_to_classes = $nonmethod_references_to_classes;
-
- $method_references_to_classes = $this->cache->getCachedMethodClassReferences();
-
- if ($method_references_to_classes === null) {
- return false;
- }
-
- self::$method_references_to_classes = $method_references_to_classes;
-
- $method_references_to_class_members = $this->cache->getCachedMethodMemberReferences();
-
- if ($method_references_to_class_members === null) {
- return false;
- }
-
- self::$method_references_to_class_members = $method_references_to_class_members;
-
- $method_dependencies = $this->cache->getCachedMethodDependencies();
-
- if ($method_dependencies === null) {
- return false;
- }
-
- self::$method_dependencies = $method_dependencies;
-
- $method_references_to_class_properties = $this->cache->getCachedMethodPropertyReferences();
-
- if ($method_references_to_class_properties === null) {
- return false;
- }
-
- self::$method_references_to_class_properties = $method_references_to_class_properties;
-
- $method_references_to_method_returns = $this->cache->getCachedMethodMethodReturnReferences();
-
- if ($method_references_to_method_returns === null) {
- return false;
- }
-
- self::$method_references_to_method_returns = $method_references_to_method_returns;
-
- $method_references_to_missing_class_members = $this->cache->getCachedMethodMissingMemberReferences();
-
- if ($method_references_to_missing_class_members === null) {
- return false;
- }
-
- self::$method_references_to_missing_class_members = $method_references_to_missing_class_members;
-
- $file_references_to_class_members = $this->cache->getCachedFileMemberReferences();
-
- if ($file_references_to_class_members === null) {
- return false;
- }
-
- self::$file_references_to_class_members = $file_references_to_class_members;
-
- $file_references_to_class_properties = $this->cache->getCachedFilePropertyReferences();
-
- if ($file_references_to_class_properties === null) {
- return false;
- }
-
- self::$file_references_to_class_properties = $file_references_to_class_properties;
-
- $file_references_to_method_returns = $this->cache->getCachedFileMethodReturnReferences();
-
- if ($file_references_to_method_returns === null) {
- return false;
- }
-
- self::$file_references_to_method_returns = $file_references_to_method_returns;
-
- $file_references_to_missing_class_members = $this->cache->getCachedFileMissingMemberReferences();
-
- if ($file_references_to_missing_class_members === null) {
- return false;
- }
-
- self::$file_references_to_missing_class_members = $file_references_to_missing_class_members;
-
- $references_to_mixed_member_names = $this->cache->getCachedMixedMemberNameReferences();
-
- if ($references_to_mixed_member_names === null) {
- return false;
- }
-
- self::$references_to_mixed_member_names = $references_to_mixed_member_names;
-
- $analyzed_methods = $this->cache->getAnalyzedMethodCache();
-
- if ($analyzed_methods === false) {
- return false;
- }
-
- self::$analyzed_methods = $analyzed_methods;
-
- $issues = $this->cache->getCachedIssues();
-
- if ($issues === null) {
- return false;
- }
-
- self::$issues = $issues;
-
- $method_param_uses = $this->cache->getCachedMethodParamUses();
-
- if ($method_param_uses === null) {
- return false;
- }
-
- self::$method_param_uses = $method_param_uses;
-
- $mixed_counts = $this->cache->getTypeCoverage();
-
- if ($mixed_counts === false) {
- return false;
- }
-
- self::$mixed_counts = $mixed_counts;
-
- $classlike_files = $this->cache->getCachedClassLikeFiles();
-
- if ($classlike_files === null) {
- return false;
- }
-
- self::$classlike_files = $classlike_files;
-
- self::$file_maps = $this->cache->getFileMapCache() ?: [];
-
- return true;
- }
-
- return false;
- }
-
- /**
- * @param array<string, string|bool> $visited_files
- *
- */
- public function updateReferenceCache(Codebase $codebase, array $visited_files): void
- {
- foreach ($visited_files as $file => $_) {
- $all_file_references = array_unique(
- array_merge(
- self::$file_references[$file]['a'] ?? [],
- $this->calculateFilesReferencingFile($codebase, $file)
- )
- );
-
- $inheritance_references = array_unique(
- array_merge(
- self::$file_references[$file]['i'] ?? [],
- $this->calculateFilesInheritingFile($codebase, $file)
- )
- );
-
- self::$file_references[$file] = [
- 'a' => $all_file_references,
- 'i' => $inheritance_references,
- ];
- }
-
- if ($this->cache) {
- $this->cache->setCachedFileReferences(self::$file_references);
- $this->cache->setCachedMethodClassReferences(self::$method_references_to_classes);
- $this->cache->setCachedNonMethodClassReferences(self::$nonmethod_references_to_classes);
- $this->cache->setCachedMethodMemberReferences(self::$method_references_to_class_members);
- $this->cache->setCachedMethodDependencies(self::$method_dependencies);
- $this->cache->setCachedMethodPropertyReferences(self::$method_references_to_class_properties);
- $this->cache->setCachedMethodMethodReturnReferences(self::$method_references_to_method_returns);
- $this->cache->setCachedFileMemberReferences(self::$file_references_to_class_members);
- $this->cache->setCachedFilePropertyReferences(self::$file_references_to_class_properties);
- $this->cache->setCachedFileMethodReturnReferences(self::$file_references_to_method_returns);
- $this->cache->setCachedMethodMissingMemberReferences(self::$method_references_to_missing_class_members);
- $this->cache->setCachedFileMissingMemberReferences(self::$file_references_to_missing_class_members);
- $this->cache->setCachedMixedMemberNameReferences(self::$references_to_mixed_member_names);
- $this->cache->setCachedMethodParamUses(self::$method_param_uses);
- $this->cache->setCachedIssues(self::$issues);
- $this->cache->setCachedClassLikeFiles(self::$classlike_files);
- $this->cache->setFileMapCache(self::$file_maps);
- $this->cache->setTypeCoverage(self::$mixed_counts);
- $this->cache->setAnalyzedMethodCache(self::$analyzed_methods);
- }
- }
-
- /**
- * @param lowercase-string $fq_class_name_lc
- */
- public function addMethodReferenceToClass(string $calling_function_id, string $fq_class_name_lc): void
- {
- if (!isset(self::$method_references_to_classes[$fq_class_name_lc])) {
- self::$method_references_to_classes[$fq_class_name_lc] = [$calling_function_id => true];
- } else {
- self::$method_references_to_classes[$fq_class_name_lc][$calling_function_id] = true;
- }
- }
-
- public function addMethodReferenceToClassMember(
- string $calling_function_id,
- string $referenced_member_id,
- bool $inside_return
- ): void {
- if (!isset(self::$method_references_to_class_members[$referenced_member_id])) {
- self::$method_references_to_class_members[$referenced_member_id] = [$calling_function_id => true];
- } else {
- self::$method_references_to_class_members[$referenced_member_id][$calling_function_id] = true;
- }
-
- if ($inside_return) {
- if (!isset(self::$method_references_to_method_returns[$referenced_member_id])) {
- self::$method_references_to_method_returns[$referenced_member_id] = [$calling_function_id => true];
- } else {
- self::$method_references_to_method_returns[$referenced_member_id][$calling_function_id] = true;
- }
- }
- }
-
- public function addMethodDependencyToClassMember(
- string $calling_function_id,
- string $referenced_member_id
- ): void {
- if (!isset(self::$method_dependencies[$referenced_member_id])) {
- self::$method_dependencies[$referenced_member_id] = [$calling_function_id => true];
- } else {
- self::$method_dependencies[$referenced_member_id][$calling_function_id] = true;
- }
- }
-
- public function addMethodReferenceToClassProperty(string $calling_function_id, string $referenced_property_id): void
- {
- if (!isset(self::$method_references_to_class_properties[$referenced_property_id])) {
- self::$method_references_to_class_properties[$referenced_property_id] = [$calling_function_id => true];
- } else {
- self::$method_references_to_class_properties[$referenced_property_id][$calling_function_id] = true;
- }
- }
-
- public function addMethodReferenceToMissingClassMember(
- string $calling_function_id,
- string $referenced_member_id
- ): void {
- if (!isset(self::$method_references_to_missing_class_members[$referenced_member_id])) {
- self::$method_references_to_missing_class_members[$referenced_member_id] = [$calling_function_id => true];
- } else {
- self::$method_references_to_missing_class_members[$referenced_member_id][$calling_function_id] = true;
- }
- }
-
- public function addCallingLocationForClassMethod(CodeLocation $code_location, string $referenced_member_id): void
- {
- if (!isset(self::$class_method_locations[$referenced_member_id])) {
- self::$class_method_locations[$referenced_member_id] = [$code_location];
- } else {
- self::$class_method_locations[$referenced_member_id][] = $code_location;
- }
- }
-
- public function addCallingLocationForClassProperty(
- CodeLocation $code_location,
- string $referenced_property_id
- ): void {
- if (!isset(self::$class_property_locations[$referenced_property_id])) {
- self::$class_property_locations[$referenced_property_id] = [$code_location];
- } else {
- self::$class_property_locations[$referenced_property_id][] = $code_location;
- }
- }
-
- public function addCallingLocationForClass(CodeLocation $code_location, string $referenced_class): void
- {
- if (!isset(self::$class_locations[$referenced_class])) {
- self::$class_locations[$referenced_class] = [$code_location];
- } else {
- self::$class_locations[$referenced_class][] = $code_location;
- }
- }
-
- public function isClassMethodReferenced(string $method_id): bool
- {
- return !empty(self::$file_references_to_class_members[$method_id])
- || !empty(self::$method_references_to_class_members[$method_id]);
- }
-
- public function isClassPropertyReferenced(string $property_id): bool
- {
- return !empty(self::$file_references_to_class_properties[$property_id])
- || !empty(self::$method_references_to_class_properties[$property_id]);
- }
-
- public function isMethodReturnReferenced(string $method_id): bool
- {
- return !empty(self::$file_references_to_method_returns[$method_id])
- || !empty(self::$method_references_to_method_returns[$method_id]);
- }
-
- public function isClassReferenced(string $fq_class_name_lc): bool
- {
- return isset(self::$method_references_to_classes[$fq_class_name_lc])
- || isset(self::$nonmethod_references_to_classes[$fq_class_name_lc]);
- }
-
- public function isMethodParamUsed(string $method_id, int $offset): bool
- {
- return !empty(self::$method_param_uses[$method_id][$offset]);
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setNonMethodReferencesToClasses(array $references): void
- {
- self::$nonmethod_references_to_classes = $references;
- }
-
- /**
- * @return array<string, array<int, CodeLocation>>
- */
- public function getAllClassMethodLocations(): array
- {
- return self::$class_method_locations;
- }
-
- /**
- * @return array<string, array<int, CodeLocation>>
- */
- public function getAllClassPropertyLocations(): array
- {
- return self::$class_property_locations;
- }
-
- /**
- * @return array<string, array<int, CodeLocation>>
- */
- public function getAllClassLocations(): array
- {
- return self::$class_locations;
- }
-
- /**
- * @return array<int, CodeLocation>
- */
- public function getClassMethodLocations(string $method_id): array
- {
- return self::$class_method_locations[$method_id] ?? [];
- }
-
- /**
- * @return array<int, CodeLocation>
- */
- public function getClassPropertyLocations(string $property_id): array
- {
- return self::$class_property_locations[$property_id] ?? [];
- }
-
- /**
- * @return array<int, CodeLocation>
- */
- public function getClassLocations(string $fq_class_name_lc): array
- {
- return self::$class_locations[$fq_class_name_lc] ?? [];
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addMethodReferencesToClassMembers(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$method_references_to_class_members[$key])) {
- self::$method_references_to_class_members[$key] = array_merge(
- $reference,
- self::$method_references_to_class_members[$key]
- );
- } else {
- self::$method_references_to_class_members[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addMethodDependencies(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$method_dependencies[$key])) {
- self::$method_dependencies[$key] = array_merge(
- $reference,
- self::$method_dependencies[$key]
- );
- } else {
- self::$method_dependencies[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addMethodReferencesToClassProperties(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$method_references_to_class_properties[$key])) {
- self::$method_references_to_class_properties[$key] = array_merge(
- $reference,
- self::$method_references_to_class_properties[$key]
- );
- } else {
- self::$method_references_to_class_properties[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addMethodReferencesToMethodReturns(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$method_references_to_method_returns[$key])) {
- self::$method_references_to_method_returns[$key] = array_merge(
- $reference,
- self::$method_references_to_method_returns[$key]
- );
- } else {
- self::$method_references_to_method_returns[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addMethodReferencesToClasses(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$method_references_to_classes[$key])) {
- self::$method_references_to_classes[$key] = array_merge(
- $reference,
- self::$method_references_to_classes[$key]
- );
- } else {
- self::$method_references_to_classes[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function addMethodReferencesToMissingClassMembers(array $references): void
- {
- foreach ($references as $key => $reference) {
- if (isset(self::$method_references_to_missing_class_members[$key])) {
- self::$method_references_to_missing_class_members[$key] = array_merge(
- $reference,
- self::$method_references_to_missing_class_members[$key]
- );
- } else {
- self::$method_references_to_missing_class_members[$key] = $reference;
- }
- }
- }
-
- /**
- * @param array<string, array<int, array<string, bool>>> $references
- *
- */
- public function addMethodParamUses(array $references): void
- {
- foreach ($references as $method_id => $method_param_uses) {
- if (isset(self::$method_param_uses[$method_id])) {
- foreach ($method_param_uses as $offset => $reference_map) {
- if (isset(self::$method_param_uses[$method_id][$offset])) {
- self::$method_param_uses[$method_id][$offset] = array_merge(
- self::$method_param_uses[$method_id][$offset],
- $reference_map
- );
- } else {
- self::$method_param_uses[$method_id][$offset] = $reference_map;
- }
- }
- } else {
- self::$method_param_uses[$method_id] = $method_param_uses;
- }
- }
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setCallingMethodReferencesToClasses(array $references): void
- {
- self::$method_references_to_classes = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setCallingMethodReferencesToClassMembers(array $references): void
- {
- self::$method_references_to_class_members = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setMethodDependencies(array $references): void
- {
- self::$method_dependencies = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setCallingMethodReferencesToClassProperties(array $references): void
- {
- self::$method_references_to_class_properties = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setCallingMethodReferencesToMethodReturns(array $references): void
- {
- self::$method_references_to_method_returns = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setCallingMethodReferencesToMissingClassMembers(array $references): void
- {
- self::$method_references_to_missing_class_members = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setFileReferencesToClassMembers(array $references): void
- {
- self::$file_references_to_class_members = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setFileReferencesToClassProperties(array $references): void
- {
- self::$file_references_to_class_properties = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setFileReferencesToMethodReturns(array $references): void
- {
- self::$file_references_to_method_returns = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setFileReferencesToMissingClassMembers(array $references): void
- {
- self::$file_references_to_missing_class_members = $references;
- }
-
- /**
- * @param array<string, array<string,bool>> $references
- *
- */
- public function setReferencesToMixedMemberNames(array $references): void
- {
- self::$references_to_mixed_member_names = $references;
- }
-
- /**
- * @param array<string, array<int, array<string, bool>>> $references
- *
- */
- public function setMethodParamUses(array $references): void
- {
- self::$method_param_uses = $references;
- }
-
- /**
- * @param array<string, array<int, CodeLocation>> $references
- *
- */
- public function addClassMethodLocations(array $references): void
- {
- foreach ($references as $referenced_member_id => $locations) {
- if (isset(self::$class_method_locations[$referenced_member_id])) {
- self::$class_method_locations[$referenced_member_id] = array_merge(
- self::$class_method_locations[$referenced_member_id],
- $locations
- );
- } else {
- self::$class_method_locations[$referenced_member_id] = $locations;
- }
- }
- }
-
- /**
- * @param array<string, array<int, CodeLocation>> $references
- *
- */
- public function addClassPropertyLocations(array $references): void
- {
- foreach ($references as $referenced_member_id => $locations) {
- if (isset(self::$class_property_locations[$referenced_member_id])) {
- self::$class_property_locations[$referenced_member_id] = array_merge(
- self::$class_property_locations[$referenced_member_id],
- $locations
- );
- } else {
- self::$class_property_locations[$referenced_member_id] = $locations;
- }
- }
- }
-
- /**
- * @param array<string, array<int, CodeLocation>> $references
- *
- */
- public function addClassLocations(array $references): void
- {
- foreach ($references as $referenced_member_id => $locations) {
- if (isset(self::$class_locations[$referenced_member_id])) {
- self::$class_locations[$referenced_member_id] = array_merge(
- self::$class_locations[$referenced_member_id],
- $locations
- );
- } else {
- self::$class_locations[$referenced_member_id] = $locations;
- }
- }
- }
-
- /**
- * @return array<string, array<int, IssueData>>
- */
- public function getExistingIssues(): array
- {
- return self::$issues;
- }
-
- public function clearExistingIssuesForFile(string $file_path): void
- {
- unset(self::$issues[$file_path]);
- }
-
- public function clearExistingFileMapsForFile(string $file_path): void
- {
- unset(self::$file_maps[$file_path]);
- }
-
- public function addIssue(string $file_path, IssueData $issue): void
- {
- // don’t save parse errors ever, as they're not responsive to AST diffing
- if ($issue->type === 'ParseError') {
- return;
- }
-
- if (!isset(self::$issues[$file_path])) {
- self::$issues[$file_path] = [$issue];
- } else {
- self::$issues[$file_path][] = $issue;
- }
- }
-
- /**
- * @param array<string, array<string, int>> $analyzed_methods
- *
- */
- public function setAnalyzedMethods(array $analyzed_methods): void
- {
- self::$analyzed_methods = $analyzed_methods;
- }
-
- /**
- * @param array<string, FileMapType> $file_maps
- */
- public function setFileMaps(array $file_maps): void
- {
- self::$file_maps = $file_maps;
- }
-
- /**
- * @return array<string, array{int, int}>
- */
- public function getTypeCoverage(): array
- {
- return self::$mixed_counts;
- }
-
- /**
- * @param array<string, array{int, int}> $mixed_counts
- *
- */
- public function setTypeCoverage(array $mixed_counts): void
- {
- self::$mixed_counts = array_merge(self::$mixed_counts, $mixed_counts);
- }
-
- /**
- * @return array<string, array<string, int>>
- */
- public function getAnalyzedMethods(): array
- {
- return self::$analyzed_methods;
- }
-
- /**
- * @return array<string, FileMapType>
- */
- public function getFileMaps(): array
- {
- return self::$file_maps;
- }
-
- public static function clearCache(): void
- {
- self::$files_inheriting_classes = [];
- self::$deleted_files = null;
- self::$file_references = [];
- self::$file_references_to_class_members = [];
- self::$file_references_to_class_properties = [];
- self::$file_references_to_method_returns = [];
- self::$method_references_to_class_members = [];
- self::$method_dependencies = [];
- self::$method_references_to_class_properties = [];
- self::$method_references_to_method_returns = [];
- self::$method_references_to_classes = [];
- self::$nonmethod_references_to_classes = [];
- self::$file_references_to_missing_class_members = [];
- self::$method_references_to_missing_class_members = [];
- self::$references_to_mixed_member_names = [];
- self::$class_method_locations = [];
- self::$class_property_locations = [];
- self::$class_locations = [];
- self::$analyzed_methods = [];
- self::$issues = [];
- self::$file_maps = [];
- self::$method_param_uses = [];
- self::$classlike_files = [];
- self::$mixed_counts = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageCacheProvider.php
deleted file mode 100644
index d52b416..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageCacheProvider.php
+++ /dev/null
@@ -1,184 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Psalm\Config;
-use Psalm\Storage\FileStorage;
-use UnexpectedValueException;
-
-use function array_merge;
-use function dirname;
-use function file_exists;
-use function file_get_contents;
-use function file_put_contents;
-use function filemtime;
-use function get_class;
-use function hash;
-use function igbinary_serialize;
-use function igbinary_unserialize;
-use function is_dir;
-use function mkdir;
-use function serialize;
-use function strtolower;
-use function unlink;
-use function unserialize;
-
-use const DIRECTORY_SEPARATOR;
-use const PHP_VERSION_ID;
-
-/**
- * @internal
- */
-class FileStorageCacheProvider
-{
- /**
- * @var string
- */
- private $modified_timestamps = '';
-
- /**
- * @var Config
- */
- private $config;
-
- private const FILE_STORAGE_CACHE_DIRECTORY = 'file_cache';
-
- public function __construct(Config $config)
- {
- $this->config = $config;
-
- $storage_dir = dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'Storage' . DIRECTORY_SEPARATOR;
-
- $dependent_files = [
- $storage_dir . 'FileStorage.php',
- $storage_dir . 'FunctionLikeStorage.php',
- $storage_dir . 'ClassLikeStorage.php',
- $storage_dir . 'MethodStorage.php',
- $storage_dir . 'FunctionLikeParameter.php',
- ];
-
- if ($config->eventDispatcher->hasAfterClassLikeVisitHandlers()) {
- $dependent_files = array_merge($dependent_files, $config->plugin_paths);
- }
-
- foreach ($dependent_files as $dependent_file_path) {
- if (!file_exists($dependent_file_path)) {
- throw new UnexpectedValueException($dependent_file_path . ' must exist');
- }
-
- $this->modified_timestamps .= ' ' . filemtime($dependent_file_path);
- }
-
- $this->modified_timestamps .= $this->config->computeHash();
- }
-
- public function writeToCache(FileStorage $storage, string $file_contents): void
- {
- $file_path = strtolower($storage->file_path);
- $cache_location = $this->getCacheLocationForPath($file_path, true);
- $storage->hash = $this->getCacheHash($file_path, $file_contents);
-
- if ($this->config->use_igbinary) {
- file_put_contents($cache_location, igbinary_serialize($storage));
- } else {
- file_put_contents($cache_location, serialize($storage));
- }
- }
-
- public function getLatestFromCache(string $file_path, string $file_contents): ?FileStorage
- {
- $file_path = strtolower($file_path);
- $cached_value = $this->loadFromCache($file_path);
-
- if (!$cached_value) {
- return null;
- }
-
- $cache_hash = $this->getCacheHash($file_path, $file_contents);
-
- /** @psalm-suppress TypeDoesNotContainType */
- if (@get_class($cached_value) === '__PHP_Incomplete_Class'
- || $cache_hash !== $cached_value->hash
- ) {
- $this->removeCacheForFile($file_path);
-
- return null;
- }
-
- return $cached_value;
- }
-
- public function removeCacheForFile(string $file_path): void
- {
- $cache_path = $this->getCacheLocationForPath($file_path);
-
- if (file_exists($cache_path)) {
- unlink($cache_path);
- }
- }
-
- private function getCacheHash(string $_unused_file_path, string $file_contents): string
- {
- // do not concatenate, as $file_contents can be big and performance will be bad
- // the timestamp is only needed if we don't have file contents
- // as same contents should give same results, independent of when file was modified
- $data = $file_contents ? $file_contents : $this->modified_timestamps;
- return PHP_VERSION_ID >= 80100 ? hash('xxh128', $data) : hash('md4', $data);
- }
-
- /**
- * @psalm-suppress MixedAssignment
- */
- private function loadFromCache(string $file_path): ?FileStorage
- {
- $cache_location = $this->getCacheLocationForPath($file_path);
-
- if (file_exists($cache_location)) {
- if ($this->config->use_igbinary) {
- $storage = igbinary_unserialize((string)file_get_contents($cache_location));
-
- if ($storage instanceof FileStorage) {
- return $storage;
- }
-
- return null;
- }
-
- $storage = unserialize((string)file_get_contents($cache_location));
-
- if ($storage instanceof FileStorage) {
- return $storage;
- }
-
- return null;
- }
-
- return null;
- }
-
- private function getCacheLocationForPath(string $file_path, bool $create_directory = false): string
- {
- $root_cache_directory = $this->config->getCacheDirectory();
-
- if (!$root_cache_directory) {
- throw new UnexpectedValueException('No cache directory defined');
- }
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_STORAGE_CACHE_DIRECTORY;
-
- if ($create_directory && !is_dir($parser_cache_directory)) {
- mkdir($parser_cache_directory, 0777, true);
- }
-
- if (PHP_VERSION_ID >= 80100) {
- $hash = hash('xxh128', $file_path);
- } else {
- $hash = hash('md4', $file_path);
- }
-
- return $parser_cache_directory
- . DIRECTORY_SEPARATOR
- . $hash
- . ($this->config->use_igbinary ? '-igbinary' : '');
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageProvider.php
deleted file mode 100644
index ff48840..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileStorageProvider.php
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use InvalidArgumentException;
-use Psalm\Storage\FileStorage;
-
-use function array_merge;
-use function strtolower;
-
-/**
- * @internal
- */
-class FileStorageProvider
-{
- /**
- * A list of data useful to analyse files
- * Storing this statically is much faster (at least in PHP 7.2.1)
- *
- * @var array<lowercase-string, FileStorage>
- */
- private static $storage = [];
-
- /**
- * A list of data useful to analyse new files
- * Storing this statically is much faster (at least in PHP 7.2.1)
- *
- * @var array<string, FileStorage>
- */
- private static $new_storage = [];
-
- /**
- * @var ?FileStorageCacheProvider
- */
- public $cache;
-
- public function __construct(?FileStorageCacheProvider $cache = null)
- {
- $this->cache = $cache;
- }
-
- public function get(string $file_path): FileStorage
- {
- $file_path = strtolower($file_path);
-
- if (!isset(self::$storage[$file_path])) {
- throw new InvalidArgumentException('Could not get file storage for ' . $file_path);
- }
-
- return self::$storage[$file_path];
- }
-
- public function remove(string $file_path): void
- {
- unset(self::$storage[strtolower($file_path)]);
- }
-
- public function has(string $file_path, ?string $file_contents = null): bool
- {
- $file_path = strtolower($file_path);
-
- if (isset(self::$storage[$file_path])) {
- return true;
- }
-
- if ($file_contents === null) {
- return false;
- }
-
- if (!$this->cache) {
- return false;
- }
-
- $cached_value = $this->cache->getLatestFromCache($file_path, $file_contents);
-
- if (!$cached_value) {
- return false;
- }
-
- self::$storage[$file_path] = $cached_value;
- self::$new_storage[$file_path] = $cached_value;
-
- return true;
- }
-
- /**
- * @return array<lowercase-string, FileStorage>
- */
- public function getAll(): array
- {
- return self::$storage;
- }
-
- /**
- * @return array<string, FileStorage>
- */
- public function getNew(): array
- {
- return self::$new_storage;
- }
-
- /**
- * @param array<lowercase-string, FileStorage> $more
- */
- public function addMore(array $more): void
- {
- self::$new_storage = array_merge(self::$new_storage, $more);
- self::$storage = array_merge(self::$storage, $more);
- }
-
- public function create(string $file_path): FileStorage
- {
- $file_path_lc = strtolower($file_path);
-
- $storage = new FileStorage($file_path);
- self::$storage[$file_path_lc] = $storage;
- self::$new_storage[$file_path_lc] = $storage;
-
- return $storage;
- }
-
- public static function deleteAll(): void
- {
- self::$storage = [];
- }
-
- public static function populated(): void
- {
- self::$new_storage = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionExistenceProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionExistenceProvider.php
deleted file mode 100644
index 57c4a8c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionExistenceProvider.php
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use Psalm\Plugin\EventHandler\Event\FunctionExistenceProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionExistenceProviderInterface;
-use Psalm\Plugin\Hook\FunctionExistenceProviderInterface as LegacyFunctionExistenceProviderInterface;
-use Psalm\StatementsSource;
-
-use function is_subclass_of;
-use function strtolower;
-
-class FunctionExistenceProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(FunctionExistenceProviderEvent): ?bool>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * StatementsSource,
- * string
- * ): ?bool>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
- }
-
- /**
- * @param class-string $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyFunctionExistenceProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'doesFunctionExist']);
-
- foreach ($class::getFunctionIds() as $function_id) {
- $this->registerLegacyClosure($function_id, $callable);
- }
- } elseif (is_subclass_of($class, FunctionExistenceProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'doesFunctionExist']);
-
- foreach ($class::getFunctionIds() as $function_id) {
- $this->registerClosure($function_id, $callable);
- }
- }
- }
-
- /**
- * @param lowercase-string $function_id
- * @param Closure(FunctionExistenceProviderEvent): ?bool $c
- */
- public function registerClosure(string $function_id, Closure $c): void
- {
- self::$handlers[$function_id][] = $c;
- }
-
- /**
- * @param lowercase-string $function_id
- * @param Closure(
- * StatementsSource,
- * string
- * ): ?bool $c
- */
- public function registerLegacyClosure(string $function_id, Closure $c): void
- {
- self::$legacy_handlers[$function_id][] = $c;
- }
-
- public function has(string $function_id): bool
- {
- return isset(self::$handlers[strtolower($function_id)]) ||
- isset(self::$legacy_handlers[strtolower($function_id)]);
- }
-
- public function doesFunctionExist(
- StatementsSource $statements_source,
- string $function_id
- ): ?bool {
- foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $function_handler) {
- $function_exists = $function_handler(
- $statements_source,
- $function_id
- );
-
- if ($function_exists !== null) {
- return $function_exists;
- }
- }
-
- foreach (self::$handlers[strtolower($function_id)] ?? [] as $function_handler) {
- $event = new FunctionExistenceProviderEvent(
- $statements_source,
- $function_id
- );
- $function_exists = $function_handler($event);
-
- if ($function_exists !== null) {
- return $function_exists;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionParamsProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionParamsProvider.php
deleted file mode 100644
index d6b70c4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionParamsProvider.php
+++ /dev/null
@@ -1,139 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use PhpParser\Node\Arg;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Plugin\EventHandler\Event\FunctionParamsProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionParamsProviderInterface;
-use Psalm\Plugin\Hook\FunctionParamsProviderInterface as LegacyFunctionParamsProviderInterface;
-use Psalm\StatementsSource;
-use Psalm\Storage\FunctionLikeParameter;
-
-use function is_subclass_of;
-use function strtolower;
-
-class FunctionParamsProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(FunctionParamsProviderEvent): ?array<int, FunctionLikeParameter>>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * StatementsSource,
- * string,
- * list<Arg>,
- * ?Context=,
- * ?CodeLocation=
- * ): ?array<int, FunctionLikeParameter>>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
- }
-
- /**
- * @param class-string<LegacyFunctionParamsProviderInterface>|class-string<FunctionParamsProviderInterface> $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyFunctionParamsProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getFunctionParams']);
-
- foreach ($class::getFunctionIds() as $function_id) {
- $this->registerLegacyClosure($function_id, $callable);
- }
- } elseif (is_subclass_of($class, FunctionParamsProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getFunctionParams']);
-
- foreach ($class::getFunctionIds() as $function_id) {
- $this->registerClosure($function_id, $callable);
- }
- }
- }
-
- /**
- * @param Closure(FunctionParamsProviderEvent): ?array<int, FunctionLikeParameter> $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * StatementsSource,
- * string,
- * list<Arg>,
- * ?Context=,
- * ?CodeLocation=
- * ): ?array<int, FunctionLikeParameter> $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- /**
- * @param list<Arg> $call_args
- *
- * @return ?array<int, FunctionLikeParameter>
- */
- public function getFunctionParams(
- StatementsSource $statements_source,
- string $function_id,
- array $call_args,
- ?Context $context = null,
- ?CodeLocation $code_location = null
- ): ?array {
- foreach (self::$handlers[strtolower($function_id)] ?? [] as $class_handler) {
- $event = new FunctionParamsProviderEvent(
- $statements_source,
- $function_id,
- $call_args,
- $context,
- $code_location
- );
- $result = $class_handler($event);
-
- if ($result) {
- return $result;
- }
- }
-
- foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $class_handler) {
- $result = $class_handler(
- $statements_source,
- $function_id,
- $call_args,
- $context,
- $code_location
- );
-
- if ($result) {
- return $result;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php
deleted file mode 100644
index 8b124ea..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php
+++ /dev/null
@@ -1,205 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use PhpParser;
-use PhpParser\Node\Arg;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayChunkReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayColumnReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayFillReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayFilterReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayMapReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayMergeReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayPadReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayPointerAdjustmentReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayPopReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayRandReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayReduceReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayReverseReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArraySliceReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArraySpliceReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayUniqueReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ArrayValuesReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ExplodeReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\FilterVarReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\FirstArgStringReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\GetClassMethodsReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\GetObjectVarsReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\HexdecReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\InArrayReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\IteratorToArrayReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\MinMaxReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\MktimeReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\ParseUrlReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\RandReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\StrReplaceReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\StrTrReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\TriggerErrorReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\VersionCompareReturnTypeProvider;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Plugin\Hook\FunctionReturnTypeProviderInterface as LegacyFunctionReturnTypeProviderInterface;
-use Psalm\StatementsSource;
-use Psalm\Type\Union;
-
-use function is_subclass_of;
-use function strtolower;
-
-class FunctionReturnTypeProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(FunctionReturnTypeProviderEvent): ?Union>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * StatementsSource,
- * non-empty-string,
- * list<Arg>,
- * Context,
- * CodeLocation
- * ): ?Union>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
-
- $this->registerClass(ArrayChunkReturnTypeProvider::class);
- $this->registerClass(ArrayColumnReturnTypeProvider::class);
- $this->registerClass(ArrayFilterReturnTypeProvider::class);
- $this->registerClass(ArrayMapReturnTypeProvider::class);
- $this->registerClass(ArrayMergeReturnTypeProvider::class);
- $this->registerClass(ArrayPadReturnTypeProvider::class);
- $this->registerClass(ArrayPointerAdjustmentReturnTypeProvider::class);
- $this->registerClass(ArrayPopReturnTypeProvider::class);
- $this->registerClass(ArrayRandReturnTypeProvider::class);
- $this->registerClass(ArrayReduceReturnTypeProvider::class);
- $this->registerClass(ArraySliceReturnTypeProvider::class);
- $this->registerClass(ArraySpliceReturnTypeProvider::class);
- $this->registerClass(ArrayReverseReturnTypeProvider::class);
- $this->registerClass(ArrayUniqueReturnTypeProvider::class);
- $this->registerClass(ArrayValuesReturnTypeProvider::class);
- $this->registerClass(ArrayFillReturnTypeProvider::class);
- $this->registerClass(FilterVarReturnTypeProvider::class);
- $this->registerClass(IteratorToArrayReturnTypeProvider::class);
- $this->registerClass(ParseUrlReturnTypeProvider::class);
- $this->registerClass(StrReplaceReturnTypeProvider::class);
- $this->registerClass(StrTrReturnTypeProvider::class);
- $this->registerClass(VersionCompareReturnTypeProvider::class);
- $this->registerClass(MktimeReturnTypeProvider::class);
- $this->registerClass(ExplodeReturnTypeProvider::class);
- $this->registerClass(GetObjectVarsReturnTypeProvider::class);
- $this->registerClass(GetClassMethodsReturnTypeProvider::class);
- $this->registerClass(FirstArgStringReturnTypeProvider::class);
- $this->registerClass(HexdecReturnTypeProvider::class);
- $this->registerClass(MinMaxReturnTypeProvider::class);
- $this->registerClass(TriggerErrorReturnTypeProvider::class);
- $this->registerClass(RandReturnTypeProvider::class);
- $this->registerClass(InArrayReturnTypeProvider::class);
- }
-
- /**
- * @param class-string $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyFunctionReturnTypeProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getFunctionReturnType']);
-
- foreach ($class::getFunctionIds() as $function_id) {
- $this->registerLegacyClosure($function_id, $callable);
- }
- } elseif (is_subclass_of($class, FunctionReturnTypeProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getFunctionReturnType']);
-
- foreach ($class::getFunctionIds() as $function_id) {
- $this->registerClosure($function_id, $callable);
- }
- }
- }
-
- /**
- * @param lowercase-string $function_id
- * @param Closure(FunctionReturnTypeProviderEvent): ?Union $c
- */
- public function registerClosure(string $function_id, Closure $c): void
- {
- self::$handlers[$function_id][] = $c;
- }
-
- /**
- * @param lowercase-string $function_id
- * @param Closure(
- * StatementsSource,
- * non-empty-string,
- * list<Arg>,
- * Context,
- * CodeLocation
- * ): ?Union $c
- */
- public function registerLegacyClosure(string $function_id, Closure $c): void
- {
- self::$legacy_handlers[$function_id][] = $c;
- }
-
- public function has(string $function_id): bool
- {
- return isset(self::$handlers[strtolower($function_id)]) ||
- isset(self::$legacy_handlers[strtolower($function_id)]);
- }
-
- /**
- * @param non-empty-string $function_id
- */
- public function getReturnType(
- StatementsSource $statements_source,
- string $function_id,
- PhpParser\Node\Expr\FuncCall $stmt,
- Context $context,
- CodeLocation $code_location
- ): ?Union {
- foreach (self::$legacy_handlers[strtolower($function_id)] ?? [] as $function_handler) {
- $return_type = $function_handler(
- $statements_source,
- $function_id,
- $stmt->getArgs(),
- $context,
- $code_location
- );
-
- if ($return_type) {
- return $return_type;
- }
- }
-
- foreach (self::$handlers[strtolower($function_id)] ?? [] as $function_handler) {
- $event = new FunctionReturnTypeProviderEvent(
- $statements_source,
- $function_id,
- $stmt,
- $context,
- $code_location
- );
- $return_type = $function_handler($event);
-
- if ($return_type) {
- return $return_type;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodExistenceProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodExistenceProvider.php
deleted file mode 100644
index 0f63772..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodExistenceProvider.php
+++ /dev/null
@@ -1,126 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use Psalm\CodeLocation;
-use Psalm\Plugin\EventHandler\Event\MethodExistenceProviderEvent;
-use Psalm\Plugin\EventHandler\MethodExistenceProviderInterface;
-use Psalm\Plugin\Hook\MethodExistenceProviderInterface as LegacyMethodExistenceProviderInterface;
-use Psalm\StatementsSource;
-
-use function is_subclass_of;
-use function strtolower;
-
-class MethodExistenceProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(MethodExistenceProviderEvent): ?bool>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * string,
- * string,
- * ?StatementsSource=,
- * ?CodeLocation
- * ): ?bool>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
- }
-
- /**
- * @param class-string<LegacyMethodExistenceProviderInterface>|class-string<MethodExistenceProviderInterface> $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyMethodExistenceProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'doesMethodExist']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, MethodExistenceProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'doesMethodExist']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(MethodExistenceProviderEvent): ?bool $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * string,
- * string,
- * ?StatementsSource=,
- * ?CodeLocation
- * ): ?bool $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- public function doesMethodExist(
- string $fq_classlike_name,
- string $method_name_lowercase,
- ?StatementsSource $source = null,
- ?CodeLocation $code_location = null
- ): ?bool {
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) {
- $method_exists = $method_handler(
- $fq_classlike_name,
- $method_name_lowercase,
- $source,
- $code_location
- );
-
- if ($method_exists !== null) {
- return $method_exists;
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) {
- $event = new MethodExistenceProviderEvent(
- $fq_classlike_name,
- $method_name_lowercase,
- $source,
- $code_location
- );
- $method_exists = $method_handler($event);
-
- if ($method_exists !== null) {
- return $method_exists;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodParamsProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodParamsProvider.php
deleted file mode 100644
index 0033a1e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodParamsProvider.php
+++ /dev/null
@@ -1,148 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use PhpParser\Node\Arg;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementSetFetchMode;
-use Psalm\Plugin\EventHandler\Event\MethodParamsProviderEvent;
-use Psalm\Plugin\EventHandler\MethodParamsProviderInterface;
-use Psalm\Plugin\Hook\MethodParamsProviderInterface as LegacyMethodParamsProviderInterface;
-use Psalm\StatementsSource;
-use Psalm\Storage\FunctionLikeParameter;
-
-use function array_values;
-use function is_subclass_of;
-use function strtolower;
-
-class MethodParamsProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(MethodParamsProviderEvent): ?array<int, FunctionLikeParameter>>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * string,
- * string,
- * ?list<Arg>=,
- * ?StatementsSource=,
- * ?Context=,
- * ?CodeLocation=
- * ): ?array<int, FunctionLikeParameter>>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
-
- $this->registerClass(PdoStatementSetFetchMode::class);
- }
-
- /**
- * @param class-string $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyMethodParamsProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getMethodParams']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, MethodParamsProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getMethodParams']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(MethodParamsProviderEvent): ?array<int, FunctionLikeParameter> $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * string,
- * string,
- * ?list<Arg>=,
- * ?StatementsSource=,
- * ?Context=,
- * ?CodeLocation=
- * ): ?array<int, FunctionLikeParameter> $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- /**
- * @param ?list<Arg> $call_args
- *
- * @return ?list<FunctionLikeParameter>
- */
- public function getMethodParams(
- string $fq_classlike_name,
- string $method_name_lowercase,
- ?array $call_args = null,
- ?StatementsSource $statements_source = null,
- ?Context $context = null,
- ?CodeLocation $code_location = null
- ): ?array {
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) {
- $result = $class_handler(
- $fq_classlike_name,
- $method_name_lowercase,
- $call_args,
- $statements_source,
- $context,
- $code_location
- );
-
- if ($result !== null) {
- return array_values($result);
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) {
- $event = new MethodParamsProviderEvent(
- $fq_classlike_name,
- $method_name_lowercase,
- $call_args,
- $statements_source,
- $context,
- $code_location
- );
- $result = $class_handler($event);
-
- if ($result !== null) {
- return array_values($result);
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php
deleted file mode 100644
index f29dc77..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodReturnTypeProvider.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Provider\ReturnTypeProvider\ClosureFromCallableReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\DomNodeAppendChild;
-use Psalm\Internal\Provider\ReturnTypeProvider\ImagickPixelColorReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\PdoStatementReturnTypeProvider;
-use Psalm\Internal\Provider\ReturnTypeProvider\SimpleXmlElementAsXml;
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
-use Psalm\Plugin\Hook\MethodReturnTypeProviderInterface as LegacyMethodReturnTypeProviderInterface;
-use Psalm\StatementsSource;
-use Psalm\Type\Union;
-
-use function is_subclass_of;
-use function strtolower;
-
-class MethodReturnTypeProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(MethodReturnTypeProviderEvent): ?Union>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * StatementsSource,
- * string,
- * lowercase-string,
- * list<PhpParser\Node\Arg>,
- * Context,
- * CodeLocation,
- * ?array<Union>=,
- * ?string=,
- * ?lowercase-string=
- * ): ?Union>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
-
- $this->registerClass(DomNodeAppendChild::class);
- $this->registerClass(ImagickPixelColorReturnTypeProvider::class);
- $this->registerClass(SimpleXmlElementAsXml::class);
- $this->registerClass(PdoStatementReturnTypeProvider::class);
- $this->registerClass(ClosureFromCallableReturnTypeProvider::class);
- }
-
- /**
- * @param class-string $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyMethodReturnTypeProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getMethodReturnType']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, MethodReturnTypeProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getMethodReturnType']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(MethodReturnTypeProviderEvent): ?Union $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * StatementsSource,
- * string,
- * lowercase-string,
- * list<PhpParser\Node\Arg>,
- * Context,
- * CodeLocation,
- * ?array<Union>=,
- * ?string=,
- * ?lowercase-string=
- * ): ?Union $c
- *
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- /**
- * @param PhpParser\Node\Expr\MethodCall|PhpParser\Node\Expr\StaticCall $stmt
- * @param ?array<Union> $template_type_parameters
- */
- public function getReturnType(
- StatementsSource $statements_source,
- string $fq_classlike_name,
- string $method_name,
- $stmt,
- Context $context,
- CodeLocation $code_location,
- ?array $template_type_parameters = null,
- ?string $called_fq_classlike_name = null,
- ?string $called_method_name = null
- ): ?Union {
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) {
- $result = $class_handler(
- $statements_source,
- $fq_classlike_name,
- strtolower($method_name),
- $stmt->getArgs(),
- $context,
- $code_location,
- $template_type_parameters,
- $called_fq_classlike_name,
- $called_method_name ? strtolower($called_method_name) : null
- );
-
- if ($result) {
- return $result;
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $class_handler) {
- $event = new MethodReturnTypeProviderEvent(
- $statements_source,
- $fq_classlike_name,
- strtolower($method_name),
- $stmt,
- $context,
- $code_location,
- $template_type_parameters,
- $called_fq_classlike_name,
- $called_method_name ? strtolower($called_method_name) : null
- );
- $result = $class_handler($event);
-
- if ($result) {
- return $result;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodVisibilityProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodVisibilityProvider.php
deleted file mode 100644
index e9a876b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/MethodVisibilityProvider.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Plugin\EventHandler\Event\MethodVisibilityProviderEvent;
-use Psalm\Plugin\EventHandler\MethodVisibilityProviderInterface;
-use Psalm\Plugin\Hook\MethodVisibilityProviderInterface as LegacyMethodVisibilityProviderInterface;
-use Psalm\StatementsSource;
-
-use function is_subclass_of;
-use function strtolower;
-
-class MethodVisibilityProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(MethodVisibilityProviderEvent): ?bool>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * StatementsSource,
- * string,
- * string,
- * Context,
- * ?CodeLocation
- * ): ?bool>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
- }
-
- /**
- * @param class-string<LegacyMethodVisibilityProviderInterface>
- * |class-string<MethodVisibilityProviderInterface> $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyMethodVisibilityProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'isMethodVisible']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, MethodVisibilityProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'isMethodVisible']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(MethodVisibilityProviderEvent): ?bool $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * StatementsSource,
- * string,
- * string,
- * Context,
- * ?CodeLocation
- * ): ?bool $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- public function isMethodVisible(
- StatementsSource $source,
- string $fq_classlike_name,
- string $method_name,
- Context $context,
- ?CodeLocation $code_location = null
- ): ?bool {
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) {
- $method_visible = $method_handler(
- $source,
- $fq_classlike_name,
- $method_name,
- $context,
- $code_location
- );
-
- if ($method_visible !== null) {
- return $method_visible;
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $method_handler) {
- $event = new MethodVisibilityProviderEvent(
- $source,
- $fq_classlike_name,
- $method_name,
- $context,
- $code_location
- );
- $method_visible = $method_handler($event);
-
- if ($method_visible !== null) {
- return $method_visible;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/NodeDataProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/NodeDataProvider.php
deleted file mode 100644
index 3987a23..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/NodeDataProvider.php
+++ /dev/null
@@ -1,133 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use PhpParser\Node;
-use PhpParser\Node\Expr;
-use PhpParser\Node\Expr\FuncCall;
-use PhpParser\Node\Expr\MethodCall;
-use PhpParser\Node\Expr\New_;
-use PhpParser\Node\Expr\StaticCall;
-use PhpParser\Node\Name;
-use PhpParser\Node\Stmt\Return_;
-use PhpParser\NodeAbstract;
-use Psalm\NodeTypeProvider;
-use Psalm\Storage\Assertion;
-use Psalm\Type\Union;
-use SplObjectStorage;
-
-class NodeDataProvider implements NodeTypeProvider
-{
- /** @var SplObjectStorage<Node, Union> */
- private $node_types;
-
- /**
- * @var SplObjectStorage<Node,list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>|null>
- */
- private $node_assertions;
-
- /** @var SplObjectStorage<Node, array<int, Assertion>> */
- private $node_if_true_assertions;
-
- /** @var SplObjectStorage<Node, array<int, Assertion>> */
- private $node_if_false_assertions;
-
- /** @var bool */
- public $cache_assertions = true;
-
- public function __construct()
- {
- $this->node_types = new SplObjectStorage();
- $this->node_assertions = new SplObjectStorage();
- $this->node_if_true_assertions = new SplObjectStorage();
- $this->node_if_false_assertions = new SplObjectStorage();
- }
-
- /**
- * @param Expr|Name|Return_ $node
- */
- public function setType(NodeAbstract $node, Union $type): void
- {
- $this->node_types[$node] = $type;
- }
-
- /**
- * @param Expr|Name|Return_ $node
- */
- public function getType(NodeAbstract $node): ?Union
- {
- return $this->node_types[$node] ?? null;
- }
-
- /**
- * @param list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>|null $assertions
- */
- public function setAssertions(Expr $node, ?array $assertions): void
- {
- if (!$this->cache_assertions) {
- return;
- }
-
- $this->node_assertions[$node] = $assertions;
- }
-
- /**
- * @return list<non-empty-array<string, non-empty-list<non-empty-list<string>>>>|null
- */
- public function getAssertions(Expr $node): ?array
- {
- if (!$this->cache_assertions) {
- return null;
- }
-
- return $this->node_assertions[$node] ?? null;
- }
-
- /**
- * @param FuncCall|MethodCall|StaticCall|New_ $node
- * @param array<int, Assertion> $assertions
- */
- public function setIfTrueAssertions(Expr $node, array $assertions): void
- {
- $this->node_if_true_assertions[$node] = $assertions;
- }
-
- /**
- * @param Expr\FuncCall|MethodCall|StaticCall|New_ $node
- * @return array<int, Assertion>|null
- */
- public function getIfTrueAssertions(Expr $node): ?array
- {
- return $this->node_if_true_assertions[$node] ?? null;
- }
-
- /**
- * @param FuncCall|MethodCall|StaticCall|New_ $node
- * @param array<int, Assertion> $assertions
- */
- public function setIfFalseAssertions(Expr $node, array $assertions): void
- {
- $this->node_if_false_assertions[$node] = $assertions;
- }
-
- /**
- * @param FuncCall|MethodCall|StaticCall|New_ $node
- * @return array<int, Assertion>|null
- */
- public function getIfFalseAssertions(Expr $node): ?array
- {
- return $this->node_if_false_assertions[$node] ?? null;
- }
-
- public function isPureCompatible(Expr $node): bool
- {
- $node_type = $this->getType($node);
-
- return ($node_type && $node_type->reference_free) || $node->getAttribute('pure', false);
- }
-
- public function clearNodeOfTypeAndAssertions(Expr $node): void
- {
- unset($this->node_types[$node], $this->node_assertions[$node]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ParserCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ParserCacheProvider.php
deleted file mode 100644
index a8f053f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ParserCacheProvider.php
+++ /dev/null
@@ -1,399 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use PhpParser;
-use PhpParser\Node\Stmt;
-use Psalm\Config;
-use RuntimeException;
-
-use function clearstatcache;
-use function error_log;
-use function fclose;
-use function file_get_contents;
-use function file_put_contents;
-use function filemtime;
-use function filesize;
-use function flock;
-use function fopen;
-use function fread;
-use function gettype;
-use function igbinary_serialize;
-use function igbinary_unserialize;
-use function is_array;
-use function is_dir;
-use function is_readable;
-use function is_writable;
-use function json_decode;
-use function json_encode;
-use function md5;
-use function mkdir;
-use function scandir;
-use function serialize;
-use function touch;
-use function unlink;
-use function unserialize;
-use function usleep;
-
-use const DIRECTORY_SEPARATOR;
-use const LOCK_EX;
-use const LOCK_SH;
-use const SCANDIR_SORT_NONE;
-
-/**
- * @internal
- */
-class ParserCacheProvider
-{
- private const FILE_HASHES = 'file_hashes_json';
- private const PARSER_CACHE_DIRECTORY = 'php-parser';
- private const FILE_CONTENTS_CACHE_DIRECTORY = 'file-caches';
-
- /**
- * A map of filename hashes to contents hashes
- *
- * @var array<string, string>|null
- */
- private $existing_file_content_hashes;
-
- /**
- * A map of recently-added filename hashes to contents hashes
- *
- * @var array<string, string>
- */
- private $new_file_content_hashes = [];
-
- /**
- * @var bool
- */
- private $use_file_cache;
-
- /** @var bool */
- private $use_igbinary;
-
- public function __construct(Config $config, bool $use_file_cache = true)
- {
- $this->use_igbinary = $config->use_igbinary;
- $this->use_file_cache = $use_file_cache;
- }
-
- /**
- * @return list<PhpParser\Node\Stmt>|null
- */
- public function loadStatementsFromCache(
- string $file_path,
- int $file_modified_time,
- string $file_content_hash
- ): ?array {
- $root_cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$root_cache_directory) {
- return null;
- }
-
- $file_cache_key = $this->getParserCacheKey(
- $file_path
- );
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::PARSER_CACHE_DIRECTORY;
-
- $file_content_hashes = $this->new_file_content_hashes + $this->getExistingFileContentHashes();
-
- $cache_location = $parser_cache_directory . DIRECTORY_SEPARATOR . $file_cache_key;
-
- if (isset($file_content_hashes[$file_cache_key])
- && $file_content_hash === $file_content_hashes[$file_cache_key]
- && is_readable($cache_location)
- && filemtime($cache_location) > $file_modified_time
- ) {
- if ($this->use_igbinary) {
- /** @var list<Stmt> */
- $stmts = igbinary_unserialize((string)file_get_contents($cache_location));
- } else {
- /** @var list<Stmt> */
- $stmts = unserialize((string)file_get_contents($cache_location));
- }
-
- return $stmts;
- }
-
- return null;
- }
-
- /**
- * @return list<PhpParser\Node\Stmt>|null
- */
- public function loadExistingStatementsFromCache(string $file_path): ?array
- {
- $root_cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$root_cache_directory) {
- return null;
- }
-
- $file_cache_key = $this->getParserCacheKey(
- $file_path
- );
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::PARSER_CACHE_DIRECTORY;
-
- $cache_location = $parser_cache_directory . DIRECTORY_SEPARATOR . $file_cache_key;
-
- if (is_readable($cache_location)) {
- if ($this->use_igbinary) {
- /** @var list<Stmt> */
- return igbinary_unserialize((string)file_get_contents($cache_location)) ?: null;
- }
-
- /** @var list<Stmt> */
- return unserialize((string)file_get_contents($cache_location)) ?: null;
- }
-
- return null;
- }
-
- public function loadExistingFileContentsFromCache(string $file_path): ?string
- {
- if (!$this->use_file_cache) {
- return null;
- }
-
- $root_cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$root_cache_directory) {
- return null;
- }
-
- $file_cache_key = $this->getParserCacheKey(
- $file_path
- );
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_CONTENTS_CACHE_DIRECTORY;
-
- $cache_location = $parser_cache_directory . DIRECTORY_SEPARATOR . $file_cache_key;
-
- if (is_readable($cache_location)) {
- return file_get_contents($cache_location);
- }
-
- return null;
- }
-
- /**
- * @return array<string, string>
- */
- private function getExistingFileContentHashes(): array
- {
- $config = Config::getInstance();
- $root_cache_directory = $config->getCacheDirectory();
-
- if ($this->existing_file_content_hashes === null) {
- $file_hashes_path = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_HASHES;
-
- if ($root_cache_directory && is_readable($file_hashes_path)) {
- $fp = fopen($file_hashes_path, 'r');
- $max_wait_cycles = 5;
- $has_lock = false;
- while ($max_wait_cycles > 0) {
- if (flock($fp, LOCK_SH)) {
- $has_lock = true;
- break;
- }
- $max_wait_cycles--;
- usleep(50000);
- }
-
- if (!$has_lock) {
- fclose($fp);
- error_log('Could not acquire lock for content hashes file');
- $this->existing_file_content_hashes = [];
-
- return [];
- }
-
- $hashes_encoded = fread($fp, filesize($file_hashes_path));
- fclose($fp);
-
- if (!$hashes_encoded) {
- error_log('Unexpected value when loading from file content hashes');
- $this->existing_file_content_hashes = [];
-
- return [];
- }
-
- $hashes_decoded = json_decode($hashes_encoded, true);
-
- if (!is_array($hashes_decoded)) {
- error_log('Unexpected value ' . gettype($hashes_decoded));
- $this->existing_file_content_hashes = [];
-
- return [];
- }
-
- /** @var array<string, string> $hashes_decoded */
- $this->existing_file_content_hashes = $hashes_decoded;
- } else {
- $this->existing_file_content_hashes = [];
- }
- }
-
- return $this->existing_file_content_hashes;
- }
-
- /**
- * @param list<PhpParser\Node\Stmt> $stmts
- */
- public function saveStatementsToCache(
- string $file_path,
- string $file_content_hash,
- array $stmts,
- bool $touch_only
- ): void {
- $root_cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$root_cache_directory) {
- return;
- }
-
- $file_cache_key = $this->getParserCacheKey(
- $file_path
- );
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::PARSER_CACHE_DIRECTORY;
-
- $cache_location = $parser_cache_directory . DIRECTORY_SEPARATOR . $file_cache_key;
-
- if ($touch_only) {
- touch($cache_location);
- } else {
- $this->createCacheDirectory($parser_cache_directory);
-
- if ($this->use_igbinary) {
- file_put_contents($cache_location, igbinary_serialize($stmts));
- } else {
- file_put_contents($cache_location, serialize($stmts));
- }
-
- $this->new_file_content_hashes[$file_cache_key] = $file_content_hash;
- }
- }
-
- /**
- * @return array<string, string>
- */
- public function getNewFileContentHashes(): array
- {
- return $this->new_file_content_hashes;
- }
-
- /**
- * @param array<string, string> $file_content_hashes
- *
- */
- public function addNewFileContentHashes(array $file_content_hashes): void
- {
- $this->new_file_content_hashes = $file_content_hashes + $this->new_file_content_hashes;
- }
-
- public function saveFileContentHashes(): void
- {
- $root_cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$root_cache_directory) {
- return;
- }
-
- // directory was removed
- // most likely due to a race condition with other psalm instances that were manually started at the same time
- clearstatcache(true, $root_cache_directory);
- if (!is_dir($root_cache_directory)) {
- return;
- }
-
- $file_content_hashes = $this->new_file_content_hashes + $this->getExistingFileContentHashes();
-
- $file_hashes_path = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_HASHES;
-
- file_put_contents(
- $file_hashes_path,
- json_encode($file_content_hashes),
- LOCK_EX
- );
- }
-
- public function cacheFileContents(string $file_path, string $file_contents): void
- {
- if (!$this->use_file_cache) {
- return;
- }
-
- $root_cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$root_cache_directory) {
- return;
- }
-
- $file_cache_key = $this->getParserCacheKey(
- $file_path
- );
-
- $parser_cache_directory = $root_cache_directory . DIRECTORY_SEPARATOR . self::FILE_CONTENTS_CACHE_DIRECTORY;
-
- $cache_location = $parser_cache_directory . DIRECTORY_SEPARATOR . $file_cache_key;
-
- $this->createCacheDirectory($parser_cache_directory);
-
- file_put_contents($cache_location, $file_contents);
- }
-
- public function deleteOldParserCaches(float $time_before): int
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$cache_directory) {
- return 0;
- }
-
- $removed_count = 0;
-
- $cache_directory .= DIRECTORY_SEPARATOR . self::PARSER_CACHE_DIRECTORY;
-
- if (is_dir($cache_directory)) {
- $directory_files = scandir($cache_directory, SCANDIR_SORT_NONE);
-
- foreach ($directory_files as $directory_file) {
- $full_path = $cache_directory . DIRECTORY_SEPARATOR . $directory_file;
-
- if ($directory_file[0] === '.') {
- continue;
- }
-
- if (filemtime($full_path) < $time_before && is_writable($full_path)) {
- unlink($full_path);
- ++$removed_count;
- }
- }
- }
-
- return $removed_count;
- }
-
- private function getParserCacheKey(string $file_name): string
- {
- return md5($file_name) . ($this->use_igbinary ? '-igbinary' : '') . '-r';
- }
-
- private function createCacheDirectory(string $parser_cache_directory): void
- {
- if (!is_dir($parser_cache_directory)) {
- try {
- mkdir($parser_cache_directory, 0777, true);
- } catch (RuntimeException $e) {
- // Race condition (#4483)
- if (!is_dir($parser_cache_directory)) {
- error_log('Could not create parser cache directory: ' . $parser_cache_directory);
- }
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ProjectCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ProjectCacheProvider.php
deleted file mode 100644
index 5775577..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ProjectCacheProvider.php
+++ /dev/null
@@ -1,141 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Psalm\Config;
-
-use function file_exists;
-use function file_get_contents;
-use function file_put_contents;
-use function filemtime;
-use function hash;
-use function mkdir;
-use function touch;
-
-use const DIRECTORY_SEPARATOR;
-use const PHP_VERSION_ID;
-
-/**
- * Used to determine which files reference other files, necessary for using the --diff
- * option from the command line.
- */
-class ProjectCacheProvider
-{
- private const GOOD_RUN_NAME = 'good_run';
- private const COMPOSER_LOCK_HASH = 'composer_lock_hash';
-
- /**
- * @var int|null
- */
- private $last_run;
-
- /**
- * @var string|null
- */
- private $composer_lock_hash;
-
- private $composer_lock_location;
-
- public function __construct(string $composer_lock_location)
- {
- $this->composer_lock_location = $composer_lock_location;
- }
-
- public function canDiffFiles(): bool
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- return $cache_directory && file_exists($cache_directory . DIRECTORY_SEPARATOR . self::GOOD_RUN_NAME);
- }
-
- public function processSuccessfulRun(float $start_time, string $psalm_version): void
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$cache_directory) {
- return;
- }
-
- $run_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::GOOD_RUN_NAME;
-
- file_put_contents($run_cache_location, $psalm_version);
-
- touch($run_cache_location, (int)$start_time);
- }
-
- public function getLastRun(string $psalm_version): int
- {
- if ($this->last_run === null) {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- $run_cache_location = $cache_directory . DIRECTORY_SEPARATOR . self::GOOD_RUN_NAME;
-
- if (file_exists($run_cache_location) && file_get_contents($run_cache_location) === $psalm_version) {
- $this->last_run = filemtime($run_cache_location);
- } else {
- $this->last_run = 0;
- }
- }
-
- return $this->last_run;
- }
-
- public function hasLockfileChanged(): bool
- {
- if (!file_exists($this->composer_lock_location)) {
- return true;
- }
-
- $lockfile_contents = file_get_contents($this->composer_lock_location);
-
- if (!$lockfile_contents) {
- return true;
- }
-
- if (PHP_VERSION_ID >= 80100) {
- $hash = hash('xxh128', $lockfile_contents);
- } else {
- $hash = hash('md4', $lockfile_contents);
- }
-
- $changed = $hash !== $this->getComposerLockHash();
-
- $this->composer_lock_hash = $hash;
-
- return $changed;
- }
-
- public function updateComposerLockHash(): void
- {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- if (!$cache_directory || !$this->composer_lock_hash) {
- return;
- }
-
- if (!file_exists($cache_directory)) {
- mkdir($cache_directory, 0777, true);
- }
-
- $lock_hash_location = $cache_directory . DIRECTORY_SEPARATOR . self::COMPOSER_LOCK_HASH;
-
- file_put_contents($lock_hash_location, $this->composer_lock_hash);
- }
-
- protected function getComposerLockHash(): string
- {
- if ($this->composer_lock_hash === null) {
- $cache_directory = Config::getInstance()->getCacheDirectory();
-
- $lock_hash_location = $cache_directory . DIRECTORY_SEPARATOR . self::COMPOSER_LOCK_HASH;
-
- if (file_exists($lock_hash_location)) {
- $this->composer_lock_hash = file_get_contents($lock_hash_location) ?: '';
- } else {
- $this->composer_lock_hash = '';
- }
- }
-
- return $this->composer_lock_hash;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyExistenceProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyExistenceProvider.php
deleted file mode 100644
index 6af68fe..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyExistenceProvider.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Plugin\EventHandler\Event\PropertyExistenceProviderEvent;
-use Psalm\Plugin\EventHandler\PropertyExistenceProviderInterface;
-use Psalm\Plugin\Hook\PropertyExistenceProviderInterface as LegacyPropertyExistenceProviderInterface;
-use Psalm\StatementsSource;
-
-use function is_subclass_of;
-use function strtolower;
-
-class PropertyExistenceProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(PropertyExistenceProviderEvent): ?bool>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * string,
- * string,
- * bool,
- * ?StatementsSource=,
- * ?Context=,
- * ?CodeLocation=
- * ): ?bool>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
- }
-
- /**
- * @param class-string<LegacyPropertyExistenceProviderInterface>
- * |class-string<PropertyExistenceProviderInterface> $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyPropertyExistenceProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'doesPropertyExist']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, PropertyExistenceProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'doesPropertyExist']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(PropertyExistenceProviderEvent): ?bool $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * string,
- * string,
- * bool,
- * ?StatementsSource=,
- * ?Context=,
- * ?CodeLocation=
- * ): ?bool $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- public function doesPropertyExist(
- string $fq_classlike_name,
- string $property_name,
- bool $read_mode,
- ?StatementsSource $source = null,
- ?Context $context = null,
- ?CodeLocation $code_location = null
- ): ?bool {
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) {
- $property_exists = $property_handler(
- $fq_classlike_name,
- $property_name,
- $read_mode,
- $source,
- $context,
- $code_location
- );
-
- if ($property_exists !== null) {
- return $property_exists;
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) {
- $event = new PropertyExistenceProviderEvent(
- $fq_classlike_name,
- $property_name,
- $read_mode,
- $source,
- $context,
- $code_location
- );
- $property_exists = $property_handler($event);
-
- if ($property_exists !== null) {
- return $property_exists;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider.php
deleted file mode 100644
index 2e3d1db..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider.php
+++ /dev/null
@@ -1,140 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use Psalm\Context;
-use Psalm\Internal\Provider\PropertyTypeProvider\DomDocumentPropertyTypeProvider;
-use Psalm\Plugin\EventHandler\Event\PropertyTypeProviderEvent;
-use Psalm\Plugin\EventHandler\PropertyTypeProviderInterface;
-use Psalm\Plugin\Hook\PropertyTypeProviderInterface as LegacyPropertyTypeProviderInterface;
-use Psalm\StatementsSource;
-use Psalm\Type\Union;
-
-use function is_subclass_of;
-use function strtolower;
-
-class PropertyTypeProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(PropertyTypeProviderEvent): ?Union>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * string,
- * string,
- * bool,
- * ?StatementsSource=,
- * ?Context=
- * ): ?Union>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
-
- $this->registerClass(DomDocumentPropertyTypeProvider::class);
- }
-
- /**
- * @param class-string $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyPropertyTypeProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getPropertyType']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, PropertyTypeProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'getPropertyType']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(PropertyTypeProviderEvent): ?Union $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * string,
- * string,
- * bool,
- * ?StatementsSource=,
- * ?Context=
- * ): ?Union $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- public function getPropertyType(
- string $fq_classlike_name,
- string $property_name,
- bool $read_mode,
- ?StatementsSource $source = null,
- ?Context $context = null
- ): ?Union {
-
- if ($source) {
- $source->addSuppressedIssues(['NonInvariantDocblockPropertyType']);
- }
-
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) {
- $property_type = $property_handler(
- $fq_classlike_name,
- $property_name,
- $read_mode,
- $source,
- $context
- );
-
- if ($property_type !== null) {
- return $property_type;
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) {
- $event = new PropertyTypeProviderEvent(
- $fq_classlike_name,
- $property_name,
- $read_mode,
- $source,
- $context
- );
- $property_type = $property_handler($event);
-
- if ($property_type !== null) {
- return $property_type;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php
deleted file mode 100644
index 2b12767..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyTypeProvider/DomDocumentPropertyTypeProvider.php
+++ /dev/null
@@ -1,33 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Provider\PropertyTypeProvider;
-
-use Psalm\Plugin\EventHandler\Event\PropertyTypeProviderEvent;
-use Psalm\Plugin\EventHandler\PropertyTypeProviderInterface;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-use function strtolower;
-
-class DomDocumentPropertyTypeProvider implements PropertyTypeProviderInterface
-{
- public static function getPropertyType(PropertyTypeProviderEvent $event): ?Union
- {
- if (strtolower($event->getPropertyName()) === 'documentelement') {
- $type = new Union([new TNamedObject('DOMElement'), new TNull()]);
- $type->ignore_nullable_issues = true;
-
- return $type;
- }
-
- return null;
- }
-
- public static function getClassLikeNames(): array
- {
- return ['domdocument'];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php
deleted file mode 100644
index 4ad5794..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/PropertyVisibilityProvider.php
+++ /dev/null
@@ -1,138 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use Closure;
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Plugin\EventHandler\Event\PropertyVisibilityProviderEvent;
-use Psalm\Plugin\EventHandler\PropertyVisibilityProviderInterface;
-use Psalm\Plugin\Hook\PropertyVisibilityProviderInterface as LegacyPropertyVisibilityProviderInterface;
-use Psalm\StatementsSource;
-
-use function is_subclass_of;
-use function strtolower;
-
-class PropertyVisibilityProvider
-{
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(PropertyVisibilityProviderEvent): ?bool>
- * >
- */
- private static $handlers = [];
-
- /**
- * @var array<
- * lowercase-string,
- * array<Closure(
- * StatementsSource,
- * string,
- * string,
- * bool,
- * Context,
- * CodeLocation
- * ): ?bool>
- * >
- */
- private static $legacy_handlers = [];
-
- public function __construct()
- {
- self::$handlers = [];
- self::$legacy_handlers = [];
- }
-
- /**
- * @param class-string<LegacyPropertyVisibilityProviderInterface>
- * |class-string<PropertyVisibilityProviderInterface> $class
- */
- public function registerClass(string $class): void
- {
- if (is_subclass_of($class, LegacyPropertyVisibilityProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'isPropertyVisible']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerLegacyClosure($fq_classlike_name, $callable);
- }
- } elseif (is_subclass_of($class, PropertyVisibilityProviderInterface::class, true)) {
- $callable = Closure::fromCallable([$class, 'isPropertyVisible']);
-
- foreach ($class::getClassLikeNames() as $fq_classlike_name) {
- $this->registerClosure($fq_classlike_name, $callable);
- }
- }
- }
-
- /**
- * @param Closure(PropertyVisibilityProviderEvent): ?bool $c
- */
- public function registerClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- /**
- * @param Closure(
- * StatementsSource,
- * string,
- * string,
- * bool,
- * Context,
- * CodeLocation
- * ): ?bool $c
- */
- public function registerLegacyClosure(string $fq_classlike_name, Closure $c): void
- {
- self::$legacy_handlers[strtolower($fq_classlike_name)][] = $c;
- }
-
- public function has(string $fq_classlike_name): bool
- {
- return isset(self::$handlers[strtolower($fq_classlike_name)]) ||
- isset(self::$legacy_handlers[strtolower($fq_classlike_name)]);
- }
-
- public function isPropertyVisible(
- StatementsSource $source,
- string $fq_classlike_name,
- string $property_name,
- bool $read_mode,
- Context $context,
- CodeLocation $code_location
- ): ?bool {
- foreach (self::$legacy_handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) {
- $property_visible = $property_handler(
- $source,
- $fq_classlike_name,
- $property_name,
- $read_mode,
- $context,
- $code_location
- );
-
- if ($property_visible !== null) {
- return $property_visible;
- }
- }
-
- foreach (self::$handlers[strtolower($fq_classlike_name)] ?? [] as $property_handler) {
- $event = new PropertyVisibilityProviderEvent(
- $source,
- $fq_classlike_name,
- $property_name,
- $read_mode,
- $context,
- $code_location
- );
- $property_visible = $property_handler($event);
-
- if ($property_visible !== null) {
- return $property_visible;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/Providers.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/Providers.php
deleted file mode 100644
index d42e100..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/Providers.php
+++ /dev/null
@@ -1,66 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-/**
- * @internal
- */
-class Providers
-{
- /**
- * @var FileProvider
- */
- public $file_provider;
-
- /**
- * @var ?ParserCacheProvider
- */
- public $parser_cache_provider;
-
- /**
- * @var FileStorageProvider
- */
- public $file_storage_provider;
-
- /**
- * @var ClassLikeStorageProvider
- */
- public $classlike_storage_provider;
-
- /**
- * @var StatementsProvider
- */
- public $statements_provider;
-
- /**
- * @var FileReferenceProvider
- */
- public $file_reference_provider;
-
- /**
- * @var ?ProjectCacheProvider
- */
- public $project_cache_provider;
-
- public function __construct(
- FileProvider $file_provider,
- ?ParserCacheProvider $parser_cache_provider = null,
- ?FileStorageCacheProvider $file_storage_cache_provider = null,
- ?ClassLikeStorageCacheProvider $classlike_storage_cache_provider = null,
- ?FileReferenceCacheProvider $file_reference_cache_provider = null,
- ?ProjectCacheProvider $project_cache_provider = null
- ) {
- $this->file_provider = $file_provider;
- $this->parser_cache_provider = $parser_cache_provider;
- $this->project_cache_provider = $project_cache_provider;
-
- $this->file_storage_provider = new FileStorageProvider($file_storage_cache_provider);
- $this->classlike_storage_provider = new ClassLikeStorageProvider($classlike_storage_cache_provider);
- $this->statements_provider = new StatementsProvider(
- $file_provider,
- $parser_cache_provider,
- $file_storage_cache_provider
- );
- $this->file_reference_provider = new FileReferenceProvider($file_reference_cache_provider);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php
deleted file mode 100644
index 8687709..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayChunkReturnTypeProvider.php
+++ /dev/null
@@ -1,55 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Type\ArrayType;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-
-use function count;
-
-class ArrayChunkReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_chunk'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $call_args = $event->getCallArgs();
- $statements_source = $event->getStatementsSource();
- if (count($call_args) >= 2
- && ($array_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[0]->value))
- && $array_arg_type->isSingle()
- && $array_arg_type->hasArray()
- && ($array_type = ArrayType::infer($array_arg_type->getAtomicTypes()['array']))
- ) {
- $preserve_keys = isset($call_args[2])
- && ($preserve_keys_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[2]->value))
- && (string) $preserve_keys_arg_type !== 'false';
-
- return new Union([
- new TList(
- new Union([
- $preserve_keys
- ? new TNonEmptyArray([$array_type->key, $array_type->value])
- : new TNonEmptyList($array_type->value)
- ])
- )
- ]);
- }
-
- return new Union([new TList(Type::getArray())]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php
deleted file mode 100644
index 89e68a1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayColumnReturnTypeProvider.php
+++ /dev/null
@@ -1,142 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-
-use function count;
-use function reset;
-
-class ArrayColumnReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_column'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer
- || count($call_args) < 2
- ) {
- return Type::getMixed();
- }
-
- $row_shape = null;
- $input_array_not_empty = false;
-
- // calculate row shape
- if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
- && $first_arg_type->isSingle()
- && $first_arg_type->hasArray()
- ) {
- $input_array = $first_arg_type->getAtomicTypes()['array'];
- $row_type = null;
- if ($input_array instanceof TKeyedArray) {
- $row_type = $input_array->getGenericArrayType()->type_params[1];
- } elseif ($input_array instanceof TArray) {
- $row_type = $input_array->type_params[1];
- } elseif ($input_array instanceof TList) {
- $row_type = $input_array->type_param;
- }
-
- if ($row_type && $row_type->isSingle()) {
- if ($row_type->hasArray()) {
- $row_shape = $row_type->getAtomicTypes()['array'];
- } elseif ($row_type->hasObjectType()) {
- $row_shape_union = GetObjectVarsReturnTypeProvider::getGetObjectVarsReturnType(
- $row_type,
- $statements_source,
- $event->getContext(),
- $event->getCodeLocation()
- );
- if ($row_shape_union->isSingle()) {
- $row_shape_union_parts = $row_shape_union->getAtomicTypes();
- $row_shape = reset($row_shape_union_parts);
- }
- }
- }
-
- $input_array_not_empty = $input_array instanceof TNonEmptyList ||
- $input_array instanceof TNonEmptyArray ||
- $input_array instanceof TKeyedArray;
- }
-
- $value_column_name = null;
- $value_column_name_is_null = false;
- // calculate value column name
- if (($second_arg_type = $statements_source->node_data->getType($call_args[1]->value))) {
- if ($second_arg_type->isSingleIntLiteral()) {
- $value_column_name = $second_arg_type->getSingleIntLiteral()->value;
- } elseif ($second_arg_type->isSingleStringLiteral()) {
- $value_column_name = $second_arg_type->getSingleStringLiteral()->value;
- }
- $value_column_name_is_null = $second_arg_type->isNull();
- }
-
- $key_column_name = null;
- $third_arg_type = null;
- // calculate key column name
- if (isset($call_args[2])) {
- $third_arg_type = $statements_source->node_data->getType($call_args[2]->value);
-
- if ($third_arg_type) {
- if ($third_arg_type->isSingleIntLiteral()) {
- $key_column_name = $third_arg_type->getSingleIntLiteral()->value;
- } elseif ($third_arg_type->isSingleStringLiteral()) {
- $key_column_name = $third_arg_type->getSingleStringLiteral()->value;
- }
- }
- }
-
- $result_key_type = Type::getArrayKey();
- $result_element_type = null;
- $have_at_least_one_res = false;
- // calculate results
- if ($row_shape instanceof TKeyedArray) {
- if ((null !== $value_column_name) && isset($row_shape->properties[$value_column_name])) {
- $result_element_type = $row_shape->properties[$value_column_name];
- // When the selected key is possibly_undefined, the resulting array can be empty
- if ($input_array_not_empty && $result_element_type->possibly_undefined !== true) {
- $have_at_least_one_res = true;
- }
- //array_column skips undefined elements so resulting type is necessarily defined
- $result_element_type->possibly_undefined = false;
- } elseif ($value_column_name_is_null) {
- $result_element_type = new Union([$row_shape]);
- } else {
- $result_element_type = Type::getMixed();
- }
-
- if ((null !== $key_column_name) && isset($row_shape->properties[$key_column_name])) {
- $result_key_type = $row_shape->properties[$key_column_name];
- }
- }
-
- if (isset($call_args[2]) && (string)$third_arg_type !== 'null') {
- $type = $have_at_least_one_res ?
- new TNonEmptyArray([$result_key_type, $result_element_type ?? Type::getMixed()])
- : new TArray([$result_key_type, $result_element_type ?? Type::getMixed()]);
- } else {
- $type = $have_at_least_one_res ?
- new TNonEmptyList($result_element_type ?? Type::getMixed())
- : new TList($result_element_type ?? Type::getMixed());
- }
-
- return new Union([$type]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php
deleted file mode 100644
index 9a16f98..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFillReturnTypeProvider.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-
-class ArrayFillReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_fill'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg_type = isset($call_args[0]) ? $statements_source->node_data->getType($call_args[0]->value) : null;
- $second_arg_type = isset($call_args[1]) ? $statements_source->node_data->getType($call_args[1]->value) : null;
- $third_arg_type = isset($call_args[2]) ? $statements_source->node_data->getType($call_args[2]->value) : null;
-
- $value_type_from_third_arg = $third_arg_type ? clone $third_arg_type : Type::getMixed();
-
- if ($first_arg_type
- && $first_arg_type->isSingleIntLiteral()
- && $first_arg_type->getSingleIntLiteral()->value === 0
- ) {
- if ($second_arg_type
- && self::isPositiveNumericType($second_arg_type)
- ) {
- return new Union([
- new TNonEmptyList(
- $value_type_from_third_arg
- )
- ]);
- }
-
- return new Union([
- new TList(
- $value_type_from_third_arg
- )
- ]);
- }
-
- if ($second_arg_type
- && self::isPositiveNumericType($second_arg_type)
- ) {
- if ($first_arg_type
- && $first_arg_type->isSingleIntLiteral()
- && $second_arg_type->isSingleIntLiteral()
- ) {
- return new Union([
- new TNonEmptyArray([
- new Union([new TIntRange(
- $first_arg_type->getSingleIntLiteral()->value,
- $second_arg_type->getSingleIntLiteral()->value
- )]),
- $value_type_from_third_arg,
- ])
- ]);
- }
-
- return new Union([
- new TNonEmptyArray([
- Type::getInt(),
- $value_type_from_third_arg,
- ])
- ]);
- }
-
- return new Union([
- new TArray([
- Type::getInt(),
- $value_type_from_third_arg,
- ])
- ]);
- }
-
- private static function isPositiveNumericType(Union $arg): bool
- {
- if ($arg->isSingle() && $arg->hasPositiveInt()) {
- return true;
- }
-
- if ($arg->isSingle()) {
- foreach ($arg->getRangeInts() as $range_int) {
- if ($range_int->isPositive()) {
- return true;
- }
- }
- }
-
- return $arg->isSingleIntLiteral() && $arg->getSingleIntLiteral()->value > 0;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php
deleted file mode 100644
index b8612e1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayFilterReturnTypeProvider.php
+++ /dev/null
@@ -1,331 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Exception\ComplicatedExpressionException;
-use Psalm\Internal\Algebra;
-use Psalm\Internal\Algebra\FormulaGenerator;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\ExpressionIdentifier;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\AssertionReconciler;
-use Psalm\Issue\InvalidReturnType;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function array_filter;
-use function array_map;
-use function array_slice;
-use function count;
-use function is_string;
-use function mt_rand;
-use function reset;
-use function spl_object_id;
-
-class ArrayFilterReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_filter'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $context = $event->getContext();
- $code_location = $event->getCodeLocation();
- if (!$statements_source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- $array_arg = $call_args[0]->value ?? null;
-
- $first_arg_array = $array_arg
- && ($first_arg_type = $statements_source->node_data->getType($array_arg))
- && $first_arg_type->hasType('array')
- && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
- && ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- || $array_atomic_type instanceof TList)
- ? $array_atomic_type
- : null;
-
- if (!$first_arg_array) {
- return Type::getArray();
- }
-
- if ($first_arg_array instanceof TArray) {
- $inner_type = $first_arg_array->type_params[1];
- $key_type = clone $first_arg_array->type_params[0];
- } elseif ($first_arg_array instanceof TList) {
- $inner_type = $first_arg_array->type_param;
- $key_type = Type::getInt();
- } else {
- $inner_type = $first_arg_array->getGenericValueType();
- $key_type = $first_arg_array->getGenericKeyType();
-
- if (!isset($call_args[1]) && !$first_arg_array->previous_value_type) {
- $had_one = count($first_arg_array->properties) === 1;
-
- $first_arg_array = clone $first_arg_array;
-
- $new_properties = array_filter(
- array_map(
- static function ($keyed_type) use ($statements_source, $context) {
- $prev_keyed_type = $keyed_type;
-
- $keyed_type = AssertionReconciler::reconcile(
- '!falsy',
- clone $keyed_type,
- '',
- $statements_source,
- $context->inside_loop,
- [],
- null,
- $statements_source->getSuppressedIssues()
- );
-
- $keyed_type->possibly_undefined = !$prev_keyed_type->isAlwaysTruthy();
-
- return $keyed_type;
- },
- $first_arg_array->properties
- ),
- static function ($keyed_type) {
- return !$keyed_type->isEmpty();
- }
- );
-
- if (!$new_properties) {
- return Type::getEmptyArray();
- }
-
- $first_arg_array->properties = $new_properties;
-
- $first_arg_array->is_list = $first_arg_array->is_list && $had_one;
- $first_arg_array->sealed = false;
-
- return new Union([$first_arg_array]);
- }
- }
-
- if (!isset($call_args[1])) {
- $inner_type = AssertionReconciler::reconcile(
- '!falsy',
- clone $inner_type,
- '',
- $statements_source,
- $context->inside_loop,
- [],
- null,
- $statements_source->getSuppressedIssues()
- );
-
- if ($first_arg_array instanceof TKeyedArray
- && $first_arg_array->is_list
- && $key_type->isSingleIntLiteral()
- && $key_type->getSingleIntLiteral()->value === 0
- ) {
- return new Union([
- new TList(
- $inner_type
- ),
- ]);
- }
-
- if ($key_type->getLiteralStrings()) {
- $key_type->addType(new TString);
- }
-
- if ($key_type->getLiteralInts()) {
- $key_type->addType(new TInt);
- }
-
- if ($inner_type->isUnionEmpty()) {
- return Type::getEmptyArray();
- }
-
- return new Union([
- new TArray([
- $key_type,
- $inner_type,
- ]),
- ]);
- }
-
- if (!isset($call_args[2])) {
- $function_call_arg = $call_args[1];
-
- if ($function_call_arg->value instanceof PhpParser\Node\Scalar\String_
- || $function_call_arg->value instanceof PhpParser\Node\Expr\Array_
- || $function_call_arg->value instanceof PhpParser\Node\Expr\BinaryOp\Concat
- ) {
- $mapping_function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
- $statements_source,
- $function_call_arg->value
- );
-
- if ($array_arg && $mapping_function_ids) {
- $assertions = [];
-
- $fake_var_discriminator = mt_rand();
- ArrayMapReturnTypeProvider::getReturnTypeFromMappingIds(
- $statements_source,
- $mapping_function_ids,
- $context,
- $function_call_arg,
- array_slice($call_args, 0, 1),
- $assertions,
- $fake_var_discriminator
- );
-
- $array_var_id = ExpressionIdentifier::getArrayVarId(
- $array_arg,
- null,
- $statements_source
- );
-
- if (isset($assertions[$array_var_id . "[\$__fake_{$fake_var_discriminator}_offset_var__]"])) {
- $changed_var_ids = [];
-
- $assertions = [
- '$inner_type' =>
- $assertions["{$array_var_id}[\$__fake_{$fake_var_discriminator}_offset_var__]"],
- ];
-
- $reconciled_types = Reconciler::reconcileKeyedTypes(
- $assertions,
- $assertions,
- ['$inner_type' => $inner_type],
- $changed_var_ids,
- ['$inner_type' => true],
- $statements_source,
- $statements_source->getTemplateTypeMap() ?: [],
- false,
- new CodeLocation($statements_source, $function_call_arg->value)
- );
-
- if (isset($reconciled_types['$inner_type'])) {
- $inner_type = $reconciled_types['$inner_type'];
- }
- }
-
- ArrayMapReturnTypeProvider::cleanContext($context, $fake_var_discriminator);
- }
- } elseif (($function_call_arg->value instanceof PhpParser\Node\Expr\Closure
- || $function_call_arg->value instanceof PhpParser\Node\Expr\ArrowFunction)
- && ($second_arg_type = $statements_source->node_data->getType($function_call_arg->value))
- && ($closure_types = $second_arg_type->getClosureTypes())
- ) {
- $closure_atomic_type = reset($closure_types);
- $closure_return_type = $closure_atomic_type->return_type ?: Type::getMixed();
-
- if ($closure_return_type->isVoid()) {
- IssueBuffer::maybeAdd(
- new InvalidReturnType(
- 'No return type could be found in the closure passed to array_filter',
- $code_location
- ),
- $statements_source->getSuppressedIssues()
- );
-
- return Type::getArray();
- }
-
- /** @var list<PhpParser\Node\Stmt> */
- $function_call_stmts = $function_call_arg->value->getStmts();
-
- if (count($function_call_stmts) === 1 && count($function_call_arg->value->params)) {
- $first_param = $function_call_arg->value->params[0];
- $stmt = $function_call_stmts[0];
-
- if ($first_param->variadic === false
- && $first_param->var instanceof PhpParser\Node\Expr\Variable
- && is_string($first_param->var->name)
- && $stmt instanceof PhpParser\Node\Stmt\Return_
- && $stmt->expr
- ) {
- $codebase = $statements_source->getCodebase();
-
- $cond_object_id = spl_object_id($stmt->expr);
-
- try {
- $filter_clauses = FormulaGenerator::getFormula(
- $cond_object_id,
- $cond_object_id,
- $stmt->expr,
- $context->self,
- $statements_source,
- $codebase
- );
- } catch (ComplicatedExpressionException $e) {
- $filter_clauses = [];
- }
-
- $assertions = Algebra::getTruthsFromFormula(
- $filter_clauses,
- $cond_object_id
- );
-
- if (isset($assertions['$' . $first_param->var->name])) {
- $changed_var_ids = [];
-
- $assertions = ['$inner_type' => $assertions['$' . $first_param->var->name]];
-
- $reconciled_types = Reconciler::reconcileKeyedTypes(
- $assertions,
- $assertions,
- ['$inner_type' => $inner_type],
- $changed_var_ids,
- ['$inner_type' => true],
- $statements_source,
- $statements_source->getTemplateTypeMap() ?: [],
- false,
- new CodeLocation($statements_source, $stmt)
- );
-
- if (isset($reconciled_types['$inner_type'])) {
- $inner_type = $reconciled_types['$inner_type'];
- }
- }
- }
- }
- }
-
- return new Union([
- new TArray([
- $key_type,
- $inner_type,
- ]),
- ]);
- }
-
- if ($inner_type->isUnionEmpty()) {
- return Type::getEmptyArray();
- }
-
- return new Union([
- new TArray([
- $key_type,
- $inner_type,
- ]),
- ]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php
deleted file mode 100644
index 47c786e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMapReturnTypeProvider.php
+++ /dev/null
@@ -1,499 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PhpParser;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\Statements\Expression\AssertionFinder;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\FunctionCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\MethodCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\Call\StaticCallAnalyzer;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\ArrayType;
-use Psalm\Node\Expr\VirtualArrayDimFetch;
-use Psalm\Node\Expr\VirtualFuncCall;
-use Psalm\Node\Expr\VirtualMethodCall;
-use Psalm\Node\Expr\VirtualStaticCall;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\VirtualArg;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_map;
-use function array_shift;
-use function array_slice;
-use function count;
-use function explode;
-use function in_array;
-use function mt_rand;
-use function reset;
-use function strpos;
-use function substr;
-
-class ArrayMapReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_map'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $context = $event->getContext();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $function_call_arg = $call_args[0] ?? null;
-
- $function_call_type = $function_call_arg
- ? $statements_source->node_data->getType($function_call_arg->value)
- : null;
-
- if ($function_call_type && $function_call_type->isNull()) {
- array_shift($call_args);
-
- $array_arg_types = [];
-
- foreach ($call_args as $call_arg) {
- $call_arg_type = $statements_source->node_data->getType($call_arg->value);
-
- if ($call_arg_type) {
- $array_arg_types[] = clone $call_arg_type;
- } else {
- $array_arg_types[] = Type::getMixed();
- break;
- }
- }
-
- if ($array_arg_types) {
- return new Union([new TKeyedArray($array_arg_types)]);
- }
-
- return Type::getArray();
- }
-
- $array_arg = $call_args[1] ?? null;
-
- if (!$array_arg) {
- return Type::getArray();
- }
-
- $array_arg_atomic_type = null;
- $array_arg_type = null;
-
- if ($array_arg_union_type = $statements_source->node_data->getType($array_arg->value)) {
- $arg_types = $array_arg_union_type->getAtomicTypes();
-
- if (isset($arg_types['array'])) {
- $array_arg_atomic_type = $arg_types['array'];
- $array_arg_type = ArrayType::infer($array_arg_atomic_type);
- }
- }
-
- $generic_key_type = null;
- $mapping_return_type = null;
-
- if ($function_call_arg && $function_call_type) {
- if (count($call_args) === 2) {
- $generic_key_type = $array_arg_type->key ?? Type::getArrayKey();
- } else {
- $generic_key_type = Type::getInt();
- }
-
- if ($function_call_type->hasCallableType()) {
- $closure_types = $function_call_type->getClosureTypes() ?: $function_call_type->getCallableTypes();
- $closure_atomic_type = reset($closure_types);
-
- $closure_return_type = $closure_atomic_type->return_type ?: Type::getMixed();
-
- if ($closure_return_type->isVoid()) {
- $closure_return_type = Type::getNull();
- }
-
- $mapping_return_type = clone $closure_return_type;
- } elseif ($function_call_arg->value instanceof PhpParser\Node\Scalar\String_
- || $function_call_arg->value instanceof PhpParser\Node\Expr\Array_
- || $function_call_arg->value instanceof PhpParser\Node\Expr\BinaryOp\Concat
- ) {
- $mapping_function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
- $statements_source,
- $function_call_arg->value
- );
-
- if ($mapping_function_ids) {
- $mapping_return_type = self::getReturnTypeFromMappingIds(
- $statements_source,
- $mapping_function_ids,
- $context,
- $function_call_arg,
- array_slice($call_args, 1)
- );
- }
-
- if ($function_call_arg->value instanceof PhpParser\Node\Expr\Array_
- && isset($function_call_arg->value->items[0])
- && isset($function_call_arg->value->items[1])
- && $function_call_arg->value->items[1]->value instanceof PhpParser\Node\Scalar\String_
- && $function_call_arg->value->items[0]->value instanceof PhpParser\Node\Expr\Variable
- && ($variable_type
- = $statements_source->node_data->getType($function_call_arg->value->items[0]->value))
- ) {
- $fake_method_call = null;
-
- foreach ($variable_type->getAtomicTypes() as $variable_atomic_type) {
- if ($variable_atomic_type instanceof TTemplateParam
- || $variable_atomic_type instanceof TTemplateParamClass
- ) {
- $fake_method_call = new VirtualStaticCall(
- $function_call_arg->value->items[0]->value,
- $function_call_arg->value->items[1]->value->value,
- []
- );
- }
- }
-
- if ($fake_method_call) {
- $fake_method_return_type = self::executeFakeCall(
- $statements_source,
- $fake_method_call,
- $context
- );
-
- if ($fake_method_return_type) {
- $mapping_return_type = $fake_method_return_type;
- }
- }
- }
- }
- }
-
- if ($mapping_return_type && $generic_key_type) {
- if ($array_arg_atomic_type instanceof TKeyedArray && count($call_args) === 2) {
- $atomic_type = new TKeyedArray(
- array_map(
- /**
- * @return Union
- */
- function (Union $_) use ($mapping_return_type): Union {
- return clone $mapping_return_type;
- },
- $array_arg_atomic_type->properties
- )
- );
- $atomic_type->is_list = $array_arg_atomic_type->is_list;
- $atomic_type->sealed = $array_arg_atomic_type->sealed;
- $atomic_type->previous_key_type = $array_arg_atomic_type->previous_key_type;
- $atomic_type->previous_value_type = $mapping_return_type;
-
- return new Union([$atomic_type]);
- }
-
- if ($array_arg_atomic_type instanceof TList
- || count($call_args) !== 2
- ) {
- if ($array_arg_atomic_type instanceof TNonEmptyList) {
- return new Union([
- new TNonEmptyList(
- $mapping_return_type
- ),
- ]);
- }
-
- return new Union([
- new TList(
- $mapping_return_type
- ),
- ]);
- }
-
- if ($array_arg_atomic_type instanceof TNonEmptyArray) {
- return new Union([
- new TNonEmptyArray([
- $generic_key_type,
- $mapping_return_type,
- ]),
- ]);
- }
-
- return new Union([
- new TArray([
- $generic_key_type,
- $mapping_return_type,
- ])
- ]);
- }
-
- return count($call_args) === 2 && !($array_arg_type->is_list ?? false)
- ? new Union([
- new TArray([
- $array_arg_type->key ?? Type::getArrayKey(),
- Type::getMixed(),
- ])
- ])
- : Type::getList();
- }
-
- /**
- * @param-out array<string, array<array<int, string>>>|null $assertions
- */
- private static function executeFakeCall(
- StatementsAnalyzer $statements_analyzer,
- PhpParser\Node\Expr $fake_call,
- Context $context,
- ?array &$assertions = null
- ): ?Union {
- $old_data_provider = $statements_analyzer->node_data;
-
- $statements_analyzer->node_data = clone $statements_analyzer->node_data;
-
- $suppressed_issues = $statements_analyzer->getSuppressedIssues();
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('MixedArrayOffset', $suppressed_issues, true)) {
- $statements_analyzer->addSuppressedIssues(['MixedArrayOffset']);
- }
-
- $was_inside_call = $context->inside_call;
-
- $context->inside_call = true;
-
- if ($fake_call instanceof PhpParser\Node\Expr\StaticCall) {
- StaticCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_call,
- $context
- );
- } elseif ($fake_call instanceof PhpParser\Node\Expr\MethodCall) {
- MethodCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_call,
- $context
- );
- } elseif ($fake_call instanceof PhpParser\Node\Expr\FuncCall) {
- FunctionCallAnalyzer::analyze(
- $statements_analyzer,
- $fake_call,
- $context
- );
- } else {
- throw new UnexpectedValueException('UnrecognizedCall');
- }
-
- $codebase = $statements_analyzer->getCodebase();
-
- if ($assertions !== null) {
- $anded_assertions = AssertionFinder::scrapeAssertions(
- $fake_call,
- null,
- $statements_analyzer,
- $codebase
- );
-
- $assertions = $anded_assertions[0] ?? [];
- }
-
- $context->inside_call = $was_inside_call;
-
- if (!in_array('PossiblyInvalidMethodCall', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['PossiblyInvalidMethodCall']);
- }
-
- if (!in_array('MixedArrayOffset', $suppressed_issues, true)) {
- $statements_analyzer->removeSuppressedIssues(['MixedArrayOffset']);
- }
-
- $return_type = $statements_analyzer->node_data->getType($fake_call) ?? null;
-
- $statements_analyzer->node_data = $old_data_provider;
-
- return $return_type;
- }
-
- /**
- * @param non-empty-array<int, string> $mapping_function_ids
- * @param list<PhpParser\Node\Arg> $array_args
- * @param int|null $fake_var_discriminator Set the fake variable id to a known value with the discriminator
- * as a substring, and don't clear it from the context.
- * @param-out array<string, array<array<int, string>>>|null $assertions
- */
- public static function getReturnTypeFromMappingIds(
- StatementsAnalyzer $statements_source,
- array $mapping_function_ids,
- Context $context,
- PhpParser\Node\Arg $function_call_arg,
- array $array_args,
- ?array &$assertions = null,
- ?int $fake_var_discriminator = null
- ): Union {
- $mapping_return_type = null;
-
- $codebase = $statements_source->getCodebase();
-
- $clean_context = false;
-
- foreach ($mapping_function_ids as $mapping_function_id) {
- $mapping_function_id_parts = explode('&', $mapping_function_id);
-
- if ($fake_var_discriminator === null) {
- $fake_var_discriminator = mt_rand();
- $clean_context = true;
- }
-
- foreach ($mapping_function_id_parts as $mapping_function_id_part) {
- $fake_args = [];
-
- foreach ($array_args as $array_arg) {
- $fake_args[] = new VirtualArg(
- new VirtualArrayDimFetch(
- $array_arg->value,
- new VirtualVariable(
- "__fake_{$fake_var_discriminator}_offset_var__",
- $array_arg->value->getAttributes()
- ),
- $array_arg->value->getAttributes()
- ),
- false,
- false,
- $array_arg->getAttributes()
- );
- }
-
- if (strpos($mapping_function_id_part, '::') !== false) {
- $is_instance = false;
-
- if ($mapping_function_id_part[0] === '$') {
- $mapping_function_id_part = substr($mapping_function_id_part, 1);
- $is_instance = true;
- }
-
- $method_id_parts = explode('::', $mapping_function_id_part);
- [$callable_fq_class_name, $callable_method_name] = $method_id_parts;
-
- if ($is_instance) {
- $fake_method_call = new VirtualMethodCall(
- new VirtualVariable(
- "__fake_{$fake_var_discriminator}_method_call_var__",
- $function_call_arg->getAttributes()
- ),
- new VirtualIdentifier(
- $callable_method_name,
- $function_call_arg->getAttributes()
- ),
- $fake_args,
- $function_call_arg->getAttributes()
- );
-
- $lhs_instance_type = null;
-
- $callable_type = $statements_source->node_data->getType($function_call_arg->value);
-
- if ($callable_type) {
- foreach ($callable_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TKeyedArray
- && count($atomic_type->properties) === 2
- && isset($atomic_type->properties[0])
- ) {
- $lhs_instance_type = clone $atomic_type->properties[0];
- }
- }
- }
-
- $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_offset_var__"] = Type::getMixed();
- $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_method_call_var__"] =
- $lhs_instance_type ?: new Union([new TNamedObject($callable_fq_class_name)]);
- } else {
- $fake_method_call = new VirtualStaticCall(
- new VirtualFullyQualified(
- $callable_fq_class_name,
- $function_call_arg->getAttributes()
- ),
- new VirtualIdentifier(
- $callable_method_name,
- $function_call_arg->getAttributes()
- ),
- $fake_args,
- $function_call_arg->getAttributes()
- );
-
- $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_offset_var__"] = Type::getMixed();
- }
-
- $fake_method_return_type = self::executeFakeCall(
- $statements_source,
- $fake_method_call,
- $context,
- $assertions
- );
-
- $function_id_return_type = $fake_method_return_type ?? Type::getMixed();
- } else {
- $fake_function_call = new VirtualFuncCall(
- new VirtualFullyQualified(
- $mapping_function_id_part,
- $function_call_arg->getAttributes()
- ),
- $fake_args,
- $function_call_arg->getAttributes()
- );
-
- $context->vars_in_scope["\$__fake_{$fake_var_discriminator}_offset_var__"] = Type::getMixed();
-
- $fake_function_return_type = self::executeFakeCall(
- $statements_source,
- $fake_function_call,
- $context,
- $assertions
- );
-
- $function_id_return_type = $fake_function_return_type ?? Type::getMixed();
- }
- }
-
- if ($clean_context) {
- self::cleanContext($context, $fake_var_discriminator);
- }
-
- $fake_var_discriminator = null;
-
- $mapping_return_type = Type::combineUnionTypes(
- $function_id_return_type,
- $mapping_return_type,
- $codebase
- );
- }
-
- return $mapping_return_type;
- }
-
- public static function cleanContext(Context $context, int $fake_var_discriminator): void
- {
- foreach ($context->vars_in_scope as $var_in_scope => $_) {
- if (strpos($var_in_scope, "__fake_{$fake_var_discriminator}_") !== false) {
- unset($context->vars_in_scope[$var_in_scope]);
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php
deleted file mode 100644
index 6102ba4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayMergeReturnTypeProvider.php
+++ /dev/null
@@ -1,287 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function array_values;
-use function count;
-use function is_string;
-use function max;
-use function mb_strcut;
-
-class ArrayMergeReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_merge', 'array_replace'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- $is_replace = mb_strcut($event->getFunctionId(), 6, 7) === 'replace';
-
- $inner_value_types = [];
- $inner_key_types = [];
-
- $codebase = $statements_source->getCodebase();
-
- $generic_properties = [];
- $class_strings = [];
- $all_keyed_arrays = true;
- $all_int_offsets = true;
- $all_nonempty_lists = true;
- $any_nonempty = false;
-
- $max_keyed_array_size = 0;
-
- foreach ($call_args as $call_arg) {
- if (!($call_arg_type = $statements_source->node_data->getType($call_arg->value))) {
- return Type::getArray();
- }
-
- foreach ($call_arg_type->getAtomicTypes() as $type_part) {
- if ($call_arg->unpack) {
- if (!$type_part instanceof TArray) {
- if ($type_part instanceof TKeyedArray) {
- $type_part_value_type = $type_part->getGenericValueType();
- } elseif ($type_part instanceof TList) {
- $type_part_value_type = $type_part->type_param;
- } else {
- return Type::getArray();
- }
- } else {
- $type_part_value_type = $type_part->type_params[1];
- }
-
- $unpacked_type_parts = [];
-
- foreach ($type_part_value_type->getAtomicTypes() as $value_type_part) {
- $unpacked_type_parts[] = $value_type_part;
- }
- } else {
- $unpacked_type_parts = [$type_part];
- }
-
- foreach ($unpacked_type_parts as $unpacked_type_part) {
- if (!$unpacked_type_part instanceof TArray) {
- if (($unpacked_type_part instanceof TFalse
- && $call_arg_type->ignore_falsable_issues)
- || ($unpacked_type_part instanceof TNull
- && $call_arg_type->ignore_nullable_issues)
- ) {
- continue;
- }
-
- if ($unpacked_type_part instanceof TKeyedArray) {
- $max_keyed_array_size = max(
- $max_keyed_array_size,
- count($unpacked_type_part->properties)
- );
-
- foreach ($unpacked_type_part->properties as $key => $type) {
- if (!is_string($key)) {
- if ($is_replace) {
- $generic_properties[$key] = $type;
- } else {
- $generic_properties[] = $type;
- }
- continue;
- }
-
- if (isset($unpacked_type_part->class_strings[$key])) {
- $class_strings[$key] = true;
- }
-
- if (!isset($generic_properties[$key]) || !$type->possibly_undefined) {
- $generic_properties[$key] = $type;
- } else {
- $was_possibly_undefined = $generic_properties[$key]->possibly_undefined;
-
- $generic_properties[$key] = Type::combineUnionTypes(
- $generic_properties[$key],
- $type,
- $codebase
- );
-
- $generic_properties[$key]->possibly_undefined = $was_possibly_undefined;
- }
- }
-
- if (!$unpacked_type_part->is_list) {
- $all_nonempty_lists = false;
- }
-
- if ($unpacked_type_part->sealed) {
- $any_nonempty = true;
- }
-
- continue;
- }
-
- if ($unpacked_type_part instanceof TList) {
- $all_keyed_arrays = false;
-
- if (!$unpacked_type_part instanceof TNonEmptyList) {
- $all_nonempty_lists = false;
- } else {
- $any_nonempty = true;
- }
- } else {
- if ($unpacked_type_part instanceof TMixed
- && $unpacked_type_part->from_loop_isset
- ) {
- $unpacked_type_part = new TArray([
- Type::getArrayKey(),
- Type::getMixed(true),
- ]);
- } else {
- return Type::getArray();
- }
- }
- } else {
- if (!$unpacked_type_part->type_params[0]->isEmpty()) {
- foreach ($generic_properties as $key => $keyed_type) {
- $generic_properties[$key] = Type::combineUnionTypes(
- $keyed_type,
- $unpacked_type_part->type_params[1],
- $codebase
- );
- }
-
- $all_keyed_arrays = false;
- $all_nonempty_lists = false;
- }
- }
-
- if ($unpacked_type_part instanceof TArray) {
- if ($unpacked_type_part->type_params[1]->isEmpty()) {
- continue;
- }
-
- if (!$unpacked_type_part->type_params[0]->isInt()) {
- $all_int_offsets = false;
- }
-
- if ($unpacked_type_part instanceof TNonEmptyArray) {
- $any_nonempty = true;
- }
- }
-
- $inner_key_types = array_merge(
- $inner_key_types,
- $unpacked_type_part instanceof TList
- ? [new TInt()]
- : array_values($unpacked_type_part->type_params[0]->getAtomicTypes())
- );
- $inner_value_types = array_merge(
- $inner_value_types,
- $unpacked_type_part instanceof TList
- ? array_values($unpacked_type_part->type_param->getAtomicTypes())
- : array_values($unpacked_type_part->type_params[1]->getAtomicTypes())
- );
- }
- }
- }
-
- $inner_key_type = null;
- $inner_value_type = null;
-
- if ($inner_key_types) {
- /**
- * Truthy&array-shape-list doesn't reconcile correctly, will be fixed for 5.x by #8050.
- * @psalm-suppress InvalidScalarArgument
- */
- $inner_key_type = TypeCombiner::combine($inner_key_types, $codebase, true);
- }
-
- if ($inner_value_types) {
- $inner_value_type = TypeCombiner::combine($inner_value_types, $codebase, true);
- }
-
- $generic_property_count = count($generic_properties);
-
- if ($generic_properties
- && $generic_property_count < 64
- && ($generic_property_count < $max_keyed_array_size * 2
- || $generic_property_count < 16)
- ) {
- $objectlike = new TKeyedArray($generic_properties);
-
- if ($class_strings !== []) {
- $objectlike->class_strings = $class_strings;
- }
-
- if ($all_nonempty_lists || $all_int_offsets) {
- $objectlike->is_list = true;
- }
-
- if (!$all_keyed_arrays) {
- $objectlike->previous_key_type = $inner_key_type;
- $objectlike->previous_value_type = $inner_value_type;
- }
-
- return new Union([$objectlike]);
- }
-
- if ($inner_value_type) {
- if ($all_int_offsets) {
- if ($any_nonempty) {
- return new Union([
- new TNonEmptyList($inner_value_type),
- ]);
- }
-
- return new Union([
- new TList($inner_value_type),
- ]);
- }
-
- $inner_key_type = $inner_key_type ?? Type::getArrayKey();
-
- if ($any_nonempty) {
- return new Union([
- new TNonEmptyArray([
- $inner_key_type,
- $inner_value_type,
- ]),
- ]);
- }
-
- return new Union([
- new TArray([
- $inner_key_type,
- $inner_value_type,
- ]),
- ]);
- }
-
- return Type::getArray();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php
deleted file mode 100644
index f5859c1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPadReturnTypeProvider.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Type\ArrayType;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-
-use function count;
-
-class ArrayPadReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_pad'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $type_provider = $statements_source->getNodeTypeProvider();
-
- if (count($call_args) >= 3
- && ($array_arg_type = $type_provider->getType($call_args[0]->value))
- && ($size_arg_type = $type_provider->getType($call_args[1]->value))
- && ($value_arg_type = $type_provider->getType($call_args[2]->value))
- && $array_arg_type->isSingle()
- && $array_arg_type->hasArray()
- && ($array_type = ArrayType::infer($array_arg_type->getAtomicTypes()['array']))
- ) {
- $codebase = $statements_source->getCodebase();
- $key_type = Type::combineUnionTypes($array_type->key, Type::getInt(), $codebase);
- $value_type = Type::combineUnionTypes($array_type->value, $value_arg_type, $codebase);
- $can_return_empty = (
- !$size_arg_type->isSingleIntLiteral()
- || $size_arg_type->getSingleIntLiteral()->value === 0
- );
-
- return new Union([
- $array_type->is_list
- ? (
- $can_return_empty
- ? new TList($value_type)
- : new TNonEmptyList($value_type)
- )
- : (
- $can_return_empty
- ? new TArray([$key_type, $value_type])
- : new TNonEmptyArray([$key_type, $value_type])
- )
- ]);
- }
-
- return Type::getArray();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php
deleted file mode 100644
index 66d3952..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPointerAdjustmentReturnTypeProvider.php
+++ /dev/null
@@ -1,105 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\ArrayFetchAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_shift;
-
-class ArrayPointerAdjustmentReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['current', 'next', 'prev', 'reset', 'end'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- if (!$first_arg) {
- return Type::getMixed();
- }
-
- $first_arg_type = $statements_source->node_data->getType($first_arg);
-
- if (!$first_arg_type) {
- return Type::getMixed();
- }
-
- $atomic_types = $first_arg_type->getAtomicTypes();
-
- $value_type = null;
- $definitely_has_items = false;
-
- while ($atomic_type = array_shift($atomic_types)) {
- if ($atomic_type instanceof TTemplateParam) {
- $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes());
- continue;
- }
-
- if ($atomic_type instanceof TArray) {
- $value_type = clone $atomic_type->type_params[1];
- $definitely_has_items = $atomic_type instanceof TNonEmptyArray;
- } elseif ($atomic_type instanceof TList) {
- $value_type = clone $atomic_type->type_param;
- $definitely_has_items = $atomic_type instanceof TNonEmptyList;
- } elseif ($atomic_type instanceof TKeyedArray) {
- $value_type = $atomic_type->getGenericValueType();
- $definitely_has_items = $atomic_type->getGenericArrayType() instanceof TNonEmptyArray;
- } else {
- return Type::getMixed();
- }
- }
-
- if (!$value_type) {
- throw new UnexpectedValueException('This should never happen');
- }
-
- if ($value_type->isEmpty()) {
- $value_type = Type::getFalse();
- } elseif (($function_id !== 'reset' && $function_id !== 'end') || !$definitely_has_items) {
- $value_type->addType(new TFalse);
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_falsable_issues) {
- $value_type->ignore_falsable_issues = true;
- }
- }
-
- ArrayFetchAnalyzer::taintArrayFetch(
- $statements_source,
- $first_arg,
- null,
- $value_type,
- Type::getMixed()
- );
-
- return $value_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php
deleted file mode 100644
index 25dd2bd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayPopReturnTypeProvider.php
+++ /dev/null
@@ -1,96 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-class ArrayPopReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_pop', 'array_shift'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- $first_arg_array = $first_arg
- && ($first_arg_type = $statements_source->node_data->getType($first_arg))
- && $first_arg_type->hasType('array')
- && !$first_arg_type->hasMixed()
- && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
- && ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- || $array_atomic_type instanceof TList)
- ? $array_atomic_type
- : null;
-
- if (!$first_arg_array) {
- return Type::getMixed();
- }
-
- $nullable = false;
-
- if ($first_arg_array instanceof TArray) {
- $value_type = clone $first_arg_array->type_params[1];
-
- if ($value_type->isEmpty()) {
- return Type::getNull();
- }
-
- if (!$first_arg_array instanceof TNonEmptyArray) {
- $nullable = true;
- }
- } elseif ($first_arg_array instanceof TList) {
- $value_type = clone $first_arg_array->type_param;
-
- if (!$first_arg_array instanceof TNonEmptyList) {
- $nullable = true;
- }
- } else {
- // special case where we know the type of the first element
- if ($function_id === 'array_shift' && $first_arg_array->is_list && isset($first_arg_array->properties[0])) {
- $value_type = clone $first_arg_array->properties[0];
- } else {
- $value_type = $first_arg_array->getGenericValueType();
-
- if (!$first_arg_array->sealed && !$first_arg_array->previous_value_type) {
- $nullable = true;
- }
- }
- }
-
- if ($nullable) {
- $value_type->addType(new TNull);
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_nullable_issues) {
- $value_type->ignore_nullable_issues = true;
- }
- }
-
- return $value_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php
deleted file mode 100644
index 8c7062b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayRandReturnTypeProvider.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PhpParser;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Union;
-
-class ArrayRandReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_rand'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
- $second_arg = $call_args[1]->value ?? null;
-
- $first_arg_array = $first_arg
- && ($first_arg_type = $statements_source->node_data->getType($first_arg))
- && $first_arg_type->hasType('array')
- && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
- && ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- || $array_atomic_type instanceof TList)
- ? $array_atomic_type
- : null;
-
- if (!$first_arg_array) {
- return Type::getMixed();
- }
-
- if ($first_arg_array instanceof TArray) {
- $key_type = clone $first_arg_array->type_params[0];
- } elseif ($first_arg_array instanceof TList) {
- $key_type = Type::getInt();
- } else {
- $key_type = $first_arg_array->getGenericKeyType();
- }
-
- if (!$second_arg
- || ($second_arg instanceof PhpParser\Node\Scalar\LNumber && $second_arg->value === 1)
- ) {
- return $key_type;
- }
-
- $arr_type = new Union([
- new TList(
- $key_type
- ),
- ]);
-
- if ($second_arg instanceof PhpParser\Node\Scalar\LNumber) {
- return $arr_type;
- }
-
- return Type::combineUnionTypes($key_type, $arr_type);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php
deleted file mode 100644
index a9d27ba..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReduceReturnTypeProvider.php
+++ /dev/null
@@ -1,298 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PhpParser;
-use Psalm\CodeLocation;
-use Psalm\Internal\Analyzer\Statements\Expression\CallAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\InvalidArgument;
-use Psalm\IssueBuffer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Union;
-
-use function count;
-use function explode;
-use function in_array;
-use function reset;
-use function strpos;
-use function strtolower;
-use function substr;
-
-class ArrayReduceReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_reduce'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $context = $event->getContext();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (!isset($call_args[0]) || !isset($call_args[1])) {
- return Type::getMixed();
- }
-
- $codebase = $statements_source->getCodebase();
-
- $array_arg = $call_args[0]->value;
- $function_call_arg = $call_args[1]->value;
-
- $array_arg_type = $statements_source->node_data->getType($array_arg);
- $function_call_arg_type = $statements_source->node_data->getType($function_call_arg);
-
- if (!$array_arg_type || !$function_call_arg_type) {
- return Type::getMixed();
- }
-
- $array_arg_types = $array_arg_type->getAtomicTypes();
-
- $array_arg_atomic_type = null;
-
- if (isset($array_arg_types['array'])
- && ($array_arg_types['array'] instanceof TArray
- || $array_arg_types['array'] instanceof TKeyedArray
- || $array_arg_types['array'] instanceof TList)
- ) {
- $array_arg_atomic_type = $array_arg_types['array'];
-
- if ($array_arg_atomic_type instanceof TKeyedArray) {
- $array_arg_atomic_type = $array_arg_atomic_type->getGenericArrayType();
- } elseif ($array_arg_atomic_type instanceof TList) {
- $array_arg_atomic_type = new TArray([
- Type::getInt(),
- clone $array_arg_atomic_type->type_param
- ]);
- }
- }
-
- if (!isset($call_args[2])) {
- $reduce_return_type = Type::getNull();
- $reduce_return_type->ignore_nullable_issues = true;
- } else {
- $reduce_return_type = $statements_source->node_data->getType($call_args[2]->value);
-
- if (!$reduce_return_type) {
- return Type::getMixed();
- }
-
- if ($reduce_return_type->hasMixed()) {
- return Type::getMixed();
- }
- }
-
- $initial_type = $reduce_return_type;
-
- if ($closure_types = $function_call_arg_type->getClosureTypes()) {
- $closure_atomic_type = reset($closure_types);
-
- $closure_return_type = $closure_atomic_type->return_type ?: Type::getMixed();
-
- if ($closure_return_type->isVoid()) {
- $closure_return_type = Type::getNull();
- }
-
- $reduce_return_type = Type::combineUnionTypes($closure_return_type, $reduce_return_type);
-
- if ($closure_atomic_type->params !== null) {
- if (count($closure_atomic_type->params) < 1) {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'The closure passed to array_reduce at least one parameter',
- new CodeLocation($statements_source, $function_call_arg)
- ),
- $statements_source->getSuppressedIssues()
- );
-
- return Type::getMixed();
- }
-
- $carry_param = $closure_atomic_type->params[0];
- $item_param = $closure_atomic_type->params[1] ?? null;
-
- if ($carry_param->type
- && (
- !UnionTypeComparator::isContainedBy(
- $codebase,
- $initial_type,
- $carry_param->type
- )
- || (
- !$reduce_return_type->hasMixed()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $reduce_return_type,
- $carry_param->type
- )
- )
- )
- ) {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'The first param of the closure passed to array_reduce must take '
- . $reduce_return_type . ' but only accepts ' . $carry_param->type,
- $carry_param->type_location
- ?: new CodeLocation($statements_source, $function_call_arg)
- ),
- $statements_source->getSuppressedIssues()
- );
-
- return Type::getMixed();
- }
-
- if ($item_param
- && $item_param->type
- && $array_arg_atomic_type
- && !$array_arg_atomic_type->type_params[1]->hasMixed()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $array_arg_atomic_type->type_params[1],
- $item_param->type
- )
- ) {
- IssueBuffer::maybeAdd(
- new InvalidArgument(
- 'The second param of the closure passed to array_reduce must take '
- . $array_arg_atomic_type->type_params[1] . ' but only accepts ' . $item_param->type,
- $item_param->type_location
- ?: new CodeLocation($statements_source, $function_call_arg)
- ),
- $statements_source->getSuppressedIssues()
- );
-
- return Type::getMixed();
- }
- }
-
- return $reduce_return_type;
- }
-
- if ($function_call_arg instanceof PhpParser\Node\Scalar\String_
- || $function_call_arg instanceof PhpParser\Node\Expr\Array_
- || $function_call_arg instanceof PhpParser\Node\Expr\BinaryOp\Concat
- ) {
- $mapping_function_ids = CallAnalyzer::getFunctionIdsFromCallableArg(
- $statements_source,
- $function_call_arg
- );
-
- $call_map = InternalCallMapHandler::getCallMap();
-
- foreach ($mapping_function_ids as $mapping_function_id) {
- $mapping_function_id_parts = explode('&', $mapping_function_id);
-
- $part_match_found = false;
-
- foreach ($mapping_function_id_parts as $mapping_function_id_part) {
- if (isset($call_map[$mapping_function_id_part][0])) {
- if ($call_map[$mapping_function_id_part][0]) {
- $mapped_function_return =
- Type::parseString($call_map[$mapping_function_id_part][0]);
-
- $reduce_return_type = Type::combineUnionTypes(
- $reduce_return_type,
- $mapped_function_return
- );
-
- $part_match_found = true;
- }
- } elseif ($mapping_function_id_part) {
- if (strpos($mapping_function_id_part, '::') !== false) {
- if ($mapping_function_id_part[0] === '$') {
- $mapping_function_id_part = substr($mapping_function_id_part, 1);
- }
-
- [$callable_fq_class_name, $method_name] = explode('::', $mapping_function_id_part);
-
- if (in_array($callable_fq_class_name, ['self', 'static'], true)) {
- $callable_fq_class_name = $statements_source->getFQCLN();
- if ($callable_fq_class_name === null) {
- continue;
- }
- }
-
- if ($callable_fq_class_name === 'parent') {
- continue;
- }
-
- $method_id = new MethodIdentifier(
- $callable_fq_class_name,
- strtolower($method_name)
- );
-
- if (!$codebase->methods->methodExists(
- $method_id,
- !$context->collect_initializations
- && !$context->collect_mutations
- ? $context->calling_method_id
- : null,
- $codebase->collect_locations
- ? new CodeLocation(
- $statements_source,
- $function_call_arg
- ) : null,
- null,
- $statements_source->getFilePath()
- )) {
- continue;
- }
-
- $part_match_found = true;
-
- $self_class = 'self';
-
- $return_type = $codebase->methods->getMethodReturnType(
- $method_id,
- $self_class
- ) ?? Type::getMixed();
- } else {
- if (!$codebase->functions->functionExists(
- $statements_source,
- strtolower($mapping_function_id_part)
- )
- ) {
- return Type::getMixed();
- }
-
- $part_match_found = true;
-
- $function_storage = $codebase->functions->getStorage(
- $statements_source,
- strtolower($mapping_function_id_part)
- );
-
- $return_type = $function_storage->return_type ?: Type::getMixed();
- }
-
- $reduce_return_type = Type::combineUnionTypes($reduce_return_type, $return_type);
- }
- }
-
- if ($part_match_found === false) {
- return Type::getMixed();
- }
- }
-
- return $reduce_return_type;
- }
-
- return Type::getMixed();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php
deleted file mode 100644
index 0fab066..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayReverseReturnTypeProvider.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Union;
-
-class ArrayReverseReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_reverse'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- $first_arg_array = $first_arg
- && ($first_arg_type = $statements_source->node_data->getType($first_arg))
- && $first_arg_type->hasType('array')
- && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
- && ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- || $array_atomic_type instanceof TList)
- ? $array_atomic_type
- : null;
-
- if (!$first_arg_array) {
- return Type::getArray();
- }
-
- if ($first_arg_array instanceof TArray) {
- return new Union([clone $first_arg_array]);
- }
-
- if ($first_arg_array instanceof TList) {
- $second_arg = $call_args[1]->value ?? null;
-
- if (!$second_arg
- || (($second_arg_type = $statements_source->node_data->getType($second_arg))
- && $second_arg_type->isFalse()
- )
- ) {
- return new Union([clone $first_arg_array]);
- }
-
- return new Union([new TArray([Type::getInt(), clone $first_arg_array->type_param])]);
- }
-
- return new Union([$first_arg_array->getGenericArrayType()]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php
deleted file mode 100644
index 399bb25..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySliceReturnTypeProvider.php
+++ /dev/null
@@ -1,98 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_shift;
-
-class ArraySliceReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_slice'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
-
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- if (!$first_arg) {
- return Type::getArray();
- }
-
- $first_arg_type = $statements_source->node_data->getType($first_arg);
-
- if (!$first_arg_type) {
- return Type::getArray();
- }
-
- $atomic_types = $first_arg_type->getAtomicTypes();
-
- $return_atomic_type = null;
-
- while ($atomic_type = array_shift($atomic_types)) {
- if ($atomic_type instanceof TTemplateParam) {
- $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes());
- continue;
- }
-
- $already_cloned = false;
-
- if ($atomic_type instanceof TKeyedArray) {
- $already_cloned = true;
- $atomic_type = $atomic_type->getGenericArrayType();
- }
-
- if ($atomic_type instanceof TArray) {
- if (!$already_cloned) {
- $atomic_type = clone $atomic_type;
- }
-
- $return_atomic_type = new TArray($atomic_type->type_params);
- continue;
- }
-
- if ($atomic_type instanceof TList) {
- $return_atomic_type = new TArray([Type::getInt(), clone $atomic_type->type_param]);
- continue;
- }
-
- return Type::getArray();
- }
-
- if (!$return_atomic_type) {
- throw new UnexpectedValueException('This should never happen');
- }
-
- $dont_preserve_int_keys = !isset($call_args[3]->value)
- || (($third_arg_type = $statements_source->node_data->getType($call_args[3]->value))
- && ((string) $third_arg_type === 'false'));
-
- if ($dont_preserve_int_keys && $return_atomic_type->type_params[0]->isInt()) {
- $return_atomic_type = new TList($return_atomic_type->type_params[1]);
- }
-
- return new Union([$return_atomic_type]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php
deleted file mode 100644
index 6f4d062..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArraySpliceReturnTypeProvider.php
+++ /dev/null
@@ -1,76 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Union;
-
-class ArraySpliceReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_splice'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- $first_arg_array = $first_arg
- && ($first_arg_type = $statements_source->node_data->getType($first_arg))
- && $first_arg_type->hasType('array')
- && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
- && ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- || $array_atomic_type instanceof TList)
- ? $array_atomic_type
- : null;
-
- if (!$first_arg_array) {
- return Type::getArray();
- }
-
- $already_cloned = false;
-
- if ($first_arg_array instanceof TKeyedArray) {
- $already_cloned = true;
- $first_arg_array = $first_arg_array->getGenericArrayType();
- }
-
- if ($first_arg_array instanceof TArray) {
- if (!$already_cloned) {
- $first_arg_array = clone $first_arg_array;
- }
- $array_type = new TArray($first_arg_array->type_params);
- } else {
- $array_type = new TArray([Type::getInt(), clone $first_arg_array->type_param]);
- }
-
- if (!$array_type->type_params[0]->hasString()) {
- if ($array_type->type_params[1]->isString()) {
- $array_type = new TList(Type::getString());
- } elseif ($array_type->type_params[1]->isInt()) {
- $array_type = new TList(Type::getInt());
- } else {
- $array_type = new TList(Type::getMixed());
- }
- }
-
- return new Union([$array_type]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php
deleted file mode 100644
index 5499ad2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-
-class ArrayUniqueReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_unique'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- $first_arg_array = $first_arg
- && ($first_arg_type = $statements_source->node_data->getType($first_arg))
- && $first_arg_type->hasType('array')
- && ($array_atomic_type = $first_arg_type->getAtomicTypes()['array'])
- && ($array_atomic_type instanceof TArray
- || $array_atomic_type instanceof TKeyedArray
- || $array_atomic_type instanceof TList)
- ? $array_atomic_type
- : null;
-
- if (!$first_arg_array) {
- return Type::getArray();
- }
-
- if ($first_arg_array instanceof TArray) {
- $first_arg_array = clone $first_arg_array;
-
- if ($first_arg_array instanceof TNonEmptyArray) {
- $first_arg_array->count = null;
- }
-
- return new Union([$first_arg_array]);
- }
-
- if ($first_arg_array instanceof TList) {
- if ($first_arg_array instanceof TNonEmptyList) {
- return new Union([
- new TNonEmptyArray([
- Type::getInt(),
- clone $first_arg_array->type_param
- ])
- ]);
- }
-
- return new Union([
- new TArray([
- Type::getInt(),
- clone $first_arg_array->type_param
- ])
- ]);
- }
-
- return new Union([$first_arg_array->getGenericArrayType()]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php
deleted file mode 100644
index 0544510..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayValuesReturnTypeProvider.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_shift;
-
-class ArrayValuesReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['array_values'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- $first_arg = $call_args[0]->value ?? null;
-
- if (!$first_arg) {
- return Type::getArray();
- }
-
- $first_arg_type = $statements_source->node_data->getType($first_arg);
-
- if (!$first_arg_type) {
- return Type::getArray();
- }
-
- $atomic_types = $first_arg_type->getAtomicTypes();
-
- $return_atomic_type = null;
-
- while ($atomic_type = array_shift($atomic_types)) {
- if ($atomic_type instanceof TTemplateParam) {
- $atomic_types = array_merge($atomic_types, $atomic_type->as->getAtomicTypes());
- continue;
- }
-
- if ($atomic_type instanceof TKeyedArray) {
- $atomic_type = $atomic_type->getGenericArrayType();
- }
-
- if ($atomic_type instanceof TArray) {
- if ($atomic_type instanceof TNonEmptyArray) {
- $return_atomic_type = new TNonEmptyList(
- clone $atomic_type->type_params[1]
- );
- } else {
- $return_atomic_type = new TList(
- clone $atomic_type->type_params[1]
- );
- }
- } elseif ($atomic_type instanceof TList) {
- $return_atomic_type = $atomic_type;
- } else {
- return Type::getArray();
- }
- }
-
- if (!$return_atomic_type) {
- throw new UnexpectedValueException('This should never happen');
- }
-
- return new Union([$return_atomic_type]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php
deleted file mode 100644
index a94e581..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ClosureFromCallableReturnTypeProvider.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\Comparator\CallableTypeComparator;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Union;
-
-class ClosureFromCallableReturnTypeProvider implements MethodReturnTypeProviderInterface
-{
- public static function getClassLikeNames(): array
- {
- return ['Closure'];
- }
-
- public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
- {
- $source = $event->getSource();
- $method_name_lowercase = $event->getMethodNameLowercase();
- $call_args = $event->getCallArgs();
- if (!$source instanceof StatementsAnalyzer) {
- return null;
- }
-
- $type_provider = $source->getNodeTypeProvider();
- $codebase = $source->getCodebase();
-
- if ($method_name_lowercase === 'fromcallable') {
- $closure_types = [];
-
- if (isset($call_args[0])
- && ($input_type = $type_provider->getType($call_args[0]->value))
- ) {
- foreach ($input_type->getAtomicTypes() as $atomic_type) {
- $candidate_callable = CallableTypeComparator::getCallableFromAtomic(
- $codebase,
- $atomic_type,
- null,
- $source,
- true
- );
-
- if ($candidate_callable) {
- $closure_types[] = new TClosure(
- 'Closure',
- $candidate_callable->params,
- $candidate_callable->return_type,
- $candidate_callable->is_pure
- );
- } else {
- return Type::getClosure();
- }
- }
- }
-
- if ($closure_types) {
- return TypeCombiner::combine($closure_types, $codebase);
- }
-
- return Type::getClosure();
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php
deleted file mode 100644
index 6d7723b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DomNodeAppendChild.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Union;
-
-class DomNodeAppendChild implements MethodReturnTypeProviderInterface
-{
- public static function getClassLikeNames(): array
- {
- return ['DomNode'];
- }
-
- public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
- {
- $source = $event->getSource();
- $call_args = $event->getCallArgs();
- $method_name_lowercase = $event->getMethodNameLowercase();
-
- if ($method_name_lowercase !== 'appendchild') {
- return null;
- }
-
- if (!$source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- if (($first_arg_type = $source->node_data->getType($call_args[0]->value))
- && $first_arg_type->hasObjectType()
- ) {
- return clone $first_arg_type;
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php
deleted file mode 100644
index 81001ff..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ExplodeReturnTypeProvider.php
+++ /dev/null
@@ -1,102 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PhpParser;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Union;
-
-use function count;
-
-class ExplodeReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['explode'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (count($call_args) >= 2) {
- $second_arg_type = $statements_source->node_data->getType($call_args[1]->value);
-
- $inner_type = new Union([
- $second_arg_type && $second_arg_type->hasLowercaseString()
- ? new TLowercaseString()
- : new TString
- ]);
-
- $can_return_empty = isset($call_args[2])
- && (
- !$call_args[2]->value instanceof PhpParser\Node\Scalar\LNumber
- || $call_args[2]->value->value < 0
- );
-
- if ($call_args[0]->value instanceof PhpParser\Node\Scalar\String_) {
- if ($call_args[0]->value->value === '') {
- return Type::getFalse();
- }
-
- return new Union([
- $can_return_empty
- ? new TList($inner_type)
- : new TNonEmptyList($inner_type)
- ]);
- }
-
- if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
- && $first_arg_type->hasString()) {
- $can_be_false = true;
- if ($first_arg_type->isString()) {
- $can_be_false = false;
- foreach ($first_arg_type->getAtomicTypes() as $string_type) {
- if (!($string_type instanceof TNonEmptyString)) {
- $can_be_false = true;
- break;
- }
- }
- }
- if ($can_be_false) {
- $array_type = new Union([
- $can_return_empty
- ? new TList($inner_type)
- : new TNonEmptyList($inner_type),
- new TFalse
- ]);
-
- if ($statements_source->getCodebase()->config->ignore_internal_falsable_issues) {
- $array_type->ignore_falsable_issues = true;
- }
- } else {
- $array_type = new Union([
- $can_return_empty
- ? new TList($inner_type)
- : new TNonEmptyList($inner_type),
- ]);
- }
-
- return $array_type;
- }
- }
-
- return Type::getMixed();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php
deleted file mode 100644
index 3ebc5e1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FilterVarReturnTypeProvider.php
+++ /dev/null
@@ -1,170 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function in_array;
-
-use const FILTER_NULL_ON_FAILURE;
-use const FILTER_VALIDATE_BOOLEAN;
-use const FILTER_VALIDATE_DOMAIN;
-use const FILTER_VALIDATE_EMAIL;
-use const FILTER_VALIDATE_FLOAT;
-use const FILTER_VALIDATE_INT;
-use const FILTER_VALIDATE_IP;
-use const FILTER_VALIDATE_MAC;
-use const FILTER_VALIDATE_REGEXP;
-use const FILTER_VALIDATE_URL;
-
-class FilterVarReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['filter_var'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- $code_location = $event->getCodeLocation();
- if (!$statements_source instanceof StatementsAnalyzer) {
- throw new UnexpectedValueException();
- }
-
- $filter_type = null;
-
- if (isset($call_args[1])
- && ($second_arg_type = $statements_source->node_data->getType($call_args[1]->value))
- && $second_arg_type->isSingleIntLiteral()
- ) {
- $filter_type_type = $second_arg_type->getSingleIntLiteral();
-
- switch ($filter_type_type->value) {
- case FILTER_VALIDATE_INT:
- $filter_type = Type::getInt();
- break;
-
- case FILTER_VALIDATE_FLOAT:
- $filter_type = Type::getFloat();
- break;
-
- case FILTER_VALIDATE_BOOLEAN:
- $filter_type = Type::getBool();
-
- break;
-
- case FILTER_VALIDATE_IP:
- case FILTER_VALIDATE_MAC:
- case FILTER_VALIDATE_REGEXP:
- case FILTER_VALIDATE_URL:
- case FILTER_VALIDATE_EMAIL:
- case FILTER_VALIDATE_DOMAIN:
- $filter_type = Type::getString();
- break;
- }
-
- $has_object_like = false;
- $filter_null = false;
-
- if (isset($call_args[2])
- && ($third_arg_type = $statements_source->node_data->getType($call_args[2]->value))
- && $filter_type
- ) {
- foreach ($third_arg_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TKeyedArray) {
- $has_object_like = true;
-
- if (isset($atomic_type->properties['options'])
- && $atomic_type->properties['options']->hasArray()
- && ($options_array = $atomic_type->properties['options']->getAtomicTypes()['array'] ?? null)
- && $options_array instanceof TKeyedArray
- && isset($options_array->properties['default'])
- ) {
- $filter_type = Type::combineUnionTypes(
- $filter_type,
- $options_array->properties['default']
- );
- } else {
- $filter_type->addType(new TFalse);
- }
-
- if (isset($atomic_type->properties['flags'])
- && $atomic_type->properties['flags']->isSingleIntLiteral()
- ) {
- $filter_flag_type =
- $atomic_type->properties['flags']->getSingleIntLiteral();
-
- if ($filter_type->hasBool()
- && $filter_flag_type->value === FILTER_NULL_ON_FAILURE
- ) {
- $filter_type->addType(new TNull);
- }
- }
- } elseif ($atomic_type instanceof TLiteralInt) {
- if ($atomic_type->value === FILTER_NULL_ON_FAILURE) {
- $filter_null = true;
- $filter_type->addType(new TNull);
- }
- }
- }
- }
-
- if (!$has_object_like && !$filter_null && $filter_type) {
- $filter_type->addType(new TFalse);
- }
- }
-
- if (!$filter_type) {
- $filter_type = Type::getMixed();
- }
-
- if ($statements_source->data_flow_graph
- && !in_array('TaintedInput', $statements_source->getSuppressedIssues())
- ) {
- $function_return_sink = DataFlowNode::getForMethodReturn(
- $function_id,
- $function_id,
- null,
- $code_location
- );
-
- $statements_source->data_flow_graph->addNode($function_return_sink);
-
- $function_param_sink = DataFlowNode::getForMethodArgument(
- $function_id,
- $function_id,
- 0,
- null,
- $code_location
- );
-
- $statements_source->data_flow_graph->addNode($function_param_sink);
-
- $statements_source->data_flow_graph->addPath(
- $function_param_sink,
- $function_return_sink,
- 'arg'
- );
-
- $filter_type->parent_nodes = [$function_return_sink->id => $function_return_sink];
- }
-
- return $filter_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php
deleted file mode 100644
index 4f99fdd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/FirstArgStringReturnTypeProvider.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-class FirstArgStringReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'crypt',
- ];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- $return_type = Type::getString();
-
- if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
- && $first_arg_type->isString()
- ) {
- return $return_type;
- }
-
- $return_type->addType(new TNull);
- $return_type->ignore_nullable_issues = true;
-
- return $return_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php
deleted file mode 100644
index 4c7d786..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetClassMethodsReturnTypeProvider.php
+++ /dev/null
@@ -1,41 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Union;
-
-class GetClassMethodsReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'get_class_methods',
- ];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
- && ($first_arg_type->hasObjectType() || $first_arg_type->hasString())
- ) {
- return Type::parseString('array<string>');
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php
deleted file mode 100644
index cc8a43d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/GetObjectVarsReturnTypeProvider.php
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\CodeLocation;
-use Psalm\Context;
-use Psalm\Internal\Analyzer\ClassAnalyzer;
-use Psalm\Internal\Analyzer\SourceAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Union;
-use stdClass;
-
-use function reset;
-use function strtolower;
-
-class GetObjectVarsReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'get_object_vars',
- ];
- }
-
- public static function getGetObjectVarsReturnType(
- Union $first_arg_type,
- SourceAnalyzer $statements_source,
- Context $context,
- CodeLocation $location
- ): Union {
- if ($first_arg_type->isSingle()) {
- $atomics = $first_arg_type->getAtomicTypes();
- $object_type = reset($atomics);
-
- if ($object_type instanceof TObjectWithProperties) {
- if ([] === $object_type->properties) {
- return Type::parseString('array<string, mixed>');
- }
- return new Union([
- new TKeyedArray($object_type->properties)
- ]);
- }
-
- if ($object_type instanceof TNamedObject) {
- if (strtolower($object_type->value) === strtolower(stdClass::class)) {
- return Type::parseString('array<string, mixed>');
- }
- $codebase = $statements_source->getCodebase();
- $class_storage = $codebase->classlikes->getStorageFor($object_type->value);
-
- if (null === $class_storage) {
- return Type::parseString('array<string, mixed>');
- }
-
- if ([] === $class_storage->appearing_property_ids) {
- if ($class_storage->final) {
- return Type::getEmptyArray();
- }
-
- return Type::parseString('array<string, mixed>');
- }
-
- $properties = [];
- foreach ($class_storage->appearing_property_ids as $name => $property_id) {
- if (ClassAnalyzer::checkPropertyVisibility(
- $property_id,
- $context,
- $statements_source,
- $location,
- $statements_source->getSuppressedIssues(),
- false
- ) === true) {
- $property_type = $codebase->properties->getPropertyType(
- $property_id,
- false,
- $statements_source,
- $context
- );
- $properties[$name] = $property_type ?? Type::getMixed();
- }
- }
-
- if ([] === $properties) {
- if ($class_storage->final) {
- return Type::getEmptyArray();
- }
-
- return Type::parseString('array<string, mixed>');
- }
-
- return new Union([
- new TKeyedArray($properties)
- ]);
- }
- }
- return Type::parseString('array<string, mixed>');
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))
- && $first_arg_type->isObjectType()
- ) {
- return self::getGetObjectVarsReturnType(
- $first_arg_type,
- $statements_source,
- $event->getContext(),
- $event->getCodeLocation()
- );
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php
deleted file mode 100644
index b6c6078..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/HexdecReturnTypeProvider.php
+++ /dev/null
@@ -1,30 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Union;
-
-class HexdecReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['hexdec'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- return Type::getInt(true);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php
deleted file mode 100644
index c97048a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ImagickPixelColorReturnTypeProvider.php
+++ /dev/null
@@ -1,88 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Union;
-
-use function assert;
-use function in_array;
-
-class ImagickPixelColorReturnTypeProvider implements MethodReturnTypeProviderInterface
-{
- public static function getClassLikeNames(): array
- {
- return ['imagickpixel'];
- }
-
- public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
- {
- $source = $event->getSource();
- $call_args = $event->getCallArgs();
- $method_name_lowercase = $event->getMethodNameLowercase();
-
- if ($method_name_lowercase !== 'getcolor') {
- return null;
- }
-
- if (!$source instanceof StatementsAnalyzer) {
- return null;
- }
-
- if (!$call_args) {
- $formats = [0 => true];
- } else {
- $normalized = $source->node_data->getType($call_args[0]->value) ?? Type::getMixed();
- $formats = [];
- foreach ($normalized->getAtomicTypes() as $t) {
- if ($t instanceof TLiteralInt && in_array($t->value, [0, 1, 2], true)) {
- $formats[$t->value] = true;
- } else {
- $formats[0] = true;
- $formats[1] = true;
- $formats[2] = true;
- }
- }
- }
- $types = [];
- if (isset($formats[0])) {
- $types []= new Union([
- new TKeyedArray([
- 'r' => new Union([new TIntRange(0, 255)]),
- 'g' => new Union([new TIntRange(0, 255)]),
- 'b' => new Union([new TIntRange(0, 255)]),
- 'a' => new Union([new TIntRange(0, 1)])
- ])
- ]);
- }
- if (isset($formats[1])) {
- $types []= new Union([
- new TKeyedArray([
- 'r' => Type::getFloat(),
- 'g' => Type::getFloat(),
- 'b' => Type::getFloat(),
- 'a' => Type::getFloat()
- ])
- ]);
- }
- if (isset($formats[2])) {
- $types []= new Union([
- new TKeyedArray([
- 'r' => new Union([new TIntRange(0, 255)]),
- 'g' => new Union([new TIntRange(0, 255)]),
- 'b' => new Union([new TIntRange(0, 255)]),
- 'a' => new Union([new TIntRange(0, 255)])
- ])
- ]);
- }
-
- assert($types !== []);
- return Type::combineUnionTypeArray($types, $event->getSource()->getCodebase());
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php
deleted file mode 100644
index 519984f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/InArrayReturnTypeProvider.php
+++ /dev/null
@@ -1,84 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Union;
-
-class InArrayReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['in_array'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $call_args = $event->getCallArgs();
- $bool = Type::getBool();
-
- if (!isset($call_args[0]) || !isset($call_args[1])) {
- return $bool;
- }
-
- $needle_type = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[0]->value);
- $haystack_type = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[1]->value);
-
- if ($needle_type === null || $haystack_type === null) {
- return $bool;
- }
-
- $false = Type::getFalse();
- $false->from_docblock = $bool->from_docblock = $needle_type->from_docblock || $haystack_type->from_docblock;
-
- if (!isset($call_args[2])) {
- return $bool;
- }
-
- $strict_type = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[2]->value);
-
- if ($strict_type === null || !$strict_type->isTrue()) {
- return $bool;
- }
-
- /**
- * @var TKeyedArray|TArray|TList|null
- */
- $array_arg_type = ($types = $haystack_type->getAtomicTypes()) && isset($types['array'])
- ? $types['array']
- : null;
-
- if ($array_arg_type instanceof TKeyedArray) {
- $array_arg_type = $array_arg_type->getGenericArrayType();
- }
-
- if ($array_arg_type instanceof TList) {
- $array_arg_type = new TArray([Type::getInt(), $array_arg_type->type_param]);
- }
-
- if (!$array_arg_type instanceof TArray) {
- return $bool;
- }
-
- $haystack_item_type = $array_arg_type->type_params[1];
-
- if (UnionTypeComparator::canExpressionTypesBeIdentical(
- $event->getStatementsSource()->getCodebase(),
- $needle_type,
- $haystack_item_type
- )) {
- return $bool;
- }
-
- return $false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php
deleted file mode 100644
index 52d2647..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/IteratorToArrayReturnTypeProvider.php
+++ /dev/null
@@ -1,129 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\Statements\Block\ForeachAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function array_shift;
-use function assert;
-
-class IteratorToArrayReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'iterator_to_array',
- ];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- $context = $event->getContext();
- if (!$statements_source instanceof StatementsAnalyzer
- || !$call_args
- ) {
- return Type::getMixed();
- }
-
- if (($first_arg_type = $statements_source->node_data->getType($call_args[0]->value))) {
- $key_type = null;
- $value_type = null;
-
- $codebase = $statements_source->getCodebase();
-
- $atomic_types = $first_arg_type->getAtomicTypes();
-
- while ($call_arg_atomic_type = array_shift($atomic_types)) {
- if ($call_arg_atomic_type instanceof TTemplateParam) {
- $atomic_types = array_merge($atomic_types, $call_arg_atomic_type->as->getAtomicTypes());
- continue;
- }
-
- if ($call_arg_atomic_type instanceof TNamedObject
- && AtomicTypeComparator::isContainedBy(
- $codebase,
- $call_arg_atomic_type,
- new TIterable([Type::getMixed(), Type::getMixed()])
- )
- ) {
- $has_valid_iterator = true;
- ForeachAnalyzer::handleIterable(
- $statements_source,
- $call_arg_atomic_type,
- $call_args[0]->value,
- $codebase,
- $context,
- $key_type,
- $value_type,
- $has_valid_iterator
- );
- }
- }
-
- if ($value_type) {
- $second_arg_type = isset($call_args[1])
- ? $statements_source->node_data->getType($call_args[1]->value)
- : null;
-
- if ($second_arg_type
- && ((string) $second_arg_type === 'false')
- ) {
- return new Union([
- new TList($value_type),
- ]);
- }
-
- $key_type = $key_type
- && (!isset($call_args[1])
- || ($second_arg_type && ((string) $second_arg_type === 'true')))
- ? $key_type
- : Type::getArrayKey();
-
- if ($key_type->hasMixed()) {
- $key_type = Type::getArrayKey();
- }
-
- if ($key_type->isSingle() && $key_type->hasTemplate()) {
- $template_types = $key_type->getTemplateTypes();
- $template_type = array_shift($template_types);
- if ($template_type->as->hasMixed()) {
- $template_type->as = Type::getArrayKey();
- $key_type = new Union([$template_type]);
- }
- }
-
- return new Union([
- new TArray([
- $key_type,
- $value_type,
- ]),
- ]);
- }
- }
-
- $callmap_callables = InternalCallMapHandler::getCallablesFromCallMap($function_id);
-
- assert($callmap_callables && $callmap_callables[0]->return_type);
-
- return $callmap_callables[0]->return_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php
deleted file mode 100644
index 1a24e22..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MinMaxReturnTypeProvider.php
+++ /dev/null
@@ -1,143 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Type\ArrayType;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_filter;
-use function assert;
-use function count;
-use function get_class;
-use function in_array;
-use function max;
-use function min;
-
-class MinMaxReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['min', 'max'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $call_args = $event->getCallArgs();
- if (count($call_args) === 0) {
- return null;
- }
-
- $statements_source = $event->getStatementsSource();
- $nodeTypeProvider = $statements_source->getNodeTypeProvider();
-
- if (count($call_args) === 1
- && ($array_arg_type = $nodeTypeProvider->getType($call_args[0]->value))
- && $array_arg_type->isSingle()
- && $array_arg_type->hasArray()
- && ($array_type = ArrayType::infer($array_arg_type->getAtomicTypes()['array']))
- ) {
- return $array_type->value;
- }
-
- $all_int = true;
- $min_bounds = [];
- $max_bounds = [];
- foreach ($call_args as $arg) {
- if ($array_arg_type = $nodeTypeProvider->getType($arg->value)) {
- foreach ($array_arg_type->getAtomicTypes() as $atomic_type) {
- if (!$atomic_type instanceof TInt) {
- $all_int = false;
- break;
- }
-
- if ($atomic_type instanceof TLiteralInt) {
- $min_bounds[] = $atomic_type->value;
- $max_bounds[] = $atomic_type->value;
- } elseif ($atomic_type instanceof TIntRange) {
- $min_bounds[] = $atomic_type->min_bound;
- $max_bounds[] = $atomic_type->max_bound;
- } elseif ($atomic_type instanceof TPositiveInt) {
- $min_bounds[] = 1;
- $max_bounds[] = null;
- } elseif (get_class($atomic_type) === TInt::class) {
- $min_bounds[] = null;
- $max_bounds[] = null;
- } else {
- throw new UnexpectedValueException('Unexpected type');
- }
- }
- } else {
- return Type::getMixed();
- }
- }
-
- if ($all_int) {
- if ($event->getFunctionId() === 'min') {
- assert(count($min_bounds) !== 0);
- //null values in $max_bounds doesn't make sense for min() so we remove them
- $max_bounds = array_filter($max_bounds, function ($v) {
- return $v !== null;
- }) ?: [null];
-
- $min_potential_int = in_array(null, $min_bounds, true) ? null : min($min_bounds);
- $max_potential_int = in_array(null, $max_bounds, true) ? null : min($max_bounds);
- } else {
- assert(count($max_bounds) !== 0);
- //null values in $min_bounds doesn't make sense for max() so we remove them
- $min_bounds = array_filter($min_bounds, function ($v) {
- return $v !== null;
- }) ?: [null];
-
- $min_potential_int = in_array(null, $min_bounds, true) ? null : max($min_bounds);
- $max_potential_int = in_array(null, $max_bounds, true) ? null : max($max_bounds);
- }
-
- if ($min_potential_int === null && $max_potential_int === null) {
- return Type::getInt();
- }
-
- if ($min_potential_int === $max_potential_int) {
- return Type::getInt(false, $min_potential_int);
- }
-
- return new Union([new TIntRange($min_potential_int, $max_potential_int)]);
- }
-
- //if we're dealing with non-int elements, just combine them all together
- $return_type = null;
- foreach ($call_args as $arg) {
- if ($array_arg_type = $nodeTypeProvider->getType($arg->value)) {
- if ($array_arg_type->isSingle()) {
- $atomic_type = $array_arg_type->getSingleAtomic();
- if ($atomic_type instanceof TPositiveInt) {
- //we replace TPositiveInt with a range for better combination
- $array_arg_type->removeType('int');
- $array_arg_type->addType(new TIntRange(1, null));
- }
- }
-
- $return_type = Type::combineUnionTypes(
- $return_type,
- $array_arg_type
- );
- } else {
- return Type::getMixed();
- }
- }
-
- return $return_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php
deleted file mode 100644
index 4b9ea03..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/MktimeReturnTypeProvider.php
+++ /dev/null
@@ -1,51 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Union;
-
-class MktimeReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'mktime',
- ];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- foreach ($call_args as $call_arg) {
- if (!($call_arg_type = $statements_source->node_data->getType($call_arg->value))
- || !$call_arg_type->isInt()
- ) {
- $value_type = new Union([new TInt, new TFalse]);
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_falsable_issues) {
- $value_type->ignore_falsable_issues = true;
- }
-
- return $value_type;
- }
- }
-
- return Type::getInt();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php
deleted file mode 100644
index 6d51c7d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ParseUrlReturnTypeProvider.php
+++ /dev/null
@@ -1,163 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Union;
-
-use const PHP_URL_FRAGMENT;
-use const PHP_URL_HOST;
-use const PHP_URL_PASS;
-use const PHP_URL_PATH;
-use const PHP_URL_PORT;
-use const PHP_URL_QUERY;
-use const PHP_URL_SCHEME;
-use const PHP_URL_USER;
-
-class ParseUrlReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['parse_url'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (isset($call_args[1])) {
- $is_default_component = false;
- if ($component_type = $statements_source->node_data->getType($call_args[1]->value)) {
- if (!$component_type->hasMixed()) {
- $codebase = $statements_source->getCodebase();
-
- $acceptable_string_component_type = new Union([
- new TLiteralInt(PHP_URL_SCHEME),
- new TLiteralInt(PHP_URL_USER),
- new TLiteralInt(PHP_URL_PASS),
- new TLiteralInt(PHP_URL_HOST),
- new TLiteralInt(PHP_URL_PATH),
- new TLiteralInt(PHP_URL_QUERY),
- new TLiteralInt(PHP_URL_FRAGMENT),
- ]);
-
- $acceptable_int_component_type = new Union([
- new TLiteralInt(PHP_URL_PORT),
- ]);
-
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $component_type,
- $acceptable_string_component_type
- )) {
- $nullable_falsable_string = new Union([
- new TString,
- new TFalse,
- new TNull,
- ]);
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_nullable_issues) {
- $nullable_falsable_string->ignore_nullable_issues = true;
- }
-
- if ($codebase->config->ignore_internal_falsable_issues) {
- $nullable_falsable_string->ignore_falsable_issues = true;
- }
-
- return $nullable_falsable_string;
- }
-
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $component_type,
- $acceptable_int_component_type
- )) {
- $nullable_falsable_int = new Union([
- new TInt,
- new TFalse,
- new TNull,
- ]);
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_nullable_issues) {
- $nullable_falsable_int->ignore_nullable_issues = true;
- }
-
- if ($codebase->config->ignore_internal_falsable_issues) {
- $nullable_falsable_int->ignore_falsable_issues = true;
- }
-
- return $nullable_falsable_int;
- }
-
- if ($component_type->isSingleIntLiteral()) {
- $component_type_type = $component_type->getSingleIntLiteral();
- $is_default_component = $component_type_type->value <= -1;
- }
- }
- }
-
- if (!$is_default_component) {
- $nullable_string_or_int = new Union([
- new TString,
- new TInt,
- new TNull,
- ]);
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_nullable_issues) {
- $nullable_string_or_int->ignore_nullable_issues = true;
- }
-
- return $nullable_string_or_int;
- }
- }
-
- $component_types = [
- 'scheme' => Type::getString(),
- 'user' => Type::getString(),
- 'pass' => Type::getString(),
- 'host' => Type::getString(),
- 'port' => Type::getInt(),
- 'path' => Type::getString(),
- 'query' => Type::getString(),
- 'fragment' => Type::getString(),
- ];
-
- foreach ($component_types as $component_type) {
- $component_type->possibly_undefined = true;
- }
-
- $return_type = new Union([
- new TKeyedArray($component_types),
- new TFalse(),
- ]);
-
- if ($statements_source->getCodebase()->config->ignore_internal_falsable_issues) {
- $return_type->ignore_falsable_issues = true;
- }
-
- return $return_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php
deleted file mode 100644
index 86ee127..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementReturnTypeProvider.php
+++ /dev/null
@@ -1,115 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PDO;
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Union;
-
-use function class_exists;
-
-class PdoStatementReturnTypeProvider implements MethodReturnTypeProviderInterface
-{
- public static function getClassLikeNames(): array
- {
- return ['PDOStatement'];
- }
-
- public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
- {
- $source = $event->getSource();
- $call_args = $event->getCallArgs();
- $method_name_lowercase = $event->getMethodNameLowercase();
- if ($method_name_lowercase === 'fetch'
- && class_exists('PDO')
- && isset($call_args[0])
- && ($first_arg_type = $source->getNodeTypeProvider()->getType($call_args[0]->value))
- && $first_arg_type->isSingleIntLiteral()
- ) {
- $fetch_mode = $first_arg_type->getSingleIntLiteral()->value;
-
- switch ($fetch_mode) {
- case PDO::FETCH_ASSOC: // array<string,scalar|null>|false
- return new Union([
- new TArray([
- Type::getString(),
- new Union([
- new TScalar(),
- new TNull()
- ])
- ]),
- new TFalse(),
- ]);
-
- case PDO::FETCH_BOTH: // array<array-key,scalar|null>|false
- return new Union([
- new TArray([
- Type::getArrayKey(),
- new Union([
- new TScalar(),
- new TNull()
- ])
- ]),
- new TFalse(),
- ]);
-
- case PDO::FETCH_BOUND: // bool
- return Type::getBool();
-
- case PDO::FETCH_CLASS: // object|false
- return new Union([
- new TObject(),
- new TFalse(),
- ]);
-
- case PDO::FETCH_LAZY: // object|false
- // This actually returns a PDORow object, but that class is
- // undocumented, and its attributes are all dynamic anyway
- return new Union([
- new TObject(),
- new TFalse(),
- ]);
-
- case PDO::FETCH_NAMED: // array<string, scalar|list<scalar>>|false
- return new Union([
- new TArray([
- Type::getString(),
- new Union([
- new TScalar(),
- new TList(Type::getScalar())
- ])
- ]),
- new TFalse(),
- ]);
-
- case PDO::FETCH_NUM: // list<scalar|null>|false
- return new Union([
- new TList(
- new Union([
- new TScalar(),
- new TNull()
- ])
- ),
- new TFalse(),
- ]);
-
- case PDO::FETCH_OBJ: // stdClass|false
- return new Union([
- new TNamedObject('stdClass'),
- new TFalse(),
- ]);
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php
deleted file mode 100644
index e352ffd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PdoStatementSetFetchMode.php
+++ /dev/null
@@ -1,111 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use PDO;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\MethodParamsProviderEvent;
-use Psalm\Plugin\EventHandler\MethodParamsProviderInterface;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-
-class PdoStatementSetFetchMode implements MethodParamsProviderInterface
-{
- public static function getClassLikeNames(): array
- {
- return ['PDOStatement'];
- }
-
- /**
- * @return ?array<int, FunctionLikeParameter>
- */
- public static function getMethodParams(MethodParamsProviderEvent $event): ?array
- {
- $statements_source = $event->getStatementsSource();
- $method_name_lowercase = $event->getMethodNameLowercase();
- $context = $event->getContext();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return null;
- }
-
- if ($method_name_lowercase === 'setfetchmode') {
- if (!$context
- || !$call_args
- || ExpressionAnalyzer::analyze(
- $statements_source,
- $call_args[0]->value,
- $context
- ) === false
- ) {
- return null;
- }
-
- if (($first_call_arg_type = $statements_source->node_data->getType($call_args[0]->value))
- && $first_call_arg_type->isSingleIntLiteral()
- ) {
- $params = [
- new FunctionLikeParameter(
- 'mode',
- false,
- Type::getInt(),
- null,
- null,
- false
- ),
- ];
-
- $value = $first_call_arg_type->getSingleIntLiteral()->value;
-
- switch ($value) {
- case PDO::FETCH_COLUMN:
- $params[] = new FunctionLikeParameter(
- 'colno',
- false,
- Type::getInt(),
- null,
- null,
- false
- );
- break;
-
- case PDO::FETCH_CLASS:
- $params[] = new FunctionLikeParameter(
- 'classname',
- false,
- Type::getClassString(),
- null,
- null,
- false
- );
-
- $params[] = new FunctionLikeParameter(
- 'ctorargs',
- false,
- Type::getArray(),
- null,
- null,
- true
- );
- break;
-
- case PDO::FETCH_INTO:
- $params[] = new FunctionLikeParameter(
- 'object',
- false,
- Type::getObject(),
- null,
- null,
- false
- );
- break;
- }
-
- return $params;
- }
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php
deleted file mode 100644
index 32530db..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/RandReturnTypeProvider.php
+++ /dev/null
@@ -1,70 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Union;
-
-use function count;
-
-class RandReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['rand', 'mt_rand', 'random_int'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $call_args = $event->getCallArgs();
- if (count($call_args) === 0) {
- return Type::getInt();
- }
-
- if (count($call_args) !== 2) {
- return null;
- }
-
- $statements_source = $event->getStatementsSource();
- $nodeTypeProvider = $statements_source->getNodeTypeProvider();
-
- $first_arg = $nodeTypeProvider->getType($call_args[0]->value);
- $second_arg = $nodeTypeProvider->getType($call_args[1]->value);
-
- $min_value = null;
- if ($first_arg !== null && $first_arg->isSingle()) {
- $first_atomic_type = $first_arg->getSingleAtomic();
- if ($first_atomic_type instanceof TLiteralInt) {
- $min_value = $first_atomic_type->value;
- } elseif ($first_atomic_type instanceof TIntRange) {
- $min_value = $first_atomic_type->min_bound;
- } elseif ($first_atomic_type instanceof TPositiveInt) {
- $min_value = 1;
- }
- }
-
- $max_value = null;
- if ($second_arg !== null && $second_arg->isSingle()) {
- $second_atomic_type = $second_arg->getSingleAtomic();
- if ($second_atomic_type instanceof TLiteralInt) {
- $max_value = $second_atomic_type->value;
- } elseif ($second_atomic_type instanceof TIntRange) {
- $max_value = $second_atomic_type->max_bound;
- } elseif ($second_atomic_type instanceof TPositiveInt) {
- // no max value, we keep null
- }
- }
-
- return new Union([new TIntRange($min_value, $max_value)]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php
deleted file mode 100644
index 88e71c3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/SimpleXmlElementAsXml.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\MethodReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Union;
-
-use function count;
-
-class SimpleXmlElementAsXml implements MethodReturnTypeProviderInterface
-{
- public static function getClassLikeNames(): array
- {
- return ['SimpleXMLElement'];
- }
-
- public static function getMethodReturnType(MethodReturnTypeProviderEvent $event): ?Union
- {
- $call_args = $event->getCallArgs();
- $method_name_lowercase = $event->getMethodNameLowercase();
- if ($method_name_lowercase === 'asxml'
- && !count($call_args)
- ) {
- return Type::parseString('string|false');
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php
deleted file mode 100644
index d32da31..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrReplaceReturnTypeProvider.php
+++ /dev/null
@@ -1,64 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-use function count;
-use function in_array;
-
-class StrReplaceReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'str_replace',
- 'str_ireplace',
- 'substr_replace',
- 'preg_replace',
- 'preg_replace_callback',
- ];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- if (!$statements_source instanceof StatementsAnalyzer
- || count($call_args) < 3
- ) {
- return Type::getMixed();
- }
-
- if ($subject_type = $statements_source->node_data->getType($call_args[2]->value)) {
- if (!$subject_type->hasString() && $subject_type->hasArray()) {
- return Type::getArray();
- }
-
- $return_type = Type::getString();
-
- if (in_array($function_id, ['preg_replace', 'preg_replace_callback'], true)) {
- $return_type->addType(new TNull());
-
- $codebase = $statements_source->getCodebase();
-
- if ($codebase->config->ignore_internal_nullable_issues) {
- $return_type->ignore_nullable_issues = true;
- }
- }
-
- return $return_type;
- }
-
- return Type::getMixed();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php
deleted file mode 100644
index cb3b019..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/StrTrReturnTypeProvider.php
+++ /dev/null
@@ -1,72 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\DataFlow\DataFlowNode;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function in_array;
-
-class StrTrReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return [
- 'strtr',
- ];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- $code_location = $event->getCodeLocation();
- if (!$statements_source instanceof StatementsAnalyzer) {
- throw new UnexpectedValueException();
- }
-
- $type = Type::getString();
-
- if ($statements_source->data_flow_graph
- && !in_array('TaintedInput', $statements_source->getSuppressedIssues())) {
- $function_return_sink = DataFlowNode::getForMethodReturn(
- $function_id,
- $function_id,
- null,
- $code_location
- );
-
- $statements_source->data_flow_graph->addNode($function_return_sink);
- foreach ($call_args as $i => $_) {
- $function_param_sink = DataFlowNode::getForMethodArgument(
- $function_id,
- $function_id,
- $i,
- null,
- $code_location
- );
-
- $statements_source->data_flow_graph->addNode($function_param_sink);
-
- $statements_source->data_flow_graph->addPath(
- $function_param_sink,
- $function_return_sink,
- 'arg'
- );
- }
-
- $type->parent_nodes = [$function_return_sink->id => $function_return_sink];
- }
-
- return $type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php
deleted file mode 100644
index 385c6b6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/TriggerErrorReturnTypeProvider.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Union;
-
-use function in_array;
-
-use const E_USER_DEPRECATED;
-use const E_USER_ERROR;
-use const E_USER_NOTICE;
-use const E_USER_WARNING;
-
-class TriggerErrorReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['trigger_error'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union
- {
- $codebase = $event->getStatementsSource()->getCodebase();
- $config = $codebase->config;
- if ($config->trigger_error_exits === 'always') {
- return new Union([new TNever()]);
- }
-
- if ($config->trigger_error_exits === 'never') {
- return new Union([new TTrue()]);
- }
-
- //default behaviour
- $call_args = $event->getCallArgs();
- $statements_source = $event->getStatementsSource();
- if (isset($call_args[1])
- && ($array_arg_type = $statements_source->getNodeTypeProvider()->getType($call_args[1]->value))
- ) {
- $return_types = [];
- foreach ($array_arg_type->getAtomicTypes() as $atomicType) {
- if ($atomicType instanceof TLiteralInt) {
- if (in_array($atomicType->value, [E_USER_WARNING, E_USER_DEPRECATED, E_USER_NOTICE], true)) {
- $return_types[] = new TTrue();
- } elseif ($atomicType->value === E_USER_ERROR) {
- $return_types[] = new TNever();
- } else {
- // not recognized int literal. return false before PHP8, fatal error since
- $return_types[] = new TFalse();
- }
- } else {
- $return_types[] = new TBool();
- }
- }
-
- return TypeCombiner::combine($return_types, $codebase);
- }
-
- //default value is E_USER_NOTICE, so return true
- return new Union([new TTrue()]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php
deleted file mode 100644
index cab2b99..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/VersionCompareReturnTypeProvider.php
+++ /dev/null
@@ -1,82 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider\ReturnTypeProvider;
-
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\FunctionReturnTypeProviderInterface;
-use Psalm\Type;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Union;
-
-use function count;
-
-class VersionCompareReturnTypeProvider implements FunctionReturnTypeProviderInterface
-{
- /**
- * @return array<lowercase-string>
- */
- public static function getFunctionIds(): array
- {
- return ['version_compare'];
- }
-
- public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union
- {
- $statements_source = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- if (!$statements_source instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (count($call_args) > 2) {
- $operator_type = $statements_source->node_data->getType($call_args[2]->value);
-
- if ($operator_type) {
- if (!$operator_type->hasMixed()) {
- $acceptable_operator_type = new Union([
- new TLiteralString('<'),
- new TLiteralString('lt'),
- new TLiteralString('<='),
- new TLiteralString('le'),
- new TLiteralString('>'),
- new TLiteralString('gt'),
- new TLiteralString('>='),
- new TLiteralString('ge'),
- new TLiteralString('=='),
- new TLiteralString('='),
- new TLiteralString('eq'),
- new TLiteralString('!='),
- new TLiteralString('<>'),
- new TLiteralString('ne'),
- ]);
-
- $codebase = $statements_source->getCodebase();
-
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $operator_type,
- $acceptable_operator_type
- )) {
- return Type::getBool();
- }
- }
- }
-
- return new Union([
- new TBool,
- new TNull,
- ]);
- }
-
- return new Union([
- new TLiteralInt(-1),
- new TLiteralInt(0),
- new TLiteralInt(1),
- ]);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsProvider.php
deleted file mode 100644
index 431cb15..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsProvider.php
+++ /dev/null
@@ -1,538 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use PhpParser;
-use PhpParser\ErrorHandler\Collecting;
-use PhpParser\Node\Stmt;
-use Psalm\CodeLocation\ParseErrorLocation;
-use Psalm\Config;
-use Psalm\Internal\Diff\FileDiffer;
-use Psalm\Internal\Diff\FileStatementsDiffer;
-use Psalm\Internal\PhpTraverser\CustomTraverser;
-use Psalm\Internal\PhpVisitor\CloningVisitor;
-use Psalm\Internal\PhpVisitor\PartialParserVisitor;
-use Psalm\Internal\PhpVisitor\SimpleNameResolver;
-use Psalm\Issue\ParseError;
-use Psalm\IssueBuffer;
-use Psalm\Progress\Progress;
-use Psalm\Progress\VoidProgress;
-use Throwable;
-
-use function abs;
-use function array_flip;
-use function array_intersect_key;
-use function array_map;
-use function array_merge;
-use function count;
-use function filemtime;
-use function md5;
-use function strlen;
-use function strpos;
-
-/**
- * @internal
- */
-class StatementsProvider
-{
- /**
- * @var FileProvider
- */
- private $file_provider;
-
- /**
- * @var ?ParserCacheProvider
- */
- public $parser_cache_provider;
-
- /**
- * @var int
- */
- private $this_modified_time;
-
- /**
- * @var ?FileStorageCacheProvider
- */
- private $file_storage_cache_provider;
-
- /**
- * @var StatementsVolatileCache
- */
- private $statements_volatile_cache;
-
- /**
- * @var array<string, array<string, bool>>
- */
- private $unchanged_members = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private $unchanged_signature_members = [];
-
- /**
- * @var array<string, array<string, bool>>
- */
- private $changed_members = [];
-
- /**
- * @var array<string, bool>
- */
- private $errors = [];
-
- /**
- * @var array<string, array<int, array{int, int, int, int}>>
- */
- private $diff_map = [];
-
- /**
- * @var array<string, array<int, array{int, int}>>
- */
- private $deletion_ranges = [];
-
- /**
- * @var PhpParser\Lexer|null
- */
- private static $lexer;
-
- /**
- * @var PhpParser\Parser|null
- */
- private static $parser;
-
- public function __construct(
- FileProvider $file_provider,
- ?ParserCacheProvider $parser_cache_provider = null,
- ?FileStorageCacheProvider $file_storage_cache_provider = null
- ) {
- $this->file_provider = $file_provider;
- $this->parser_cache_provider = $parser_cache_provider;
- $this->this_modified_time = filemtime(__FILE__);
- $this->file_storage_cache_provider = $file_storage_cache_provider;
- $this->statements_volatile_cache = StatementsVolatileCache::getInstance();
- }
-
- /**
- * @return list<Stmt>
- */
- public function getStatementsForFile(string $file_path, string $php_version, ?Progress $progress = null): array
- {
- unset($this->errors[$file_path]);
-
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- $from_cache = false;
-
- $version = PHP_PARSER_VERSION . $this->this_modified_time;
-
- $file_contents = $this->file_provider->getContents($file_path);
- $modified_time = $this->file_provider->getModifiedTime($file_path);
-
- $config = Config::getInstance();
-
- $file_content_hash = md5($version . $file_contents);
-
- if (!$this->parser_cache_provider
- || (!$config->isInProjectDirs($file_path) && strpos($file_path, 'vendor'))
- ) {
- $cache_key = "{$file_content_hash}:{$php_version}";
- if ($this->statements_volatile_cache->has($cache_key)) {
- return $this->statements_volatile_cache->get($cache_key);
- }
-
- $progress->debug('Parsing ' . $file_path . "\n");
-
- $has_errors = false;
-
- $stmts = self::parseStatements($file_contents, $php_version, $has_errors, $file_path);
-
- $this->statements_volatile_cache->set($cache_key, $stmts);
-
- return $stmts;
- }
-
- $stmts = $this->parser_cache_provider->loadStatementsFromCache(
- $file_path,
- $modified_time,
- $file_content_hash
- );
-
- if ($stmts === null) {
- $progress->debug('Parsing ' . $file_path . "\n");
-
- $existing_statements = $this->parser_cache_provider->loadExistingStatementsFromCache($file_path);
-
- if ($existing_statements && !$existing_statements[0] instanceof PhpParser\Node\Stmt) {
- $existing_statements = null;
- }
-
- $existing_file_contents = $this->parser_cache_provider->loadExistingFileContentsFromCache($file_path);
-
- // this happens after editing temporary file
- if ($existing_file_contents === $file_contents && $existing_statements) {
- $this->diff_map[$file_path] = [];
- $this->parser_cache_provider->saveStatementsToCache(
- $file_path,
- $file_content_hash,
- $existing_statements,
- true
- );
-
- return $existing_statements;
- }
-
- $file_changes = null;
-
- $existing_statements_copy = null;
-
- if ($existing_statements
- && $existing_file_contents
- && abs(strlen($existing_file_contents) - strlen($file_contents)) < 5000
- ) {
- $file_changes = FileDiffer::getDiff($existing_file_contents, $file_contents);
-
- if (count($file_changes) < 10) {
- $traverser = new PhpParser\NodeTraverser;
- $traverser->addVisitor(new CloningVisitor);
- // performs a deep clone
- /** @var list<PhpParser\Node\Stmt> */
- $existing_statements_copy = $traverser->traverse($existing_statements);
- } else {
- $file_changes = null;
- }
- }
-
- $has_errors = false;
-
- $stmts = self::parseStatements(
- $file_contents,
- $php_version,
- $has_errors,
- $file_path,
- $existing_file_contents,
- $existing_statements_copy,
- $file_changes
- );
-
- if ($existing_file_contents && $existing_statements && (!$has_errors || $stmts)) {
- [$unchanged_members, $unchanged_signature_members, $changed_members, $diff_map, $deletion_ranges]
- = FileStatementsDiffer::diff(
- $existing_statements,
- $stmts,
- $existing_file_contents,
- $file_contents
- );
-
- $unchanged_members = array_map(
- function (int $_): bool {
- return true;
- },
- array_flip($unchanged_members)
- );
-
- $unchanged_signature_members = array_map(
- function (int $_): bool {
- return true;
- },
- array_flip($unchanged_signature_members)
- );
-
- $file_path_hash = md5($file_path);
-
- $changed_members = array_map(
- function (string $key) use ($file_path_hash): string {
- if (strpos($key, 'use:') === 0) {
- return $key . ':' . $file_path_hash;
- }
-
- return $key;
- },
- $changed_members
- );
-
- $changed_members = array_map(
- /**
- * @param int $_
- *
- * @return bool
- */
- function ($_): bool {
- return true;
- },
- array_flip($changed_members)
- );
-
- if (isset($this->unchanged_members[$file_path])) {
- $this->unchanged_members[$file_path] = array_intersect_key(
- $this->unchanged_members[$file_path],
- $unchanged_members
- );
- } else {
- $this->unchanged_members[$file_path] = $unchanged_members;
- }
-
- if (isset($this->unchanged_signature_members[$file_path])) {
- $this->unchanged_signature_members[$file_path] = array_intersect_key(
- $this->unchanged_signature_members[$file_path],
- $unchanged_signature_members
- );
- } else {
- $this->unchanged_signature_members[$file_path] = $unchanged_signature_members;
- }
-
- if (isset($this->changed_members[$file_path])) {
- $this->changed_members[$file_path] = array_merge(
- $this->changed_members[$file_path],
- $changed_members
- );
- } else {
- $this->changed_members[$file_path] = $changed_members;
- }
-
- $this->diff_map[$file_path] = $diff_map;
- $this->deletion_ranges[$file_path] = $deletion_ranges;
- } elseif ($has_errors && !$stmts) {
- $this->errors[$file_path] = true;
- }
-
- if ($this->file_storage_cache_provider) {
- $this->file_storage_cache_provider->removeCacheForFile($file_path);
- }
-
- $this->parser_cache_provider->cacheFileContents($file_path, $file_contents);
- } else {
- $from_cache = true;
- $this->diff_map[$file_path] = [];
- $this->deletion_ranges[$file_path] = [];
- }
-
- $this->parser_cache_provider->saveStatementsToCache($file_path, $file_content_hash, $stmts, $from_cache);
-
- if (!$stmts) {
- return [];
- }
-
- return $stmts;
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getChangedMembers(): array
- {
- return $this->changed_members;
- }
-
- /**
- * @param array<string, array<string, bool>> $more_changed_members
- *
- */
- public function addChangedMembers(array $more_changed_members): void
- {
- $this->changed_members = array_merge($more_changed_members, $this->changed_members);
- }
-
- /**
- * @return array<string, array<string, bool>>
- */
- public function getUnchangedSignatureMembers(): array
- {
- return $this->unchanged_signature_members;
- }
-
- /**
- * @param array<string, array<string, bool>> $more_unchanged_members
- *
- */
- public function addUnchangedSignatureMembers(array $more_unchanged_members): void
- {
- $this->unchanged_signature_members = array_merge($more_unchanged_members, $this->unchanged_signature_members);
- }
-
- /**
- * @return array<string, bool>
- */
- public function getErrors(): array
- {
- return $this->errors;
- }
-
- /**
- * @param array<string, bool> $errors
- *
- */
- public function addErrors(array $errors): void
- {
- $this->errors += $errors;
- }
-
- public function setUnchangedFile(string $file_path): void
- {
- if (!isset($this->diff_map[$file_path])) {
- $this->diff_map[$file_path] = [];
- }
-
- if (!isset($this->deletion_ranges[$file_path])) {
- $this->deletion_ranges[$file_path] = [];
- }
- }
-
- /**
- * @return array<string, array<int, array{int, int, int, int}>>
- */
- public function getDiffMap(): array
- {
- return $this->diff_map;
- }
-
- /**
- * @return array<string, array<int, array{int, int}>>
- */
- public function getDeletionRanges(): array
- {
- return $this->deletion_ranges;
- }
-
- /**
- * @param array<string, array<int, array{int, int, int, int}>> $diff_map
- *
- */
- public function addDiffMap(array $diff_map): void
- {
- $this->diff_map = array_merge($diff_map, $this->diff_map);
- }
-
- /**
- * @param array<string, array<int, array{int, int}>> $deletion_ranges
- *
- */
- public function addDeletionRanges(array $deletion_ranges): void
- {
- $this->deletion_ranges = array_merge($deletion_ranges, $this->deletion_ranges);
- }
-
- public function resetDiffs(): void
- {
- $this->changed_members = [];
- $this->unchanged_members = [];
- $this->unchanged_signature_members = [];
- $this->diff_map = [];
- $this->deletion_ranges = [];
- }
-
- /**
- * @param list<Stmt> $existing_statements
- * @param array<int, array{0:int, 1:int, 2: int, 3: int, 4: int, 5:string}> $file_changes
- *
- * @return list<Stmt>
- */
- public static function parseStatements(
- string $file_contents,
- string $php_version,
- bool &$has_errors,
- ?string $file_path = null,
- ?string $existing_file_contents = null,
- ?array $existing_statements = null,
- ?array $file_changes = null
- ): array {
- $attributes = [
- 'comments', 'startLine', 'startFilePos', 'endFilePos',
- ];
-
- if (!self::$lexer) {
- self::$lexer = new PhpParser\Lexer\Emulative([
- 'usedAttributes' => $attributes,
- 'phpVersion' => $php_version,
- ]);
- }
-
- if (!self::$parser) {
- self::$parser = (new PhpParser\ParserFactory())->create(PhpParser\ParserFactory::ONLY_PHP7, self::$lexer);
- }
-
- $used_cached_statements = false;
-
- $error_handler = new Collecting();
-
- if ($existing_statements && $file_changes && $existing_file_contents) {
- $clashing_traverser = new CustomTraverser;
- $offset_analyzer = new PartialParserVisitor(
- self::$parser,
- $error_handler,
- $file_changes,
- $existing_file_contents,
- $file_contents
- );
- $clashing_traverser->addVisitor($offset_analyzer);
- $clashing_traverser->traverse($existing_statements);
-
- if (!$offset_analyzer->mustRescan()) {
- $used_cached_statements = true;
- $stmts = $existing_statements;
- } else {
- try {
- /** @var list<Stmt> */
- $stmts = self::$parser->parse($file_contents, $error_handler) ?: [];
- } catch (Throwable $t) {
- $stmts = [];
-
- // hope this got caught below
- }
- }
- } else {
- try {
- /** @var list<Stmt> */
- $stmts = self::$parser->parse($file_contents, $error_handler) ?: [];
- } catch (Throwable $t) {
- $stmts = [];
-
- // hope this got caught below
- }
- }
-
- if ($error_handler->hasErrors() && $file_path) {
- $config = Config::getInstance();
- $has_errors = true;
-
- foreach ($error_handler->getErrors() as $error) {
- if ($error->hasColumnInfo()) {
- IssueBuffer::maybeAdd(
- new ParseError(
- $error->getMessage(),
- new ParseErrorLocation(
- $error,
- $file_contents,
- $file_path,
- $config->shortenFileName($file_path)
- )
- )
- );
- }
- }
- }
-
- $error_handler->clearErrors();
-
- $resolving_traverser = new PhpParser\NodeTraverser;
- $name_resolver = new SimpleNameResolver(
- $error_handler,
- $used_cached_statements ? $file_changes : []
- );
- $resolving_traverser->addVisitor($name_resolver);
- $resolving_traverser->traverse($stmts);
-
- return $stmts;
- }
-
- public static function clearLexer(): void
- {
- self::$lexer = null;
- }
-
- public static function clearParser(): void
- {
- self::$parser = null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsVolatileCache.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsVolatileCache.php
deleted file mode 100644
index 2dd19ea..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/StatementsVolatileCache.php
+++ /dev/null
@@ -1,108 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Provider;
-
-use InvalidArgumentException;
-use PhpParser\Node\Stmt;
-
-use function array_key_exists;
-use function array_search;
-use function array_splice;
-use function count;
-use function is_null;
-use function key;
-use function reset;
-
-/**
- * @internal Owned by StatementsProvider
- * @todo: track variables size instead
- */
-final class StatementsVolatileCache
-{
- /**
- * @var array<string, list<Stmt>>
- */
- protected $cache = [];
-
- /**
- * @var array<int, string>
- */
- protected $access = [];
-
- /**
- * @var int
- */
- protected $max_size;
-
- /**
- * @var ?StatementsVolatileCache
- */
- protected static $instance;
-
- public function __construct(int $max_size = 4096)
- {
- $this->max_size = $max_size;
- }
-
- public static function getInstance(): StatementsVolatileCache
- {
- if (is_null(self::$instance)) {
- self::$instance = new self();
- }
-
- return self::$instance;
- }
-
- public function has(string $key): bool
- {
- return array_key_exists($key, $this->cache);
- }
-
- /**
- * @param string $key
- * @return list<Stmt>
- * @throws InvalidArgumentException
- */
- public function get(string $key): array
- {
- if (! $this->has($key)) {
- throw new InvalidArgumentException('Given $key does not exists');
- }
-
- $access_index = array_search($key, $this->access);
- if (false !== $access_index) {
- array_splice($this->access, $access_index, 1);
- }
- $this->access[] = $key;
-
- return $this->cache[$key];
- }
-
- /**
- * @param string $key
- * @param list<Stmt> $content
- */
- public function set(string $key, array $content): void
- {
- if (count($this->cache) > $this->max_size) {
- reset($this->access);
-
- $oldest_key_index = key($this->access);
-
- if (! is_null($oldest_key_index)) {
- $oldest_key = $this->access[$oldest_key_index];
- unset($this->cache[$oldest_key]);
- unset($this->access[$oldest_key_index]);
- }
- }
-
- $this->cache[$key] = $content;
- $this->access[] = $key;
- }
-
- public function clearCache(): void
- {
- $this->cache = [];
- $this->access = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/ReferenceConstraint.php b/vendor/vimeo/psalm/src/Psalm/Internal/ReferenceConstraint.php
deleted file mode 100644
index 14bb170..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/ReferenceConstraint.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class ReferenceConstraint
-{
- /** @var Union|null */
- public $type;
-
- public function __construct(?Union $type = null)
- {
- if ($type) {
- $this->type = clone $type;
-
- if ($this->type->getLiteralStrings()) {
- $this->type->addType(new TString);
- }
-
- if ($this->type->getLiteralInts()) {
- $this->type->addType(new TInt);
- }
-
- if ($this->type->getLiteralFloats()) {
- $this->type->addType(new TFloat);
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/RuntimeCaches.php b/vendor/vimeo/psalm/src/Psalm/Internal/RuntimeCaches.php
deleted file mode 100644
index 64c398d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/RuntimeCaches.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?php
-
-namespace Psalm\Internal;
-
-use Psalm\Internal\Analyzer\FileAnalyzer;
-use Psalm\Internal\Analyzer\FunctionLikeAnalyzer;
-use Psalm\Internal\Codebase\Functions;
-use Psalm\Internal\Codebase\Reflection;
-use Psalm\Internal\FileManipulation\ClassDocblockManipulator;
-use Psalm\Internal\FileManipulation\FileManipulationBuffer;
-use Psalm\Internal\FileManipulation\FunctionDocblockManipulator;
-use Psalm\Internal\FileManipulation\PropertyDocblockManipulator;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileReferenceProvider;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\Internal\Provider\StatementsProvider;
-use Psalm\Internal\Provider\StatementsVolatileCache;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\Internal\Type\TypeTokenizer;
-use Psalm\IssueBuffer;
-
-abstract class RuntimeCaches
-{
- public static function clearAll(): void
- {
- IssueBuffer::clearCache();
- Reflection::clearCache();
- Functions::clearCache();
- TypeTokenizer::clearCache();
- FileReferenceProvider::clearCache();
- FileManipulationBuffer::clearCache();
- ClassDocblockManipulator::clearCache();
- FunctionDocblockManipulator::clearCache();
- PropertyDocblockManipulator::clearCache();
- FileAnalyzer::clearCache();
- FunctionLikeAnalyzer::clearCache();
- ClassLikeStorageProvider::deleteAll();
- FileStorageProvider::deleteAll();
- StatementsProvider::clearLexer();
- StatementsProvider::clearParser();
- ParsedDocblock::resetNewlineBetweenAnnotations();
- StatementsVolatileCache::getInstance()->clearCache();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php
deleted file mode 100644
index a9168cc..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php
+++ /dev/null
@@ -1,147 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-use PhpParser\Node\Stmt\ClassMethod;
-
-/**
- * @internal
- */
-class ClassLikeDocblockComment
-{
- /**
- * Whether or not the class is deprecated
- *
- * @var bool
- */
- public $deprecated = false;
-
- /**
- * Whether or not the class is internal
- *
- * @var bool
- */
- public $internal = false;
-
- /**
- * Whether or not the class is final
- *
- * @var bool
- */
- public $final = false;
-
- /**
- * If set, the class is internal to the given namespace.
- *
- * @var list<non-empty-string>
- */
- public $psalm_internal = [];
-
- /**
- * @var string[]
- */
- public $mixins = [];
-
- /**
- * @var array<int, array{string, ?string, ?string, bool, int}>
- */
- public $templates = [];
-
- /**
- * @var array<int, string>
- */
- public $template_extends = [];
-
- /**
- * @var array<int, string>
- */
- public $template_implements = [];
-
- /**
- * @var ?string
- */
- public $yield;
-
- /**
- * @var array<int, array{name:string, type:string, tag:string, line_number:int}>
- */
- public $properties = [];
-
- /**
- * @var array<int, ClassMethod>
- */
- public $methods = [];
-
- /**
- * @var bool
- */
- public $sealed_properties = false;
-
- /**
- * @var bool
- */
- public $sealed_methods = false;
-
- /**
- * @var bool
- */
- public $override_property_visibility = false;
-
- /**
- * @var bool
- */
- public $override_method_visibility = false;
-
- /**
- * @var bool
- */
- public $mutation_free = false;
-
- /**
- * @var bool
- */
- public $external_mutation_free = false;
-
- /**
- * @var bool
- */
- public $taint_specialize = false;
-
- /**
- * @var array<int, string>
- */
- public $suppressed_issues = [];
-
- /**
- * @var list<array{line_number:int,start_offset:int,end_offset:int,parts:list<string>}>
- */
- public $imported_types = [];
-
- /**
- * @var bool
- */
- public $consistent_constructor = false;
-
- /**
- * @var bool
- */
- public $consistent_templates = false;
-
- /** @var bool */
- public $stub_override = false;
-
- /**
- * @var null|string
- */
- public $extension_requirement;
-
- /**
- * @var array<int, string>
- */
- public $implementation_requirements = [];
-
- /**
- * @var ?string
- */
- public $description;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/DocblockParser.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/DocblockParser.php
deleted file mode 100644
index 2ddf134..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/DocblockParser.php
+++ /dev/null
@@ -1,300 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-use Psalm\Exception\DocblockParseException;
-
-use function array_filter;
-use function array_map;
-use function array_values;
-use function assert;
-use function count;
-use function explode;
-use function implode;
-use function is_string;
-use function min;
-use function preg_match;
-use function preg_replace;
-use function rtrim;
-use function str_replace;
-use function strlen;
-use function strpos;
-use function strspn;
-use function substr;
-use function trim;
-
-use const PREG_OFFSET_CAPTURE;
-
-/**
- * This class will parse Docblocks in order to extract known tags from them
- */
-class DocblockParser
-{
- /**
- * $offsetStart is the absolute position of the docblock in the file. It'll be used to add to the position of some
- * special tags (like `psalm-suppress`) for future uses
- */
- public static function parse(string $docblock, int $offsetStart): ParsedDocblock
- {
- // Strip off comments.
- $docblock = trim($docblock);
-
- if (strpos($docblock, '/**') === 0) {
- $docblock = substr($docblock, 3);
- }
-
- if (substr($docblock, -2) === '*/') {
- $docblock = substr($docblock, 0, -2);
-
- if (substr($docblock, -1) === '*') {
- $docblock = substr($docblock, 0, -1);
- }
- }
-
- // Normalize multi-line @specials.
- $lines = explode("\n", $docblock);
-
- $special = [];
-
- $first_line_padding = null;
-
- $last = false;
- foreach ($lines as $k => $line) {
- if (preg_match('/^[ \t]*\*?\s*@\w/i', $line)) {
- $last = $k;
- } elseif (preg_match('/^\s*\r?$/', $line)) {
- $last = false;
- } elseif ($last !== false) {
- $old_last_line = $lines[$last];
- $lines[$last] = $old_last_line . "\n" . $line;
-
- unset($lines[$k]);
- }
- }
-
- $line_offset = 0;
-
- foreach ($lines as $k => $line) {
- $original_line_length = strlen($line);
-
- $line = str_replace("\r", '', $line);
-
- if ($first_line_padding === null) {
- $asterisk_pos = strpos($line, '*');
-
- if ($asterisk_pos) {
- $first_line_padding = substr($line, 0, $asterisk_pos - 1);
- }
- }
-
- if (preg_match('/^[ \t]*\*?\s*@([\w\-\\\:]+)[\t ]*(.*)$/sm', $line, $matches, PREG_OFFSET_CAPTURE)) {
- /** @var array<int, array{string, int}> $matches */
- [, $type_info, $data_info] = $matches;
-
- [$type] = $type_info;
- [$data, $data_offset] = $data_info;
-
- if (strpos($data, '*')) {
- $data = rtrim(preg_replace('/^[ \t]*\*\s*$/m', '', $data));
- }
-
- if (empty($special[$type])) {
- $special[$type] = [];
- }
-
- $data_offset += $line_offset;
-
- $special[$type][$data_offset + 3 + $offsetStart] = $data;
-
- unset($lines[$k]);
- } else {
- // Strip the leading *, if present.
- $text = $lines[$k];
- $text = str_replace("\t", ' ', $text);
- $text = preg_replace('/^ *\*/', '', $text);
- $lines[$k] = $text;
- }
-
- $line_offset += $original_line_length + 1;
- }
-
- // Smush the whole docblock to the left edge.
- $min_indent = 80;
- foreach ($lines as $k => $line) {
- $indent = strspn($line, ' ');
- if ($indent === strlen($line)) {
- // This line consists of only spaces. Trim it completely.
- $lines[$k] = '';
- continue;
- }
- $min_indent = min($indent, $min_indent);
- }
- if ($min_indent > 0) {
- foreach ($lines as $k => $line) {
- if (strlen($line) < $min_indent) {
- continue;
- }
- $lines[$k] = substr($line, $min_indent);
- }
- }
- $docblock = implode("\n", $lines);
- $docblock = rtrim($docblock);
-
- // Trim any empty lines off the front, but leave the indent level if there
- // is one.
- $docblock = preg_replace('/^\s*\n/', '', $docblock);
-
- $parsed = new ParsedDocblock($docblock, $special, $first_line_padding ?: '');
-
- self::resolveTags($parsed);
-
- return $parsed;
- }
-
- private static function resolveTags(ParsedDocblock $docblock): void
- {
- if (isset($docblock->tags['template'])
- || isset($docblock->tags['psalm-template'])
- || isset($docblock->tags['phpstan-template'])
- ) {
- $docblock->combined_tags['template']
- = ($docblock->tags['template'] ?? [])
- + ($docblock->tags['phpstan-template'] ?? [])
- + ($docblock->tags['psalm-template'] ?? []);
- }
-
- if (isset($docblock->tags['template-covariant'])
- || isset($docblock->tags['psalm-template-covariant'])
- || isset($docblock->tags['phpstan-template-covariant'])
- ) {
- $docblock->combined_tags['template-covariant']
- = ($docblock->tags['template-covariant'] ?? [])
- + ($docblock->tags['phpstan-template-covariant'] ?? [])
- + ($docblock->tags['psalm-template-covariant'] ?? []);
- }
-
- if (isset($docblock->tags['template-extends'])
- || isset($docblock->tags['inherits'])
- || isset($docblock->tags['extends'])
- || isset($docblock->tags['psalm-extends'])
- || isset($docblock->tags['phpstan-extends'])
- ) {
- $docblock->combined_tags['extends']
- = ($docblock->tags['template-extends'] ?? [])
- + ($docblock->tags['inherits'] ?? [])
- + ($docblock->tags['extends'] ?? [])
- + ($docblock->tags['psalm-extends'] ?? [])
- + ($docblock->tags['phpstan-extends'] ?? []);
- }
-
- if (isset($docblock->tags['template-implements'])
- || isset($docblock->tags['implements'])
- || isset($docblock->tags['phpstan-implements'])
- || isset($docblock->tags['psalm-implements'])
- ) {
- $docblock->combined_tags['implements']
- = ($docblock->tags['template-implements'] ?? [])
- + ($docblock->tags['implements'] ?? [])
- + ($docblock->tags['phpstan-implements'] ?? [])
- + ($docblock->tags['psalm-implements'] ?? []);
- }
-
- if (isset($docblock->tags['template-use'])
- || isset($docblock->tags['use'])
- || isset($docblock->tags['phpstan-use'])
- || isset($docblock->tags['psalm-use'])
- ) {
- $docblock->combined_tags['use']
- = ($docblock->tags['template-use'] ?? [])
- + ($docblock->tags['use'] ?? [])
- + ($docblock->tags['phpstan-use'] ?? [])
- + ($docblock->tags['psalm-use'] ?? []);
- }
-
- if (isset($docblock->tags['method'])
- || isset($docblock->tags['psalm-method'])
- ) {
- $docblock->combined_tags['method']
- = ($docblock->tags['method'] ?? [])
- + ($docblock->tags['psalm-method'] ?? []);
- }
-
- if (isset($docblock->tags['return'])
- || isset($docblock->tags['psalm-return'])
- || isset($docblock->tags['phpstan-return'])
- ) {
- if (isset($docblock->tags['psalm-return'])) {
- $docblock->combined_tags['return'] = $docblock->tags['psalm-return'];
- } elseif (isset($docblock->tags['phpstan-return'])) {
- $docblock->combined_tags['return'] = $docblock->tags['phpstan-return'];
- } else {
- $docblock->combined_tags['return'] = $docblock->tags['return'];
- }
- }
-
- if (isset($docblock->tags['param'])
- || isset($docblock->tags['psalm-param'])
- || isset($docblock->tags['phpstan-param'])
- ) {
- $docblock->combined_tags['param']
- = ($docblock->tags['param'] ?? [])
- + ($docblock->tags['phpstan-param'] ?? [])
- + ($docblock->tags['psalm-param'] ?? []);
- }
-
- if (isset($docblock->tags['var'])
- || isset($docblock->tags['psalm-var'])
- || isset($docblock->tags['phpstan-var'])
- ) {
- if (!isset($docblock->tags['ignore-var'])
- && !isset($docblock->tags['psalm-ignore-var'])
- ) {
- $docblock->combined_tags['var']
- = ($docblock->tags['var'] ?? [])
- + ($docblock->tags['phpstan-var'] ?? [])
- + ($docblock->tags['psalm-var'] ?? []);
- }
- }
-
- if (isset($docblock->tags['param-out'])
- || isset($docblock->tags['psalm-param-out'])
- ) {
- $docblock->combined_tags['param-out']
- = ($docblock->tags['param-out'] ?? [])
- + ($docblock->tags['psalm-param-out'] ?? []);
- }
- }
-
- /**
- * @return list<non-empty-string>
- * @throws DocblockParseException when a @psalm-internal tag doesn't include a namespace
- */
- public static function handlePsalmInternal(ParsedDocblock $parsed_docblock): array
- {
- if (isset($parsed_docblock->tags['psalm-internal'])) {
- $psalm_internal = array_map("trim", $parsed_docblock->tags['psalm-internal']);
-
- if (count($psalm_internal) !== count(array_filter($psalm_internal))) {
- throw new DocblockParseException('psalm-internal annotation used without specifying namespace');
- }
- // assert($psalm_internal === array_filter($psalm_internal)); // TODO get this to work
- assert(self::assertArrayOfNonEmptyString($psalm_internal));
-
- return array_values($psalm_internal);
- }
-
- return [];
- }
-
- /** @psalm-assert-if-true array<array-key, non-empty-string> $arr */
- private static function assertArrayOfNonEmptyString(array $arr): bool
- {
- foreach ($arr as $val) {
- if (!is_string($val) || $val === "") {
- return false;
- }
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FileScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FileScanner.php
deleted file mode 100644
index 1cc9b6f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FileScanner.php
+++ /dev/null
@@ -1,117 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-use PhpParser;
-use PhpParser\NodeTraverser;
-use Psalm\Aliases;
-use Psalm\Codebase;
-use Psalm\FileSource;
-use Psalm\Internal\PhpVisitor\ReflectorVisitor;
-use Psalm\Progress\Progress;
-use Psalm\Progress\VoidProgress;
-use Psalm\Storage\FileStorage;
-
-/**
- * @internal
- * @psalm-consistent-constructor
- */
-class FileScanner implements FileSource
-{
- /**
- * @var string
- */
- public $file_path;
-
- /**
- * @var string
- */
- public $file_name;
-
- /**
- * @var bool
- */
- public $will_analyze;
-
- public function __construct(string $file_path, string $file_name, bool $will_analyze)
- {
- $this->file_path = $file_path;
- $this->file_name = $file_name;
- $this->will_analyze = $will_analyze;
- }
-
- public function scan(
- Codebase $codebase,
- FileStorage $file_storage,
- bool $storage_from_cache = false,
- ?Progress $progress = null
- ): void {
- if ($progress === null) {
- $progress = new VoidProgress();
- }
-
- if ((!$this->will_analyze || $file_storage->deep_scan)
- && $storage_from_cache
- && !$codebase->register_stub_files
- ) {
- return;
- }
-
- $stmts = $codebase->statements_provider->getStatementsForFile(
- $file_storage->file_path,
- $codebase->php_major_version . '.' . $codebase->php_minor_version,
- $progress
- );
-
- foreach ($stmts as $stmt) {
- if (!$stmt instanceof PhpParser\Node\Stmt\ClassLike
- && !$stmt instanceof PhpParser\Node\Stmt\Function_
- && !($stmt instanceof PhpParser\Node\Stmt\Expression
- && $stmt->expr instanceof PhpParser\Node\Expr\Include_)
- ) {
- $file_storage->has_extra_statements = true;
- break;
- }
- }
-
- if ($this->will_analyze) {
- $progress->debug('Deep scanning ' . $file_storage->file_path . "\n");
- } else {
- $progress->debug('Scanning ' . $file_storage->file_path . "\n");
- }
-
- $traverser = new NodeTraverser();
- $traverser->addVisitor(
- new ReflectorVisitor($codebase, $this, $file_storage)
- );
-
- $traverser->traverse($stmts);
-
- $file_storage->deep_scan = $this->will_analyze;
- }
-
- public function getFilePath(): string
- {
- return $this->file_path;
- }
-
- public function getFileName(): string
- {
- return $this->file_name;
- }
-
- public function getRootFilePath(): string
- {
- return $this->file_path;
- }
-
- public function getRootFileName(): string
- {
- return $this->file_name;
- }
-
- public function getAliases(): Aliases
- {
- return new Aliases();
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FunctionDocblockComment.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FunctionDocblockComment.php
deleted file mode 100644
index a3819b4..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/FunctionDocblockComment.php
+++ /dev/null
@@ -1,228 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-/**
- * @internal
- */
-class FunctionDocblockComment
-{
- /**
- * @var string|null
- */
- public $return_type;
-
- /**
- * @var string|null
- */
- public $return_type_description;
-
- /**
- * @var int|null
- */
- public $return_type_start;
-
- /**
- * @var int|null
- */
- public $return_type_end;
-
- /**
- * @var int|null
- */
- public $return_type_line_number;
-
- /**
- * @var array<
- * int,
- * array{
- * name:string,
- * type:string,
- * line_number: int,
- * start: int,
- * end: int,
- * description?: string
- * }
- * >
- */
- public $params = [];
-
- /**
- * @var array<int, array{name:string, type:string, line_number: int}>
- */
- public $params_out = [];
-
- /**
- * @var array{type:string, line_number: int}|null
- */
- public $self_out;
-
- /**
- * @var array{type:string, line_number: int}|null
- */
- public $if_this_is;
-
- /**
- * @var array<int, array{name:string, type:string, line_number: int}>
- */
- public $globals = [];
-
- /**
- * Whether or not the function is deprecated
- *
- * @var bool
- */
- public $deprecated = false;
-
- /**
- * If set, the function is internal to the given namespace.
- *
- * @var list<non-empty-string>
- */
- public $psalm_internal = [];
-
- /**
- * Whether or not the function is internal
- *
- * @var bool
- */
- public $internal = false;
-
- /**
- * Whether or not the function uses get_args
- *
- * @var bool
- */
- public $variadic = false;
-
- /**
- * Whether or not the function is pure
- *
- * @var bool
- */
- public $pure = false;
-
- /**
- * Whether or not to specialize a given call (useful for taint analysis)
- *
- * @var bool
- */
- public $specialize_call = false;
-
- /**
- * Represents the flow from function params to return type
- *
- * @var array<string>
- */
- public $flows = [];
-
- /**
- * @var array<string>
- */
- public $added_taints = [];
-
- /**
- * @var array<string>
- */
- public $removed_taints = [];
-
- /**
- * @var array<int, array{name:string, taint: string}>
- */
- public $taint_sink_params = [];
-
- /**
- * @var array<string>
- */
- public $taint_source_types = [];
-
- /**
- * @var array<int, array{name:string}>
- */
- public $assert_untainted_params = [];
-
- /**
- * Whether or not to ignore the nullability of this function's return type
- *
- * @var bool
- */
- public $ignore_nullable_return = false;
-
- /**
- * Whether or not to ignore the nullability of this function's return type
- *
- * @var bool
- */
- public $ignore_falsable_return = false;
-
- /**
- * @var array<int, string>
- */
- public $suppressed_issues = [];
-
- /**
- * @var array<int, array{0: string, 1: int, 2: int}>
- */
- public $throws = [];
-
- /**
- * @var array<int, array{string, ?string, ?string, bool}>
- */
- public $templates = [];
-
- /**
- * @var array<int, array{type: string, param_name: string}>
- */
- public $assertions = [];
-
- /**
- * @var array<int, array{type: string, param_name: string}>
- */
- public $if_true_assertions = [];
-
- /**
- * @var array<int, array{type: string, param_name: string}>
- */
- public $if_false_assertions = [];
-
- /**
- * @var bool
- */
- public $inheritdoc = false;
-
- /**
- * @var bool
- */
- public $mutation_free = false;
-
- /**
- * @var bool
- */
- public $external_mutation_free = false;
-
- /**
- * @var bool
- */
- public $no_named_args = false;
-
- /** @var bool */
- public $stub_override = false;
-
- /**
- * @var int
- */
- public $since_php_major_version = 0;
-
- /**
- * @var int
- */
- public $since_php_minor_version = 0;
-
- /**
- * @var ?string
- */
- public $description;
-
- /** @var array<string, array{lines:list<int>, suggested_replacement?:string}> */
- public $unexpected_tags = [];
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ParsedDocblock.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ParsedDocblock.php
deleted file mode 100644
index 26c5b01..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ParsedDocblock.php
+++ /dev/null
@@ -1,95 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-use function explode;
-use function trim;
-
-class ParsedDocblock
-{
- /** @var string */
- public $description;
-
- /** @var string */
- public $first_line_padding;
-
- /** @var array<string, array<int, string>> */
- public $tags = [];
-
- /** @var array<string, array<int, string>> */
- public $combined_tags = [];
-
- /**
- * @var bool
- */
- private static $shouldAddNewLineBetweenAnnotations = true;
-
- /** @param array<string, array<int, string>> $tags */
- public function __construct(string $description, array $tags, string $first_line_padding = '')
- {
- $this->description = $description;
- $this->tags = $tags;
- $this->first_line_padding = $first_line_padding;
- }
-
- public function render(string $left_padding): string
- {
- $doc_comment_text = '/**' . "\n";
-
- $trimmed_description = trim($this->description);
-
- if ($trimmed_description !== '') {
- $description_lines = explode("\n", $this->description);
-
- foreach ($description_lines as $line) {
- $doc_comment_text .= $left_padding . ' *' . (trim($line) ? ' ' . $line : '') . "\n";
- }
- }
-
- if ($this->tags) {
- if ($trimmed_description !== '') {
- $doc_comment_text .= $left_padding . ' *' . "\n";
- }
-
- $last_type = null;
-
- foreach ($this->tags as $type => $lines) {
- if ($last_type !== null
- && $last_type !== 'psalm-return'
- && static::shouldAddNewLineBetweenAnnotations()
- ) {
- $doc_comment_text .= $left_padding . ' *' . "\n";
- }
-
- foreach ($lines as $line) {
- $doc_comment_text .= $left_padding . ' * @' . $type . ($line !== '' ? ' ' . $line : '') . "\n";
- }
-
- $last_type = $type;
- }
- }
-
- $doc_comment_text .= $left_padding . ' */' . "\n" . $left_padding;
-
- return $doc_comment_text;
- }
-
- private static function shouldAddNewLineBetweenAnnotations(): bool
- {
- return static::$shouldAddNewLineBetweenAnnotations;
- }
-
- /**
- * Sets whether a new line should be added between the annotations or not.
- *
- */
- public static function addNewLineBetweenAnnotations(bool $should = true): void
- {
- static::$shouldAddNewLineBetweenAnnotations = $should;
- }
-
- public static function resetNewlineBetweenAnnotations(): void
- {
- static::$shouldAddNewLineBetweenAnnotations = true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php
deleted file mode 100644
index c412736..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/PhpStormMetaScanner.php
+++ /dev/null
@@ -1,398 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-use PhpParser;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Plugin\EventHandler\Event\FunctionReturnTypeProviderEvent;
-use Psalm\Plugin\EventHandler\Event\MethodReturnTypeProviderEvent;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Union;
-
-use function count;
-use function implode;
-use function is_string;
-use function str_replace;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class PhpStormMetaScanner
-{
- /**
- * @param list<PhpParser\Node\Arg> $args
- */
- public static function handleOverride(array $args, Codebase $codebase): void
- {
- if (count($args) < 2) {
- return;
- }
-
- $identifier = $args[0]->value;
-
- if (!$args[1]->value instanceof PhpParser\Node\Expr\FuncCall
- || !$args[1]->value->name instanceof PhpParser\Node\Name
- ) {
- return;
- }
-
- $map = [];
-
- if ($args[1]->value->name->parts === ['map']
- && $args[1]->value->getArgs()
- && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Expr\Array_
- ) {
- foreach ($args[1]->value->getArgs()[0]->value->items as $array_item) {
- if ($array_item
- && $array_item->key instanceof PhpParser\Node\Scalar\String_
- ) {
- if ($array_item->value instanceof PhpParser\Node\Expr\ClassConstFetch
- && $array_item->value->class instanceof PhpParser\Node\Name\FullyQualified
- && $array_item->value->name instanceof PhpParser\Node\Identifier
- && strtolower($array_item->value->name->name)
- ) {
- $map[$array_item->key->value] = new Union([
- new TNamedObject(implode('\\', $array_item->value->class->parts))
- ]);
- } elseif ($array_item->value instanceof PhpParser\Node\Scalar\String_) {
- $map[$array_item->key->value] = $array_item->value->value;
- }
- }
- }
- }
-
- $type_offset = null;
-
- if ($args[1]->value->name->parts === ['type']
- && $args[1]->value->getArgs()
- && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $type_offset = $args[1]->value->getArgs()[0]->value->value;
- }
-
- $element_type_offset = null;
-
- if ($args[1]->value->name->parts === ['elementType']
- && $args[1]->value->getArgs()
- && $args[1]->value->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $element_type_offset = $args[1]->value->getArgs()[0]->value->value;
- }
-
- if ($identifier instanceof PhpParser\Node\Expr\StaticCall
- && $identifier->class instanceof PhpParser\Node\Name\FullyQualified
- && $identifier->name instanceof PhpParser\Node\Identifier
- && $identifier->getArgs()
- && $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $meta_fq_classlike_name = implode('\\', $identifier->class->parts);
-
- $meta_method_name = strtolower($identifier->name->name);
-
- if ($map) {
- $offset = $identifier->getArgs()[0]->value->value;
-
- $codebase->methods->return_type_provider->registerClosure(
- $meta_fq_classlike_name,
- /**
- * @param list<PhpParser\Node\Arg> $call_args
- */
- function (
- MethodReturnTypeProviderEvent $event
- ) use (
- $map,
- $offset,
- $meta_fq_classlike_name,
- $meta_method_name
- ): ?Union {
- $statements_analyzer = $event->getSource();
- $call_args = $event->getCallArgs();
- $method_name = $event->getMethodNameLowercase();
- $fq_classlike_name = $event->getFqClasslikeName();
- if (!$statements_analyzer instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if ($meta_method_name !== $method_name
- || $meta_fq_classlike_name !== $fq_classlike_name
- ) {
- return null;
- }
-
- if (isset($call_args[$offset]->value)
- && ($call_arg_type = $statements_analyzer->node_data->getType($call_args[$offset]->value))
- && $call_arg_type->isSingleStringLiteral()
- ) {
- $offset_arg_value = $call_arg_type->getSingleStringLiteral()->value;
-
- if ($mapped_type = $map[$offset_arg_value] ?? null) {
- if ($mapped_type instanceof Union) {
- return clone $mapped_type;
- }
- }
-
- if (($mapped_type = $map[''] ?? null) && is_string($mapped_type)) {
- if (strpos($mapped_type, '@') !== false) {
- $mapped_type = str_replace('@', $offset_arg_value, $mapped_type);
-
- if (strpos($mapped_type, '.') === false) {
- return new Union([
- new TNamedObject($mapped_type)
- ]);
- }
- }
- }
- }
-
- return null;
- }
- );
- } elseif ($type_offset !== null) {
- $codebase->methods->return_type_provider->registerClosure(
- $meta_fq_classlike_name,
- /**
- * @param list<PhpParser\Node\Arg> $call_args
- */
- function (
- MethodReturnTypeProviderEvent $event
- ) use (
- $type_offset,
- $meta_fq_classlike_name,
- $meta_method_name
- ): ?Union {
- $statements_analyzer = $event->getSource();
- $call_args = $event->getCallArgs();
- $method_name = $event->getMethodNameLowercase();
- $fq_classlike_name = $event->getFqClasslikeName();
- if (!$statements_analyzer instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if ($meta_method_name !== $method_name
- || $meta_fq_classlike_name !== $fq_classlike_name
- ) {
- return null;
- }
-
- if (isset($call_args[$type_offset]->value)
- && ($call_arg_type
- = $statements_analyzer->node_data->getType($call_args[$type_offset]->value))
- ) {
- return clone $call_arg_type;
- }
-
- return null;
- }
- );
- } elseif ($element_type_offset !== null) {
- $codebase->methods->return_type_provider->registerClosure(
- $meta_fq_classlike_name,
- /**
- * @param list<PhpParser\Node\Arg> $call_args
- */
- function (
- MethodReturnTypeProviderEvent $event
- ) use (
- $element_type_offset,
- $meta_fq_classlike_name,
- $meta_method_name
- ): ?Union {
- $statements_analyzer = $event->getSource();
- $call_args = $event->getCallArgs();
- $method_name = $event->getMethodNameLowercase();
- $fq_classlike_name = $event->getFqClasslikeName();
- if (!$statements_analyzer instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if ($meta_method_name !== $method_name
- || $meta_fq_classlike_name !== $fq_classlike_name
- ) {
- return null;
- }
-
- if (isset($call_args[$element_type_offset]->value)
- && ($call_arg_type
- = $statements_analyzer->node_data->getType($call_args[$element_type_offset]->value))
- && $call_arg_type->hasArray()
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_atomic_type = $call_arg_type->getAtomicTypes()['array'];
-
- if ($array_atomic_type instanceof TKeyedArray) {
- return $array_atomic_type->getGenericValueType();
- }
-
- if ($array_atomic_type instanceof TList) {
- return $array_atomic_type->type_param;
- }
-
- return clone $array_atomic_type->type_params[1];
- }
-
- return null;
- }
- );
- }
- }
-
- if ($identifier instanceof PhpParser\Node\Expr\FuncCall
- && $identifier->name instanceof PhpParser\Node\Name\FullyQualified
- && $identifier->getArgs()
- && $identifier->getArgs()[0]->value instanceof PhpParser\Node\Scalar\LNumber
- ) {
- $function_id = strtolower(implode('\\', $identifier->name->parts));
-
- if ($map) {
- $offset = $identifier->getArgs()[0]->value->value;
-
- $codebase->functions->return_type_provider->registerClosure(
- $function_id,
- /**
- * @param non-empty-string $function_id
- * @param list<PhpParser\Node\Arg> $call_args
- */
- function (
- FunctionReturnTypeProviderEvent $event
- ) use (
- $map,
- $offset
- ): Union {
- $statements_analyzer = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- if (!$statements_analyzer instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (isset($call_args[$offset]->value)
- && ($call_arg_type
- = $statements_analyzer->node_data->getType($call_args[$offset]->value))
- && $call_arg_type->isSingleStringLiteral()
- ) {
- $offset_arg_value = $call_arg_type->getSingleStringLiteral()->value;
-
- if ($mapped_type = $map[$offset_arg_value] ?? null) {
- if ($mapped_type instanceof Union) {
- return clone $mapped_type;
- }
- }
-
- if (($mapped_type = $map[''] ?? null) && is_string($mapped_type)) {
- if (strpos($mapped_type, '@') !== false) {
- $mapped_type = str_replace('@', $offset_arg_value, $mapped_type);
-
- if (strpos($mapped_type, '.') === false) {
- return new Union([
- new TNamedObject($mapped_type)
- ]);
- }
- }
- }
- }
-
- $storage = $statements_analyzer->getCodebase()->functions->getStorage(
- $statements_analyzer,
- strtolower($function_id)
- );
-
- return $storage->return_type ?: Type::getMixed();
- }
- );
- } elseif ($type_offset !== null) {
- $codebase->functions->return_type_provider->registerClosure(
- $function_id,
- /**
- * @param non-empty-string $function_id
- * @param list<PhpParser\Node\Arg> $call_args
- */
- function (
- FunctionReturnTypeProviderEvent $event
- ) use (
- $type_offset
- ): Union {
- $statements_analyzer = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- if (!$statements_analyzer instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (isset($call_args[$type_offset]->value)
- && ($call_arg_type
- = $statements_analyzer->node_data->getType($call_args[$type_offset]->value))
- ) {
- return clone $call_arg_type;
- }
-
- $storage = $statements_analyzer->getCodebase()->functions->getStorage(
- $statements_analyzer,
- strtolower($function_id)
- );
-
- return $storage->return_type ?: Type::getMixed();
- }
- );
- } elseif ($element_type_offset !== null) {
- $codebase->functions->return_type_provider->registerClosure(
- $function_id,
- /**
- * @param non-empty-string $function_id
- * @param list<PhpParser\Node\Arg> $call_args
- */
- function (
- FunctionReturnTypeProviderEvent $event
- ) use (
- $element_type_offset
- ): Union {
- $statements_analyzer = $event->getStatementsSource();
- $call_args = $event->getCallArgs();
- $function_id = $event->getFunctionId();
- if (!$statements_analyzer instanceof StatementsAnalyzer) {
- return Type::getMixed();
- }
-
- if (isset($call_args[$element_type_offset]->value)
- && ($call_arg_type
- = $statements_analyzer->node_data->getType($call_args[$element_type_offset]->value))
- && $call_arg_type->hasArray()
- ) {
- /**
- * @psalm-suppress PossiblyUndefinedStringArrayOffset
- * @var TArray|TKeyedArray|TList
- */
- $array_atomic_type = $call_arg_type->getAtomicTypes()['array'];
-
- if ($array_atomic_type instanceof TKeyedArray) {
- return $array_atomic_type->getGenericValueType();
- }
-
- if ($array_atomic_type instanceof TList) {
- return $array_atomic_type->type_param;
- }
-
- return clone $array_atomic_type->type_params[1];
- }
-
- $storage = $statements_analyzer->getCodebase()->functions->getStorage(
- $statements_analyzer,
- strtolower($function_id)
- );
-
- return $storage->return_type ?: Type::getMixed();
- }
- );
- }
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php
deleted file mode 100644
index fde6296..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayOffsetFetch.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class ArrayOffsetFetch extends UnresolvedConstantComponent
-{
- /** @var UnresolvedConstantComponent */
- public $array;
-
- /** @var UnresolvedConstantComponent */
- public $offset;
-
- public function __construct(UnresolvedConstantComponent $left, UnresolvedConstantComponent $right)
- {
- $this->array = $left;
- $this->offset = $right;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php
deleted file mode 100644
index 1a1ec7b..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArraySpread.php
+++ /dev/null
@@ -1,19 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class ArraySpread extends UnresolvedConstantComponent
-{
- /** @var UnresolvedConstantComponent */
- public $array;
-
- public function __construct(UnresolvedConstantComponent $array)
- {
- $this->array = $array;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php
deleted file mode 100644
index e42888f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ArrayValue.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class ArrayValue extends UnresolvedConstantComponent
-{
- /** @var array<int, KeyValuePair|ArraySpread> */
- public $entries;
-
- /** @param list<KeyValuePair|ArraySpread> $entries */
- public function __construct(array $entries)
- {
- $this->entries = $entries;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php
deleted file mode 100644
index 7f789b9..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ClassConstant.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class ClassConstant extends UnresolvedConstantComponent
-{
- /** @var string */
- public $fqcln;
-
- /** @var string */
- public $name;
-
- public function __construct(string $fqcln, string $name)
- {
- $this->fqcln = $fqcln;
- $this->name = $name;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php
deleted file mode 100644
index a222fb0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/Constant.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class Constant extends UnresolvedConstantComponent
-{
- /** @var string */
- public $name;
-
- /** @var bool */
- public $is_fully_qualified;
-
- public function __construct(string $name, bool $is_fully_qualified)
- {
- $this->name = $name;
- $this->is_fully_qualified = $is_fully_qualified;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php
deleted file mode 100644
index 7ba4d8d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/KeyValuePair.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class KeyValuePair extends UnresolvedConstantComponent
-{
- /** @var ?UnresolvedConstantComponent */
- public $key;
-
- /** @var UnresolvedConstantComponent */
- public $value;
-
- public function __construct(?UnresolvedConstantComponent $key, UnresolvedConstantComponent $value)
- {
- $this->key = $key;
- $this->value = $value;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php
deleted file mode 100644
index 6fa9727..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/ScalarValue.php
+++ /dev/null
@@ -1,20 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class ScalarValue extends UnresolvedConstantComponent
-{
- /** @var string|int|float|bool|null */
- public $value;
-
- /** @param string|int|float|bool|null $value */
- public function __construct($value)
- {
- $this->value = $value;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php
deleted file mode 100644
index 90c2f9e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedAdditionOp.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedAdditionOp extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php
deleted file mode 100644
index 3662cc6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBinaryOp.php
+++ /dev/null
@@ -1,23 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-abstract class UnresolvedBinaryOp extends UnresolvedConstantComponent
-{
- /** @var UnresolvedConstantComponent */
- public $left;
-
- /** @var UnresolvedConstantComponent */
- public $right;
-
- public function __construct(UnresolvedConstantComponent $left, UnresolvedConstantComponent $right)
- {
- $this->left = $left;
- $this->right = $right;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php
deleted file mode 100644
index c4fb77a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseAnd.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedBitwiseAnd extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php
deleted file mode 100644
index 751fc5e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseOr.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedBitwiseOr extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php
deleted file mode 100644
index cf72dac..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedBitwiseXor.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedBitwiseXor extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php
deleted file mode 100644
index 72c8718..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedConcatOp.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedConcatOp extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php
deleted file mode 100644
index b990265..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedDivisionOp.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedDivisionOp extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php
deleted file mode 100644
index 76905bd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedMultiplicationOp.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedMultiplicationOp extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php
deleted file mode 100644
index 76a06cc..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedSubtractionOp.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedSubtractionOp extends UnresolvedBinaryOp
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php
deleted file mode 100644
index f9618f0..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/UnresolvedTernary.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner\UnresolvedConstant;
-
-use Psalm\Internal\Scanner\UnresolvedConstantComponent;
-
-/**
- * @psalm-immutable
- */
-class UnresolvedTernary extends UnresolvedConstantComponent
-{
- /** @var UnresolvedConstantComponent */
- public $cond;
- /** @var UnresolvedConstantComponent|null */
- public $if;
- /** @var UnresolvedConstantComponent */
- public $else;
-
- public function __construct(
- UnresolvedConstantComponent $cond,
- ?UnresolvedConstantComponent $if,
- UnresolvedConstantComponent $else
- ) {
- $this->cond = $cond;
- $this->if = $if;
- $this->else = $else;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php
deleted file mode 100644
index 5f96f7d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstantComponent.php
+++ /dev/null
@@ -1,10 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-/**
- * @psalm-immutable
- */
-abstract class UnresolvedConstantComponent
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/VarDocblockComment.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/VarDocblockComment.php
deleted file mode 100644
index 8797530..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/VarDocblockComment.php
+++ /dev/null
@@ -1,86 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scanner;
-
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class VarDocblockComment
-{
- /**
- * @var ?Union
- */
- public $type;
-
- /**
- * @var string|null
- */
- public $var_id;
-
- /**
- * @var int|null
- */
- public $line_number;
-
- /**
- * @var int|null
- */
- public $type_start;
-
- /**
- * @var int|null
- */
- public $type_end;
-
- /**
- * Whether or not the property is deprecated
- *
- * @var bool
- */
- public $deprecated = false;
-
- /**
- * Whether or not the property is internal
- *
- * @var bool
- */
- public $internal = false;
-
- /**
- * If set, the property is internal to the given namespace.
- *
- * @var list<non-empty-string>
- */
- public $psalm_internal = [];
-
- /**
- * Whether or not the property is readonly
- *
- * @var bool
- */
- public $readonly = false;
-
- /**
- * Whether or not to allow mutation by internal methods
- *
- * @var bool
- */
- public $allow_private_mutation = false;
-
- /**
- * @var list<string>
- */
- public $removed_taints = [];
-
- /**
- * @var array<int, string>
- */
- public $suppressed_issues = [];
-
- /**
- * @var ?string
- */
- public $description;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/CaseScope.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scope/CaseScope.php
deleted file mode 100644
index a7ce48e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/CaseScope.php
+++ /dev/null
@@ -1,32 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scope;
-
-use Psalm\Context;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class CaseScope
-{
- /**
- * @var Context
- */
- public $parent_context;
-
- /**
- * @var array<string, Union>|null
- */
- public $break_vars;
-
- public function __construct(Context $parent_context)
- {
- $this->parent_context = $parent_context;
- }
-
- public function __destruct()
- {
- unset($this->parent_context);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/FinallyScope.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scope/FinallyScope.php
deleted file mode 100644
index 0343f19..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/FinallyScope.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scope;
-
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class FinallyScope
-{
- /**
- * @var array<string, Union>
- */
- public $vars_in_scope = [];
-
- /**
- * @param array<string, Union> $vars_in_scope
- */
- public function __construct(array $vars_in_scope)
- {
- $this->vars_in_scope = $vars_in_scope;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfConditionalScope.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfConditionalScope.php
deleted file mode 100644
index 1f98135..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfConditionalScope.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scope;
-
-use Psalm\Context;
-use Psalm\Internal\Clause;
-
-/**
- * @internal
- */
-class IfConditionalScope
-{
- /** @var Context */
- public $if_context;
-
- /** @var Context */
- public $post_if_context;
-
- /**
- * @var array<string, bool>
- */
- public $cond_referenced_var_ids;
-
- /**
- * @var array<string, int>
- */
- public $assigned_in_conditional_var_ids;
-
- /** @var list<Clause> */
- public $entry_clauses;
-
- /**
- * @param array<string, bool> $cond_referenced_var_ids
- * @param array<string, int> $assigned_in_conditional_var_ids
- * @param list<Clause> $entry_clauses
- */
- public function __construct(
- Context $if_context,
- Context $post_if_context,
- array $cond_referenced_var_ids,
- array $assigned_in_conditional_var_ids,
- array $entry_clauses
- ) {
- $this->if_context = $if_context;
- $this->post_if_context = $post_if_context;
- $this->cond_referenced_var_ids = $cond_referenced_var_ids;
- $this->assigned_in_conditional_var_ids = $assigned_in_conditional_var_ids;
- $this->entry_clauses = $entry_clauses;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfScope.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfScope.php
deleted file mode 100644
index 314d93f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/IfScope.php
+++ /dev/null
@@ -1,87 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scope;
-
-use Psalm\Context;
-use Psalm\Internal\Clause;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class IfScope
-{
- /**
- * @var array<string, Union>|null
- */
- public $new_vars;
-
- /**
- * @var array<string, bool>
- */
- public $new_vars_possibly_in_scope = [];
-
- /**
- * @var array<string, Union>|null
- */
- public $redefined_vars;
-
- /**
- * @var array<string, int>|null
- */
- public $assigned_var_ids;
-
- /**
- * @var array<string, bool>
- */
- public $possibly_assigned_var_ids = [];
-
- /**
- * @var array<string, Union>
- */
- public $possibly_redefined_vars = [];
-
- /**
- * @var array<string, bool>
- */
- public $updated_vars = [];
-
- /**
- * @var array<string, array<int, array<int, string>>>
- */
- public $negated_types = [];
-
- /**
- * @var array<string, bool>
- */
- public $if_cond_changed_var_ids = [];
-
- /**
- * @var array<string, string>|null
- */
- public $negatable_if_types;
-
- /**
- * @var list<Clause>
- */
- public $negated_clauses = [];
-
- /**
- * These are the set of clauses that could be applied after the `if`
- * statement, if the `if` statement contains branches with leaving statements,
- * and the else leaves too
- *
- * @var list<Clause>
- */
- public $reasonable_clauses = [];
-
- /**
- * @var string[]
- */
- public $final_actions = [];
-
- /**
- * @var ?Context
- */
- public $post_leaving_if_context;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/LoopScope.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scope/LoopScope.php
deleted file mode 100644
index 4bbb208..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/LoopScope.php
+++ /dev/null
@@ -1,74 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scope;
-
-use Psalm\Context;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class LoopScope
-{
- /**
- * @var int
- */
- public $iteration_count = 0;
-
- /**
- * @var Context
- */
- public $loop_context;
-
- /**
- * @var Context
- */
- public $loop_parent_context;
-
- /**
- * @var array<string, Union>|null
- */
- public $redefined_loop_vars = [];
-
- /**
- * @var array<string, Union>
- */
- public $possibly_redefined_loop_vars = [];
-
- /**
- * @var array<string, Union>|null
- */
- public $possibly_redefined_loop_parent_vars;
-
- /**
- * @var array<string, Union>
- */
- public $possibly_defined_loop_parent_vars = [];
-
- /**
- * @var array<string, bool>
- */
- public $vars_possibly_in_scope = [];
-
- /**
- * @var array<string, bool>
- */
- public $protected_var_ids = [];
-
- /**
- * @var string[]
- */
- public $final_actions = [];
-
- public function __construct(Context $loop_context, Context $parent_context)
- {
- $this->loop_context = $loop_context;
- $this->loop_parent_context = $parent_context;
- }
-
- public function __destruct()
- {
- unset($this->loop_context);
- unset($this->loop_parent_context);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/SwitchScope.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scope/SwitchScope.php
deleted file mode 100644
index 35bb99c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Scope/SwitchScope.php
+++ /dev/null
@@ -1,53 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Scope;
-
-use PhpParser;
-use Psalm\Internal\Clause;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class SwitchScope
-{
- /**
- * @var array<string, Union>|null
- */
- public $new_vars_in_scope;
-
- /**
- * @var array<string, bool>
- */
- public $new_vars_possibly_in_scope = [];
-
- /**
- * @var array<string, Union>|null
- */
- public $redefined_vars;
-
- /**
- * @var array<string, Union>|null
- */
- public $possibly_redefined_vars;
-
- /**
- * @var array<PhpParser\Node\Stmt>
- */
- public $leftover_statements = [];
-
- /**
- * @var PhpParser\Node\Expr|null
- */
- public $leftover_case_equality_expr;
-
- /**
- * @var list<Clause>
- */
- public $negated_clauses = [];
-
- /**
- * @var array<string, bool>|null
- */
- public $new_assigned_var_ids;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php
deleted file mode 100644
index 6bc8130..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/ClassLikeStubGenerator.php
+++ /dev/null
@@ -1,314 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Stubs\Generator;
-
-use PhpParser;
-use Psalm\Codebase;
-use Psalm\Internal\Codebase\ConstantTypeResolver;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\Stmt\VirtualClass;
-use Psalm\Node\Stmt\VirtualClassConst;
-use Psalm\Node\Stmt\VirtualClassMethod;
-use Psalm\Node\Stmt\VirtualInterface;
-use Psalm\Node\Stmt\VirtualProperty;
-use Psalm\Node\Stmt\VirtualPropertyProperty;
-use Psalm\Node\Stmt\VirtualTrait;
-use Psalm\Node\VirtualConst;
-use Psalm\Storage\ClassLikeStorage;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\Type;
-use Psalm\Type\Union;
-
-use ReflectionProperty;
-use UnexpectedValueException;
-use function array_slice;
-use function rtrim;
-
-class ClassLikeStubGenerator
-{
- /**
- * @return PhpParser\Node\Stmt\Class_|PhpParser\Node\Stmt\Interface_|PhpParser\Node\Stmt\Trait_
- */
- public static function getClassLikeNode(
- Codebase $codebase,
- ClassLikeStorage $storage,
- string $classlike_name
- ) : PhpParser\Node\Stmt\ClassLike {
- $subnodes = [
- 'stmts' => array_merge(
- self::getConstantNodes($codebase, $storage),
- self::getPropertyNodes($storage),
- self::getMethodNodes($storage)
- )
- ];
-
- $docblock = new ParsedDocblock('', []);
-
- $template_offset = 0;
-
- foreach ($storage->template_types ?: [] as $template_name => $map) {
- $type = array_values($map)[0];
-
- $key = isset($storage->template_covariants[$template_offset]) ? 'template-covariant' : 'template';
-
- $docblock->tags[$key][] = $template_name . ' as ' . $type->toNamespacedString(
- null,
- [],
- null,
- false
- );
-
- $template_offset++;
- }
-
- $attrs = [
- 'comments' => $docblock->tags
- ? [
- new PhpParser\Comment\Doc(
- rtrim($docblock->render(' '))
- )
- ]
- : []
- ];
-
- if ($storage->is_interface) {
- if ($storage->direct_interface_parents) {
- $subnodes['extends'] = [];
-
- foreach ($storage->direct_interface_parents as $direct_interface_parent) {
- $subnodes['extends'][] = new VirtualFullyQualified($direct_interface_parent);
- }
- }
-
- return new VirtualInterface(
- $classlike_name,
- $subnodes,
- $attrs
- );
- }
-
- if ($storage->is_trait) {
- return new VirtualTrait(
- $classlike_name,
- $subnodes,
- $attrs
- );
- }
-
- if ($storage->parent_class) {
- $subnodes['extends'] = new VirtualFullyQualified($storage->parent_class);
- } else
-
- if ($storage->direct_class_interfaces) {
- $subnodes['implements'] = [];
- foreach ($storage->direct_class_interfaces as $direct_class_interface) {
- $subnodes['implements'][] = new VirtualFullyQualified($direct_class_interface);
- }
- }
-
- return new VirtualClass(
- $classlike_name,
- $subnodes,
- $attrs
- );
- }
-
- /**
- * @return list<PhpParser\Node\Stmt\ClassConst>
- */
- private static function getConstantNodes(Codebase $codebase, ClassLikeStorage $storage): array
- {
- $constant_nodes = [];
-
- foreach ($storage->constants as $constant_name => $constant_storage) {
- if ($constant_storage->unresolved_node) {
- $type = new Union([
- ConstantTypeResolver::resolve(
- $codebase->classlikes,
- $constant_storage->unresolved_node
- )
- ]);
- } elseif ($constant_storage->type) {
- $type = $constant_storage->type;
- } else {
- throw new UnexpectedValueException('bad');
- }
-
- $constant_nodes[] = new VirtualClassConst(
- [
- new VirtualConst(
- $constant_name,
- StubsGenerator::getExpressionFromType($type)
- )
- ],
- $constant_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PUBLIC
- ? PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC
- : ($constant_storage->visibility === ClassLikeAnalyzer::VISIBILITY_PROTECTED
- ? PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED
- : PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE)
- );
- }
-
- return $constant_nodes;
- }
-
- /**
- * @return list<PhpParser\Node\Stmt\Property>
- */
- private static function getPropertyNodes(ClassLikeStorage $storage): array
- {
- $namespace_name = implode('\\', array_slice(explode('\\', $storage->name), 0, -1));
-
- $property_nodes = [];
-
- foreach ($storage->properties as $property_name => $property_storage) {
- switch ($property_storage->visibility) {
- case ClassLikeAnalyzer::VISIBILITY_PRIVATE:
- $flag = PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE;
- break;
- case ClassLikeAnalyzer::VISIBILITY_PROTECTED:
- $flag = PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED;
- break;
- default:
- $flag = PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC;
- break;
- }
-
- $docblock = new ParsedDocblock('', []);
-
- if ($property_storage->type
- && $property_storage->signature_type !== $property_storage->type
- ) {
- $docblock->tags['var'][] = $property_storage->type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- $property_nodes[] = new VirtualProperty(
- $flag | ($property_storage->is_static ? PhpParser\Node\Stmt\Class_::MODIFIER_STATIC : 0),
- [
- new VirtualPropertyProperty(
- $property_name,
- $property_storage->suggested_type
- ? StubsGenerator::getExpressionFromType($property_storage->suggested_type)
- : null
- )
- ],
- [
- 'comments' => $docblock->tags
- ? [
- new PhpParser\Comment\Doc(
- rtrim($docblock->render(' '))
- )
- ]
- : []
- ],
- $property_storage->signature_type
- ? StubsGenerator::getParserTypeFromPsalmType($property_storage->signature_type)
- : null
- );
- }
-
- return $property_nodes;
- }
-
- /**
- * @return list<PhpParser\Node\Stmt\ClassMethod>
- */
- private static function getMethodNodes(ClassLikeStorage $storage): array {
- $namespace_name = implode('\\', array_slice(explode('\\', $storage->name), 0, -1));
- $method_nodes = [];
-
- foreach ($storage->methods as $method_storage) {
- if (!$method_storage->cased_name) {
- throw new UnexpectedValueException('very bad');
- }
-
- switch ($method_storage->visibility) {
- case ReflectionProperty::IS_PRIVATE:
- $flag = PhpParser\Node\Stmt\Class_::MODIFIER_PRIVATE;
- break;
- case ReflectionProperty::IS_PROTECTED:
- $flag = PhpParser\Node\Stmt\Class_::MODIFIER_PROTECTED;
- break;
- default:
- $flag = PhpParser\Node\Stmt\Class_::MODIFIER_PUBLIC;
- break;
- }
-
- $docblock = new ParsedDocblock('', []);
-
- foreach ($method_storage->template_types ?: [] as $template_name => $map) {
- $type = array_values($map)[0];
-
- $docblock->tags['template'][] = $template_name . ' as ' . $type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- foreach ($method_storage->params as $param) {
- if ($param->type && $param->type !== $param->signature_type) {
- $docblock->tags['param'][] = $param->type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- ) . ' $' . $param->name;
- }
- }
-
- if ($method_storage->return_type
- && $method_storage->signature_return_type !== $method_storage->return_type
- ) {
- $docblock->tags['return'][] = $method_storage->return_type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- foreach ($method_storage->throws ?: [] as $exception_name => $_) {
- $docblock->tags['throws'][] = Type::getStringFromFQCLN(
- $exception_name,
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- $method_nodes[] = new VirtualClassMethod(
- $method_storage->cased_name,
- [
- 'flags' => $flag
- | ($method_storage->is_static ? PhpParser\Node\Stmt\Class_::MODIFIER_STATIC : 0)
- | ($method_storage->abstract ? PhpParser\Node\Stmt\Class_::MODIFIER_ABSTRACT : 0),
- 'params' => StubsGenerator::getFunctionParamNodes($method_storage),
- 'returnType' => $method_storage->signature_return_type
- ? StubsGenerator::getParserTypeFromPsalmType($method_storage->signature_return_type)
- : null,
- 'stmts' => $storage->is_interface || $method_storage->abstract ? null : [],
- ],
- [
- 'comments' => $docblock->tags
- ? [
- new PhpParser\Comment\Doc(
- rtrim($docblock->render(' '))
- )
- ]
- : []
- ]
- );
- }
-
- return $method_nodes;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php
deleted file mode 100644
index 23b1ca1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Stubs/Generator/StubsGenerator.php
+++ /dev/null
@@ -1,480 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Stubs\Generator;
-
-use Psalm\Codebase;
-use Psalm\Internal\Provider\ClassLikeStorageProvider;
-use Psalm\Internal\Provider\FileStorageProvider;
-use Psalm\Storage\FunctionLikeStorage;
-use Psalm\Type\Atomic\TAnonymousClassInstance;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TAssertionFalsy;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TCallableList;
-use Psalm\Type\Atomic\TCallableObject;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TClosedResource;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TDependentGetDebugType;
-use Psalm\Type\Atomic\TDependentGetType;
-use Psalm\Type\Atomic\TDependentListKey;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TEmptyMixed;
-use Psalm\Type\Atomic\TEmptyNumeric;
-use Psalm\Type\Atomic\TEmptyScalar;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntMask;
-use Psalm\Type\Atomic\TIntMaskOf;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyOfClassConstant;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNonEmptyLowercaseString;
-use Psalm\Type\Atomic\TNonEmptyMixed;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyScalar;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonFalsyString;
-use Psalm\Type\Atomic\TNonspecificLiteralInt;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TResource;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateIndexedAccess;
-use Psalm\Type\Atomic\TTemplateKeyOf;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Atomic\TTraitString;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Atomic\TTypeAlias;
-use Psalm\Type\Atomic\TValueOfClassConstant;
-use Psalm\Type\Atomic\TVoid;
-use Psalm\Type\Atomic\Scalar;
-use PhpParser;
-use Psalm\Internal\Scanner\ParsedDocblock;
-use Psalm\Node\Expr\VirtualArray;
-use Psalm\Node\Expr\VirtualArrayItem;
-use Psalm\Node\Expr\VirtualClassConstFetch;
-use Psalm\Node\Expr\VirtualConstFetch;
-use Psalm\Node\Expr\VirtualVariable;
-use Psalm\Node\Name\VirtualFullyQualified;
-use Psalm\Node\Scalar\VirtualDNumber;
-use Psalm\Node\Scalar\VirtualLNumber;
-use Psalm\Node\Scalar\VirtualString;
-use Psalm\Node\Stmt\VirtualFunction;
-use Psalm\Node\Stmt\VirtualNamespace;
-use Psalm\Node\VirtualConst;
-use Psalm\Node\Stmt\VirtualConst as StmtVirtualConst_;
-use Psalm\Node\VirtualIdentifier;
-use Psalm\Node\VirtualName;
-use Psalm\Node\VirtualNullableType;
-use Psalm\Node\VirtualParam;
-use Psalm\Type;
-use Psalm\Type\Union;
-
-use UnexpectedValueException;
-use function dirname;
-use function is_int;
-use function rtrim;
-use function strpos;
-
-class StubsGenerator
-{
- public static function getAll(
- Codebase $codebase,
- ClassLikeStorageProvider $class_provider,
- FileStorageProvider $file_provider
- ): string {
- $namespaced_nodes = [];
-
- $psalm_base = dirname(__DIR__, 5);
-
- foreach ($class_provider->getAll() as $storage) {
- if (strpos($storage->name, 'Psalm\\') === 0) {
- continue;
- }
-
- if ($storage->location
- && strpos($storage->location->file_path, $psalm_base) === 0
- ) {
- continue;
- }
-
- if ($storage->stubbed) {
- continue;
- }
-
- $name_parts = explode('\\', $storage->name);
-
- $classlike_name = array_pop($name_parts);
- $namespace_name = implode('\\', $name_parts);
-
- if (!isset($namespaced_nodes[$namespace_name])) {
- $namespaced_nodes[$namespace_name] = [];
- }
-
- $namespaced_nodes[$namespace_name][$classlike_name] = ClassLikeStubGenerator::getClassLikeNode(
- $codebase,
- $storage,
- $classlike_name
- );
- }
-
- $all_function_names = [];
-
- foreach ($codebase->functions->getAllStubbedFunctions() as $function_storage) {
- if ($function_storage->location
- && strpos($function_storage->location->file_path, $psalm_base) === 0
- ) {
- continue;
- }
-
- if (!$function_storage->cased_name) {
- throw new UnexpectedValueException('very bad');
- }
-
- $fq_name = $function_storage->cased_name;
-
- $all_function_names[$fq_name] = true;
-
- $name_parts = explode('\\', $fq_name);
- $function_name = array_pop($name_parts);
-
- $namespace_name = implode('\\', $name_parts);
-
- $namespaced_nodes[$namespace_name][$fq_name] = self::getFunctionNode(
- $function_storage,
- $function_name,
- $namespace_name
- );
- }
-
- foreach ($codebase->getAllStubbedConstants() as $fq_name => $type) {
- if ($type->isMixed()) {
- continue;
- }
-
- $name_parts = explode('\\', $fq_name);
- $constant_name = array_pop($name_parts);
-
- $namespace_name = implode('\\', $name_parts);
-
- $namespaced_nodes[$namespace_name][$fq_name] = new StmtVirtualConst_(
- [
- new VirtualConst(
- $constant_name,
- self::getExpressionFromType($type)
- )
- ]
- );
- }
-
- foreach ($file_provider->getAll() as $file_storage) {
- if (strpos($file_storage->file_path, $psalm_base) === 0) {
- continue;
- }
-
- foreach ($file_storage->functions as $function_storage) {
- if (!$function_storage->cased_name) {
- continue;
- }
-
- $fq_name = $function_storage->cased_name;
-
- if (isset($all_function_names[$fq_name])) {
- continue;
- }
-
- $all_function_names[$fq_name] = true;
-
- $name_parts = explode('\\', $fq_name);
- $function_name = array_pop($name_parts);
-
- $namespace_name = implode('\\', $name_parts);
-
- $namespaced_nodes[$namespace_name][$fq_name] = self::getFunctionNode(
- $function_storage,
- $function_name,
- $namespace_name
- );
- }
-
- foreach ($file_storage->constants as $fq_name => $type) {
- if ($type->isMixed()) {
- continue;
- }
-
- if ($type->isMixed()) {
- continue;
- }
-
- $name_parts = explode('\\', $fq_name);
- $constant_name = array_pop($name_parts);
-
- $namespace_name = implode('\\', $name_parts);
-
- $namespaced_nodes[$namespace_name][$fq_name] = new StmtVirtualConst_(
- [
- new VirtualConst(
- $constant_name,
- self::getExpressionFromType($type)
- )
- ]
- );
- }
- }
-
- ksort($namespaced_nodes);
-
- $namespace_stmts = [];
-
- foreach ($namespaced_nodes as $namespace_name => $stmts) {
- ksort($stmts);
-
- $namespace_stmts[] = new VirtualNamespace(
- $namespace_name ? new VirtualName($namespace_name) : null,
- array_values($stmts),
- ['kind' => PhpParser\Node\Stmt\Namespace_::KIND_BRACED]
- );
- }
-
- $prettyPrinter = new PhpParser\PrettyPrinter\Standard;
- return $prettyPrinter->prettyPrintFile($namespace_stmts);
- }
-
- private static function getFunctionNode(
- FunctionLikeStorage $function_storage,
- string $function_name,
- string $namespace_name
- ) : PhpParser\Node\Stmt\Function_ {
- $docblock = new ParsedDocblock('', []);
-
- foreach ($function_storage->template_types ?: [] as $template_name => $map) {
- $type = array_values($map)[0];
-
- $docblock->tags['template'][] = $template_name . ' as ' . $type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- foreach ($function_storage->params as $param) {
- if ($param->type && $param->type !== $param->signature_type) {
- $docblock->tags['param'][] = $param->type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- ) . ' $' . $param->name;
- }
- }
-
- if ($function_storage->return_type
- && $function_storage->signature_return_type !== $function_storage->return_type
- ) {
- $docblock->tags['return'][] = $function_storage->return_type->toNamespacedString(
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- foreach ($function_storage->throws ?: [] as $exception_name => $_) {
- $docblock->tags['throws'][] = Type::getStringFromFQCLN(
- $exception_name,
- $namespace_name,
- [],
- null,
- false
- );
- }
-
- return new VirtualFunction(
- $function_name,
- [
- 'params' => self::getFunctionParamNodes($function_storage),
- 'returnType' => $function_storage->signature_return_type
- ? self::getParserTypeFromPsalmType($function_storage->signature_return_type)
- : null,
- 'stmts' => [],
- ],
- [
- 'comments' => $docblock->tags
- ? [
- new PhpParser\Comment\Doc(
- rtrim($docblock->render(' '))
- )
- ]
- : []
- ]
- );
- }
-
- /**
- * @return list<PhpParser\Node\Param>
- */
- public static function getFunctionParamNodes(FunctionLikeStorage $method_storage): array
- {
- $param_nodes = [];
-
- foreach ($method_storage->params as $param) {
- $param_nodes[] = new VirtualParam(
- new VirtualVariable($param->name),
- $param->default_type instanceof Union
- ? self::getExpressionFromType($param->default_type)
- : null,
- $param->signature_type
- ? self::getParserTypeFromPsalmType($param->signature_type)
- : null,
- $param->by_ref,
- $param->is_variadic
- );
- }
-
- return $param_nodes;
- }
-
- /**
- * @return PhpParser\Node\Identifier|PhpParser\Node\Name\FullyQualified|PhpParser\Node\NullableType|null
- */
- public static function getParserTypeFromPsalmType(Union $type): ?PhpParser\NodeAbstract
- {
- $nullable = $type->isNullable();
-
- foreach ($type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TNull) {
- continue;
- }
-
- if ($atomic_type instanceof Scalar
- || $atomic_type instanceof TObject
- || $atomic_type instanceof TArray
- || $atomic_type instanceof TIterable
- ) {
- $identifier_string = $atomic_type->toPhpString(null, [], null, 8, 0);
-
- if ($identifier_string === null) {
- throw new UnexpectedValueException(
- $atomic_type->getId() . ' could not be converted to an identifier'
- );
- }
- $identifier = new VirtualIdentifier($identifier_string);
-
- if ($nullable) {
- return new VirtualNullableType($identifier);
- }
-
- return $identifier;
- }
-
- if ($atomic_type instanceof TNamedObject) {
- $name_node = new VirtualFullyQualified($atomic_type->value);
-
- if ($nullable) {
- return new VirtualNullableType($name_node);
- }
-
- return $name_node;
- }
- }
-
- return null;
- }
-
- public static function getExpressionFromType(Union $type) : PhpParser\Node\Expr
- {
- foreach ($type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TLiteralClassString) {
- return new VirtualClassConstFetch(new VirtualName('\\' . $atomic_type->value), new VirtualIdentifier('class'));
- }
-
- if ($atomic_type instanceof TLiteralString) {
- return new VirtualString($atomic_type->value);
- }
-
- if ($atomic_type instanceof TLiteralInt) {
- return new VirtualLNumber($atomic_type->value);
- }
-
- if ($atomic_type instanceof TLiteralFloat) {
- return new VirtualDNumber($atomic_type->value);
- }
-
- if ($atomic_type instanceof TFalse) {
- return new VirtualConstFetch(new VirtualName('false'));
- }
-
- if ($atomic_type instanceof TTrue) {
- return new VirtualConstFetch(new VirtualName('true'));
- }
-
- if ($atomic_type instanceof TNull) {
- return new VirtualConstFetch(new VirtualName('null'));
- }
-
- if ($atomic_type instanceof TArray) {
- return new VirtualArray([]);
- }
-
- if ($atomic_type instanceof TKeyedArray) {
- $new_items = [];
-
- foreach ($atomic_type->properties as $property_name => $property_type) {
- if ($atomic_type->is_list) {
- $key_type = null;
- } elseif (is_int($property_name)) {
- $key_type = new VirtualLNumber($property_name);
- } else {
- $key_type = new VirtualString($property_name);
- }
-
- $new_items[] = new VirtualArrayItem(
- self::getExpressionFromType($property_type),
- $key_type
- );
- }
-
- return new VirtualArray($new_items);
- }
-
- if ($atomic_type instanceof TEnumCase) {
- return new VirtualClassConstFetch(new VirtualName('\\' . $atomic_type->value), new VirtualIdentifier($atomic_type->case_name));
- }
- }
-
- return new VirtualString('Psalm could not infer this type');
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ArrayType.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ArrayType.php
deleted file mode 100644
index 0023880..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ArrayType.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-declare(strict_types=1);
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class ArrayType
-{
- /** @var Union */
- public $key;
-
- /** @var Union */
- public $value;
-
- /** @var bool */
- public $is_list;
-
- public function __construct(Union $key, Union $value, bool $is_list)
- {
- $this->key = $key;
- $this->value = $value;
- $this->is_list = $is_list;
- }
-
- public static function infer(Atomic $type): ?self
- {
- if ($type instanceof TKeyedArray) {
- return new self(
- $type->getGenericKeyType(),
- $type->getGenericValueType(),
- $type->is_list
- );
- }
-
- if ($type instanceof TList) {
- return new self(
- Type::getInt(),
- $type->type_param,
- true
- );
- }
-
- if ($type instanceof TArray) {
- return new self(
- $type->type_params[0],
- $type->type_params[1],
- false
- );
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/AssertionReconciler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/AssertionReconciler.php
deleted file mode 100644
index d1611ac..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/AssertionReconciler.php
+++ /dev/null
@@ -1,1816 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Exception;
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Analyzer\Statements\Expression\Fetch\VariableFetchAnalyzer;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
-use Psalm\Internal\Type\Comparator\TypeComparisonResult;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Issue\DocblockTypeContradiction;
-use Psalm\Issue\InvalidDocblock;
-use Psalm\Issue\TypeDoesNotContainNull;
-use Psalm\Issue\TypeDoesNotContainType;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function array_intersect_key;
-use function array_merge;
-use function count;
-use function explode;
-use function get_class;
-use function is_string;
-use function strpos;
-use function substr;
-
-class AssertionReconciler extends Reconciler
-{
- /**
- * Reconciles types
- *
- * think of this as a set of functions e.g. empty(T), notEmpty(T), null(T), notNull(T) etc. where
- * - empty(Object) => null,
- * - empty(bool) => false,
- * - notEmpty(Object|null) => Object,
- * - notEmpty(Object|false) => Object
- *
- * @param string[] $suppressed_issues
- * @param array<string, array<string, Union>> $template_type_map
- * @param-out Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- public static function reconcile(
- string $assertion,
- ?Union $existing_var_type,
- ?string $key,
- StatementsAnalyzer $statements_analyzer,
- bool $inside_loop,
- array $template_type_map,
- ?CodeLocation $code_location = null,
- array $suppressed_issues = [],
- ?int &$failed_reconciliation = Reconciler::RECONCILIATION_OK,
- bool $negated = false
- ): Union {
- $codebase = $statements_analyzer->getCodebase();
-
- $is_strict_equality = false;
- $is_loose_equality = false;
- $is_equality = false;
- $is_negation = false;
- $failed_reconciliation = Reconciler::RECONCILIATION_OK;
-
- if ($assertion[0] === '!') {
- $assertion = substr($assertion, 1);
- $is_negation = true;
- }
-
- if ($assertion[0] === '=') {
- $assertion = substr($assertion, 1);
- $is_strict_equality = true;
- $is_equality = true;
- }
-
- if ($assertion[0] === '~') {
- $assertion = substr($assertion, 1);
- $is_loose_equality = true;
- $is_equality = true;
- }
-
- $original_assertion = $assertion;
-
- if ($assertion[0] === '@') {
- $assertion = 'falsy';
- $is_negation = true;
- }
-
- if ($existing_var_type === null
- && is_string($key)
- && VariableFetchAnalyzer::isSuperGlobal($key)
- ) {
- $existing_var_type = VariableFetchAnalyzer::getGlobalType($key);
- }
-
- if ($existing_var_type === null) {
- return self::getMissingType(
- $assertion,
- $is_negation,
- $inside_loop,
- $is_equality,
- $template_type_map
- );
- }
-
- $old_var_type_string = $existing_var_type->getId();
-
- if ($is_negation) {
- return NegatedAssertionReconciler::reconcile(
- $statements_analyzer,
- $assertion,
- $is_strict_equality,
- $is_loose_equality,
- $existing_var_type,
- $template_type_map,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $inside_loop
- );
- }
-
- $simply_reconciled_type = SimpleAssertionReconciler::reconcile(
- $assertion,
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $is_strict_equality,
- $inside_loop
- );
-
- if ($simply_reconciled_type) {
- return $simply_reconciled_type;
- }
-
- if (strpos($assertion, 'isa-') === 0) {
- $should_return = false;
-
- $new_type = self::handleIsA(
- $codebase,
- $existing_var_type,
- $assertion,
- $template_type_map,
- $code_location,
- $key,
- $suppressed_issues,
- $should_return
- );
-
- if ($should_return) {
- return $new_type;
- }
- } elseif (strpos($assertion, 'getclass-') === 0) {
- $assertion = substr($assertion, 9);
- $new_type = Type::parseString($assertion, null, $template_type_map);
- } else {
- $bracket_pos = strpos($assertion, '(');
-
- if ($bracket_pos) {
- return self::handleLiteralEquality(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- $is_loose_equality,
- $existing_var_type,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($assertion === 'loaded-class-string') {
- $assertion = 'class-string';
- }
-
- try {
- $new_type = Type::parseString($assertion, null, $template_type_map);
- } catch (TypeParseTreeException $e) {
- $new_type = Type::getMixed();
- }
- }
-
- if ($existing_var_type->hasMixed()) {
- if ($is_loose_equality
- && $new_type->hasScalarType()
- ) {
- return $existing_var_type;
- }
-
- return $new_type;
- }
-
- $refined_type = self::refine(
- $statements_analyzer,
- $assertion,
- $original_assertion,
- $new_type,
- $existing_var_type,
- $template_type_map,
- $key,
- $negated,
- $code_location,
- $is_equality,
- $is_loose_equality,
- $suppressed_issues,
- $failed_reconciliation
- );
-
- return TypeExpander::expandUnion(
- $codebase,
- $refined_type,
- null,
- null,
- null,
- true,
- false,
- false,
- true
- );
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- */
- private static function getMissingType(
- string $assertion,
- bool $is_negation,
- bool $inside_loop,
- bool $is_equality,
- array $template_type_map
- ): Union {
- if (($assertion === 'isset' && !$is_negation)
- || ($assertion === 'empty' && $is_negation)
- ) {
- return Type::getMixed($inside_loop);
- }
-
- if ($assertion === 'array-key-exists'
- || $assertion === 'non-empty-countable'
- || strpos($assertion, 'has-at-least-') === 0
- || strpos($assertion, 'has-exactly-') === 0
- ) {
- return Type::getMixed();
- }
-
- if (!$is_negation && $assertion !== 'falsy' && $assertion !== 'empty') {
- if ($is_equality) {
- $bracket_pos = strpos($assertion, '(');
-
- if ($bracket_pos) {
- $assertion = substr($assertion, 0, $bracket_pos);
- }
- }
-
- try {
- return Type::parseString($assertion, null, $template_type_map);
- } catch (Exception $e) {
- return Type::getMixed();
- }
- }
-
- return Type::getMixed();
- }
-
- /**
- * This method is called when SimpleAssertionReconciler was not enough. It receives the existing type, the assertion
- * and also a new type created from the assertion string.
- *
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- * @param string[] $suppressed_issues
- * @param array<string, array<string, Union>> $template_type_map
- * @param-out Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function refine(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- string $original_assertion,
- Union $new_type,
- Union $existing_var_type,
- array $template_type_map,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- bool $is_equality,
- bool $is_loose_equality,
- array $suppressed_issues,
- int &$failed_reconciliation
- ): Union {
- $codebase = $statements_analyzer->getCodebase();
-
- $old_var_type_string = $existing_var_type->getId();
-
- $new_type_has_interface = false;
-
- if ($new_type->hasObjectType()) {
- foreach ($new_type->getAtomicTypes() as $new_type_part) {
- if ($new_type_part instanceof TNamedObject &&
- $codebase->interfaceExists($new_type_part->value)
- ) {
- $new_type_has_interface = true;
- break;
- }
- }
- }
-
- $old_type_has_interface = false;
-
- if ($existing_var_type->hasObjectType()) {
- foreach ($existing_var_type->getAtomicTypes() as $existing_type_part) {
- if ($existing_type_part instanceof TNamedObject &&
- $codebase->interfaceExists($existing_type_part->value)
- ) {
- $old_type_has_interface = true;
- break;
- }
- }
- }
-
- try {
- if (strpos($assertion, '<') || strpos($assertion, '[') || strpos($assertion, '{')) {
- $new_type_union = Type::parseString($assertion);
-
- $new_type_part = $new_type_union->getSingleAtomic();
- } else {
- $new_type_part = Atomic::create($assertion, null, $template_type_map);
- }
- } catch (TypeParseTreeException $e) {
- $new_type_part = new TMixed();
-
- if ($code_location) {
- IssueBuffer::maybeAdd(
- new InvalidDocblock(
- $assertion . ' cannot be used in an assertion',
- $code_location
- ),
- $suppressed_issues
- );
- }
- }
-
- if ($new_type_part instanceof TTemplateParam
- && $new_type_part->as->isSingle()
- ) {
- $new_as_atomic = $new_type_part->as->getSingleAtomic();
-
- $acceptable_atomic_types = [];
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_part) {
- if ($existing_var_type_part instanceof TNamedObject
- || $existing_var_type_part instanceof TTemplateParam
- ) {
- $new_type_part->addIntersectionType($existing_var_type_part);
- $acceptable_atomic_types[] = clone $existing_var_type_part;
- } else {
- if (AtomicTypeComparator::isContainedBy(
- $codebase,
- $existing_var_type_part,
- $new_as_atomic
- )) {
- $acceptable_atomic_types[] = clone $existing_var_type_part;
- }
- }
- }
-
- if ($acceptable_atomic_types) {
- $new_type_part->as = new Union($acceptable_atomic_types);
-
- return new Union([$new_type_part]);
- }
- }
-
- if ($new_type_part instanceof TKeyedArray) {
- $acceptable_atomic_types = [];
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_part) {
- if ($existing_var_type_part instanceof TKeyedArray) {
- if (!array_intersect_key(
- $existing_var_type_part->properties,
- $new_type_part->properties
- )) {
- $existing_var_type_part = clone $existing_var_type_part;
- $existing_var_type_part->properties = array_merge(
- $existing_var_type_part->properties,
- $new_type_part->properties
- );
-
- $acceptable_atomic_types[] = $existing_var_type_part;
- }
- }
- }
-
- if ($acceptable_atomic_types) {
- return new Union($acceptable_atomic_types);
- }
- }
-
- if ($new_type_part instanceof TNamedObject
- && ($new_type_has_interface || $old_type_has_interface)
- && !UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $new_type,
- $existing_var_type,
- false
- )
- ) {
- $acceptable_atomic_types = [];
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_part) {
- if (AtomicTypeComparator::isContainedBy(
- $codebase,
- $existing_var_type_part,
- $new_type_part
- )) {
- $acceptable_atomic_types[] = clone $existing_var_type_part;
- continue;
- }
-
- if ($existing_var_type_part instanceof TNamedObject
- && ($codebase->classExists($existing_var_type_part->value)
- || $codebase->interfaceExists($existing_var_type_part->value))
- ) {
- $existing_var_type_part = clone $existing_var_type_part;
- $existing_var_type_part->addIntersectionType($new_type_part);
- $acceptable_atomic_types[] = $existing_var_type_part;
- }
-
- if ($existing_var_type_part instanceof TTemplateParam) {
- $existing_var_type_part = clone $existing_var_type_part;
- $existing_var_type_part->addIntersectionType($new_type_part);
- $acceptable_atomic_types[] = $existing_var_type_part;
- }
- }
-
- if ($acceptable_atomic_types) {
- return new Union($acceptable_atomic_types);
- }
- } elseif (!$new_type->hasMixed()) {
- $has_match = true;
-
- if ($key
- && $code_location
- && $new_type->getId() === $existing_var_type->getId()
- //even if two objects are the same, equality is not guaranteed
- // example: (ErrorException and TypeError are both Throwable but not equal)
- && !$new_type->hasNamedObjectType()
- && !$is_equality
- && !($original_assertion === 'loaded-class-string' && $old_var_type_string === 'class-string')
- && (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer)
- || ($key !== '$this'
- && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString())))
- ) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- $original_assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $any_scalar_type_match_found = false;
-
- if ($code_location
- && $key
- && !$is_equality
- && $new_type_part instanceof TNamedObject
- && !$new_type_has_interface
- && (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer)
- || ($key !== '$this'
- && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString())))
- && UnionTypeComparator::isContainedBy(
- $codebase,
- $existing_var_type,
- $new_type,
- false,
- false,
- null,
- false,
- false
- )
- ) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- $original_assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $new_type = self::filterTypeWithAnother(
- $codebase,
- $existing_var_type,
- $new_type,
- $template_type_map,
- $has_match,
- $any_scalar_type_match_found
- );
-
- if ($code_location
- && !$has_match
- && (!$is_loose_equality || !$any_scalar_type_match_found)
- ) {
- if ($assertion === 'null') {
- if ($existing_var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- 'Cannot resolve types for ' . $key . ' - docblock-defined type '
- . $existing_var_type . ' does not contain null',
- $code_location,
- $existing_var_type->getId() . ' null'
- ),
- $suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainNull(
- 'Cannot resolve types for ' . $key . ' - ' . $existing_var_type
- . ' does not contain null',
- $code_location,
- $existing_var_type->getId()
- ),
- $suppressed_issues
- );
- }
- } elseif (!($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer)
- || ($key !== '$this'
- && !($existing_var_type->hasLiteralClassString() && $new_type->hasLiteralClassString()))
- ) {
- if ($existing_var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- 'Cannot resolve types for ' . $key . ' - docblock-defined type '
- . $existing_var_type->getId() . ' does not contain ' . $new_type->getId(),
- $code_location,
- $existing_var_type->getId() . ' ' . $new_type->getId()
- ),
- $suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- 'Cannot resolve types for ' . $key . ' - ' . $existing_var_type->getId()
- . ' does not contain ' . $new_type->getId(),
- $code_location,
- $existing_var_type->getId() . ' ' . $new_type->getId()
- ),
- $suppressed_issues
- );
- }
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
- }
- }
-
- return $new_type;
- }
-
- /**
- * This method receives two types. The goal is to use datas in the new type to reduce the existing_type to a more
- * precise version. For example: new is `array<int>` old is `list<mixed>` so the result is `list<int>`
- *
- * @param array<string, array<string, Union>> $template_type_map
- *
- * @psalm-suppress ComplexMethod we'd probably want to extract specific handling blocks at the end and also allow
- * early return once a specific case has been handled
- */
- private static function filterTypeWithAnother(
- Codebase $codebase,
- Union $existing_type,
- Union $new_type,
- array $template_type_map,
- bool &$has_match = false,
- bool &$any_scalar_type_match_found = false
- ): Union {
- $matching_atomic_types = [];
-
- $has_cloned_type = false;
-
- foreach ($new_type->getAtomicTypes() as $new_type_part) {
- $has_local_match = false;
-
- foreach ($existing_type->getAtomicTypes() as $key => $existing_type_part) {
- // special workaround because PHP allows floats to contain ints, but we don’t want this
- // behaviour here
- if ($existing_type_part instanceof TFloat
- && $new_type_part instanceof TInt
- ) {
- $any_scalar_type_match_found = true;
- continue;
- }
-
- $atomic_comparison_results = new TypeComparisonResult();
-
- if ($existing_type_part instanceof TNamedObject) {
- $existing_type_part->was_static = false;
- }
-
- $atomic_contained_by = AtomicTypeComparator::isContainedBy(
- $codebase,
- $new_type_part,
- $existing_type_part,
- true,
- false,
- $atomic_comparison_results
- );
-
- if ($atomic_contained_by) {
- $has_local_match = true;
-
- if ($atomic_comparison_results->type_coerced
- && get_class($new_type_part) === TNamedObject::class
- && $existing_type_part instanceof TGenericObject
- ) {
- // this is a hack - it's not actually rigorous, as the params may be different
- $matching_atomic_types[] = new TGenericObject(
- $new_type_part->value,
- $existing_type_part->type_params
- );
- } elseif ($new_type_part instanceof TNamedObject
- && $existing_type_part instanceof TTemplateParam
- && $existing_type_part->as->hasObjectType()
- ) {
- $existing_type_part = clone $existing_type_part;
- $existing_type_part->as = self::filterTypeWithAnother(
- $codebase,
- $existing_type_part->as,
- new Union([$new_type_part]),
- $template_type_map
- );
-
- $matching_atomic_types[] = $existing_type_part;
- } else {
- $matching_atomic_types[] = clone $new_type_part;
- }
-
- continue;
- }
-
- if (AtomicTypeComparator::isContainedBy(
- $codebase,
- $existing_type_part,
- $new_type_part,
- false,
- false,
- null
- )) {
- $has_local_match = true;
- $matching_atomic_types[] = $existing_type_part;
-
- continue;
- }
-
- if ($existing_type_part instanceof TNamedObject
- && $new_type_part instanceof TNamedObject
- && ($codebase->interfaceExists($existing_type_part->value)
- || $codebase->interfaceExists($new_type_part->value))
- ) {
- $matching_atomic_type = clone $new_type_part;
- $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part;
- $matching_atomic_types[] = $matching_atomic_type;
- $has_local_match = true;
-
- continue;
- }
-
- if ($new_type_part instanceof TKeyedArray
- && $existing_type_part instanceof TList
- ) {
- $new_type_key = $new_type_part->getGenericKeyType();
- $new_type_value = $new_type_part->getGenericValueType();
-
- if (!$new_type_key->hasString()) {
- $has_param_match = false;
-
- $new_type_value = self::filterTypeWithAnother(
- $codebase,
- $existing_type_part->type_param,
- $new_type_value,
- $template_type_map,
- $has_param_match,
- $any_scalar_type_match_found
- );
-
- $hybrid_type_part = new TKeyedArray($new_type_part->properties);
- $hybrid_type_part->previous_key_type = Type::getInt();
- $hybrid_type_part->previous_value_type = $new_type_value;
- $hybrid_type_part->is_list = true;
-
- if (!$has_cloned_type) {
- $new_type = clone $new_type;
- $has_cloned_type = true;
- }
-
- $has_local_match = true;
-
- $new_type->removeType($key);
- $new_type->addType($hybrid_type_part);
-
- continue;
- }
- }
-
- if ($new_type_part instanceof TTemplateParam
- && $existing_type_part instanceof TTemplateParam
- && $new_type_part->param_name !== $existing_type_part->param_name
- && $new_type_part->as->hasObject()
- && $existing_type_part->as->hasObject()
- ) {
- $matching_atomic_type = clone $new_type_part;
-
- $matching_atomic_type->extra_types[$existing_type_part->getKey()] = $existing_type_part;
- $matching_atomic_types[] = $matching_atomic_type;
- $has_local_match = true;
-
- continue;
- }
-
- //we filter both types of standard iterables
- if (($new_type_part instanceof TGenericObject
- || $new_type_part instanceof TArray
- || $new_type_part instanceof TIterable)
- && ($existing_type_part instanceof TGenericObject
- || $existing_type_part instanceof TArray
- || $existing_type_part instanceof TIterable)
- && count($new_type_part->type_params) === count($existing_type_part->type_params)
- ) {
- $has_any_param_match = false;
-
- foreach ($new_type_part->type_params as $i => $new_param) {
- $existing_param = $existing_type_part->type_params[$i];
-
- $has_param_match = true;
-
- $new_param_id = $new_param->getId();
-
- $new_param = self::filterTypeWithAnother(
- $codebase,
- $existing_param,
- $new_param,
- $template_type_map,
- $has_param_match,
- $any_scalar_type_match_found
- );
-
- if ($template_type_map) {
- TemplateInferredTypeReplacer::replace(
- $new_param,
- new TemplateResult([], $template_type_map),
- $codebase
- );
- }
-
- $existing_type->bustCache();
-
- if ($has_param_match
- && $existing_type_part->type_params[$i]->getId() !== $new_param_id
- ) {
- /** @psalm-suppress PropertyTypeCoercion */
- $existing_type_part->type_params[$i] = $new_param;
-
- if (!$has_local_match) {
- $has_any_param_match = true;
- }
- }
- }
-
- if ($has_any_param_match) {
- $has_local_match = true;
- $matching_atomic_types[] = $existing_type_part;
- $atomic_comparison_results->type_coerced = true;
- }
- }
-
- //we filter the second part of a list with the second part of standard iterables
- if (($new_type_part instanceof TArray
- || $new_type_part instanceof TIterable)
- && $existing_type_part instanceof TList
- ) {
- $has_any_param_match = false;
-
- $new_param = $new_type_part->type_params[1];
- $existing_param = $existing_type_part->type_param;
-
- $has_param_match = true;
-
- $new_param = self::filterTypeWithAnother(
- $codebase,
- $existing_param,
- $new_param,
- $template_type_map,
- $has_param_match,
- $any_scalar_type_match_found
- );
-
- if ($template_type_map) {
- TemplateInferredTypeReplacer::replace(
- $new_param,
- new TemplateResult([], $template_type_map),
- $codebase
- );
- }
-
- $existing_type->bustCache();
-
- if ($has_param_match
- && $existing_type_part->type_param->getId() !== $new_param->getId()
- ) {
- $existing_type_part->type_param = $new_param;
-
- if (!$has_local_match) {
- $has_any_param_match = true;
- }
- }
-
- if ($has_any_param_match) {
- $has_local_match = true;
- $matching_atomic_types[] = $existing_type_part;
- $atomic_comparison_results->type_coerced = true;
- }
- }
-
- //we filter each property of a Keyed Array with the second part of standard iterables
- if (($new_type_part instanceof TArray
- || $new_type_part instanceof TIterable)
- && $existing_type_part instanceof TKeyedArray
- ) {
- $has_any_param_match = false;
-
- $new_param = $new_type_part->type_params[1];
- foreach ($existing_type_part->properties as $property_key => $existing_param) {
- $has_param_match = true;
-
- $new_param = self::filterTypeWithAnother(
- $codebase,
- $existing_param,
- $new_param,
- $template_type_map,
- $has_param_match,
- $any_scalar_type_match_found
- );
-
- if ($template_type_map) {
- TemplateInferredTypeReplacer::replace(
- $new_param,
- new TemplateResult([], $template_type_map),
- $codebase
- );
- }
-
- if ($has_param_match
- && $existing_type_part->properties[$property_key]->getId() !== $new_param->getId()
- ) {
- $existing_type_part->properties[$property_key] = $new_param;
-
- if (!$has_local_match) {
- $has_any_param_match = true;
- }
- }
- }
-
- $existing_type->bustCache();
-
- if ($has_any_param_match) {
- $has_local_match = true;
- $matching_atomic_types[] = $existing_type_part;
- $atomic_comparison_results->type_coerced = true;
- }
- }
-
- //These partial match wouldn't have been handled by AtomicTypeComparator
- $new_range = null;
- if ($new_type_part instanceof TIntRange && $existing_type_part instanceof TPositiveInt) {
- $new_range = TIntRange::intersectIntRanges(
- TIntRange::convertToIntRange($existing_type_part),
- $new_type_part
- );
- } elseif ($existing_type_part instanceof TIntRange
- && $new_type_part instanceof TPositiveInt
- ) {
- $new_range = TIntRange::intersectIntRanges(
- $existing_type_part,
- TIntRange::convertToIntRange($new_type_part)
- );
- } elseif ($new_type_part instanceof TIntRange
- && $existing_type_part instanceof TIntRange
- ) {
- $new_range = TIntRange::intersectIntRanges(
- $existing_type_part,
- $new_type_part
- );
- }
-
- if ($new_range !== null) {
- $has_local_match = true;
- $matching_atomic_types[] = $new_range;
- }
-
- if ($atomic_comparison_results->type_coerced) {
- continue;
- }
-
- if ($atomic_comparison_results->scalar_type_match_found) {
- $any_scalar_type_match_found = true;
- }
- }
-
- if (!$has_local_match) {
- $has_match = false;
- break;
- }
- }
-
- if ($matching_atomic_types) {
- return new Union($matching_atomic_types);
- }
-
- return $new_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function handleLiteralEquality(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- int $bracket_pos,
- bool $is_loose_equality,
- Union $existing_var_type,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $value = substr($assertion, $bracket_pos + 1, -1);
-
- $scalar_type = substr($assertion, 0, $bracket_pos);
-
- $existing_var_atomic_types = [];
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TClassConstant) {
- $expanded = TypeExpander::expandAtomic(
- $statements_analyzer->getCodebase(),
- $existing_var_atomic_type,
- $existing_var_atomic_type->fq_classlike_name,
- $existing_var_atomic_type->fq_classlike_name,
- null,
- true,
- true
- );
-
- if ($expanded instanceof Atomic) {
- $existing_var_atomic_types[$expanded->getKey()] = $expanded;
- } else {
- foreach ($expanded as $atomic_type) {
- $existing_var_atomic_types[$atomic_type->getKey()] = $atomic_type;
- }
- }
- } else {
- $existing_var_atomic_types[$existing_var_atomic_type->getKey()] = $existing_var_atomic_type;
- }
- }
-
- if ($scalar_type === 'int') {
- return self::handleLiteralEqualityWithInt(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- $is_loose_equality,
- $existing_var_type,
- $existing_var_atomic_types,
- $old_var_type_string,
- $var_id,
- $negated,
- $code_location,
- $suppressed_issues
- );
- } elseif ($scalar_type === 'string'
- || $scalar_type === 'class-string'
- || $scalar_type === 'interface-string'
- || $scalar_type === 'callable-string'
- || $scalar_type === 'trait-string'
- ) {
- return self::handleLiteralEqualityWithString(
- $statements_analyzer,
- $assertion,
- $scalar_type,
- $bracket_pos,
- $is_loose_equality,
- $existing_var_type,
- $existing_var_atomic_types,
- $old_var_type_string,
- $var_id,
- $negated,
- $code_location,
- $suppressed_issues
- );
- } elseif ($scalar_type === 'float') {
- return self::handleLiteralEqualityWithFloat(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- $is_loose_equality,
- $existing_var_type,
- $existing_var_atomic_types,
- $old_var_type_string,
- $var_id,
- $negated,
- $code_location,
- $suppressed_issues
- );
- } elseif ($scalar_type === 'enum') {
- [$fq_enum_name, $case_name] = explode('::', $value);
-
- if ($existing_var_type->hasMixed()) {
- if ($is_loose_equality) {
- return $existing_var_type;
- }
-
- return new Union([new TEnumCase($fq_enum_name, $case_name)]);
- }
-
- $can_be_equal = false;
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $atomic_key => $atomic_type) {
- if (get_class($atomic_type) === TNamedObject::class
- && $atomic_type->value === $fq_enum_name
- ) {
- $can_be_equal = true;
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_key);
- $existing_var_type->addType(new TEnumCase($fq_enum_name, $case_name));
- } elseif ($atomic_key !== $assertion) {
- $existing_var_type->removeType($atomic_key);
- $did_remove_type = true;
- } else {
- $can_be_equal = true;
- }
- }
-
- if ($var_id
- && $code_location
- && (!$can_be_equal || (!$did_remove_type && count($existing_var_atomic_types) === 1))
- ) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- $can_be_equal,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param array<string, Atomic> $existing_var_atomic_types
- * @param string[] $suppressed_issues
- */
- private static function handleLiteralEqualityWithInt(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- int $bracket_pos,
- bool $is_loose_equality,
- Union $existing_var_type,
- array $existing_var_atomic_types,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $value = (int) substr($assertion, $bracket_pos + 1, -1);
-
- // we create the literal that is being asserted. We'll return this when we're sure this is the resulting type
- $literal_asserted_type = new Union([new TLiteralInt($value)]);
- $literal_asserted_type->from_docblock = $existing_var_type->from_docblock;
-
- $compatible_int_type = self::getCompatibleIntType(
- $existing_var_type,
- $existing_var_atomic_types,
- $value,
- $is_loose_equality
- );
-
- if ($compatible_int_type !== null) {
- return $compatible_int_type;
- }
-
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TPositiveInt && $value > 0) {
- return $literal_asserted_type;
- }
-
- if ($existing_var_atomic_type instanceof TIntRange && $existing_var_atomic_type->contains($value)) {
- return $literal_asserted_type;
- }
-
- if ($existing_var_atomic_type instanceof TLiteralInt && $existing_var_atomic_type->value === $value) {
- //if we're here, we check that we had at least another type in the union, otherwise it's redundant
-
- if ($existing_var_type->isSingleIntLiteral()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- return $existing_var_type;
- }
- return $literal_asserted_type;
- }
-
- if ($existing_var_atomic_type instanceof TInt && !$existing_var_atomic_type instanceof TLiteralInt) {
- return $literal_asserted_type;
- }
-
- if ($existing_var_atomic_type instanceof TTemplateParam) {
- $compatible_int_type = self::getCompatibleIntType(
- $existing_var_type,
- $existing_var_atomic_type->as->getAtomicTypes(),
- $value,
- $is_loose_equality
- );
- if ($compatible_int_type !== null) {
- return $compatible_int_type;
- }
-
- $existing_var_atomic_type = clone $existing_var_atomic_type;
-
- $existing_var_atomic_type->as = self::handleLiteralEquality(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- false,
- $existing_var_atomic_type->as,
- $old_var_type_string,
- $var_id,
- $negated,
- $code_location,
- $suppressed_issues
- );
-
- return new Union([$existing_var_atomic_type]);
- }
-
- if ($is_loose_equality
- && $existing_var_atomic_type instanceof TLiteralFloat
- && (int)$existing_var_atomic_type->value === $value
- ) {
- return new Union([$existing_var_atomic_type]);
- }
-
- if ($is_loose_equality
- && $existing_var_atomic_type instanceof TLiteralString
- && (int)$existing_var_atomic_type->value === $value
- ) {
- return new Union([$existing_var_atomic_type]);
- }
- }
-
- //here we'll accept non-literal type that *could* match on loose equality and return the original type
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- //here we'll accept non-literal type that *could* match on loose equality and return the original type
- if ($is_loose_equality) {
- if ($existing_var_atomic_type instanceof TString
- && !$existing_var_atomic_type instanceof TLiteralString
- ) {
- return $existing_var_type;
- }
-
- if ($existing_var_atomic_type instanceof TFloat
- && !$existing_var_atomic_type instanceof TLiteralFloat
- ) {
- return $existing_var_type;
- }
- }
- }
-
- //if we're here, no type was eligible for the given literal. We'll emit an impossible error for this assertion
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- return Type::getNever();
- }
-
- /**
- * @param array<string, Atomic> $existing_var_atomic_types
- * @param string[] $suppressed_issues
- */
- private static function handleLiteralEqualityWithString(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- string $scalar_type,
- int $bracket_pos,
- bool $is_loose_equality,
- Union $existing_var_type,
- array $existing_var_atomic_types,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $value = substr($assertion, $bracket_pos + 1, -1);
-
- // we create the literal that is being asserted. We'll return this when we're sure this is the resulting type
- $literal_asserted_type_string = new Union([new TLiteralString($value)]);
- $literal_asserted_type_string->from_docblock = $existing_var_type->from_docblock;
- $literal_asserted_type_classstring = new Union([new TLiteralClassString($value)]);
- $literal_asserted_type_classstring->from_docblock = $existing_var_type->from_docblock;
-
- $compatible_string_type = self::getCompatibleStringType(
- $existing_var_type,
- $existing_var_atomic_types,
- $value,
- $scalar_type,
- $is_loose_equality
- );
-
- if ($compatible_string_type !== null) {
- return $compatible_string_type;
- }
-
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TLiteralString && $existing_var_atomic_type->value === $value) {
- //if we're here, we check that we had at least another type in the union, otherwise it's redundant
-
- if ($existing_var_type->isSingleStringLiteral()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- return $existing_var_type;
- }
-
- if ($scalar_type === 'class-string'
- || $scalar_type === 'interface-string'
- || $scalar_type === 'trait-string'
- ) {
- return $literal_asserted_type_classstring;
- }
-
- return $literal_asserted_type_string;
- }
-
- if ($existing_var_atomic_type instanceof TString && !$existing_var_atomic_type instanceof TLiteralString) {
- if ($scalar_type === 'class-string'
- || $scalar_type === 'interface-string'
- || $scalar_type === 'trait-string'
- ) {
- return $literal_asserted_type_classstring;
- }
-
- return $literal_asserted_type_string;
- }
-
- if ($existing_var_atomic_type instanceof TTemplateParam) {
- $compatible_string_type = self::getCompatibleStringType(
- $existing_var_type,
- $existing_var_atomic_type->as->getAtomicTypes(),
- $value,
- $scalar_type,
- $is_loose_equality
- );
- if ($compatible_string_type !== null) {
- return $compatible_string_type;
- }
-
- if ($existing_var_atomic_type->as->hasString()) {
- if ($scalar_type === 'class-string'
- || $scalar_type === 'interface-string'
- || $scalar_type === 'trait-string'
- ) {
- return $literal_asserted_type_classstring;
- }
-
- return $literal_asserted_type_string;
- }
-
- $existing_var_atomic_type = clone $existing_var_atomic_type;
-
- $existing_var_atomic_type->as = self::handleLiteralEquality(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- false,
- $existing_var_atomic_type->as,
- $old_var_type_string,
- $var_id,
- $negated,
- $code_location,
- $suppressed_issues
- );
-
- return new Union([$existing_var_atomic_type]);
- }
-
- if ($is_loose_equality
- && $existing_var_atomic_type instanceof TLiteralInt
- && (string)$existing_var_atomic_type->value === $value
- ) {
- return new Union([$existing_var_atomic_type]);
- }
-
- if ($is_loose_equality
- && $existing_var_atomic_type instanceof TLiteralFloat
- && (string)$existing_var_atomic_type->value === $value
- ) {
- return new Union([$existing_var_atomic_type]);
- }
- }
-
- //here we'll accept non-literal type that *could* match on loose equality and return the original type
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- //here we'll accept non-literal type that *could* match on loose equality and return the original type
- if ($is_loose_equality) {
- if ($existing_var_atomic_type instanceof TInt
- && !$existing_var_atomic_type instanceof TLiteralInt
- ) {
- return $existing_var_type;
- }
-
- if ($existing_var_atomic_type instanceof TFloat
- && !$existing_var_atomic_type instanceof TLiteralFloat
- ) {
- return $existing_var_type;
- }
- }
- }
-
- //if we're here, no type was eligible for the given literal. We'll emit an impossible error for this assertion
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- return Type::getNever();
- }
-
- /**
- * @param array<string, Atomic> $existing_var_atomic_types
- * @param string[] $suppressed_issues
- */
- private static function handleLiteralEqualityWithFloat(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- int $bracket_pos,
- bool $is_loose_equality,
- Union $existing_var_type,
- array $existing_var_atomic_types,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $value = (float)substr($assertion, $bracket_pos + 1, -1);
-
- // we create the literal that is being asserted. We'll return this when we're sure this is the resulting type
- $literal_asserted_type = new Union([new TLiteralFloat($value)]);
- $literal_asserted_type->from_docblock = $existing_var_type->from_docblock;
-
- $compatible_float_type = self::getCompatibleFloatType(
- $existing_var_type,
- $existing_var_atomic_types,
- $value,
- $is_loose_equality
- );
-
- if ($compatible_float_type !== null) {
- return $compatible_float_type;
- }
-
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TLiteralFloat && $existing_var_atomic_type->value === $value) {
- //if we're here, we check that we had at least another type in the union, otherwise it's redundant
-
- if ($existing_var_type->isSingleFloatLiteral()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- return $existing_var_type;
- }
-
- return $literal_asserted_type;
- }
-
- if ($existing_var_atomic_type instanceof TFloat && !$existing_var_atomic_type instanceof TLiteralFloat) {
- return $literal_asserted_type;
- }
-
- if ($existing_var_atomic_type instanceof TTemplateParam) {
- $compatible_float_type = self::getCompatibleFloatType(
- $existing_var_type,
- $existing_var_atomic_type->as->getAtomicTypes(),
- $value,
- $is_loose_equality
- );
- if ($compatible_float_type !== null) {
- return $compatible_float_type;
- }
-
- if ($existing_var_atomic_type->as->hasFloat()) {
- return $literal_asserted_type;
- }
-
- $existing_var_atomic_type = clone $existing_var_atomic_type;
-
- $existing_var_atomic_type->as = self::handleLiteralEquality(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- false,
- $existing_var_atomic_type->as,
- $old_var_type_string,
- $var_id,
- $negated,
- $code_location,
- $suppressed_issues
- );
-
- return new Union([$existing_var_atomic_type]);
- }
-
- if ($is_loose_equality
- && $existing_var_atomic_type instanceof TLiteralInt
- && (float)$existing_var_atomic_type->value === $value
- ) {
- return new Union([$existing_var_atomic_type]);
- }
-
- if ($is_loose_equality
- && $existing_var_atomic_type instanceof TLiteralString
- && (float)$existing_var_atomic_type->value === $value
- ) {
- return new Union([$existing_var_atomic_type]);
- }
- }
-
- //here we'll accept non-literal type that *could* match on loose equality and return the original type
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($is_loose_equality) {
- if ($existing_var_atomic_type instanceof TInt
- && !$existing_var_atomic_type instanceof TLiteralInt
- ) {
- return $existing_var_type;
- }
-
- if ($existing_var_atomic_type instanceof TString
- && !$existing_var_atomic_type instanceof TLiteralString
- ) {
- return $existing_var_type;
- }
- }
- }
-
- //if we're here, no type was eligible for the given literal. We'll emit an impossible error for this assertion
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- return Type::getNever();
- }
-
- /**
- * @param array<string, Atomic> $existing_var_atomic_types
- */
- private static function getCompatibleIntType(
- Union $existing_var_type,
- array $existing_var_atomic_types,
- int $value,
- bool $is_loose_equality
- ): ?Union {
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TMixed
- || $existing_var_atomic_type instanceof TScalar
- || $existing_var_atomic_type instanceof TNumeric
- || $existing_var_atomic_type instanceof TArrayKey
- ) {
- if ($is_loose_equality) {
- return $existing_var_type;
- }
-
- $asserted_type = new Union([new TLiteralInt($value)]);
- $asserted_type->from_docblock = $existing_var_type->from_docblock;
- return $asserted_type;
- }
- }
-
- return null;
- }
-
- /**
- * @param array<string, Atomic> $existing_var_atomic_types
- */
- private static function getCompatibleStringType(
- Union $existing_var_type,
- array $existing_var_atomic_types,
- string $value,
- string $scalar_type,
- bool $is_loose_equality
- ): ?Union {
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TMixed
- || $existing_var_atomic_type instanceof TScalar
- || $existing_var_atomic_type instanceof TArrayKey
- ) {
- if ($is_loose_equality) {
- return $existing_var_type;
- }
-
- if ($scalar_type === 'class-string'
- || $scalar_type === 'interface-string'
- || $scalar_type === 'trait-string'
- ) {
- $asserted_type = new Union([new TLiteralClassString($value)]);
- $asserted_type->from_docblock = $existing_var_type->from_docblock;
- return $asserted_type;
- }
-
- $asserted_type = new Union([new TLiteralString($value)]);
- $asserted_type->from_docblock = $existing_var_type->from_docblock;
- return $asserted_type;
- }
- }
-
- return null;
- }
-
- /**
- * @param array<string, Atomic> $existing_var_atomic_types
- */
- private static function getCompatibleFloatType(
- Union $existing_var_type,
- array $existing_var_atomic_types,
- float $value,
- bool $is_loose_equality
- ): ?Union {
- foreach ($existing_var_atomic_types as $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TMixed
- || $existing_var_atomic_type instanceof TScalar
- || $existing_var_atomic_type instanceof TNumeric
- ) {
- if ($is_loose_equality) {
- return $existing_var_type;
- }
-
- $asserted_type = new Union([new TLiteralFloat($value)]);
- $asserted_type->from_docblock = $existing_var_type->from_docblock;
- return $asserted_type;
- }
- }
-
- return null;
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string> $suppressed_issues
- */
- private static function handleIsA(
- Codebase $codebase,
- Union $existing_var_type,
- string &$assertion,
- array $template_type_map,
- ?CodeLocation $code_location,
- ?string $key,
- array $suppressed_issues,
- bool &$should_return
- ): Union {
- $assertion = substr($assertion, 4);
-
- $allow_string_comparison = false;
-
- if (strpos($assertion, 'string-') === 0) {
- $assertion = substr($assertion, 7);
- $allow_string_comparison = true;
- }
-
- if ($existing_var_type->hasMixed()) {
- $type = new Union([
- new TNamedObject($assertion),
- ]);
-
- if ($allow_string_comparison) {
- $type->addType(
- new TClassString(
- $assertion,
- new TNamedObject($assertion)
- )
- );
- }
-
- $should_return = true;
- return $type;
- }
-
- $existing_has_object = $existing_var_type->hasObjectType();
- $existing_has_string = $existing_var_type->hasString();
-
- if ($existing_has_object && !$existing_has_string) {
- return Type::parseString($assertion, null, $template_type_map);
- }
-
- if ($existing_has_string && !$existing_has_object) {
- if (!$allow_string_comparison && $code_location) {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- 'Cannot allow string comparison to object for ' . $key,
- $code_location,
- null
- ),
- $suppressed_issues
- );
-
- return Type::getMixed();
- } else {
- $new_type_has_interface_string = $codebase->interfaceExists($assertion);
-
- $old_type_has_interface_string = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_type_part) {
- if ($existing_type_part instanceof TClassString
- && $existing_type_part->as_type
- && $codebase->interfaceExists($existing_type_part->as_type->value)
- ) {
- $old_type_has_interface_string = true;
- break;
- }
- }
-
- if (isset($template_type_map[$assertion])) {
- $new_type = Type::parseString(
- 'class-string<' . $assertion . '>',
- null,
- $template_type_map
- );
- } else {
- $new_type = Type::getClassString($assertion);
- }
-
- if ((
- $new_type_has_interface_string
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $existing_var_type,
- $new_type
- )
- )
- || (
- $old_type_has_interface_string
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $new_type,
- $existing_var_type
- )
- )
- ) {
- $new_type_part = Atomic::create($assertion, null, $template_type_map);
-
- $acceptable_atomic_types = [];
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_part) {
- if (!$new_type_part instanceof TNamedObject
- || !$existing_var_type_part instanceof TClassString
- ) {
- $acceptable_atomic_types = [];
-
- break;
- }
-
- if (!$existing_var_type_part->as_type instanceof TNamedObject) {
- $acceptable_atomic_types = [];
-
- break;
- }
-
- $existing_var_type_part = $existing_var_type_part->as_type;
-
- if (AtomicTypeComparator::isContainedBy(
- $codebase,
- $existing_var_type_part,
- $new_type_part
- )) {
- $acceptable_atomic_types[] = clone $existing_var_type_part;
- continue;
- }
-
- if ($codebase->classExists($existing_var_type_part->value)
- || $codebase->interfaceExists($existing_var_type_part->value)
- ) {
- $existing_var_type_part = clone $existing_var_type_part;
- $existing_var_type_part->addIntersectionType($new_type_part);
- $acceptable_atomic_types[] = $existing_var_type_part;
- }
- }
-
- if (count($acceptable_atomic_types) === 1) {
- $should_return = true;
-
- return new Union([
- new TClassString('object', $acceptable_atomic_types[0]),
- ]);
- }
- }
- }
-
- return $new_type;
- } else {
- return Type::getMixed();
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php
deleted file mode 100644
index d1b31b8..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ArrayTypeComparator.php
+++ /dev/null
@@ -1,293 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Union;
-
-use function array_map;
-use function range;
-
-/**
- * @internal
- */
-class ArrayTypeComparator
-{
- /**
- * @param TArray|TKeyedArray|TList|TClassStringMap $input_type_part
- * @param TArray|TKeyedArray|TList|TClassStringMap $container_type_part
- */
- public static function isContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- bool $allow_interface_equality,
- ?TypeComparisonResult $atomic_comparison_result
- ): bool {
- $all_types_contain = true;
-
- $is_empty_array = $input_type_part->equals(
- new TArray([
- new Union([new TEmpty()]),
- new Union([new TEmpty()])
- ]),
- false
- );
-
- if ($is_empty_array
- && (($container_type_part instanceof TArray
- && !$container_type_part instanceof TNonEmptyArray)
- || ($container_type_part instanceof TKeyedArray
- && !$container_type_part->isNonEmpty())
- )
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TKeyedArray
- && $input_type_part instanceof TArray
- ) {
- $all_string_int_literals = true;
-
- $properties = [];
-
- foreach ($input_type_part->type_params[0]->getAtomicTypes() as $atomic_key_type) {
- if ($atomic_key_type instanceof TLiteralString || $atomic_key_type instanceof TLiteralInt) {
- $properties[$atomic_key_type->value] = clone $input_type_part->type_params[1];
- $properties[$atomic_key_type->value]->possibly_undefined = true;
- } else {
- $all_string_int_literals = false;
- }
- }
-
- if ($all_string_int_literals && $properties) {
- $input_type_part = new TKeyedArray($properties);
-
- return KeyedArrayComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
- }
-
- if ($container_type_part instanceof TList
- && $input_type_part instanceof TKeyedArray
- ) {
- if ($input_type_part->is_list) {
- $input_type_part = $input_type_part->getList();
- } else {
- return false;
- }
- }
-
- if ($container_type_part instanceof TList
- && $input_type_part instanceof TClassStringMap
- ) {
- return false;
- }
-
- if ($container_type_part instanceof TList
- && $input_type_part instanceof TArray
- && $input_type_part->type_params[1]->isEmpty()
- ) {
- return !$container_type_part instanceof TNonEmptyList;
- }
-
- if ($container_type_part instanceof TNonEmptyList
- && $input_type_part instanceof TNonEmptyArray
- && $input_type_part->type_params[0]->isSingleIntLiteral()
- && $input_type_part->type_params[0]->getSingleIntLiteral()->value === 0
- ) {
- //this is a special case where the only offset value of an non empty array is 0, so it's a non empty list
- return UnionTypeComparator::isContainedBy(
- $codebase,
- $input_type_part->type_params[1],
- $container_type_part->type_param,
- $input_type_part->type_params[1]->ignore_nullable_issues,
- $input_type_part->type_params[1]->ignore_falsable_issues,
- $atomic_comparison_result,
- $allow_interface_equality
- );
- }
-
- if ($input_type_part instanceof TList
- && $container_type_part instanceof TList
- ) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $input_type_part->type_param,
- $container_type_part->type_param,
- $input_type_part->type_param->ignore_nullable_issues,
- $input_type_part->type_param->ignore_falsable_issues,
- $atomic_comparison_result,
- $allow_interface_equality
- )) {
- return false;
- }
-
- return $input_type_part instanceof TNonEmptyList
- || !$container_type_part instanceof TNonEmptyList;
- }
-
- if ($container_type_part instanceof TKeyedArray) {
- if ($container_type_part->is_list) {
- $container_type_part = $container_type_part->getList();
-
- return self::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- $container_type_part = $container_type_part->getGenericArrayType();
- }
-
- if ($input_type_part instanceof TKeyedArray) {
- $input_type_part = $input_type_part->getGenericArrayType();
- }
-
- if ($input_type_part instanceof TClassStringMap) {
- $input_type_part = new TArray([
- $input_type_part->getStandinKeyParam(),
- clone $input_type_part->value_param
- ]);
- }
-
- if ($container_type_part instanceof TClassStringMap) {
- $container_type_part = new TArray([
- $container_type_part->getStandinKeyParam(),
- clone $container_type_part->value_param
- ]);
- }
-
- if ($container_type_part instanceof TList) {
- $all_types_contain = false;
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- $container_type_part = new TArray([Type::getInt(), clone $container_type_part->type_param]);
- }
-
- if ($input_type_part instanceof TList) {
- if ($input_type_part instanceof TNonEmptyList) {
- // if the array has a known size < 10, make sure the array keys are literal ints
- if ($input_type_part->count !== null && $input_type_part->count < 10) {
- $literal_ints = array_map(
- function ($i) {
- return new TLiteralInt($i);
- },
- range(0, $input_type_part->count - 1)
- );
-
- $input_type_part = new TNonEmptyArray([
- new Union($literal_ints),
- clone $input_type_part->type_param
- ]);
- } else {
- $input_type_part = new TNonEmptyArray([Type::getInt(), clone $input_type_part->type_param]);
- }
- } else {
- $input_type_part = new TArray([Type::getInt(), clone $input_type_part->type_param]);
- }
- }
-
- foreach ($input_type_part->type_params as $i => $input_param) {
- if ($i > 1) {
- break;
- }
-
- $container_param = $container_type_part->type_params[$i];
-
- if ($i === 0
- && $input_param->hasMixed()
- && $container_param->hasString()
- && $container_param->hasInt()
- ) {
- continue;
- }
-
- if ($input_param->isEmpty()
- && $container_type_part instanceof TNonEmptyArray
- ) {
- return false;
- }
-
- $param_comparison_result = new TypeComparisonResult();
-
- if (!$input_param->isEmpty()) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $input_param,
- $container_param,
- $input_param->ignore_nullable_issues,
- $input_param->ignore_falsable_issues,
- $param_comparison_result,
- $allow_interface_equality
- )) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced
- = $param_comparison_result->type_coerced === true
- && $atomic_comparison_result->type_coerced !== false;
-
- $atomic_comparison_result->type_coerced_from_mixed
- = $param_comparison_result->type_coerced_from_mixed === true
- && $atomic_comparison_result->type_coerced_from_mixed !== false;
-
- $atomic_comparison_result->type_coerced_from_as_mixed
- = $param_comparison_result->type_coerced_from_as_mixed === true
- && $atomic_comparison_result->type_coerced_from_as_mixed !== false;
-
- $atomic_comparison_result->type_coerced_from_scalar
- = $param_comparison_result->type_coerced_from_scalar === true
- && $atomic_comparison_result->type_coerced_from_scalar !== false;
-
- $atomic_comparison_result->scalar_type_match_found
- = $param_comparison_result->scalar_type_match_found === true
- && $atomic_comparison_result->scalar_type_match_found !== false;
- }
-
- if (!$param_comparison_result->type_coerced_from_as_mixed) {
- $all_types_contain = false;
- }
- } else {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast
- = $atomic_comparison_result->to_string_cast === true
- || $param_comparison_result->to_string_cast === true;
- }
- }
- }
- }
-
- if ($container_type_part instanceof TNonEmptyArray
- && !$input_type_part instanceof TNonEmptyArray
- ) {
- if ($all_types_contain && $atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- return $all_types_contain;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php
deleted file mode 100644
index 6f35b1f..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php
+++ /dev/null
@@ -1,712 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TCallableObject;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TEmptyMixed;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-
-use function array_merge;
-use function array_values;
-use function count;
-use function get_class;
-use function strtolower;
-
-/**
- * @internal
- */
-class AtomicTypeComparator
-{
- /**
- * Does the input param atomic type match the given param atomic type
- */
- public static function isContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- bool $allow_interface_equality = false,
- bool $allow_float_int_equality = true,
- ?TypeComparisonResult $atomic_comparison_result = null
- ): bool {
-
- if (($container_type_part instanceof TTemplateParam
- || ($container_type_part instanceof TNamedObject
- && isset($container_type_part->extra_types)))
- && ($input_type_part instanceof TTemplateParam
- || ($input_type_part instanceof TNamedObject
- && isset($input_type_part->extra_types)))
- ) {
- return ObjectComparator::isShallowlyContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- if ($container_type_part instanceof TMixed
- || ($container_type_part instanceof TTemplateParam
- && $container_type_part->as->isMixed()
- && !$container_type_part->extra_types
- && $input_type_part instanceof TMixed)
- ) {
- if (get_class($container_type_part) === TEmptyMixed::class
- && get_class($input_type_part) === TMixed::class
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
-
- return false;
- }
-
- return true;
- }
-
- if ($input_type_part instanceof TNever || $input_type_part instanceof TEmpty) {
- return true;
- }
-
- if ($input_type_part instanceof TMixed
- || ($input_type_part instanceof TTemplateParam
- && $input_type_part->as->isMixed()
- && !$input_type_part->extra_types)
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TNull) {
- if ($container_type_part instanceof TNull) {
- return true;
- }
-
- if ($container_type_part instanceof TTemplateParam
- && ($container_type_part->as->isNullable() || $container_type_part->as->isMixed())
- ) {
- return true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TNull) {
- return false;
- }
-
- if ($input_type_part instanceof Scalar && $container_type_part instanceof Scalar) {
- return ScalarTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- );
- }
-
- if ($input_type_part instanceof TCallableKeyedArray
- && $container_type_part instanceof TArray
- ) {
- return ArrayTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- if (($container_type_part instanceof TCallable
- && $input_type_part instanceof TCallable)
- || ($container_type_part instanceof TClosure
- && $input_type_part instanceof TClosure)
- ) {
- return CallableTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $atomic_comparison_result
- );
- }
-
- if ($container_type_part instanceof TClosure) {
- if ($input_type_part instanceof TCallable) {
- if (CallableTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $atomic_comparison_result
- ) === false
- ) {
- return false;
- }
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TCallable && $input_type_part instanceof TClosure) {
- return CallableTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $atomic_comparison_result
- );
- }
-
- if ($input_type_part instanceof TNamedObject &&
- $input_type_part->value === 'Closure' &&
- $container_type_part instanceof TCallable
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TObject &&
- $container_type_part instanceof TCallable
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TCallableObject &&
- $container_type_part instanceof TObject
- ) {
- return true;
- }
-
- if (($container_type_part instanceof TKeyedArray
- && $input_type_part instanceof TKeyedArray)
- || ($container_type_part instanceof TObjectWithProperties
- && $input_type_part instanceof TObjectWithProperties)
- ) {
- return KeyedArrayComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- if ($container_type_part instanceof TObjectWithProperties
- && $input_type_part instanceof TObject
- && !$input_type_part instanceof TObjectWithProperties
- && !$input_type_part instanceof TCallableObject
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
- return false;
- }
-
- if (($input_type_part instanceof TArray
- || $input_type_part instanceof TList
- || $input_type_part instanceof TKeyedArray
- || $input_type_part instanceof TClassStringMap)
- && ($container_type_part instanceof TArray
- || $container_type_part instanceof TList
- || $container_type_part instanceof TKeyedArray
- || $container_type_part instanceof TClassStringMap)
- ) {
- return ArrayTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- if (get_class($container_type_part) === TNamedObject::class
- && $input_type_part instanceof TEnumCase
- && $input_type_part->value === $container_type_part->value
- ) {
- return true;
- }
-
- if (get_class($input_type_part) === TNamedObject::class
- && $container_type_part instanceof TEnumCase
- && $input_type_part->value === $container_type_part->value
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if (($input_type_part instanceof TNamedObject
- || ($input_type_part instanceof TTemplateParam
- && $input_type_part->as->hasObjectType())
- || $input_type_part instanceof TIterable)
- && ($container_type_part instanceof TNamedObject
- || ($container_type_part instanceof TTemplateParam
- && $container_type_part->isObjectType())
- || $container_type_part instanceof TIterable)
- && ObjectComparator::isShallowlyContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- )
- ) {
- if ($container_type_part instanceof TGenericObject || $container_type_part instanceof TIterable) {
- return GenericTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- if ($container_type_part instanceof TNamedObject
- && $input_type_part instanceof TNamedObject
- && $container_type_part->was_static
- && !$input_type_part->was_static
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast = false;
- }
-
- return true;
- }
-
- if (get_class($input_type_part) === TObject::class
- && get_class($container_type_part) === TObject::class
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TTemplateParam && $input_type_part instanceof TTemplateParam) {
- return UnionTypeComparator::isContainedBy(
- $codebase,
- $input_type_part->as,
- $container_type_part->as,
- false,
- false,
- $atomic_comparison_result,
- $allow_interface_equality
- );
- }
-
- if ($container_type_part instanceof TTemplateParam) {
- foreach ($container_type_part->as->getAtomicTypes() as $container_as_type_part) {
- if (self::isContainedBy(
- $codebase,
- $input_type_part,
- $container_as_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- )) {
- if ($allow_interface_equality) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TConditional) {
- $atomic_types = array_merge(
- array_values($container_type_part->if_type->getAtomicTypes()),
- array_values($container_type_part->else_type->getAtomicTypes())
- );
-
- foreach ($atomic_types as $container_as_type_part) {
- if (self::isContainedBy(
- $codebase,
- $input_type_part,
- $container_as_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- )) {
- return true;
- }
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TTemplateParam) {
- if ($input_type_part->extra_types) {
- foreach ($input_type_part->extra_types as $extra_type) {
- if (self::isContainedBy(
- $codebase,
- $extra_type,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- )) {
- return true;
- }
- }
- }
-
- foreach ($input_type_part->as->getAtomicTypes() as $input_as_type_part) {
- if (self::isContainedBy(
- $codebase,
- $input_as_type_part,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- )) {
- return true;
- }
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TConditional) {
- $input_atomic_types = array_merge(
- array_values($input_type_part->if_type->getAtomicTypes()),
- array_values($input_type_part->else_type->getAtomicTypes())
- );
-
- foreach ($input_atomic_types as $input_as_type_part) {
- if (self::isContainedBy(
- $codebase,
- $input_as_type_part,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- )) {
- return true;
- }
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TNamedObject
- && $input_type_part->value === 'static'
- && $container_type_part instanceof TNamedObject
- && strtolower($container_type_part->value) === 'self'
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TIterable) {
- if ($input_type_part instanceof TArray
- || $input_type_part instanceof TKeyedArray
- || $input_type_part instanceof TList
- ) {
- if ($input_type_part instanceof TKeyedArray) {
- $input_type_part = $input_type_part->getGenericArrayType();
- } elseif ($input_type_part instanceof TList) {
- $input_type_part = new TArray([Type::getInt(), $input_type_part->type_param]);
- }
-
- $all_types_contain = true;
-
- foreach ($input_type_part->type_params as $i => $input_param) {
- $container_param_offset = $i - (2 - count($container_type_part->type_params));
-
- if ($container_param_offset === -1) {
- continue;
- }
-
- $container_param = $container_type_part->type_params[$container_param_offset];
-
- if ($i === 0
- && $input_param->hasMixed()
- && $container_param->hasString()
- && $container_param->hasInt()
- ) {
- continue;
- }
-
- $array_comparison_result = new TypeComparisonResult();
-
- if (!$input_param->isEmpty()) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $input_param,
- $container_param,
- $input_param->ignore_nullable_issues,
- $input_param->ignore_falsable_issues,
- $array_comparison_result,
- $allow_interface_equality
- )
- && !$array_comparison_result->type_coerced_from_scalar
- ) {
- if ($atomic_comparison_result && $array_comparison_result->type_coerced_from_mixed) {
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
- $all_types_contain = false;
- } else {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast
- = $atomic_comparison_result->to_string_cast === true
- || $array_comparison_result->to_string_cast === true;
- }
- }
- }
- }
- return $all_types_contain;
- }
-
- if ($input_type_part->hasTraversableInterface($codebase)) {
- return true;
- }
- }
-
- if ($container_type_part instanceof TString || $container_type_part instanceof TScalar) {
- if ($input_type_part instanceof TNamedObject) {
- // check whether the object has a __toString method
- if ($codebase->classOrInterfaceExists($input_type_part->value)) {
- if ($codebase->php_major_version >= 8
- && ($input_type_part->value === 'Stringable'
- || ($codebase->classlikes->classExists($input_type_part->value)
- && $codebase->classlikes->classImplements($input_type_part->value, 'Stringable'))
- || $codebase->classlikes->interfaceExtends($input_type_part->value, 'Stringable'))
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast = true;
- }
-
- return true;
- }
-
- if ($codebase->methods->methodExists(
- new MethodIdentifier(
- $input_type_part->value,
- '__tostring'
- )
- )) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast = true;
- }
-
- return true;
- }
- }
-
- // PHP 5.6 doesn't support this natively, so this introduces a bug *just* when checking PHP 5.6 code
- if ($input_type_part->value === 'ReflectionType') {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast = true;
- }
-
- return true;
- }
- } elseif ($input_type_part instanceof TObjectWithProperties
- && isset($input_type_part->methods['__toString'])
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast = true;
- }
-
- return true;
- }
- }
-
- if ($container_type_part instanceof TCallable &&
- (
- $input_type_part instanceof TLiteralString
- || $input_type_part instanceof TCallableString
- || $input_type_part instanceof TArray
- || $input_type_part instanceof TKeyedArray
- || $input_type_part instanceof TList
- || (
- $input_type_part instanceof TNamedObject &&
- $codebase->classOrInterfaceExists($input_type_part->value) &&
- $codebase->methodExists($input_type_part->value . '::__invoke')
- )
- )
- ) {
- return CallableTypeComparator::isNotExplicitlyCallableTypeCallable(
- $codebase,
- $input_type_part,
- $container_type_part,
- $atomic_comparison_result
- );
- }
-
- if ($container_type_part instanceof TObject
- && $input_type_part instanceof TNamedObject
- ) {
- if ($container_type_part instanceof TObjectWithProperties
- && $input_type_part->value !== 'stdClass'
- ) {
- return KeyedArrayComparator::isContainedByObjectWithProperties(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- return true;
- }
-
- if ($container_type_part instanceof TNamedObject
- && $input_type_part instanceof TNamedObject
- && $container_type_part->was_static
- && !$input_type_part->was_static
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TObject && $container_type_part instanceof TNamedObject) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TNamedObject
- && $input_type_part instanceof TNamedObject
- && $codebase->classOrInterfaceOrEnumExists($input_type_part->value)
- && (
- (
- $codebase->classExists($container_type_part->value)
- && $codebase->classExtendsOrImplements(
- $container_type_part->value,
- $input_type_part->value
- )
- )
- ||
- (
- $codebase->interfaceExists($container_type_part->value)
- && $codebase->interfaceExtends(
- $container_type_part->value,
- $input_type_part->value
- )
- )
- )
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- return $input_type_part->getKey() === $container_type_part->getKey();
- }
-
- /**
- * Does the input param atomic type match the given param atomic type
- */
- public static function canBeIdentical(
- Codebase $codebase,
- Atomic $type1_part,
- Atomic $type2_part,
- bool $allow_interface_equality = true
- ): bool {
- if ((get_class($type1_part) === TList::class
- && $type2_part instanceof TNonEmptyList)
- || (get_class($type2_part) === TList::class
- && $type1_part instanceof TNonEmptyList)
- ) {
- return UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $type1_part->type_param,
- $type2_part->type_param
- );
- }
-
- if ((get_class($type1_part) === TArray::class
- && $type2_part instanceof TNonEmptyArray)
- || (get_class($type2_part) === TArray::class
- && $type1_part instanceof TNonEmptyArray)
- ) {
- return UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $type1_part->type_params[0],
- $type2_part->type_params[0]
- )
- && UnionTypeComparator::canExpressionTypesBeIdentical(
- $codebase,
- $type1_part->type_params[1],
- $type2_part->type_params[1]
- );
- }
-
- $first_comparison_result = new TypeComparisonResult();
- $second_comparison_result = new TypeComparisonResult();
-
- return (self::isContainedBy(
- $codebase,
- $type1_part,
- $type2_part,
- $allow_interface_equality,
- false,
- $first_comparison_result
- )
- && !$first_comparison_result->to_string_cast
- ) || (self::isContainedBy(
- $codebase,
- $type2_part,
- $type1_part,
- $allow_interface_equality,
- false,
- $second_comparison_result
- )
- && !$second_comparison_result->to_string_cast
- ) || ($first_comparison_result->type_coerced
- && $second_comparison_result->type_coerced
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php
deleted file mode 100644
index 107e3dd..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/CallableTypeComparator.php
+++ /dev/null
@@ -1,524 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Exception;
-use PhpParser\Node\Arg;
-use PhpParser\Node\Expr\Variable;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\MethodIdentifier;
-use Psalm\Internal\Provider\NodeDataProvider;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableList;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use UnexpectedValueException;
-
-use function end;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class CallableTypeComparator
-{
- /**
- * @param TCallable|TClosure $input_type_part
- * @param TCallable|TClosure $container_type_part
- */
- public static function isContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- ?TypeComparisonResult $atomic_comparison_result
- ): bool {
- if ($container_type_part->is_pure && !$input_type_part->is_pure) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = $input_type_part->is_pure === null;
- }
-
- return false;
- }
-
- if ($container_type_part->params !== null && $input_type_part->params === null) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
-
- return false;
- }
-
- if ($input_type_part->params !== null && $container_type_part->params !== null) {
- foreach ($input_type_part->params as $i => $input_param) {
- $container_param = null;
-
- if (isset($container_type_part->params[$i])) {
- $container_param = $container_type_part->params[$i];
- } elseif ($container_type_part->params) {
- $last_param = end($container_type_part->params);
-
- if ($last_param->is_variadic) {
- $container_param = $last_param;
- }
- }
-
- if (!$container_param) {
- if ($input_param->is_optional) {
- break;
- }
-
- return false;
- }
-
- if ($container_param->type
- && !$container_param->type->hasMixed()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $container_param->type,
- $input_param->type ?: Type::getMixed(),
- false,
- false,
- $atomic_comparison_result
- )
- ) {
- return false;
- }
- }
- }
-
- if (isset($container_type_part->return_type)) {
- if (!isset($input_type_part->return_type)) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
-
- return false;
- }
-
- $input_return = $input_type_part->return_type;
-
- if ($input_return->isVoid() && $container_type_part->return_type->isNullable()) {
- return true;
- }
-
- if (!$container_type_part->return_type->isVoid()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $input_return,
- $container_type_part->return_type,
- false,
- false,
- $atomic_comparison_result
- )
- ) {
- return false;
- }
- }
-
- return true;
- }
-
- public static function isNotExplicitlyCallableTypeCallable(
- Codebase $codebase,
- Atomic $input_type_part,
- TCallable $container_type_part,
- ?TypeComparisonResult $atomic_comparison_result
- ): bool {
- if ($input_type_part instanceof TList) {
- if ($input_type_part->type_param->isMixed()
- || $input_type_part->type_param->hasScalar()
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced_from_mixed = true;
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if (!$input_type_part->type_param->hasString()) {
- return false;
- }
-
- if (!$input_type_part instanceof TCallableList) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced_from_mixed = true;
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
- }
-
- if ($input_type_part instanceof TArray) {
- if ($input_type_part->type_params[1]->isMixed()
- || $input_type_part->type_params[1]->hasScalar()
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced_from_mixed = true;
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if (!$input_type_part->type_params[1]->hasString()) {
- return false;
- }
-
- if (!$input_type_part instanceof TCallableArray) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced_from_mixed = true;
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
- } elseif ($input_type_part instanceof TKeyedArray) {
- $method_id = self::getCallableMethodIdFromTKeyedArray($input_type_part);
-
- if ($method_id === 'not-callable') {
- return false;
- }
-
- if (!$method_id) {
- return true;
- }
-
- try {
- $method_id = $codebase->methods->getDeclaringMethodId($method_id);
-
- if (!$method_id) {
- return false;
- }
-
- $codebase->methods->getStorage($method_id);
- } catch (Exception $e) {
- return false;
- }
- }
-
- $input_callable = self::getCallableFromAtomic($codebase, $input_type_part, $container_type_part, null, true);
-
- if ($input_callable) {
- if (self::isContainedBy(
- $codebase,
- $input_callable,
- $container_type_part,
- $atomic_comparison_result
- ) === false
- ) {
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * @return TCallable|TClosure|null
- */
- public static function getCallableFromAtomic(
- Codebase $codebase,
- Atomic $input_type_part,
- ?TCallable $container_type_part = null,
- ?StatementsAnalyzer $statements_analyzer = null,
- bool $expand_callable = false
- ): ?Atomic {
- if ($input_type_part instanceof TCallable || $input_type_part instanceof TClosure) {
- return $input_type_part;
- }
-
- if ($input_type_part instanceof TLiteralString && $input_type_part->value) {
- try {
- $function_storage = $codebase->functions->getStorage(
- $statements_analyzer,
- strtolower($input_type_part->value)
- );
-
- if ($expand_callable) {
- $params = [];
-
- foreach ($function_storage->params as $param) {
- $param = clone $param;
-
- if ($param->type) {
- $param->type = TypeExpander::expandUnion(
- $codebase,
- $param->type,
- null,
- null,
- null,
- true,
- true,
- false,
- false,
- true
- );
- }
-
- $params[] = $param;
- }
-
- $return_type = null;
-
- if ($function_storage->return_type) {
- $return_type = TypeExpander::expandUnion(
- $codebase,
- $function_storage->return_type,
- null,
- null,
- null,
- true,
- true,
- false,
- false,
- true
- );
- }
- } else {
- $return_type = $function_storage->return_type;
- $params = $function_storage->params;
- }
-
- return new TCallable(
- 'callable',
- $params,
- $return_type,
- $function_storage->pure
- );
- } catch (UnexpectedValueException $e) {
- if (InternalCallMapHandler::inCallMap($input_type_part->value)) {
- $args = [];
-
- $nodes = new NodeDataProvider();
-
- if ($container_type_part && $container_type_part->params) {
- foreach ($container_type_part->params as $i => $param) {
- $arg = new Arg(
- new Variable('_' . $i)
- );
-
- if ($param->type) {
- $nodes->setType($arg->value, $param->type);
- }
-
- $args[] = $arg;
- }
- }
-
- $matching_callable = InternalCallMapHandler::getCallableFromCallMapById(
- $codebase,
- $input_type_part->value,
- $args,
- $nodes
- );
-
- $must_use = false;
-
- $matching_callable->is_pure = $codebase->functions->isCallMapFunctionPure(
- $codebase,
- $statements_analyzer->node_data ?? null,
- $input_type_part->value,
- null,
- $must_use
- );
-
- return $matching_callable;
- }
- }
- } elseif ($input_type_part instanceof TKeyedArray) {
- $method_id = self::getCallableMethodIdFromTKeyedArray($input_type_part);
- if ($method_id && $method_id !== 'not-callable') {
- try {
- $method_storage = $codebase->methods->getStorage($method_id);
- $method_fqcln = $method_id->fq_class_name;
-
- $converted_return_type = null;
-
- if ($method_storage->return_type) {
- $converted_return_type = TypeExpander::expandUnion(
- $codebase,
- $method_storage->return_type,
- $method_fqcln,
- $method_fqcln,
- null
- );
- }
-
- return new TCallable(
- 'callable',
- $method_storage->params,
- $converted_return_type,
- $method_storage->pure
- );
- } catch (UnexpectedValueException $e) {
- // do nothing
- }
- }
- } elseif ($input_type_part instanceof TNamedObject
- && $input_type_part->value === 'Closure'
- ) {
- return new TCallable();
- } elseif ($input_type_part instanceof TNamedObject
- && $codebase->classExists($input_type_part->value)
- ) {
- $invoke_id = new MethodIdentifier(
- $input_type_part->value,
- '__invoke'
- );
-
- if ($codebase->methods->methodExists($invoke_id)) {
- $declaring_method_id = $codebase->methods->getDeclaringMethodId($invoke_id);
-
- if ($declaring_method_id) {
- $method_storage = $codebase->methods->getStorage($declaring_method_id);
- $method_fqcln = $invoke_id->fq_class_name;
- $converted_return_type = null;
- if ($method_storage->return_type) {
- $converted_return_type = TypeExpander::expandUnion(
- $codebase,
- $method_storage->return_type,
- $method_fqcln,
- $method_fqcln,
- null
- );
- }
-
- return new TCallable(
- 'callable',
- $method_storage->params,
- $converted_return_type,
- $method_storage->pure
- );
- }
- }
- }
-
- return null;
- }
-
- /** @return null|'not-callable'|MethodIdentifier */
- public static function getCallableMethodIdFromTKeyedArray(
- TKeyedArray $input_type_part,
- ?Codebase $codebase = null,
- ?string $calling_method_id = null,
- ?string $file_name = null
- ) {
- if (!isset($input_type_part->properties[0])
- || !isset($input_type_part->properties[1])
- ) {
- return 'not-callable';
- }
-
- [$lhs, $rhs] = $input_type_part->properties;
-
- $rhs_low_info = $rhs->hasMixed() || $rhs->hasScalar();
-
- if ($rhs_low_info || !$rhs->isSingleStringLiteral()) {
- if (!$rhs_low_info && !$rhs->hasString()) {
- return 'not-callable';
- }
-
- if ($codebase && ($calling_method_id || $file_name)) {
- foreach ($lhs->getAtomicTypes() as $lhs_atomic_type) {
- if ($lhs_atomic_type instanceof TNamedObject) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($lhs_atomic_type->value) . '::',
- $calling_method_id ?: $file_name
- );
- } elseif ($lhs_atomic_type instanceof TTemplateParam) {
- $lhs_template_type = $lhs_atomic_type->as;
- if ($lhs_template_type->isSingle()) {
- $lhs_template_atomic_type = $lhs_template_type->getSingleAtomic();
- $member_id = null;
- if ($lhs_template_atomic_type instanceof TNamedObject) {
- $member_id = $lhs_template_atomic_type->value;
- } elseif ($lhs_template_atomic_type instanceof TClassString) {
- $member_id = $lhs_template_atomic_type->as;
- }
-
- if ($member_id) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($member_id) . '::',
- $calling_method_id ?: $file_name
- );
- }
- }
- }
- }
- }
-
- return null;
- }
-
- $method_name = $rhs->getSingleStringLiteral()->value;
-
- $class_name = null;
-
- if ($lhs->isSingleStringLiteral()) {
- $class_name = $lhs->getSingleStringLiteral()->value;
- if ($class_name[0] === '\\') {
- $class_name = substr($class_name, 1);
- }
- } elseif ($lhs->isSingle()) {
- foreach ($lhs->getAtomicTypes() as $lhs_atomic_type) {
- if ($lhs_atomic_type instanceof TNamedObject) {
- $class_name = $lhs_atomic_type->value;
- } elseif ($lhs_atomic_type instanceof TTemplateParam) {
- $lhs_template_type = $lhs_atomic_type->as;
- if ($lhs_template_type->isSingle()) {
- $lhs_template_atomic_type = $lhs_template_type->getSingleAtomic();
- if ($lhs_template_atomic_type instanceof TNamedObject) {
- $class_name = $lhs_template_atomic_type->value;
- } elseif ($lhs_template_atomic_type instanceof TClassString) {
- $class_name = $lhs_template_atomic_type->as;
- }
- }
- } elseif ($lhs_atomic_type instanceof TClassString
- && $lhs_atomic_type->as
- ) {
- $class_name = $lhs_atomic_type->as;
- }
- }
- }
-
- if ($class_name === 'self'
- || $class_name === 'static'
- || $class_name === 'parent'
- ) {
- return null;
- }
-
- if (!$class_name) {
- if ($codebase && ($calling_method_id || $file_name)) {
- $codebase->analyzer->addMixedMemberName(
- strtolower($method_name),
- $calling_method_id ?: $file_name
- );
- }
-
- return null;
- }
-
- return new MethodIdentifier(
- $class_name,
- strtolower($method_name)
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php
deleted file mode 100644
index afc0d2c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php
+++ /dev/null
@@ -1,92 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TTemplateParamClass;
-
-use function get_class;
-
-/**
- * @internal
- */
-class ClassLikeStringComparator
-{
- /**
- * @param TClassString|TLiteralClassString $input_type_part
- * @param TClassString|TLiteralClassString $container_type_part
- */
- public static function isContainedBy(
- Codebase $codebase,
- Scalar $input_type_part,
- Scalar $container_type_part,
- bool $allow_interface_equality,
- ?TypeComparisonResult $atomic_comparison_result = null
- ): bool {
- if ($container_type_part instanceof TLiteralClassString
- && $input_type_part instanceof TLiteralClassString
- ) {
- return $container_type_part->value === $input_type_part->value;
- }
-
- if ($container_type_part instanceof TTemplateParamClass
- && get_class($input_type_part) === TClassString::class
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TClassString
- && $container_type_part->as === 'object'
- && !$container_type_part->as_type
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TClassString
- && $input_type_part->as === 'object'
- && !$input_type_part->as_type
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- $fake_container_object = $container_type_part instanceof TClassString
- && $container_type_part->as_type
- ? $container_type_part->as_type
- : new TNamedObject(
- $container_type_part instanceof TClassString
- ? $container_type_part->as
- : $container_type_part->value
- );
-
- $fake_input_object = $input_type_part instanceof TClassString
- && $input_type_part->as_type
- ? $input_type_part->as_type
- : new TNamedObject(
- $input_type_part instanceof TClassString
- ? $input_type_part->as
- : $input_type_part->value
- );
-
- return AtomicTypeComparator::isContainedBy(
- $codebase,
- $fake_input_object,
- $fake_container_object,
- $allow_interface_equality,
- false,
- $atomic_comparison_result
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php
deleted file mode 100644
index 8cd529c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/GenericTypeComparator.php
+++ /dev/null
@@ -1,207 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TNamedObject;
-
-/**
- * @internal
- */
-class GenericTypeComparator
-{
- /**
- * @param TGenericObject|TIterable $container_type_part
- */
- public static function isContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- bool $allow_interface_equality = false,
- ?TypeComparisonResult $atomic_comparison_result = null
- ): bool {
- $all_types_contain = true;
- $container_was_iterable = false;
-
- if ($container_type_part instanceof TIterable
- && !$container_type_part->extra_types
- && !$input_type_part instanceof TIterable
- ) {
- $container_type_part = new TGenericObject(
- 'Traversable',
- $container_type_part->type_params
- );
-
- $container_was_iterable = true;
- }
-
- if (!$input_type_part instanceof TNamedObject && !$input_type_part instanceof TIterable) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
-
- return false;
- }
-
- $container_type_params_covariant = [];
-
- $input_type_params = TemplateStandinTypeReplacer::getMappedGenericTypeParams(
- $codebase,
- $input_type_part,
- $container_type_part,
- $container_type_params_covariant
- );
-
- foreach ($input_type_params as $i => $input_param) {
- if (!isset($container_type_part->type_params[$i])) {
- break;
- }
-
- $container_param = $container_type_part->type_params[$i];
-
- if ($input_param->isEmpty()) {
- if ($atomic_comparison_result) {
- if (!$atomic_comparison_result->replacement_atomic_type) {
- $atomic_comparison_result->replacement_atomic_type = clone $input_type_part;
- }
-
- if ($atomic_comparison_result->replacement_atomic_type instanceof TGenericObject) {
- /** @psalm-suppress PropertyTypeCoercion */
- $atomic_comparison_result->replacement_atomic_type->type_params[$i]
- = clone $container_param;
- }
- }
-
- continue;
- }
-
- $param_comparison_result = new TypeComparisonResult();
-
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $input_param,
- $container_param,
- $input_param->ignore_nullable_issues,
- $input_param->ignore_falsable_issues,
- $param_comparison_result,
- $allow_interface_equality
- )) {
- if ($input_type_part->value === 'Generator'
- && $i === 2
- && $param_comparison_result->type_coerced_from_mixed
- ) {
- continue;
- }
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced
- = $param_comparison_result->type_coerced === true
- && $atomic_comparison_result->type_coerced !== false;
-
- $atomic_comparison_result->type_coerced_from_mixed
- = $param_comparison_result->type_coerced_from_mixed === true
- && $atomic_comparison_result->type_coerced_from_mixed !== false;
-
- $atomic_comparison_result->type_coerced_from_as_mixed
- = !$container_was_iterable
- && $param_comparison_result->type_coerced_from_as_mixed === true
- && $atomic_comparison_result->type_coerced_from_as_mixed !== false;
-
- $atomic_comparison_result->to_string_cast
- = $param_comparison_result->to_string_cast === true
- && $atomic_comparison_result->to_string_cast !== false;
-
- $atomic_comparison_result->type_coerced_from_scalar
- = $param_comparison_result->type_coerced_from_scalar === true
- && $atomic_comparison_result->type_coerced_from_scalar !== false;
-
- $atomic_comparison_result->scalar_type_match_found
- = $param_comparison_result->scalar_type_match_found === true
- && $atomic_comparison_result->scalar_type_match_found !== false;
- }
-
- // if the container was an iterable then there was no mapping
- // from a template type
- if ($container_was_iterable || !$param_comparison_result->type_coerced_from_as_mixed) {
- $all_types_contain = false;
- }
- } elseif (!$input_type_part instanceof TIterable
- && !$container_type_part instanceof TIterable
- && !$container_param->hasTemplate()
- && !$input_param->hasTemplate()
- ) {
- if ($input_param->containsAnyLiteral()) {
- if ($atomic_comparison_result) {
- if (!$atomic_comparison_result->replacement_atomic_type) {
- $atomic_comparison_result->replacement_atomic_type = clone $input_type_part;
- }
-
- if ($atomic_comparison_result->replacement_atomic_type instanceof TGenericObject) {
- /** @psalm-suppress PropertyTypeCoercion */
- $atomic_comparison_result->replacement_atomic_type->type_params[$i]
- = clone $container_param;
- }
- }
- } else {
- if (!($container_type_params_covariant[$i] ?? false)
- && !$container_param->had_template
- ) {
- // Make sure types are basically the same
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $container_param,
- $input_param,
- $container_param->ignore_nullable_issues,
- $container_param->ignore_falsable_issues,
- $param_comparison_result,
- $allow_interface_equality
- ) || $param_comparison_result->type_coerced
- ) {
- if ($container_param->hasFormerStaticObject()
- && $input_param->isFormerStaticObject()
- && UnionTypeComparator::isContainedBy(
- $codebase,
- $input_param,
- $container_param,
- $container_param->ignore_nullable_issues,
- $container_param->ignore_falsable_issues,
- $param_comparison_result,
- $allow_interface_equality
- )
- ) {
- // do nothing
- } else {
- if ($container_param->hasMixed() || $container_param->isArrayKey()) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced_from_mixed = true;
- }
- } else {
- $all_types_contain = false;
- }
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = false;
- }
- }
- }
- }
- }
- }
- }
-
- if ($all_types_contain) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast = false;
- }
-
- return true;
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php
deleted file mode 100644
index 486a4df..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/IntegerRangeComparator.php
+++ /dev/null
@@ -1,180 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TNonspecificLiteralInt;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function count;
-use function get_class;
-
-/**
- * @internal
- */
-class IntegerRangeComparator
-{
- /**
- * This method is used to check if an integer range can be contained in another
- */
- public static function isContainedBy(
- TIntRange $input_type_part,
- TIntRange $container_type_part
- ): bool {
- $is_input_min = $input_type_part->min_bound === null;
- $is_input_max = $input_type_part->max_bound === null;
- $is_container_min = $container_type_part->min_bound === null;
- $is_container_max = $container_type_part->max_bound === null;
-
- $is_input_min_in_container = (
- $is_container_min ||
- (!$is_input_min && $container_type_part->min_bound <= $input_type_part->min_bound)
- );
- $is_input_max_in_container = (
- $is_container_max ||
- (!$is_input_max && $container_type_part->max_bound >= $input_type_part->max_bound)
- );
- return $is_input_min_in_container && $is_input_max_in_container;
- }
-
- /**
- * This method is used to check if an integer range can be contained by multiple int types
- * Worst case scenario, the input is `int<-50,max>` and container is `-50|int<-49,50>|positive-int|57`
- */
- public static function isContainedByUnion(
- TIntRange $input_type_part,
- Union $container_type
- ): bool {
- $container_atomic_types = $container_type->getAtomicTypes();
- $reduced_range = clone $input_type_part;
-
- if (isset($container_atomic_types['int'])) {
- if (get_class($container_atomic_types['int']) === TInt::class) {
- return true;
- }
-
- if (get_class($container_atomic_types['int']) === TNonspecificLiteralInt::class) {
- return true;
- }
-
- if (get_class($container_atomic_types['int']) === TPositiveInt::class) {
- if ($input_type_part->isPositive()) {
- return true;
- }
-
- //every positive integer is satisfied by the positive-int int container so we reduce the range
- $reduced_range->max_bound = 0;
- unset($container_atomic_types['int']);
- } else {
- throw new UnexpectedValueException('Should not happen: unknown int key');
- }
- }
-
- $new_nb_atomics = count($container_atomic_types);
- //loop until we get to a stable situation. Either we can't remove atomics or we have a definite result
- do {
- $nb_atomics = $new_nb_atomics;
- $result_reduction = self::reduceRangeIncrementally($container_atomic_types, $reduced_range);
- $new_nb_atomics = count($container_atomic_types);
- } while ($result_reduction === null && $nb_atomics !== $new_nb_atomics);
-
- if ($result_reduction === null && $nb_atomics === 0) {
- //the range could not be reduced enough and there is no more atomics, it's not contained
- return false;
- }
-
- return $result_reduction ?? false;
- }
-
- /**
- * This method receives an array of atomics from the container and a range.
- * The goal is to use values in atomics in order to reduce the range.
- * Once the range is empty, it means that every value in range was covered by some atomics combination
- * @param array<string, Atomic> $container_atomic_types
- */
- private static function reduceRangeIncrementally(array &$container_atomic_types, TIntRange $reduced_range): ?bool
- {
- foreach ($container_atomic_types as $key => $container_atomic_type) {
- if ($container_atomic_type instanceof TIntRange) {
- if (self::isContainedBy($reduced_range, $container_atomic_type)) {
- if ($container_atomic_type->max_bound === null && $container_atomic_type->min_bound === null) {
- //this container range covers any integer
- return true;
- }
- if ($container_atomic_type->max_bound === null) {
- //this container range is int<X, max>
- //X-1 becomes the max of our reduced range if it was higher
- $reduced_range->max_bound = TIntRange::getNewLowestBound(
- $container_atomic_type->min_bound - 1,
- $reduced_range->max_bound ?? $container_atomic_type->min_bound - 1
- );
- unset($container_atomic_types[$key]); //we don't need this one anymore
- continue;
- }
- if ($container_atomic_type->min_bound === null) {
- //this container range is int<min, X>
- //X+1 becomes the min of our reduced range if it was lower
- $reduced_range->min_bound = TIntRange::getNewHighestBound(
- $container_atomic_type->max_bound + 1,
- $reduced_range->min_bound ?? $container_atomic_type->max_bound + 1
- );
- unset($container_atomic_types[$key]); //we don't need this one anymore
- continue;
- }
- //if the container range has no 'null' bound, it's more complex
- //in this case, we can only reduce if the container include one bound of our reduced range
- if ($reduced_range->min_bound !== null
- && $container_atomic_type->contains($reduced_range->min_bound)
- ) {
- //this container range is int<X, Y> and contains the min of our reduced range.
- //the min from our reduced range becomes Y + 1
- $reduced_range->min_bound = $container_atomic_type->max_bound + 1;
- unset($container_atomic_types[$key]); //we don't need this one anymore
- } elseif ($reduced_range->max_bound !== null
- && $container_atomic_type->contains($reduced_range->max_bound)) {
- //this container range is int<X, Y> and contains the max of our reduced range.
- //the max from our reduced range becomes X - 1
- $reduced_range->max_bound = $container_atomic_type->min_bound - 1;
- unset($container_atomic_types[$key]); //we don't need this one anymore
- }
- //there is probably a case here where we could unset containers when they're not at all in our range
- } else {
- //the range in input is wider than container, we return false
- return false;
- }
- } elseif ($container_atomic_type instanceof TLiteralInt) {
- if (!$reduced_range->contains($container_atomic_type->value)) {
- unset($container_atomic_types[$key]); //we don't need this one anymore
- } elseif ($reduced_range->min_bound === $container_atomic_type->value) {
- $reduced_range->min_bound++;
- unset($container_atomic_types[$key]); //we don't need this one anymore
- } elseif ($reduced_range->max_bound === $container_atomic_type->value) {
- $reduced_range->max_bound--;
- unset($container_atomic_types[$key]); //we don't need this one anymore
- }
- }
- }
-
- //there is probably a case here if we're left only with TLiteralInt where we could return false if there's less
- //of them than numbers in the reduced range
-
- //there is also a case where if there's not TLiteralInt anymore and we're left with TIntRange that don't contain
- //bounds from our reduced range where we could return false
-
- //if our reduced range has its min bound superior to its max bound, it means the container covers it all.
- if ($reduced_range->min_bound !== null &&
- $reduced_range->max_bound !== null &&
- $reduced_range->min_bound > $reduced_range->max_bound
- ) {
- return true;
- }
-
- //if we didn't return true or false before then the result is inconclusive for this round
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php
deleted file mode 100644
index bb42838..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php
+++ /dev/null
@@ -1,166 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-
-use function is_string;
-
-/**
- * @internal
- */
-class KeyedArrayComparator
-{
- /**
- * @param TKeyedArray|TObjectWithProperties $input_type_part
- * @param TKeyedArray|TObjectWithProperties $container_type_part
- */
- public static function isContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- bool $allow_interface_equality,
- ?TypeComparisonResult $atomic_comparison_result
- ): bool {
- $all_types_contain = true;
-
- foreach ($container_type_part->properties as $key => $container_property_type) {
- if (!isset($input_type_part->properties[$key])) {
- if (!$container_property_type->possibly_undefined) {
- $all_types_contain = false;
- }
-
- continue;
- }
-
- $input_property_type = $input_type_part->properties[$key];
-
- $property_type_comparison = new TypeComparisonResult();
-
- if (!$input_property_type->isEmpty()) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $input_property_type,
- $container_property_type,
- $input_property_type->ignore_nullable_issues,
- $input_property_type->ignore_falsable_issues,
- $property_type_comparison,
- $allow_interface_equality
- )
- && !$property_type_comparison->type_coerced_from_scalar
- ) {
- $inverse_property_type_comparison = new TypeComparisonResult();
-
- if ($atomic_comparison_result) {
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $container_property_type,
- $input_property_type,
- false,
- false,
- $inverse_property_type_comparison,
- $allow_interface_equality
- )
- || $inverse_property_type_comparison->type_coerced_from_scalar
- ) {
- $atomic_comparison_result->type_coerced = true;
- }
- }
-
- $all_types_contain = false;
- } else {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->to_string_cast
- = $atomic_comparison_result->to_string_cast === true
- || $property_type_comparison->to_string_cast === true;
- }
- }
- }
- }
- return $all_types_contain;
- }
-
- public static function isContainedByObjectWithProperties(
- Codebase $codebase,
- TNamedObject $input_type_part,
- TObjectWithProperties $container_type_part,
- bool $allow_interface_equality,
- ?TypeComparisonResult $atomic_comparison_result
- ): bool {
- $all_types_contain = true;
-
- foreach ($container_type_part->properties as $property_name => $container_property_type) {
- if (!is_string($property_name)) {
- continue;
- }
-
- if (!$codebase->classlikes->classOrInterfaceExists($input_type_part->value)) {
- $all_types_contain = false;
-
- continue;
- }
-
- if (!$codebase->properties->propertyExists(
- $input_type_part->value . '::$' . $property_name,
- true
- )) {
- $all_types_contain = false;
-
- continue;
- }
-
- $property_declaring_class = (string) $codebase->properties->getDeclaringClassForProperty(
- $input_type_part . '::$' . $property_name,
- true
- );
-
- $class_storage = $codebase->classlike_storage_provider->get($property_declaring_class);
-
- $input_property_storage = $class_storage->properties[$property_name];
-
- $input_property_type = $input_property_storage->type ?: Type::getMixed();
-
- $property_type_comparison = new TypeComparisonResult();
-
- if (!$input_property_type->isEmpty()
- && !UnionTypeComparator::isContainedBy(
- $codebase,
- $input_property_type,
- $container_property_type,
- false,
- false,
- $property_type_comparison,
- $allow_interface_equality
- )
- && !$property_type_comparison->type_coerced_from_scalar
- ) {
- $inverse_property_type_comparison = new TypeComparisonResult();
-
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- $container_property_type,
- $input_property_type,
- false,
- false,
- $inverse_property_type_comparison,
- $allow_interface_equality
- )
- || $inverse_property_type_comparison->type_coerced_from_scalar
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
- }
-
- $all_types_contain = false;
- }
- }
-
- return $all_types_contain;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php
deleted file mode 100644
index b7b4ff3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php
+++ /dev/null
@@ -1,311 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateParam;
-
-use function array_merge;
-use function in_array;
-use function strpos;
-use function strtolower;
-
-/**
- * @internal
- */
-class ObjectComparator
-{
- /**
- * @param TNamedObject|TTemplateParam|TIterable $input_type_part
- * @param TNamedObject|TTemplateParam|TIterable $container_type_part
- *
- */
- public static function isShallowlyContainedBy(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- bool $allow_interface_equality,
- ?TypeComparisonResult $atomic_comparison_result
- ): bool {
- $intersection_input_types = $input_type_part->extra_types ?: [];
- $intersection_input_types[$input_type_part->getKey(false)] = $input_type_part;
-
- if ($input_type_part instanceof TTemplateParam) {
- foreach ($input_type_part->as->getAtomicTypes() as $g) {
- if ($g instanceof TNamedObject && $g->extra_types) {
- $intersection_input_types = array_merge(
- $intersection_input_types,
- $g->extra_types
- );
- }
- }
- }
-
- $intersection_container_types = $container_type_part->extra_types ?: [];
- $intersection_container_types[$container_type_part->getKey(false)] = $container_type_part;
-
- if ($container_type_part instanceof TTemplateParam) {
- foreach ($container_type_part->as->getAtomicTypes() as $g) {
- if ($g instanceof TNamedObject && $g->extra_types) {
- $intersection_container_types = array_merge(
- $intersection_container_types,
- $g->extra_types
- );
- }
- }
- }
-
- foreach ($intersection_container_types as $container_type_key => $intersection_container_type) {
- $container_was_static = false;
-
- if ($intersection_container_type instanceof TIterable) {
- $intersection_container_type_lower = 'iterable';
- } elseif ($intersection_container_type instanceof TObjectWithProperties) {
- $intersection_container_type_lower = 'object';
- } elseif ($intersection_container_type instanceof TTemplateParam) {
- if (!$allow_interface_equality) {
- if (isset($intersection_input_types[$container_type_key])) {
- continue;
- }
-
- foreach ($intersection_input_types as $intersection_input_type) {
- if ($intersection_input_type instanceof TTemplateParam
- && (strpos($intersection_container_type->defining_class, 'fn-') === 0
- || strpos($intersection_input_type->defining_class, 'fn-') === 0)
- ) {
- if (strpos($intersection_input_type->defining_class, 'fn-') === 0
- && strpos($intersection_container_type->defining_class, 'fn-') === 0
- && $intersection_input_type->defining_class
- !== $intersection_container_type->defining_class
- ) {
- continue 2;
- }
-
- foreach ($intersection_input_type->as->getAtomicTypes() as $input_as_atomic) {
- if ($input_as_atomic->equals($intersection_container_type, false)) {
- continue 3;
- }
- }
- } elseif ($intersection_input_type instanceof TTemplateParam) {
- $container_param = $intersection_container_type->param_name;
- $container_class = $intersection_container_type->defining_class;
- $input_class_like = $codebase->classlikes
- ->getStorageFor($intersection_input_type->defining_class);
-
- if ($codebase->classlikes->traitExists($container_class)
- && $input_class_like !== null
- && isset(
- $input_class_like->template_extended_params[$container_class][$container_param]
- )) {
- continue 2;
- }
- }
- }
-
- return false;
- }
-
- if ($intersection_container_type->as->isMixed()) {
- continue;
- }
-
- $intersection_container_type_lower = null;
-
- foreach ($intersection_container_type->as->getAtomicTypes() as $g) {
- if ($g instanceof TNull) {
- continue;
- }
-
- if ($g instanceof TObject) {
- continue 2;
- }
-
- if (!$g instanceof TNamedObject) {
- continue 2;
- }
-
- $intersection_container_type_lower = strtolower($g->value);
- }
-
- if ($intersection_container_type_lower === null) {
- return false;
- }
- } else {
- $container_was_static = $intersection_container_type->was_static;
-
- $intersection_container_type_lower = strtolower(
- $codebase->classlikes->getUnAliasedName(
- $intersection_container_type->value
- )
- );
- }
-
- foreach ($intersection_input_types as $intersection_input_key => $intersection_input_type) {
- $input_was_static = false;
-
- if ($intersection_input_type instanceof TIterable) {
- $intersection_input_type_lower = 'iterable';
- } elseif ($intersection_input_type instanceof TObjectWithProperties) {
- $intersection_input_type_lower = 'object';
- } elseif ($intersection_input_type instanceof TTemplateParam) {
- if ($intersection_input_type->as->isMixed()) {
- continue;
- }
-
- $intersection_input_type_lower = null;
-
- foreach ($intersection_input_type->as->getAtomicTypes() as $g) {
- if ($g instanceof TNull) {
- continue;
- }
-
- if (!$g instanceof TNamedObject) {
- continue 2;
- }
-
- $intersection_input_type_lower = strtolower($g->value);
- }
-
- if ($intersection_input_type_lower === null) {
- return false;
- }
- } else {
- $input_was_static = $intersection_input_type->was_static;
-
- $intersection_input_type_lower = strtolower(
- $codebase->classlikes->getUnAliasedName(
- $intersection_input_type->value
- )
- );
- }
-
- if ($intersection_container_type instanceof TTemplateParam
- && $intersection_input_type instanceof TTemplateParam
- ) {
- if ($intersection_container_type->param_name !== $intersection_input_type->param_name
- || ($intersection_container_type->defining_class
- !== $intersection_input_type->defining_class
- && strpos($intersection_input_type->defining_class, 'fn-') !== 0
- && strpos($intersection_container_type->defining_class, 'fn-') !== 0)
- ) {
- if (strpos($intersection_input_type->defining_class, 'fn-') !== 0) {
- $input_class_storage = $codebase->classlike_storage_provider->get(
- $intersection_input_type->defining_class
- );
-
- if (isset($input_class_storage->template_extended_params
- [$intersection_container_type->defining_class]
- [$intersection_container_type->param_name])
- ) {
- continue;
- }
- }
-
- return false;
- }
- }
-
- if (!$intersection_container_type instanceof TTemplateParam
- || $intersection_input_type instanceof TTemplateParam
- ) {
- if ($intersection_container_type_lower === $intersection_input_type_lower) {
- if ($container_was_static
- && !$input_was_static
- && !$intersection_input_type instanceof TTemplateParam
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- continue;
- }
-
- continue 2;
- }
-
- if ($intersection_input_type_lower === 'generator'
- && in_array($intersection_container_type_lower, ['iterator', 'traversable', 'iterable'], true)
- ) {
- continue 2;
- }
-
- if ($intersection_container_type_lower === 'iterable') {
- if ($intersection_input_type_lower === 'traversable'
- || ($codebase->classlikes->classExists($intersection_input_type_lower)
- && $codebase->classlikes->classImplements(
- $intersection_input_type_lower,
- 'Traversable'
- ))
- || ($codebase->classlikes->interfaceExists($intersection_input_type_lower)
- && $codebase->classlikes->interfaceExtends(
- $intersection_input_type_lower,
- 'Traversable'
- ))
- ) {
- continue 2;
- }
- }
-
- if ($intersection_input_type_lower === 'traversable'
- && $intersection_container_type_lower === 'iterable'
- ) {
- continue 2;
- }
-
- $input_type_is_interface = $codebase->interfaceExists($intersection_input_type_lower);
- $container_type_is_interface = $codebase->interfaceExists($intersection_container_type_lower);
-
- if ($allow_interface_equality
- && $container_type_is_interface
- && ($input_type_is_interface || !isset($intersection_container_types[$intersection_input_key]))
- ) {
- continue 2;
- }
-
- if (($codebase->classExists($intersection_input_type_lower)
- || $codebase->classlikes->enumExists($intersection_input_type_lower))
- && $codebase->classOrInterfaceExists($intersection_container_type_lower)
- && $codebase->classExtendsOrImplements(
- $intersection_input_type_lower,
- $intersection_container_type_lower
- )
- ) {
- if ($container_was_static && !$input_was_static) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- continue;
- }
-
- continue 2;
- }
-
- if ($input_type_is_interface
- && $codebase->interfaceExtends(
- $intersection_input_type_lower,
- $intersection_container_type_lower
- )
- ) {
- continue 2;
- }
- }
-
- if (ExpressionAnalyzer::isMock($intersection_input_type_lower)) {
- return true;
- }
- }
-
- return false;
- }
-
- return true;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php
deleted file mode 100644
index 12b1dd9..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ScalarTypeComparator.php
+++ /dev/null
@@ -1,640 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TDependentGetDebugType;
-use Psalm\Type\Atomic\TDependentGetType;
-use Psalm\Type\Atomic\TDependentListKey;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\THtmlEscapedString;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyLowercaseString;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonFalsyString;
-use Psalm\Type\Atomic\TNonspecificLiteralInt;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TSingleLetter;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateKeyOf;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Atomic\TTraitString;
-use Psalm\Type\Atomic\TTrue;
-
-use function get_class;
-use function is_numeric;
-use function strtolower;
-
-/**
- * @internal
- */
-class ScalarTypeComparator
-{
- public static function isContainedBy(
- Codebase $codebase,
- Scalar $input_type_part,
- Scalar $container_type_part,
- bool $allow_interface_equality = false,
- bool $allow_float_int_equality = true,
- ?TypeComparisonResult $atomic_comparison_result = null
- ): bool {
- if (get_class($container_type_part) === TString::class
- && $input_type_part instanceof TString
- ) {
- return true;
- }
-
- if (get_class($container_type_part) === TInt::class
- && $input_type_part instanceof TInt
- ) {
- return true;
- }
-
- if (get_class($container_type_part) === TFloat::class
- && $input_type_part instanceof TFloat
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TNonEmptyString
- && get_class($input_type_part) === TString::class
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TNonspecificLiteralString
- && ($input_type_part instanceof TLiteralString || $input_type_part instanceof TNonspecificLiteralString)
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TNonspecificLiteralString) {
- if ($input_type_part instanceof TString) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TNonspecificLiteralInt
- && ($input_type_part instanceof TLiteralInt
- || $input_type_part instanceof TNonspecificLiteralInt)
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TNonspecificLiteralInt) {
- if ($input_type_part instanceof TInt) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TCallableString
- && (get_class($container_type_part) === TSingleLetter::class
- || get_class($container_type_part) === TNonEmptyString::class
- || get_class($container_type_part) === TNonFalsyString::class
- || get_class($container_type_part) === TLowercaseString::class)
- ) {
- return true;
- }
-
- if (($container_type_part instanceof TLowercaseString
- || $container_type_part instanceof TNonEmptyLowercaseString)
- && $input_type_part instanceof TString
- ) {
- if (($input_type_part instanceof TLowercaseString
- && $container_type_part instanceof TLowercaseString)
- || ($input_type_part instanceof TNonEmptyLowercaseString
- && $container_type_part instanceof TNonEmptyLowercaseString)
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TNonEmptyLowercaseString
- && $container_type_part instanceof TLowercaseString
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TLowercaseString
- && $container_type_part instanceof TNonEmptyLowercaseString
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TLiteralString) {
- if (strtolower($input_type_part->value) === $input_type_part->value) {
- return $input_type_part->value || $container_type_part instanceof TLowercaseString;
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TClassString) {
- return false;
- }
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TDependentGetClass) {
- $first_type = $container_type_part->as_type->getSingleAtomic();
-
- $container_type_part = new TClassString(
- 'object',
- $first_type instanceof TNamedObject ? $first_type : null
- );
- }
-
- if ($input_type_part instanceof TDependentGetClass) {
- $first_type = $input_type_part->as_type->getSingleAtomic();
-
- if ($first_type instanceof TTemplateParam) {
- $object_type = $first_type->as->getSingleAtomic();
-
- $input_type_part = new TTemplateParamClass(
- $first_type->param_name,
- $first_type->as->getId(),
- $object_type instanceof TNamedObject ? $object_type : null,
- $first_type->defining_class
- );
- } else {
- $input_type_part = new TClassString(
- 'object',
- $first_type instanceof TNamedObject ? $first_type : null
- );
- }
- }
-
- if ($input_type_part instanceof TDependentGetType) {
- $input_type_part = new TString();
-
- if ($container_type_part instanceof TLiteralString) {
- return isset(ClassLikeAnalyzer::GETTYPE_TYPES[$container_type_part->value]);
- }
- }
-
- if ($container_type_part instanceof TDependentGetDebugType) {
- return $input_type_part instanceof TString;
- }
-
- if ($input_type_part instanceof TDependentGetDebugType) {
- $input_type_part = new TString();
- }
-
- if ($container_type_part instanceof TDependentGetType) {
- $container_type_part = new TString();
-
- if ($input_type_part instanceof TLiteralString) {
- return isset(ClassLikeAnalyzer::GETTYPE_TYPES[$input_type_part->value]);
- }
- }
-
- if ($input_type_part instanceof TFalse
- && $container_type_part instanceof TBool
- && !($container_type_part instanceof TTrue)
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TTrue
- && $container_type_part instanceof TBool
- && !($container_type_part instanceof TFalse)
- ) {
- return true;
- }
-
- // from https://wiki.php.net/rfc/scalar_type_hints_v5:
- //
- // > int types can resolve a parameter type of float
- if ($input_type_part instanceof TInt
- && $container_type_part instanceof TFloat
- && !$container_type_part instanceof TLiteralFloat
- && $allow_float_int_equality
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TArrayKey
- && $input_type_part instanceof TNumeric
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TArrayKey
- && ($input_type_part instanceof TInt
- || $input_type_part instanceof TString
- || $input_type_part instanceof TTemplateKeyOf)
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TTemplateKeyOf) {
- foreach ($input_type_part->as->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TArray) {
- /** @var Scalar $array_key_atomic */
- foreach ($atomic_type->type_params[0]->getAtomicTypes() as $array_key_atomic) {
- if (!self::isContainedBy(
- $codebase,
- $array_key_atomic,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- )) {
- return false;
- }
- }
- }
- }
-
- return true;
- }
-
- if ($input_type_part instanceof TArrayKey &&
- ($container_type_part instanceof TInt || $container_type_part instanceof TString)
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_mixed = true;
- $atomic_comparison_result->scalar_type_match_found = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TScalar && $input_type_part instanceof Scalar) {
- return true;
- }
-
- if (get_class($container_type_part) === TDependentListKey::class
- && ($input_type_part instanceof TLiteralInt
- || $input_type_part instanceof TPositiveInt)
- ) {
- return true;
- }
-
- if (get_class($container_type_part) === TFloat::class && $input_type_part instanceof TLiteralFloat) {
- return true;
- }
-
- if ((get_class($container_type_part) === TNonEmptyString::class
- || get_class($container_type_part) === TNonEmptyNonspecificLiteralString::class)
- && $input_type_part instanceof TNonFalsyString
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TNonFalsyString
- && $input_type_part instanceof TNonFalsyString
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TNonFalsyString
- && ($input_type_part instanceof TNonEmptyString
- || $input_type_part instanceof TNonEmptyNonspecificLiteralString)
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TNonEmptyString
- && $input_type_part instanceof TLiteralString
- && $input_type_part->value === ''
- ) {
- return false;
- }
-
- if ($container_type_part instanceof TNonFalsyString
- && $input_type_part instanceof TLiteralString
- && $input_type_part->value === '0'
- ) {
- return false;
- }
-
- if ((get_class($container_type_part) === TNonEmptyString::class
- || get_class($container_type_part) === TNonFalsyString::class
- || get_class($container_type_part) === TSingleLetter::class)
- && $input_type_part instanceof TLiteralString
- ) {
- return true;
- }
-
- if (get_class($container_type_part) === TDependentListKey::class
- && $input_type_part instanceof TInt
- ) {
- return true;
- }
-
- if ((get_class($input_type_part) === TInt::class && $container_type_part instanceof TLiteralInt)
- || (get_class($input_type_part) === TPositiveInt::class
- && $container_type_part instanceof TLiteralInt
- && $container_type_part->value > 0)
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TIntRange && $container_type_part instanceof TIntRange) {
- return IntegerRangeComparator::isContainedBy(
- $input_type_part,
- $container_type_part
- );
- }
-
- if ($input_type_part instanceof TInt && $container_type_part instanceof TPositiveInt) {
- if ($input_type_part instanceof TPositiveInt) {
- return true;
- }
- if ($input_type_part instanceof TLiteralInt) {
- return $input_type_part->value > 0;
- }
- if ($input_type_part instanceof TIntRange) {
- return $input_type_part->isPositive();
- }
-
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- if ($input_type_part instanceof TInt && $container_type_part instanceof TIntRange) {
- if ($input_type_part instanceof TPositiveInt) {
- if ($container_type_part->min_bound > 1) {
- //any positive int can't be pushed inside a range with a min > 1
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- if ($container_type_part->max_bound !== null) {
- //any positive int can't be pushed inside a range where the max bound isn't max without coercion
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- return true;
- }
- if ($input_type_part instanceof TLiteralInt) {
- $min_bound = $container_type_part->min_bound;
- $max_bound = $container_type_part->max_bound;
-
- return
- ($min_bound === null || $min_bound <= $input_type_part->value) &&
- ($max_bound === null || $max_bound >= $input_type_part->value);
- }
-
- //any int can't be pushed inside a range without coercion (unless the range is from min to max)
- if ($container_type_part->min_bound !== null || $container_type_part->max_bound !== null) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
- }
-
- return false;
- }
-
- if (get_class($input_type_part) === TFloat::class && $container_type_part instanceof TLiteralFloat) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- if ((get_class($input_type_part) === TString::class
- || get_class($input_type_part) === TSingleLetter::class
- || $input_type_part instanceof TNonEmptyString
- || $input_type_part instanceof TNonspecificLiteralString)
- && $container_type_part instanceof TLiteralString
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- if (($input_type_part instanceof TLowercaseString
- || $input_type_part instanceof TNonEmptyLowercaseString)
- && $container_type_part instanceof TLiteralString
- && strtolower($container_type_part->value) === $container_type_part->value
- ) {
- if ($atomic_comparison_result
- && ($container_type_part->value)
- ) {
- $atomic_comparison_result->type_coerced = true;
- $atomic_comparison_result->type_coerced_from_scalar = true;
- }
-
- return false;
- }
-
- if (($container_type_part instanceof TClassString || $container_type_part instanceof TLiteralClassString)
- && ($input_type_part instanceof TClassString || $input_type_part instanceof TLiteralClassString)
- ) {
- return ClassLikeStringComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $atomic_comparison_result
- );
- }
-
- if ($container_type_part instanceof TString && $input_type_part instanceof TTraitString) {
- return true;
- }
-
- if ($container_type_part instanceof TTraitString
- && (get_class($input_type_part) === TString::class
- || $input_type_part instanceof TNonEmptyString
- || $input_type_part instanceof TNonEmptyNonspecificLiteralString)
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if (($input_type_part instanceof TClassString
- || $input_type_part instanceof TLiteralClassString)
- && (get_class($container_type_part) === TSingleLetter::class
- || get_class($container_type_part) === TNonEmptyString::class
- || get_class($container_type_part) === TNonFalsyString::class)
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TNumericString
- && get_class($container_type_part) === TNonEmptyString::class
- ) {
- return true;
- }
-
- if ($container_type_part instanceof TString
- && ($input_type_part instanceof TNumericString
- || $input_type_part instanceof THtmlEscapedString)
- ) {
- if ($container_type_part instanceof TLiteralString) {
- if (is_numeric($container_type_part->value) && $atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- return true;
- }
-
- if ($input_type_part instanceof TString
- && ($container_type_part instanceof TNumericString
- || $container_type_part instanceof THtmlEscapedString)
- ) {
- if ($input_type_part instanceof TLiteralString) {
- return is_numeric($input_type_part->value);
- }
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TCallableString
- && $input_type_part instanceof TLiteralString
- ) {
- $input_callable = CallableTypeComparator::getCallableFromAtomic($codebase, $input_type_part);
- $container_callable = CallableTypeComparator::getCallableFromAtomic($codebase, $container_type_part);
-
- if ($input_callable && $container_callable) {
- if (CallableTypeComparator::isContainedBy(
- $codebase,
- $input_callable,
- $container_callable,
- $atomic_comparison_result ?? new TypeComparisonResult()
- ) === false
- ) {
- return false;
- }
- }
-
- if (!$input_callable) {
- //we could not find a callable for the input type, so the input is not contained in the container
- return false;
- }
-
- return true;
- }
-
- if ($input_type_part instanceof TLowercaseString
- && get_class($container_type_part) === TNonEmptyString::class) {
- return false;
- }
-
- if ($input_type_part->getKey() === $container_type_part->getKey()) {
- return true;
- }
-
- if (($container_type_part instanceof TClassString
- || $container_type_part instanceof TLiteralClassString
- || $container_type_part instanceof TCallableString)
- && $input_type_part instanceof TString
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->type_coerced = true;
- }
-
- return false;
- }
-
- if ($container_type_part instanceof TNumeric
- && $input_type_part->isNumericType()
- ) {
- return true;
- }
-
- if ($input_type_part instanceof TNumeric) {
- if ($container_type_part->isNumericType()) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->scalar_type_match_found = true;
- }
- }
- }
-
- if ($input_type_part instanceof Scalar) {
- if (!$container_type_part instanceof TLiteralInt
- && !$container_type_part instanceof TLiteralString
- && !$container_type_part instanceof TLiteralFloat
- ) {
- if ($atomic_comparison_result) {
- $atomic_comparison_result->scalar_type_match_found = true;
- }
- }
- }
-
- return false;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php
deleted file mode 100644
index 5fbe29a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/TypeComparisonResult.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Type\Atomic;
-use Psalm\Type\Union;
-
-class TypeComparisonResult
-{
- /** @var ?bool */
- public $scalar_type_match_found;
-
- /** @var ?bool */
- public $type_coerced;
-
- /** @var ?bool */
- public $type_coerced_from_mixed;
-
- /** @var ?bool */
- public $type_coerced_from_as_mixed;
-
- /** @var ?bool */
- public $to_string_cast;
-
- /**
- * This is primarily used for array access.
- * For example in this function we know that there are only two possible keys, 0 and 1
- * But we allow the array to be addressed by an arbitrary integer $i.
- *
- * function takesAnInt(int $i): string {
- * return ["foo", "bar"][$i];
- * }
- *
- * @var ?bool
- */
- public $type_coerced_from_scalar;
-
- /** @var ?Union */
- public $replacement_union_type;
-
- /** @var ?Atomic */
- public $replacement_atomic_type;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php
deleted file mode 100644
index c997199..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php
+++ /dev/null
@@ -1,499 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\Comparator;
-
-use Psalm\Codebase;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTypeAlias;
-use Psalm\Type\Union;
-
-use function array_merge;
-use function array_pop;
-use function array_push;
-use function array_reverse;
-
-/**
- * @internal
- */
-class UnionTypeComparator
-{
- /**
- * Does the input param type match the given param type
- */
- public static function isContainedBy(
- Codebase $codebase,
- Union $input_type,
- Union $container_type,
- bool $ignore_null = false,
- bool $ignore_false = false,
- ?TypeComparisonResult $union_comparison_result = null,
- bool $allow_interface_equality = false,
- bool $allow_float_int_equality = true
- ): bool {
- if ($container_type->isMixed()) {
- return true;
- }
-
- if ($input_type->isNever()) {
- return true;
- }
-
- if ($union_comparison_result) {
- $union_comparison_result->scalar_type_match_found = true;
- }
-
- if ($input_type->possibly_undefined
- && !$input_type->possibly_undefined_from_try
- && !$container_type->possibly_undefined
- ) {
- return false;
- }
-
- if ($container_type->hasMixed() && !$container_type->isEmptyMixed()) {
- return true;
- }
-
- $container_has_template = $container_type->hasTemplateOrStatic();
-
- $input_atomic_types = array_reverse(self::getTypeParts($codebase, $input_type));
-
- while ($input_type_part = array_pop($input_atomic_types)) {
- if ($input_type_part instanceof TNull && $ignore_null) {
- continue;
- }
-
- if ($input_type_part instanceof TFalse && $ignore_false) {
- continue;
- }
-
- if ($input_type_part instanceof TTemplateParam
- && !$container_has_template
- && !$input_type_part->extra_types
- ) {
- $input_atomic_types = array_merge($input_type_part->as->getAtomicTypes(), $input_atomic_types);
- continue;
- }
-
-
- $type_match_found = false;
- $scalar_type_match_found = false;
- $all_to_string_cast = true;
-
- $all_type_coerced = null;
- $all_type_coerced_from_mixed = null;
- $all_type_coerced_from_as_mixed = null;
-
- $some_type_coerced = false;
- $some_type_coerced_from_mixed = false;
-
- if ($input_type_part instanceof TArrayKey
- && ($container_type->hasInt() && $container_type->hasString())
- ) {
- continue;
- }
-
- if ($input_type_part instanceof TArrayKey && $container_type->hasTemplate()) {
- foreach ($container_type->getTemplateTypes() as $template_type) {
- if ($template_type->as->isArrayKey()) {
- continue 2;
- }
- }
- }
-
- if ($input_type_part instanceof TIntRange && $container_type->hasInt()) {
- if (IntegerRangeComparator::isContainedByUnion(
- $input_type_part,
- $container_type
- )) {
- continue;
- }
- }
-
- foreach (self::getTypeParts($codebase, $container_type) as $container_type_part) {
- if ($ignore_null
- && $container_type_part instanceof TNull
- && !$input_type_part instanceof TNull
- ) {
- continue;
- }
-
- if ($ignore_false
- && $container_type_part instanceof TFalse
- && !$input_type_part instanceof TFalse
- ) {
- continue;
- }
-
- if ($union_comparison_result) {
- $atomic_comparison_result = new TypeComparisonResult();
- } else {
- $atomic_comparison_result = null;
- }
-
- $is_atomic_contained_by = AtomicTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- $allow_interface_equality,
- $allow_float_int_equality,
- $atomic_comparison_result
- );
-
- if ($input_type_part instanceof TMixed
- && $input_type->from_template_default
- && $input_type->from_docblock
- && $atomic_comparison_result
- && $atomic_comparison_result->type_coerced_from_mixed
- ) {
- $atomic_comparison_result->type_coerced_from_as_mixed = true;
- }
-
- if ($atomic_comparison_result) {
- if ($atomic_comparison_result->scalar_type_match_found !== null) {
- $scalar_type_match_found = $atomic_comparison_result->scalar_type_match_found;
- }
-
- if ($union_comparison_result
- && $atomic_comparison_result->type_coerced_from_scalar !== null
- ) {
- $union_comparison_result->type_coerced_from_scalar
- = $atomic_comparison_result->type_coerced_from_scalar;
- }
-
- if ($is_atomic_contained_by
- && $union_comparison_result
- && $atomic_comparison_result->replacement_atomic_type
- ) {
- if (!$union_comparison_result->replacement_union_type) {
- $union_comparison_result->replacement_union_type = clone $input_type;
- }
-
- $union_comparison_result->replacement_union_type->removeType($input_type->getKey());
-
- $union_comparison_result->replacement_union_type->addType(
- $atomic_comparison_result->replacement_atomic_type
- );
- }
- }
-
- if ($input_type_part instanceof TNumeric
- && $container_type->hasString()
- && $container_type->hasInt()
- && $container_type->hasFloat()
- ) {
- $scalar_type_match_found = false;
- $is_atomic_contained_by = true;
- }
-
- if ($atomic_comparison_result) {
- if ($atomic_comparison_result->type_coerced) {
- $some_type_coerced = true;
- }
-
- if ($atomic_comparison_result->type_coerced_from_mixed) {
- $some_type_coerced_from_mixed = true;
- }
-
- if ($atomic_comparison_result->type_coerced !== true || $all_type_coerced === false) {
- $all_type_coerced = false;
- } else {
- $all_type_coerced = true;
- }
-
- if ($atomic_comparison_result->type_coerced_from_mixed !== true
- || $all_type_coerced_from_mixed === false
- ) {
- $all_type_coerced_from_mixed = false;
- } else {
- $all_type_coerced_from_mixed = true;
- }
-
- if ($atomic_comparison_result->type_coerced_from_as_mixed !== true
- || $all_type_coerced_from_as_mixed === false
- ) {
- $all_type_coerced_from_as_mixed = false;
- } else {
- $all_type_coerced_from_as_mixed = true;
- }
- }
-
- if ($is_atomic_contained_by) {
- $type_match_found = true;
-
- if ($atomic_comparison_result) {
- if ($atomic_comparison_result->to_string_cast !== true) {
- $all_to_string_cast = false;
- }
- }
-
- $all_type_coerced_from_mixed = false;
- $all_type_coerced_from_as_mixed = false;
- $all_type_coerced = false;
- }
- }
-
- if ($union_comparison_result) {
- // only set this flag if we're definite that the only
- // reason the type match has been found is because there
- // was a __toString cast
- if ($all_to_string_cast && $type_match_found) {
- $union_comparison_result->to_string_cast = true;
- }
-
- if ($all_type_coerced) {
- $union_comparison_result->type_coerced = true;
- }
-
- if ($all_type_coerced_from_mixed) {
- $union_comparison_result->type_coerced_from_mixed = true;
-
- if (($input_type->from_template_default && $input_type->from_docblock)
- || $all_type_coerced_from_as_mixed
- ) {
- $union_comparison_result->type_coerced_from_as_mixed = true;
- }
- }
- }
-
- if (!$type_match_found) {
- if ($union_comparison_result) {
- if ($some_type_coerced) {
- $union_comparison_result->type_coerced = true;
- }
-
- if ($some_type_coerced_from_mixed) {
- $union_comparison_result->type_coerced_from_mixed = true;
-
- if (($input_type->from_template_default && $input_type->from_docblock)
- || $all_type_coerced_from_as_mixed
- ) {
- $union_comparison_result->type_coerced_from_as_mixed = true;
- }
- }
-
- if (!$scalar_type_match_found) {
- $union_comparison_result->scalar_type_match_found = false;
- }
- }
-
- return false;
- }
- }
-
- return true;
- }
-
- /**
- * Used for comparing signature typehints, uses PHP's light contravariance rules
- *
- *
- */
- public static function isContainedByInPhp(
- ?Union $input_type,
- Union $container_type
- ): bool {
- if ($container_type->isMixed()) {
- return true;
- }
-
- if (!$input_type) {
- return false;
- }
-
- if ($input_type->isNever()) {
- return true;
- }
-
- if ($input_type->getId() === $container_type->getId()) {
- return true;
- }
-
- if ($input_type->isNullable() && !$container_type->isNullable()) {
- return false;
- }
-
- $input_type_not_null = clone $input_type;
- $input_type_not_null->removeType('null');
-
- $container_type_not_null = clone $container_type;
- $container_type_not_null->removeType('null');
-
- if ($input_type_not_null->getId() === $container_type_not_null->getId()) {
- return true;
- }
-
- if ($input_type_not_null->hasArray() && $container_type_not_null->hasType('iterable')) {
- return true;
- }
-
- return false;
- }
-
- /**
- * Does the input param type match the given param type
- */
- public static function canBeContainedBy(
- Codebase $codebase,
- Union $input_type,
- Union $container_type,
- bool $ignore_null = false,
- bool $ignore_false = false,
- array &$matching_input_keys = []
- ): bool {
- if ($container_type->hasMixed()) {
- return true;
- }
-
- if ($input_type->isNever()) {
- return true;
- }
-
- if ($input_type->possibly_undefined && !$container_type->possibly_undefined) {
- return false;
- }
-
- foreach (self::getTypeParts($codebase, $container_type) as $container_type_part) {
- if ($container_type_part instanceof TNull && $ignore_null) {
- continue;
- }
-
- if ($container_type_part instanceof TFalse && $ignore_false) {
- continue;
- }
-
- foreach (self::getTypeParts($codebase, $input_type) as $input_type_part) {
- $atomic_comparison_result = new TypeComparisonResult();
- $is_atomic_contained_by = AtomicTypeComparator::isContainedBy(
- $codebase,
- $input_type_part,
- $container_type_part,
- false,
- false,
- $atomic_comparison_result
- );
-
- if (($is_atomic_contained_by && !$atomic_comparison_result->to_string_cast)
- || $atomic_comparison_result->type_coerced_from_mixed
- ) {
- $matching_input_keys[$input_type_part->getKey()] = true;
- }
- }
- }
-
- return (bool)$matching_input_keys;
- }
-
- /**
- * Can any part of the $type1 be equal to any part of $type2
- *
- */
- public static function canExpressionTypesBeIdentical(
- Codebase $codebase,
- Union $type1,
- Union $type2,
- bool $allow_interface_equality = true
- ): bool {
- if ($type1->hasMixed() || $type2->hasMixed()) {
- return true;
- }
-
- if ($type1->isNullable() && $type2->isNullable()) {
- return true;
- }
-
- foreach (self::getTypeParts($codebase, $type1) as $type1_part) {
- foreach (self::getTypeParts($codebase, $type2) as $type2_part) {
- //special cases for TIntRange because it can contain a part of the other type.
- //For exemple int<0,1> and positive-int can be identical but none contain the other
- if (($type1_part instanceof TIntRange && $type2_part instanceof TPositiveInt)) {
- $intersection_range = TIntRange::intersectIntRanges(
- TIntRange::convertToIntRange($type2_part),
- $type1_part
- );
- return $intersection_range !== null;
- }
-
- if ($type2_part instanceof TIntRange && $type1_part instanceof TPositiveInt) {
- $intersection_range = TIntRange::intersectIntRanges(
- TIntRange::convertToIntRange($type1_part),
- $type2_part
- );
- return $intersection_range !== null;
- }
-
- if ($type1_part instanceof TIntRange && $type2_part instanceof TIntRange) {
- $intersection_range = TIntRange::intersectIntRanges(
- $type1_part,
- $type2_part
- );
- return $intersection_range !== null;
- }
-
- $either_contains = AtomicTypeComparator::canBeIdentical(
- $codebase,
- $type1_part,
- $type2_part,
- $allow_interface_equality
- );
-
- if ($either_contains) {
- return true;
- }
- }
- }
-
- return false;
- }
-
- /**
- * @return list<Atomic>
- */
- private static function getTypeParts(
- Codebase $codebase,
- Union $union_type
- ): array {
- $atomic_types = [];
- foreach ($union_type->getAtomicTypes() as $atomic_type) {
- if (!$atomic_type instanceof TTypeAlias && !$atomic_type instanceof TClassConstant) {
- $atomic_types[] = $atomic_type;
- continue;
- }
-
- if ($atomic_type instanceof TTypeAlias) {
- $fq_classlike_name = $atomic_type->declaring_fq_classlike_name;
- } else {
- $fq_classlike_name = $atomic_type->fq_classlike_name;
- }
-
- $expanded = TypeExpander::expandAtomic(
- $codebase,
- $atomic_type,
- $fq_classlike_name,
- $fq_classlike_name,
- null,
- true,
- true
- );
- if ($expanded instanceof Atomic) {
- if (!$expanded instanceof TTypeAlias && !$expanded instanceof TClassConstant) {
- $atomic_types[] = $expanded;
- }
- continue;
- }
-
- array_push($atomic_types, ...$expanded);
- }
-
- return $atomic_types;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php
deleted file mode 100644
index b3387f3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php
+++ /dev/null
@@ -1,425 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\CodeLocation;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Analyzer\TraitAnalyzer;
-use Psalm\Internal\Type\Comparator\AtomicTypeComparator;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Type;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TEmptyMixed;
-use Psalm\Type\Atomic\TEnumCase;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function count;
-use function explode;
-use function get_class;
-use function strpos;
-use function strtolower;
-use function substr;
-
-class NegatedAssertionReconciler extends Reconciler
-{
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- public static function reconcile(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- bool $is_strict_equality,
- bool $is_loose_equality,
- Union $existing_var_type,
- array $template_type_map,
- string $old_var_type_string,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $inside_loop
- ): Union {
- $is_equality = $is_strict_equality || $is_loose_equality;
-
- // this is a specific value comparison type that cannot be negated
- if ($is_equality && $bracket_pos = strpos($assertion, '(')) {
- if ($existing_var_type->hasMixed()) {
- return $existing_var_type;
- }
-
- return self::handleLiteralNegatedEquality(
- $statements_analyzer,
- $assertion,
- $bracket_pos,
- $existing_var_type,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $is_strict_equality
- );
- }
-
- if ($is_equality && $assertion === 'positive-numeric') {
- return $existing_var_type;
- }
-
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($assertion === 'false' && isset($existing_var_atomic_types['bool'])) {
- $existing_var_type->removeType('bool');
- $existing_var_type->addType(new TTrue);
- } elseif ($assertion === 'true' && isset($existing_var_atomic_types['bool'])) {
- $existing_var_type->removeType('bool');
- $existing_var_type->addType(new TFalse);
- } else {
- $simple_negated_type = SimpleNegatedAssertionReconciler::reconcile(
- $statements_analyzer->getCodebase(),
- $assertion,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $inside_loop
- );
-
- if ($simple_negated_type) {
- return $simple_negated_type;
- }
- }
-
- if ($assertion === 'iterable' || $assertion === 'countable') {
- $existing_var_type->removeType('array');
- }
-
- if (!$is_equality
- && isset($existing_var_atomic_types['int'])
- && $existing_var_type->from_calculation
- && ($assertion === 'int' || $assertion === 'float')
- ) {
- $existing_var_type->removeType($assertion);
-
- if ($assertion === 'int') {
- $existing_var_type->addType(new TFloat);
- } else {
- $existing_var_type->addType(new TInt);
- }
-
- $existing_var_type->from_calculation = false;
-
- return $existing_var_type;
- }
-
- if (!$is_equality
- && ($assertion === 'DateTime' || $assertion === 'DateTimeImmutable')
- && isset($existing_var_atomic_types['DateTimeInterface'])
- ) {
- $existing_var_type->removeType('DateTimeInterface');
-
- if ($assertion === 'DateTime') {
- $existing_var_type->addType(new TNamedObject('DateTimeImmutable'));
- } else {
- $existing_var_type->addType(new TNamedObject('DateTime'));
- }
-
- return $existing_var_type;
- }
-
- if (strtolower($assertion) === 'traversable'
- && isset($existing_var_atomic_types['iterable'])
- ) {
- /** @var TIterable */
- $iterable = $existing_var_atomic_types['iterable'];
- $existing_var_type->removeType('iterable');
- $existing_var_type->addType(new TArray(
- [
- $iterable->type_params[0]->hasMixed()
- ? Type::getArrayKey()
- : clone $iterable->type_params[0],
- clone $iterable->type_params[1],
- ]
- ));
- } elseif (strtolower($assertion) === 'int'
- && isset($existing_var_type->getAtomicTypes()['array-key'])
- ) {
- $existing_var_type->removeType('array-key');
- $existing_var_type->addType(new TString);
- } elseif (strpos($assertion, 'getclass-') === 0) {
- $assertion = substr($assertion, 9);
- } elseif ($existing_var_type->isSingle()
- && $existing_var_type->hasNamedObjectType()
- && isset($existing_var_type->getAtomicTypes()[$assertion])
- ) {
- // checking if two types share a common parent is not enough to guarantee childs are instanceof each other
- // fall through
- } elseif (!$is_equality) {
- $codebase = $statements_analyzer->getCodebase();
-
- // if there wasn't a direct hit, go deeper, eliminating subtypes
- if (!$existing_var_type->removeType($assertion)) {
- foreach ($existing_var_type->getAtomicTypes() as $part_name => $existing_var_type_part) {
- if (!$existing_var_type_part->isObjectType() || strpos($assertion, '-')) {
- continue;
- }
-
- $assertion_type = Type::parseString($assertion, null, $template_type_map);
-
- if (!$assertion_type->isSingle()) {
- continue;
- }
-
- $new_type_part = $assertion_type->getSingleAtomic();
-
- if (!$new_type_part instanceof TNamedObject) {
- continue;
- }
-
- if (AtomicTypeComparator::isContainedBy(
- $codebase,
- $existing_var_type_part,
- $new_type_part,
- false,
- false
- )) {
- $existing_var_type->removeType($part_name);
- } elseif (AtomicTypeComparator::isContainedBy(
- $codebase,
- $new_type_part,
- $existing_var_type_part,
- false,
- false
- )) {
- $existing_var_type->different = true;
- }
- }
- }
- }
-
- if ($is_strict_equality
- && $assertion !== 'isset'
- && ($key !== '$this'
- || !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer))
- ) {
- $assertion = Type::parseString($assertion, null, $template_type_map);
-
- if ($key
- && $code_location
- && !UnionTypeComparator::canExpressionTypesBeIdentical(
- $statements_analyzer->getCodebase(),
- $existing_var_type,
- $assertion
- )
- ) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!=' . $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($existing_var_type->isUnionEmpty()) {
- if ($key !== '$this'
- || !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer)
- ) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!' . $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return new Union([new TEmptyMixed]);
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- *
- */
- private static function handleLiteralNegatedEquality(
- StatementsAnalyzer $statements_analyzer,
- string $assertion,
- int $bracket_pos,
- Union $existing_var_type,
- string $old_var_type_string,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- bool $is_strict_equality
- ): Union {
- $scalar_type = substr($assertion, 0, $bracket_pos);
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- $did_remove_type = false;
- $did_match_literal_type = false;
-
- $scalar_var_type = null;
-
- if ($scalar_type === 'int') {
- if ($existing_var_type->hasInt()) {
- if ($existing_int_types = $existing_var_type->getLiteralInts()) {
- if (!$existing_var_type->hasPositiveInt()) {
- $did_match_literal_type = true;
- }
-
- if (isset($existing_int_types[$assertion])) {
- $existing_var_type->removeType($assertion);
-
- $did_remove_type = true;
- }
- }
- } else {
- $scalar_value = substr($assertion, $bracket_pos + 1, -1);
- $scalar_var_type = Type::getInt(false, (int) $scalar_value);
- }
- } elseif ($scalar_type === 'string'
- || $scalar_type === 'class-string'
- || $scalar_type === 'interface-string'
- || $scalar_type === 'trait-string'
- || $scalar_type === 'callable-string'
- ) {
- if ($existing_var_type->hasString()) {
- if ($existing_string_types = $existing_var_type->getLiteralStrings()) {
- $did_match_literal_type = true;
-
- if (isset($existing_string_types[$assertion])) {
- $existing_var_type->removeType($assertion);
-
- $did_remove_type = true;
- }
- } elseif ($assertion === 'string()') {
- $existing_var_type->addType(new TNonEmptyString());
- }
- } elseif ($scalar_type === 'string') {
- $scalar_value = substr($assertion, $bracket_pos + 1, -1);
- $scalar_var_type = Type::getString($scalar_value);
- }
- } elseif ($scalar_type === 'float') {
- if ($existing_var_type->hasFloat()) {
- if ($existing_float_types = $existing_var_type->getLiteralFloats()) {
- $did_match_literal_type = true;
-
- if (isset($existing_float_types[$assertion])) {
- $existing_var_type->removeType($assertion);
-
- $did_remove_type = true;
- }
- }
- } else {
- $scalar_value = substr($assertion, $bracket_pos + 1, -1);
- $scalar_var_type = Type::getFloat((float) $scalar_value);
- }
- } elseif ($scalar_type === 'enum') {
- [$fq_enum_name, $case_name] = explode('::', substr($assertion, $bracket_pos + 1, -1));
-
- foreach ($existing_var_type->getAtomicTypes() as $atomic_key => $atomic_type) {
- if (get_class($atomic_type) === TNamedObject::class
- && $atomic_type->value === $fq_enum_name
- ) {
- $codebase = $statements_analyzer->getCodebase();
-
- $enum_storage = $codebase->classlike_storage_provider->get($fq_enum_name);
-
- if (!$enum_storage->is_enum || !$enum_storage->enum_cases) {
- $scalar_var_type = new Union([new TEnumCase($fq_enum_name, $case_name)]);
- } else {
- $existing_var_type->removeType($atomic_type->getKey());
- $did_remove_type = true;
-
- foreach ($enum_storage->enum_cases as $alt_case_name => $_) {
- if ($alt_case_name === $case_name) {
- continue;
- }
-
- $existing_var_type->addType(new TEnumCase($fq_enum_name, $alt_case_name));
- }
- }
- } elseif ($atomic_type instanceof TEnumCase
- && $atomic_type->value === $fq_enum_name
- && $atomic_type->case_name !== $case_name
- ) {
- $did_match_literal_type = true;
- } elseif ($atomic_key === $assertion) {
- $existing_var_type->removeType($assertion);
- $did_remove_type = true;
- }
- }
- }
-
- if ($key && $code_location) {
- if ($did_match_literal_type
- && (!$did_remove_type || count($existing_var_atomic_types) === 1)
- ) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!' . $assertion,
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- } elseif ($scalar_var_type
- && $is_strict_equality
- && ($key !== '$this'
- || !($statements_analyzer->getSource()->getSource() instanceof TraitAnalyzer))
- ) {
- if (!UnionTypeComparator::canExpressionTypesBeIdentical(
- $statements_analyzer->getCodebase(),
- $existing_var_type,
- $scalar_var_type
- )) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!=' . $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
- }
-
- return $existing_var_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree.php
deleted file mode 100644
index fa3f0b3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-/**
- * @internal
- */
-class ParseTree
-{
- /**
- * @var list<ParseTree>
- */
- public $children = [];
-
- /**
- * @var null|ParseTree
- */
- public $parent;
-
- /**
- * @var bool
- */
- public $possibly_undefined = false;
-
- public function __construct(?ParseTree $parent = null)
- {
- $this->parent = $parent;
- }
-
- public function __destruct()
- {
- $this->parent = null;
- }
-
- public function cleanParents(): void
- {
- foreach ($this->children as $child) {
- $child->cleanParents();
- }
-
- $this->parent = null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableParamTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableParamTree.php
deleted file mode 100644
index ae0b8ca..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableParamTree.php
+++ /dev/null
@@ -1,21 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class CallableParamTree extends ParseTree
-{
- /**
- * @var bool
- */
- public $variadic = false;
-
- /**
- * @var bool
- */
- public $has_default = false;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableTree.php
deleted file mode 100644
index 404211d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableTree.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class CallableTree extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- /**
- * @var bool
- */
- public $terminated = false;
-
- public function __construct(string $value, ?ParseTree $parent = null)
- {
- $this->value = $value;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableWithReturnTypeTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableWithReturnTypeTree.php
deleted file mode 100644
index 3069d73..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/CallableWithReturnTypeTree.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class CallableWithReturnTypeTree extends ParseTree
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/ConditionalTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/ConditionalTree.php
deleted file mode 100644
index b743f0a..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/ConditionalTree.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class ConditionalTree extends ParseTree
-{
- /**
- * @var TemplateIsTree
- */
- public $condition;
-
- public function __construct(TemplateIsTree $condition, ?ParseTree $parent = null)
- {
- $this->condition = $condition;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/EncapsulationTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/EncapsulationTree.php
deleted file mode 100644
index 5fc3464..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/EncapsulationTree.php
+++ /dev/null
@@ -1,16 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class EncapsulationTree extends ParseTree
-{
- /**
- * @var bool
- */
- public $terminated = false;
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/GenericTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/GenericTree.php
deleted file mode 100644
index fc17d02..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/GenericTree.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class GenericTree extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- /**
- * @var bool
- */
- public $terminated = false;
-
- public function __construct(string $value, ?ParseTree $parent = null)
- {
- $this->value = $value;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IndexedAccessTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IndexedAccessTree.php
deleted file mode 100644
index a38b8e7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IndexedAccessTree.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class IndexedAccessTree extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- public function __construct(string $value, ?ParseTree $parent = null)
- {
- $this->value = $value;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IntersectionTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IntersectionTree.php
deleted file mode 100644
index 7025160..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/IntersectionTree.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class IntersectionTree extends ParseTree
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayPropertyTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayPropertyTree.php
deleted file mode 100644
index e06d863..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayPropertyTree.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class KeyedArrayPropertyTree extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- public function __construct(string $value, ?ParseTree $parent = null)
- {
- $this->value = $value;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayTree.php
deleted file mode 100644
index 4da6cf7..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/KeyedArrayTree.php
+++ /dev/null
@@ -1,27 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class KeyedArrayTree extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- /**
- * @var bool
- */
- public $terminated = false;
-
- public function __construct(string $value, ?ParseTree $parent = null)
- {
- $this->value = $value;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodParamTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodParamTree.php
deleted file mode 100644
index a3a531e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodParamTree.php
+++ /dev/null
@@ -1,43 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class MethodParamTree extends ParseTree
-{
- /**
- * @var bool
- */
- public $variadic;
-
- /**
- * @var string
- */
- public $default = '';
-
- /**
- * @var bool
- */
- public $byref;
-
- /**
- * @var string
- */
- public $name;
-
- public function __construct(
- string $name,
- bool $byref,
- bool $variadic,
- ?ParseTree $parent = null
- ) {
- $this->name = $name;
- $this->byref = $byref;
- $this->variadic = $variadic;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodTree.php
deleted file mode 100644
index f21ef8e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodTree.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class MethodTree extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- public function __construct(string $value, ?ParseTree $parent = null)
- {
- $this->value = $value;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodWithReturnTypeTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodWithReturnTypeTree.php
deleted file mode 100644
index 47701b3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/MethodWithReturnTypeTree.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class MethodWithReturnTypeTree extends ParseTree
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/NullableTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/NullableTree.php
deleted file mode 100644
index 2471184..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/NullableTree.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class NullableTree extends ParseTree
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Root.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Root.php
deleted file mode 100644
index b08f963..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Root.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class Root extends ParseTree
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateAsTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateAsTree.php
deleted file mode 100644
index c8dbd79..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateAsTree.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class TemplateAsTree extends ParseTree
-{
- /**
- * @var string
- */
- public $param_name;
-
- /**
- * @var string
- */
- public $as;
-
- public function __construct(string $param_name, string $as, ?ParseTree $parent = null)
- {
- $this->param_name = $param_name;
- $this->as = $as;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateIsTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateIsTree.php
deleted file mode 100644
index 3a96b6c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/TemplateIsTree.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class TemplateIsTree extends ParseTree
-{
- /**
- * @var string
- */
- public $param_name;
-
- public function __construct(string $param_name, ?ParseTree $parent = null)
- {
- $this->param_name = $param_name;
- $this->parent = $parent;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/UnionTree.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/UnionTree.php
deleted file mode 100644
index 1f8de48..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/UnionTree.php
+++ /dev/null
@@ -1,12 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class UnionTree extends ParseTree
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Value.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Value.php
deleted file mode 100644
index 952aad3..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTree/Value.php
+++ /dev/null
@@ -1,48 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\ParseTree;
-
-use Psalm\Internal\Type\ParseTree;
-
-/**
- * @internal
- */
-class Value extends ParseTree
-{
- /**
- * @var string
- */
- public $value;
-
- /**
- * @var int
- */
- public $offset_start;
-
- /**
- * @var int
- */
- public $offset_end;
-
- /**
- * @var ?string
- */
- public $text;
-
- /**
- * @param ParseTree|null $parent
- */
- public function __construct(
- string $value,
- int $offset_start,
- int $offset_end,
- ?string $text,
- ParseTree $parent = null
- ) {
- $this->offset_start = $offset_start;
- $this->offset_end = $offset_end;
- $this->value = $value;
- $this->parent = $parent;
- $this->text = $text === $value ? null : $text;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php
deleted file mode 100644
index edad53c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php
+++ /dev/null
@@ -1,842 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Type\ParseTree\CallableParamTree;
-use Psalm\Internal\Type\ParseTree\CallableTree;
-use Psalm\Internal\Type\ParseTree\CallableWithReturnTypeTree;
-use Psalm\Internal\Type\ParseTree\ConditionalTree;
-use Psalm\Internal\Type\ParseTree\EncapsulationTree;
-use Psalm\Internal\Type\ParseTree\GenericTree;
-use Psalm\Internal\Type\ParseTree\IndexedAccessTree;
-use Psalm\Internal\Type\ParseTree\IntersectionTree;
-use Psalm\Internal\Type\ParseTree\KeyedArrayPropertyTree;
-use Psalm\Internal\Type\ParseTree\KeyedArrayTree;
-use Psalm\Internal\Type\ParseTree\MethodParamTree;
-use Psalm\Internal\Type\ParseTree\MethodTree;
-use Psalm\Internal\Type\ParseTree\MethodWithReturnTypeTree;
-use Psalm\Internal\Type\ParseTree\NullableTree;
-use Psalm\Internal\Type\ParseTree\Root;
-use Psalm\Internal\Type\ParseTree\TemplateAsTree;
-use Psalm\Internal\Type\ParseTree\TemplateIsTree;
-use Psalm\Internal\Type\ParseTree\UnionTree;
-use Psalm\Internal\Type\ParseTree\Value;
-
-use function array_pop;
-use function count;
-use function in_array;
-use function preg_match;
-use function strlen;
-use function strtolower;
-
-/**
- * @internal
- */
-class ParseTreeCreator
-{
- /** @var ParseTree */
- private $parse_tree;
-
- /** @var ParseTree */
- private $current_leaf;
-
- /** @var array<int, array{0: string, 1: int}> */
- private $type_tokens;
-
- /** @var int */
- private $type_token_count;
-
- /** @var int */
- private $t = 0;
-
- /**
- * @param list<array{0: string, 1: int, 2?: string}> $type_tokens
- */
- public function __construct(array $type_tokens)
- {
- $this->type_tokens = $type_tokens;
- $this->type_token_count = count($type_tokens);
- $this->parse_tree = new Root();
- $this->current_leaf = $this->parse_tree;
- }
-
- public function create(): ParseTree
- {
- while ($this->t < $this->type_token_count) {
- $type_token = $this->type_tokens[$this->t];
-
- switch ($type_token[0]) {
- case '<':
- case '{':
- case ']':
- throw new TypeParseTreeException('Unexpected token ' . $type_token[0]);
-
- case '[':
- $this->handleOpenSquareBracket();
- break;
-
- case '(':
- $this->handleOpenRoundBracket();
- break;
-
- case ')':
- $this->handleClosedRoundBracket();
- break;
-
- case '>':
- do {
- if ($this->current_leaf->parent === null) {
- throw new TypeParseTreeException('Cannot parse generic type');
- }
-
- $this->current_leaf = $this->current_leaf->parent;
- } while (!$this->current_leaf instanceof GenericTree);
-
- $this->current_leaf->terminated = true;
-
- break;
-
- case '}':
- do {
- if ($this->current_leaf->parent === null) {
- throw new TypeParseTreeException('Cannot parse array type');
- }
-
- $this->current_leaf = $this->current_leaf->parent;
- } while (!$this->current_leaf instanceof KeyedArrayTree);
-
- $this->current_leaf->terminated = true;
-
- break;
-
- case ',':
- $this->handleComma();
- break;
-
- case '...':
- case '=':
- $this->handleEllipsisOrEquals($type_token);
- break;
-
- case ':':
- $this->handleColon();
- break;
-
- case ' ':
- $this->handleSpace();
- break;
-
- case '?':
- $this->handleQuestionMark();
- break;
-
- case '|':
- $this->handleBar();
- break;
-
- case '&':
- $this->handleAmpersand();
- break;
-
- case 'is':
- case 'as':
- $this->handleIsOrAs($type_token);
- break;
-
- default:
- $this->handleValue($type_token);
- break;
- }
-
- $this->t++;
- }
-
- $this->parse_tree->cleanParents();
-
- if ($this->current_leaf !== $this->parse_tree
- && ($this->parse_tree instanceof GenericTree
- || $this->parse_tree instanceof CallableTree
- || $this->parse_tree instanceof KeyedArrayTree)
- ) {
- throw new TypeParseTreeException(
- 'Unterminated bracket'
- );
- }
-
- return $this->parse_tree;
- }
-
- /**
- * @param array{0: string, 1: int} $current_token
- */
- private function createMethodParam(array $current_token, ParseTree $current_parent): void
- {
- $byref = false;
- $variadic = false;
- $has_default = false;
- $default = '';
-
- if ($current_token[0] === '&') {
- $byref = true;
- ++$this->t;
- $current_token = $this->t < $this->type_token_count ? $this->type_tokens[$this->t] : null;
- } elseif ($current_token[0] === '...') {
- $variadic = true;
-
- ++$this->t;
- $current_token = $this->t < $this->type_token_count ? $this->type_tokens[$this->t] : null;
- }
-
- if (!$current_token || $current_token[0][0] !== '$') {
- throw new TypeParseTreeException('Unexpected token after space');
- }
-
- $new_parent_leaf = new MethodParamTree(
- $current_token[0],
- $byref,
- $variadic,
- $current_parent
- );
-
- for ($j = $this->t + 1; $j < $this->type_token_count; ++$j) {
- $ahead_type_token = $this->type_tokens[$j];
-
- if ($ahead_type_token[0] === ','
- || ($ahead_type_token[0] === ')' && $this->type_tokens[$j - 1][0] !== '(')
- ) {
- $this->t = $j - 1;
- break;
- }
-
- if ($has_default) {
- $default .= $ahead_type_token[0];
- }
-
- if ($ahead_type_token[0] === '=') {
- $has_default = true;
- continue;
- }
-
- if ($j === $this->type_token_count - 1) {
- throw new TypeParseTreeException('Unterminated method');
- }
- }
-
- $new_parent_leaf->default = $default;
-
- if ($this->current_leaf !== $current_parent) {
- $new_parent_leaf->children = [$this->current_leaf];
- array_pop($current_parent->children);
- }
-
- $current_parent->children[] = $new_parent_leaf;
-
- $this->current_leaf = $new_parent_leaf;
- }
-
- private function handleOpenSquareBracket(): void
- {
- if ($this->current_leaf instanceof Root) {
- throw new TypeParseTreeException('Unexpected token [');
- }
-
- $indexed_access = false;
-
- $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null;
-
- if (!$next_token || $next_token[0] !== ']') {
- $next_next_token = $this->t + 2 < $this->type_token_count ? $this->type_tokens[$this->t + 2] : null;
-
- if ($next_next_token !== null && $next_next_token[0] === ']') {
- $indexed_access = true;
- ++$this->t;
- } else {
- throw new TypeParseTreeException('Unexpected token [');
- }
- }
-
- $current_parent = $this->current_leaf->parent;
-
- if ($indexed_access) {
- if ($next_token === null) {
- throw new TypeParseTreeException('Unexpected token [');
- }
-
- $new_parent_leaf = new IndexedAccessTree($next_token[0], $current_parent);
- } else {
- if ($this->current_leaf instanceof KeyedArrayPropertyTree) {
- throw new TypeParseTreeException('Unexpected token [');
- }
-
- $new_parent_leaf = new GenericTree('array', $current_parent);
- }
-
- $this->current_leaf->parent = $new_parent_leaf;
- $new_parent_leaf->children = [$this->current_leaf];
-
- if ($current_parent) {
- array_pop($current_parent->children);
- $current_parent->children[] = $new_parent_leaf;
- } else {
- $this->parse_tree = $new_parent_leaf;
- }
-
- $this->current_leaf = $new_parent_leaf;
- ++$this->t;
- }
-
- private function handleOpenRoundBracket(): void
- {
- if ($this->current_leaf instanceof Value) {
- throw new TypeParseTreeException('Unrecognised token (');
- }
-
- $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null;
-
- $new_leaf = new EncapsulationTree(
- $new_parent
- );
-
- if ($this->current_leaf instanceof Root) {
- $this->current_leaf = $this->parse_tree = $new_leaf;
- return;
- }
-
- if ($new_leaf->parent) {
- $new_leaf->parent->children[] = $new_leaf;
- }
-
- $this->current_leaf = $new_leaf;
- }
-
- private function handleClosedRoundBracket(): void
- {
- $prev_token = $this->t > 0 ? $this->type_tokens[$this->t - 1] : null;
-
- if ($prev_token !== null
- && $prev_token[0] === '('
- && $this->current_leaf instanceof CallableTree
- ) {
- return;
- }
-
- do {
- if ($this->current_leaf->parent === null) {
- break;
- }
-
- $this->current_leaf = $this->current_leaf->parent;
- } while (!$this->current_leaf instanceof EncapsulationTree
- && !$this->current_leaf instanceof CallableTree
- && !$this->current_leaf instanceof MethodTree);
-
- if ($this->current_leaf instanceof EncapsulationTree
- || $this->current_leaf instanceof CallableTree
- ) {
- $this->current_leaf->terminated = true;
- }
- }
-
- private function handleComma(): void
- {
- if ($this->current_leaf instanceof Root) {
- throw new TypeParseTreeException('Unexpected token ,');
- }
-
- if (!$this->current_leaf->parent) {
- throw new TypeParseTreeException('Cannot parse comma without a parent node');
- }
-
- $context_node = $this->current_leaf;
-
- if ($context_node instanceof GenericTree
- || $context_node instanceof KeyedArrayTree
- || $context_node instanceof CallableTree
- || $context_node instanceof MethodTree
- ) {
- $context_node = $context_node->parent;
- }
-
- while ($context_node
- && !$context_node instanceof GenericTree
- && !$context_node instanceof KeyedArrayTree
- && !$context_node instanceof CallableTree
- && !$context_node instanceof MethodTree
- ) {
- $context_node = $context_node->parent;
- }
-
- if (!$context_node) {
- throw new TypeParseTreeException('Cannot parse comma in non-generic/array type');
- }
-
- $this->current_leaf = $context_node;
- }
-
- /** @param array{0: string, 1: int} $type_token */
- private function handleEllipsisOrEquals(array $type_token): void
- {
- $prev_token = $this->t > 0 ? $this->type_tokens[$this->t - 1] : null;
-
- if ($prev_token && ($prev_token[0] === '...' || $prev_token[0] === '=')) {
- throw new TypeParseTreeException('Cannot have duplicate tokens');
- }
-
- $current_parent = $this->current_leaf->parent;
-
- if ($this->current_leaf instanceof MethodTree && $type_token[0] === '...') {
- $this->createMethodParam($type_token, $this->current_leaf);
- return;
- }
-
- while ($current_parent
- && !$current_parent instanceof CallableTree
- && !$current_parent instanceof CallableParamTree
- ) {
- $this->current_leaf = $current_parent;
- $current_parent = $current_parent->parent;
- }
-
- if (!$current_parent) {
- if ($this->current_leaf instanceof CallableTree
- && $type_token[0] === '...'
- ) {
- $current_parent = $this->current_leaf;
- } else {
- throw new TypeParseTreeException('Unexpected token ' . $type_token[0]);
- }
- }
-
- if ($current_parent instanceof CallableParamTree) {
- throw new TypeParseTreeException('Cannot have variadic param with a default');
- }
-
- $new_leaf = new CallableParamTree($current_parent);
- $new_leaf->has_default = $type_token[0] === '=';
- $new_leaf->variadic = $type_token[0] === '...';
-
- if ($current_parent !== $this->current_leaf) {
- $new_leaf->children = [$this->current_leaf];
- array_pop($current_parent->children);
- }
- $current_parent->children[] = $new_leaf;
-
- $this->current_leaf = $new_leaf;
- }
-
- private function handleColon(): void
- {
- if ($this->current_leaf instanceof Root) {
- throw new TypeParseTreeException('Unexpected token :');
- }
-
- $current_parent = $this->current_leaf->parent;
-
- if ($this->current_leaf instanceof CallableTree) {
- $new_parent_leaf = new CallableWithReturnTypeTree($current_parent);
- $this->current_leaf->parent = $new_parent_leaf;
- $new_parent_leaf->children = [$this->current_leaf];
-
- if ($current_parent) {
- array_pop($current_parent->children);
- $current_parent->children[] = $new_parent_leaf;
- } else {
- $this->parse_tree = $new_parent_leaf;
- }
-
- $this->current_leaf = $new_parent_leaf;
- return;
- }
-
- if ($this->current_leaf instanceof MethodTree) {
- $new_parent_leaf = new MethodWithReturnTypeTree($current_parent);
- $this->current_leaf->parent = $new_parent_leaf;
- $new_parent_leaf->children = [$this->current_leaf];
-
- if ($current_parent) {
- array_pop($current_parent->children);
- $current_parent->children[] = $new_parent_leaf;
- } else {
- $this->parse_tree = $new_parent_leaf;
- }
-
- $this->current_leaf = $new_parent_leaf;
- return;
- }
-
- if ($current_parent instanceof KeyedArrayPropertyTree) {
- return;
- }
-
- while (($current_parent instanceof UnionTree
- || $current_parent instanceof CallableWithReturnTypeTree)
- && $this->current_leaf->parent
- ) {
- $this->current_leaf = $this->current_leaf->parent;
- $current_parent = $this->current_leaf->parent;
- }
-
- if ($current_parent instanceof ConditionalTree) {
- if (count($current_parent->children) > 1) {
- throw new TypeParseTreeException('Cannot process colon in conditional twice');
- }
-
- $this->current_leaf = $current_parent;
- return;
- }
-
- if (!$current_parent) {
- throw new TypeParseTreeException('Cannot process colon without parent');
- }
-
- if (!$this->current_leaf instanceof Value) {
- throw new TypeParseTreeException('Unexpected LHS of property');
- }
-
- if (!$current_parent instanceof KeyedArrayTree) {
- throw new TypeParseTreeException('Saw : outside of object-like array');
- }
-
- $prev_token = $this->t > 0 ? $this->type_tokens[$this->t - 1] : null;
-
- $new_parent_leaf = new KeyedArrayPropertyTree($this->current_leaf->value, $current_parent);
- $new_parent_leaf->possibly_undefined = $prev_token !== null && $prev_token[0] === '?';
- array_pop($current_parent->children);
- $current_parent->children[] = $new_parent_leaf;
-
- $this->current_leaf = $new_parent_leaf;
- }
-
- private function handleSpace(): void
- {
- if ($this->current_leaf instanceof Root) {
- throw new TypeParseTreeException('Unexpected space');
- }
-
- if ($this->current_leaf instanceof KeyedArrayTree) {
- return;
- }
-
- $current_parent = $this->current_leaf->parent;
-
- if ($current_parent instanceof CallableTree) {
- return;
- }
-
- while ($current_parent && !$current_parent instanceof MethodTree) {
- $this->current_leaf = $current_parent;
- $current_parent = $current_parent->parent;
- }
-
- $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null;
-
- if (!$current_parent instanceof MethodTree || !$next_token) {
- throw new TypeParseTreeException('Unexpected space');
- }
-
- ++$this->t;
-
- $this->createMethodParam($next_token, $current_parent);
- }
-
- private function handleQuestionMark(): void
- {
- $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null;
-
- if ($next_token === null || $next_token[0] !== ':') {
- while (($this->current_leaf instanceof Value
- || $this->current_leaf instanceof UnionTree
- || ($this->current_leaf instanceof KeyedArrayTree
- && $this->current_leaf->terminated)
- || ($this->current_leaf instanceof GenericTree
- && $this->current_leaf->terminated)
- || ($this->current_leaf instanceof EncapsulationTree
- && $this->current_leaf->terminated)
- || ($this->current_leaf instanceof CallableTree
- && $this->current_leaf->terminated)
- || $this->current_leaf instanceof IntersectionTree)
- && $this->current_leaf->parent
- ) {
- $this->current_leaf = $this->current_leaf->parent;
- }
-
- if ($this->current_leaf instanceof TemplateIsTree && $this->current_leaf->parent) {
- $current_parent = $this->current_leaf->parent;
-
- $new_leaf = new ConditionalTree(
- $this->current_leaf,
- $this->current_leaf->parent
- );
-
- array_pop($current_parent->children);
- $current_parent->children[] = $new_leaf;
- $this->current_leaf = $new_leaf;
- } else {
- $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null;
-
- if (!$next_token) {
- throw new TypeParseTreeException('Unexpected token ?');
- }
-
- $new_leaf = new NullableTree(
- $new_parent
- );
-
- if ($this->current_leaf instanceof Root) {
- $this->current_leaf = $this->parse_tree = $new_leaf;
- return;
- }
-
- if ($new_leaf->parent) {
- $new_leaf->parent->children[] = $new_leaf;
- }
-
- $this->current_leaf = $new_leaf;
- }
- }
- }
-
- private function handleBar(): void
- {
- if ($this->current_leaf instanceof Root) {
- throw new TypeParseTreeException('Unexpected token |');
- }
-
- $current_parent = $this->current_leaf->parent;
-
- if ($current_parent instanceof CallableWithReturnTypeTree) {
- $this->current_leaf = $current_parent;
- $current_parent = $current_parent->parent;
- }
-
- if ($current_parent instanceof NullableTree) {
- $this->current_leaf = $current_parent;
- $current_parent = $current_parent->parent;
- }
-
- if ($this->current_leaf instanceof UnionTree) {
- throw new TypeParseTreeException('Unexpected token |');
- }
-
- if ($current_parent instanceof UnionTree) {
- $this->current_leaf = $current_parent;
- return;
- }
-
- if ($current_parent instanceof IntersectionTree) {
- $this->current_leaf = $current_parent;
- $current_parent = $this->current_leaf->parent;
- }
-
- if ($current_parent instanceof TemplateIsTree) {
- $new_parent_leaf = new UnionTree($this->current_leaf);
- $new_parent_leaf->children = [$this->current_leaf];
- $new_parent_leaf->parent = $current_parent;
- } else {
- $new_parent_leaf = new UnionTree($current_parent);
- $new_parent_leaf->children = [$this->current_leaf];
- }
-
- if ($current_parent) {
- array_pop($current_parent->children);
- $current_parent->children[] = $new_parent_leaf;
- } else {
- $this->parse_tree = $new_parent_leaf;
- }
-
- $this->current_leaf = $new_parent_leaf;
- }
-
- private function handleAmpersand(): void
- {
- if ($this->current_leaf instanceof Root) {
- throw new TypeParseTreeException(
- 'Unexpected &'
- );
- }
-
- $current_parent = $this->current_leaf->parent;
-
- if ($current_parent instanceof MethodTree) {
- $this->createMethodParam($this->type_tokens[$this->t], $current_parent);
- return;
- }
-
- if ($current_parent instanceof IntersectionTree) {
- $this->current_leaf = $current_parent;
- return;
- }
-
- $new_parent_leaf = new IntersectionTree($current_parent);
- $new_parent_leaf->children = [$this->current_leaf];
-
- if ($current_parent) {
- array_pop($current_parent->children);
- $current_parent->children[] = $new_parent_leaf;
- } else {
- $this->parse_tree = $new_parent_leaf;
- }
-
- $this->current_leaf = $new_parent_leaf;
- }
-
- /** @param array{0: string, 1: int} $type_token */
- private function handleIsOrAs(array $type_token): void
- {
- if ($this->t === 0) {
- $this->handleValue($type_token);
- } else {
- $current_parent = $this->current_leaf->parent;
-
- if ($current_parent) {
- array_pop($current_parent->children);
- }
-
- if ($type_token[0] === 'as') {
- $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null;
-
- if (!$this->current_leaf instanceof Value
- || !$current_parent instanceof GenericTree
- || !$next_token
- ) {
- throw new TypeParseTreeException('Unexpected token ' . $type_token[0]);
- }
-
- $this->current_leaf = new TemplateAsTree(
- $this->current_leaf->value,
- $next_token[0],
- $current_parent
- );
-
- $current_parent->children[] = $this->current_leaf;
- ++$this->t;
- } elseif ($this->current_leaf instanceof Value) {
- $this->current_leaf = new TemplateIsTree(
- $this->current_leaf->value,
- $current_parent
- );
-
- if ($current_parent) {
- $current_parent->children[] = $this->current_leaf;
- }
- }
- }
- }
-
- /** @param array{0: string, 1: int, 2?: string} $type_token */
- private function handleValue(array $type_token): void
- {
- $new_parent = !$this->current_leaf instanceof Root ? $this->current_leaf : null;
-
- if ($this->current_leaf instanceof MethodTree && $type_token[0][0] === '$') {
- $this->createMethodParam($type_token, $this->current_leaf);
- return;
- }
-
- $next_token = $this->t + 1 < $this->type_token_count ? $this->type_tokens[$this->t + 1] : null;
-
- switch ($next_token[0] ?? null) {
- case '<':
- $new_leaf = new GenericTree(
- $type_token[0],
- $new_parent
- );
- ++$this->t;
- break;
-
- case '{':
- $new_leaf = new KeyedArrayTree(
- $type_token[0],
- $new_parent
- );
- ++$this->t;
- break;
-
- case '(':
- if (in_array(
- $type_token[0],
- ['callable', 'pure-callable', 'Closure', '\Closure', 'pure-Closure'],
- true
- )) {
- $new_leaf = new CallableTree(
- $type_token[0],
- $new_parent
- );
- } elseif ($type_token[0] !== 'array'
- && $type_token[0][0] !== '\\'
- && $this->current_leaf instanceof Root
- ) {
- $new_leaf = new MethodTree(
- $type_token[0],
- $new_parent
- );
- } else {
- throw new TypeParseTreeException(
- 'Parenthesis must be preceded by “Closure”, “callable”, "pure-callable" or a valid @method name'
- );
- }
-
- ++$this->t;
- break;
-
- case '::':
- $nexter_token = $this->t + 2 < $this->type_token_count ? $this->type_tokens[$this->t + 2] : null;
-
- if ($this->current_leaf instanceof ParseTree\KeyedArrayTree
- && $nexter_token
- && strtolower($nexter_token[0]) !== 'class'
- ) {
- throw new TypeParseTreeException(
- ':: in array key is only allowed for ::class'
- );
- }
-
- if (!$nexter_token
- || (!preg_match('/^([a-zA-Z_][a-zA-Z_0-9]*\*?|\*)$/', $nexter_token[0])
- && strtolower($nexter_token[0]) !== 'class')
- ) {
- throw new TypeParseTreeException(
- 'Invalid class constant ' . ($nexter_token[0] ?? '<empty>')
- );
- }
-
- $new_leaf = new Value(
- $type_token[0] . '::' . $nexter_token[0],
- $type_token[1],
- $type_token[1] + 2 + strlen($nexter_token[0]),
- $type_token[2] ?? null,
- $new_parent
- );
-
- $this->t += 2;
-
- break;
-
- default:
- if ($type_token[0] === '$this') {
- $type_token[0] = 'static';
- }
-
- $new_leaf = new Value(
- $type_token[0],
- $type_token[1],
- $type_token[1] + strlen($type_token[0]),
- $type_token[2] ?? null,
- $new_parent
- );
- break;
- }
-
- if ($this->current_leaf instanceof Root) {
- $this->current_leaf = $this->parse_tree = $new_leaf;
- return;
- }
-
- if ($new_leaf->parent) {
- $new_leaf->parent->children[] = $new_leaf;
- }
-
- $this->current_leaf = $new_leaf;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php
deleted file mode 100644
index 0c46ef2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php
+++ /dev/null
@@ -1,2554 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Internal\Codebase\ClassConstantByWildcardResolver;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Type;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TCallableList;
-use Psalm\Type\Atomic\TCallableObject;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TEmptyMixed;
-use Psalm\Type\Atomic\TEmptyNumeric;
-use Psalm\Type\Atomic\TEmptyScalar;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNonEmptyLowercaseString;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonFalsyString;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TResource;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function assert;
-use function count;
-use function explode;
-use function get_class;
-use function max;
-use function min;
-use function strpos;
-use function substr;
-
-/**
- * This class receives a known type and an assertion (probably coming from AssertionFinder). The goal is to refine
- * the known type using the assertion. For example: old type is `int` assertion is `>5` result is `int<6, max>`.
- * Complex reconciliation takes part in AssertionReconciler if this class couldn't handle the reconciliation
- */
-class SimpleAssertionReconciler extends Reconciler
-{
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- public static function reconcile(
- string $assertion,
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key = null,
- bool $negated = false,
- ?CodeLocation $code_location = null,
- array $suppressed_issues = [],
- int &$failed_reconciliation = Reconciler::RECONCILIATION_OK,
- bool $is_equality = false,
- bool $is_strict_equality = false,
- bool $inside_loop = false
- ): ?Union {
- if ($assertion === 'mixed' && $existing_var_type->hasMixed()) {
- return $existing_var_type;
- }
-
- $old_var_type_string = $existing_var_type->getId();
-
- if ($assertion === 'isset') {
- return self::reconcileIsset(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $inside_loop
- );
- }
-
- if ($assertion === 'array-key-exists') {
- $existing_var_type->possibly_undefined = false;
-
- return $existing_var_type;
- }
-
- if (strpos($assertion, 'in-array-') === 0) {
- return self::reconcileInArray(
- $codebase,
- $existing_var_type,
- substr($assertion, 9),
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation
- );
- }
-
- if (strpos($assertion, 'has-array-key-') === 0) {
- return self::reconcileHasArrayKey(
- $existing_var_type,
- substr($assertion, 14)
- );
- }
-
- if ($assertion[0] === '>') {
- return self::reconcileSuperiorTo(
- $existing_var_type,
- $assertion,
- $inside_loop,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($assertion[0] === '<') {
- return self::reconcileInferiorTo(
- $existing_var_type,
- $assertion,
- $inside_loop,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($assertion === 'falsy' || $assertion === 'empty') {
- return self::reconcileFalsyOrEmpty(
- $assertion,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- false
- );
- }
-
- if ($assertion === 'object') {
- return self::reconcileObject(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'resource') {
- return self::reconcileResource(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'callable') {
- return self::reconcileCallable(
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'iterable') {
- return self::reconcileIterable(
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'array') {
- return self::reconcileArray(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'list') {
- return self::reconcileList(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- false
- );
- }
-
- if ($assertion === 'non-empty-list') {
- return self::reconcileList(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- true
- );
- }
-
- if ($assertion === 'Traversable') {
- return self::reconcileTraversable(
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'countable') {
- return self::reconcileCountable(
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'string-array-access') {
- return self::reconcileStringArrayAccess(
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $inside_loop
- );
- }
-
- if ($assertion === 'int-or-string-array-access') {
- return self::reconcileIntArrayAccess(
- $codebase,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $inside_loop
- );
- }
-
- if ($assertion === 'numeric') {
- return self::reconcileNumeric(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'scalar') {
- return self::reconcileScalar(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'bool') {
- return self::reconcileBool(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'string') {
- return self::reconcileString(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $is_strict_equality
- );
- }
-
- if ($assertion === 'int') {
- return self::reconcileInt(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $is_strict_equality
- );
- }
-
- if ($assertion === 'positive-numeric') {
- return self::reconcilePositiveNumeric(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'float'
- && $existing_var_type->from_calculation
- && $existing_var_type->hasInt()
- ) {
- return Type::getFloat();
- }
-
- if ($assertion === 'float' && $is_equality && !$is_strict_equality && $existing_var_type->isString()) {
- return Type::getNumericString();
- }
-
- if ($assertion === 'non-empty-countable') {
- return self::reconcileNonEmptyCountable(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- null
- );
- }
-
- if (strpos($assertion, 'has-at-least-') === 0) {
- return self::reconcileNonEmptyCountable(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- (int) substr($assertion, 13)
- );
- }
-
- if (strpos($assertion, 'has-exactly-') === 0) {
- /** @psalm-suppress ArgumentTypeCoercion */
- return self::reconcileExactlyCountable(
- $existing_var_type,
- (int) substr($assertion, 12)
- );
- }
-
- if (strpos($assertion, 'hasmethod-') === 0) {
- return self::reconcileHasMethod(
- $codebase,
- substr($assertion, 10),
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation
- );
- }
-
- if (substr($assertion, 0, 15) === 'class-constant(') {
- return self::reconcileClassConstant(
- $codebase,
- substr($assertion, 15, -1),
- $existing_var_type,
- $failed_reconciliation
- );
- }
-
- if ($existing_var_type->isSingle()
- && $existing_var_type->hasTemplate()
- && strpos($assertion, '-') === false
- && strpos($assertion, '(') === false
- ) {
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TTemplateParam) {
- if ($atomic_type->as->hasMixed()
- || $atomic_type->as->hasObject()
- ) {
- $atomic_type->as = Type::parseString($assertion);
-
- return $existing_var_type;
- }
- }
- }
- }
-
- return null;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileIsset(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality,
- bool $inside_loop
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- // if key references an array offset
- $did_remove_type = ($key && strpos($key, '['))
- || !$existing_var_type->initialized
- || $existing_var_type->possibly_undefined
- || $existing_var_type->ignore_isset;
-
- if ($existing_var_type->isNullable()) {
- $existing_var_type->removeType('null');
-
- $did_remove_type = true;
- }
-
- if (!$existing_var_type->hasMixed()
- && !$is_equality
- && (!$did_remove_type || $existing_var_type->isUnionEmpty())
- && $key
- && $code_location
- ) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'isset',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
-
- if ($existing_var_type->isUnionEmpty()) {
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
- return Type::getEmpty();
- }
- }
-
- if ($existing_var_type->hasType('empty')) {
- $existing_var_type->removeType('empty');
- $existing_var_type->addType(new TMixed($inside_loop));
- }
-
- $existing_var_type->from_property = false;
- $existing_var_type->from_static_property = false;
- $existing_var_type->possibly_undefined = false;
- $existing_var_type->possibly_undefined_from_try = false;
- $existing_var_type->ignore_isset = false;
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileNonEmptyCountable(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality,
- ?int $min_count
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- if ($existing_var_type->hasType('array')) {
- $array_atomic_type = $existing_var_type->getAtomicTypes()['array'];
- $did_remove_type = false;
-
- if ($array_atomic_type instanceof TArray) {
- if (!$array_atomic_type instanceof TNonEmptyArray
- || ($array_atomic_type->count < $min_count)
- ) {
- if ($array_atomic_type->getId() === 'array<empty, empty>') {
- $existing_var_type->removeType('array');
- } else {
- $non_empty_array = new TNonEmptyArray(
- $array_atomic_type->type_params
- );
-
- if ($min_count) {
- $non_empty_array->count = $min_count;
- }
-
- $existing_var_type->addType($non_empty_array);
- }
-
- $did_remove_type = true;
- }
- } elseif ($array_atomic_type instanceof TList) {
- if (!$array_atomic_type instanceof TNonEmptyList
- || ($array_atomic_type->count < $min_count)
- ) {
- $non_empty_list = new TNonEmptyList(
- $array_atomic_type->type_param
- );
-
- if ($min_count) {
- $non_empty_list->count = $min_count;
- }
-
- $did_remove_type = true;
- $existing_var_type->addType($non_empty_list);
- }
- } elseif ($array_atomic_type instanceof TKeyedArray) {
- foreach ($array_atomic_type->properties as $property_type) {
- if ($property_type->possibly_undefined) {
- $did_remove_type = true;
- break;
- }
- }
- }
-
- if (!$is_equality
- && !$existing_var_type->hasMixed()
- && (!$did_remove_type || $existing_var_type->isUnionEmpty())
- ) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'non-empty-countable',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param positive-int $count
- */
- private static function reconcileExactlyCountable(
- Union $existing_var_type,
- int $count
- ): Union {
- if ($existing_var_type->hasType('array')) {
- $array_atomic_type = $existing_var_type->getAtomicTypes()['array'];
-
- if ($array_atomic_type instanceof TArray) {
- $non_empty_array = new TNonEmptyArray(
- $array_atomic_type->type_params
- );
-
- $non_empty_array->count = $count;
-
- $existing_var_type->addType(
- $non_empty_array
- );
- } elseif ($array_atomic_type instanceof TList) {
- $non_empty_list = new TNonEmptyList(
- $array_atomic_type->type_param
- );
-
- $non_empty_list->count = $count;
-
- $existing_var_type->addType(
- $non_empty_list
- );
- }
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcilePositiveNumeric(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $did_remove_type = false;
-
- $positive_types = [];
-
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TLiteralInt) {
- if ($atomic_type->value < 1) {
- $did_remove_type = true;
- } else {
- $positive_types[] = $atomic_type;
- }
- } elseif ($atomic_type instanceof TPositiveInt) {
- $positive_types[] = $atomic_type;
- } elseif ($atomic_type instanceof TIntRange) {
- if (!$atomic_type->isPositive()) {
- $did_remove_type = true;
- }
- $positive_types[] = new TIntRange(
- $atomic_type->min_bound === null ? 1 : max(1, $atomic_type->min_bound),
- $atomic_type->max_bound === null ? null : max(1, $atomic_type->max_bound)
- );
- } elseif (get_class($atomic_type) === TInt::class) {
- $positive_types[] = new TPositiveInt();
- $did_remove_type = true;
- } else {
- // for now allow this check everywhere else
- if (!$atomic_type instanceof TNull
- && !$atomic_type instanceof TFalse
- ) {
- $positive_types[] = $atomic_type;
- }
-
- $did_remove_type = true;
- }
- }
-
- if (!$is_equality
- && !$existing_var_type->hasMixed()
- && (!$did_remove_type || !$positive_types)
- ) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'positive-numeric',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($positive_types) {
- return new Union($positive_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileHasMethod(
- Codebase $codebase,
- string $method_name,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- $object_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TNamedObject
- && $codebase->classOrInterfaceExists($type->value)
- ) {
- $object_types[] = $type;
-
- if (!$codebase->methodExists($type->value . '::' . $method_name)) {
- $match_found = false;
-
- if ($type->extra_types) {
- foreach ($type->extra_types as $extra_type) {
- if ($extra_type instanceof TNamedObject
- && $codebase->classOrInterfaceExists($extra_type->value)
- && $codebase->methodExists($extra_type->value . '::' . $method_name)
- ) {
- $match_found = true;
- } elseif ($extra_type instanceof TObjectWithProperties) {
- $match_found = true;
-
- if (!isset($extra_type->methods[$method_name])) {
- $extra_type->methods[$method_name] = 'object::' . $method_name;
- $did_remove_type = true;
- }
- }
- }
- }
-
- if (!$match_found) {
- $obj = new TObjectWithProperties(
- [],
- [$method_name => $type->value . '::' . $method_name]
- );
- $type->extra_types[$obj->getKey()] = $obj;
- $did_remove_type = true;
- }
- }
- } elseif ($type instanceof TObjectWithProperties) {
- $object_types[] = $type;
-
- if (!isset($type->methods[$method_name])) {
- $type->methods[$method_name] = 'object::' . $method_name;
- $did_remove_type = true;
- }
- } elseif ($type instanceof TObject || $type instanceof TMixed) {
- $object_types[] = new TObjectWithProperties(
- [],
- [$method_name => 'object::' . $method_name]
- );
- $did_remove_type = true;
- } elseif ($type instanceof TString) {
- // we don’t know
- $object_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- $object_types[] = $type;
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if (!$object_types || !$did_remove_type) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'object with method ' . $method_name,
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($object_types) {
- return new Union($object_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileString(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality,
- bool $is_strict_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed()) {
- if ($is_equality && !$is_strict_equality) {
- return $existing_var_type;
- }
-
- return Type::getString();
- }
-
- $string_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TString) {
- $string_types[] = $type;
-
- if (get_class($type) === TString::class) {
- $type->from_docblock = false;
- }
- } elseif ($type instanceof TCallable) {
- $string_types[] = new TCallableString;
- $did_remove_type = true;
- } elseif ($type instanceof TNumeric) {
- $string_types[] = new TNumericString;
- $did_remove_type = true;
- } elseif ($type instanceof TScalar || $type instanceof TArrayKey) {
- $string_types[] = new TString;
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasString() || $type->as->hasMixed() || $type->as->hasScalar()) {
- $type = clone $type;
-
- $type->as = self::reconcileString(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $is_strict_equality
- );
-
- $string_types[] = $type;
- }
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$did_remove_type || !$string_types) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'string',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($string_types) {
- return new Union($string_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileInt(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality,
- bool $is_strict_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- if ($is_equality && !$is_strict_equality) {
- return $existing_var_type;
- }
-
- return Type::getInt();
- }
-
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- $int_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TInt) {
- $int_types[] = $type;
-
- if (get_class($type) === TInt::class) {
- $type->from_docblock = false;
- }
-
- if ($existing_var_type->from_calculation) {
- $did_remove_type = true;
- }
- } elseif ($type instanceof TNumeric) {
- $int_types[] = new TInt;
- $did_remove_type = true;
- } elseif ($type instanceof TScalar || $type instanceof TArrayKey) {
- $int_types[] = new TInt;
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasInt() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileInt(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- $is_strict_equality
- );
-
- $int_types[] = $type;
- }
-
- $did_remove_type = true;
- } elseif ($type instanceof TString && $is_equality && !$is_strict_equality) {
- $int_types[] = new TNumericString();
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$did_remove_type || !$int_types) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'int',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($int_types) {
- return new Union($int_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileBool(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- return Type::getBool();
- }
-
- $bool_types = [];
- $did_remove_type = false;
-
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TBool) {
- $bool_types[] = $type;
- $type->from_docblock = false;
- } elseif ($type instanceof TScalar) {
- $bool_types[] = new TBool;
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasBool() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileBool(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $bool_types[] = $type;
- }
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$did_remove_type || !$bool_types) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'bool',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($bool_types) {
- return new Union($bool_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileScalar(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- return Type::getScalar();
- }
-
- $scalar_types = [];
- $did_remove_type = false;
-
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof Scalar) {
- $scalar_types[] = $type;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasScalar() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileScalar(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $scalar_types[] = $type;
- }
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$did_remove_type || !$scalar_types) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'scalar',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($scalar_types) {
- return new Union($scalar_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileNumeric(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- return Type::getNumeric();
- }
-
- $old_var_type_string = $existing_var_type->getId();
-
- $numeric_types = [];
- $did_remove_type = false;
-
- if ($existing_var_type->hasString()) {
- $did_remove_type = true;
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TNumericString);
- }
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TNumeric || $type instanceof TNumericString) {
- // this is a workaround for a possible issue running
- // is_numeric($a) && is_string($a)
- $did_remove_type = true;
- $numeric_types[] = $type;
- } elseif ($type->isNumericType()) {
- $numeric_types[] = $type;
- } elseif ($type instanceof TScalar) {
- $did_remove_type = true;
- $numeric_types[] = new TNumeric();
- } elseif ($type instanceof TArrayKey) {
- $did_remove_type = true;
- $numeric_types[] = new TInt();
- $numeric_types[] = new TNumericString();
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasNumeric() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileNumeric(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $numeric_types[] = $type;
- }
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$did_remove_type || !$numeric_types) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'numeric',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($numeric_types) {
- return new Union($numeric_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileObject(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- return Type::getObject();
- }
-
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- $object_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->isObjectType()) {
- $object_types[] = $type;
- } elseif ($type instanceof TCallable) {
- $object_types[] = new TCallableObject();
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam
- && $type->as->isMixed()
- ) {
- $type = clone $type;
- $type->as = Type::getObject();
- $object_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasObject() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileObject(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $object_types[] = $type;
- }
-
- $did_remove_type = true;
- } elseif ($type instanceof TIterable) {
- $clone_type = clone $type;
-
- self::refineArrayKey($clone_type->type_params[0]);
-
- $object_types[] = new TGenericObject('Traversable', $clone_type->type_params);
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$object_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'object',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($object_types) {
- return new Union($object_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileResource(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- return Type::getResource();
- }
-
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- $resource_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TResource) {
- $resource_types[] = $type;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$resource_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'resource',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($resource_types) {
- return new Union($resource_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileCountable(
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
-
- if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) {
- return new Union([
- new TArray([Type::getArrayKey(), Type::getMixed()]),
- new TNamedObject('Countable'),
- ]);
- }
-
- $iterable_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->isCountable($codebase)) {
- $iterable_types[] = $type;
- } elseif ($type instanceof TObject) {
- $iterable_types[] = new TNamedObject('Countable');
- $did_remove_type = true;
- } elseif ($type instanceof TNamedObject || $type instanceof TIterable) {
- $countable = new TNamedObject('Countable');
- $type->extra_types[$countable->getKey()] = $countable;
- $iterable_types[] = $type;
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$iterable_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'countable',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($iterable_types) {
- return new Union($iterable_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileIterable(
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) {
- return new Union([new TIterable]);
- }
-
- $iterable_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->isIterable($codebase)) {
- $iterable_types[] = $type;
- } elseif ($type instanceof TObject) {
- $iterable_types[] = new TNamedObject('Traversable');
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$iterable_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'iterable',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($iterable_types) {
- return new Union($iterable_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileInArray(
- Codebase $codebase,
- Union $existing_var_type,
- string $assertion,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation
- ): Union {
- $new_var_type = Type::parseString($assertion);
-
- $intersection = Type::intersectUnionTypes($new_var_type, $existing_var_type, $codebase);
-
- if ($intersection === null) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $existing_var_type->getId(),
- $key,
- '!' . $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- return $intersection;
- }
-
- private static function reconcileHasArrayKey(
- Union $existing_var_type,
- string $assertion
- ): Union {
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($atomic_type instanceof TKeyedArray) {
- $is_class_string = false;
-
- if (strpos($assertion, '::class')) {
- [$assertion] = explode('::', $assertion);
- $is_class_string = true;
- }
-
- if (isset($atomic_type->properties[$assertion])) {
- $atomic_type->properties[$assertion]->possibly_undefined = false;
- } else {
- $atomic_type->properties[$assertion] = Type::getMixed();
-
- if ($is_class_string) {
- $atomic_type->class_strings[$assertion] = true;
- }
- }
- }
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function reconcileSuperiorTo(
- Union $existing_var_type,
- string $assertion,
- bool $inside_loop,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $assertion_value = (int)substr($assertion, 1);
-
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($inside_loop) {
- continue;
- }
-
- if ($atomic_type instanceof TIntRange) {
- if ($atomic_type->contains($assertion_value)) {
- // if the range contains the assertion, the range must be adapted
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->min_bound === null) {
- $atomic_type->min_bound = $assertion_value;
- } else {
- $atomic_type->min_bound = TIntRange::getNewHighestBound(
- $assertion_value,
- $atomic_type->min_bound
- );
- }
- $existing_var_type->addType($atomic_type);
- } elseif ($atomic_type->isLesserThan($assertion_value)) {
- // if the range is lesser than the assertion, the type must be removed
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- } elseif ($atomic_type->isGreaterThan($assertion_value)) {
- // if the range is greater than the assertion, the check is redundant
- }
- } elseif ($atomic_type instanceof TLiteralInt) {
- if ($atomic_type->value < $assertion_value) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- } /*elseif ($inside_loop) {
- //when inside a loop, allow the range to extends the type
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->value < $assertion_value) {
- $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
- } else {
- $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
- }
- }*/
- } elseif ($atomic_type instanceof TPositiveInt) {
- if ($assertion_value > 1) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- $existing_var_type->addType(new TIntRange($assertion_value, null));
- }
- } elseif ($atomic_type instanceof TInt) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- $existing_var_type->addType(new TIntRange($assertion_value, null));
- } else {
- // we assume that other types may have been removed (empty strings? numeric strings?)
- //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison
- $did_remove_type = true;
- }
- }
-
- if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($existing_var_type->isUnionEmpty()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- $existing_var_type->addType(new TNever());
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function reconcileInferiorTo(
- Union $existing_var_type,
- string $assertion,
- bool $inside_loop,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $assertion_value = (int)substr($assertion, 1);
-
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($inside_loop) {
- continue;
- }
-
- if ($atomic_type instanceof TIntRange) {
- if ($atomic_type->contains($assertion_value)) {
- // if the range contains the assertion, the range must be adapted
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->max_bound === null) {
- $atomic_type->max_bound = $assertion_value;
- } else {
- $atomic_type->max_bound = min($atomic_type->max_bound, $assertion_value);
- }
- $existing_var_type->addType($atomic_type);
- } elseif ($atomic_type->isLesserThan($assertion_value)) {
- // if the range is lesser than the assertion, the check is redundant
- } elseif ($atomic_type->isGreaterThan($assertion_value)) {
- // if the range is greater than the assertion, the type must be removed
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- }
- } elseif ($atomic_type instanceof TLiteralInt) {
- if ($atomic_type->value > $assertion_value) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- } /* elseif ($inside_loop) {
- //when inside a loop, allow the range to extends the type
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->value < $assertion_value) {
- $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
- } else {
- $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
- }
- }*/
- } elseif ($atomic_type instanceof TPositiveInt) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- if ($assertion_value >= 1) {
- $existing_var_type->addType(new TIntRange(1, $assertion_value));
- }
- } elseif ($atomic_type instanceof TInt) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- $existing_var_type->addType(new TIntRange(null, $assertion_value));
- } else {
- // we assume that other types may have been removed (empty strings? numeric strings?)
- //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison
- $did_remove_type = true;
- }
- }
-
- if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($existing_var_type->isUnionEmpty()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- $existing_var_type->addType(new TNever());
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileTraversable(
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) {
- return new Union([new TNamedObject('Traversable')]);
- }
-
- $traversable_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->hasTraversableInterface($codebase)) {
- $traversable_types[] = $type;
- } elseif ($type instanceof TIterable) {
- $clone_type = clone $type;
- $traversable_types[] = new TGenericObject('Traversable', $clone_type->type_params);
- $did_remove_type = true;
- } elseif ($type instanceof TObject) {
- $traversable_types[] = new TNamedObject('Traversable');
- $did_remove_type = true;
- } elseif ($type instanceof TNamedObject) {
- $traversable = new TNamedObject('Traversable');
- $type->extra_types[$traversable->getKey()] = $traversable;
- $traversable_types[] = $type;
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$traversable_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'Traversable',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($traversable_types) {
- return new Union($traversable_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileArray(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed()) {
- return Type::getArray();
- }
-
- $array_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TArray || $type instanceof TKeyedArray || $type instanceof TList) {
- $array_types[] = $type;
- } elseif ($type instanceof TCallable) {
- $array_types[] = new TCallableKeyedArray([
- new Union([new TClassString, new TObject]),
- Type::getString()
- ]);
-
- $did_remove_type = true;
- } elseif ($type instanceof TIterable) {
- $clone_type = clone $type;
-
- self::refineArrayKey($clone_type->type_params[0]);
-
- $array_types[] = new TArray($clone_type->type_params);
-
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasArray() || $type->as->hasIterable() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileArray(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $array_types[] = $type;
- }
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$array_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'array',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
- }
-
- if ($array_types) {
- return TypeCombiner::combine($array_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileList(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality,
- bool $is_non_empty
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) {
- return $is_non_empty ? Type::getNonEmptyList() : Type::getList();
- }
-
- $array_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type instanceof TList
- || ($type instanceof TKeyedArray && $type->is_list)
- ) {
- if ($is_non_empty && $type instanceof TList && !$type instanceof TNonEmptyList) {
- $array_types[] = new TNonEmptyList($type->type_param);
- $did_remove_type = true;
- } else {
- $array_types[] = $type;
- }
- } elseif ($type instanceof TArray || $type instanceof TKeyedArray) {
- if ($type instanceof TKeyedArray) {
- $type = $type->getGenericArrayType();
- }
-
- if ($type->type_params[0]->hasArrayKey()
- || $type->type_params[0]->hasInt()
- ) {
- if ($type instanceof TNonEmptyArray) {
- $array_types[] = new TNonEmptyList($type->type_params[1]);
- } else {
- $array_types[] = new TList($type->type_params[1]);
- }
- }
-
- if ($type->type_params[0]->isEmpty()
- || $type->type_params[1]->isEmpty()
- ) {
- //we allow an empty array to pass as a list. We keep the type as empty array though (more precise)
- $array_types[] = $type;
- }
-
- $did_remove_type = true;
- } elseif ($type instanceof TCallable) {
- $array_types[] = new TCallableKeyedArray([
- new Union([new TClassString, new TObject]),
- Type::getString()
- ]);
-
- $did_remove_type = true;
- } elseif ($type instanceof TIterable) {
- $clone_type = clone $type;
- $array_types[] = new TList($clone_type->type_params[1]);
-
- $did_remove_type = true;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$array_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'array',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
- }
-
- if ($array_types) {
- return TypeCombiner::combine($array_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return $existing_var_type->from_docblock
- ? Type::getMixed()
- : Type::getEmpty();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileStringArrayAccess(
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $inside_loop
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed() || $existing_var_type->hasTemplate()) {
- return new Union([
- new TNonEmptyArray([Type::getArrayKey(), Type::getMixed()]),
- new TNamedObject('ArrayAccess'),
- ]);
- }
-
- $array_types = [];
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->isArrayAccessibleWithStringKey($codebase)) {
- if (get_class($type) === TArray::class) {
- $array_types[] = new TNonEmptyArray($type->type_params);
- } elseif (get_class($type) === TList::class) {
- $array_types[] = new TNonEmptyList($type->type_param);
- } else {
- $array_types[] = $type;
- }
- } elseif ($type instanceof TTemplateParam) {
- $array_types[] = $type;
- }
- }
-
- if (!$array_types) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'string-array-access',
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($array_types) {
- return new Union($array_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed($inside_loop);
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileIntArrayAccess(
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $inside_loop
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if ($existing_var_type->hasMixed()) {
- return Type::getMixed();
- }
-
- $array_types = [];
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->isArrayAccessibleWithIntOrStringKey($codebase)) {
- if (get_class($type) === TArray::class) {
- $array_types[] = new TNonEmptyArray($type->type_params);
- } else {
- $array_types[] = $type;
- }
- } elseif ($type instanceof TTemplateParam) {
- $array_types[] = $type;
- }
- }
-
- if (!$array_types) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'int-or-string-array-access',
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($array_types) {
- return TypeCombiner::combine($array_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed($inside_loop);
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileCallable(
- Codebase $codebase,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- if ($existing_var_type->hasMixed()) {
- return Type::parseString('callable');
- }
-
- $old_var_type_string = $existing_var_type->getId();
-
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- $callable_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_atomic_types as $type) {
- if ($type->isCallableType()) {
- $callable_types[] = $type;
- } elseif ($type instanceof TObject) {
- $callable_types[] = new TCallableObject();
- $did_remove_type = true;
- } elseif ($type instanceof TNamedObject
- && $codebase->classExists($type->value)
- && $codebase->methodExists($type->value . '::__invoke')
- ) {
- $callable_types[] = $type;
- } elseif (get_class($type) === TString::class
- || get_class($type) === TNonEmptyString::class
- || get_class($type) === TNonFalsyString::class
- ) {
- $callable_types[] = new TCallableString();
- $did_remove_type = true;
- } elseif (get_class($type) === TLiteralString::class
- && InternalCallMapHandler::inCallMap($type->value)
- ) {
- $callable_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TArray) {
- $type = clone $type;
- $type = new TCallableArray($type->type_params);
- $callable_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TList) {
- $type = clone $type;
- $type = new TCallableList($type->type_param);
- $callable_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TKeyedArray && count($type->properties) === 2) {
- $type = clone $type;
- $type = new TCallableKeyedArray($type->properties);
- $callable_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TTemplateParam) {
- if ($type->as->hasCallableType() || $type->as->hasMixed()) {
- $type = clone $type;
-
- $type->as = self::reconcileCallable(
- $codebase,
- $type->as,
- null,
- $negated,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- $did_remove_type = true;
-
- $callable_types[] = $type;
- } else {
- $did_remove_type = true;
- }
- }
-
- if ((!$callable_types || !$did_remove_type) && !$is_equality) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- 'callable',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
-
- if ($callable_types) {
- return TypeCombiner::combine($callable_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileFalsyOrEmpty(
- string $assertion,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $recursive_check
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- $did_remove_type = $existing_var_type->possibly_undefined
- || $existing_var_type->possibly_undefined_from_try;
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_key => $existing_var_type_part) {
- //if any atomic in the union is either always truthy, we remove it. If not always falsy, we mark the check
- //as not redundant.
- if (!$existing_var_type->possibly_undefined
- && !$existing_var_type->possibly_undefined_from_try
- && $existing_var_type_part->isTruthy()
- ) {
- $did_remove_type = true;
- $existing_var_type->removeType($existing_var_type_key);
- } elseif (!$existing_var_type_part->isFalsy()) {
- $did_remove_type = true;
- }
- }
-
- if ($did_remove_type && $existing_var_type->isUnionEmpty()) {
- //every type was removed, this is an impossible assertion
- if ($code_location && $key && !$recursive_check) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $failed_reconciliation = 2;
-
- return Type::getEmpty();
- }
-
- if (!$did_remove_type) {
- //nothing was removed, this is a redundant assertion
- if ($code_location && $key && !$recursive_check) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $failed_reconciliation = 1;
-
- return $existing_var_type;
- }
-
- if ($existing_var_type->hasType('bool')) {
- $existing_var_type->removeType('bool');
- $existing_var_type->addType(new TFalse());
- }
-
- if ($existing_var_type->hasArray()) {
- $existing_var_type->removeType('array');
- $existing_var_type->addType(new TArray(
- [
- new Union([new TEmpty()]),
- new Union([new TEmpty()]),
- ]
- ));
- }
-
- if ($existing_var_type->hasMixed()) {
- $mixed_atomic_type = $existing_var_type->getAtomicTypes()['mixed'];
-
- if (get_class($mixed_atomic_type) === TMixed::class) {
- $existing_var_type->removeType('mixed');
- $existing_var_type->addType(new TEmptyMixed());
- }
- }
-
- if ($existing_var_type->hasScalar()) {
- $scalar_atomic_type = $existing_var_type->getAtomicTypes()['scalar'];
-
- if (get_class($scalar_atomic_type) === TScalar::class) {
- $existing_var_type->removeType('scalar');
- $existing_var_type->addType(new TEmptyScalar());
- }
- }
-
- if ($existing_var_type->hasType('string')) {
- $string_atomic_type = $existing_var_type->getAtomicTypes()['string'];
-
- if (get_class($string_atomic_type) === TString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TLiteralString(''));
- $existing_var_type->addType(new TLiteralString('0'));
- } elseif (get_class($string_atomic_type) === TNonEmptyString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TLiteralString('0'));
- } elseif (get_class($string_atomic_type) === TNonEmptyLowercaseString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TLiteralString('0'));
- } elseif (get_class($string_atomic_type) === TNonEmptyNonspecificLiteralString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TLiteralString('0'));
- }
- }
-
- if ($existing_var_type->hasInt()) {
- $existing_range_types = $existing_var_type->getRangeInts();
-
- if ($existing_range_types) {
- foreach ($existing_range_types as $int_key => $literal_type) {
- if ($literal_type->contains(0)) {
- $existing_var_type->removeType($int_key);
- $existing_var_type->addType(new TLiteralInt(0));
- }
- }
- } else {
- $existing_var_type->removeType('int');
- $existing_var_type->addType(new TLiteralInt(0));
- }
- }
-
- if ($existing_var_type->hasFloat()) {
- $existing_var_type->removeType('float');
- $existing_var_type->addType(new TLiteralFloat(0.0));
- }
-
- if ($existing_var_type->hasNumeric()) {
- $existing_var_type->removeType('numeric');
- $existing_var_type->addType(new TEmptyNumeric());
- }
-
- foreach ($existing_var_type->getAtomicTypes() as $type_key => $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TTemplateParam) {
- if (!$existing_var_atomic_type->as->isMixed()) {
- $template_did_fail = 0;
-
- $existing_var_atomic_type = clone $existing_var_atomic_type;
-
- $existing_var_atomic_type->as = self::reconcileFalsyOrEmpty(
- $assertion,
- $existing_var_atomic_type->as,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $template_did_fail,
- $recursive_check
- );
-
- if (!$template_did_fail) {
- $existing_var_type->removeType($type_key);
- $existing_var_type->addType($existing_var_atomic_type);
- }
- }
- }
- }
-
- assert(!$existing_var_type->isUnionEmpty());
- return $existing_var_type;
- }
-
- /**
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileClassConstant(
- Codebase $codebase,
- string $class_constant_expression,
- Union $existing_type,
- int &$failed_reconciliation
- ): Union {
- if (strpos($class_constant_expression, '::') === false) {
- return $existing_type;
- }
-
- [$class_name, $constant_pattern] = explode('::', $class_constant_expression, 2);
-
- $resolver = new ClassConstantByWildcardResolver($codebase);
- $matched_class_constant_types = $resolver->resolve($class_name, $constant_pattern);
- if ($matched_class_constant_types === null) {
- return $existing_type;
- }
-
- if ($matched_class_constant_types === []) {
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
- return Type::getMixed();
- }
-
- return TypeCombiner::combine($matched_class_constant_types, $codebase);
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php
deleted file mode 100644
index 02fac14..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleNegatedAssertionReconciler.php
+++ /dev/null
@@ -1,1819 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\CodeLocation;
-use Psalm\Codebase;
-use Psalm\Internal\Codebase\InternalCallMapHandler;
-use Psalm\Issue\DocblockTypeContradiction;
-use Psalm\Issue\RedundantPropertyInitializationCheck;
-use Psalm\Issue\TypeDoesNotContainType;
-use Psalm\IssueBuffer;
-use Psalm\Type;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableObject;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNonEmptyLowercaseString;
-use Psalm\Type\Atomic\TNonEmptyMixed;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyScalar;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonFalsyString;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Atomic\TNumeric;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Reconciler;
-use Psalm\Type\Union;
-
-use function assert;
-use function get_class;
-use function max;
-use function strpos;
-use function substr;
-
-class SimpleNegatedAssertionReconciler extends Reconciler
-{
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- public static function reconcile(
- Codebase $codebase,
- string $assertion,
- Union $existing_var_type,
- ?string $key = null,
- bool $negated = false,
- ?CodeLocation $code_location = null,
- array $suppressed_issues = [],
- int &$failed_reconciliation = Reconciler::RECONCILIATION_EMPTY,
- bool $is_equality = false,
- bool $inside_loop = false
- ): ?Union {
- $old_var_type_string = $existing_var_type->getId();
-
- if ($assertion === 'isset') {
- if ($existing_var_type->possibly_undefined) {
- return Type::getEmpty();
- }
-
- if (!$existing_var_type->isNullable()
- && $key
- && strpos($key, '[') === false
- && (!$existing_var_type->hasMixed() || $existing_var_type->isAlwaysTruthy())
- ) {
- if ($code_location) {
- if ($existing_var_type->from_static_property) {
- IssueBuffer::maybeAdd(
- new RedundantPropertyInitializationCheck(
- 'Static property ' . $key . ' with type '
- . $existing_var_type
- . ' has unexpected isset check — should it be nullable?',
- $code_location
- ),
- $suppressed_issues
- );
- } elseif ($existing_var_type->from_property) {
- IssueBuffer::maybeAdd(
- new RedundantPropertyInitializationCheck(
- 'Property ' . $key . ' with type '
- . $existing_var_type . ' should already be set in the constructor',
- $code_location
- ),
- $suppressed_issues
- );
- } elseif ($existing_var_type->from_docblock) {
- IssueBuffer::maybeAdd(
- new DocblockTypeContradiction(
- 'Cannot resolve types for ' . $key . ' with docblock-defined type '
- . $existing_var_type . ' and !isset assertion',
- $code_location,
- null
- ),
- $suppressed_issues
- );
- } else {
- IssueBuffer::maybeAdd(
- new TypeDoesNotContainType(
- 'Cannot resolve types for ' . $key . ' with type '
- . $existing_var_type . ' and !isset assertion',
- $code_location,
- null
- ),
- $suppressed_issues
- );
- }
- }
-
- return $existing_var_type->from_docblock
- ? Type::getNull()
- : Type::getEmpty();
- }
-
- return Type::getNull();
- }
-
- if ($assertion === 'array-key-exists') {
- return Type::getEmpty();
- }
-
- if (strpos($assertion, 'in-array-') === 0) {
- $assertion = substr($assertion, 9);
- $new_var_type = Type::parseString($assertion);
-
- $intersection = Type::intersectUnionTypes(
- $new_var_type,
- $existing_var_type,
- $codebase
- );
-
- if ($intersection === null) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $existing_var_type->getId(),
- $key,
- '!' . $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
- }
-
- return $existing_var_type;
- }
-
- if ($assertion === 'object' && !$existing_var_type->hasMixed()) {
- return self::reconcileObject(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'scalar' && !$existing_var_type->hasMixed()) {
- return self::reconcileScalar(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'resource' && !$existing_var_type->hasMixed()) {
- return self::reconcileResource(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'bool' && !$existing_var_type->hasMixed()) {
- return self::reconcileBool(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'numeric' && !$existing_var_type->hasMixed()) {
- return self::reconcileNumeric(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'float' && !$existing_var_type->hasMixed()) {
- return self::reconcileFloat(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'int' && !$existing_var_type->hasMixed()) {
- return self::reconcileInt(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'string' && !$existing_var_type->hasMixed()) {
- return self::reconcileString(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'array' && !$existing_var_type->hasMixed()) {
- return self::reconcileArray(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'falsy' || $assertion === 'empty') {
- return self::reconcileFalsyOrEmpty(
- $assertion,
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- false
- );
- }
-
- if ($assertion === 'null' && !$existing_var_type->hasMixed()) {
- return self::reconcileNull(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'false' && !$existing_var_type->hasMixed()) {
- return self::reconcileFalse(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
- }
-
- if ($assertion === 'non-empty-countable') {
- return self::reconcileNonEmptyCountable(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- null
- );
- }
-
- if ($assertion === 'callable') {
- return self::reconcileCallable(
- $existing_var_type
- );
- }
-
- if (strpos($assertion, 'has-at-least-') === 0) {
- return self::reconcileNonEmptyCountable(
- $existing_var_type,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality,
- (int) substr($assertion, 13)
- );
- }
-
- if (strpos($assertion, 'has-exactly-') === 0) {
- return $existing_var_type;
- }
-
- if ($assertion[0] === '>') {
- return self::reconcileSuperiorTo(
- $existing_var_type,
- $assertion,
- $inside_loop,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($assertion[0] === '<') {
- return self::reconcileInferiorTo(
- $existing_var_type,
- $assertion,
- $inside_loop,
- $old_var_type_string,
- $key,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- return null;
- }
-
- private static function reconcileCallable(
- Union $existing_var_type
- ): Union {
- foreach ($existing_var_type->getAtomicTypes() as $atomic_key => $type) {
- if ($type instanceof TLiteralString
- && InternalCallMapHandler::inCallMap($type->value)
- ) {
- $existing_var_type->removeType($atomic_key);
- }
-
- if ($type->isCallableType()) {
- $existing_var_type->removeType($atomic_key);
- }
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileBool(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_bool_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$type->as->hasBool()) {
- $non_bool_types[] = $type;
- }
-
- $did_remove_type = true;
- } elseif (!$type instanceof TBool
- || ($is_equality && get_class($type) === TBool::class)
- ) {
- if ($type instanceof TScalar) {
- $did_remove_type = true;
- $non_bool_types[] = new TString();
- $non_bool_types[] = new TInt();
- $non_bool_types[] = new TFloat();
- } else {
- $non_bool_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- }
- }
-
- if (!$did_remove_type || !$non_bool_types) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!bool',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_bool_types) {
- return new Union($non_bool_types);
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileNonEmptyCountable(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality,
- ?int $min_count
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $existing_var_atomic_types = $existing_var_type->getAtomicTypes();
-
- if (isset($existing_var_atomic_types['array'])) {
- $array_atomic_type = $existing_var_atomic_types['array'];
- $did_remove_type = false;
-
- if (($array_atomic_type instanceof TNonEmptyArray
- || $array_atomic_type instanceof TNonEmptyList)
- && ($min_count === null
- || $array_atomic_type->count >= $min_count)
- ) {
- $did_remove_type = true;
-
- $existing_var_type->removeType('array');
- } elseif ($array_atomic_type->getId() !== 'array<empty, empty>') {
- $did_remove_type = true;
-
- if (!$min_count) {
- $existing_var_type->addType(new TArray(
- [
- new Union([new TEmpty]),
- new Union([new TEmpty]),
- ]
- ));
- }
- } elseif ($array_atomic_type instanceof TKeyedArray) {
- $did_remove_type = true;
-
- foreach ($array_atomic_type->properties as $property_type) {
- if (!$property_type->possibly_undefined) {
- $did_remove_type = false;
- break;
- }
- }
- }
-
- if (!$is_equality
- && !$existing_var_type->hasMixed()
- && (!$did_remove_type || $existing_var_type->isUnionEmpty())
- ) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!non-empty-countable',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- }
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileNull(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $did_remove_type = false;
-
- if ($existing_var_type->hasType('null')) {
- $did_remove_type = true;
- $existing_var_type->removeType('null');
- }
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- $type->as = self::reconcileNull(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $did_remove_type = true;
- $existing_var_type->bustCache();
- }
- }
-
- if (!$did_remove_type || $existing_var_type->isUnionEmpty()) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!null',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if (!$existing_var_type->isUnionEmpty()) {
- return $existing_var_type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileFalse(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $did_remove_type = $existing_var_type->hasScalar();
-
- if ($existing_var_type->hasType('false')) {
- $did_remove_type = true;
- $existing_var_type->removeType('false');
- }
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- $type->as = self::reconcileFalse(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $did_remove_type = true;
- $existing_var_type->bustCache();
- }
- }
-
- if (!$did_remove_type || $existing_var_type->isUnionEmpty()) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!false',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if (!$existing_var_type->isUnionEmpty()) {
- return $existing_var_type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileFalsyOrEmpty(
- string $assertion,
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $recursive_check
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
-
- //empty is used a lot to check for array offset existence, so we have to silent errors a lot
- $is_empty_assertion = $assertion === 'empty';
-
- $did_remove_type = $existing_var_type->possibly_undefined
- || $existing_var_type->possibly_undefined_from_try;
-
- foreach ($existing_var_type->getAtomicTypes() as $existing_var_type_key => $existing_var_type_part) {
- //if any atomic in the union is either always falsy, we remove it. If not always truthy, we mark the check
- //as not redundant.
- if ($existing_var_type_part->isFalsy()) {
- $did_remove_type = true;
- $existing_var_type->removeType($existing_var_type_key);
- } elseif ($existing_var_type->possibly_undefined
- || $existing_var_type->possibly_undefined_from_try
- || !$existing_var_type_part->isTruthy()
- ) {
- $did_remove_type = true;
- }
- }
-
- if ($did_remove_type && $existing_var_type->isUnionEmpty()) {
- //every type was removed, this is an impossible assertion
- if ($code_location && $key && !$is_empty_assertion && !$recursive_check) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!' . $assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $failed_reconciliation = 2;
-
- return Type::getEmpty();
- }
-
- if (!$did_remove_type) {
- if ($code_location && $key && !$is_empty_assertion && !$recursive_check) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!' . $assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- $failed_reconciliation = 1;
-
- return $existing_var_type;
- }
-
- $existing_var_type->possibly_undefined = false;
- $existing_var_type->possibly_undefined_from_try = false;
-
- if ($existing_var_type->hasType('bool')) {
- $existing_var_type->removeType('bool');
- $existing_var_type->addType(new TTrue());
- }
-
- if ($existing_var_type->hasArray()) {
- $array_atomic_type = $existing_var_type->getAtomicTypes()['array'];
-
- if ($array_atomic_type instanceof TArray
- && !$array_atomic_type instanceof TNonEmptyArray
- ) {
- $existing_var_type->removeType('array');
- $existing_var_type->addType(
- new TNonEmptyArray(
- $array_atomic_type->type_params
- )
- );
- } elseif ($array_atomic_type instanceof TList
- && !$array_atomic_type instanceof TNonEmptyList
- ) {
- $existing_var_type->removeType('array');
- $existing_var_type->addType(
- new TNonEmptyList(
- $array_atomic_type->type_param
- )
- );
- }
- }
-
- if ($existing_var_type->hasMixed()) {
- $mixed_atomic_type = $existing_var_type->getAtomicTypes()['mixed'];
-
- if (get_class($mixed_atomic_type) === TMixed::class) {
- $existing_var_type->removeType('mixed');
- $existing_var_type->addType(new TNonEmptyMixed());
- }
- }
-
- if ($existing_var_type->hasScalar()) {
- $scalar_atomic_type = $existing_var_type->getAtomicTypes()['scalar'];
-
- if (get_class($scalar_atomic_type) === TScalar::class) {
- $existing_var_type->removeType('scalar');
- $existing_var_type->addType(new TNonEmptyScalar());
- }
- }
-
- if ($existing_var_type->hasType('string')) {
- $string_atomic_type = $existing_var_type->getAtomicTypes()['string'];
-
- if (get_class($string_atomic_type) === TString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TNonFalsyString());
- } elseif (get_class($string_atomic_type) === TLowercaseString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TNonEmptyLowercaseString());
- } elseif (get_class($string_atomic_type) === TNonspecificLiteralString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TNonEmptyNonspecificLiteralString());
- } elseif (get_class($string_atomic_type) === TNonEmptyString::class) {
- $existing_var_type->removeType('string');
- $existing_var_type->addType(new TNonFalsyString());
- }
- }
-
- if ($existing_var_type->hasInt()) {
- $existing_range_types = $existing_var_type->getRangeInts();
-
- if ($existing_range_types) {
- foreach ($existing_range_types as $int_key => $literal_type) {
- if ($literal_type->contains(0)) {
- $existing_var_type->removeType($int_key);
- if ($literal_type->min_bound === null || $literal_type->min_bound <= -1) {
- $existing_var_type->addType(new TIntRange($literal_type->min_bound, -1));
- }
- if ($literal_type->max_bound === null || $literal_type->max_bound >= 1) {
- $existing_var_type->addType(new TIntRange(1, $literal_type->max_bound));
- }
- }
- }
- }
-
- if ($existing_var_type->isSingle()) {
- return $existing_var_type;
- }
- }
-
- foreach ($existing_var_type->getAtomicTypes() as $type_key => $existing_var_atomic_type) {
- if ($existing_var_atomic_type instanceof TTemplateParam) {
- if (!$existing_var_atomic_type->as->isMixed()) {
- $template_did_fail = 0;
-
- $existing_var_atomic_type = clone $existing_var_atomic_type;
-
- $existing_var_atomic_type->as = self::reconcileFalsyOrEmpty(
- $assertion,
- $existing_var_atomic_type->as,
- $key,
- $negated,
- $code_location,
- $suppressed_issues,
- $template_did_fail,
- true
- );
-
- if (!$template_did_fail) {
- $existing_var_type->removeType($type_key);
- $existing_var_type->addType($existing_var_atomic_type);
- }
- }
- }
- }
-
- assert(!$existing_var_type->isUnionEmpty());
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileScalar(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_scalar_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileScalar(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_scalar_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_scalar_types[] = $type;
- }
- } elseif (!($type instanceof Scalar)) {
- $non_scalar_types[] = $type;
- } else {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_scalar_types[] = $type;
- }
- }
- }
-
- if (!$did_remove_type || !$non_scalar_types) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!scalar',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_scalar_types) {
- $type = new Union($non_scalar_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileObject(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_object_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileObject(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_object_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_object_types[] = $type;
- }
- } elseif ($type instanceof TCallable) {
- $non_object_types[] = new TCallableArray([
- Type::getArrayKey(),
- Type::getMixed()
- ]);
- $non_object_types[] = new TCallableString();
- $did_remove_type = true;
- } elseif ($type instanceof TIterable) {
- $clone_type = clone $type;
-
- self::refineArrayKey($clone_type->type_params[0]);
-
- $non_object_types[] = new TArray($clone_type->type_params);
-
- $did_remove_type = true;
- } elseif (!$type->isObjectType()) {
- $non_object_types[] = $type;
- } else {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_object_types[] = $type;
- }
- }
- }
-
- if (!$non_object_types || !$did_remove_type) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!object',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_object_types) {
- $type = new Union($non_object_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileNumeric(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_numeric_types = [];
- $did_remove_type = $existing_var_type->hasString()
- || $existing_var_type->hasScalar();
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileNumeric(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_numeric_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_numeric_types[] = $type;
- }
- } elseif ($type instanceof TArrayKey) {
- $did_remove_type = true;
- $non_numeric_types[] = new TString();
- } elseif (!$type->isNumericType()) {
- $non_numeric_types[] = $type;
- } else {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_numeric_types[] = $type;
- }
- }
- }
-
- if (!$non_numeric_types || !$did_remove_type) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!numeric',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_numeric_types) {
- $type = new Union($non_numeric_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileInt(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_int_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileInt(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_int_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_int_types[] = $type;
- }
- } elseif ($type instanceof TArrayKey) {
- $did_remove_type = true;
- $non_int_types[] = new TString();
- } elseif ($type instanceof TScalar) {
- $did_remove_type = true;
- $non_int_types[] = new TString();
- $non_int_types[] = new TFloat();
- $non_int_types[] = new TBool();
- } elseif ($type instanceof TInt) {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_int_types[] = $type;
- } elseif ($existing_var_type->from_calculation) {
- $non_int_types[] = new TFloat();
- }
- } elseif ($type instanceof TNumeric) {
- $did_remove_type = true;
- $non_int_types[] = new TString();
- $non_int_types[] = new TFloat();
- } else {
- $non_int_types[] = $type;
- }
- }
-
- if (!$non_int_types || !$did_remove_type) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!int',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_int_types) {
- $type = new Union($non_int_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileFloat(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_float_types = [];
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileFloat(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_float_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_float_types[] = $type;
- }
- } elseif ($type instanceof TScalar) {
- $did_remove_type = true;
- $non_float_types[] = new TString();
- $non_float_types[] = new TInt();
- $non_float_types[] = new TBool();
- } elseif ($type instanceof TFloat) {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_float_types[] = $type;
- }
- } elseif ($type instanceof TNumeric) {
- $did_remove_type = true;
- $non_float_types[] = new TString();
- $non_float_types[] = new TInt();
- } else {
- $non_float_types[] = $type;
- }
- }
-
- if (!$non_float_types || !$did_remove_type) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!float',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_float_types) {
- $type = new Union($non_float_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileString(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_string_types = [];
- $did_remove_type = $existing_var_type->hasScalar();
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileString(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_string_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_string_types[] = $type;
- }
- } elseif ($type instanceof TArrayKey) {
- $non_string_types[] = new TInt();
- $did_remove_type = true;
- } elseif ($type instanceof TCallable) {
- $non_string_types[] = new TCallableArray([
- Type::getArrayKey(),
- Type::getMixed()
- ]);
- $non_string_types[] = new TCallableObject();
- $did_remove_type = true;
- } elseif ($type instanceof TNumeric) {
- $non_string_types[] = $type;
- $did_remove_type = true;
- } elseif ($type instanceof TScalar) {
- $did_remove_type = true;
- $non_string_types[] = new TFloat();
- $non_string_types[] = new TInt();
- $non_string_types[] = new TBool();
- } elseif (!$type instanceof TString) {
- $non_string_types[] = $type;
- } else {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_string_types[] = $type;
- }
- }
- }
-
- if (!$non_string_types || !$did_remove_type) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!string',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_string_types) {
- $type = new Union($non_string_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileArray(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $non_array_types = [];
- $did_remove_type = $existing_var_type->hasScalar();
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- if (!$is_equality && !$type->as->isMixed()) {
- $template_did_fail = 0;
-
- $type = clone $type;
-
- $type->as = self::reconcileArray(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $template_did_fail,
- $is_equality
- );
-
- $did_remove_type = true;
-
- if (!$template_did_fail) {
- $non_array_types[] = $type;
- }
- } else {
- $did_remove_type = true;
- $non_array_types[] = $type;
- }
- } elseif ($type instanceof TCallable) {
- $non_array_types[] = new TCallableString();
- $non_array_types[] = new TCallableObject();
- $did_remove_type = true;
- } elseif ($type instanceof TIterable) {
- if (!$type->type_params[0]->isMixed() || !$type->type_params[1]->isMixed()) {
- $non_array_types[] = new TGenericObject('Traversable', $type->type_params);
- } else {
- $non_array_types[] = new TNamedObject('Traversable');
- }
-
- $did_remove_type = true;
- } elseif (!$type instanceof TArray
- && !$type instanceof TKeyedArray
- && !$type instanceof TList
- ) {
- $non_array_types[] = $type;
- } else {
- $did_remove_type = true;
-
- if ($is_equality) {
- $non_array_types[] = $type;
- }
- }
- }
-
- if ((!$non_array_types || !$did_remove_type)) {
- if ($key && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!array',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if ($non_array_types) {
- $type = new Union($non_array_types);
- $type->ignore_falsable_issues = $existing_var_type->ignore_falsable_issues;
- $type->ignore_nullable_issues = $existing_var_type->ignore_nullable_issues;
- $type->from_docblock = $existing_var_type->from_docblock;
- return $type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- * @param Reconciler::RECONCILIATION_* $failed_reconciliation
- */
- private static function reconcileResource(
- Union $existing_var_type,
- ?string $key,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues,
- int &$failed_reconciliation,
- bool $is_equality
- ): Union {
- $old_var_type_string = $existing_var_type->getId();
- $did_remove_type = false;
-
- if ($existing_var_type->hasType('resource')) {
- $did_remove_type = true;
- $existing_var_type->removeType('resource');
- }
-
- foreach ($existing_var_type->getAtomicTypes() as $type) {
- if ($type instanceof TTemplateParam) {
- $type->as = self::reconcileResource(
- $type->as,
- null,
- false,
- null,
- $suppressed_issues,
- $failed_reconciliation,
- $is_equality
- );
-
- $did_remove_type = true;
- $existing_var_type->bustCache();
- }
- }
-
- if (!$did_remove_type || $existing_var_type->isUnionEmpty()) {
- if ($key && $code_location && !$is_equality) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $key,
- '!resource',
- !$did_remove_type,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if (!$did_remove_type) {
- $failed_reconciliation = Reconciler::RECONCILIATION_REDUNDANT;
- }
- }
-
- if (!$existing_var_type->isUnionEmpty()) {
- return $existing_var_type;
- }
-
- $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY;
-
- return Type::getMixed();
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function reconcileSuperiorTo(
- Union $existing_var_type,
- string $assertion,
- bool $inside_loop,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $assertion_value = (int)substr($assertion, 1) - 1;
-
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($inside_loop) {
- continue;
- }
-
- if ($atomic_type instanceof TIntRange) {
- if ($atomic_type->contains($assertion_value)) {
- // if the range contains the assertion, the range must be adapted
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->max_bound === null) {
- $atomic_type->max_bound = $assertion_value;
- } else {
- $atomic_type->max_bound = TIntRange::getNewLowestBound(
- $assertion_value,
- $atomic_type->max_bound
- );
- }
- $existing_var_type->addType($atomic_type);
- } elseif ($atomic_type->isLesserThan($assertion_value)) {
- // if the range is lesser than the assertion, the check is redundant
- } elseif ($atomic_type->isGreaterThan($assertion_value)) {
- // if the range is greater than the assertion, the type must be removed
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- }
- } elseif ($atomic_type instanceof TLiteralInt) {
- if ($atomic_type->value > $assertion_value) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- } /*elseif ($inside_loop) {
- //when inside a loop, allow the range to extends the type
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->value < $assertion_value) {
- $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
- } else {
- $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
- }
- }*/
- } elseif ($atomic_type instanceof TPositiveInt) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- if ($assertion_value >= 1) {
- $existing_var_type->addType(new TIntRange(1, $assertion_value));
- }
- } elseif ($atomic_type instanceof TInt) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- $existing_var_type->addType(new TIntRange(null, $assertion_value));
- } else {
- // we assume that other types may have been removed (empty strings? numeric strings?)
- //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison
- $did_remove_type = true;
- }
- }
-
- if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- '!'.$assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($existing_var_type->isUnionEmpty()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- '!'.$assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- $existing_var_type->addType(new TNever());
- }
-
- return $existing_var_type;
- }
-
- /**
- * @param string[] $suppressed_issues
- */
- private static function reconcileInferiorTo(
- Union $existing_var_type,
- string $assertion,
- bool $inside_loop,
- string $old_var_type_string,
- ?string $var_id,
- bool $negated,
- ?CodeLocation $code_location,
- array $suppressed_issues
- ): Union {
- $assertion_value = (int)substr($assertion, 1) + 1;
-
- $did_remove_type = false;
-
- foreach ($existing_var_type->getAtomicTypes() as $atomic_type) {
- if ($inside_loop) {
- continue;
- }
-
- if ($atomic_type instanceof TIntRange) {
- if ($atomic_type->contains($assertion_value)) {
- // if the range contains the assertion, the range must be adapted
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->min_bound === null) {
- $atomic_type->min_bound = $assertion_value;
- } else {
- $atomic_type->min_bound = max($atomic_type->min_bound, $assertion_value);
- }
- $existing_var_type->addType($atomic_type);
- } elseif ($atomic_type->isLesserThan($assertion_value)) {
- // if the range is lesser than the assertion, the type must be removed
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- } elseif ($atomic_type->isGreaterThan($assertion_value)) {
- // if the range is greater than the assertion, the check is redundant
- }
- } elseif ($atomic_type instanceof TLiteralInt) {
- if ($atomic_type->value < $assertion_value) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- } /* elseif ($inside_loop) {
- //when inside a loop, allow the range to extends the type
- $existing_var_type->removeType($atomic_type->getKey());
- if ($atomic_type->value < $assertion_value) {
- $existing_var_type->addType(new TIntRange($atomic_type->value, $assertion_value));
- } else {
- $existing_var_type->addType(new TIntRange($assertion_value, $atomic_type->value));
- }
- }*/
- } elseif ($atomic_type instanceof TPositiveInt) {
- if ($assertion_value > 1) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- $existing_var_type->addType(new TIntRange($assertion_value, null));
- }
- } elseif ($atomic_type instanceof TInt) {
- $did_remove_type = true;
- $existing_var_type->removeType($atomic_type->getKey());
- $existing_var_type->addType(new TIntRange($assertion_value, null));
- } else {
- // we assume that other types may have been removed (empty strings? numeric strings?)
- //It may be worth refining to improve reconciliation while keeping in mind we're on loose comparison
- $did_remove_type = true;
- }
- }
-
- if (!$inside_loop && !$did_remove_type && $var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- '!'.$assertion,
- true,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
-
- if ($existing_var_type->isUnionEmpty()) {
- if ($var_id && $code_location) {
- self::triggerIssueForImpossible(
- $existing_var_type,
- $old_var_type_string,
- $var_id,
- '!'.$assertion,
- false,
- $negated,
- $code_location,
- $suppressed_issues
- );
- }
- $existing_var_type->addType(new TNever());
- }
-
- return $existing_var_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateBound.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateBound.php
deleted file mode 100644
index da8da46..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateBound.php
+++ /dev/null
@@ -1,52 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Type\Union;
-
-class TemplateBound
-{
- /**
- * @var Union
- */
- public $type;
-
- /**
- * This is the depth at which the template appears in a given type.
- *
- * In the type Foo<T, Bar<T, array<T>>> the type T appears at three different depths.
- *
- * The shallowest-appearance of the template takes prominence when inferring the type of T.
- *
- * @var int
- */
- public $appearance_depth;
-
- /**
- * The argument offset where this template was set
- *
- * In the type Foo<T, string, T> the type appears at argument offsets 0 and 2
- *
- * @var ?int
- */
- public $arg_offset;
-
- /**
- * When non-null, indicates an equality template bound (vs a lower or upper bound)
- *
- * @var ?string
- */
- public $equality_bound_classlike;
-
- public function __construct(
- Union $type,
- int $appearance_depth = 0,
- ?int $arg_offset = null,
- ?string $equality_bound_classlike = null
- ) {
- $this->type = $type;
- $this->appearance_depth = $appearance_depth;
- $this->arg_offset = $arg_offset;
- $this->equality_bound_classlike = $equality_bound_classlike;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php
deleted file mode 100644
index d0aff71..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateInferredTypeReplacer.php
+++ /dev/null
@@ -1,416 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use InvalidArgumentException;
-use Psalm\Codebase;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TemplateBound;
-use Psalm\Internal\Type\TemplateStandinTypeReplacer;
-use Psalm\Type;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateIndexedAccess;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_merge;
-use function array_shift;
-use function array_values;
-use function strpos;
-
-class TemplateInferredTypeReplacer
-{
- /**
- * This replaces template types in unions with the inferred types they should be
- */
- public static function replace(
- Union $union,
- TemplateResult $template_result,
- ?Codebase $codebase
- ): void {
- $keys_to_unset = [];
-
- $new_types = [];
-
- $is_mixed = false;
-
- $inferred_lower_bounds = $template_result->lower_bounds ?: [];
-
- foreach ($union->getAtomicTypes() as $key => $atomic_type) {
- $atomic_type->replaceTemplateTypesWithArgTypes($template_result, $codebase);
-
- if ($atomic_type instanceof TTemplateParam) {
- $template_type = null;
-
- $traversed_type = TemplateStandinTypeReplacer::getRootTemplateType(
- $inferred_lower_bounds,
- $atomic_type->param_name,
- $atomic_type->defining_class,
- [],
- $codebase
- );
-
- if ($traversed_type) {
- $template_type = $traversed_type;
-
- if (!$atomic_type->as->isMixed() && $template_type->isMixed()) {
- $template_type = clone $atomic_type->as;
- } else {
- $template_type = clone $template_type;
- }
-
- if ($atomic_type->extra_types) {
- foreach ($template_type->getAtomicTypes() as $template_type_key => $atomic_template_type) {
- if ($atomic_template_type instanceof TNamedObject
- || $atomic_template_type instanceof TTemplateParam
- || $atomic_template_type instanceof TIterable
- || $atomic_template_type instanceof TObjectWithProperties
- ) {
- $atomic_template_type->extra_types = array_merge(
- $atomic_type->extra_types,
- $atomic_template_type->extra_types ?: []
- );
- } elseif ($atomic_template_type instanceof TObject) {
- $first_atomic_type = array_shift($atomic_type->extra_types);
-
- if ($atomic_type->extra_types) {
- $first_atomic_type->extra_types = $atomic_type->extra_types;
- }
-
- $template_type->removeType($template_type_key);
- $template_type->addType($first_atomic_type);
- }
- }
- }
- } elseif ($codebase) {
- foreach ($inferred_lower_bounds as $template_type_map) {
- foreach ($template_type_map as $template_class => $_) {
- if (strpos($template_class, 'fn-') === 0) {
- continue;
- }
-
- try {
- $classlike_storage = $codebase->classlike_storage_provider->get($template_class);
-
- if ($classlike_storage->template_extended_params) {
- $defining_class = $atomic_type->defining_class;
-
- if (isset($classlike_storage->template_extended_params[$defining_class])) {
- $param_map = $classlike_storage->template_extended_params[$defining_class];
-
- if (isset($param_map[$key])
- && isset($inferred_lower_bounds[(string) $param_map[$key]][$template_class])
- ) {
- $template_name = (string) $param_map[$key];
-
- $template_type
- = clone TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $inferred_lower_bounds[$template_name][$template_class],
- $codebase
- );
- }
- }
- }
- } catch (InvalidArgumentException $e) {
- }
- }
- }
- }
-
- if ($template_type) {
- $keys_to_unset[] = $key;
-
- foreach ($template_type->getAtomicTypes() as $template_type_part) {
- if ($template_type_part instanceof TMixed) {
- $is_mixed = true;
- }
-
- $new_types[] = $template_type_part;
- }
- }
- } elseif ($atomic_type instanceof TTemplateParamClass) {
- $template_type = isset($inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class])
- ? clone TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class],
- $codebase
- )
- : null;
-
- $class_template_type = null;
-
- if ($template_type) {
- foreach ($template_type->getAtomicTypes() as $template_type_part) {
- if ($template_type_part instanceof TMixed
- || $template_type_part instanceof TObject
- ) {
- $class_template_type = new TClassString();
- } elseif ($template_type_part instanceof TNamedObject) {
- $class_template_type = new TClassString(
- $template_type_part->value,
- $template_type_part
- );
- } elseif ($template_type_part instanceof TTemplateParam) {
- $first_atomic_type = $template_type_part->as->getSingleAtomic();
-
- $class_template_type = new TTemplateParamClass(
- $template_type_part->param_name,
- $template_type_part->as->getId(),
- $first_atomic_type instanceof TNamedObject ? $first_atomic_type : null,
- $template_type_part->defining_class
- );
- }
- }
- }
-
- if ($class_template_type) {
- $keys_to_unset[] = $key;
- $new_types[] = $class_template_type;
- }
- } elseif ($atomic_type instanceof TTemplateIndexedAccess) {
- $keys_to_unset[] = $key;
-
- $template_type = null;
-
- if (isset($inferred_lower_bounds[$atomic_type->array_param_name][$atomic_type->defining_class])
- && !empty($inferred_lower_bounds[$atomic_type->offset_param_name])
- ) {
- $array_template_type
- = TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $inferred_lower_bounds[$atomic_type->array_param_name][$atomic_type->defining_class],
- $codebase
- );
-
- $offset_template_type
- = TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- array_values($inferred_lower_bounds[$atomic_type->offset_param_name])[0],
- $codebase
- );
-
- if ($array_template_type->isSingle()
- && $offset_template_type->isSingle()
- && !$array_template_type->isMixed()
- && !$offset_template_type->isMixed()
- ) {
- $array_template_type = $array_template_type->getSingleAtomic();
- $offset_template_type = $offset_template_type->getSingleAtomic();
-
- if ($array_template_type instanceof TKeyedArray
- && ($offset_template_type instanceof TLiteralString
- || $offset_template_type instanceof TLiteralInt)
- && isset($array_template_type->properties[$offset_template_type->value])
- ) {
- $template_type = clone $array_template_type->properties[$offset_template_type->value];
- }
- }
- }
-
- if ($template_type) {
- foreach ($template_type->getAtomicTypes() as $template_type_part) {
- if ($template_type_part instanceof TMixed) {
- $is_mixed = true;
- }
-
- $new_types[] = $template_type_part;
- }
- } else {
- $new_types[] = new TMixed();
- }
- } elseif ($atomic_type instanceof TConditional
- && $codebase
- ) {
- $template_type = isset($inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class])
- ? clone TemplateStandinTypeReplacer::getMostSpecificTypeFromBounds(
- $inferred_lower_bounds[$atomic_type->param_name][$atomic_type->defining_class],
- $codebase
- )
- : null;
-
- $if_template_type = null;
- $else_template_type = null;
-
- $atomic_type = clone $atomic_type;
-
- if ($template_type) {
- self::replace(
- $atomic_type->as_type,
- $template_result,
- $codebase
- );
-
- if ($atomic_type->as_type->isNullable() && $template_type->isVoid()) {
- $template_type = Type::getNull();
- }
-
- $matching_if_types = [];
- $matching_else_types = [];
-
- foreach ($template_type->getAtomicTypes() as $candidate_atomic_type) {
- if (UnionTypeComparator::isContainedBy(
- $codebase,
- new Union([$candidate_atomic_type]),
- $atomic_type->conditional_type,
- false,
- false,
- null,
- false,
- false
- )
- && (!$candidate_atomic_type instanceof TInt
- || $atomic_type->conditional_type->getId() !== 'float')
- ) {
- $matching_if_types[] = $candidate_atomic_type;
- } elseif (!UnionTypeComparator::isContainedBy(
- $codebase,
- $atomic_type->conditional_type,
- new Union([$candidate_atomic_type]),
- false,
- false,
- null,
- false,
- false
- )) {
- $matching_else_types[] = $candidate_atomic_type;
- }
- }
-
- $if_candidate_type = $matching_if_types ? new Union($matching_if_types) : null;
- $else_candidate_type = $matching_else_types ? new Union($matching_else_types) : null;
-
- if ($if_candidate_type
- && UnionTypeComparator::isContainedBy(
- $codebase,
- $if_candidate_type,
- $atomic_type->conditional_type,
- false,
- false,
- null,
- false,
- false
- )
- ) {
- $if_template_type = clone $atomic_type->if_type;
-
- $refined_template_result = clone $template_result;
-
- $refined_template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]
- = [
- new TemplateBound(
- $if_candidate_type
- )
- ];
-
- self::replace(
- $if_template_type,
- $refined_template_result,
- $codebase
- );
- }
-
- if ($else_candidate_type
- && UnionTypeComparator::isContainedBy(
- $codebase,
- $else_candidate_type,
- $atomic_type->as_type,
- false,
- false,
- null,
- false,
- false
- )
- ) {
- $else_template_type = clone $atomic_type->else_type;
-
- $refined_template_result = clone $template_result;
-
- $refined_template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]
- = [
- new TemplateBound(
- $else_candidate_type
- )
- ];
-
- self::replace(
- $else_template_type,
- $refined_template_result,
- $codebase
- );
- }
- }
-
- if (!$if_template_type && !$else_template_type) {
- self::replace(
- $atomic_type->if_type,
- $template_result,
- $codebase
- );
-
- self::replace(
- $atomic_type->else_type,
- $template_result,
- $codebase
- );
-
- $class_template_type = Type::combineUnionTypes(
- $atomic_type->if_type,
- $atomic_type->else_type,
- $codebase
- );
- } else {
- $class_template_type = Type::combineUnionTypes(
- $if_template_type,
- $else_template_type,
- $codebase
- );
- }
-
- $keys_to_unset[] = $key;
-
- foreach ($class_template_type->getAtomicTypes() as $class_template_atomic_type) {
- $new_types[] = $class_template_atomic_type;
- }
- }
- }
-
- $union->bustCache();
-
- if ($is_mixed) {
- if (!$new_types) {
- throw new UnexpectedValueException('This array should be full');
- }
-
- $union->replaceTypes(
- TypeCombiner::combine(
- $new_types,
- $codebase
- )->getAtomicTypes()
- );
-
- return;
- }
-
- foreach ($keys_to_unset as $key) {
- $union->removeType($key);
- }
-
- $atomic_types = array_values(array_merge($union->getAtomicTypes(), $new_types));
-
- $union->replaceTypes(
- TypeCombiner::combine(
- $atomic_types,
- $codebase
- )->getAtomicTypes()
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateResult.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateResult.php
deleted file mode 100644
index c213eb5..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateResult.php
+++ /dev/null
@@ -1,73 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Type\Union;
-
-use function array_map;
-
-/**
- * This class captures the result of running Psalm's argument analysis with
- * regard to generic parameters.
- *
- * It captures upper and lower bounds for parameters. Mostly we just care about
- * lower bounds — those are captured when calling a function that expects a
- * non-callable templated argument.
- *
- * Upper bounds are found in callable parameter types. Given a parameter type
- * `callable(T1): void` and an argument typed as `callable(int): void`, `int` will
- * be added as an _upper_ bound for the template param `T1`. This only applies to
- * parameters — given a parameter type `callable(): T2` and an argument typed as
- * `callable(): string`, `string` will be added as a _lower_ bound for the template
- * param `T2`.
- */
-class TemplateResult
-{
- /**
- * @var array<string, array<string, Union>>
- */
- public $template_types;
-
- /**
- * @var array<string, array<string, non-empty-list<TemplateBound>>>
- */
- public $lower_bounds;
-
- /**
- * @var array<string, array<string, TemplateBound>>
- */
- public $upper_bounds = [];
-
- /**
- * If set to true then we shouldn't update the template bounds
- *
- * @var bool
- */
- public $readonly = false;
-
- /**
- * @var list<Union>
- */
- public $upper_bounds_unintersectable_types = [];
-
- /**
- * @param array<string, array<string, Union>> $template_types
- * @param array<string, array<string, Union>> $lower_bounds
- */
- public function __construct(array $template_types, array $lower_bounds)
- {
- $this->template_types = $template_types;
-
- $this->lower_bounds = array_map(
- function ($type_map) {
- return array_map(
- function ($type) {
- return [new TemplateBound($type)];
- },
- $type_map
- );
- },
- $lower_bounds
- );
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php
deleted file mode 100644
index 7d6dbf6..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php
+++ /dev/null
@@ -1,1249 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use InvalidArgumentException;
-use Psalm\Codebase;
-use Psalm\Internal\Analyzer\StatementsAnalyzer;
-use Psalm\Internal\Codebase\Methods;
-use Psalm\Internal\Type\Comparator\CallableTypeComparator;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeCombiner;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TDependentGetClass;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateIndexedAccess;
-use Psalm\Type\Atomic\TTemplateKeyOf;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Union;
-use Throwable;
-
-use function array_fill;
-use function array_keys;
-use function array_merge;
-use function array_search;
-use function array_slice;
-use function array_values;
-use function count;
-use function in_array;
-use function reset;
-use function strpos;
-use function strtolower;
-use function substr;
-use function usort;
-
-class TemplateStandinTypeReplacer
-{
- /**
- * This replaces template types in unions with standins (normally the template as type)
- *
- * $input_type here is normally the argument passed to a templated function or method.
- *
- * This method fills in the values in $template_result based on how the various atomic types
- * of $union_type match up to the types inside $input_type
- */
- public static function replace(
- Union $union_type,
- TemplateResult $template_result,
- ?Codebase $codebase,
- ?StatementsAnalyzer $statements_analyzer,
- ?Union $input_type,
- ?int $input_arg_offset = null,
- ?string $calling_class = null,
- ?string $calling_function = null,
- bool $replace = true,
- bool $add_lower_bound = false,
- ?string $bound_equality_classlike = null,
- int $depth = 1
- ): Union {
- $atomic_types = [];
-
- $original_atomic_types = $union_type->getAtomicTypes();
-
- // here we want to subtract atomic types from the input type
- // when they're also in the union type, so those shared atomic
- // types will never be inferred as part of the generic type
- if ($input_type && !$input_type->isSingle()) {
- $new_input_type = clone $input_type;
-
- foreach ($original_atomic_types as $key => $_) {
- if ($new_input_type->hasType($key)) {
- $new_input_type->removeType($key);
- }
- }
-
- if (!$new_input_type->isUnionEmpty()) {
- $input_type = $new_input_type;
- }
- }
-
- $had_template = false;
-
- foreach ($original_atomic_types as $key => $atomic_type) {
- $atomic_types = array_merge(
- $atomic_types,
- self::handleAtomicStandin(
- $atomic_type,
- $key,
- $template_result,
- $codebase,
- $statements_analyzer,
- $input_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $replace,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth,
- count($original_atomic_types) === 1,
- $had_template
- )
- );
- }
-
- if ($replace) {
- if (array_values($original_atomic_types) === $atomic_types) {
- return $union_type;
- }
-
- if (!$atomic_types) {
- return $union_type;
- }
-
- if (count($atomic_types) > 1) {
- $new_union_type = TypeCombiner::combine(
- $atomic_types,
- $codebase
- );
- } else {
- $new_union_type = new Union($atomic_types);
- }
-
- $new_union_type->ignore_nullable_issues = $union_type->ignore_nullable_issues;
- $new_union_type->ignore_falsable_issues = $union_type->ignore_falsable_issues;
- $new_union_type->possibly_undefined = $union_type->possibly_undefined;
-
- if ($had_template) {
- $new_union_type->had_template = true;
- }
-
- return $new_union_type;
- }
-
- return $union_type;
- }
-
- /**
- * @return list<Atomic>
- */
- private static function handleAtomicStandin(
- Atomic $atomic_type,
- string $key,
- TemplateResult $template_result,
- ?Codebase $codebase,
- ?StatementsAnalyzer $statements_analyzer,
- ?Union $input_type,
- ?int $input_arg_offset,
- ?string $calling_class,
- ?string $calling_function,
- bool $replace,
- bool $add_lower_bound,
- ?string $bound_equality_classlike,
- int $depth,
- bool $was_single,
- bool &$had_template
- ): array {
- if ($bracket_pos = strpos($key, '<')) {
- $key = substr($key, 0, $bracket_pos);
- }
-
- if ($atomic_type instanceof TTemplateParam
- && isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class])
- ) {
- return self::handleTemplateParamStandin(
- $atomic_type,
- $key,
- $input_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $template_result,
- $codebase,
- $statements_analyzer,
- $replace,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth,
- $had_template
- );
- }
-
- if ($atomic_type instanceof TTemplateParamClass
- && isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class])
- ) {
- if ($replace) {
- return self::handleTemplateParamClassStandin(
- $atomic_type,
- $input_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $template_result,
- $codebase,
- $statements_analyzer,
- true,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth,
- $was_single,
- $had_template
- );
- }
- }
-
- if ($atomic_type instanceof TTemplateIndexedAccess) {
- if ($replace) {
- $atomic_types = [];
-
- $include_first = true;
-
- if (isset($template_result->template_types[$atomic_type->array_param_name][$atomic_type->defining_class])
- && !empty($template_result->lower_bounds[$atomic_type->offset_param_name])
- ) {
- $array_template_type
- = $template_result->template_types[$atomic_type->array_param_name][$atomic_type->defining_class];
- $offset_template_type
- = self::getMostSpecificTypeFromBounds(
- array_values($template_result->lower_bounds[$atomic_type->offset_param_name])[0],
- $codebase
- );
-
- if ($array_template_type->isSingle()
- && $offset_template_type->isSingle()
- && !$array_template_type->isMixed()
- && !$offset_template_type->isMixed()
- ) {
- $array_template_type = $array_template_type->getSingleAtomic();
- $offset_template_type = $offset_template_type->getSingleAtomic();
-
- if ($array_template_type instanceof TKeyedArray
- && ($offset_template_type instanceof TLiteralString
- || $offset_template_type instanceof TLiteralInt)
- && isset($array_template_type->properties[$offset_template_type->value])
- ) {
- $include_first = false;
-
- $replacement_type
- = clone $array_template_type->properties[$offset_template_type->value];
-
- foreach ($replacement_type->getAtomicTypes() as $replacement_atomic_type) {
- $atomic_types[] = $replacement_atomic_type;
- }
- }
- }
- }
-
- if ($include_first) {
- $atomic_types[] = $atomic_type;
- }
-
- return $atomic_types;
- }
-
- return [$atomic_type];
- }
-
- if ($atomic_type instanceof TTemplateKeyOf) {
- if ($replace) {
- $atomic_types = [];
-
- $include_first = true;
-
- if (isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class])) {
- $template_type
- = $template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class];
-
- if ($template_type->isSingle()) {
- $template_type = $template_type->getSingleAtomic();
-
- if ($template_type instanceof TKeyedArray
- || $template_type instanceof TArray
- || $template_type instanceof TList
- ) {
- if ($template_type instanceof TKeyedArray) {
- $key_type = $template_type->getGenericKeyType();
- } elseif ($template_type instanceof TList) {
- $key_type = Type::getInt();
- } else {
- $key_type = clone $template_type->type_params[0];
- }
-
- $include_first = false;
-
- foreach ($key_type->getAtomicTypes() as $key_atomic_type) {
- $atomic_types[] = $key_atomic_type;
- }
- }
- }
- }
-
- if ($include_first) {
- $atomic_types[] = $atomic_type;
- }
-
- return $atomic_types;
- }
-
- return [$atomic_type];
- }
-
- $matching_atomic_types = [];
-
- if ($input_type && $codebase && !$input_type->hasMixed()) {
- $matching_atomic_types = self::findMatchingAtomicTypesForTemplate(
- $atomic_type,
- $key,
- $codebase,
- $statements_analyzer,
- $input_type
- );
- }
-
- if (!$matching_atomic_types) {
- $atomic_type = $atomic_type->replaceTemplateTypesWithStandins(
- $template_result,
- $codebase,
- $statements_analyzer,
- null,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $replace,
- $add_lower_bound,
- $depth + 1
- );
-
- return [$atomic_type];
- }
-
- $atomic_types = [];
-
- foreach ($matching_atomic_types as $matching_atomic_type) {
- $atomic_types[] = $atomic_type->replaceTemplateTypesWithStandins(
- $template_result,
- $codebase,
- $statements_analyzer,
- $matching_atomic_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $replace,
- $add_lower_bound,
- $depth + 1
- );
- }
-
- return $atomic_types;
- }
-
- /**
- * This method attempts to find bits of the input type (normally the argument type of a method call)
- * that match the base type (normally the param type of the method). These matches are used to infer
- * more template types
- *
- * Example: when passing `array<string|int>` to a function that expects `array<T>`, a rule in this method
- * identifies the matching atomic types for `T` as `string|int`
- *
- * @return list<Atomic>
- */
- private static function findMatchingAtomicTypesForTemplate(
- Atomic $base_type,
- string $key,
- Codebase $codebase,
- ?StatementsAnalyzer $statements_analyzer,
- Union $input_type
- ): array {
- $matching_atomic_types = [];
-
- foreach ($input_type->getAtomicTypes() as $input_key => $atomic_input_type) {
- if ($bracket_pos = strpos($input_key, '<')) {
- $input_key = substr($input_key, 0, $bracket_pos);
- }
-
- if ($input_key === $key) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if ($atomic_input_type instanceof TClosure && $base_type instanceof TClosure) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if ($atomic_input_type instanceof TCallable
- && $base_type instanceof TCallable
- ) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if ($atomic_input_type instanceof TClosure && $base_type instanceof TCallable) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if (($atomic_input_type instanceof TArray
- || $atomic_input_type instanceof TKeyedArray
- || $atomic_input_type instanceof TList)
- && $key === 'iterable'
- ) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if (strpos($input_key, $key . '&') === 0) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if ($atomic_input_type instanceof TLiteralClassString
- && $base_type instanceof TClassString
- && $base_type->as_type
- ) {
- try {
- $classlike_storage =
- $codebase->classlike_storage_provider->get($atomic_input_type->value);
-
- if (!empty($classlike_storage->template_extended_params[$base_type->as_type->value])) {
- $atomic_input_type = new TClassString(
- $base_type->as_type->value,
- new TGenericObject(
- $base_type->as_type->value,
- array_values($classlike_storage->template_extended_params[$base_type->as_type->value])
- )
- );
-
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
- } catch (InvalidArgumentException $e) {
- // do nothing
- }
- }
-
- if ($base_type instanceof TCallable) {
- $matching_atomic_type = CallableTypeComparator::getCallableFromAtomic(
- $codebase,
- $atomic_input_type,
- null,
- $statements_analyzer
- );
-
- if ($matching_atomic_type) {
- $matching_atomic_types[$matching_atomic_type->getId()] = $matching_atomic_type;
- continue;
- }
- }
-
- if ($atomic_input_type instanceof TNamedObject
- && ($base_type instanceof TNamedObject
- || $base_type instanceof TIterable)
- ) {
- if ($base_type instanceof TIterable) {
- if ($atomic_input_type->value === 'Traversable') {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- $base_type = new TGenericObject(
- 'Traversable',
- $base_type->type_params
- );
- }
-
- try {
- $classlike_storage =
- $codebase->classlike_storage_provider->get($atomic_input_type->value);
-
- if ($atomic_input_type instanceof TGenericObject
- && isset($classlike_storage->template_extended_params[$base_type->value])
- ) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if (!empty($classlike_storage->template_extended_params[$base_type->value])) {
- $atomic_input_type = new TGenericObject(
- $atomic_input_type->value,
- array_values($classlike_storage->template_extended_params[$base_type->value])
- );
-
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
-
- if (in_array('Traversable', $classlike_storage->class_implements)
- && $base_type->value === 'Iterator'
- ) {
- $matching_atomic_types[$atomic_input_type->getId()] = $atomic_input_type;
- continue;
- }
- } catch (InvalidArgumentException $e) {
- // do nothing
- }
- }
-
- if ($atomic_input_type instanceof TTemplateParam) {
- $matching_atomic_types = array_merge(
- $matching_atomic_types,
- self::findMatchingAtomicTypesForTemplate(
- $base_type,
- $key,
- $codebase,
- $statements_analyzer,
- $atomic_input_type->as
- )
- );
- continue;
- }
- }
-
- return array_values($matching_atomic_types);
- }
-
- /**
- * @return list<Atomic>
- */
- private static function handleTemplateParamStandin(
- TTemplateParam $atomic_type,
- string $key,
- ?Union $input_type,
- ?int $input_arg_offset,
- ?string $calling_class,
- ?string $calling_function,
- TemplateResult $template_result,
- ?Codebase $codebase,
- ?StatementsAnalyzer $statements_analyzer,
- bool $replace,
- bool $add_lower_bound,
- ?string $bound_equality_classlike,
- int $depth,
- bool &$had_template
- ): array {
- if ($atomic_type->defining_class === $calling_class) {
- return [$atomic_type];
- }
-
- $template_type = $template_result->template_types
- [$atomic_type->param_name]
- [$atomic_type->defining_class];
-
- if ($template_type->getId() === $key) {
- return array_values($template_type->getAtomicTypes());
- }
-
- $replacement_type = $template_type;
-
- $param_name_key = $atomic_type->param_name;
-
- if (strpos($key, '&')) {
- $param_name_key = $key;
- }
-
- $extra_types = [];
-
- if ($atomic_type->extra_types) {
- foreach ($atomic_type->extra_types as $extra_type) {
- $extra_type = self::replace(
- new Union([$extra_type]),
- $template_result,
- $codebase,
- $statements_analyzer,
- $input_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $replace,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth + 1
- );
-
- if ($extra_type->isSingle()) {
- $extra_type = $extra_type->getSingleAtomic();
-
- if ($extra_type instanceof TNamedObject
- || $extra_type instanceof TTemplateParam
- || $extra_type instanceof TIterable
- || $extra_type instanceof TObjectWithProperties
- ) {
- $extra_types[$extra_type->getKey()] = $extra_type;
- }
- }
- }
- }
-
- if ($replace) {
- $atomic_types = [];
-
- if ($replacement_type->hasMixed()
- && !$atomic_type->as->hasMixed()
- ) {
- foreach ($atomic_type->as->getAtomicTypes() as $as_atomic_type) {
- $atomic_types[] = clone $as_atomic_type;
- }
- } else {
- if ($codebase) {
- $replacement_type = TypeExpander::expandUnion(
- $codebase,
- $replacement_type,
- $calling_class,
- $calling_class,
- null
- );
- }
-
- if ($depth < 10) {
- $replacement_type = self::replace(
- $replacement_type,
- $template_result,
- $codebase,
- $statements_analyzer,
- $input_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- true,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth + 1
- );
- }
-
- foreach ($replacement_type->getAtomicTypes() as $replacement_atomic_type) {
- $replacements_found = false;
-
- // @codingStandardsIgnoreStart
- if ($replacement_atomic_type instanceof TTemplateKeyOf
- && isset($template_result->template_types[$replacement_atomic_type->param_name][$replacement_atomic_type->defining_class])
- && count($template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class])
- === 1
- ) {
- $keyed_template = $template_result->template_types[$replacement_atomic_type->param_name][$replacement_atomic_type->defining_class];
-
- if ($keyed_template->isSingle()) {
- $keyed_template = $keyed_template->getSingleAtomic();
- }
-
- if ($keyed_template instanceof TKeyedArray
- || $keyed_template instanceof TArray
- || $keyed_template instanceof TList
- ) {
- if ($keyed_template instanceof TKeyedArray) {
- $key_type = $keyed_template->getGenericKeyType();
- } elseif ($keyed_template instanceof TList) {
- $key_type = Type::getInt();
- } else {
- $key_type = $keyed_template->type_params[0];
- }
-
- $replacements_found = true;
-
- foreach ($key_type->getAtomicTypes() as $key_type_atomic) {
- $atomic_types[] = clone $key_type_atomic;
- }
-
- $existing_lower_bound = reset($template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]);
-
- $existing_lower_bound->type = clone $key_type;
- }
- }
-
- if ($replacement_atomic_type instanceof TTemplateParam
- && $replacement_atomic_type->defining_class !== $calling_class
- && $replacement_atomic_type->defining_class !== 'fn-' . $calling_function
- ) {
- foreach ($replacement_atomic_type->as->getAtomicTypes() as $nested_type_atomic) {
- $replacements_found = true;
- $atomic_types[] = clone $nested_type_atomic;
- }
- }
- // @codingStandardsIgnoreEnd
-
- if (!$replacements_found) {
- $atomic_types[] = clone $replacement_atomic_type;
- }
-
- $had_template = true;
- }
- }
-
- $matching_input_keys = [];
-
- if ($codebase) {
- $atomic_type->as = TypeExpander::expandUnion(
- $codebase,
- $atomic_type->as,
- $calling_class,
- $calling_class,
- null
- );
- }
-
- $atomic_type->as = self::replace(
- $atomic_type->as,
- $template_result,
- $codebase,
- $statements_analyzer,
- $input_type,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- true,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth + 1
- );
-
- if ($input_type
- && !$template_result->readonly
- && (
- $atomic_type->as->isMixed()
- || !$codebase
- || UnionTypeComparator::canBeContainedBy(
- $codebase,
- $input_type,
- $atomic_type->as,
- false,
- false,
- $matching_input_keys
- )
- )
- ) {
- $generic_param = clone $input_type;
-
- if ($matching_input_keys) {
- $generic_param_keys = array_keys($generic_param->getAtomicTypes());
-
- foreach ($generic_param_keys as $atomic_key) {
- if (!isset($matching_input_keys[$atomic_key])) {
- $generic_param->removeType($atomic_key);
- }
- }
- }
-
- if ($add_lower_bound) {
- return array_values($generic_param->getAtomicTypes());
- }
-
- $generic_param->setFromDocblock();
-
- if (isset(
- $template_result->lower_bounds[$param_name_key][$atomic_type->defining_class]
- )) {
- $existing_lower_bounds = $template_result->lower_bounds
- [$param_name_key]
- [$atomic_type->defining_class];
-
- $has_matching_lower_bound = false;
-
- foreach ($existing_lower_bounds as $existing_lower_bound) {
- $existing_depth = $existing_lower_bound->appearance_depth;
- $existing_arg_offset = $existing_lower_bound->arg_offset ?? $input_arg_offset;
-
- if ($existing_depth === $depth
- && $input_arg_offset === $existing_arg_offset
- && $existing_lower_bound->type->getId() === $generic_param->getId()
- && $existing_lower_bound->equality_bound_classlike === $bound_equality_classlike
- ) {
- $has_matching_lower_bound = true;
- break;
- }
- }
-
- if (!$has_matching_lower_bound) {
- $template_result->lower_bounds
- [$param_name_key]
- [$atomic_type->defining_class]
- [] = new TemplateBound(
- $generic_param,
- $depth,
- $input_arg_offset,
- $bound_equality_classlike
- );
- }
- } else {
- $template_result->lower_bounds[$param_name_key][$atomic_type->defining_class] = [
- new TemplateBound(
- $generic_param,
- $depth,
- $input_arg_offset,
- $bound_equality_classlike
- )
- ];
- }
- }
-
- foreach ($atomic_types as &$atomic_type) {
- if ($atomic_type instanceof TNamedObject
- || $atomic_type instanceof TTemplateParam
- || $atomic_type instanceof TIterable
- || $atomic_type instanceof TObjectWithProperties
- ) {
- $atomic_type->extra_types = $extra_types;
- } elseif ($atomic_type instanceof TObject && $extra_types) {
- $atomic_type = reset($extra_types);
- $atomic_type->extra_types = array_slice($extra_types, 1);
- }
- }
-
- return $atomic_types;
- }
-
- if ($add_lower_bound && $input_type && !$template_result->readonly) {
- $matching_input_keys = [];
-
- if ($codebase
- && UnionTypeComparator::canBeContainedBy(
- $codebase,
- $input_type,
- $replacement_type,
- false,
- false,
- $matching_input_keys
- )
- ) {
- $generic_param = clone $input_type;
-
- if ($matching_input_keys) {
- $generic_param_keys = array_keys($generic_param->getAtomicTypes());
-
- foreach ($generic_param_keys as $atomic_key) {
- if (!isset($matching_input_keys[$atomic_key])) {
- $generic_param->removeType($atomic_key);
- }
- }
- }
-
- if (isset($template_result->upper_bounds[$param_name_key][$atomic_type->defining_class])) {
- if (!UnionTypeComparator::isContainedBy(
- $codebase,
- $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class]->type,
- $generic_param
- ) || !UnionTypeComparator::isContainedBy(
- $codebase,
- $generic_param,
- $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class]->type
- )) {
- $intersection_type = Type::intersectUnionTypes(
- $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class]->type,
- $generic_param,
- $codebase
- );
- } else {
- $intersection_type = $generic_param;
- }
-
- if ($intersection_type) {
- $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class]->type
- = $intersection_type;
- } else {
- $template_result->upper_bounds_unintersectable_types[]
- = $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class]->type;
- $template_result->upper_bounds_unintersectable_types[] = $generic_param;
-
- $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class]->type
- = Type::getMixed();
- }
- } else {
- $template_result->upper_bounds[$param_name_key][$atomic_type->defining_class] = new TemplateBound(
- $generic_param
- );
- }
- }
- }
-
- return [$atomic_type];
- }
-
- /**
- * @return non-empty-list<TClassString>
- */
- public static function handleTemplateParamClassStandin(
- TTemplateParamClass $atomic_type,
- ?Union $input_type,
- ?int $input_arg_offset,
- ?string $calling_class,
- ?string $calling_function,
- TemplateResult $template_result,
- ?Codebase $codebase,
- ?StatementsAnalyzer $statements_analyzer,
- bool $replace,
- bool $add_lower_bound,
- ?string $bound_equality_classlike,
- int $depth,
- bool $was_single,
- bool &$had_template
- ): array {
- if ($atomic_type->defining_class === $calling_class) {
- return [$atomic_type];
- }
-
- $atomic_types = [];
-
- if ($input_type && !$template_result->readonly) {
- $valid_input_atomic_types = [];
-
- foreach ($input_type->getAtomicTypes() as $input_atomic_type) {
- if ($input_atomic_type instanceof TLiteralClassString) {
- $valid_input_atomic_types[] = new TNamedObject(
- $input_atomic_type->value
- );
- } elseif ($input_atomic_type instanceof TTemplateParamClass) {
- $valid_input_atomic_types[] = new TTemplateParam(
- $input_atomic_type->param_name,
- $input_atomic_type->as_type
- ? new Union([$input_atomic_type->as_type])
- : ($input_atomic_type->as === 'object'
- ? Type::getObject()
- : Type::getMixed()),
- $input_atomic_type->defining_class
- );
- } elseif ($input_atomic_type instanceof TClassString) {
- if ($input_atomic_type->as_type) {
- $valid_input_atomic_types[] = clone $input_atomic_type->as_type;
- } elseif ($input_atomic_type->as !== 'object') {
- $valid_input_atomic_types[] = new TNamedObject(
- $input_atomic_type->as
- );
- } else {
- $valid_input_atomic_types[] = new TObject();
- }
- } elseif ($input_atomic_type instanceof TDependentGetClass) {
- $valid_input_atomic_types[] = new TObject();
- }
- }
-
- $generic_param = null;
-
- if ($valid_input_atomic_types) {
- $generic_param = new Union($valid_input_atomic_types);
- $generic_param->setFromDocblock();
- } elseif ($was_single) {
- $generic_param = Type::getMixed();
- }
-
- if ($atomic_type->as_type) {
- // sometimes templated class-strings can contain nested templates
- // in the as type that need to be resolved as well.
- $as_type_union = self::replace(
- new Union([$atomic_type->as_type]),
- $template_result,
- $codebase,
- $statements_analyzer,
- $generic_param,
- $input_arg_offset,
- $calling_class,
- $calling_function,
- $replace,
- $add_lower_bound,
- $bound_equality_classlike,
- $depth + 1
- );
-
- $first = $as_type_union->getSingleAtomic();
-
- if (count($as_type_union->getAtomicTypes()) === 1 && $first instanceof TNamedObject) {
- $atomic_type->as_type = $first;
- } else {
- $atomic_type->as_type = null;
- }
- }
-
- if ($generic_param) {
- if (isset($template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class])) {
- $template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class] = [
- new TemplateBound(
- Type::combineUnionTypes(
- $generic_param,
- self::getMostSpecificTypeFromBounds(
- $template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class],
- $codebase
- )
- ),
- $depth
- )
- ];
- } else {
- $template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class] = [
- new TemplateBound(
- $generic_param,
- $depth,
- $input_arg_offset
- )
- ];
- }
- }
- } else {
- $template_type = $template_result->template_types
- [$atomic_type->param_name]
- [$atomic_type->defining_class];
-
- foreach ($template_type->getAtomicTypes() as $template_atomic_type) {
- if ($template_atomic_type instanceof TNamedObject) {
- $atomic_types[] = new TClassString(
- $template_atomic_type->value,
- $template_atomic_type
- );
- } elseif ($template_atomic_type instanceof TObject) {
- $atomic_types[] = new TClassString();
- }
- }
- }
-
- $class_string = new TClassString($atomic_type->as, $atomic_type->as_type);
-
- if (!$atomic_types) {
- $atomic_types[] = $class_string;
- }
-
- return $atomic_types;
- }
-
- /**
- * @param array<string, array<string, non-empty-list<TemplateBound>>> $template_types
- */
- public static function getRootTemplateType(
- array $template_types,
- string $param_name,
- string $defining_class,
- array $visited_classes,
- ?Codebase $codebase
- ): ?Union {
- if (isset($visited_classes[$defining_class])) {
- return null;
- }
-
- if (isset($template_types[$param_name][$defining_class])) {
- $mapped_type = self::getMostSpecificTypeFromBounds(
- $template_types[$param_name][$defining_class],
- $codebase
- );
-
- $mapped_type_atomic_types = array_values($mapped_type->getAtomicTypes());
-
- if (count($mapped_type_atomic_types) > 1
- || !$mapped_type_atomic_types[0] instanceof TTemplateParam
- ) {
- return $mapped_type;
- }
-
- $first_template = $mapped_type_atomic_types[0];
-
- return self::getRootTemplateType(
- $template_types,
- $first_template->param_name,
- $first_template->defining_class,
- $visited_classes + [$defining_class => true],
- $codebase
- ) ?? $mapped_type;
- }
-
- return null;
- }
-
- /**
- * This takes a list of lower bounds and returns the most general type.
- *
- * If given a single bound that's just the type of that bound.
- *
- * If instead given a collection of lower bounds it normally returns a union of those
- * bound types.
- *
- * @param non-empty-list<TemplateBound> $lower_bounds
- */
- public static function getMostSpecificTypeFromBounds(array $lower_bounds, ?Codebase $codebase): Union
- {
- if (count($lower_bounds) === 1) {
- return reset($lower_bounds)->type;
- }
-
- usort(
- $lower_bounds,
- function (TemplateBound $bound_a, TemplateBound $bound_b) {
- return $bound_b->appearance_depth <=> $bound_a->appearance_depth;
- }
- );
-
- $current_depth = null;
- $current_type = null;
- $had_invariant = false;
- $last_arg_offset = -1;
-
- foreach ($lower_bounds as $template_bound) {
- if ($current_depth === null) {
- $current_depth = $template_bound->appearance_depth;
- } elseif ($current_depth !== $template_bound->appearance_depth && $current_type) {
- if (!$current_type->isEmpty()
- && ($had_invariant || $last_arg_offset === $template_bound->arg_offset)
- ) {
- // escape switches when matching on invariant generic params
- // and when matching
- break;
- }
-
- $current_depth = $template_bound->appearance_depth;
- }
-
- $had_invariant = $had_invariant ?: $template_bound->equality_bound_classlike !== null;
-
- $current_type = Type::combineUnionTypes(
- $current_type,
- $template_bound->type,
- $codebase
- );
-
- $last_arg_offset = $template_bound->arg_offset;
- }
-
- return $current_type ?? Type::getMixed();
- }
-
- /**
- * @param TGenericObject|TNamedObject|TIterable $input_type_part
- * @param TGenericObject|TIterable $container_type_part
- * @return list<Union>
- */
- public static function getMappedGenericTypeParams(
- Codebase $codebase,
- Atomic $input_type_part,
- Atomic $container_type_part,
- ?array &$container_type_params_covariant = null
- ): array {
- if ($input_type_part instanceof TGenericObject || $input_type_part instanceof TIterable) {
- $input_type_params = $input_type_part->type_params;
- } else {
- $class_storage = $codebase->classlike_storage_provider->get($input_type_part->value);
-
- $container_class = $container_type_part->value;
-
- if (strtolower($input_type_part->value) === strtolower($container_type_part->value)) {
- $input_type_params = $class_storage->getClassTemplateTypes();
- } elseif (!empty($class_storage->template_extended_params[$container_class])) {
- $input_type_params = array_values($class_storage->template_extended_params[$container_class]);
- } else {
- $input_type_params = array_fill(0, count($class_storage->template_types ?? []), Type::getMixed());
- }
- }
-
- try {
- $input_class_storage = $codebase->classlike_storage_provider->get($input_type_part->value);
- $container_class_storage = $codebase->classlike_storage_provider->get($container_type_part->value);
- $container_type_params_covariant = $container_class_storage->template_covariants;
- } catch (Throwable $e) {
- $input_class_storage = null;
- }
-
- if ($input_type_part->value !== $container_type_part->value
- && $input_class_storage
- ) {
- $input_template_types = $input_class_storage->template_types;
- $i = 0;
-
- $replacement_templates = [];
-
- if ($input_template_types
- && (!$input_type_part instanceof TGenericObject || !$input_type_part->remapped_params)
- && (!$container_type_part instanceof TGenericObject || !$container_type_part->remapped_params)
- ) {
- foreach ($input_template_types as $template_name => $_) {
- if (!isset($input_type_params[$i])) {
- break;
- }
-
- $replacement_templates[$template_name][$input_type_part->value] = $input_type_params[$i];
-
- $i++;
- }
- }
-
- $template_extends = $input_class_storage->template_extended_params;
-
- if (isset($template_extends[$container_type_part->value])) {
- $params = $template_extends[$container_type_part->value];
-
- $new_input_params = [];
-
- foreach ($params as $extended_input_param_type) {
- $new_input_param = null;
-
- foreach ($extended_input_param_type->getAtomicTypes() as $et) {
- if ($et instanceof TTemplateParam) {
- $ets = Methods::getExtendedTemplatedTypes(
- $et,
- $template_extends
- );
- } else {
- $ets = [];
- }
-
- if ($ets
- && $ets[0] instanceof TTemplateParam
- && isset(
- $input_class_storage->template_types
- [$ets[0]->param_name]
- [$ets[0]->defining_class]
- )
- ) {
- $old_params_offset = (int) array_search(
- $ets[0]->param_name,
- array_keys($input_class_storage->template_types)
- );
-
- $candidate_param_type = $input_type_params[$old_params_offset] ?? Type::getMixed();
- } else {
- $candidate_param_type = new Union([clone $et]);
- }
-
- $candidate_param_type->from_template_default = true;
-
- $new_input_param = Type::combineUnionTypes(
- $new_input_param,
- $candidate_param_type
- );
- }
-
- $new_input_param = clone $new_input_param;
-
- TemplateInferredTypeReplacer::replace(
- $new_input_param,
- new TemplateResult([], $replacement_templates),
- $codebase
- );
-
- $new_input_params[] = $new_input_param;
- }
-
- $input_type_params = $new_input_params;
- }
- }
-
- return $input_type_params;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias.php
deleted file mode 100644
index a8c05fb..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias.php
+++ /dev/null
@@ -1,7 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-interface TypeAlias
-{
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php
deleted file mode 100644
index 5f5146e..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/ClassTypeAlias.php
+++ /dev/null
@@ -1,22 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\TypeAlias;
-
-use Psalm\Internal\Type\TypeAlias;
-use Psalm\Type\Atomic;
-
-class ClassTypeAlias implements TypeAlias
-{
- /**
- * @var list<Atomic>
- */
- public $replacement_atomic_types;
-
- /**
- * @param list<Atomic> $replacement_atomic_types
- */
- public function __construct(array $replacement_atomic_types)
- {
- $this->replacement_atomic_types = $replacement_atomic_types;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php
deleted file mode 100644
index 9ed473c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/InlineTypeAlias.php
+++ /dev/null
@@ -1,24 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\TypeAlias;
-
-use Psalm\Internal\Type\TypeAlias;
-
-/**
- * @psalm-immutable
- */
-class InlineTypeAlias implements TypeAlias
-{
- /**
- * @var list<array{0: string, 1: int}>
- */
- public $replacement_tokens;
-
- /**
- * @param list<array{0: string, 1: int}> $replacement_tokens
- */
- public function __construct(array $replacement_tokens)
- {
- $this->replacement_tokens = $replacement_tokens;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php
deleted file mode 100644
index a0c309d..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeAlias/LinkableTypeAlias.php
+++ /dev/null
@@ -1,35 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type\TypeAlias;
-
-use Psalm\Internal\Type\TypeAlias;
-
-/**
- * @psalm-immutable
- */
-class LinkableTypeAlias implements TypeAlias
-{
- public $declaring_fq_classlike_name;
-
- public $alias_name;
-
- public $line_number;
-
- public $start_offset;
-
- public $end_offset;
-
- public function __construct(
- string $declaring_fq_classlike_name,
- string $alias_name,
- int $line_number,
- int $start_offset,
- int $end_offset
- ) {
- $this->declaring_fq_classlike_name = $declaring_fq_classlike_name;
- $this->alias_name = $alias_name;
- $this->line_number = $line_number;
- $this->start_offset = $start_offset;
- $this->end_offset = $end_offset;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombination.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombination.php
deleted file mode 100644
index d45af1c..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombination.php
+++ /dev/null
@@ -1,99 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Union;
-
-/**
- * @internal
- */
-class TypeCombination
-{
- /** @var array<string, Atomic> */
- public $value_types = [];
-
- /** @var array<string, TNamedObject>|null */
- public $named_object_types = [];
-
- /** @var list<Union> */
- public $array_type_params = [];
-
- /** @var array<string, non-empty-list<Union>> */
- public $builtin_type_params = [];
-
- /** @var array<string, non-empty-list<Union>> */
- public $object_type_params = [];
-
- /** @var array<string, bool> */
- public $object_static = [];
-
- /** @var array<int, bool>|null */
- public $array_counts = [];
-
- /** @var bool */
- public $array_sometimes_filled = false;
-
- /** @var bool */
- public $array_always_filled = true;
-
- /** @var array<string|int, Union> */
- public $objectlike_entries = [];
-
- /** @var bool */
- public $objectlike_sealed = true;
-
- /** @var ?Union */
- public $objectlike_key_type;
-
- /** @var ?Union */
- public $objectlike_value_type;
-
- /** @var bool */
- public $empty_mixed = false;
-
- /** @var bool */
- public $non_empty_mixed = false;
-
- /** @var ?bool */
- public $mixed_from_loop_isset;
-
- /** @var array<string, TLiteralString>|null */
- public $strings = [];
-
- /** @var array<string, TLiteralInt>|null */
- public $ints = [];
-
- /** @var array<string, TLiteralFloat>|null */
- public $floats = [];
-
- /** @var array<string, TNamedObject|TObject>|null */
- public $class_string_types = [];
-
- /**
- * @var array<string, TNamedObject|TTemplateParam|TIterable|TObject>|null
- */
- public $extra_types;
-
- /** @var ?bool */
- public $all_arrays_lists;
-
- /** @var ?bool */
- public $all_arrays_callable;
-
- /** @var ?bool */
- public $all_arrays_class_string_maps;
-
- /** @var array<string, bool> */
- public $class_string_map_names = [];
-
- /** @var array<string, ?TNamedObject> */
- public $class_string_map_as_types = [];
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php
deleted file mode 100644
index ec939c1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php
+++ /dev/null
@@ -1,1528 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use InvalidArgumentException;
-use Psalm\Codebase;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\Scalar;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TBool;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableArray;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TCallableObject;
-use Psalm\Type\Atomic\TCallableString;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TEmptyMixed;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TFloat;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TLowercaseString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNonEmptyLowercaseString;
-use Psalm\Type\Atomic\TNonEmptyMixed;
-use Psalm\Type\Atomic\TNonEmptyNonspecificLiteralString;
-use Psalm\Type\Atomic\TNonEmptyString;
-use Psalm\Type\Atomic\TNonFalsyString;
-use Psalm\Type\Atomic\TNonspecificLiteralInt;
-use Psalm\Type\Atomic\TNonspecificLiteralString;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TNumericString;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TScalar;
-use Psalm\Type\Atomic\TString;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\Union;
-use UnexpectedValueException;
-
-use function array_filter;
-use function array_intersect_key;
-use function array_key_exists;
-use function array_keys;
-use function array_merge;
-use function array_values;
-use function count;
-use function get_class;
-use function is_int;
-use function is_numeric;
-use function strpos;
-use function strtolower;
-use function substr;
-
-class TypeCombiner
-{
- /**
- * Combines types together
- * - so `int + string = int|string`
- * - so `array<int> + array<string> = array<int|string>`
- * - and `array<int> + string = array<int>|string`
- * - and `array<empty> + array<empty> = array<empty>`
- * - and `array<string> + array<empty> = array<string>`
- * - and `array + array<string> = array<mixed>`
- *
- * @param non-empty-list<Atomic> $types
- * @param int $literal_limit any greater number of literal types than this
- * will be merged to a scalar
- *
- */
- public static function combine(
- array $types,
- ?Codebase $codebase = null,
- bool $overwrite_empty_array = false,
- bool $allow_mixed_union = true,
- int $literal_limit = 500
- ): Union {
- if (count($types) === 1) {
- $union_type = new Union([$types[0]]);
-
- if ($types[0]->from_docblock) {
- $union_type->from_docblock = true;
- }
-
- return $union_type;
- }
-
- $combination = new TypeCombination();
-
- $from_docblock = false;
-
- foreach ($types as $type) {
- $from_docblock = $from_docblock || $type->from_docblock;
-
- $result = self::scrapeTypeProperties(
- $type,
- $combination,
- $codebase,
- $overwrite_empty_array,
- $allow_mixed_union,
- $literal_limit
- );
-
- if ($result) {
- if ($from_docblock) {
- $result->from_docblock = true;
- }
-
- return $result;
- }
- }
-
- if (count($combination->value_types) === 1
- && !count($combination->objectlike_entries)
- && !$combination->array_type_params
- && !$combination->builtin_type_params
- && !$combination->object_type_params
- && !$combination->named_object_types
- && !$combination->strings
- && !$combination->class_string_types
- && !$combination->ints
- && !$combination->floats
- ) {
- if (isset($combination->value_types['false'])) {
- $union_type = Type::getFalse();
-
- if ($from_docblock) {
- $union_type->from_docblock = true;
- }
-
- return $union_type;
- }
-
- if (isset($combination->value_types['true'])) {
- $union_type = Type::getTrue();
-
- if ($from_docblock) {
- $union_type->from_docblock = true;
- }
-
- return $union_type;
- }
- } elseif (isset($combination->value_types['void'])) {
- unset($combination->value_types['void']);
-
- // if we're merging with another type, we cannot represent it in PHP
- $from_docblock = true;
-
- if (!isset($combination->value_types['null'])) {
- $combination->value_types['null'] = new TNull();
- }
- }
-
- if (isset($combination->value_types['true']) && isset($combination->value_types['false'])) {
- unset($combination->value_types['true'], $combination->value_types['false']);
-
- $combination->value_types['bool'] = new TBool();
- }
-
- if ($combination->array_type_params
- && (isset($combination->named_object_types['Traversable'])
- || isset($combination->builtin_type_params['Traversable']))
- && (
- ($codebase && $codebase->config->allow_phpstorm_generics)
- || isset($combination->builtin_type_params['Traversable'])
- || (isset($combination->named_object_types['Traversable'])
- && $combination->named_object_types['Traversable']->from_docblock)
- )
- && !$combination->extra_types
- ) {
- $array_param_types = $combination->array_type_params;
- $traversable_param_types = $combination->builtin_type_params['Traversable']
- ?? [Type::getMixed(), Type::getMixed()];
-
- $combined_param_types = [];
-
- foreach ($array_param_types as $i => $array_param_type) {
- $combined_param_types[] = Type::combineUnionTypes($array_param_type, $traversable_param_types[$i]);
- }
-
- $combination->value_types['iterable'] = new TIterable($combined_param_types);
-
- $combination->array_type_params = [];
-
- /**
- * @psalm-suppress PossiblyNullArrayAccess
- */
- unset(
- $combination->value_types['array'],
- $combination->named_object_types['Traversable'],
- $combination->builtin_type_params['Traversable']
- );
- }
-
- if ($combination->empty_mixed && $combination->non_empty_mixed) {
- $combination->value_types['mixed'] = new TMixed((bool) $combination->mixed_from_loop_isset);
- }
-
- $new_types = [];
-
- if ($combination->objectlike_entries) {
- $new_types = self::handleKeyedArrayEntries(
- $combination,
- $overwrite_empty_array
- );
- }
-
- if ($combination->array_type_params) {
- if (count($combination->array_type_params) !== 2) {
- throw new UnexpectedValueException('Unexpected number of parameters');
- }
-
- $new_types[] = self::getArrayTypeFromGenericParams(
- $codebase,
- $combination,
- $overwrite_empty_array,
- $allow_mixed_union,
- $type,
- $combination->array_type_params
- );
- }
-
- if ($combination->extra_types) {
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->extra_types = self::combine(
- array_values($combination->extra_types),
- $codebase
- )->getAtomicTypes();
- }
-
- foreach ($combination->builtin_type_params as $generic_type => $generic_type_params) {
- if ($generic_type === 'iterable') {
- $new_types[] = new TIterable($generic_type_params);
- } else {
- $generic_object = new TGenericObject($generic_type, $generic_type_params);
-
- /** @psalm-suppress PropertyTypeCoercion */
- $generic_object->extra_types = $combination->extra_types;
- $new_types[] = $generic_object;
-
- if ($combination->named_object_types) {
- unset($combination->named_object_types[$generic_type]);
- }
- }
- }
-
- foreach ($combination->object_type_params as $generic_type => $generic_type_params) {
- $generic_type = substr($generic_type, 0, (int) strpos($generic_type, '<'));
-
- $generic_object = new TGenericObject($generic_type, $generic_type_params);
-
- if ($combination->object_static[$generic_type] ?? false) {
- $generic_object->was_static = true;
- }
-
- /** @psalm-suppress PropertyTypeCoercion */
- $generic_object->extra_types = $combination->extra_types;
- $new_types[] = $generic_object;
- }
-
- if ($combination->class_string_types) {
- if ($combination->strings) {
- foreach ($combination->strings as $k => $string) {
- if ($string instanceof TLiteralClassString) {
- $combination->class_string_types[$string->value] = new TNamedObject($string->value);
- unset($combination->strings[$k]);
- }
- }
- }
-
- $has_non_specific_string = isset($combination->value_types['string'])
- && get_class($combination->value_types['string']) === TString::class;
-
- if (!$has_non_specific_string) {
- $object_type = self::combine(
- array_values($combination->class_string_types),
- $codebase
- );
-
- foreach ($object_type->getAtomicTypes() as $object_atomic_type) {
- if ($object_atomic_type instanceof TNamedObject) {
- $new_types[] = new TClassString($object_atomic_type->value, $object_atomic_type);
- } elseif ($object_atomic_type instanceof TObject) {
- $new_types[] = new TClassString();
- }
- }
- }
- }
-
- if ($combination->strings) {
- $new_types = array_merge($new_types, array_values($combination->strings));
- }
-
- if ($combination->ints) {
- $new_types = array_merge($new_types, array_values($combination->ints));
- }
-
- if ($combination->floats) {
- $new_types = array_merge($new_types, array_values($combination->floats));
- }
-
- if (isset($combination->value_types['string'])
- && isset($combination->value_types['int'])
- && isset($combination->value_types['bool'])
- && isset($combination->value_types['float'])
- ) {
- unset(
- $combination->value_types['string'],
- $combination->value_types['int'],
- $combination->value_types['bool'],
- $combination->value_types['float']
- );
- $combination->value_types['scalar'] = new TScalar;
- }
-
- if ($combination->named_object_types !== null) {
- $combination->value_types += $combination->named_object_types;
- }
-
- $has_empty = (int) isset($combination->value_types['empty']);
- $has_never = false;
-
- foreach ($combination->value_types as $type) {
- if ($type instanceof TMixed
- && $combination->mixed_from_loop_isset
- && (count($combination->value_types) > (1 + $has_empty) || count($new_types) > $has_empty)
- ) {
- continue;
- }
-
- if (($type instanceof TEmpty || $type instanceof TNever)
- && (count($combination->value_types) > 1 || count($new_types))
- ) {
- $has_never = true;
- continue;
- }
-
- $new_types[] = $type;
- }
-
- if (!$new_types && !$has_never) {
- throw new UnexpectedValueException('There should be types here');
- } elseif (!$new_types && $has_never) {
- $union_type = Type::getNever();
- } else {
- $union_type = new Union($new_types);
- }
-
- if ($from_docblock) {
- $union_type->from_docblock = true;
- }
-
- return $union_type;
- }
-
- private static function scrapeTypeProperties(
- Atomic $type,
- TypeCombination $combination,
- ?Codebase $codebase,
- bool $overwrite_empty_array,
- bool $allow_mixed_union,
- int $literal_limit
- ): ?Union {
- if ($type instanceof TMixed) {
- if ($type->from_loop_isset) {
- if ($combination->mixed_from_loop_isset === null) {
- $combination->mixed_from_loop_isset = true;
- } else {
- return null;
- }
- } else {
- $combination->mixed_from_loop_isset = false;
- }
-
- if ($type instanceof TNonEmptyMixed) {
- $combination->non_empty_mixed = true;
-
- if ($combination->empty_mixed) {
- return null;
- }
- } elseif ($type instanceof TEmptyMixed) {
- $combination->empty_mixed = true;
-
- if ($combination->non_empty_mixed) {
- return null;
- }
- } else {
- $combination->empty_mixed = true;
- $combination->non_empty_mixed = true;
- }
-
- if (!$allow_mixed_union) {
- return Type::getMixed($combination->mixed_from_loop_isset);
- }
- }
-
- // deal with false|bool => bool
- if (($type instanceof TFalse || $type instanceof TTrue) && isset($combination->value_types['bool'])) {
- return null;
- }
-
- if (get_class($type) === TBool::class && isset($combination->value_types['false'])) {
- unset($combination->value_types['false']);
- }
-
- if (get_class($type) === TBool::class && isset($combination->value_types['true'])) {
- unset($combination->value_types['true']);
- }
-
- if ($type instanceof TArray && isset($combination->builtin_type_params['iterable'])) {
- $type_key = 'iterable';
- } elseif ($type instanceof TArray
- && $type->type_params[1]->isMixed()
- && isset($combination->value_types['iterable'])
- ) {
- $type_key = 'iterable';
- $combination->builtin_type_params['iterable'] = [Type::getMixed(), Type::getMixed()];
- } elseif ($type instanceof TNamedObject
- && $type->value === 'Traversable'
- && (isset($combination->builtin_type_params['iterable']) || isset($combination->value_types['iterable']))
- ) {
- $type_key = 'iterable';
-
- if (!isset($combination->builtin_type_params['iterable'])) {
- $combination->builtin_type_params['iterable'] = [Type::getMixed(), Type::getMixed()];
- }
-
- if (!$type instanceof TGenericObject) {
- $type = new TGenericObject($type->value, [Type::getMixed(), Type::getMixed()]);
- }
- } elseif ($type instanceof TNamedObject && ($type->value === 'Traversable' || $type->value === 'Generator')) {
- $type_key = $type->value;
- } else {
- $type_key = $type->getKey();
- }
-
- if ($type instanceof TIterable
- && $combination->array_type_params
- && ($type->has_docblock_params || $combination->array_type_params[1]->isMixed())
- ) {
- if (!isset($combination->builtin_type_params['iterable'])) {
- $combination->builtin_type_params['iterable'] = $combination->array_type_params;
- } else {
- foreach ($combination->array_type_params as $i => $array_type_param) {
- $iterable_type_param = $combination->builtin_type_params['iterable'][$i];
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->builtin_type_params['iterable'][$i] = Type::combineUnionTypes(
- $iterable_type_param,
- $array_type_param
- );
- }
- }
-
- $combination->array_type_params = [];
- }
-
- if ($type instanceof TIterable
- && (isset($combination->named_object_types['Traversable'])
- || isset($combination->builtin_type_params['Traversable']))
- ) {
- if (!isset($combination->builtin_type_params['iterable'])) {
- $combination->builtin_type_params['iterable']
- = $combination->builtin_type_params['Traversable'] ?? [Type::getMixed(), Type::getMixed()];
- } elseif (isset($combination->builtin_type_params['Traversable'])) {
- foreach ($combination->builtin_type_params['Traversable'] as $i => $array_type_param) {
- $iterable_type_param = $combination->builtin_type_params['iterable'][$i];
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->builtin_type_params['iterable'][$i] = Type::combineUnionTypes(
- $iterable_type_param,
- $array_type_param
- );
- }
- } else {
- $combination->builtin_type_params['iterable'] = [Type::getMixed(), Type::getMixed()];
- }
-
- /** @psalm-suppress PossiblyNullArrayAccess */
- unset(
- $combination->named_object_types['Traversable'],
- $combination->builtin_type_params['Traversable']
- );
- }
-
- if ($type instanceof TNamedObject
- || $type instanceof TTemplateParam
- || $type instanceof TIterable
- || $type instanceof TObjectWithProperties
- ) {
- if ($type->extra_types) {
- $combination->extra_types = array_merge(
- $combination->extra_types ?: [],
- $type->extra_types
- );
- }
- }
-
- if ($type instanceof TNamedObject) {
- if (array_key_exists($type->value, $combination->object_static)) {
- if ($combination->object_static[$type->value] && !$type->was_static) {
- $combination->object_static[$type->value] = false;
- }
- } else {
- $combination->object_static[$type->value] = $type->was_static;
- }
- }
-
- if ($type instanceof TArray && $type_key === 'array') {
- if ($type instanceof TCallableArray && isset($combination->value_types['callable'])) {
- return null;
- }
-
- foreach ($type->type_params as $i => $type_param) {
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->array_type_params[$i] = Type::combineUnionTypes(
- $combination->array_type_params[$i] ?? null,
- $type_param,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- if ($type instanceof TNonEmptyArray) {
- if ($combination->array_counts !== null) {
- if ($type->count === null) {
- $combination->array_counts = null;
- } else {
- $combination->array_counts[$type->count] = true;
- }
- }
-
- $combination->array_sometimes_filled = true;
- } else {
- $combination->array_always_filled = false;
- }
-
- if (!$type->type_params[1]->isEmpty()) {
- $combination->all_arrays_lists = false;
- $combination->all_arrays_class_string_maps = false;
- }
-
- if ($type instanceof TCallableArray) {
- if ($combination->all_arrays_callable !== false) {
- $combination->all_arrays_callable = true;
- }
- } else {
- $combination->all_arrays_callable = false;
- }
-
- return null;
- }
-
- if ($type instanceof TList) {
- foreach ([Type::getInt(), $type->type_param] as $i => $type_param) {
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->array_type_params[$i] = Type::combineUnionTypes(
- $combination->array_type_params[$i] ?? null,
- $type_param,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- if ($type instanceof TNonEmptyList) {
- if ($combination->array_counts !== null) {
- if ($type->count === null) {
- $combination->array_counts = null;
- } else {
- $combination->array_counts[$type->count] = true;
- }
- }
-
- $combination->array_sometimes_filled = true;
- } else {
- $combination->array_always_filled = false;
- }
-
- if ($combination->all_arrays_lists !== false) {
- $combination->all_arrays_lists = true;
- }
-
- $combination->all_arrays_callable = false;
- $combination->all_arrays_class_string_maps = false;
-
- return null;
- }
-
- if ($type instanceof TClassStringMap) {
- foreach ([$type->getStandinKeyParam(), $type->value_param] as $i => $type_param) {
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->array_type_params[$i] = Type::combineUnionTypes(
- $combination->array_type_params[$i] ?? null,
- $type_param,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- $combination->array_always_filled = false;
-
- if ($combination->all_arrays_class_string_maps !== false) {
- $combination->all_arrays_class_string_maps = true;
- $combination->class_string_map_names[$type->param_name] = true;
- $combination->class_string_map_as_types[(string) $type->as_type] = $type->as_type;
- }
-
- return null;
- }
-
- if (($type instanceof TGenericObject && ($type->value === 'Traversable' || $type->value === 'Generator'))
- || ($type instanceof TIterable && $type->has_docblock_params)
- || ($type instanceof TArray && $type_key === 'iterable')
- ) {
- foreach ($type->type_params as $i => $type_param) {
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->builtin_type_params[$type_key][$i] = Type::combineUnionTypes(
- $combination->builtin_type_params[$type_key][$i] ?? null,
- $type_param,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- return null;
- }
-
- if ($type instanceof TGenericObject) {
- foreach ($type->type_params as $i => $type_param) {
- /** @psalm-suppress PropertyTypeCoercion */
- $combination->object_type_params[$type_key][$i] = Type::combineUnionTypes(
- $combination->object_type_params[$type_key][$i] ?? null,
- $type_param,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- return null;
- }
-
- if ($type instanceof TKeyedArray) {
- if ($type instanceof TCallableKeyedArray && isset($combination->value_types['callable'])) {
- return null;
- }
-
- $existing_objectlike_entries = (bool) $combination->objectlike_entries;
- $possibly_undefined_entries = $combination->objectlike_entries;
- $combination->objectlike_sealed = $combination->objectlike_sealed && $type->sealed;
-
- if ($type->previous_value_type) {
- $combination->objectlike_value_type = Type::combineUnionTypes(
- $type->previous_value_type,
- $combination->objectlike_value_type,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- if ($type->previous_key_type) {
- $combination->objectlike_key_type = Type::combineUnionTypes(
- $type->previous_key_type,
- $combination->objectlike_key_type,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- $has_defined_keys = false;
-
- foreach ($type->properties as $candidate_property_name => $candidate_property_type) {
- $value_type = $combination->objectlike_entries[$candidate_property_name] ?? null;
-
- if (!$value_type) {
- $combination->objectlike_entries[$candidate_property_name] = clone $candidate_property_type;
- // it's possibly undefined if there are existing objectlike entries and
- $combination->objectlike_entries[$candidate_property_name]->possibly_undefined
- = $existing_objectlike_entries || $candidate_property_type->possibly_undefined;
- } else {
- $combination->objectlike_entries[$candidate_property_name] = Type::combineUnionTypes(
- $value_type,
- $candidate_property_type,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- if (!$type->previous_value_type) {
- unset($possibly_undefined_entries[$candidate_property_name]);
- }
-
- if (!$candidate_property_type->possibly_undefined) {
- $has_defined_keys = true;
- }
- }
-
- if (!$has_defined_keys) {
- $combination->array_always_filled = false;
- }
-
- if ($combination->array_counts !== null) {
- $combination->array_counts[count($type->properties)] = true;
- }
-
- foreach ($possibly_undefined_entries as $possibly_undefined_type) {
- $possibly_undefined_type->possibly_undefined = true;
- }
-
- if (!$type->is_list) {
- $combination->all_arrays_lists = false;
- } elseif ($combination->all_arrays_lists !== false) {
- $combination->all_arrays_lists = true;
- }
-
- if ($type instanceof TCallableKeyedArray) {
- if ($combination->all_arrays_callable !== false) {
- $combination->all_arrays_callable = true;
- }
- } else {
- $combination->all_arrays_callable = false;
- }
-
- $combination->all_arrays_class_string_maps = false;
-
- return null;
- }
-
- if ($type instanceof TObject) {
- if ($type instanceof TCallableObject && isset($combination->value_types['callable'])) {
- return null;
- }
-
- $combination->named_object_types = null;
- $combination->value_types[$type_key] = $type;
-
- return null;
- }
-
- if ($type instanceof TIterable) {
- $combination->value_types[$type_key] = $type;
-
- return null;
- }
-
- if ($type instanceof TNamedObject) {
- if ($combination->named_object_types === null) {
- return null;
- }
-
- if (isset($combination->named_object_types[$type_key])) {
- return null;
- }
-
- if (!$codebase) {
- $combination->named_object_types[$type_key] = $type;
-
- return null;
- }
-
- if (!$codebase->classlikes->classOrInterfaceOrEnumExists($type_key)) {
- // write this to the main list
- $combination->value_types[$type_key] = $type;
-
- return null;
- }
-
- $is_class = $codebase->classExists($type_key);
-
- foreach ($combination->named_object_types as $key => $_) {
- if ($codebase->classExists($key)) {
- if ($codebase->classExtendsOrImplements($key, $type_key)) {
- unset($combination->named_object_types[$key]);
- continue;
- }
-
- if ($is_class) {
- if ($codebase->classExtends($type_key, $key)) {
- return null;
- }
- }
- } else {
- if ($codebase->interfaceExtends($key, $type_key)) {
- unset($combination->named_object_types[$key]);
- continue;
- }
-
- if ($is_class) {
- if ($codebase->classImplements($type_key, $key)) {
- return null;
- }
- } else {
- if ($codebase->interfaceExtends($type_key, $key)) {
- return null;
- }
- }
- }
- }
-
- $combination->named_object_types[$type_key] = $type;
-
- return null;
- }
-
- if ($type instanceof TScalar) {
- $combination->strings = null;
- $combination->ints = null;
- $combination->floats = null;
- unset(
- $combination->value_types['string'],
- $combination->value_types['int'],
- $combination->value_types['bool'],
- $combination->value_types['true'],
- $combination->value_types['false'],
- $combination->value_types['float']
- );
-
- if (!isset($combination->value_types[$type_key])
- || $combination->value_types[$type_key]->getId() === $type->getId()
- ) {
- $combination->value_types[$type_key] = $type;
- } else {
- $combination->value_types[$type_key] = new TScalar();
- }
-
- return null;
- }
-
- if ($type instanceof Scalar && isset($combination->value_types['scalar'])) {
- return null;
- }
-
- if ($type instanceof TArrayKey) {
- $combination->strings = null;
- $combination->ints = null;
- unset(
- $combination->value_types['string'],
- $combination->value_types['int']
- );
- $combination->value_types[$type_key] = $type;
-
- return null;
- }
-
- if ($type instanceof TString) {
- self::scrapeStringProperties(
- $type_key,
- $type,
- $combination,
- $codebase,
- $literal_limit
- );
-
- return null;
- }
-
- if ($type instanceof TInt) {
- self::scrapeIntProperties(
- $type_key,
- $type,
- $combination,
- $literal_limit
- );
-
- return null;
- }
-
- if ($type instanceof TFloat) {
- if ($type instanceof TLiteralFloat) {
- if ($combination->floats !== null && count($combination->floats) < $literal_limit) {
- $combination->floats[$type_key] = $type;
- } else {
- $combination->floats = null;
- $combination->value_types['float'] = new TFloat();
- }
- } else {
- $combination->floats = null;
- $combination->value_types['float'] = $type;
- }
-
- return null;
- }
-
- if ($type instanceof TCallable && $type_key === 'callable') {
- if (($combination->value_types['string'] ?? null) instanceof TCallableString) {
- unset($combination->value_types['string']);
- } elseif (!empty($combination->array_type_params) && $combination->all_arrays_callable) {
- $combination->array_type_params = [];
- } elseif (isset($combination->value_types['callable-object'])) {
- unset($combination->value_types['callable-object']);
- }
- }
-
- $combination->value_types[$type_key] = $type;
- return null;
- }
-
- private static function scrapeStringProperties(
- string $type_key,
- Atomic $type,
- TypeCombination $combination,
- ?Codebase $codebase,
- int $literal_limit
- ): void {
- if ($type instanceof TCallableString && isset($combination->value_types['callable'])) {
- return;
- }
-
- if (isset($combination->value_types['array-key'])) {
- return;
- }
-
- if ($type instanceof TTemplateParamClass) {
- $combination->value_types[$type_key] = $type;
- } elseif ($type instanceof TClassString) {
- if (!$type->as_type) {
- $combination->class_string_types['object'] = new TObject();
- } else {
- $combination->class_string_types[$type->as] = $type->as_type;
- }
- } elseif ($type instanceof TLiteralString) {
- if ($combination->strings !== null && count($combination->strings) < $literal_limit) {
- $combination->strings[$type_key] = $type;
- } else {
- $shared_classlikes = $codebase ? self::getSharedTypes($combination, $codebase) : [];
-
- $combination->strings = null;
-
- if (isset($combination->value_types['string'])
- && $combination->value_types['string'] instanceof TNumericString
- && is_numeric($type->value)
- ) {
- // do nothing
- } elseif (isset($combination->value_types['class-string'])
- && $type instanceof TLiteralClassString
- ) {
- // do nothing
- } elseif ($type instanceof TLiteralClassString) {
- $type_classlikes = $codebase
- ? self::getClassLikes($codebase, $type->value)
- : [];
-
- $mutual = array_intersect_key($type_classlikes, $shared_classlikes);
-
- if ($mutual) {
- $first_class = array_keys($mutual)[0];
-
- $combination->class_string_types[$first_class] = new TNamedObject($first_class);
- } else {
- $combination->class_string_types['object'] = new TObject();
- }
- } elseif (isset($combination->value_types['string'])
- && $combination->value_types['string'] instanceof TNonspecificLiteralString
- ) {
- // do nothing
- } elseif (isset($combination->value_types['string'])
- && $combination->value_types['string'] instanceof TLowercaseString
- && strtolower($type->value) === $type->value
- ) {
- // do nothing
- } elseif (isset($combination->value_types['string'])
- && $combination->value_types['string'] instanceof TNonFalsyString
- && $type->value
- ) {
- // do nothing
- } elseif (isset($combination->value_types['string'])
- && $combination->value_types['string'] instanceof TNonEmptyString
- && $type->value !== ''
- ) {
- // do nothing
- } else {
- $combination->value_types['string'] = new TString();
- }
- }
- } else {
- $type_key = 'string';
-
- if (!isset($combination->value_types['string'])) {
- if ($combination->strings) {
- if ($type instanceof TNumericString) {
- $has_non_numeric_string = false;
-
- foreach ($combination->strings as $string_type) {
- if (!is_numeric($string_type->value)) {
- $has_non_numeric_string = true;
- break;
- }
- }
-
- if ($has_non_numeric_string) {
- $combination->value_types['string'] = new TString();
- } else {
- $combination->value_types['string'] = $type;
- }
- } elseif ($type instanceof TLowercaseString) {
- $has_non_lowercase_string = false;
-
- foreach ($combination->strings as $string_type) {
- if (strtolower($string_type->value) !== $string_type->value) {
- $has_non_lowercase_string = true;
- break;
- }
- }
-
- if ($has_non_lowercase_string) {
- $combination->value_types['string'] = new TString();
- } else {
- $combination->value_types['string'] = $type;
- }
- } elseif ($type instanceof TNonEmptyString) {
- $has_empty_string = false;
-
- foreach ($combination->strings as $string_type) {
- if (!$string_type->value) {
- $has_empty_string = true;
- break;
- }
- }
-
- if ($has_empty_string) {
- $combination->value_types['string'] = new TString();
- } else {
- $combination->value_types['string'] = $type;
- }
- } elseif ($type instanceof TNonspecificLiteralString) {
- $combination->value_types['string'] = $type;
- } else {
- $combination->value_types[$type_key] = new TString();
- }
- } else {
- $combination->value_types[$type_key] = $type;
- }
- } elseif (get_class($combination->value_types['string']) !== TString::class) {
- if (get_class($type) === TString::class) {
- $combination->value_types['string'] = $type;
- } elseif (get_class($combination->value_types['string']) !== get_class($type)) {
- if (get_class($type) === TNonEmptyString::class
- && get_class($combination->value_types['string']) === TNumericString::class
- ) {
- $combination->value_types['string'] = $type;
- } elseif (get_class($type) === TNumericString::class
- && get_class($combination->value_types['string']) === TNonEmptyString::class
- ) {
- // do nothing
- } elseif ((get_class($type) === TNonEmptyString::class
- || get_class($type) === TNumericString::class)
- && get_class($combination->value_types['string']) === TNonFalsyString::class
- ) {
- $combination->value_types['string'] = $type;
- } elseif (get_class($type) === TNonFalsyString::class
- && (get_class($combination->value_types['string']) === TNonEmptyString::class
- || get_class($combination->value_types['string']) === TNumericString::class)
- ) {
- // do nothing
- } elseif ((get_class($type) === TNonEmptyString::class
- || get_class($type) === TNonFalsyString::class)
- && get_class($combination->value_types['string']) === TNonEmptyLowercaseString::class
- ) {
- $combination->value_types['string'] = new TNonEmptyString();
- } elseif ((get_class($combination->value_types['string']) === TNonEmptyString::class
- || get_class($combination->value_types['string']) === TNonFalsyString::class)
- && get_class($type) === TNonEmptyLowercaseString::class
- ) {
- $combination->value_types['string'] = new TNonEmptyString();
- } elseif (get_class($type) === TLowercaseString::class
- && get_class($combination->value_types['string']) === TNonEmptyLowercaseString::class
- ) {
- $combination->value_types['string'] = $type;
- } elseif (get_class($combination->value_types['string']) === TLowercaseString::class
- && get_class($type) === TNonEmptyLowercaseString::class
- ) {
- //no-change
- } elseif (get_class($combination->value_types['string'])
- === TNonEmptyNonspecificLiteralString::class
- && $type instanceof TNonEmptyString
- ) {
- $combination->value_types['string'] = new TNonEmptyString();
- } elseif (get_class($type) === TNonEmptyNonspecificLiteralString::class
- && $combination->value_types['string'] instanceof TNonEmptyString
- ) {
- // do nothing
- } else {
- $combination->value_types['string'] = new TString();
- }
- }
- }
-
- $combination->strings = null;
- }
- }
-
- private static function scrapeIntProperties(
- string $type_key,
- Atomic $type,
- TypeCombination $combination,
- int $literal_limit
- ): void {
- if (isset($combination->value_types['array-key'])) {
- return;
- }
-
- $had_zero = isset($combination->ints['int(0)']);
-
- if ($type instanceof TLiteralInt) {
- if ($type->value === 0) {
- $had_zero = true;
- }
-
- if ($combination->ints !== null && count($combination->ints) < $literal_limit) {
- $combination->ints[$type_key] = $type;
- } else {
- $combination->ints[$type_key] = $type;
-
- $all_nonnegative = !array_filter(
- $combination->ints,
- function ($int): bool {
- return $int->value < 0;
- }
- );
-
- if (isset($combination->value_types['int'])) {
- $current_int_type = $combination->value_types['int'];
- if ($current_int_type instanceof TIntRange) {
- foreach ($combination->ints as $int) {
- if (!$current_int_type->contains($int->value)) {
- $current_int_type->min_bound = TIntRange::getNewLowestBound(
- $current_int_type->min_bound,
- $int->value
- );
- $current_int_type->max_bound = TIntRange::getNewHighestBound(
- $current_int_type->max_bound,
- $int->value
- );
- }
- }
- }
- }
-
- $combination->ints = null;
-
- if (!isset($combination->value_types['int'])) {
- $combination->value_types['int'] = $all_nonnegative
- ? new TPositiveInt()
- : new TNonspecificLiteralInt();
- } elseif ($combination->value_types['int'] instanceof TPositiveInt
- && !$all_nonnegative
- ) {
- $combination->value_types['int'] = new TInt();
- }
- }
- } else {
- if ($type instanceof TPositiveInt) {
- if ($combination->ints) {
- $all_nonnegative = !array_filter(
- $combination->ints,
- function ($int): bool {
- return $int->value < 0;
- }
- );
-
- if ($all_nonnegative) {
- $combination->value_types['int'] = $type;
- } else {
- $combination->value_types['int'] = new TInt();
- }
- } elseif (!isset($combination->value_types['int'])) {
- $combination->value_types['int'] = $type;
- } elseif ($combination->value_types['int'] instanceof TIntRange) {
- //if we already had a range, we ensure the min is no higher than 1
- $combination->value_types['int']->min_bound = TIntRange::getNewLowestBound(
- $combination->value_types['int']->min_bound,
- 1
- );
- $combination->value_types['int']->max_bound = null;
- } elseif (get_class($combination->value_types['int']) !== get_class($type)) {
- $combination->value_types['int'] = new TInt();
- }
- } elseif ($type instanceof TNonspecificLiteralInt) {
- if ($combination->ints || !isset($combination->value_types['int'])) {
- $combination->value_types['int'] = $type;
- } elseif (isset($combination->value_types['int'])
- && get_class($combination->value_types['int'])
- !== get_class($type)
- ) {
- $combination->value_types['int'] = new TInt();
- }
- } elseif ($type instanceof TIntRange) {
- $type = clone $type;
- if ($combination->ints) {
- foreach ($combination->ints as $int) {
- if (!$type->contains($int->value)) {
- $type->min_bound = TIntRange::getNewLowestBound($type->min_bound, $int->value);
- $type->max_bound = TIntRange::getNewHighestBound($type->max_bound, $int->value);
- }
- }
-
- $combination->value_types['int'] = $type;
- } elseif (!isset($combination->value_types['int'])) {
- $combination->value_types['int'] = $type;
- } else {
- $old_type = $combination->value_types['int'];
- if ($old_type instanceof TIntRange) {
- $type->min_bound = TIntRange::getNewLowestBound($old_type->min_bound, $type->min_bound);
- $type->max_bound = TIntRange::getNewHighestBound($old_type->max_bound, $type->max_bound);
- } elseif ($old_type instanceof TPositiveInt) {
- $type->min_bound = TIntRange::getNewLowestBound($type->min_bound, 0);
- $type->max_bound = null;
- } else {
- $type = new TInt();
- }
- $combination->value_types['int'] = $type;
- }
- } else {
- $combination->value_types['int'] = $type;
- }
-
- $combination->ints = null;
- }
-
- if ($had_zero
- && isset($combination->value_types['int'])
- && $combination->value_types['int'] instanceof TPositiveInt
- ) {
- if ($combination->ints === null) {
- $combination->ints = ['int(0)' => new TLiteralInt(0)];
- } elseif ($type instanceof TLiteralInt && $type->value < 0) {
- $combination->ints = null;
- $combination->value_types['int'] = new TInt();
- }
- }
- }
-
- /**
- * @return array<string, bool>
- */
- private static function getSharedTypes(TypeCombination $combination, Codebase $codebase): array
- {
- /** @var array<string, bool>|null */
- $shared_classlikes = null;
-
- if ($combination->strings) {
- foreach ($combination->strings as $string_type) {
- $classlikes = self::getClassLikes($codebase, $string_type->value);
-
- if ($shared_classlikes === null) {
- $shared_classlikes = $classlikes;
- } elseif ($shared_classlikes) {
- $shared_classlikes = array_intersect_key($shared_classlikes, $classlikes);
- }
- }
- }
-
- if ($combination->class_string_types) {
- foreach ($combination->class_string_types as $value_type) {
- if ($value_type instanceof TNamedObject) {
- $classlikes = self::getClassLikes($codebase, $value_type->value);
-
- if ($shared_classlikes === null) {
- $shared_classlikes = $classlikes;
- } elseif ($shared_classlikes) {
- $shared_classlikes = array_intersect_key($shared_classlikes, $classlikes);
- }
- }
- }
- }
-
- return $shared_classlikes ?: [];
- }
-
- /**
- * @return array<string, true>
- */
- private static function getClassLikes(Codebase $codebase, string $fq_classlike_name): array
- {
- try {
- $class_storage = $codebase->classlike_storage_provider->get($fq_classlike_name);
- } catch (InvalidArgumentException $e) {
- return [];
- }
-
- $classlikes = [];
-
- $classlikes[$fq_classlike_name] = true;
-
- foreach ($class_storage->parent_classes as $parent_class) {
- $classlikes[$parent_class] = true;
- }
-
- foreach ($class_storage->parent_interfaces as $parent_interface) {
- $classlikes[$parent_interface] = true;
- }
-
- foreach ($class_storage->class_implements as $interface) {
- $classlikes[$interface] = true;
- }
-
- return $classlikes;
- }
-
- /**
- * @return list<Atomic>
- */
- private static function handleKeyedArrayEntries(
- TypeCombination $combination,
- bool $overwrite_empty_array
- ): array {
- $new_types = [];
-
- if ($combination->array_type_params
- && $combination->array_type_params[0]->allStringLiterals()
- && $combination->array_always_filled
- ) {
- foreach ($combination->array_type_params[0]->getAtomicTypes() as $atomic_key_type) {
- if ($atomic_key_type instanceof TLiteralString) {
- $combination->objectlike_entries[$atomic_key_type->value]
- = $combination->array_type_params[1];
- }
- }
-
- $combination->array_type_params = [];
- $combination->objectlike_sealed = false;
- }
-
- if (!$combination->array_type_params
- || $combination->array_type_params[1]->isEmpty()
- ) {
- if (!$overwrite_empty_array
- && ($combination->array_type_params
- && ($combination->array_type_params[1]->isEmpty()
- || $combination->array_type_params[1]->isMixed()))
- ) {
- foreach ($combination->objectlike_entries as $objectlike_entry) {
- $objectlike_entry->possibly_undefined = true;
- }
- }
-
- if ($combination->objectlike_value_type
- && $combination->objectlike_value_type->isMixed()
- ) {
- $combination->objectlike_entries = array_filter(
- $combination->objectlike_entries,
- function (Union $type): bool {
- return !$type->possibly_undefined;
- }
- );
- }
-
- if ($combination->objectlike_entries) {
- if ($combination->all_arrays_callable) {
- $objectlike = new TCallableKeyedArray($combination->objectlike_entries);
- } else {
- $objectlike = new TKeyedArray($combination->objectlike_entries);
- }
-
- if ($combination->objectlike_sealed && !$combination->array_type_params) {
- $objectlike->sealed = true;
- }
-
- if ($combination->objectlike_key_type) {
- $objectlike->previous_key_type = $combination->objectlike_key_type;
- } elseif ($combination->array_type_params
- && $combination->array_type_params[0]->isArrayKey()
- ) {
- $objectlike->previous_key_type = $combination->array_type_params[0];
- }
-
- if ($combination->objectlike_value_type) {
- $objectlike->previous_value_type = $combination->objectlike_value_type;
- } elseif ($combination->array_type_params
- && $combination->array_type_params[1]->isMixed()
- ) {
- $objectlike->previous_value_type = $combination->array_type_params[1];
- }
-
- if ($combination->all_arrays_lists) {
- $objectlike->is_list = true;
- }
-
- $new_types[] = $objectlike;
- } else {
- $new_types[] = new TArray([Type::getArrayKey(), Type::getMixed()]);
- }
-
- // if we're merging an empty array with an object-like, clobber empty array
- $combination->array_type_params = [];
- }
-
- return $new_types;
- }
-
- /**
- * @param array{Union, Union} $generic_type_params
- */
- private static function getArrayTypeFromGenericParams(
- ?Codebase $codebase,
- TypeCombination $combination,
- bool $overwrite_empty_array,
- bool $allow_mixed_union,
- Atomic $type,
- array $generic_type_params
- ): Atomic {
- if ($combination->objectlike_entries) {
- $objectlike_generic_type = null;
-
- $objectlike_keys = [];
-
- foreach ($combination->objectlike_entries as $property_name => $property_type) {
- $objectlike_generic_type = Type::combineUnionTypes(
- clone $property_type,
- $objectlike_generic_type,
- $codebase,
- $overwrite_empty_array
- );
-
- if (is_int($property_name)) {
- $objectlike_keys[$property_name] = new TLiteralInt($property_name);
- } elseif ($type instanceof TKeyedArray && isset($type->class_strings[$property_name])) {
- $objectlike_keys[$property_name] = new TLiteralClassString($property_name);
- } else {
- $objectlike_keys[$property_name] = new TLiteralString($property_name);
- }
- }
-
- if ($combination->objectlike_value_type) {
- $objectlike_generic_type = Type::combineUnionTypes(
- $combination->objectlike_value_type,
- $objectlike_generic_type,
- $codebase,
- $overwrite_empty_array
- );
- }
-
- $objectlike_generic_type->possibly_undefined = false;
-
- $objectlike_key_type = new Union(array_values($objectlike_keys));
-
- $objectlike_key_type = Type::combineUnionTypes(
- $combination->objectlike_key_type,
- $objectlike_key_type,
- $codebase,
- $overwrite_empty_array
- );
-
- $generic_type_params[0] = Type::combineUnionTypes(
- $generic_type_params[0],
- $objectlike_key_type,
- $codebase,
- $overwrite_empty_array,
- $allow_mixed_union
- );
-
- if (!$generic_type_params[1]->isMixed()) {
- $generic_type_params[1] = Type::combineUnionTypes(
- $generic_type_params[1],
- $objectlike_generic_type,
- $codebase,
- $overwrite_empty_array,
- $allow_mixed_union
- );
- }
- }
-
- if ($combination->all_arrays_callable) {
- $array_type = new TCallableArray($generic_type_params);
- } elseif ($combination->array_always_filled
- || ($combination->array_sometimes_filled && $overwrite_empty_array)
- || ($combination->objectlike_entries
- && $combination->objectlike_sealed
- && $overwrite_empty_array)
- ) {
- if ($combination->all_arrays_lists) {
- if ($combination->objectlike_entries
- && $combination->objectlike_sealed
- ) {
- $array_type = new TKeyedArray([$generic_type_params[1]]);
- $array_type->previous_key_type = Type::getInt();
- $array_type->previous_value_type = $combination->array_type_params[1];
- $array_type->is_list = true;
- } else {
- $array_type = new TNonEmptyList($generic_type_params[1]);
-
- if ($combination->array_counts && count($combination->array_counts) === 1) {
- $array_type->count = array_keys($combination->array_counts)[0];
- }
- }
- } else {
- $array_type = new TNonEmptyArray($generic_type_params);
-
- if ($combination->array_counts && count($combination->array_counts) === 1) {
- $array_type->count = array_keys($combination->array_counts)[0];
- }
- }
- } else {
- if ($combination->all_arrays_class_string_maps
- && count($combination->class_string_map_as_types) === 1
- && count($combination->class_string_map_names) === 1
- ) {
- $array_type = new TClassStringMap(
- array_keys($combination->class_string_map_names)[0],
- array_values($combination->class_string_map_as_types)[0],
- $generic_type_params[1]
- );
- } elseif ($combination->all_arrays_lists) {
- $array_type = new TList($generic_type_params[1]);
- } else {
- $array_type = new TArray($generic_type_params);
- }
- }
-
- return $array_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php
deleted file mode 100644
index 3709534..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php
+++ /dev/null
@@ -1,909 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Codebase;
-use Psalm\Exception\CircularReferenceException;
-use Psalm\Internal\Type\SimpleAssertionReconciler;
-use Psalm\Internal\Type\SimpleNegatedAssertionReconciler;
-use Psalm\Internal\Type\TypeParser;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TEmpty;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntMask;
-use Psalm\Type\Atomic\TIntMaskOf;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyOfClassConstant;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNever;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTypeAlias;
-use Psalm\Type\Atomic\TValueOfClassConstant;
-use Psalm\Type\Atomic\TVoid;
-use Psalm\Type\Union;
-use ReflectionProperty;
-
-use function array_filter;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_values;
-use function count;
-use function get_class;
-use function is_array;
-use function is_string;
-use function reset;
-use function strpos;
-use function strtolower;
-use function substr;
-
-/**
- * @internal
- */
-class TypeExpander
-{
- /**
- * @param string|TNamedObject|TTemplateParam|null $static_class_type
- *
- */
- public static function expandUnion(
- Codebase $codebase,
- Union $return_type,
- ?string $self_class,
- $static_class_type,
- ?string $parent_class,
- bool $evaluate_class_constants = true,
- bool $evaluate_conditional_types = false,
- bool $final = false,
- bool $expand_generic = false,
- bool $expand_templates = false
- ): Union {
- $return_type = clone $return_type;
-
- $new_return_type_parts = [];
-
- $has_array_output = false;
-
- foreach ($return_type->getAtomicTypes() as $return_type_part) {
- $parts = self::expandAtomic(
- $codebase,
- $return_type_part,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if (is_array($parts)) {
- $new_return_type_parts = array_merge($new_return_type_parts, $parts);
- $has_array_output = true;
- } else {
- $new_return_type_parts[] = $parts;
- }
- }
-
- if ($has_array_output) {
- $fleshed_out_type = TypeCombiner::combine(
- $new_return_type_parts,
- $codebase
- );
- } else {
- $fleshed_out_type = new Union($new_return_type_parts);
- }
-
- $fleshed_out_type->from_docblock = $return_type->from_docblock;
- $fleshed_out_type->ignore_nullable_issues = $return_type->ignore_nullable_issues;
- $fleshed_out_type->ignore_falsable_issues = $return_type->ignore_falsable_issues;
- $fleshed_out_type->possibly_undefined = $return_type->possibly_undefined;
- $fleshed_out_type->possibly_undefined_from_try = $return_type->possibly_undefined_from_try;
- $fleshed_out_type->by_ref = $return_type->by_ref;
- $fleshed_out_type->initialized = $return_type->initialized;
- $fleshed_out_type->from_property = $return_type->from_property;
- $fleshed_out_type->from_static_property = $return_type->from_static_property;
- $fleshed_out_type->had_template = $return_type->had_template;
- $fleshed_out_type->parent_nodes = $return_type->parent_nodes;
-
- return $fleshed_out_type;
- }
-
- /**
- * @param string|TNamedObject|TTemplateParam|null $static_class_type
- *
- * @return Atomic|non-empty-list<Atomic>
- */
- public static function expandAtomic(
- Codebase $codebase,
- Atomic &$return_type,
- ?string $self_class,
- $static_class_type,
- ?string $parent_class,
- bool $evaluate_class_constants = true,
- bool $evaluate_conditional_types = false,
- bool $final = false,
- bool $expand_generic = false,
- bool $expand_templates = false
- ) {
- if ($return_type instanceof TNamedObject
- || $return_type instanceof TTemplateParam
- ) {
- if ($return_type->extra_types) {
- $new_intersection_types = [];
-
- foreach ($return_type->extra_types as &$extra_type) {
- self::expandAtomic(
- $codebase,
- $extra_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $expand_generic,
- $expand_templates
- );
-
- if ($extra_type instanceof TNamedObject && $extra_type->extra_types) {
- $new_intersection_types = array_merge(
- $new_intersection_types,
- $extra_type->extra_types
- );
- $extra_type->extra_types = [];
- }
- }
-
- if ($new_intersection_types) {
- $return_type->extra_types = array_merge($return_type->extra_types, $new_intersection_types);
- }
- }
-
- if ($return_type instanceof TNamedObject) {
- $return_type = self::expandNamedObject(
- $codebase,
- $return_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $final,
- $expand_generic
- );
- }
- }
-
- if ($return_type instanceof TClassString
- && $return_type->as_type
- ) {
- $new_as_type = clone $return_type->as_type;
-
- self::expandAtomic(
- $codebase,
- $new_as_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if ($new_as_type instanceof TNamedObject) {
- $return_type->as_type = $new_as_type;
- $return_type->as = $return_type->as_type->value;
- }
- } elseif ($return_type instanceof TTemplateParam) {
- $new_as_type = self::expandUnion(
- $codebase,
- clone $return_type->as,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if ($expand_templates) {
- return array_values($new_as_type->getAtomicTypes());
- }
-
- $return_type->as = $new_as_type;
- }
-
- if ($return_type instanceof TClassConstant) {
- if ($return_type->fq_classlike_name === 'self' && $self_class) {
- $return_type->fq_classlike_name = $self_class;
- }
-
- if ($return_type->fq_classlike_name === 'static' && $self_class) {
- $return_type->fq_classlike_name = is_string($static_class_type) ? $static_class_type : $self_class;
- }
-
- if ($evaluate_class_constants && $codebase->classOrInterfaceOrEnumExists($return_type->fq_classlike_name)) {
- if (strtolower($return_type->const_name) === 'class') {
- return new TLiteralClassString($return_type->fq_classlike_name);
- }
-
- $class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name);
-
- if (strpos($return_type->const_name, '*') !== false) {
- $matching_constants = array_merge(
- array_keys($class_storage->constants),
- array_keys($class_storage->enum_cases)
- );
-
- $const_name_part = substr($return_type->const_name, 0, -1);
-
- if ($const_name_part) {
- $matching_constants = array_filter(
- $matching_constants,
- function ($constant_name) use ($const_name_part): bool {
- return $constant_name !== $const_name_part
- && strpos($constant_name, $const_name_part) === 0;
- }
- );
- }
- } else {
- $matching_constants = [$return_type->const_name];
- }
-
- $matching_constant_types = [];
-
- foreach ($matching_constants as $matching_constant) {
- try {
- $class_constant = $codebase->classlikes->getClassConstantType(
- $return_type->fq_classlike_name,
- $matching_constant,
- ReflectionProperty::IS_PRIVATE
- );
- } catch (CircularReferenceException $e) {
- $class_constant = null;
- }
-
- if ($class_constant) {
- if ($class_constant->isSingle()) {
- $class_constant = clone $class_constant;
-
- $matching_constant_types = array_merge(
- array_values($class_constant->getAtomicTypes()),
- $matching_constant_types
- );
- }
- }
- }
-
- if ($matching_constant_types) {
- return $matching_constant_types;
- }
- }
-
- return $return_type;
- }
-
- if ($return_type instanceof TTypeAlias) {
- $declaring_fq_classlike_name = $return_type->declaring_fq_classlike_name;
-
- if ($declaring_fq_classlike_name === 'self' && $self_class) {
- $declaring_fq_classlike_name = $self_class;
- }
-
- if ($evaluate_class_constants && $codebase->classOrInterfaceExists($declaring_fq_classlike_name)) {
- $class_storage = $codebase->classlike_storage_provider->get($declaring_fq_classlike_name);
-
- $type_alias_name = $return_type->alias_name;
-
- if (isset($class_storage->type_aliases[$type_alias_name])) {
- $resolved_type_alias = $class_storage->type_aliases[$type_alias_name];
-
- if ($resolved_type_alias->replacement_atomic_types) {
- $replacement_atomic_types = $resolved_type_alias->replacement_atomic_types;
-
- $recursively_fleshed_out_types = [];
-
- foreach ($replacement_atomic_types as $replacement_atomic_type) {
- $recursively_fleshed_out_type = self::expandAtomic(
- $codebase,
- $replacement_atomic_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if (is_array($recursively_fleshed_out_type)) {
- $recursively_fleshed_out_types = array_merge(
- $recursively_fleshed_out_type,
- $recursively_fleshed_out_types
- );
- } else {
- $recursively_fleshed_out_types[] = $recursively_fleshed_out_type;
- }
- }
-
- return $recursively_fleshed_out_types;
- }
- }
- }
-
- return $return_type;
- }
-
- if ($return_type instanceof TKeyOfClassConstant
- || $return_type instanceof TValueOfClassConstant
- ) {
- if ($return_type->fq_classlike_name === 'self' && $self_class) {
- $return_type->fq_classlike_name = $self_class;
- }
-
- if ($evaluate_class_constants && $codebase->classOrInterfaceExists($return_type->fq_classlike_name)) {
- try {
- $class_constant_type = $codebase->classlikes->getClassConstantType(
- $return_type->fq_classlike_name,
- $return_type->const_name,
- ReflectionProperty::IS_PRIVATE
- );
- } catch (CircularReferenceException $e) {
- $class_constant_type = null;
- }
-
- if ($class_constant_type) {
- foreach ($class_constant_type->getAtomicTypes() as $const_type_atomic) {
- if ($const_type_atomic instanceof TKeyedArray
- || $const_type_atomic instanceof TArray
- ) {
- if ($const_type_atomic instanceof TKeyedArray) {
- $const_type_atomic = $const_type_atomic->getGenericArrayType();
- }
-
- if ($return_type instanceof TKeyOfClassConstant) {
- return array_values($const_type_atomic->type_params[0]->getAtomicTypes());
- }
-
- return array_values($const_type_atomic->type_params[1]->getAtomicTypes());
- }
- }
- }
- }
-
- return $return_type;
- }
-
- if ($return_type instanceof TIntMask) {
- if (!$evaluate_class_constants) {
- return new TInt();
- }
-
- $potential_ints = [];
-
- foreach ($return_type->values as $value_type) {
- $new_value_type = self::expandAtomic(
- $codebase,
- $value_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if (is_array($new_value_type)) {
- $new_value_type = reset($new_value_type);
- }
-
- if (!$new_value_type instanceof TLiteralInt) {
- return new TInt();
- }
-
- $potential_ints[] = $new_value_type->value;
- }
-
- return TypeParser::getComputedIntsFromMask($potential_ints);
- }
-
- if ($return_type instanceof TIntMaskOf) {
- if (!$evaluate_class_constants) {
- return new TInt();
- }
-
- $value_type = $return_type->value;
-
- $new_value_types = self::expandAtomic(
- $codebase,
- $value_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if (!is_array($new_value_types)) {
- return new TInt();
- }
-
- $potential_ints = [];
-
- foreach ($new_value_types as $new_value_type) {
- if (!$new_value_type instanceof TLiteralInt) {
- return new TInt();
- }
-
- $potential_ints[] = $new_value_type->value;
- }
-
- return TypeParser::getComputedIntsFromMask($potential_ints);
- }
-
- if ($return_type instanceof TArray
- || $return_type instanceof TGenericObject
- || $return_type instanceof TIterable
- ) {
- foreach ($return_type->type_params as $k => $type_param) {
- /** @psalm-suppress PropertyTypeCoercion */
- $return_type->type_params[$k] = self::expandUnion(
- $codebase,
- $type_param,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
- } elseif ($return_type instanceof TKeyedArray) {
- foreach ($return_type->properties as &$property_type) {
- $property_type = self::expandUnion(
- $codebase,
- $property_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
- } elseif ($return_type instanceof TList) {
- $return_type->type_param = self::expandUnion(
- $codebase,
- $return_type->type_param,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
-
- if ($return_type instanceof TObjectWithProperties) {
- foreach ($return_type->properties as &$property_type) {
- $property_type = self::expandUnion(
- $codebase,
- $property_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
- }
-
- if ($return_type instanceof TCallable
- || $return_type instanceof TClosure
- ) {
- if ($return_type->params) {
- foreach ($return_type->params as $param) {
- if ($param->type) {
- $param->type = self::expandUnion(
- $codebase,
- $param->type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
- }
- }
- if ($return_type->return_type) {
- $return_type->return_type = self::expandUnion(
- $codebase,
- $return_type->return_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
- }
-
- if ($return_type instanceof TConditional) {
- return self::expandConditional(
- $codebase,
- $return_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
- }
-
- return $return_type;
- }
-
- /**
- * @param string|TNamedObject|TTemplateParam|null $static_class_type
- * @return TNamedObject|TTemplateParam
- */
- private static function expandNamedObject(
- Codebase $codebase,
- TNamedObject $return_type,
- ?string $self_class,
- $static_class_type,
- ?string $parent_class,
- bool $final = false,
- bool &$expand_generic = false
- ) {
- if ($expand_generic
- && get_class($return_type) === TNamedObject::class
- && !$return_type->extra_types
- && $codebase->classOrInterfaceExists($return_type->value)
- ) {
- $value = $codebase->classlikes->getUnAliasedName($return_type->value);
- $container_class_storage = $codebase->classlike_storage_provider->get(
- $value
- );
-
- if ($container_class_storage->template_types
- && array_filter(
- $container_class_storage->template_types,
- function ($type_map) {
- return !reset($type_map)->hasMixed();
- }
- )
- ) {
- $return_type = new TGenericObject(
- $return_type->value,
- array_values(
- array_map(
- function ($type_map) {
- return clone reset($type_map);
- },
- $container_class_storage->template_types
- )
- )
- );
-
- // we don't want to expand generic types recursively
- $expand_generic = false;
- }
- }
-
- $return_type_lc = strtolower($return_type->value);
-
- if ($static_class_type && ($return_type_lc === 'static' || $return_type_lc === '$this')) {
- if (is_string($static_class_type)) {
- $return_type->value = $static_class_type;
- } else {
- if ($return_type instanceof TGenericObject
- && $static_class_type instanceof TGenericObject
- ) {
- $return_type->value = $static_class_type->value;
- } else {
- $return_type = clone $static_class_type;
- }
- }
-
- if (!$final && $return_type instanceof TNamedObject) {
- $return_type->was_static = true;
- }
- } elseif ($return_type->was_static
- && ($static_class_type instanceof TNamedObject
- || $static_class_type instanceof TTemplateParam)
- ) {
- $return_type = clone $return_type;
- $cloned_static = clone $static_class_type;
- $extra_static = $cloned_static->extra_types ?: [];
- $cloned_static->extra_types = null;
-
- if ($cloned_static->getKey(false) !== $return_type->getKey(false)) {
- $return_type->extra_types[$static_class_type->getKey()] = clone $cloned_static;
- }
-
- foreach ($extra_static as $extra_static_type) {
- if ($extra_static_type->getKey(false) !== $return_type->getKey(false)) {
- $return_type->extra_types[$extra_static_type->getKey()] = clone $extra_static_type;
- }
- }
- } elseif ($return_type->was_static && is_string($static_class_type) && $final) {
- $return_type->value = $static_class_type;
- $return_type->was_static = false;
- } elseif ($self_class && $return_type_lc === 'self') {
- $return_type->value = $self_class;
- } elseif ($parent_class && $return_type_lc === 'parent') {
- $return_type->value = $parent_class;
- } else {
- $return_type->value = $codebase->classlikes->getUnAliasedName($return_type->value);
- }
-
- return $return_type;
- }
-
- /**
- * @param string|TNamedObject|TTemplateParam|null $static_class_type
- *
- * @return Atomic|non-empty-list<Atomic>
- */
- private static function expandConditional(
- Codebase $codebase,
- TConditional $return_type,
- ?string $self_class,
- $static_class_type,
- ?string $parent_class,
- bool $evaluate_class_constants = true,
- bool $evaluate_conditional_types = false,
- bool $final = false,
- bool $expand_generic = false,
- bool $expand_templates = false
- ) {
- $new_as_type = self::expandUnion(
- $codebase,
- $return_type->as_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- $return_type->as_type = $new_as_type;
-
- if ($evaluate_conditional_types) {
- $assertion = null;
-
- if ($return_type->conditional_type->isSingle()) {
- foreach ($return_type->conditional_type->getAtomicTypes() as $condition_atomic_type) {
- $candidate = self::expandAtomic(
- $codebase,
- $condition_atomic_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- if (!is_array($candidate)) {
- $assertion = $candidate->getAssertionString();
- }
- }
- }
-
- $if_conditional_return_types = [];
-
- foreach ($return_type->if_type->getAtomicTypes() as $if_atomic_type) {
- $candidate = self::expandAtomic(
- $codebase,
- $if_atomic_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- $candidate_types = is_array($candidate) ? $candidate : [$candidate];
-
- $if_conditional_return_types = array_merge(
- $if_conditional_return_types,
- $candidate_types
- );
- }
-
- $else_conditional_return_types = [];
-
- foreach ($return_type->else_type->getAtomicTypes() as $else_atomic_type) {
- $candidate = self::expandAtomic(
- $codebase,
- $else_atomic_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- $candidate_types = is_array($candidate) ? $candidate : [$candidate];
-
- $else_conditional_return_types = array_merge(
- $else_conditional_return_types,
- $candidate_types
- );
- }
-
- if ($assertion && $return_type->param_name === (string) $return_type->if_type) {
- $if_conditional_return_type = TypeCombiner::combine(
- $if_conditional_return_types,
- $codebase
- );
-
- $if_conditional_return_type = SimpleAssertionReconciler::reconcile(
- $assertion,
- $codebase,
- $if_conditional_return_type
- );
-
-
- if ($if_conditional_return_type) {
- $if_conditional_return_types = array_values($if_conditional_return_type->getAtomicTypes());
- }
- }
-
- if ($assertion && $return_type->param_name === (string) $return_type->else_type) {
- $else_conditional_return_type = TypeCombiner::combine(
- $else_conditional_return_types,
- $codebase
- );
-
- $else_conditional_return_type = SimpleNegatedAssertionReconciler::reconcile(
- $codebase,
- $assertion,
- $else_conditional_return_type
- );
-
- if ($else_conditional_return_type) {
- $else_conditional_return_types = array_values($else_conditional_return_type->getAtomicTypes());
- }
- }
-
- $all_conditional_return_types = array_merge(
- $if_conditional_return_types,
- $else_conditional_return_types
- );
-
- $number_of_types = count($all_conditional_return_types);
- // we filter TNever and TEmpty that have no bearing on the return type
- if ($number_of_types > 1) {
- $all_conditional_return_types = array_filter(
- $all_conditional_return_types,
- static function (Atomic $atomic_type): bool {
- return !($atomic_type instanceof TEmpty
- || $atomic_type instanceof TNever);
- }
- );
- }
-
- // if we still have more than one type, we remove TVoid and replace it by TNull
- $number_of_types = count($all_conditional_return_types);
- if ($number_of_types > 1) {
- $all_conditional_return_types = array_filter(
- $all_conditional_return_types,
- static function (Atomic $atomic_type): bool {
- return !$atomic_type instanceof TVoid;
- }
- );
-
- if (count($all_conditional_return_types) !== $number_of_types) {
- $null_type = new TNull();
- $null_type->from_docblock = true;
- $all_conditional_return_types[] = $null_type;
- }
- }
-
- if ($all_conditional_return_types) {
- $combined = TypeCombiner::combine(
- array_values($all_conditional_return_types),
- $codebase
- );
-
- return array_values($combined->getAtomicTypes());
- }
- }
-
- $return_type->conditional_type = self::expandUnion(
- $codebase,
- $return_type->conditional_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- $return_type->if_type = self::expandUnion(
- $codebase,
- $return_type->if_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- $return_type->else_type = self::expandUnion(
- $codebase,
- $return_type->else_type,
- $self_class,
- $static_class_type,
- $parent_class,
- $evaluate_class_constants,
- $evaluate_conditional_types,
- $final,
- $expand_generic,
- $expand_templates
- );
-
- return $return_type;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php
deleted file mode 100644
index fbcad54..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php
+++ /dev/null
@@ -1,1336 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use InvalidArgumentException;
-use LogicException;
-use Psalm\Codebase;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Analyzer\ProjectAnalyzer;
-use Psalm\Internal\Type\ParseTree\CallableParamTree;
-use Psalm\Internal\Type\ParseTree\CallableTree;
-use Psalm\Internal\Type\ParseTree\CallableWithReturnTypeTree;
-use Psalm\Internal\Type\ParseTree\ConditionalTree;
-use Psalm\Internal\Type\ParseTree\EncapsulationTree;
-use Psalm\Internal\Type\ParseTree\GenericTree;
-use Psalm\Internal\Type\ParseTree\IndexedAccessTree;
-use Psalm\Internal\Type\ParseTree\IntersectionTree;
-use Psalm\Internal\Type\ParseTree\KeyedArrayPropertyTree;
-use Psalm\Internal\Type\ParseTree\KeyedArrayTree;
-use Psalm\Internal\Type\ParseTree\MethodTree;
-use Psalm\Internal\Type\ParseTree\MethodWithReturnTypeTree;
-use Psalm\Internal\Type\ParseTree\NullableTree;
-use Psalm\Internal\Type\ParseTree\TemplateAsTree;
-use Psalm\Internal\Type\ParseTree\UnionTree;
-use Psalm\Internal\Type\ParseTree\Value;
-use Psalm\Storage\FunctionLikeParameter;
-use Psalm\Type;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TArrayKey;
-use Psalm\Type\Atomic\TCallable;
-use Psalm\Type\Atomic\TCallableKeyedArray;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TClassString;
-use Psalm\Type\Atomic\TClassStringMap;
-use Psalm\Type\Atomic\TClosure;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TInt;
-use Psalm\Type\Atomic\TIntMask;
-use Psalm\Type\Atomic\TIntMaskOf;
-use Psalm\Type\Atomic\TIntRange;
-use Psalm\Type\Atomic\TIterable;
-use Psalm\Type\Atomic\TKeyOfClassConstant;
-use Psalm\Type\Atomic\TKeyedArray;
-use Psalm\Type\Atomic\TList;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TMixed;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TNonEmptyArray;
-use Psalm\Type\Atomic\TNonEmptyList;
-use Psalm\Type\Atomic\TNull;
-use Psalm\Type\Atomic\TObject;
-use Psalm\Type\Atomic\TObjectWithProperties;
-use Psalm\Type\Atomic\TPositiveInt;
-use Psalm\Type\Atomic\TTemplateIndexedAccess;
-use Psalm\Type\Atomic\TTemplateKeyOf;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\Atomic\TTypeAlias;
-use Psalm\Type\Atomic\TValueOfClassConstant;
-use Psalm\Type\TypeNode;
-use Psalm\Type\Union;
-
-use function array_key_exists;
-use function array_keys;
-use function array_map;
-use function array_merge;
-use function array_pop;
-use function array_shift;
-use function array_unique;
-use function array_unshift;
-use function array_values;
-use function assert;
-use function constant;
-use function count;
-use function defined;
-use function end;
-use function explode;
-use function get_class;
-use function in_array;
-use function is_int;
-use function is_numeric;
-use function preg_match;
-use function preg_replace;
-use function reset;
-use function stripslashes;
-use function strlen;
-use function strpos;
-use function strtolower;
-use function substr;
-
-class TypeParser
-{
- /**
- * Parses a string type representation
- *
- * @param list<array{0: string, 1: int, 2?: string}> $type_tokens
- * @param array{int,int}|null $php_version
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- *
- */
- public static function parseTokens(
- array $type_tokens,
- ?array $php_version = null,
- array $template_type_map = [],
- array $type_aliases = []
- ): Union {
- if (count($type_tokens) === 1) {
- $only_token = $type_tokens[0];
-
- // Note: valid identifiers can include class names or $this
- if (!preg_match('@^(\$this|\\\\?[a-zA-Z_\x7f-\xff][\\\\\-0-9a-zA-Z_\x7f-\xff]*)$@', $only_token[0])) {
- if (!is_numeric($only_token[0])
- && strpos($only_token[0], '\'') !== false
- && strpos($only_token[0], '"') !== false
- ) {
- throw new TypeParseTreeException("Invalid type '$only_token[0]'");
- }
- } else {
- $only_token[0] = TypeTokenizer::fixScalarTerms($only_token[0], $php_version);
-
- $atomic = Atomic::create($only_token[0], $php_version, $template_type_map, $type_aliases);
- $atomic->offset_start = 0;
- $atomic->offset_end = strlen($only_token[0]);
- $atomic->text = isset($only_token[2]) && $only_token[2] !== $only_token[0] ? $only_token[2] : null;
-
- return new Union([$atomic]);
- }
- }
-
- $parse_tree = (new ParseTreeCreator($type_tokens))->create();
- $codebase = ProjectAnalyzer::getInstance()->getCodebase();
- $parsed_type = self::getTypeFromTree(
- $parse_tree,
- $codebase,
- $php_version,
- $template_type_map,
- $type_aliases
- );
-
- if (!($parsed_type instanceof Union)) {
- $parsed_type = new Union([$parsed_type]);
- }
-
- return $parsed_type;
- }
-
- /**
- * @param array{int,int}|null $php_version
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- *
- * @return Atomic|Union
- */
- public static function getTypeFromTree(
- ParseTree $parse_tree,
- Codebase $codebase,
- ?array $php_version = null,
- array $template_type_map = [],
- array $type_aliases = []
- ): TypeNode {
- if ($parse_tree instanceof GenericTree) {
- return self::getTypeFromGenericTree(
- $parse_tree,
- $codebase,
- $template_type_map,
- $type_aliases
- );
- }
-
- if ($parse_tree instanceof UnionTree) {
- return self::getTypeFromUnionTree($parse_tree, $codebase, $template_type_map, $type_aliases);
- }
-
- if ($parse_tree instanceof IntersectionTree) {
- return self::getTypeFromIntersectionTree($parse_tree, $codebase, $template_type_map, $type_aliases);
- }
-
- if ($parse_tree instanceof KeyedArrayTree) {
- return self::getTypeFromKeyedArrayTree($parse_tree, $codebase, $template_type_map, $type_aliases);
- }
-
- if ($parse_tree instanceof CallableWithReturnTypeTree) {
- $callable_type = self::getTypeFromTree(
- $parse_tree->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- if (!$callable_type instanceof TCallable && !$callable_type instanceof TClosure) {
- throw new InvalidArgumentException('Parsing callable tree node should return TCallable');
- }
-
- if (!isset($parse_tree->children[1])) {
- throw new TypeParseTreeException('Invalid return type');
- }
-
- $return_type = self::getTypeFromTree(
- $parse_tree->children[1],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- $callable_type->return_type = $return_type instanceof Union ? $return_type : new Union([$return_type]);
-
- return $callable_type;
- }
-
- if ($parse_tree instanceof CallableTree) {
- return self::getTypeFromCallableTree($parse_tree, $codebase, $template_type_map, $type_aliases);
- }
-
- if ($parse_tree instanceof EncapsulationTree) {
- if (!$parse_tree->terminated) {
- throw new TypeParseTreeException('Unterminated parentheses');
- }
-
- if (!isset($parse_tree->children[0])) {
- throw new TypeParseTreeException('Empty parentheses');
- }
-
- return self::getTypeFromTree(
- $parse_tree->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- }
-
- if ($parse_tree instanceof NullableTree) {
- if (!isset($parse_tree->children[0])) {
- throw new TypeParseTreeException('Misplaced question mark');
- }
-
- $non_nullable_type = self::getTypeFromTree(
- $parse_tree->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- if ($non_nullable_type instanceof Union) {
- $non_nullable_type->addType(new TNull);
-
- return $non_nullable_type;
- }
-
- return TypeCombiner::combine([
- new TNull,
- $non_nullable_type,
- ]);
- }
-
- if ($parse_tree instanceof MethodTree
- || $parse_tree instanceof MethodWithReturnTypeTree
- ) {
- throw new TypeParseTreeException('Misplaced brackets');
- }
-
- if ($parse_tree instanceof IndexedAccessTree) {
- return self::getTypeFromIndexAccessTree($parse_tree, $template_type_map);
- }
-
- if ($parse_tree instanceof TemplateAsTree) {
- return new TTemplateParam(
- $parse_tree->param_name,
- new Union([new TNamedObject($parse_tree->as)]),
- 'class-string-map'
- );
- }
-
- if ($parse_tree instanceof ConditionalTree) {
- $template_param_name = $parse_tree->condition->param_name;
-
- if (!isset($template_type_map[$template_param_name])) {
- throw new TypeParseTreeException('Unrecognized template \'' . $template_param_name . '\'');
- }
-
- if (count($parse_tree->children) !== 2) {
- throw new TypeParseTreeException('Invalid conditional');
- }
-
- $first_class = array_keys($template_type_map[$template_param_name])[0];
-
- $conditional_type = self::getTypeFromTree(
- $parse_tree->condition->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- $if_type = self::getTypeFromTree(
- $parse_tree->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- $else_type = self::getTypeFromTree(
- $parse_tree->children[1],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- if ($conditional_type instanceof Atomic) {
- $conditional_type = new Union([$conditional_type]);
- }
-
- if ($if_type instanceof Atomic) {
- $if_type = new Union([$if_type]);
- }
-
- if ($else_type instanceof Atomic) {
- $else_type = new Union([$else_type]);
- }
-
- return new TConditional(
- $template_param_name,
- $first_class,
- $template_type_map[$template_param_name][$first_class],
- $conditional_type,
- $if_type,
- $else_type
- );
- }
-
- if (!$parse_tree instanceof Value) {
- throw new InvalidArgumentException('Unrecognised parse tree type ' . get_class($parse_tree));
- }
-
- if ($parse_tree->value[0] === '"' || $parse_tree->value[0] === '\'') {
- return new TLiteralString(substr($parse_tree->value, 1, -1));
- }
-
- if (strpos($parse_tree->value, '::')) {
- [$fq_classlike_name, $const_name] = explode('::', $parse_tree->value);
-
- if (isset($template_type_map[$fq_classlike_name]) && $const_name === 'class') {
- $first_class = array_keys($template_type_map[$fq_classlike_name])[0];
-
- return self::getGenericParamClass(
- $fq_classlike_name,
- $template_type_map[$fq_classlike_name][$first_class],
- $first_class
- );
- }
-
- if ($const_name === 'class') {
- return new TLiteralClassString($fq_classlike_name);
- }
-
- return new TClassConstant($fq_classlike_name, $const_name);
- }
-
- if (preg_match('/^\-?(0|[1-9][0-9]*)(\.[0-9]{1,})$/', $parse_tree->value)) {
- return new TLiteralFloat((float) $parse_tree->value);
- }
-
- if (preg_match('/^\-?(0|[1-9][0-9]*)$/', $parse_tree->value)) {
- return new TLiteralInt((int) $parse_tree->value);
- }
-
- if (!preg_match('@^(\$this|\\\\?[a-zA-Z_\x7f-\xff][\\\\\-0-9a-zA-Z_\x7f-\xff]*)$@', $parse_tree->value)) {
- throw new TypeParseTreeException('Invalid type \'' . $parse_tree->value . '\'');
- }
-
- $atomic_type_string = TypeTokenizer::fixScalarTerms($parse_tree->value, $php_version);
-
- $atomic_type = Atomic::create($atomic_type_string, $php_version, $template_type_map, $type_aliases);
-
- $atomic_type->offset_start = $parse_tree->offset_start;
- $atomic_type->offset_end = $parse_tree->offset_end;
- $atomic_type->text = $parse_tree->text;
-
- return $atomic_type;
- }
-
- private static function getGenericParamClass(
- string $param_name,
- Union $as,
- string $defining_class
- ): TTemplateParamClass {
- if ($as->hasMixed()) {
- return new TTemplateParamClass(
- $param_name,
- 'object',
- null,
- $defining_class
- );
- }
-
- if (!$as->isSingle()) {
- throw new TypeParseTreeException(
- 'Invalid templated classname \'' . $as . '\''
- );
- }
-
- foreach ($as->getAtomicTypes() as $t) {
- if ($t instanceof TObject) {
- return new TTemplateParamClass(
- $param_name,
- 'object',
- null,
- $defining_class
- );
- }
-
- if ($t instanceof TIterable) {
- $traversable = new TGenericObject(
- 'Traversable',
- $t->type_params
- );
-
- $as->substitute(new Union([$t]), new Union([$traversable]));
-
- return new TTemplateParamClass(
- $param_name,
- $traversable->value,
- $traversable,
- $defining_class
- );
- }
-
- if ($t instanceof TTemplateParam) {
- $t_atomic_type = count($t->as->getAtomicTypes()) === 1 ? $t->as->getSingleAtomic() : null;
-
- if (!$t_atomic_type instanceof TNamedObject) {
- $t_atomic_type = null;
- }
-
- return new TTemplateParamClass(
- $t->param_name,
- $t_atomic_type->value ?? 'object',
- $t_atomic_type,
- $t->defining_class
- );
- }
-
- if (!$t instanceof TNamedObject) {
- throw new TypeParseTreeException(
- 'Invalid templated classname \'' . $t->getId() . '\''
- );
- }
-
- return new TTemplateParamClass(
- $param_name,
- $t->value,
- $t,
- $defining_class
- );
- }
-
- throw new LogicException('Should never get here');
- }
-
- /**
- * @param non-empty-list<int> $potential_ints
- * @return non-empty-list<TLiteralInt>
- */
- public static function getComputedIntsFromMask(array $potential_ints): array
- {
- /** @var list<int> */
- $potential_values = [];
-
- foreach ($potential_ints as $ith) {
- $new_values = [];
-
- $new_values[] = $ith;
-
- if ($ith !== 0) {
- foreach ($potential_values as $potential_value) {
- $new_values[] = $ith | $potential_value;
- }
- }
-
- $potential_values = array_merge($new_values, $potential_values);
- }
-
- array_unshift($potential_values, 0);
- $potential_values = array_unique($potential_values);
-
- return array_map(
- function ($int) {
- return new TLiteralInt($int);
- },
- array_values($potential_values)
- );
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- * @return Atomic|Union
- * @throws TypeParseTreeException
- * @psalm-suppress ComplexMethod to be refactored
- */
- private static function getTypeFromGenericTree(
- GenericTree $parse_tree,
- Codebase $codebase,
- array $template_type_map,
- array $type_aliases
- ) {
- $generic_type = $parse_tree->value;
-
- $generic_params = [];
-
- foreach ($parse_tree->children as $i => $child_tree) {
- $tree_type = self::getTypeFromTree(
- $child_tree,
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- if ($generic_type === 'class-string-map'
- && $i === 0
- ) {
- if ($tree_type instanceof TTemplateParam) {
- $template_type_map[$tree_type->param_name] = ['class-string-map' => $tree_type->as];
- } elseif ($tree_type instanceof TNamedObject) {
- $template_type_map[$tree_type->value] = ['class-string-map' => Type::getObject()];
- }
- }
-
- $generic_params[] = $tree_type instanceof Union ? $tree_type : new Union([$tree_type]);
- }
-
- $generic_type_value = TypeTokenizer::fixScalarTerms($generic_type);
-
- if (($generic_type_value === 'array'
- || $generic_type_value === 'non-empty-array'
- || $generic_type_value === 'associative-array')
- && count($generic_params) === 1
- ) {
- array_unshift($generic_params, new Union([new TArrayKey]));
- } elseif (count($generic_params) === 1
- && in_array(
- $generic_type_value,
- ['iterable', 'Traversable', 'Iterator', 'IteratorAggregate', 'arraylike-object'],
- true
- )
- ) {
- array_unshift($generic_params, new Union([new TMixed]));
- } elseif ($generic_type_value === 'Generator') {
- if (count($generic_params) === 1) {
- array_unshift($generic_params, new Union([new TMixed]));
- }
-
- for ($i = 0, $l = 4 - count($generic_params); $i < $l; ++$i) {
- $generic_params[] = new Union([new TMixed]);
- }
- }
-
- if (!$generic_params) {
- throw new TypeParseTreeException('No generic params provided for type');
- }
-
- if ($generic_type_value === 'array' || $generic_type_value === 'associative-array') {
- if ($generic_params[0]->isMixed()) {
- $generic_params[0] = Type::getArrayKey();
- }
-
- if (count($generic_params) !== 2) {
- throw new TypeParseTreeException('Too many template parameters for array');
- }
-
- return new TArray($generic_params);
- }
-
- if ($generic_type_value === 'arraylike-object') {
- $traversable = new TGenericObject('Traversable', $generic_params);
- $array_acccess = new TGenericObject('ArrayAccess', $generic_params);
- $countable = new TNamedObject('Countable');
-
- $traversable->extra_types[$array_acccess->getKey()] = $array_acccess;
- $traversable->extra_types[$countable->getKey()] = $countable;
-
- return $traversable;
- }
-
- if ($generic_type_value === 'non-empty-array') {
- if ($generic_params[0]->isMixed()) {
- $generic_params[0] = Type::getArrayKey();
- }
-
- if (count($generic_params) !== 2) {
- throw new TypeParseTreeException('Too many template parameters for non-empty-array');
- }
-
- return new TNonEmptyArray($generic_params);
- }
-
- if ($generic_type_value === 'iterable') {
- return new TIterable($generic_params);
- }
-
- if ($generic_type_value === 'list') {
- return new TList($generic_params[0]);
- }
-
- if ($generic_type_value === 'non-empty-list') {
- return new TNonEmptyList($generic_params[0]);
- }
-
- if ($generic_type_value === 'class-string' || $generic_type_value === 'interface-string') {
- $class_name = (string)$generic_params[0];
-
- if (isset($template_type_map[$class_name])) {
- $first_class = array_keys($template_type_map[$class_name])[0];
-
- return self::getGenericParamClass(
- $class_name,
- $template_type_map[$class_name][$first_class],
- $first_class
- );
- }
-
- $param_union_types = array_values($generic_params[0]->getAtomicTypes());
-
- if (count($param_union_types) > 1) {
- throw new TypeParseTreeException('Union types are not allowed in class string param');
- }
-
- if (!$param_union_types[0] instanceof TNamedObject) {
- throw new TypeParseTreeException('Class string param should be a named object');
- }
-
- return new TClassString($class_name, $param_union_types[0]);
- }
-
- if ($generic_type_value === 'class-string-map') {
- if (count($generic_params) !== 2) {
- throw new TypeParseTreeException(
- 'There should only be two params for class-string-map, '
- . count($generic_params) . ' provided'
- );
- }
-
- $template_marker_parts = array_values($generic_params[0]->getAtomicTypes());
-
- $template_marker = $template_marker_parts[0];
-
- $template_as_type = null;
-
- if ($template_marker instanceof TNamedObject) {
- $template_param_name = $template_marker->value;
- } elseif ($template_marker instanceof TTemplateParam) {
- $template_param_name = $template_marker->param_name;
- $template_as_type = $template_marker->as->getSingleAtomic();
-
- if (!$template_as_type instanceof TNamedObject) {
- throw new TypeParseTreeException(
- 'Unrecognised as type'
- );
- }
- } else {
- throw new TypeParseTreeException(
- 'Unrecognised class-string-map templated param'
- );
- }
-
- return new TClassStringMap(
- $template_param_name,
- $template_as_type,
- $generic_params[1]
- );
- }
-
- if ($generic_type_value === 'key-of') {
- $param_name = (string)$generic_params[0];
-
- if (isset($template_type_map[$param_name])) {
- $defining_class = array_keys($template_type_map[$param_name])[0];
-
- return new TTemplateKeyOf(
- $param_name,
- $defining_class,
- $template_type_map[$param_name][$defining_class]
- );
- }
-
- $param_union_types = array_values($generic_params[0]->getAtomicTypes());
-
- if (count($param_union_types) > 1) {
- throw new TypeParseTreeException('Union types are not allowed in key-of type');
- }
-
- if (!$param_union_types[0] instanceof TClassConstant) {
- throw new TypeParseTreeException(
- 'Untemplated key-of param ' . $param_name . ' should be a class constant'
- );
- }
-
- return new TKeyOfClassConstant(
- $param_union_types[0]->fq_classlike_name,
- $param_union_types[0]->const_name
- );
- }
-
- if ($generic_type_value === 'value-of') {
- $param_name = (string)$generic_params[0];
-
- $param_union_types = array_values($generic_params[0]->getAtomicTypes());
-
- if (count($param_union_types) > 1) {
- throw new TypeParseTreeException('Union types are not allowed in value-of type');
- }
-
- if (!$param_union_types[0] instanceof TClassConstant) {
- throw new TypeParseTreeException(
- 'Untemplated value-of param ' . $param_name . ' should be a class constant'
- );
- }
-
- return new TValueOfClassConstant(
- $param_union_types[0]->fq_classlike_name,
- $param_union_types[0]->const_name
- );
- }
-
- if ($generic_type_value === 'int-mask') {
- $atomic_types = [];
-
- foreach ($generic_params as $generic_param) {
- if (!$generic_param->isSingle()) {
- throw new TypeParseTreeException(
- 'int-mask types must all be non-union'
- );
- }
-
- $generic_param_atomics = $generic_param->getAtomicTypes();
-
- $atomic_type = reset($generic_param_atomics);
-
- if ($atomic_type instanceof TNamedObject) {
- if (defined($atomic_type->value)) {
- /** @var mixed */
- $constant_value = constant($atomic_type->value);
-
- if (!is_int($constant_value)) {
- throw new TypeParseTreeException(
- 'int-mask types must all be integer values'
- );
- }
-
- $atomic_type = new TLiteralInt($constant_value);
- } else {
- throw new TypeParseTreeException(
- 'int-mask types must all be integer values'
- );
- }
- }
-
- if (!$atomic_type instanceof TLiteralInt
- && !($atomic_type instanceof TClassConstant
- && strpos($atomic_type->const_name, '*') === false)
- ) {
- throw new TypeParseTreeException(
- 'int-mask types must all be integer values or scalar class constants'
- );
- }
-
- $atomic_types[] = $atomic_type;
- }
-
- $potential_ints = [];
-
- foreach ($atomic_types as $atomic_type) {
- if (!$atomic_type instanceof TLiteralInt) {
- return new TIntMask($atomic_types);
- }
-
- $potential_ints[] = $atomic_type->value;
- }
-
- return new Union(self::getComputedIntsFromMask($potential_ints));
- }
-
- if ($generic_type_value === 'int-mask-of') {
- $param_union_types = array_values($generic_params[0]->getAtomicTypes());
-
- if (count($param_union_types) > 1) {
- throw new TypeParseTreeException('Union types are not allowed in value-of type');
- }
-
- $param_type = $param_union_types[0];
-
- if (!$param_type instanceof TClassConstant
- && !$param_type instanceof TValueOfClassConstant
- && !$param_type instanceof TKeyOfClassConstant
- ) {
- throw new TypeParseTreeException(
- 'Invalid reference passed to int-mask-of'
- );
- } elseif ($param_type instanceof TClassConstant
- && strpos($param_type->const_name, '*') === false
- ) {
- throw new TypeParseTreeException(
- 'Class constant passed to int-mask-of must be a wildcard type'
- );
- }
-
- return new TIntMaskOf($param_type);
- }
-
- if ($generic_type_value === 'int') {
- if (count($generic_params) !== 2) {
- throw new TypeParseTreeException('int range must have 2 params');
- }
- assert(count($parse_tree->children) === 2);
-
- $get_int_range_bound = function (ParseTree $parse_tree, Union $generic_param, string $bound_name): ?int {
- if (!$parse_tree instanceof Value
- || count($generic_param->getAtomicTypes()) > 1
- || (!$generic_param->getSingleAtomic() instanceof TLiteralInt
- && $parse_tree->value !== $bound_name
- && $parse_tree->text !== $bound_name
- )
- ) {
- throw new TypeParseTreeException(
- "Invalid type \"{$generic_param->getId()}\" as int $bound_name boundary"
- );
- }
-
- $generic_param_atomic = $generic_param->getSingleAtomic();
- return $generic_param_atomic instanceof TLiteralInt ? $generic_param_atomic->value : null;
- };
-
- $min_bound = $get_int_range_bound($parse_tree->children[0], $generic_params[0], TIntRange::BOUND_MIN);
- $max_bound = $get_int_range_bound($parse_tree->children[1], $generic_params[1], TIntRange::BOUND_MAX);
-
- if ($min_bound === null && $max_bound === null) {
- return new TInt();
- }
-
- if ($min_bound === 1 && $max_bound === null) {
- return new TPositiveInt();
- }
-
- if (is_int($min_bound) && is_int($max_bound) && $min_bound > $max_bound) {
- throw new TypeParseTreeException(
- "Min bound can't be greater than max bound, int<$min_bound, $max_bound> given"
- );
- }
-
- return new TIntRange($min_bound, $max_bound);
- }
-
- if (isset(TypeTokenizer::PSALM_RESERVED_WORDS[$generic_type_value])
- && $generic_type_value !== 'self'
- && $generic_type_value !== 'static'
- ) {
- throw new TypeParseTreeException('Cannot create generic object with reserved word');
- }
-
- return new TGenericObject($generic_type_value, $generic_params);
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- * @throws TypeParseTreeException
- */
- private static function getTypeFromUnionTree(
- UnionTree $parse_tree,
- Codebase $codebase,
- array $template_type_map,
- array $type_aliases
- ): Union {
- $has_null = false;
-
- $atomic_types = [];
-
- foreach ($parse_tree->children as $child_tree) {
- if ($child_tree instanceof NullableTree) {
- if (!isset($child_tree->children[0])) {
- throw new TypeParseTreeException('Invalid ? character');
- }
-
- $atomic_type = self::getTypeFromTree(
- $child_tree->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- $has_null = true;
- } else {
- $atomic_type = self::getTypeFromTree(
- $child_tree,
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- }
-
- if ($atomic_type instanceof Union) {
- foreach ($atomic_type->getAtomicTypes() as $type) {
- $atomic_types[] = $type;
- }
-
- continue;
- }
-
- $atomic_types[] = $atomic_type;
- }
-
- if ($has_null) {
- $atomic_types[] = new TNull;
- }
-
- if (!$atomic_types) {
- throw new TypeParseTreeException(
- 'No atomic types found'
- );
- }
-
- return TypeCombiner::combine($atomic_types);
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- * @throws TypeParseTreeException
- */
- private static function getTypeFromIntersectionTree(
- IntersectionTree $parse_tree,
- Codebase $codebase,
- array $template_type_map,
- array $type_aliases
- ): Atomic {
- $intersection_types = array_map(
- function (ParseTree $child_tree) use ($codebase, $template_type_map, $type_aliases) {
- $atomic_type = self::getTypeFromTree(
- $child_tree,
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
-
- if (!$atomic_type instanceof Atomic) {
- throw new TypeParseTreeException(
- 'Intersection types cannot contain unions'
- );
- }
-
- return $atomic_type;
- },
- $parse_tree->children
- );
-
- $first_type = reset($intersection_types);
- $last_type = end($intersection_types);
-
- $onlyTKeyedArray = $first_type instanceof TKeyedArray
- || $last_type instanceof TKeyedArray;
-
- foreach ($intersection_types as $intersection_type) {
- if (!$intersection_type instanceof TKeyedArray
- && ($intersection_type !== $first_type
- || !$first_type instanceof TArray)
- && ($intersection_type !== $last_type
- || !$last_type instanceof TArray)
- ) {
- $onlyTKeyedArray = false;
- break;
- }
- }
-
- if ($onlyTKeyedArray) {
- /** @var non-empty-array<string|int, Union> */
- $properties = [];
-
- if ($first_type instanceof TArray) {
- array_shift($intersection_types);
- } elseif ($last_type instanceof TArray) {
- array_pop($intersection_types);
- }
-
- /** @var TKeyedArray $intersection_type */
- foreach ($intersection_types as $intersection_type) {
- foreach ($intersection_type->properties as $property => $property_type) {
- if (!array_key_exists($property, $properties)) {
- $properties[$property] = clone $property_type;
- continue;
- }
-
- $intersection_type = Type::intersectUnionTypes(
- $properties[$property],
- $property_type,
- $codebase
- );
- if ($intersection_type === null) {
- throw new TypeParseTreeException(
- 'Incompatible intersection types for "' . $property . '", '
- . $properties[$property] . ' and ' . $property_type
- . ' provided'
- );
- }
- $properties[$property] = $intersection_type;
- }
- }
-
- $keyed_array = new TKeyedArray($properties);
-
- if ($first_type instanceof TArray) {
- $keyed_array->previous_key_type = $first_type->type_params[0];
- $keyed_array->previous_value_type = $first_type->type_params[1];
- } elseif ($last_type instanceof TArray) {
- $keyed_array->previous_key_type = $last_type->type_params[0];
- $keyed_array->previous_value_type = $last_type->type_params[1];
- }
-
- return $keyed_array;
- }
-
- $keyed_intersection_types = [];
-
- if ($intersection_types[0] instanceof TTypeAlias) {
- foreach ($intersection_types as $intersection_type) {
- if (!$intersection_type instanceof TTypeAlias) {
- throw new TypeParseTreeException(
- 'Intersection types with a type alias can only be comprised of other type aliases, '
- . get_class($intersection_type) . ' provided'
- );
- }
-
- $keyed_intersection_types[$intersection_type->getKey()] = $intersection_type;
- }
-
- $first_type = array_shift($keyed_intersection_types);
-
- if ($keyed_intersection_types) {
- $first_type->extra_types = $keyed_intersection_types;
- }
- } else {
- foreach ($intersection_types as $intersection_type) {
- if (!$intersection_type instanceof TIterable
- && !$intersection_type instanceof TNamedObject
- && !$intersection_type instanceof TTemplateParam
- && !$intersection_type instanceof TObjectWithProperties
- ) {
- throw new TypeParseTreeException(
- 'Intersection types must be all objects, '
- . get_class($intersection_type) . ' provided'
- );
- }
-
- $keyed_intersection_types[$intersection_type instanceof TIterable
- ? $intersection_type->getId()
- : $intersection_type->getKey()] = $intersection_type;
- }
-
- $intersect_static = false;
-
- if (isset($keyed_intersection_types['static'])) {
- unset($keyed_intersection_types['static']);
- $intersect_static = true;
- }
-
- if (!$keyed_intersection_types && $intersect_static) {
- return new TNamedObject('static');
- }
-
- $first_type = array_shift($keyed_intersection_types);
-
- if ($intersect_static
- && $first_type instanceof TNamedObject
- ) {
- $first_type->was_static = true;
- }
-
- if ($keyed_intersection_types) {
- $first_type->extra_types = $keyed_intersection_types;
- }
- }
-
- return $first_type;
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- * @return TCallable|TClosure
- * @throws TypeParseTreeException
- */
- private static function getTypeFromCallableTree(
- CallableTree $parse_tree,
- Codebase $codebase,
- array $template_type_map,
- array $type_aliases
- ) {
- $params = array_map(
- /**
- * @return FunctionLikeParameter
- */
- function (ParseTree $child_tree) use (
- $codebase,
- $template_type_map,
- $type_aliases
- ): FunctionLikeParameter {
- $is_variadic = false;
- $is_optional = false;
-
- if ($child_tree instanceof CallableParamTree) {
- if (isset($child_tree->children[0])) {
- $tree_type = self::getTypeFromTree(
- $child_tree->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- } else {
- $tree_type = new TMixed();
- }
-
- $is_variadic = $child_tree->variadic;
- $is_optional = $child_tree->has_default;
- } else {
- if ($child_tree instanceof Value && strpos($child_tree->value, '$') > 0) {
- $child_tree->value = preg_replace('/(.+)\$.*/', '$1', $child_tree->value);
- }
-
- $tree_type = self::getTypeFromTree(
- $child_tree,
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- }
-
- $tree_type = $tree_type instanceof Union ? $tree_type : new Union([$tree_type]);
-
- $param = new FunctionLikeParameter(
- '',
- false,
- $tree_type,
- null,
- null,
- $is_optional,
- false,
- $is_variadic
- );
-
- // type is not authoritative
- $param->signature_type = null;
-
- return $param;
- },
- $parse_tree->children
- );
- $pure = strpos($parse_tree->value, 'pure-') === 0 ? true : null;
-
- if (in_array(strtolower($parse_tree->value), ['closure', '\closure', 'pure-closure'], true)) {
- return new TClosure('Closure', $params, null, $pure);
- }
-
- return new TCallable('callable', $params, null, $pure);
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @throws TypeParseTreeException
- */
- private static function getTypeFromIndexAccessTree(
- IndexedAccessTree $parse_tree,
- array $template_type_map
- ): TTemplateIndexedAccess {
- if (!isset($parse_tree->children[0]) || !$parse_tree->children[0] instanceof Value) {
- throw new TypeParseTreeException('Unrecognised indexed access');
- }
-
- $offset_param_name = $parse_tree->value;
- $array_param_name = $parse_tree->children[0]->value;
-
- if (!isset($template_type_map[$offset_param_name])) {
- throw new TypeParseTreeException('Unrecognised template param ' . $offset_param_name);
- }
-
- if (!isset($template_type_map[$array_param_name])) {
- throw new TypeParseTreeException('Unrecognised template param ' . $array_param_name);
- }
-
- $offset_template_data = $template_type_map[$offset_param_name];
-
- $offset_defining_class = array_keys($offset_template_data)[0];
-
- if (!$offset_defining_class
- && isset($offset_template_data[''])
- && $offset_template_data['']->isSingle()
- ) {
- $offset_template_type = $offset_template_data['']->getSingleAtomic();
-
- if ($offset_template_type instanceof TTemplateKeyOf) {
- $offset_defining_class = $offset_template_type->defining_class;
- }
- }
-
- $array_defining_class = array_keys($template_type_map[$array_param_name])[0];
-
- if ($offset_defining_class !== $array_defining_class
- && strpos($offset_defining_class, 'fn-') !== 0
- ) {
- throw new TypeParseTreeException('Template params are defined in different locations');
- }
-
- return new TTemplateIndexedAccess(
- $array_param_name,
- $offset_param_name,
- $array_defining_class
- );
- }
-
- /**
- * @param array<string, array<string, Union>> $template_type_map
- * @param array<string, TypeAlias> $type_aliases
- * @return TCallableKeyedArray|TKeyedArray|TObjectWithProperties
- * @throws TypeParseTreeException
- */
- private static function getTypeFromKeyedArrayTree(
- KeyedArrayTree $parse_tree,
- Codebase $codebase,
- array $template_type_map,
- array $type_aliases
- ) {
- $properties = [];
- $class_strings = [];
-
- $type = $parse_tree->value;
-
- $is_tuple = true;
-
- foreach ($parse_tree->children as $i => $property_branch) {
- $class_string = false;
-
- if (!$property_branch instanceof KeyedArrayPropertyTree) {
- $property_type = self::getTypeFromTree(
- $property_branch,
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- $property_maybe_undefined = false;
- $property_key = (string)$i;
- } elseif (count($property_branch->children) === 1) {
- $property_type = self::getTypeFromTree(
- $property_branch->children[0],
- $codebase,
- null,
- $template_type_map,
- $type_aliases
- );
- $property_maybe_undefined = $property_branch->possibly_undefined;
- if (strpos($property_branch->value, '::')) {
- [$fq_classlike_name, $const_name] = explode('::', $property_branch->value);
- if ($const_name === 'class') {
- $property_key = $fq_classlike_name;
- $class_string = true;
- } else {
- $property_key = $property_branch->value;
- }
- } else {
- $property_key = $property_branch->value;
- }
- $is_tuple = false;
- } else {
- throw new TypeParseTreeException(
- 'Missing property type'
- );
- }
-
- if ($property_key[0] === '\'' || $property_key[0] === '"') {
- $property_key = stripslashes(substr($property_key, 1, -1));
- }
-
- if (!$property_type instanceof Union) {
- $property_type = new Union([$property_type]);
- }
-
- if ($property_maybe_undefined) {
- $property_type->possibly_undefined = true;
- }
-
- $properties[$property_key] = $property_type;
- if ($class_string) {
- $class_strings[$property_key] = true;
- }
- }
-
- if ($type !== 'array' && $type !== 'object' && $type !== 'callable-array') {
- throw new TypeParseTreeException('Unexpected brace character');
- }
-
- if (!$properties) {
- throw new TypeParseTreeException('No properties supplied for TKeyedArray');
- }
-
- if ($type === 'object') {
- return new TObjectWithProperties($properties);
- }
-
- if ($type === 'callable-array') {
- return new TCallableKeyedArray($properties);
- }
-
- $object_like = new TKeyedArray($properties, $class_strings);
-
- if ($is_tuple) {
- $object_like->sealed = true;
- $object_like->is_list = true;
- }
-
- return $object_like;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php
deleted file mode 100644
index 020a3b2..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php
+++ /dev/null
@@ -1,511 +0,0 @@
-<?php
-
-namespace Psalm\Internal\Type;
-
-use Psalm\Aliases;
-use Psalm\Exception\TypeParseTreeException;
-use Psalm\Internal\Type\TypeAlias\InlineTypeAlias;
-use Psalm\Type;
-
-use function array_splice;
-use function array_unshift;
-use function count;
-use function in_array;
-use function is_numeric;
-use function preg_match;
-use function preg_replace;
-use function str_split;
-use function strlen;
-use function strpos;
-use function strtolower;
-
-class TypeTokenizer
-{
- /**
- * @var array<string, bool>
- */
- public const PSALM_RESERVED_WORDS = [
- 'int' => true,
- 'string' => true,
- 'float' => true,
- 'bool' => true,
- 'false' => true,
- 'true' => true,
- 'object' => true,
- 'empty' => true,
- 'callable' => true,
- 'array' => true,
- 'non-empty-array' => true,
- 'non-empty-string' => true,
- 'non-falsy-string' => true,
- 'iterable' => true,
- 'null' => true,
- 'mixed' => true,
- 'numeric-string' => true,
- 'class-string' => true,
- 'interface-string' => true,
- 'trait-string' => true,
- 'callable-string' => true,
- 'callable-array' => true,
- 'callable-object' => true,
- 'stringable-object' => true,
- 'pure-callable' => true,
- 'pure-Closure' => true,
- 'mysql-escaped-string' => true, // deprecated
- 'html-escaped-string' => true, // deprecated
- 'literal-string' => true,
- 'non-empty-literal-string' => true,
- 'lowercase-string' => true,
- 'non-empty-lowercase-string' => true,
- 'positive-int' => true,
- 'literal-int' => true,
- 'boolean' => true,
- 'integer' => true,
- 'double' => true,
- 'real' => true,
- 'resource' => true,
- 'void' => true,
- 'self' => true,
- 'static' => true,
- 'scalar' => true,
- 'numeric' => true,
- 'no-return' => true,
- 'never-return' => true,
- 'never-returns' => true,
- 'never' => true,
- 'array-key' => true,
- 'key-of' => true,
- 'value-of' => true,
- 'non-empty-countable' => true,
- 'list' => true,
- 'non-empty-list' => true,
- 'class-string-map' => true,
- 'open-resource' => true,
- 'closed-resource' => true,
- 'associative-array' => true,
- 'arraylike-object' => true,
- 'int-mask' => true,
- 'int-mask-of' => true,
- ];
-
- /**
- * @var array<string, list<array{0: string, 1: int}>>
- */
- private static $memoized_tokens = [];
-
- /**
- * Tokenises a type string into an array of tuples where the first element
- * contains the string token and the second element contains its offset,
- *
- * @return list<array{string, int}>
- *
- * @psalm-suppress PossiblyUndefinedIntArrayOffset
- */
- public static function tokenize(string $string_type, bool $ignore_space = true): array
- {
- $type_tokens = [['', 0]];
- $was_char = false;
- $quote_char = null;
- $escaped = false;
-
- if (isset(self::$memoized_tokens[$string_type])) {
- return self::$memoized_tokens[$string_type];
- }
-
- // index of last type token
- $rtc = 0;
-
- $chars = str_split($string_type);
- $was_space = false;
-
- for ($i = 0, $c = count($chars); $i < $c; ++$i) {
- $char = $chars[$i];
-
- if (!$quote_char && $char === ' ' && $ignore_space) {
- $was_space = true;
- continue;
- }
-
- if ($was_space
- && ($char === '$'
- || ($char === '.'
- && ($chars[$i + 1] ?? null) === '.'
- && ($chars[$i + 2] ?? null) === '.'
- && ($chars[$i + 3] ?? null) === '$'))
- ) {
- $type_tokens[++$rtc] = [' ', $i - 1];
- $type_tokens[++$rtc] = ['', $i];
- } elseif ($was_space
- && ($char === 'a' || $char === 'i')
- && ($chars[$i + 1] ?? null) === 's'
- && ($chars[$i + 2] ?? null) === ' '
- ) {
- $type_tokens[++$rtc] = [$char . 's', $i - 1];
- $type_tokens[++$rtc] = ['', ++$i];
- $was_char = false;
- continue;
- } elseif ($was_char) {
- $type_tokens[++$rtc] = ['', $i];
- }
-
- if ($quote_char) {
- if ($char === $quote_char && $i > 0 && !$escaped) {
- $quote_char = null;
-
- $type_tokens[$rtc][0] .= $char;
- $was_char = true;
-
- continue;
- }
-
- $was_char = false;
-
- if ($char === '\\'
- && !$escaped
- && $i < $c - 1
- && ($chars[$i + 1] === $quote_char || $chars[$i + 1] === '\\')
- ) {
- $escaped = true;
- continue;
- }
-
- $escaped = false;
-
- $type_tokens[$rtc][0] .= $char;
-
- continue;
- }
-
- if ($char === '"' || $char === '\'') {
- if ($type_tokens[$rtc][0] === '') {
- $type_tokens[$rtc] = [$char, $i];
- } else {
- $type_tokens[++$rtc] = [$char, $i];
- }
-
- $quote_char = $char;
-
- $was_char = false;
- $was_space = false;
-
- continue;
- }
-
- if ($char === '<'
- || $char === '>'
- || $char === '|'
- || $char === '?'
- || $char === ','
- || $char === '{'
- || $char === '}'
- || $char === '['
- || $char === ']'
- || $char === '('
- || $char === ')'
- || $char === ' '
- || $char === '&'
- || $char === '='
- ) {
- if ($char === '('
- && $type_tokens[$rtc][0] === 'func_num_args'
- && isset($chars[$i + 1])
- && $chars[$i + 1] === ')'
- ) {
- $type_tokens[$rtc][0] = 'func_num_args()';
- ++$i;
-
- continue;
- }
-
- if ($type_tokens[$rtc][0] === '') {
- $type_tokens[$rtc] = [$char, $i];
- } else {
- $type_tokens[++$rtc] = [$char, $i];
- }
-
- $was_char = true;
- $was_space = false;
-
- continue;
- }
-
- if ($char === ':') {
- if ($i + 1 < $c && $chars[$i + 1] === ':') {
- if ($type_tokens[$rtc][0] === '') {
- $type_tokens[$rtc] = ['::', $i];
- } else {
- $type_tokens[++$rtc] = ['::', $i];
- }
-
- $was_char = true;
- $was_space = false;
-
- ++$i;
-
- continue;
- }
-
- if ($type_tokens[$rtc][0] === '') {
- $type_tokens[$rtc] = [':', $i];
- } else {
- $type_tokens[++$rtc] = [':', $i];
- }
-
- $was_char = true;
- $was_space = false;
-
- continue;
- }
-
- if ($char === '.') {
- if ($i + 1 < $c
- && is_numeric($chars[$i + 1])
- && $i > 0
- && is_numeric($chars[$i - 1])
- ) {
- $type_tokens[$rtc][0] .= $char;
- $was_char = false;
- $was_space = false;
-
- continue;
- }
-
- if ($i + 2 > $c || $chars[$i + 1] !== '.' || $chars[$i + 2] !== '.') {
- throw new TypeParseTreeException('Unexpected token ' . $char);
- }
-
- if ($type_tokens[$rtc][0] === '') {
- $type_tokens[$rtc] = ['...', $i];
- } else {
- $type_tokens[++$rtc] = ['...', $i];
- }
-
- $was_char = true;
- $was_space = false;
-
- $i += 2;
-
- continue;
- }
-
- $type_tokens[$rtc][0] .= $char;
- $was_char = false;
- $was_space = false;
- }
-
- /** @var list<array{0: string, 1: int}> $type_tokens */
- self::$memoized_tokens[$string_type] = $type_tokens;
-
- return $type_tokens;
- }
-
- /**
- * @param array{int,int}|null $php_version
- *
- *
- * @psalm-pure
- */
- public static function fixScalarTerms(
- string $type_string,
- ?array $php_version = null
- ): string {
- $type_string_lc = strtolower($type_string);
-
- switch ($type_string_lc) {
- case 'int':
- case 'void':
- case 'float':
- case 'string':
- case 'bool':
- case 'callable':
- case 'iterable':
- case 'array':
- case 'object':
- case 'true':
- case 'false':
- case 'null':
- case 'mixed':
- return $type_string_lc;
- }
-
- switch ($type_string) {
- case 'boolean':
- return $php_version !== null ? $type_string : 'bool';
-
- case 'integer':
- return $php_version !== null ? $type_string : 'int';
-
- case 'double':
- case 'real':
- return $php_version !== null ? $type_string : 'float';
- }
-
- return $type_string;
- }
-
- /**
- * @param array<string, mixed>|null $template_type_map
- * @param array<string, TypeAlias>|null $type_aliases
- *
- * @return list<array{0: string, 1: int, 2?: string}>
- */
- public static function getFullyQualifiedTokens(
- string $string_type,
- Aliases $aliases,
- ?array $template_type_map = null,
- ?array $type_aliases = null,
- ?string $self_fqcln = null,
- ?string $parent_fqcln = null,
- bool $allow_assertions = false
- ): array {
- $type_tokens = self::tokenize($string_type);
-
- for ($i = 0, $l = count($type_tokens); $i < $l; ++$i) {
- $string_type_token = $type_tokens[$i];
-
- if (in_array(
- $string_type_token[0],
- [
- '<', '>', '|', '?', ',', '{', '}', ':', '::', '[', ']', '(', ')', '&', '=', '...', 'as', 'is',
- ],
- true
- )) {
- continue;
- }
-
- if ($string_type_token[0][0] === '\\'
- && strlen($string_type_token[0]) === 1
- ) {
- throw new TypeParseTreeException("Backslash \"\\\" has to be part of class name.");
- }
-
- if ($string_type_token[0][0] === '"'
- || $string_type_token[0][0] === '\''
- || preg_match('/[0-9]/', $string_type_token[0][0])
- ) {
- continue;
- }
-
- if ($string_type_token[0][0] === '-' && is_numeric($string_type_token[0])) {
- continue;
- }
-
- if (isset($type_tokens[$i + 1])
- && $type_tokens[$i + 1][0] === ':'
- && isset($type_tokens[$i - 1])
- && ($type_tokens[$i - 1][0] === '{' || $type_tokens[$i - 1][0] === ',')
- ) {
- continue;
- }
-
- if ($i > 0 && $type_tokens[$i - 1][0] === '::') {
- continue;
- }
-
- if (strpos($string_type_token[0], '$')) {
- $string_type_token[0] = preg_replace('/(.+)\$.*/', '$1', $string_type_token[0]);
- }
-
- $fixed_token = !isset($type_tokens[$i + 1]) || $type_tokens[$i + 1][0] !== '('
- ? self::fixScalarTerms($string_type_token[0])
- : $string_type_token[0];
-
- $type_tokens[$i][0] = $fixed_token;
- $string_type_token[0] = $fixed_token;
-
- if ($string_type_token[0] === 'self' && $self_fqcln) {
- $type_tokens[$i][0] = $self_fqcln;
- continue;
- }
-
- if ($string_type_token[0] === 'parent' && $parent_fqcln) {
- $type_tokens[$i][0] = $parent_fqcln;
- continue;
- }
-
- if (isset(self::PSALM_RESERVED_WORDS[$string_type_token[0]])) {
- continue;
- }
-
- if (isset($template_type_map[$string_type_token[0]])) {
- continue;
- }
-
- if ($i > 1
- && ($type_tokens[$i - 2][0] === 'class-string-map')
- && ($type_tokens[$i - 1][0] === '<')
- ) {
- $template_type_map[$string_type_token[0]] = true;
- continue;
- }
-
- if (isset($type_tokens[$i + 1])
- && isset($type_tokens[$i - 1])
- && ($type_tokens[$i - 1][0] === '{' || $type_tokens[$i - 1][0] === ',')
- ) {
- $next_char = $type_tokens[$i + 1][0];
-
- if ($next_char === ':') {
- continue;
- }
-
- if ($next_char === '?' && isset($type_tokens[$i + 2]) && $type_tokens[$i + 2][0] === ':') {
- continue;
- }
- }
-
- if ($string_type_token[0][0] === '$' || $string_type_token[0][0] === ' ') {
- continue;
- }
-
- if (isset($type_tokens[$i + 1]) && $type_tokens[$i + 1][0] === '(') {
- continue;
- }
-
- if ($allow_assertions && $string_type_token[0] === 'falsy') {
- $type_tokens[$i][0] = 'false-y';
- continue;
- }
-
- if ($string_type_token[0] === 'func_num_args()'
- || $string_type_token[0] === 'PHP_MAJOR_VERSION'
- || $string_type_token[0] === 'PHP_VERSION_ID'
- ) {
- continue;
- }
-
- $type_tokens[$i][2] = $string_type_token[0];
-
- if (isset($type_aliases[$string_type_token[0]])) {
- $type_alias = $type_aliases[$string_type_token[0]];
-
- if ($type_alias instanceof InlineTypeAlias) {
- $replacement_tokens = $type_alias->replacement_tokens;
-
- array_unshift($replacement_tokens, ['(', $i]);
- $replacement_tokens[] = [')', $i];
-
- $diff = count($replacement_tokens) - 1;
-
- array_splice($type_tokens, $i, 1, $replacement_tokens);
-
- $i += $diff;
- $l += $diff;
- }
- } else {
- $type_tokens[$i][0] = Type::getFQCLNFromString(
- $string_type_token[0],
- $aliases
- );
- }
- }
-
- /** @var list<array{0: string, 1: int, 2?: string}> */
- return $type_tokens;
- }
-
- public static function clearCache(): void
- {
- self::$memoized_tokens = [];
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php
deleted file mode 100644
index 94d55b1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsClassLikeVisitor.php
+++ /dev/null
@@ -1,63 +0,0 @@
-<?php
-
-namespace Psalm\Internal\TypeVisitor;
-
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\NodeVisitor;
-use Psalm\Type\TypeNode;
-
-use function strtolower;
-
-class ContainsClassLikeVisitor extends NodeVisitor
-{
- /**
- * @var lowercase-string
- */
- private $fq_classlike_name;
-
- /**
- * @var bool
- */
- private $contains_classlike = false;
-
- /**
- * @param lowercase-string $fq_classlike_name
- */
- public function __construct(string $fq_classlike_name)
- {
- $this->fq_classlike_name = $fq_classlike_name;
- }
-
- protected function enterNode(TypeNode $type): ?int
- {
- if ($type instanceof TNamedObject) {
- if (strtolower($type->value) === $this->fq_classlike_name) {
- $this->contains_classlike = true;
- return NodeVisitor::STOP_TRAVERSAL;
- }
- }
-
- if ($type instanceof TClassConstant) {
- if (strtolower($type->fq_classlike_name) === $this->fq_classlike_name) {
- $this->contains_classlike = true;
- return NodeVisitor::STOP_TRAVERSAL;
- }
- }
-
- if ($type instanceof TLiteralClassString) {
- if (strtolower($type->value) === $this->fq_classlike_name) {
- $this->contains_classlike = true;
- return NodeVisitor::STOP_TRAVERSAL;
- }
- }
-
- return null;
- }
-
- public function matches(): bool
- {
- return $this->contains_classlike;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php
deleted file mode 100644
index e7d3f06..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/ContainsLiteralVisitor.php
+++ /dev/null
@@ -1,45 +0,0 @@
-<?php
-
-namespace Psalm\Internal\TypeVisitor;
-
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TFalse;
-use Psalm\Type\Atomic\TLiteralFloat;
-use Psalm\Type\Atomic\TLiteralInt;
-use Psalm\Type\Atomic\TLiteralString;
-use Psalm\Type\Atomic\TTrue;
-use Psalm\Type\NodeVisitor;
-use Psalm\Type\TypeNode;
-
-class ContainsLiteralVisitor extends NodeVisitor
-{
- /**
- * @var bool
- */
- private $contains_literal = false;
-
- protected function enterNode(TypeNode $type): ?int
- {
- if ($type instanceof TLiteralString
- || $type instanceof TLiteralInt
- || $type instanceof TLiteralFloat
- || $type instanceof TTrue
- || $type instanceof TFalse
- ) {
- $this->contains_literal = true;
- return NodeVisitor::STOP_TRAVERSAL;
- }
-
- if ($type instanceof TArray && $type->type_params[1]->isEmpty()) {
- $this->contains_literal = true;
- return NodeVisitor::STOP_TRAVERSAL;
- }
-
- return null;
- }
-
- public function matches(): bool
- {
- return $this->contains_literal;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php b/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php
deleted file mode 100644
index 3846e76..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/FromDocblockSetter.php
+++ /dev/null
@@ -1,31 +0,0 @@
-<?php
-
-namespace Psalm\Internal\TypeVisitor;
-
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\NodeVisitor;
-use Psalm\Type\TypeNode;
-use Psalm\Type\Union;
-
-class FromDocblockSetter extends NodeVisitor
-{
- /**
- * @psalm-suppress MoreSpecificImplementedParamType
- *
- * @param Atomic|Union $type
- * @return self::STOP_TRAVERSAL|self::DONT_TRAVERSE_CHILDREN|null
- */
- protected function enterNode(TypeNode $type): ?int
- {
- $type->from_docblock = true;
-
- if ($type instanceof TTemplateParam
- && $type->as->isMixed()
- ) {
- return NodeVisitor::DONT_TRAVERSE_CHILDREN;
- }
-
- return null;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php b/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php
deleted file mode 100644
index 1b3d365..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TemplateTypeCollector.php
+++ /dev/null
@@ -1,50 +0,0 @@
-<?php
-
-namespace Psalm\Internal\TypeVisitor;
-
-use Psalm\Type;
-use Psalm\Type\Atomic\TConditional;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\Atomic\TTemplateParamClass;
-use Psalm\Type\NodeVisitor;
-use Psalm\Type\TypeNode;
-use Psalm\Type\Union;
-
-class TemplateTypeCollector extends NodeVisitor
-{
- /**
- * @var list<TTemplateParam>
- */
- private $template_types = [];
-
- protected function enterNode(TypeNode $type): ?int
- {
- if ($type instanceof TTemplateParam) {
- $this->template_types[] = $type;
- } elseif ($type instanceof TTemplateParamClass) {
- $extends = $type->as_type;
-
- $this->template_types[] = new TTemplateParam(
- $type->param_name,
- $extends ? new Union([$extends]) : Type::getMixed(),
- $type->defining_class
- );
- } elseif ($type instanceof TConditional) {
- $this->template_types[] = new TTemplateParam(
- $type->param_name,
- Type::getMixed(),
- $type->defining_class
- );
- }
-
- return null;
- }
-
- /**
- * @return list<TTemplateParam>
- */
- public function getTemplateTypes(): array
- {
- return $this->template_types;
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeChecker.php b/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeChecker.php
deleted file mode 100644
index 7cb18b1..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeChecker.php
+++ /dev/null
@@ -1,410 +0,0 @@
-<?php
-
-namespace Psalm\Internal\TypeVisitor;
-
-use InvalidArgumentException;
-use Psalm\CodeLocation;
-use Psalm\CodeLocation\DocblockTypeLocation;
-use Psalm\Internal\Analyzer\ClassLikeAnalyzer;
-use Psalm\Internal\Analyzer\ClassLikeNameOptions;
-use Psalm\Internal\Analyzer\MethodAnalyzer;
-use Psalm\Internal\Type\Comparator\UnionTypeComparator;
-use Psalm\Internal\Type\TypeExpander;
-use Psalm\Issue\DeprecatedClass;
-use Psalm\Issue\InvalidTemplateParam;
-use Psalm\Issue\MissingTemplateParam;
-use Psalm\Issue\ReservedWord;
-use Psalm\Issue\TooManyTemplateParams;
-use Psalm\Issue\UndefinedConstant;
-use Psalm\IssueBuffer;
-use Psalm\StatementsSource;
-use Psalm\Storage\MethodStorage;
-use Psalm\Type\Atomic;
-use Psalm\Type\Atomic\TArray;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TGenericObject;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\Atomic\TResource;
-use Psalm\Type\Atomic\TTemplateParam;
-use Psalm\Type\NodeVisitor;
-use Psalm\Type\TypeNode;
-use Psalm\Type\Union;
-use ReflectionProperty;
-
-use function array_keys;
-use function array_search;
-use function count;
-use function is_array;
-use function md5;
-use function strpos;
-use function strtolower;
-
-class TypeChecker extends NodeVisitor
-{
- /**
- * @var StatementsSource
- */
- private $source;
-
- /**
- * @var CodeLocation
- */
- private $code_location;
-
- /**
- * @var array<string>
- */
- private $suppressed_issues;
-
- /**
- * @var array<string, bool>
- */
- private $phantom_classes;
-
- /**
- * @var bool
- */
- private $inferred;
-
- /**
- * @var bool
- */
- private $inherited;
-
- /**
- * @var bool
- */
- private $prevent_template_covariance;
-
- /** @var bool */
- private $has_errors = false;
-
- private $calling_method_id;
-
- /**
- * @param array<string> $suppressed_issues
- * @param array<string, bool> $phantom_classes
- */
- public function __construct(
- StatementsSource $source,
- CodeLocation $code_location,
- array $suppressed_issues,
- array $phantom_classes = [],
- bool $inferred = true,
- bool $inherited = false,
- bool $prevent_template_covariance = false,
- ?string $calling_method_id = null
- ) {
- $this->source = $source;
- $this->code_location = $code_location;
- $this->suppressed_issues = $suppressed_issues;
- $this->phantom_classes = $phantom_classes;
- $this->inferred = $inferred;
- $this->inherited = $inherited;
- $this->prevent_template_covariance = $prevent_template_covariance;
- $this->calling_method_id = $calling_method_id;
- }
-
- /**
- * @psalm-suppress MoreSpecificImplementedParamType
- *
- * @param Atomic|Union $type
- * @return self::STOP_TRAVERSAL|self::DONT_TRAVERSE_CHILDREN|null
- */
- protected function enterNode(TypeNode $type): ?int
- {
- if ($type->checked) {
- return NodeVisitor::DONT_TRAVERSE_CHILDREN;
- }
-
- if ($type instanceof TNamedObject) {
- $this->checkNamedObject($type);
- } elseif ($type instanceof TClassConstant) {
- $this->checkScalarClassConstant($type);
- } elseif ($type instanceof TTemplateParam) {
- $this->checkTemplateParam($type);
- } elseif ($type instanceof TResource) {
- $this->checkResource($type);
- } elseif ($type instanceof TArray) {
- if (count($type->type_params) > 2) {
- IssueBuffer::maybeAdd(
- new TooManyTemplateParams(
- $type->getId(). ' has too many template params, expecting 2',
- $this->code_location
- ),
- $this->suppressed_issues
- );
- }
- }
-
- $type->checked = true;
-
- return null;
- }
-
- public function hasErrors(): bool
- {
- return $this->has_errors;
- }
-
- private function checkNamedObject(TNamedObject $atomic): void
- {
- $codebase = $this->source->getCodebase();
-
- if ($this->code_location instanceof DocblockTypeLocation
- && $codebase->store_node_types
- && $atomic->offset_start !== null
- && $atomic->offset_end !== null
- ) {
- $codebase->analyzer->addOffsetReference(
- $this->source->getFilePath(),
- $this->code_location->raw_file_start + $atomic->offset_start,
- $this->code_location->raw_file_start + $atomic->offset_end,
- $atomic->value
- );
- }
-
- if ($this->calling_method_id
- && $atomic->text !== null
- ) {
- $codebase->file_reference_provider->addMethodReferenceToClassMember(
- $this->calling_method_id,
- 'use:' . $atomic->text . ':' . md5($this->source->getFilePath()),
- false
- );
- }
-
- if (!isset($this->phantom_classes[strtolower($atomic->value)]) &&
- ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $this->source,
- $atomic->value,
- $this->code_location,
- $this->source->getFQCLN(),
- $this->calling_method_id,
- $this->suppressed_issues,
- new ClassLikeNameOptions($this->inferred, false, true, true, $atomic->from_docblock)
- ) === false
- ) {
- $this->has_errors = true;
- return;
- }
-
- $fq_class_name_lc = strtolower($atomic->value);
-
- if (!$this->inherited
- && $codebase->classlike_storage_provider->has($fq_class_name_lc)
- && $this->source->getFQCLN() !== $atomic->value
- ) {
- $class_storage = $codebase->classlike_storage_provider->get($fq_class_name_lc);
-
- if ($class_storage->deprecated) {
- IssueBuffer::maybeAdd(
- new DeprecatedClass(
- 'Class ' . $atomic->value . ' is marked as deprecated',
- $this->code_location,
- $atomic->value
- ),
- $this->source->getSuppressedIssues() + $this->suppressed_issues
- );
- }
- }
-
- if ($atomic instanceof TGenericObject) {
- $this->checkGenericParams($atomic);
- }
- }
-
- private function checkGenericParams(TGenericObject $atomic): void
- {
- $codebase = $this->source->getCodebase();
-
- try {
- $class_storage = $codebase->classlike_storage_provider->get(strtolower($atomic->value));
- } catch (InvalidArgumentException $e) {
- return;
- }
-
- $expected_type_params = $class_storage->template_types ?: [];
- $expected_param_covariants = $class_storage->template_covariants;
-
- $template_type_count = count($expected_type_params);
- $template_param_count = count($atomic->type_params);
-
- if ($template_type_count > $template_param_count) {
- IssueBuffer::maybeAdd(
- new MissingTemplateParam(
- $atomic->value . ' has missing template params, expecting '
- . $template_type_count,
- $this->code_location
- ),
- $this->suppressed_issues
- );
- } elseif ($template_type_count < $template_param_count) {
- IssueBuffer::maybeAdd(
- new TooManyTemplateParams(
- $atomic->getId(). ' has too many template params, expecting '
- . $template_type_count,
- $this->code_location
- ),
- $this->suppressed_issues
- );
- }
-
- $expected_type_param_keys = array_keys($expected_type_params);
-
- foreach ($atomic->type_params as $i => $type_param) {
- $this->prevent_template_covariance = $this->source instanceof MethodAnalyzer
- && $this->source->getMethodName() !== '__construct'
- && empty($expected_param_covariants[$i]);
-
- if (isset($expected_type_param_keys[$i])) {
- $expected_template_name = $expected_type_param_keys[$i];
-
- foreach ($expected_type_params[$expected_template_name] as $defining_class => $expected_type_param) {
- $expected_type_param = TypeExpander::expandUnion(
- $codebase,
- $expected_type_param,
- $defining_class,
- null,
- null
- );
-
- $type_param = TypeExpander::expandUnion(
- $codebase,
- $type_param,
- $defining_class,
- null,
- null
- );
-
- if (!UnionTypeComparator::isContainedBy($codebase, $type_param, $expected_type_param)) {
- IssueBuffer::maybeAdd(
- new InvalidTemplateParam(
- 'Extended template param ' . $expected_template_name
- . ' of ' . $atomic->getId()
- . ' expects type '
- . $expected_type_param->getId()
- . ', type ' . $type_param->getId() . ' given',
- $this->code_location
- ),
- $this->suppressed_issues
- );
- }
- }
- }
- }
- }
-
- public function checkScalarClassConstant(TClassConstant $atomic): void
- {
- $fq_classlike_name = $atomic->fq_classlike_name === 'self'
- ? $this->source->getClassName()
- : $atomic->fq_classlike_name;
-
- if (!$fq_classlike_name) {
- return;
- }
-
- if (ClassLikeAnalyzer::checkFullyQualifiedClassLikeName(
- $this->source,
- $fq_classlike_name,
- $this->code_location,
- null,
- null,
- $this->suppressed_issues,
- new ClassLikeNameOptions($this->inferred, false, true, true, $atomic->from_docblock)
- ) === false
- ) {
- $this->has_errors = true;
- return;
- }
-
- $const_name = $atomic->const_name;
- if (strpos($const_name, '*') !== false) {
- $expanded = TypeExpander::expandAtomic(
- $this->source->getCodebase(),
- $atomic,
- $fq_classlike_name,
- $fq_classlike_name,
- null,
- true,
- true
- );
-
- $is_defined = is_array($expanded) && count($expanded) > 0;
- } else {
- $class_constant_type = $this->source->getCodebase()->classlikes->getClassConstantType(
- $fq_classlike_name,
- $atomic->const_name,
- ReflectionProperty::IS_PRIVATE,
- null
- );
-
- $is_defined = null !== $class_constant_type;
- }
-
- if (!$is_defined) {
- IssueBuffer::maybeAdd(
- new UndefinedConstant(
- 'Constant ' . $fq_classlike_name . '::' . $const_name . ' is not defined',
- $this->code_location
- ),
- $this->source->getSuppressedIssues()
- );
- }
- }
-
- public function checkTemplateParam(TTemplateParam $atomic): void
- {
- if ($this->prevent_template_covariance
- && strpos($atomic->defining_class, 'fn-') !== 0
- ) {
- $codebase = $this->source->getCodebase();
-
- $class_storage = $codebase->classlike_storage_provider->get($atomic->defining_class);
-
- $template_offset = $class_storage->template_types
- ? array_search($atomic->param_name, array_keys($class_storage->template_types), true)
- : false;
-
- if ($template_offset !== false
- && isset($class_storage->template_covariants[$template_offset])
- && $class_storage->template_covariants[$template_offset]
- ) {
- $method_storage = $this->source instanceof MethodAnalyzer
- ? $this->source->getFunctionLikeStorage()
- : null;
-
- if ($method_storage instanceof MethodStorage
- && $method_storage->mutation_free
- && !$method_storage->mutation_free_inferred
- ) {
- // do nothing
- } else {
- IssueBuffer::maybeAdd(
- new InvalidTemplateParam(
- 'Template param ' . $atomic->param_name . ' of '
- . $atomic->defining_class . ' is marked covariant and cannot be used here',
- $this->code_location
- ),
- $this->source->getSuppressedIssues()
- );
- }
- }
- }
- }
-
- public function checkResource(TResource $atomic): void
- {
- if (!$atomic->from_docblock) {
- IssueBuffer::maybeAdd(
- new ReservedWord(
- '\'resource\' is a reserved word',
- $this->code_location,
- 'resource'
- ),
- $this->source->getSuppressedIssues()
- );
- }
- }
-}
diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeScanner.php
deleted file mode 100644
index 3e74288..0000000
--- a/vendor/vimeo/psalm/src/Psalm/Internal/TypeVisitor/TypeScanner.php
+++ /dev/null
@@ -1,89 +0,0 @@
-<?php
-
-namespace Psalm\Internal\TypeVisitor;
-
-use Psalm\Internal\Codebase\Scanner;
-use Psalm\Storage\FileStorage;
-use Psalm\Type\Atomic\TClassConstant;
-use Psalm\Type\Atomic\TLiteralClassString;
-use Psalm\Type\Atomic\TNamedObject;
-use Psalm\Type\NodeVisitor;
-use Psalm\Type\TypeNode;
-
-use function strtolower;
-
-class TypeScanner extends NodeVisitor
-{
- private $scanner;
-
- private $file_storage;
-
- private $phantom_classes;
-
- /**
- * @param array<string, mixed> $phantom_classes
- */
- public function __construct(
- Scanner $scanner,
- ?FileStorage $file_storage,
- array $phantom_classes
- ) {
- $this->scanner = $scanner;
- $this->file_storage = $file_storage;
- $this->phantom_classes = $phantom_classes;
- }
-
- protected function enterNode(TypeNode $type): ?int
- {
- if ($type instanceof TNamedObject) {
- $fq_classlike_name_lc = strtolower($type->value);
-
- if (!isset($this->phantom_classes[$type->value])
- && !isset($this->phantom_classes[$fq_classlike_name_lc])
- ) {
- $this->scanner->queueClassLikeForScanning(
- $type->value,
- false,
- !$type->from_docblock,
- $this->phantom_classes
- );
-
- if ($this->file_storage) {
- $this->file_storage->referenced_classlikes[$fq_classlike_name_lc] = $type->value;
- }
- }
- }
-
- if ($type instanceof TClassConstant) {
- $this->scanner->queueClassLikeForScanning(
- $type->fq_classlike_name,
- false,
- !$type->from_docblock,
- $this->phantom_classes
- );
-
- if ($this->file_storage) {
- $fq_classlike_name_lc = strtolower($type->fq_classlike_name);
-
- $this->file_storage->referenced_classlikes[$fq_classlike_name_lc] = $type->fq_classlike_name;
- }
- }
-
- if ($type instanceof TLiteralClassString) {
- $this->scanner->queueClassLikeForScanning(
- $type->value,
- false,
- !$type->from_docblock,
- $this->phantom_classes
- );
-
- if ($this->file_storage) {
- $fq_classlike_name_lc = strtolower($type->value);
-
- $this->file_storage->referenced_classlikes[$fq_classlike_name_lc] = $type->value;
- }
- }
-
- return null;
- }
-}