1
0
Fork 0

Solver: Prevent infinite recursion in analyzeUnsolvableRule

In complex scenarios reasons for learned rules can themselves be learned
rules caused by other learned rules which had the some of the same
reasons. In this situation iterating over all problem rules requires
keeping track of which rules have previously been analyzed to avoid and
endless loop.

Side effect is that the sorting of problems including learned rules
changes slightly.
pull/9312/head
Nils Adermann 2020-10-21 17:11:13 +02:00
parent 6e8e5f6ba6
commit 7b4cb9c370
4 changed files with 341 additions and 10 deletions

View File

@ -518,15 +518,19 @@ class Solver
* @param Problem $problem * @param Problem $problem
* @param Rule $conflictRule * @param Rule $conflictRule
*/ */
private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule) private function analyzeUnsolvableRule(Problem $problem, Rule $conflictRule, &$ruleSeen)
{ {
$ruleSeen[spl_object_hash($conflictRule)] = true;
if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) { if ($conflictRule->getType() == RuleSet::TYPE_LEARNED) {
$why = spl_object_hash($conflictRule); $why = spl_object_hash($conflictRule);
$learnedWhy = $this->learnedWhy[$why]; $learnedWhy = $this->learnedWhy[$why];
$problemRules = $this->learnedPool[$learnedWhy]; $problemRules = $this->learnedPool[$learnedWhy];
foreach ($problemRules as $problemRule) { foreach ($problemRules as $problemRule) {
$this->analyzeUnsolvableRule($problem, $problemRule); if (!isset($ruleSeen[spl_object_hash($problemRule)])) {
$this->analyzeUnsolvableRule($problem, $problemRule, $ruleSeen);
}
} }
return; return;
@ -550,7 +554,9 @@ class Solver
$problem = new Problem(); $problem = new Problem();
$problem->addRule($conflictRule); $problem->addRule($conflictRule);
$this->analyzeUnsolvableRule($problem, $conflictRule); $ruleSeen = array();
$this->analyzeUnsolvableRule($problem, $conflictRule, $ruleSeen);
$this->problems[] = $problem; $this->problems[] = $problem;
@ -576,7 +582,7 @@ class Solver
$why = $decision[Decisions::DECISION_REASON]; $why = $decision[Decisions::DECISION_REASON];
$problem->addRule($why); $problem->addRule($why);
$this->analyzeUnsolvableRule($problem, $why); $this->analyzeUnsolvableRule($problem, $why, $ruleSeen);
$literals = $why->getLiterals(); $literals = $why->getLiterals();

View File

@ -116,13 +116,13 @@ Your requirements could not be resolved to an installable set of packages.
Problem 1 Problem 1
- Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5 (conflict analysis result) - Conclusion: don't install friendsofphp/php-cs-fixer v2.10.5 (conflict analysis result)
- Conclusion: don't install symfony/console v3.4.29 (conflict analysis result) - Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5].
- Conclusion: don't install symfony/console v2.8.8 (conflict analysis result)
- Conclusion: don't install symfony/console v3.4.28 (conflict analysis result)
- illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26]. - illuminate/queue v5.2.0 requires illuminate/console 5.2.* -> satisfiable by illuminate/console[v5.2.25, v5.2.26].
- illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8]. - illuminate/console v5.2.26 requires symfony/console 2.8.* -> satisfiable by symfony/console[v2.8.7, v2.8.8].
- Conclusion: don't install symfony/console v2.8.7 (conflict analysis result) - Conclusion: don't install symfony/console v2.8.7 (conflict analysis result)
- Root composer.json requires friendsofphp/php-cs-fixer * -> satisfiable by friendsofphp/php-cs-fixer[v2.10.4, v2.10.5]. - Conclusion: don't install symfony/console v3.4.28 (conflict analysis result)
- Conclusion: don't install symfony/console v2.8.8 (conflict analysis result)
- Conclusion: don't install symfony/console v3.4.29 (conflict analysis result)
- Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0]. - Root composer.json requires illuminate/queue * -> satisfiable by illuminate/queue[v5.2.0].
- friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29]. - friendsofphp/php-cs-fixer v2.10.4 requires symfony/console ^3.2 || ^4.0 -> satisfiable by symfony/console[v3.2.13, ..., v3.4.29].
- You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29]. - You can only install one version of a package, so only one of these can be installed: symfony/console[v2.8.7, v2.8.8, v3.1.9, ..., v3.4.29].

View File

@ -0,0 +1,325 @@
--TEST--
See Github issue #9012 ( https://github.com/composer/composer/issues/9012 and https://gist.github.com/Seldaek/4e2dbc2cea4b4fd7a8207bb310ec8e34).
Recursive analysis of the same learnt rules can lead to infinite recursion in solver.
--COMPOSER--
{
"require": {
"php": ">=7.1.7",
"laravel/framework": "^6.0",
"nunomaduro/collision": "^4.0"
},
"config": {
"preferred-install": "dist",
"sort-packages": true,
"optimize-autoloader": true,
"process-timeout": 0
},
"repositories": {
"laravel/framework": {
"type": "package",
"package": [
{
"name": "laravel/framework",
"version": "v6.0.0",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "6.x-dev"
}
},
"require": {
"symfony/console": "^4.3.4"
}
}
]
},
"nunomaduro/collision": {
"type": "package",
"package": [
{
"name": "nunomaduro/collision",
"version": "v4.2.0",
"type": "library",
"extra": {
"laravel": {
"providers": [
"NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider"
]
}
},
"require": {
"symfony/console": "^5.0"
}
}
]
},
"symfony/console": {
"type": "package",
"package": [
{
"name": "symfony/console",
"version": "v5.1.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.4",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.3",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0-RC2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0-RC1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.1.0-BETA1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.11",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.10",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.9",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.8",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.4",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.3",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v5.0.0",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.15",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.14",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.13",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.12",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.11",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.10",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.9",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.8",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.4",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.3",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0-RC1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0-BETA2",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.4.0-BETA1",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.11",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.10",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.9",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.8",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.7",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.6",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.5",
"type": "library"
},
{
"name": "symfony/console",
"version": "v4.3.4",
"type": "library"
}
]
}
}
}
--RUN--
update
--EXPECT--
--EXPECT-OUTPUT--
--EXPECT-EXIT-CODE--
2

View File

@ -37,9 +37,9 @@ Updating dependencies
Your requirements could not be resolved to an installable set of packages. Your requirements could not be resolved to an installable set of packages.
Problem 1 Problem 1
- Conclusion: don't install regular/pkg 1.0.3 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.2 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.1 (conflict analysis result) - Conclusion: don't install regular/pkg 1.0.1 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.2 (conflict analysis result)
- Conclusion: don't install regular/pkg 1.0.3 (conflict analysis result)
- Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it. - Only one of these can be installed: regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3], replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. replacer/pkg replaces regular/pkg and thus cannot coexist with it.
- Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3]. - Root composer.json requires regular/pkg 1.* -> satisfiable by regular/pkg[1.0.0, 1.0.1, 1.0.2, 1.0.3].
- Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3]. - Root composer.json requires replacer/pkg 2.* -> satisfiable by replacer/pkg[2.0.0, 2.0.1, 2.0.2, 2.0.3].