Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,21 @@ private void registerNestedScanningRecipes(Recipe recipe, Accumulator acc, Execu
if (recipe instanceof ScanningRecipe && isScanningRequired(recipe)) {
acc.recipeToAccumulator.put(recipe, ((ScanningRecipe<?>) recipe).getInitialValue(ctx));
}
for (Recipe nested : recipe.getRecipeList()) {
registerNestedScanningRecipes(nested, acc, ctx);
// Recurse into DeclarativeRecipes using raw fields (preconditions + recipeList)
// to avoid getRecipeList() which wraps entries with bellwethers.
// Don't recurse into leaf ScanningRecipes — they have no precondition children.
if (recipe instanceof DeclarativeRecipe) {
DeclarativeRecipe dr = (DeclarativeRecipe) recipe;
for (Recipe precondition : dr.preconditions) {
registerNestedScanningRecipes(precondition, acc, ctx);
}
for (Recipe r : dr.recipeList) {
registerNestedScanningRecipes(r, acc, ctx);
}
} else if (!(recipe instanceof ScanningRecipe)) {
for (Recipe nested : recipe.getRecipeList()) {
registerNestedScanningRecipes(nested, acc, ctx);
}
}
}

Expand All @@ -203,8 +216,18 @@ private void scanNestedScanningRecipes(Recipe recipe, Accumulator acc, @Nullable
Object recipeAcc = acc.recipeToAccumulator.get(recipe);
scanningRecipe.getScanner(recipeAcc).visit(tree, ctx);
}
for (Recipe nested : recipe.getRecipeList()) {
scanNestedScanningRecipes(nested, acc, tree, ctx);
// Recurse into nested DeclarativeRecipes used as preconditions, scanning their
// raw preconditions and recipeList fields directly. We avoid getRecipeList() which
// wraps entries with bellwethers. Leaf ScanningRecipes (e.g. AddDependency) are
// not recursed into — their recipeList is scanned during recipe execution.
if (recipe instanceof DeclarativeRecipe) {
DeclarativeRecipe nested = (DeclarativeRecipe) recipe;
for (Recipe precondition : nested.preconditions) {
scanNestedScanningRecipes(precondition, acc, tree, ctx);
}
for (Recipe r : nested.recipeList) {
scanNestedScanningRecipes(r, acc, tree, ctx);
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1990,6 +1990,98 @@ void doNotAddDependencyToAppliedScripts() {
}


@Test
@Issue("https://github.com/openrewrite/rewrite/issues/6821")
void addDependencyOnlyIfUsingInDeclarativeRecipe() {
rewriteRun(
spec -> spec.recipeFromYaml(
"""
---
type: specs.openrewrite.org/v1beta/recipe
name: com.example.AddGuavaIfUsed
displayName: Add Guava if used
description: Adds Guava dependency when com.google.common types are used.
recipeList:
- org.openrewrite.gradle.AddDependency:
groupId: com.google.guava
artifactId: guava
version: 29.0-jre
onlyIfUsing: com.google.common.collect.*
""",
"com.example.AddGuavaIfUsed"
),
// Project that DOES use Guava - dependency should be added
mavenProject("uses-guava",
srcMainJava(
java(
"""
package com.example;

import com.google.common.collect.ImmutableList;

public class UsesGuava {
public void useGuava() {
ImmutableList<String> list = ImmutableList.of("a", "b", "c");
}
}
"""
)
),
buildGradle(
"""
plugins {
id "java-library"
}

repositories {
mavenCentral()
}
""",
"""
plugins {
id "java-library"
}

repositories {
mavenCentral()
}

dependencies {
implementation "com.google.guava:guava:29.0-jre"
}
"""
)
),
// Project that does NOT use Guava - dependency should NOT be added
mavenProject("no-guava",
srcMainJava(
java(
"""
package com.example;

public class NoGuava {
public void doesNotUseGuava() {
System.out.println("No Guava here");
}
}
"""
)
),
buildGradle(
"""
plugins {
id "java-library"
}

repositories {
mavenCentral()
}
"""
)
)
);
}

private AddDependency addDependency(@SuppressWarnings("SameParameterValue") String gav) {
return addDependency(gav, null, null);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2181,6 +2181,96 @@ void addDependencyUpgradesScopeOnlyWhenExistingDependencyHasNarrowerScopeAndEqua
)
);
}

@Issue("https://github.com/openrewrite/rewrite/issues/6821")
@Test
void addDependencyOnlyIfUsingInDeclarativeRecipe() {
rewriteRun(
spec -> spec.recipeFromYaml(
"""
---
type: specs.openrewrite.org/v1beta/recipe
name: com.example.AddGuavaIfUsed
displayName: Add Guava if used
description: Adds Guava dependency when com.google.common types are used.
recipeList:
- org.openrewrite.maven.AddDependency:
groupId: com.google.guava
artifactId: guava
version: 29.0-jre
onlyIfUsing: com.google.common.collect.*
""",
"com.example.AddGuavaIfUsed"
),
// Project that DOES use Guava - dependency should be added
mavenProject("uses-guava",
srcMainJava(
java(
"""
package com.example;

import com.google.common.collect.ImmutableList;

public class UsesGuava {
public void useGuava() {
ImmutableList<String> list = ImmutableList.of("a", "b", "c");
}
}
"""
)
),
pomXml(
"""
<project>
<groupId>com.mycompany.app</groupId>
<artifactId>uses-guava</artifactId>
<version>1</version>
</project>
""",
"""
<project>
<groupId>com.mycompany.app</groupId>
<artifactId>uses-guava</artifactId>
<version>1</version>
<dependencies>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>29.0-jre</version>
</dependency>
</dependencies>
</project>
"""
)
),
// Project that does NOT use Guava - dependency should NOT be added
// This test case fails in v3.24.0 (dependency incorrectly added)
mavenProject("no-guava",
srcMainJava(
java(
"""
package com.example;

public class NoGuava {
public void doesNotUseGuava() {
System.out.println("No Guava here");
}
}
"""
)
),
pomXml(
"""
<project>
<groupId>com.mycompany.app</groupId>
<artifactId>no-guava</artifactId>
<version>1</version>
</project>
"""
)
)
);
}

private AddDependency addDependency(@SuppressWarnings("SameParameterValue") String gav) {
return addDependency(gav, null, null, null);
Expand Down