diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java index 334470242e..953429a83c 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/JDTDelegateCommandHandler.java @@ -17,6 +17,7 @@ import java.util.Map; import org.apache.commons.lang3.StringUtils; +import org.eclipse.buildship.core.internal.util.gradle.GradleVersion; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.jdt.ls.core.internal.commands.BuildPathCommand; @@ -33,7 +34,6 @@ import org.eclipse.jdt.ls.core.internal.handlers.FormatterHandler; import org.eclipse.jdt.ls.core.internal.handlers.ResolveSourceMappingHandler; import org.eclipse.jdt.ls.core.internal.managers.GradleProjectImporter; -import org.eclipse.jdt.ls.core.internal.managers.GradleUtils; import org.eclipse.lsp4j.ResolveTypeHierarchyItemParams; import org.eclipse.lsp4j.SymbolInformation; import org.eclipse.lsp4j.TextDocumentPositionParams; @@ -126,7 +126,7 @@ public Object executeCommand(String commandId, List arguments, IProgress String projectUri = (String) arguments.get(0); String gradleVersion = arguments.size() > 1 ? (String) arguments.get(1) : null; if (gradleVersion == null) { - gradleVersion = GradleUtils.CURRENT_GRADLE; + gradleVersion = GradleVersion.current().getVersion(); } return GradleProjectImporter.upgradeGradleVersion(projectUri, gradleVersion, monitor); case "java.project.resolveWorkspaceSymbol": diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java index 8448645e0f..8b60f3616d 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.java @@ -314,6 +314,8 @@ private CorrectionMessages() { public static String NewCUCompletionUsingWizardProposal_tooltip_enclosingtype; public static String NewCUCompletionUsingWizardProposal_tooltip_package; + public static String NotAccessibleType_upgrade_Gradle_label; + public static String JavaCorrectionProcessor_addquote_description; public static String JavaCorrectionProcessor_error_quickfix_message; public static String JavaCorrectionProcessor_error_status; diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties index 8506a9adf5..f257b8e6a2 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/CorrectionMessages.properties @@ -397,6 +397,8 @@ NewCUCompletionUsingWizardProposal_dialogtitle=New NewCUCompletionUsingWizardProposal_tooltip_enclosingtype=Enclosing Type: NewCUCompletionUsingWizardProposal_tooltip_package=Package: +NotAccessibleType_upgrade_Gradle_label=Upgrade Gradle version to 7.0.1 + JavaCorrectionProcessor_addquote_description=Insert missing quote JavaCorrectionProcessor_error_quickfix_message=An error occurred while computing quick fixes. Check log for details. JavaCorrectionProcessor_error_status=Exception while processing quick fixes or quick assists diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java index 4fb93920be..72274cdf19 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/QuickFixProcessor.java @@ -43,6 +43,7 @@ import org.eclipse.jdt.ls.core.internal.corrections.proposals.CUCorrectionProposal; import org.eclipse.jdt.ls.core.internal.corrections.proposals.ChangeCorrectionProposal; import org.eclipse.jdt.ls.core.internal.corrections.proposals.GetterSetterCorrectionSubProcessor; +import org.eclipse.jdt.ls.core.internal.corrections.proposals.GradleCompatibilityProcessor; import org.eclipse.jdt.ls.core.internal.corrections.proposals.IProposalRelevance; import org.eclipse.jdt.ls.core.internal.corrections.proposals.JavadocTagsSubProcessor; import org.eclipse.jdt.ls.core.internal.corrections.proposals.LocalCorrectionsSubProcessor; @@ -53,6 +54,7 @@ import org.eclipse.jdt.ls.core.internal.handlers.OrganizeImportsHandler; import org.eclipse.jdt.ls.core.internal.text.correction.ModifierCorrectionSubProcessor; import org.eclipse.lsp4j.CodeActionKind; +import org.eclipse.lsp4j.CodeActionParams; /** */ @@ -73,7 +75,7 @@ private static int moveBack(int offset, int start, String ignoreCharacters, ICom return start; } - public List getCorrections(IInvocationContext context, IProblemLocationCore[] locations) throws CoreException { + public List getCorrections(CodeActionParams params, IInvocationContext context, IProblemLocationCore[] locations) throws CoreException { if (locations == null || locations.length == 0) { return Collections.emptyList(); } @@ -82,7 +84,7 @@ public List getCorrections(IInvocationContext context, for (int i = 0; i < locations.length; i++) { IProblemLocationCore curr = locations[i]; if (handledProblems(curr, locations, handledProblems)) { - process(context, curr, resultingCollections); + process(params, context, curr, resultingCollections); } } return resultingCollections; @@ -105,7 +107,7 @@ private static boolean handledProblems(IProblemLocationCore location, IProblemLo return handledProblems.add(problemId); } - private void process(IInvocationContext context, IProblemLocationCore problem, Collection proposals) throws CoreException { + private void process(CodeActionParams params, IInvocationContext context, IProblemLocationCore problem, Collection proposals) throws CoreException { int id = problem.getProblemId(); if (id == 0) { // no proposals for none-problem locations return; @@ -551,6 +553,9 @@ private void process(IInvocationContext context, IProblemLocationCore problem, C case IProblem.PotentiallyUnclosedCloseable: LocalCorrectionsSubProcessor.getTryWithResourceProposals(context, problem, proposals); break; + case IProblem.NotAccessibleType: + GradleCompatibilityProcessor.getGradleCompatibilityProposals(context, problem, proposals); + break; // case IProblem.MissingSynchronizedModifierInInheritedMethod: // ModifierCorrectionSubProcessor.addSynchronizedMethodProposal(context, // problem, proposals); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/GradleCompatibilityProcessor.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/GradleCompatibilityProcessor.java new file mode 100644 index 0000000000..6e510d96a8 --- /dev/null +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/corrections/proposals/GradleCompatibilityProcessor.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2022 Microsoft Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * Microsoft Corporation - initial API and implementation +*******************************************************************************/ +package org.eclipse.jdt.ls.core.internal.corrections.proposals; + +import java.net.URI; +import java.net.URISyntaxException; +import java.util.Arrays; +import java.util.Collection; + +import org.eclipse.buildship.core.internal.CorePlugin; +import org.eclipse.buildship.core.internal.preferences.PersistentModel; +import org.eclipse.buildship.core.internal.util.gradle.GradleVersion; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.Path; +import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.core.IModuleDescription; +import org.eclipse.jdt.core.IPackageFragmentRoot; +import org.eclipse.jdt.internal.core.JarPackageFragmentRoot; +import org.eclipse.jdt.internal.ui.text.correction.IProblemLocationCore; +import org.eclipse.jdt.ls.core.internal.ProjectUtils; +import org.eclipse.jdt.ls.core.internal.commands.ProjectCommand; +import org.eclipse.jdt.ls.core.internal.commands.ProjectCommand.ClasspathResult; +import org.eclipse.jdt.ls.core.internal.corrections.CorrectionMessages; +import org.eclipse.jdt.ls.core.internal.corrections.IInvocationContext; +import org.eclipse.jdt.ls.core.internal.managers.GradleUtils; +import org.eclipse.jdt.ls.core.internal.text.correction.CUCorrectionCommandProposal; +import org.eclipse.lsp4j.CodeActionKind; + +public class GradleCompatibilityProcessor { + public static void getGradleCompatibilityProposals(IInvocationContext context, IProblemLocationCore problem, Collection proposals) { + IJavaProject javaProject = context.getCompilationUnit().getJavaProject(); + if (javaProject == null) { + return; + } + IProject project = javaProject.getProject(); + if (!ProjectUtils.isGradleProject(project)) { + return; + } + PersistentModel model = CorePlugin.modelPersistence().loadModel(project); + if (!model.isPresent()) { + return; + } + GradleVersion gradleVersion = model.getGradleVersion(); + if (gradleVersion != null && gradleVersion.compareTo(GradleVersion.version(GradleUtils.JPMS_SUPPORTED_VERSION)) < 0) { + IResource resource = javaProject.getResource(); + if (resource == null) { + return; + } + URI uri = resource.getLocationURI(); + if (uri == null) { + return; + } + try { + ClasspathResult result = ProjectCommand.getClasspathsFromJavaProject(javaProject, new ProjectCommand.ClasspathOptions()); + IModuleDescription moduleDescription = javaProject.getModuleDescription(); + if (moduleDescription == null) { + addProposalForNonModulerProject(result, context, uri, proposals); + } else { + addProposalForModulerProject(javaProject, result, context, uri, proposals); + } + } catch (CoreException | URISyntaxException e) { + return; + } + } + } + + /** + * Add proposal for a non-modular project. For a project doesn't have a module + * description file (module-info.java), there should be nothing in the module + * path. See: https://github.com/gradle/gradle/issues/16922 + * + * @param result + * the classpath result + * @param context + * the invocation context + * @param uri + * the project uri + * @param proposals + * the current proposals + */ + private static void addProposalForNonModulerProject(ClasspathResult result, IInvocationContext context, URI uri, Collection proposals) { + if (result.modulepaths.length > 0) { + addProposal(context, uri, proposals); + } + } + + /** + * Add proposal for a modular project. For a project has a module description + * file (module-info.java), we should check that all the dependencies in + * classpath don't contain module description (either description or automatic + * description, the supported inferred modules in Gradle) See: + * https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_modular) + * + * @param javaProject + * the java project + * @param result + * the classpath result + * @param context + * the invocation context + * @param uri + * the project uri + * @param proposals + * the current proposals + */ + private static void addProposalForModulerProject(IJavaProject javaProject, ClasspathResult result, IInvocationContext context, URI uri, Collection proposals) { + for (String classpath : result.classpaths) { + try { + IPackageFragmentRoot packageFragmentRoot = javaProject.findPackageFragmentRoot(new Path(classpath)); + if (packageFragmentRoot instanceof JarPackageFragmentRoot) { + // try to get module description + IModuleDescription jarModuleDescription = ((JarPackageFragmentRoot) packageFragmentRoot).getModuleDescription(); + if (jarModuleDescription == null) { + // fall back to get automatic module description + jarModuleDescription = ((JarPackageFragmentRoot) packageFragmentRoot).getAutomaticModuleDescription(); + } + if (jarModuleDescription != null) { + addProposal(context, uri, proposals); + break; + } + } + } catch (CoreException e) { + continue; + } + } + } + + private static void addProposal(IInvocationContext context, URI uri, Collection proposals) { + proposals.add(new CUCorrectionCommandProposal(CorrectionMessages.NotAccessibleType_upgrade_Gradle_label, CodeActionKind.QuickFix, context.getCompilationUnit(), IProposalRelevance.CONFIGURE_BUILD_PATH, "java.project.upgradeGradle", + Arrays.asList(uri.toString(), GradleUtils.JPMS_SUPPORTED_VERSION))); + } +} diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java index 9f2fcfbd73..0c353e8e63 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/handlers/CodeActionHandler.java @@ -159,7 +159,7 @@ public List> getCodeActionCommands(CodeActionParams if (containsKind(codeActionKinds, CodeActionKind.QuickFix)) { try { codeActions.addAll(nonProjectFixProcessor.getCorrections(params, context, locations)); - List quickfixProposals = this.quickFixProcessor.getCorrections(context, locations); + List quickfixProposals = this.quickFixProcessor.getCorrections(params, context, locations); this.quickFixProcessor.addAddAllMissingImportsProposal(context, quickfixProposals); Set quickSet = new TreeSet<>(comparator); quickSet.addAll(quickfixProposals); diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java index 1ad1d16b26..18ef9ef248 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleProjectImporter.java @@ -45,6 +45,7 @@ import org.eclipse.buildship.core.SynchronizationResult; import org.eclipse.buildship.core.WrapperGradleDistribution; import org.eclipse.buildship.core.internal.CorePlugin; +import org.eclipse.buildship.core.internal.DefaultGradleBuild; import org.eclipse.buildship.core.internal.preferences.PersistentModel; import org.eclipse.buildship.core.internal.util.gradle.GradleVersion; import org.eclipse.core.resources.IFile; @@ -247,7 +248,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException { } } if (JavaLanguageServerPlugin.getProjectsManager() != null && JavaLanguageServerPlugin.getProjectsManager().getConnection() != null) { - GradleCompatibilityInfo info = new GradleCompatibilityInfo(gradleStatus.getProjectUri(), gradleStatus.getMessage(), gradleStatus.getHighestJavaVersion(), GradleUtils.CURRENT_GRADLE); + GradleCompatibilityInfo info = new GradleCompatibilityInfo(gradleStatus.getProjectUri(), gradleStatus.getMessage(), gradleStatus.getHighestJavaVersion(), GradleVersion.current().getVersion()); EventNotification notification = new EventNotification().withType(EventType.IncompatibleGradleJdkIssue).withData(info); JavaLanguageServerPlugin.getProjectsManager().getConnection().sendEventNotification(notification); } @@ -271,7 +272,7 @@ public void importToWorkspace(IProgressMonitor monitor) throws CoreException { IMarker marker = ResourceUtils.createWarningMarker(GRADLE_UPGRADE_WRAPPER_MARKER_ID, wrapperProperties, GRADLE_INVALID_TYPE_CODE_MESSAGE, INVALID_TYPE_CODE_ID, reader.getLineNumber()); marker.setAttribute(GRADLE_MARKER_COLUMN_START, 0); marker.setAttribute(GRADLE_MARKER_COLUMN_END, line.length()); - UpgradeGradleWrapperInfo info = new UpgradeGradleWrapperInfo(gradleStatus.getProjectUri(), GRADLE_INVALID_TYPE_CODE_MESSAGE, GradleUtils.CURRENT_GRADLE); + UpgradeGradleWrapperInfo info = new UpgradeGradleWrapperInfo(gradleStatus.getProjectUri(), GRADLE_INVALID_TYPE_CODE_MESSAGE, GradleVersion.current().getVersion()); EventNotification notification = new EventNotification().withType(EventType.UpgradeGradleWrapper).withData(info); JavaLanguageServerPlugin.getProjectsManager().getConnection().sendEventNotification(notification); break; @@ -531,9 +532,26 @@ public boolean accept(File dir, String name) { return shouldSynchronize; } - public static boolean upgradeGradleVersion(String projectUri, String gradleVersion, IProgressMonitor monitor) { + /** + * update the gradle wrapper to the given version + * @param projectUri uri of the project + * @param gradleVersion the target gradle version + * @param monitor the progress monitor + * @return the path to the new gradle-wrapper.properties file + */ + public static String upgradeGradleVersion(String projectUri, String gradleVersion, IProgressMonitor monitor) { String newDistributionUrl = String.format("https://services.gradle.org/distributions/gradle-%s-bin.zip", gradleVersion); Path projectFolder = Paths.get(URI.create(projectUri)); + // try to get root project directory + IProject project = ProjectUtils.getProjectFromUri(projectUri); + Optional build = GradleCore.getWorkspace().getBuild(project); + if (build.isEmpty()) { + return null; + } + GradleBuild gradleBuild = build.get(); + if (gradleBuild instanceof DefaultGradleBuild) { + projectFolder = ((DefaultGradleBuild) gradleBuild).getBuildConfig().getRootProjectDirectory().toPath(); + } File propertiesFile = projectFolder.resolve("gradle").resolve("wrapper").resolve("gradle-wrapper.properties").toFile(); Properties properties = new Properties(); if (propertiesFile.exists()) { @@ -541,7 +559,7 @@ public static boolean upgradeGradleVersion(String projectUri, String gradleVersi properties.load(stream); properties.setProperty("distributionUrl", newDistributionUrl); } catch (IOException e) { - return false; + return null; } } else { properties.setProperty("distributionBase", "GRADLE_USER_HOME"); @@ -553,10 +571,8 @@ public static boolean upgradeGradleVersion(String projectUri, String gradleVersi try { properties.store(new FileOutputStream(propertiesFile), null); } catch (Exception e) { - return false; + return null; } - BuildConfiguration build = getBuildConfiguration(projectFolder); - GradleBuild gradleBuild = GradleCore.getWorkspace().createBuild(build); try { gradleBuild.withConnection(connection -> { connection.newBuild().forTasks("wrapper").run(); @@ -565,7 +581,7 @@ public static boolean upgradeGradleVersion(String projectUri, String gradleVersi } catch (Exception e) { // Do nothing } - return true; + return propertiesFile.getAbsolutePath(); } /** diff --git a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleUtils.java b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleUtils.java index 67d3233db5..48b086abfe 100644 --- a/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleUtils.java +++ b/org.eclipse.jdt.ls.core/src/org/eclipse/jdt/ls/core/internal/managers/GradleUtils.java @@ -47,9 +47,11 @@ public class GradleUtils { public static String MAX_SUPPORTED_JAVA = JavaCore.VERSION_17; - public static String CURRENT_GRADLE = "7.3.1"; // see https://github.com/gradle/gradle/pull/17397 public static String INVALID_TYPE_FIXED_VERSION = "7.2"; + // see https://github.com/gradle/gradle/issues/890 + // see https://github.com/gradle/gradle/issues/16922 + public static String JPMS_SUPPORTED_VERSION = "7.0.1"; private static final String MESSAGE_DIGEST_ALGORITHM = "SHA-256";