Skip to content
Merged
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 @@ -36,6 +36,24 @@

@RequiredArgsConstructor
public class GradleParser implements Parser {
@SuppressWarnings("LanguageMismatch")
private static final String KTS_BUILD_STUBS =
"package org.gradle.api\n" +
"import org.gradle.plugin.use.PluginDependenciesSpec\n" +
"import org.gradle.plugin.use.PluginDependencySpec\n" +
"fun Project.plugins(block: PluginDependenciesSpec.() -> Unit) {}\n" +
"fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec = id(module)\n";

@SuppressWarnings("LanguageMismatch")
private static final String KTS_SETTINGS_STUBS =
"package org.gradle.api.initialization\n" +
"import org.gradle.plugin.use.PluginDependenciesSpec\n" +
"import org.gradle.plugin.use.PluginDependencySpec\n" +
"import org.gradle.plugin.management.PluginManagementSpec\n" +
"fun Settings.plugins(block: PluginDependenciesSpec.() -> Unit) {}\n" +
"fun Settings.pluginManagement(block: PluginManagementSpec.() -> Unit) {}\n" +
"fun PluginDependenciesSpec.kotlin(module: String): PluginDependencySpec = id(module)\n";
Comment on lines +39 to +55
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So there are a lot of extension functions that help with various levels of type attribution.
https://github.com/gradle/gradle/tree/master/platforms/core-configuration/kotlin-dsl/src/main/kotlin/org/gradle/kotlin/dsl

Are we thinking that we'll just add to this one by one or do we need to have something more sophisticated?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't yet have a structured idea on adding more; figured the ones we have are most often used, also in our recipes. We can add others as needed, but figured keep it small for now.


private final GradleParser.Builder base;

private @Nullable List<Path> defaultClasspath;
Expand Down Expand Up @@ -66,6 +84,7 @@ public Stream<SourceFile> parseInputs(Iterable<Input> sources, @Nullable Path re
}
kotlinBuildParser = KotlinParser.builder(base.kotlinParser)
.classpath(buildscriptClasspath)
.dependsOn(KTS_BUILD_STUBS)
.isKotlinScript(true)
.scriptImplicitReceivers("org.gradle.api.Project")
.scriptDefaultImports(DefaultImportsCustomizer.DEFAULT_IMPORTS)
Expand All @@ -91,6 +110,7 @@ public Stream<SourceFile> parseInputs(Iterable<Input> sources, @Nullable Path re
}
kotlinSettingsParser = KotlinParser.builder(base.kotlinParser)
.classpath(settingsClasspath)
.dependsOn(KTS_SETTINGS_STUBS)
.isKotlinScript(true)
.scriptImplicitReceivers("org.gradle.api.initialization.Settings")
.scriptDefaultImports(DefaultImportsCustomizer.DEFAULT_IMPORTS)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -299,8 +299,9 @@ public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, Integ
}

private K.CompilationUnit addPluginToKotlinCompilationUnit(K.CompilationUnit cu, ExecutionContext ctx) {
MethodMatcher pluginsMatcher = new MethodMatcher("*..* plugins(..)");
MethodMatcher pluginIdMatcher = new MethodMatcher("*..* id(..)");
// Wildcard type because KTS extension functions have a file-level declaring type, not Project/Settings
MethodMatcher pluginsMatcher = new MethodMatcher("* plugins(..)", false);
MethodMatcher pluginIdMatcher = new MethodMatcher("org.gradle.plugin.use.PluginDependenciesSpec id(..)", true);
AtomicBoolean hasPlugin = new JavaIsoVisitor<AtomicBoolean>() {
@Override
public J.MethodInvocation visitMethodInvocation(J.MethodInvocation method, AtomicBoolean found) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@
public class RemovePluginVisitor extends JavaIsoVisitor<ExecutionContext> {
String pluginId;

MethodMatcher buildPluginsContainerMatcher = new MethodMatcher("org.gradle.api.Project plugins(..)", true);
// Wildcard type because KTS extension functions have a file-level declaring type, not Project/Settings
MethodMatcher pluginsMatcher = new MethodMatcher("* plugins(..)", false);
MethodMatcher applyPluginMatcher = new MethodMatcher("org.gradle.api.Project apply(..)", true);
MethodMatcher buildPluginsContainerMatcher = new MethodMatcher("org.gradle.api.Project plugins(..)", true);
MethodMatcher settingsPluginsContainerMatcher = new MethodMatcher("org.gradle.api.initialization.Settings plugins(..)", true);

MethodMatcher pluginIdMatcher = new MethodMatcher("org.gradle.plugin.use.PluginDependenciesSpec id(..)", true);
Expand All @@ -45,15 +47,7 @@ public J.Block visitBlock(J.Block block, ExecutionContext executionContext) {
J.Block b = super.visitBlock(block, executionContext);

J.MethodInvocation enclosingMethod = getCursor().firstEnclosing(J.MethodInvocation.class);
if (enclosingMethod == null) {
return b;
}

boolean isKotlin = getCursor().firstEnclosing(K.CompilationUnit.class) != null;
boolean isPluginsBlock = isKotlin ?
"plugins".equals(enclosingMethod.getSimpleName()) :
buildPluginsContainerMatcher.matches(enclosingMethod) || settingsPluginsContainerMatcher.matches(enclosingMethod);
if (!isPluginsBlock) {
if (enclosingMethod == null || !isPluginsMethod(enclosingMethod)) {
return b;
}

Expand All @@ -68,28 +62,28 @@ public J.Block visitBlock(J.Block block, ExecutionContext executionContext) {
}

// Check for id("pluginId")
if (isIdMethodInvocation(m, isKotlin)) {
if (isIdMethodInvocation(m)) {
if (isPluginLiteral(m.getArguments().get(0))) {
return null;
}
}
// Check for id("pluginId").version("...")
else if (isVersionMethodInvocation(m, isKotlin)) {
else if (isVersionMethodInvocation(m)) {
if (m.getSelect() instanceof J.MethodInvocation &&
isPluginLiteral(((J.MethodInvocation) m.getSelect()).getArguments().get(0))) {
return null;
}
}
// Check for id("pluginId").apply(...) or id("pluginId").version("...").apply(...)
else if (isApplyMethodInvocation(m, isKotlin)) {
if (isIdMethodInvocation(m.getSelect(), isKotlin)) {
else if (isApplyMethodInvocation(m)) {
if (isIdMethodInvocation(m.getSelect())) {
if (m.getSelect() instanceof J.MethodInvocation &&
isPluginLiteral(((J.MethodInvocation) m.getSelect()).getArguments().get(0))) {
return null;
}
} else if (isVersionMethodInvocation(m.getSelect(), isKotlin)) {
} else if (isVersionMethodInvocation(m.getSelect())) {
if (m.getSelect() instanceof J.MethodInvocation &&
isIdMethodInvocation(((J.MethodInvocation) m.getSelect()).getSelect(), isKotlin)) {
isIdMethodInvocation(((J.MethodInvocation) m.getSelect()).getSelect())) {
if (((J.MethodInvocation) m.getSelect()).getSelect() instanceof J.MethodInvocation &&
isPluginLiteral(((J.MethodInvocation) ((J.MethodInvocation) m.getSelect()).getSelect()).getArguments().get(0))) {
return null;
Expand All @@ -102,62 +96,53 @@ else if (isApplyMethodInvocation(m, isKotlin)) {
}));
}

private boolean isPluginsMethod(J.MethodInvocation m) {
// Specifically for Kotlin type information is still missing; match strongly where possible for Groovy
return getCursor().firstEnclosing(K.CompilationUnit.class) != null ?
pluginsMatcher.matches(m) :
buildPluginsContainerMatcher.matches(m, true) || settingsPluginsContainerMatcher.matches(m);
}

private boolean isPluginLiteral(Expression expression) {
return expression instanceof J.Literal &&
pluginId.equals(((J.Literal) expression).getValue());
}

private boolean isIdMethodInvocation(@Nullable Expression expr, boolean isKotlin) {
private boolean isIdMethodInvocation(@Nullable Expression expr) {
if (!(expr instanceof J.MethodInvocation)) {
return false;
}
J.MethodInvocation m = (J.MethodInvocation) expr;
return isKotlin ?
"id".equals(m.getSimpleName()) :
pluginIdMatcher.matches(m);
return pluginIdMatcher.matches((J.MethodInvocation) expr, true);
}

private boolean isVersionMethodInvocation(@Nullable Expression expr, boolean isKotlin) {
private boolean isVersionMethodInvocation(@Nullable Expression expr) {
if (!(expr instanceof J.MethodInvocation)) {
return false;
}
J.MethodInvocation m = (J.MethodInvocation) expr;
return isKotlin ?
"version".equals(m.getSimpleName()) :
pluginVersionMatcher.matches(m);
return pluginVersionMatcher.matches((J.MethodInvocation) expr, true);
}

private boolean isApplyMethodInvocation(@Nullable Expression expr, boolean isKotlin) {
private boolean isApplyMethodInvocation(@Nullable Expression expr) {
if (!(expr instanceof J.MethodInvocation)) {
return false;
}
J.MethodInvocation m = (J.MethodInvocation) expr;
return isKotlin ?
"apply".equals(m.getSimpleName()) :
pluginApplyMatcher.matches(m);
return pluginApplyMatcher.matches((J.MethodInvocation) expr, true);
}

@Override
public J.@Nullable MethodInvocation visitMethodInvocation(J.MethodInvocation method, ExecutionContext executionContext) {
J.MethodInvocation m = super.visitMethodInvocation(method, executionContext);

boolean isKotlin = getCursor().firstEnclosing(K.CompilationUnit.class) != null;

// Check for empty plugins{} block
boolean isPluginsMethod = isKotlin ?
"plugins".equals(m.getSimpleName()) :
(buildPluginsContainerMatcher.matches(m) || settingsPluginsContainerMatcher.matches(m));

if (isPluginsMethod) {
if (isPluginsMethod(m)) {
if (m.getArguments().get(0) instanceof J.Lambda &&
((J.Lambda) m.getArguments().get(0)).getBody() instanceof J.Block &&
((J.Block) ((J.Lambda) m.getArguments().get(0)).getBody()).getStatements().isEmpty()) {
return null;
}
}
// Check for TOP-LEVEL apply plugin: "..." or apply(plugin = "...")
else if ((isKotlin && "apply".equals(m.getSimpleName())) ||
(!isKotlin && applyPluginMatcher.matches(m))) {
else if (applyPluginMatcher.matches(m, true)) {
for (Expression arg : m.getArguments()) {
if (arg instanceof G.MapEntry) {
G.MapEntry me = (G.MapEntry) arg;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
import org.openrewrite.internal.ListUtils;
import org.openrewrite.internal.StringUtils;
import org.openrewrite.java.JavaVisitor;
import org.openrewrite.java.MethodMatcher;
import org.openrewrite.java.tree.Expression;
import org.openrewrite.java.tree.J;
import org.openrewrite.maven.MavenDownloadingException;
Expand All @@ -53,6 +54,7 @@
@EqualsAndHashCode(callSuper = false)
public class UpgradePluginVersion extends ScanningRecipe<UpgradePluginVersion.DependencyVersionState> {
private static final String GRADLE_PROPERTIES_FILE_NAME = "gradle.properties";
private static final MethodMatcher VERSION_MATCHER = new MethodMatcher("org.gradle.plugin.use.PluginDependencySpec version(..)", true);

@EqualsAndHashCode.Exclude
transient MavenMetadataFailures metadataFailures = new MavenMetadataFailures(this);
Expand Down Expand Up @@ -112,7 +114,7 @@ private boolean isPluginVersion(Cursor cursor) {
return false;
}
J.MethodInvocation maybeVersion = cursor.getValue();
if (!"version".equals(maybeVersion.getSimpleName())) {
if (!VERSION_MATCHER.matches(maybeVersion, true)) {
return false;
}
Cursor parent = cursor.dropParentUntil(it -> (it instanceof J.MethodInvocation) || it == Cursor.ROOT_VALUE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ public Validated<Object> validate(ExecutionContext ctx) {

@Override
public TreeVisitor<?, ExecutionContext> getVisitor() {
MethodMatcher pluginMatcher = new MethodMatcher("PluginSpec id(..)", false);
MethodMatcher pluginMatcher = new MethodMatcher("org.gradle.plugin.use.PluginDependenciesSpec id(..)", true);

return new TreeVisitor<Tree, ExecutionContext>() {
@Override
Expand All @@ -85,7 +85,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {

@Override
public J visitMethodInvocation(J.MethodInvocation method, ExecutionContext ctx) {
if (pluginMatcher.matches(method)) {
if (pluginMatcher.matches(method, true)) {
if (method.getArguments().get(0) instanceof J.Literal &&
pluginId.equals(((J.Literal) method.getArguments().get(0)).getValue())) {
found.set(true);
Expand Down Expand Up @@ -126,11 +126,13 @@ public static List<GradlePlugin> find(J j, String pluginIdPattern) {
Function.identity()
);

MethodMatcher idMatcher = new MethodMatcher("PluginSpec id(..)", false);
MethodMatcher versionMatcher = new MethodMatcher("Plugin version(..)", false);
MethodMatcher idMatcher = new MethodMatcher("org.gradle.plugin.use.PluginDependenciesSpec id(..)", true);
MethodMatcher versionMatcher = new MethodMatcher("org.gradle.plugin.use.PluginDependencySpec version(..)", true);
List<GradlePlugin> pluginsWithVersion = plugins.stream()
.flatMap(plugin -> {
if (versionMatcher.matches(plugin) && idMatcher.matches(plugin.getSelect()) && plugin.getArguments().get(0) instanceof J.Literal) {
if (versionMatcher.matches(plugin, true) &&
plugin.getSelect() instanceof J.MethodInvocation && idMatcher.matches((J.MethodInvocation) plugin.getSelect(), true) &&
plugin.getArguments().get(0) instanceof J.Literal) {
return Stream.of(new GradlePlugin(
plugin,
requireNonNull(((J.Literal) requireNonNull(((J.MethodInvocation) plugin.getSelect()))
Expand All @@ -141,7 +143,7 @@ public static List<GradlePlugin> find(J j, String pluginIdPattern) {
return Stream.empty();
}).collect(toList());
List<GradlePlugin> pluginsWithoutVersion = plugins.stream().flatMap(plugin -> {
if (idMatcher.matches(plugin) && pluginsWithVersion.stream()
if (idMatcher.matches(plugin, true) && pluginsWithVersion.stream()
.noneMatch(it -> it.getPluginId().equals(plugin.getSimpleName()))) {
return Stream.of(new GradlePlugin(
plugin,
Expand Down