Skip to content

Commit 58b3cb4

Browse files
committed
Always include Gradle API stubs in GradleParser classpath
When `settingsClasspath` or `buildscriptClasspath` is explicitly set (even to empty), `GradleParser` previously replaced the default Gradle API stubs entirely. This caused incorrect type attribution on DSL methods like `pluginManagement()`, `repositories()`, and `gradlePluginPortal()`, leading recipes like `AddSettingsPluginRepository` to fail to detect existing entries. Introduce `mergeClasspath()` which always includes the default Gradle API stubs alongside any externally-provided classpath. Add a reproducer test that sets `settingsClasspath` to empty and verifies `AddSettingsPluginRepository` correctly detects an existing `gradlePluginPortal()`.
1 parent 5b212d9 commit 58b3cb4

2 files changed

Lines changed: 60 additions & 22 deletions

File tree

rewrite-gradle/src/main/java/org/openrewrite/gradle/GradleParser.java

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@
2727
import org.openrewrite.kotlin.KotlinParser;
2828

2929
import java.nio.file.Path;
30-
import java.util.Collection;
31-
import java.util.List;
30+
import java.util.*;
3231
import java.util.stream.Stream;
3332
import java.util.stream.StreamSupport;
3433

@@ -65,51 +64,35 @@ public class GradleParser implements Parser {
6564
@Override
6665
public Stream<SourceFile> parseInputs(Iterable<Input> sources, @Nullable Path relativeTo, ExecutionContext ctx) {
6766
if (groovyBuildParser == null) {
68-
Collection<Path> buildscriptClasspath = base.buildscriptClasspath;
69-
if (buildscriptClasspath == null) {
70-
buildscriptClasspath = defaultClasspath(ctx);
71-
}
7267
groovyBuildParser = GroovyParser.builder(base.groovyParser)
73-
.classpath(buildscriptClasspath)
68+
.classpath(mergeClasspath(base.buildscriptClasspath, ctx))
7469
.compilerCustomizers(
7570
new DefaultImportsCustomizer(),
7671
config -> config.setScriptBaseClass("RewriteGradleProject")
7772
)
7873
.build();
7974
}
8075
if (kotlinBuildParser == null) {
81-
Collection<Path> buildscriptClasspath = base.buildscriptClasspath;
82-
if (buildscriptClasspath == null) {
83-
buildscriptClasspath = defaultClasspath(ctx);
84-
}
8576
kotlinBuildParser = KotlinParser.builder(base.kotlinParser)
86-
.classpath(buildscriptClasspath)
77+
.classpath(mergeClasspath(base.buildscriptClasspath, ctx))
8778
.dependsOn(KTS_BUILD_STUBS)
8879
.isKotlinScript(true)
8980
.scriptImplicitReceivers("org.gradle.api.Project")
9081
.scriptDefaultImports(DefaultImportsCustomizer.DEFAULT_IMPORTS)
9182
.build();
9283
}
9384
if (groovySettingsParser == null) {
94-
Collection<Path> settingsClasspath = base.settingsClasspath;
95-
if (settingsClasspath == null) {
96-
settingsClasspath = defaultClasspath(ctx);
97-
}
9885
groovySettingsParser = GroovyParser.builder(base.groovyParser)
99-
.classpath(settingsClasspath)
86+
.classpath(mergeClasspath(base.settingsClasspath, ctx))
10087
.compilerCustomizers(
10188
new DefaultImportsCustomizer(),
10289
config -> config.setScriptBaseClass("RewriteSettings")
10390
)
10491
.build();
10592
}
10693
if (kotlinSettingsParser == null) {
107-
Collection<Path> settingsClasspath = base.settingsClasspath;
108-
if (settingsClasspath == null) {
109-
settingsClasspath = defaultClasspath(ctx);
110-
}
11194
kotlinSettingsParser = KotlinParser.builder(base.kotlinParser)
112-
.classpath(settingsClasspath)
95+
.classpath(mergeClasspath(base.settingsClasspath, ctx))
11396
.dependsOn(KTS_SETTINGS_STUBS)
11497
.isKotlinScript(true)
11598
.scriptImplicitReceivers("org.gradle.api.initialization.Settings")
@@ -210,6 +193,21 @@ public String getDslName() {
210193
}
211194
}
212195

196+
/**
197+
* Always include the default Gradle API stubs alongside any externally-provided classpath.
198+
* The external classpath (e.g. settings buildscript dependencies) typically contains custom
199+
* plugin jars but not the Gradle API itself, which is needed for correct type attribution
200+
* of DSL methods like pluginManagement(), repositories(), gradlePluginPortal(), etc.
201+
*/
202+
private Collection<Path> mergeClasspath(@Nullable Collection<Path> provided, ExecutionContext ctx) {
203+
if (provided == null) {
204+
return defaultClasspath(ctx);
205+
}
206+
Set<Path> merged = new LinkedHashSet<>(defaultClasspath(ctx));
207+
merged.addAll(provided);
208+
return merged;
209+
}
210+
213211
private List<Path> defaultClasspath(ExecutionContext ctx) {
214212
if (defaultClasspath == null) {
215213
defaultClasspath = JavaParser.dependenciesFromResources(ctx,

rewrite-gradle/src/test/java/org/openrewrite/gradle/plugins/AddSettingsPluginRepositoryTest.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,16 @@
1717

1818
import org.junit.jupiter.api.Test;
1919
import org.openrewrite.DocumentExample;
20+
import org.openrewrite.gradle.GradleParser;
21+
import org.openrewrite.kotlin.KotlinParser;
22+
import org.openrewrite.kotlin.tree.K;
2023
import org.openrewrite.test.RewriteTest;
24+
import org.openrewrite.test.SourceSpec;
25+
import org.openrewrite.test.SourceSpecs;
26+
import org.openrewrite.test.TypeValidation;
27+
28+
import java.nio.file.Paths;
29+
import java.util.Collections;
2130

2231
import static org.openrewrite.gradle.Assertions.settingsGradle;
2332
import static org.openrewrite.gradle.Assertions.settingsGradleKts;
@@ -624,4 +633,35 @@ void addToExistingPluginManagementWithPluginsBlockKts() {
624633
)
625634
);
626635
}
636+
637+
/**
638+
* Helper to create settingsGradleKts specs using a GradleParser with empty settingsClasspath.
639+
* When settingsClasspath is explicitly set to empty (e.g. no custom plugins in the settings
640+
* buildscript), the Gradle API stubs must still be included for correct type attribution.
641+
*/
642+
private static SourceSpecs settingsGradleKtsWithEmptyClasspath(String before) {
643+
GradleParser.Builder parser = GradleParser.builder()
644+
.kotlinParser(KotlinParser.builder().logCompilationWarningsAndErrors(true))
645+
.settingsClasspath(Collections.emptyList());
646+
SourceSpec<K.CompilationUnit> gradle = new SourceSpec<>(K.CompilationUnit.class, "gradle", parser, before, null);
647+
gradle.path(Paths.get("settings.gradle.kts"));
648+
return gradle;
649+
}
650+
651+
@Test
652+
void skipWhenExistsGradlePluginPortalKtsWithEmptyClasspath() {
653+
rewriteRun(
654+
spec -> spec.recipe(new AddSettingsPluginRepository("gradlePluginPortal", null))
655+
.typeValidationOptions(TypeValidation.none()),
656+
settingsGradleKtsWithEmptyClasspath(
657+
"""
658+
pluginManagement {
659+
repositories {
660+
gradlePluginPortal()
661+
}
662+
}
663+
"""
664+
)
665+
);
666+
}
627667
}

0 commit comments

Comments
 (0)