From fb3f405f26e99ef215da8c2e24e3948bd263a32a Mon Sep 17 00:00:00 2001 From: Lars Vogel Date: Fri, 6 Nov 2026 09:11:09 +0100 Subject: [PATCH] Fix #187: Report warnings and infos from get_errors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The get_errors tool only returned error markers, printed as raw marker dumps, so Copilot could not see the warnings and infos the Problems view shows. It now lists every problem marker of the requested files with its severity, line and message, errors first, and says when a file has no problems. Assisted-by: multiple AI agents and layers of automated tooling 🤖 --- .../ui/chat/tools/GetErrorsToolTests.java | 49 ++++++++++++++++ .../eclipse/ui/chat/tools/GetErrorsTool.java | 58 +++++++++++++++---- 2 files changed, 96 insertions(+), 11 deletions(-) diff --git a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsToolTests.java b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsToolTests.java index e13ef1de7..78b94e327 100644 --- a/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsToolTests.java +++ b/com.microsoft.copilot.eclipse.ui.test/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsToolTests.java @@ -6,11 +6,14 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import org.eclipse.core.resources.IMarker; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; @@ -92,4 +95,50 @@ void testGetErrorsWithInvalidFilePath() { assertTrue(result.content().contains("Resource not found for filePath: invalidPath1")); assertTrue(result.content().contains("Resource not found for filePath: invalidPath2")); } + + @Test + void testFormatProblemsListsAllSeveritiesErrorsFirst() { + IMarker[] markers = { + mockMarker(IMarker.SEVERITY_INFO, 3, "Info message"), + mockMarker(IMarker.SEVERITY_WARNING, 20, "Later warning"), + mockMarker(IMarker.SEVERITY_ERROR, 7, "Error message"), + mockMarker(IMarker.SEVERITY_WARNING, 5, "Earlier warning") }; + + String result = GetErrorsTool.formatProblems("/project/A.java", markers); + + assertEquals(""" + Problems in /project/A.java: + ERROR at line 7: Error message + WARNING at line 5: Earlier warning + WARNING at line 20: Later warning + INFO at line 3: Info message + """, result); + } + + @Test + void testFormatProblemsWithoutLineNumber() { + IMarker[] markers = { mockMarker(IMarker.SEVERITY_ERROR, -1, "Build path is incomplete") }; + + String result = GetErrorsTool.formatProblems("/project/A.java", markers); + + assertEquals(""" + Problems in /project/A.java: + ERROR: Build path is incomplete + """, result); + } + + @Test + void testFormatProblemsWithoutMarkers() { + String result = GetErrorsTool.formatProblems("/project/A.java", new IMarker[0]); + + assertEquals("No problems found in /project/A.java\n", result); + } + + private static IMarker mockMarker(int severity, int lineNumber, String message) { + IMarker marker = mock(IMarker.class); + when(marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO)).thenReturn(severity); + when(marker.getAttribute(IMarker.LINE_NUMBER, -1)).thenReturn(lineNumber); + when(marker.getAttribute(IMarker.MESSAGE, "")).thenReturn(message); + return marker; + } } diff --git a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsTool.java b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsTool.java index 63250eb9e..f552e2f29 100644 --- a/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsTool.java +++ b/com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/tools/GetErrorsTool.java @@ -4,6 +4,8 @@ package com.microsoft.copilot.eclipse.ui.chat.tools; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -49,9 +51,9 @@ public LanguageModelToolInformation getToolInformation() { // Set the name and description of the tool toolInfo.setName(TOOL_NAME); toolInfo.setDescription(""" - Get any compile or lint errors in a code file. - If the user mentions errors or problems in a file, they may be referring to these. - Use the tool to see the same errors that the user is seeing. + Get the compile and lint problems (errors, warnings and infos) of code files, as shown in the Problems view. + If the user mentions errors, warnings or problems in a file, they may be referring to these. + Use the tool to see the same problems that the user is seeing. Also use this tool after editing a file to validate the change. """); @@ -61,9 +63,9 @@ public LanguageModelToolInformation getToolInformation() { // Define the properties of the input schema InputSchemaPropertyValue filePathsProperty = new InputSchemaPropertyValue("array", - "Array of absolute file paths to check for errors"); + "Array of absolute file paths to check for problems"); filePathsProperty.setItems(new InputSchemaPropertyValue("string", - "The absolute path of a file to check for compile/lint errors")); + "The absolute path of a file to check for compile/lint problems")); Map properties = new HashMap<>(); properties.put(FILE_PATHS, filePathsProperty); @@ -160,7 +162,7 @@ private List parseMalformedJsonArray(String jsonStr) { } /** - * Retrieves errors from the Problems view for the given file URIs. + * Retrieves the problems shown in the Problems view for the given file paths. * * @param filePaths The list of file paths to check for errors. * @return A GetErrorsResult containing the errors found and whether any exceptions occurred. @@ -179,11 +181,7 @@ public GetErrorsResult getErrors(List filePaths) { } IMarker[] markers = file.findMarkers(IMarker.PROBLEM, true, IResource.DEPTH_ZERO); - for (IMarker marker : markers) { - if (marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO) == IMarker.SEVERITY_ERROR) { - toolResult.append(marker.toString()).append(StringUtils.LF); - } - } + toolResult.append(formatProblems(filePath, markers)); } catch (CoreException e) { toolResult.append("Failed on file: ").append(filePath).append(e.getMessage()).append(StringUtils.LF); hasException = true; @@ -193,4 +191,42 @@ public GetErrorsResult getErrors(List filePaths) { return new GetErrorsResult(toolResult.toString(), hasException); } + /** + * Formats the problem markers of a file, errors first, then warnings and infos, each ordered by line. + */ + static String formatProblems(String filePath, IMarker[] markers) { + if (markers.length == 0) { + return "No problems found in " + filePath + StringUtils.LF; + } + StringBuilder result = new StringBuilder("Problems in ").append(filePath).append(':').append(StringUtils.LF); + Arrays.stream(markers) + .sorted(Comparator.comparingInt(GetErrorsTool::getSeverity).reversed() + .thenComparingInt(GetErrorsTool::getLineNumber)) + .forEach(marker -> { + result.append(getSeverityLabel(getSeverity(marker))); + int lineNumber = getLineNumber(marker); + if (lineNumber > 0) { + result.append(" at line ").append(lineNumber); + } + result.append(": ").append(marker.getAttribute(IMarker.MESSAGE, StringUtils.EMPTY)).append(StringUtils.LF); + }); + return result.toString(); + } + + private static int getSeverity(IMarker marker) { + return marker.getAttribute(IMarker.SEVERITY, IMarker.SEVERITY_INFO); + } + + private static int getLineNumber(IMarker marker) { + return marker.getAttribute(IMarker.LINE_NUMBER, -1); + } + + private static String getSeverityLabel(int severity) { + return switch (severity) { + case IMarker.SEVERITY_ERROR -> "ERROR"; + case IMarker.SEVERITY_WARNING -> "WARNING"; + default -> "INFO"; + }; + } + } \ No newline at end of file