From 9f40d505357e12e0e00b6bd3e2e32426d51bbb68 Mon Sep 17 00:00:00 2001 From: Tobias Hahnen Date: Thu, 7 May 2026 22:14:49 +0200 Subject: [PATCH 1/5] Do not require CFamily plug-in on forks When having a fork of the repository, e.g. to contribute to it, the CFamily analyzer cannot be downloaded ("-Dskip-sonarsource-repo") since it is closed source. In this case, running the plug-in would not be possible, neither from the IDE nor the packaged version. --- .../core/internal/backend/SonarLintBackendService.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java index 32d3d139c..cbea57c83 100644 --- a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java +++ b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java @@ -187,7 +187,14 @@ protected IStatus run(IProgressMonitor monitor) { embeddedPlugins.put("web", requireNonNull(PluginPathHelper.findEmbeddedHtmlPlugin(), "HTML plugin not found")); embeddedPlugins.put("xml", requireNonNull(PluginPathHelper.findEmbeddedXmlPlugin(), "XML plugin not found")); embeddedPlugins.put("text", requireNonNull(PluginPathHelper.findEmbeddedSecretsPlugin(), "Secrets plugin not found")); - embeddedPlugins.put("cpp", requireNonNull(PluginPathHelper.findEmbeddedCFamilyPlugin(), "CFamily plugin not found")); + + // In forks of SonarQube for Eclipse the CFamily analyzer is not available (Maven option + // "-Dskip-sonarsource-repo"). In this case the plug-in would not start when it is + // required, we therefore keep this not strictly required. + var cFamilyPlugin = PluginPathHelper.findEmbeddedCFamilyPlugin(); + if (cFamilyPlugin != null) { + embeddedPlugins.put("cpp", cFamilyPlugin); + } var sqConnections = ConnectionSynchronizer.buildSqConnectionDtos(); var scConnections = ConnectionSynchronizer.buildScConnectionDtos(); From d077d60bbed05af1d8f4ca6b5fd69fe995e6eb18 Mon Sep 17 00:00:00 2001 From: Tobias Hahnen Date: Thu, 7 May 2026 22:22:13 +0200 Subject: [PATCH 2/5] Quick fix to ignore markers on SonarQube related "SuppressWarnings" Eclipse JDT only accepts specific values for the "SuppressWarnings" annotation, that also cannot be extended. For SonarQube analyzers linked to Java (incl. Security, DBD, Architecture) one can use the annotation to suppress rule violations on different levels. Eclipse JDT raising warning markers on these suppressed warnings as "unsupported" is highly annoying and there should be an option to ignore the compiler warning. This adds a quick fix to this warning, that will configure Eclipse JDT to ignore these markers. --- .../SuppressWarningsQuickFixTest.java | 99 ++++++++++++ org.sonarlint.eclipse.jdt/plugin.xml | 11 ++ .../JavaProjectConfiguratorExtension.java | 2 +- .../internal/SuppressWarningsQuickFix.java | 52 +++++++ .../SuppressWarningsQuickFixGenerator.java | 71 +++++++++ org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF | 2 +- .../ui/internal/ExtendedSonarLintImages.java | 141 ++++++++++++++++++ .../eclipse/ui/internal/SonarLintImages.java | 141 +----------------- .../internal/rule/LegacyRuleHeaderPanel.java | 6 +- .../ui/internal/rule/RuleHeaderPanel.java | 4 +- .../views/issues/IssueDescriptionField.java | 13 +- 11 files changed, 396 insertions(+), 146 deletions(-) create mode 100644 org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java create mode 100644 org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java create mode 100644 org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixGenerator.java create mode 100644 org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java diff --git a/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java b/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java new file mode 100644 index 000000000..89f8f9768 --- /dev/null +++ b/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java @@ -0,0 +1,99 @@ +/* + * SonarLint for Eclipse + * Copyright (C) SonarSource Sàrl + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarlint.eclipse.jdt.internal; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.jdt.core.JavaCore; +import org.junit.AfterClass; +import org.junit.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class SuppressWarningsQuickFixTest { + private static final SuppressWarningsQuickFixGenerator generator = new SuppressWarningsQuickFixGenerator(); + + @AfterClass + public static void after() { + var options = JavaCore.getOptions(); + options.put(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN, JavaCore.WARNING); + JavaCore.setOptions(options); + } + + @Test + public void testIrrelevantMarkers() throws CoreException { + var marker = mock(IMarker.class); + when(marker.getType()).thenReturn("noJavaProblem"); + + assertThat(generator.hasResolutions(marker)).isFalse(); + assertThat(generator.getResolutions(marker).length).isEqualTo(0); + + when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(null); + + assertThat(generator.hasResolutions(marker)).isFalse(); + + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL); + + assertThat(generator.hasResolutions(marker)).isFalse(); + } + + @Test + public void testRelevantMarkers() throws CoreException { + var marker = mock(IMarker.class); + when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); + + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL + + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_ISSUE); + assertThat(generator.hasResolutions(marker)).isTrue(); + + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL + + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_SECURITY_ISSUE); + assertThat(generator.hasResolutions(marker)).isTrue(); + + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL + + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_BUGS_ISSUE); + assertThat(generator.hasResolutions(marker)).isTrue(); + + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL + + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_ARCHITECTURE_ISSUE); + assertThat(generator.hasResolutions(marker)).isTrue(); + } + + @Test + public void testQuickFix() throws CoreException { + var marker = mock(IMarker.class); + when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL + + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_ISSUE); + + var quickFixes = generator.getResolutions(marker); + assertThat(quickFixes.length).isEqualTo(1); + + var quickFix = (SuppressWarningsQuickFix) quickFixes[0]; + assertThat(quickFix.getLabel()).contains("SonarQube"); + assertThat(quickFix.getDescription()).contains("SonarQube"); + + quickFix.run(marker); + assertThat(JavaCore.getOption(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN)).isEqualTo(JavaCore.IGNORE); + } +} diff --git a/org.sonarlint.eclipse.jdt/plugin.xml b/org.sonarlint.eclipse.jdt/plugin.xml index 5d1ef0187..19b703612 100644 --- a/org.sonarlint.eclipse.jdt/plugin.xml +++ b/org.sonarlint.eclipse.jdt/plugin.xml @@ -36,4 +36,15 @@ class="org.sonarlint.eclipse.jdt.internal.JavaProjectConfiguratorExtension"> + + + + + diff --git a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/JavaProjectConfiguratorExtension.java b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/JavaProjectConfiguratorExtension.java index cb7d01a59..807acd208 100644 --- a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/JavaProjectConfiguratorExtension.java +++ b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/JavaProjectConfiguratorExtension.java @@ -63,7 +63,7 @@ public JavaProjectConfiguratorExtension() { javaProjectConfigurator = jdtPresent ? new JdtUtils() : null; } - private static boolean isJdtPresent() { + static boolean isJdtPresent() { return isClassPresentAtRuntime("org.eclipse.jdt.core.JavaCore"); } diff --git a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java new file mode 100644 index 000000000..e641b3197 --- /dev/null +++ b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java @@ -0,0 +1,52 @@ +/* + * SonarLint for Eclipse + * Copyright (C) SonarSource Sàrl + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarlint.eclipse.jdt.internal; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.jdt.core.JavaCore; +import org.eclipse.swt.graphics.Image; +import org.eclipse.ui.IMarkerResolution2; +import org.sonarlint.eclipse.ui.internal.SonarLintImages; + +public class SuppressWarningsQuickFix implements IMarkerResolution2 { + @Override + public String getLabel() { + return "Disable warnings on \"@SuppressWarnings(...)\" for SonarQube issues"; + } + + @Override + public String getDescription() { + return "SonarQube uses custom ids that can be set in \"@SuppressWarnings\" to be picked up by the language " + + "analyzers to suppress potential rule violations. Eclipse JDT does not recognize these IDs and reports them " + + "as unsupported. This quick fix will disable this compiler warning."; + } + + @Override + public Image getImage() { + return SonarLintImages.RESOLUTION_QUICKFIX_CHANGE; + } + + @Override + public void run(IMarker marker) { + var options = JavaCore.getOptions(); + options.put(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN, JavaCore.IGNORE); + JavaCore.setOptions(options); + } +} diff --git a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixGenerator.java b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixGenerator.java new file mode 100644 index 000000000..cf60166c4 --- /dev/null +++ b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixGenerator.java @@ -0,0 +1,71 @@ +/* + * SonarLint for Eclipse + * Copyright (C) SonarSource Sàrl + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +package org.sonarlint.eclipse.jdt.internal; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.ui.IMarkerResolution; +import org.eclipse.ui.IMarkerResolutionGenerator2; +import org.sonarlint.eclipse.core.SonarLintLogger; + +/** + * Generator for the Quick Fox on the "Unsupported @SuppressWarnings(...)" marker that is added when the value(s) + * provided is one indicating a SonarQube-related rule - these are unknown to Eclipse JDT. + */ +public class SuppressWarningsQuickFixGenerator implements IMarkerResolutionGenerator2 { + static final String JAVA_PROBLEM_TYPE = "org.eclipse.jdt.core.problem"; //$NON-NLS-1$ + static final String MARKER_MESSAGE_GENERAL = "Unsupported @SuppressWarnings"; //$NON-NLS-1$ + static final String MARKER_MESSAGE_JAVA_ISSUE = "java:"; //$NON-NLS-1$ + static final String MARKER_MESSAGE_JAVA_SECURITY_ISSUE = "javasecurity:"; //$NON-NLS-1$ + static final String MARKER_MESSAGE_JAVA_BUGS_ISSUE = "javabugs:"; //$NON-NLS-1$ + static final String MARKER_MESSAGE_JAVA_ARCHITECTURE_ISSUE = "javaarchitecture:"; //$NON-NLS-1$ + + @Override + public IMarkerResolution[] getResolutions(IMarker marker) { + if (isSuppressWarningsJavaProblem(marker)) { + return new IMarkerResolution[] {new SuppressWarningsQuickFix()}; + } + return new IMarkerResolution[0]; + } + + @Override + public boolean hasResolutions(IMarker marker) { + return isSuppressWarningsJavaProblem(marker); + } + + private boolean isSuppressWarningsJavaProblem(IMarker marker) { + if (!JavaProjectConfiguratorExtension.isJdtPresent()) { + return false; + } + + try { + var isJavaProblem = marker.getType().equals(JAVA_PROBLEM_TYPE); + var message = (String) marker.getAttribute(IMarker.MESSAGE); + return isJavaProblem && message != null && message.contains(MARKER_MESSAGE_GENERAL) + && (message.toLowerCase().contains(MARKER_MESSAGE_JAVA_ISSUE) + || message.toLowerCase().contains(MARKER_MESSAGE_JAVA_SECURITY_ISSUE) + || message.toLowerCase().contains(MARKER_MESSAGE_JAVA_BUGS_ISSUE) + || message.toLowerCase().contains(MARKER_MESSAGE_JAVA_ARCHITECTURE_ISSUE)); + } catch (CoreException err) { + SonarLintLogger.get().error(err.getMessage(), err); + return false; + } + } +} diff --git a/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF b/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF index b67b8624d..aae740bd3 100644 --- a/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF +++ b/org.sonarlint.eclipse.ui/META-INF/MANIFEST.MF @@ -28,7 +28,7 @@ Require-Bundle: org.eclipse.core.runtime, org.eclipse.compare, org.eclipse.core.expressions, org.sonarsource.sonarlint.core.sonarlint-java-client-osgi;bundle-version="[11.2.0,11.3.0)" -Export-Package: org.sonarlint.eclipse.ui.internal;x-friends:="org.sonarlint.eclipse.core.tests", +Export-Package: org.sonarlint.eclipse.ui.internal;x-friends:="org.sonarlint.eclipse.core.tests,org.sonarlint.eclipse.jdt", org.sonarlint.eclipse.ui.internal.backend;x-friends:="org.sonarlint.eclipse.core.tests", org.sonarlint.eclipse.ui.internal.notifications;x-friends:="org.sonarlint.eclipse.core.tests", org.sonarlint.eclipse.ui.internal.popup;x-friends:="org.sonarlint.eclipse.core.tests", diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java new file mode 100644 index 000000000..1726d39f8 --- /dev/null +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java @@ -0,0 +1,141 @@ +package org.sonarlint.eclipse.ui.internal; + +import java.util.Locale; +import org.eclipse.jdt.annotation.Nullable; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.Image; +import org.sonarlint.eclipse.core.internal.markers.MarkerUtils.FindingMatchingStatus; +import org.sonarsource.sonarlint.core.rpc.protocol.common.ImpactSeverity; +import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; +import org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType; + +/** + * Purpose of this class is to remove the dependencies to "SonarLint Core" from the base "SonarLintImages" class so it + * can be imported safely in the "org.sonarlint.eclipse.jdt" bundle. + */ +public final class ExtendedSonarLintImages extends SonarLintImages { + private ExtendedSonarLintImages() { + super(); + } + + /** + * Mapping of the matching status of an issue to the specific image + * + * @param matchingStatus specific matching status of an issue + * @param isResolved resolution status of an issue + * @return the corresponding connection mode icon (including standalone) + */ + private static ImageDescriptor matchingStatusToImageDescriptor(FindingMatchingStatus matchingStatus, + boolean isResolved) { + if (matchingStatus == FindingMatchingStatus.NOT_MATCHED) { + return isResolved ? STANDALONE_RESOLVED_16 : STANDALONE_16; + } else if (matchingStatus == FindingMatchingStatus.MATCHED_WITH_SC) { + return isResolved ? SONARCLOUD_RESOLVED_16 : SONARCLOUD_16; + } + return isResolved ? SONARQUBE_RESOLVED_16 : SONARQUBE_16; + } + + /** + * Create a composite image with the markers' project connection mode, issue severity and type + * + * @param matchingStatus matching status of an issue (nullable when grouped) + * @param issueSeverity issue severity (NOT impact severity) + * @param type issue type + * @param isResolved issue status + * @return composite image if found, null otherwise + */ + @Nullable + public static Image getIssueImage(@Nullable FindingMatchingStatus matchingStatus, String issueSeverity, + @Nullable String type, boolean isResolved) { + var key = matchingStatus + "/" + issueSeverity + "/" + type + "/" + isResolved; + var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); + var image = imageRegistry.get(key); + if (image == null) { + ImageDescriptor matchingStatusImage = null; + if (matchingStatus != null) { + matchingStatusImage = matchingStatusToImageDescriptor(matchingStatus, isResolved); + } + var severityImage = getImpactSeverityImageDescriptor(issueSeverity); + ImageDescriptor typeImage = null; + if (type != null) { + typeImage = createImageDescriptor("type/" + type.toLowerCase(Locale.ENGLISH) + ".png"); + } + imageRegistry.put(key, new CompositeIssueImage(matchingStatusImage, severityImage, typeImage)); + } + return imageRegistry.get(key); + } + + /** + * Create a composite image for the new clean code taxonomy with the markers' connection mode and highest impact + * + * @param matchingStatus matching status of an issue (nullable when grouped) + * @param impact highest issue impact + * @param isResolved issue status + * @return composite image if found, null otherwise + */ + @Nullable + public static Image getIssueImage(@Nullable FindingMatchingStatus matchingStatus, String impact, + boolean isResolved) { + var key = matchingStatus + "/" + impact + "/" + isResolved; + var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); + var image = imageRegistry.get(key); + if (image == null) { + ImageDescriptor matchingStatusImage = null; + if (matchingStatus != null) { + matchingStatusImage = matchingStatusToImageDescriptor(matchingStatus, isResolved); + } + var impactImage = createImageDescriptor(IMPACT_FOLDER_PREFIX + impact.toLowerCase(Locale.ENGLISH) + ".png"); + imageRegistry.put(key, new CompositeIssueImage(matchingStatusImage, impactImage, null)); + } + return imageRegistry.get(key); + } + + @Nullable + public static ImageDescriptor getImpactSeverityImageDescriptor(String issueSeverity) { + ImpactSeverity impactSeverity; + if (IssueSeverity.BLOCKER.name().equals(issueSeverity)) { + impactSeverity = ImpactSeverity.BLOCKER; + } else if (IssueSeverity.CRITICAL.name().equals(issueSeverity)) { + impactSeverity = ImpactSeverity.HIGH; + } else if (IssueSeverity.MAJOR.name().equals(issueSeverity)) { + impactSeverity = ImpactSeverity.MEDIUM; + } else if (IssueSeverity.MINOR.name().equals(issueSeverity)) { + impactSeverity = ImpactSeverity.LOW; + } else { + impactSeverity = ImpactSeverity.INFO; + } + return createImageDescriptor(IMPACT_FOLDER_PREFIX + impactSeverity.name().toLowerCase(Locale.ENGLISH) + ".png"); + } + + @Nullable + public static Image getSeverityImage(IssueSeverity severity) { + ImpactSeverity impactSeverity; + switch (severity) { + case BLOCKER: + impactSeverity = ImpactSeverity.BLOCKER; + break; + case CRITICAL: + impactSeverity = ImpactSeverity.HIGH; + break; + case MAJOR: + impactSeverity = ImpactSeverity.MEDIUM; + break; + case MINOR: + impactSeverity = ImpactSeverity.LOW; + break; + default: + impactSeverity = ImpactSeverity.INFO; + } + return getImpactImage(impactSeverity); + } + + @Nullable + public static Image getTypeImage(RuleType type) { + return createImage("type/" + type.name().toLowerCase(Locale.ENGLISH) + ".png"); + } + + @Nullable + public static Image getImpactImage(ImpactSeverity impact) { + return createImage(IMPACT_FOLDER_PREFIX + impact.name().toLowerCase(Locale.ENGLISH) + ".png"); + } +} diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/SonarLintImages.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/SonarLintImages.java index c0566d47a..d211eb19f 100644 --- a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/SonarLintImages.java +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/SonarLintImages.java @@ -20,19 +20,14 @@ package org.sonarlint.eclipse.ui.internal; import java.net.URL; -import java.util.Locale; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.jface.resource.CompositeImageDescriptor; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; -import org.sonarlint.eclipse.core.internal.markers.MarkerUtils.FindingMatchingStatus; -import org.sonarsource.sonarlint.core.rpc.protocol.common.ImpactSeverity; -import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; -import org.sonarsource.sonarlint.core.rpc.protocol.common.RuleType; -public final class SonarLintImages { - private static final String IMPACT_FOLDER_PREFIX = "impact/"; +public class SonarLintImages { + protected static final String IMPACT_FOLDER_PREFIX = "impact/"; public static final ImageDescriptor IMG_WIZBAN_NEW_CONNECTION = createImageDescriptor("new_server_wiz.png"); //$NON-NLS-1$ public static final ImageDescriptor UPDATE_IMG = createImageDescriptor("update.gif"); //$NON-NLS-1$ @@ -92,84 +87,14 @@ public final class SonarLintImages { public static final Image NOTIFICATION_CLOSE = createImage("notifications/eview16/notification-close.png"); //$NON-NLS-1$ public static final Image NOTIFICATION_CLOSE_HOVER = createImage("notifications/eview16/notification-close-active.png"); //$NON-NLS-1$ - private SonarLintImages() { - } - - /** - * Mapping of the matching status of an issue to the specific image - * - * @param matchingStatus specific matching status of an issue - * @param isResolved resolution status of an issue - * @return the corresponding connection mode icon (including standalone) - */ - private static ImageDescriptor matchingStatusToImageDescriptor(FindingMatchingStatus matchingStatus, - boolean isResolved) { - if (matchingStatus == FindingMatchingStatus.NOT_MATCHED) { - return isResolved ? STANDALONE_RESOLVED_16 : STANDALONE_16; - } else if (matchingStatus == FindingMatchingStatus.MATCHED_WITH_SC) { - return isResolved ? SONARCLOUD_RESOLVED_16 : SONARCLOUD_16; - } - return isResolved ? SONARQUBE_RESOLVED_16 : SONARQUBE_16; - } - - /** - * Create a composite image with the markers' project connection mode, issue severity and type - * - * @param matchingStatus matching status of an issue (nullable when grouped) - * @param issueSeverity issue severity (NOT impact severity) - * @param type issue type - * @param isResolved issue status - * @return composite image if found, null otherwise - */ - @Nullable - public static Image getIssueImage(@Nullable FindingMatchingStatus matchingStatus, String issueSeverity, - @Nullable String type, boolean isResolved) { - var key = matchingStatus + "/" + issueSeverity + "/" + type + "/" + isResolved; - var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); - var image = imageRegistry.get(key); - if (image == null) { - ImageDescriptor matchingStatusImage = null; - if (matchingStatus != null) { - matchingStatusImage = matchingStatusToImageDescriptor(matchingStatus, isResolved); - } - var severityImage = getImpactSeverityImageDescriptor(issueSeverity); - ImageDescriptor typeImage = null; - if (type != null) { - typeImage = createImageDescriptor("type/" + type.toLowerCase(Locale.ENGLISH) + ".png"); - } - imageRegistry.put(key, new CompositeIssueImage(matchingStatusImage, severityImage, typeImage)); - } - return imageRegistry.get(key); - } - - /** - * Create a composite image for the new clean code taxonomy with the markers' connection mode and highest impact - * - * @param matchingStatus matching status of an issue (nullable when grouped) - * @param impact highest issue impact - * @param isResolved issue status - * @return composite image if found, null otherwise - */ - @Nullable - public static Image getIssueImage(@Nullable FindingMatchingStatus matchingStatus, String impact, - boolean isResolved) { - var key = matchingStatus + "/" + impact + "/" + isResolved; - var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); - var image = imageRegistry.get(key); - if (image == null) { - ImageDescriptor matchingStatusImage = null; - if (matchingStatus != null) { - matchingStatusImage = matchingStatusToImageDescriptor(matchingStatus, isResolved); - } - var impactImage = createImageDescriptor(IMPACT_FOLDER_PREFIX + impact.toLowerCase(Locale.ENGLISH) + ".png"); - imageRegistry.put(key, new CompositeIssueImage(matchingStatusImage, impactImage, null)); - } - return imageRegistry.get(key); + protected SonarLintImages() { + // utility class } /** For issue markers where no grouping can be applied (e.g. new CCT unavailable for old SonarQube connections) */ public static Image getNotAvailableImage() { var key = "notAvailable"; + var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); var image = imageRegistry.get(key); if (image == null) { @@ -178,61 +103,12 @@ public static Image getNotAvailableImage() { return imageRegistry.get(key); } - @Nullable - public static ImageDescriptor getImpactSeverityImageDescriptor(String issueSeverity) { - ImpactSeverity impactSeverity; - if (IssueSeverity.BLOCKER.name().equals(issueSeverity)) { - impactSeverity = ImpactSeverity.BLOCKER; - } else if (IssueSeverity.CRITICAL.name().equals(issueSeverity)) { - impactSeverity = ImpactSeverity.HIGH; - } else if (IssueSeverity.MAJOR.name().equals(issueSeverity)) { - impactSeverity = ImpactSeverity.MEDIUM; - } else if (IssueSeverity.MINOR.name().equals(issueSeverity)) { - impactSeverity = ImpactSeverity.LOW; - } else { - impactSeverity = ImpactSeverity.INFO; - } - return createImageDescriptor(IMPACT_FOLDER_PREFIX + impactSeverity.name().toLowerCase(Locale.ENGLISH) + ".png"); - } - - @Nullable - public static Image getSeverityImage(IssueSeverity severity) { - ImpactSeverity impactSeverity; - switch (severity) { - case BLOCKER: - impactSeverity = ImpactSeverity.BLOCKER; - break; - case CRITICAL: - impactSeverity = ImpactSeverity.HIGH; - break; - case MAJOR: - impactSeverity = ImpactSeverity.MEDIUM; - break; - case MINOR: - impactSeverity = ImpactSeverity.LOW; - break; - default: - impactSeverity = ImpactSeverity.INFO; - } - return getImpactImage(impactSeverity); - } - - @Nullable - public static Image getTypeImage(RuleType type) { - return createImage("type/" + type.name().toLowerCase(Locale.ENGLISH) + ".png"); - } - - @Nullable - public static Image getImpactImage(ImpactSeverity impact) { - return createImage(IMPACT_FOLDER_PREFIX + impact.name().toLowerCase(Locale.ENGLISH) + ".png"); - } - /** * Due to Eclipse platform restrictions on the composite marker images: Even though the number of icons on the old * and new CCT differ, we can't display them without "empty" icons. When the old CCT will be removed at some point * the composite image will be correctly displayed at last! */ - private static class CompositeIssueImage extends CompositeImageDescriptor { + protected static class CompositeIssueImage extends CompositeImageDescriptor { @Nullable private final ImageDescriptor matchingStatus; private final ImageDescriptor requiredPart; @@ -282,13 +158,13 @@ private static URL getIconUrl(String key) { return SonarLintUiPlugin.getDefault().getBundle().getEntry("icons/" + key); } - private static Image createImage(String key) { + protected static Image createImage(String key) { createImageDescriptor(key); var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); return imageRegistry.get(key); } - private static ImageDescriptor createImageDescriptor(String key) { + protected static ImageDescriptor createImageDescriptor(String key) { var imageRegistry = SonarLintUiPlugin.getDefault().getImageRegistry(); var imageDescriptor = imageRegistry.getDescriptor(key); if (imageDescriptor == null) { @@ -302,5 +178,4 @@ private static ImageDescriptor createImageDescriptor(String key) { } return imageDescriptor; } - } diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/LegacyRuleHeaderPanel.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/LegacyRuleHeaderPanel.java index 6f87229cf..6eac79189 100644 --- a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/LegacyRuleHeaderPanel.java +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/LegacyRuleHeaderPanel.java @@ -23,7 +23,7 @@ import org.eclipse.swt.layout.GridData; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; -import org.sonarlint.eclipse.ui.internal.SonarLintImages; +import org.sonarlint.eclipse.ui.internal.ExtendedSonarLintImages; import org.sonarsource.sonarlint.core.rpc.protocol.common.StandardModeDetails; /** Rule header for the old CCT */ @@ -51,11 +51,11 @@ public LegacyRuleHeaderPanel(Composite parent, StandardModeDetails details, Stri @Override public void updateRule() { var type = details.getType(); - ruleTypeIcon.setImage(SonarLintImages.getTypeImage(type)); + ruleTypeIcon.setImage(ExtendedSonarLintImages.getTypeImage(type)); ruleTypeLabel.setText(clean(type.toString())); var severity = details.getSeverity(); - ruleSeverityIcon.setImage(SonarLintImages.getSeverityImage(severity)); + ruleSeverityIcon.setImage(ExtendedSonarLintImages.getSeverityImage(severity)); ruleSeverityLabel.setText(clean(severity.toString())); ruleKeyLabel.setText(ruleKey); diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/RuleHeaderPanel.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/RuleHeaderPanel.java index ab3199384..95864aafe 100644 --- a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/RuleHeaderPanel.java +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/rule/RuleHeaderPanel.java @@ -25,7 +25,7 @@ import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Label; -import org.sonarlint.eclipse.ui.internal.SonarLintImages; +import org.sonarlint.eclipse.ui.internal.ExtendedSonarLintImages; import org.sonarsource.sonarlint.core.client.utils.ImpactSeverity; import org.sonarsource.sonarlint.core.client.utils.SoftwareQuality; import org.sonarsource.sonarlint.core.rpc.protocol.backend.rules.ImpactDto; @@ -94,7 +94,7 @@ public void updateImpact(ImpactDto impact) { softwareQualityLabel.setText(sqWithLabel.getLabel()); softwareQualityLabel.setToolTipText(tooltip); - impactSeverityIcon.setImage(SonarLintImages.getImpactImage(impact.getImpactSeverity())); + impactSeverityIcon.setImage(ExtendedSonarLintImages.getImpactImage(impact.getImpactSeverity())); impactSeverityIcon.setToolTipText(tooltip); } } diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/views/issues/IssueDescriptionField.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/views/issues/IssueDescriptionField.java index 823d7bb2f..7469ee0b8 100644 --- a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/views/issues/IssueDescriptionField.java +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/views/issues/IssueDescriptionField.java @@ -29,6 +29,7 @@ import org.eclipse.ui.views.markers.MarkerField; import org.eclipse.ui.views.markers.MarkerItem; import org.sonarlint.eclipse.core.internal.markers.MarkerUtils; +import org.sonarlint.eclipse.ui.internal.ExtendedSonarLintImages; import org.sonarlint.eclipse.ui.internal.SonarLintImages; import org.sonarsource.sonarlint.core.rpc.protocol.common.IssueSeverity; @@ -118,13 +119,13 @@ private static Image getImage(MarkerItem item) { var highestImpact = MarkerUtils.decodeHighestImpact( item.getAttributeValue(MarkerUtils.SONAR_MARKER_ISSUE_HIGHEST_IMPACT_ATTR, null)); if (cleanCodeAttribute == null || highestImpact == null) { - return SonarLintImages.getIssueImage(matchingStatus, + return ExtendedSonarLintImages.getIssueImage(matchingStatus, item.getAttributeValue(MarkerUtils.SONAR_MARKER_ISSUE_SEVERITY_ATTR, "major"), item.getAttributeValue(MarkerUtils.SONAR_MARKER_ISSUE_TYPE_ATTR, "code_smell"), isResolved); } - return SonarLintImages.getIssueImage(matchingStatus, highestImpact.name(), isResolved); + return ExtendedSonarLintImages.getIssueImage(matchingStatus, highestImpact.name(), isResolved); } else { // It is no actual marker but a groupBy item which groups the headers var groupByTitle = item.getAttributeValue(IMarker.MESSAGE, "") @@ -133,16 +134,16 @@ private static Image getImage(MarkerItem item) { .trim(); // As we offer multiple different grouping options (severity / impacts), we have to display - // the correct grouping icon. + // the correct grouping icon. if (groupByTitle.endsWith(" IMPACT")) { - return SonarLintImages.getIssueImage(null, groupByTitle.replace(" IMPACT", ""), false); + return ExtendedSonarLintImages.getIssueImage(null, groupByTitle.replace(" IMPACT", ""), false); } else if (IssueSeverity.BLOCKER.name().equals(groupByTitle) || IssueSeverity.CRITICAL.name().equals(groupByTitle) || IssueSeverity.MAJOR.name().equals(groupByTitle) || IssueSeverity.MINOR.name().equals(groupByTitle) || IssueSeverity.INFO.name().equals(groupByTitle)) { - return SonarLintImages.getIssueImage(null, groupByTitle, null, false); - } + return ExtendedSonarLintImages.getIssueImage(null, groupByTitle, null, false); + } return SonarLintImages.getNotAvailableImage(); } } From ded1d36466f8ee05a87936a04cd5375a3f6d5d8f Mon Sep 17 00:00:00 2001 From: Tobias Hahnen Date: Thu, 7 May 2026 22:23:26 +0200 Subject: [PATCH 3/5] Fix Eclipse API incompatibility This method is not available in the newest version of the Eclipse IDE (SDK to be precise), replace this with a method from the Java Standard Library to be compatible with every (supported) version of the Eclipse IDE. --- .../ui/internal/dialog/AbstractFixSuggestionDialog.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/dialog/AbstractFixSuggestionDialog.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/dialog/AbstractFixSuggestionDialog.java index 32419b8ca..45a7e91ed 100644 --- a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/dialog/AbstractFixSuggestionDialog.java +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/dialog/AbstractFixSuggestionDialog.java @@ -21,12 +21,12 @@ import java.io.ByteArrayInputStream; import java.io.InputStream; +import java.nio.charset.StandardCharsets; import java.util.Locale; import org.eclipse.compare.CompareConfiguration; import org.eclipse.compare.IEncodedStreamContentAccessor; import org.eclipse.compare.ITypedElement; import org.eclipse.compare.contentmergeviewer.TextMergeViewer; -import org.eclipse.compare.internal.Utilities; import org.eclipse.compare.structuremergeviewer.DiffNode; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.annotation.Nullable; @@ -161,7 +161,7 @@ public CodeNode(String content) { @Override public InputStream getContents() throws CoreException { - return new ByteArrayInputStream(Utilities.getBytes(content, "UTF-8")); + return new ByteArrayInputStream(content.getBytes(StandardCharsets.UTF_8)); } @Override From b1caaf709413bdcf77cc24928fc5059412563bfe Mon Sep 17 00:00:00 2001 From: Tobias Hahnen Date: Mon, 18 May 2026 20:40:41 +0200 Subject: [PATCH 4/5] Apply PR feedback and therefore adjust tests --- .../SuppressWarningsQuickFixTest.java | 81 +++++++++++++++---- .../internal/SuppressWarningsQuickFix.java | 13 +++ .../ui/internal/ExtendedSonarLintImages.java | 19 +++++ 3 files changed, 96 insertions(+), 17 deletions(-) diff --git a/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java b/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java index 89f8f9768..9ff3c4934 100644 --- a/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java +++ b/org.sonarlint.eclipse.core.tests/src/test/java/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFixTest.java @@ -20,9 +20,11 @@ package org.sonarlint.eclipse.jdt.internal; import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.resources.IResource; import org.eclipse.core.runtime.CoreException; import org.eclipse.jdt.core.JavaCore; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; @@ -32,8 +34,8 @@ public class SuppressWarningsQuickFixTest { private static final SuppressWarningsQuickFixGenerator generator = new SuppressWarningsQuickFixGenerator(); - @AfterClass - public static void after() { + @After + public void after() { var options = JavaCore.getOptions(); options.put(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN, JavaCore.WARNING); JavaCore.setOptions(options); @@ -45,7 +47,7 @@ public void testIrrelevantMarkers() throws CoreException { when(marker.getType()).thenReturn("noJavaProblem"); assertThat(generator.hasResolutions(marker)).isFalse(); - assertThat(generator.getResolutions(marker).length).isEqualTo(0); + assertThat(generator.getResolutions(marker)).isEmpty(); when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(null); @@ -59,11 +61,7 @@ public void testIrrelevantMarkers() throws CoreException { @Test public void testRelevantMarkers() throws CoreException { - var marker = mock(IMarker.class); - when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); - - when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL - + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_ISSUE); + var marker = getRelevantMarker(); assertThat(generator.hasResolutions(marker)).isTrue(); when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL @@ -81,19 +79,68 @@ public void testRelevantMarkers() throws CoreException { @Test public void testQuickFix() throws CoreException { - var marker = mock(IMarker.class); - when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); - when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL - + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_ISSUE); + var marker = getRelevantMarker(); - var quickFixes = generator.getResolutions(marker); - assertThat(quickFixes.length).isEqualTo(1); - - var quickFix = (SuppressWarningsQuickFix) quickFixes[0]; + var quickFix = getQuickFix(marker); assertThat(quickFix.getLabel()).contains("SonarQube"); assertThat(quickFix.getDescription()).contains("SonarQube"); quickFix.run(marker); assertThat(JavaCore.getOption(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN)).isEqualTo(JavaCore.IGNORE); } + + /** + * This tests the conditions to check whether the project related resource has a project and therefore can delegate + * it to the JDT logic to create a Java project. As there is no project, it is not possible. + */ + @Test + public void testWithoutProjectMarker() throws CoreException { + var resourceWithoutProject = mock(IResource.class); + when(resourceWithoutProject.getProject()).thenReturn(null); + + var markerWithResource = getRelevantMarker(); + when(markerWithResource.getResource()).thenReturn(resourceWithoutProject); + + var quickFix = getQuickFix(markerWithResource); + quickFix.run(markerWithResource); + assertThat(JavaCore.getOption(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN)).isEqualTo(JavaCore.IGNORE); + } + + /** + * This tests the marker with a project, but the JavaModel cannot be mocked and therefore the Java project created + * by JDT logic is closed. The quickfix therefore falls back to the global options. + * + * The JavaModel and the related logic can only be tested by an integration test which is quite costly. + */ + @Test + public void testClosedJavaProjectMarker() throws CoreException { + var closedProject = mock(IProject.class); + when(closedProject.getType()).thenReturn(IResource.PROJECT); + + var resourceWithProject = mock(IResource.class); + when(resourceWithProject.getProject()).thenReturn(closedProject); + + var markerWithResource = getRelevantMarker(); + when(markerWithResource.getResource()).thenReturn(resourceWithProject); + + var quickFix = getQuickFix(markerWithResource); + quickFix.run(markerWithResource); + + // This means the option is not applied on the project but global level. + assertThat(JavaCore.getOption(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN)).isEqualTo(JavaCore.IGNORE); + } + + private IMarker getRelevantMarker() throws CoreException { + var marker = mock(IMarker.class); + when(marker.getType()).thenReturn(SuppressWarningsQuickFixGenerator.JAVA_PROBLEM_TYPE); + when(marker.getAttribute(IMarker.MESSAGE)).thenReturn(SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_GENERAL + + SuppressWarningsQuickFixGenerator.MARKER_MESSAGE_JAVA_ISSUE); + return marker; + } + + private SuppressWarningsQuickFix getQuickFix(IMarker marker) { + var quickFixes = generator.getResolutions(marker); + assertThat(quickFixes).hasSize(1); + return (SuppressWarningsQuickFix) quickFixes[0]; + } } diff --git a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java index e641b3197..f33f0fc3a 100644 --- a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java +++ b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java @@ -45,6 +45,19 @@ public Image getImage() { @Override public void run(IMarker marker) { + var resource = marker.getResource(); + if (resource != null) { + var javaProject = JavaCore.create(resource.getProject()); + if (javaProject != null && javaProject.exists()) { + var projectOptions = javaProject.getOptions(false); + if (!projectOptions.isEmpty()) { + projectOptions.put(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN, JavaCore.IGNORE); + javaProject.setOptions(projectOptions); + return; + } + } + } + var options = JavaCore.getOptions(); options.put(JavaCore.COMPILER_PB_UNHANDLED_WARNING_TOKEN, JavaCore.IGNORE); JavaCore.setOptions(options); diff --git a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java index 1726d39f8..c3fafe14d 100644 --- a/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java +++ b/org.sonarlint.eclipse.ui/src/org/sonarlint/eclipse/ui/internal/ExtendedSonarLintImages.java @@ -1,3 +1,22 @@ +/* + * SonarLint for Eclipse + * Copyright (C) SonarSource Sàrl + * sonarlint@sonarsource.com + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ package org.sonarlint.eclipse.ui.internal; import java.util.Locale; From 906492698355217f68a78e8e340b1660da2324d4 Mon Sep 17 00:00:00 2001 From: Tobias Hahnen Date: Thu, 18 Jun 2026 14:20:43 +0200 Subject: [PATCH 5/5] Apply PR feedback --- .../eclipse/core/internal/backend/SonarLintBackendService.java | 2 ++ .../eclipse/jdt/internal/SuppressWarningsQuickFix.java | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java index cbea57c83..0d9d47700 100644 --- a/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java +++ b/org.sonarlint.eclipse.core/src/org/sonarlint/eclipse/core/internal/backend/SonarLintBackendService.java @@ -194,6 +194,8 @@ protected IStatus run(IProgressMonitor monitor) { var cFamilyPlugin = PluginPathHelper.findEmbeddedCFamilyPlugin(); if (cFamilyPlugin != null) { embeddedPlugins.put("cpp", cFamilyPlugin); + } else { + SonarLintLogger.get().info("CFamily plug-in not found - C/C++ analysis will not be available"); } var sqConnections = ConnectionSynchronizer.buildSqConnectionDtos(); diff --git a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java index f33f0fc3a..1540dc075 100644 --- a/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java +++ b/org.sonarlint.eclipse.jdt/src/org/sonarlint/eclipse/jdt/internal/SuppressWarningsQuickFix.java @@ -35,7 +35,8 @@ public String getLabel() { public String getDescription() { return "SonarQube uses custom ids that can be set in \"@SuppressWarnings\" to be picked up by the language " + "analyzers to suppress potential rule violations. Eclipse JDT does not recognize these IDs and reports them " - + "as unsupported. This quick fix will disable this compiler warning."; + + "as unsupported. This quick fix will disable this compiler warning on the project level if project specific " + + "Eclipse JDT configurations are available, otherwise on the workspace level."; } @Override