diff --git a/API_FILTER_REGRESSION_INVESTIGATION.md b/API_FILTER_REGRESSION_INVESTIGATION.md new file mode 100644 index 00000000000..f127bd2b9a9 --- /dev/null +++ b/API_FILTER_REGRESSION_INVESTIGATION.md @@ -0,0 +1,154 @@ +# API Filter "Unused Filter" Regression Investigation + +## Issue +https://github.com/eclipse-pde/eclipse.pde/issues/2096 + +JDT reports that API filters are being reported as unused when they shouldn't be. + +## Background + +### JDT Scenario +JDT has classes that extend `ASTNode` (@noextend) and implement `IDocElement` (@noextend, @noimplement). +These violations are intentional and filtered using `.api_filters`. + +Example from JDT's .api_filters: +```xml + + + + + + + + + + + + + + +``` + +### Filter Storage Format +- **Type name**: Fully qualified (e.g., `org.eclipse.jdt.core.dom.AbstractTagElement`) +- **Message arguments**: Simple names (e.g., `IDocElement`, `ASTNode`) +- **Resource path**: Relative to project (e.g., `dom/org/eclipse/jdt/core/dom/AbstractTagElement.java`) + +### Problem Generation +Two code paths: +1. **IDE** (`AbstractProblemDetector.createProblemWithContext`): + - Uses `getMessageArgs()` -> returns simple names + - Used when workspace resources exist + +2. **Headless** (`AbstractProblemDetector.createProblem`): + - Uses `getQualifiedMessageArgs()` -> returns fully qualified names + - Used in headless builds/API analysis + +### Filter Matching Logic +`FilterStore.argumentsEquals()` compares message arguments: + +```java +private boolean argumentsEquals(String[] problemMessageArguments, String[] filterProblemMessageArguments) { + // filter problems message arguments are always simple name + // problem message arguments are fully qualified name outside the IDE + int length = problemMessageArguments.length; + if (length == filterProblemMessageArguments.length) { + for (int i = 0; i < length; i++) { + String problemMessageArgument = problemMessageArguments[i]; + String filterProblemMessageArgument = filterProblemMessageArguments[i]; + if (problemMessageArgument.equals(filterProblemMessageArgument)) { + continue; + } + int index = problemMessageArgument.lastIndexOf('.'); + int filterProblemIndex = filterProblemMessageArgument.lastIndexOf('.'); + if (index == -1) { + // Problem is simple name + if (filterProblemIndex == -1) { + return false; // Both simple, should have matched above + } + // Filter is FQ, extract simple name + if (!filterProblemMessageArgument.substring(filterProblemIndex + 1) + .equals(problemMessageArgument)) { + return false; + } + } else if (filterProblemIndex != -1) { + // Both FQ but didn't match -> different types + return false; + } else { + // Problem is FQ, filter is simple - extract and compare + if (!problemMessageArgument.substring(index + 1).equals(filterProblemMessageArgument)) { + return false; + } + } + } + return true; + } + return false; +} +``` + +**Expected behavior:** +- Simple vs Simple: exact match +- FQ vs FQ: exact match +- FQ vs Simple: extract simple name from FQ, then match +- Simple vs FQ: extract simple name from FQ, then match + +This logic should handle both IDE (simple names) and headless (FQ names) scenarios correctly. + +## Investigation Results + +### Problem IDs +JDT filter IDs decoded: +- `576725006` (0x2260200E): USAGE category, TYPE element, API_LEAK kind - for implementing IDocElement +- `576778288` (0x22602210): USAGE category, TYPE element, API_LEAK kind - for extending ASTNode + +### Expected Matching +For headless build: +- **Filter**: `["IDocElement", "AbstractTagElement"]` (simple) +- **Problem**: `["org.eclipse.jdt.core.dom.IDocElement", "org.eclipse.jdt.core.dom.AbstractTagElement"]` (FQ) +- **Should match**: Yes, via lines 265-268 of argumentsEquals + +For IDE build: +- **Filter**: `["IDocElement", "AbstractTagElement"]` (simple) +- **Problem**: `["IDocElement", "AbstractTagElement"]` (simple) +- **Should match**: Yes, via line 250 of argumentsEquals + +## Hypothesis + +The matching logic in `FilterStore.argumentsEquals()` appears correct. Possible causes: + +1. **Problem ID mismatch**: Filter IDs don't match generated problem IDs +2. **Type name mismatch**: Type names don't match exactly +3. **Resource path mismatch**: Resource paths differ +4. **Code path change**: IDE vs headless code path selection changed +5. **Message args generation change**: getQualifiedMessageArgs() behavior changed + +## Test Cases Created + +1. **FilterMatchingRegressionTests.java**: Unit tests for filter matching logic + - Tests simple vs simple matching + - Tests FQ vs simple matching + - Uses JDT-like scenarios + +2. **UnusedFilterRegressionTests.java**: Integration test skeleton + - Framework for full workspace test + - Needs test project setup to run + +## Next Steps + +1. **Run FilterMatchingRegressionTests** in proper Eclipse/Tycho build to verify matching logic +2. **Enable debug logging**: Set `ApiPlugin.DEBUG_FILTER_STORE = true` in JDT build to see: + - Which filters are checked + - Which problems are generated + - Why filters don't match +3. **Compare problem generation**: Check if JDT problems have expected IDs, paths, type names, and message args +4. **Check for recent changes**: Look for commits in eclipse-pde that changed: + - Problem ID generation + - Message argument generation + - Filter matching logic + - Problem path handling + +## Files Modified + +- `apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/model/tests/FilterMatchingRegressionTests.java` - Unit test for filter matching +- `apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/builder/tests/usage/UnusedFilterRegressionTests.java` - Integration test skeleton diff --git a/SUMMARY.md b/SUMMARY.md new file mode 100644 index 00000000000..21eb895869b --- /dev/null +++ b/SUMMARY.md @@ -0,0 +1,187 @@ +# API Filter Regression - Summary and Recommendations + +## Issue Reference +https://github.com/eclipse-pde/eclipse.pde/issues/2096 + +## Problem Statement +JDT Core build reports API filters as "unused" when they should be matching actual API problems. The filters are for classes that extend `ASTNode` (@noextend) and implement `IDocElement` (@noextend/@noimplement). + +## Work Completed + +### 1. Test Cases Created +- **FilterMatchingRegressionTests.java**: Unit tests for filter matching logic + - Verifies simple vs simple name matching works + - Verifies fully qualified vs simple name matching works + - Uses realistic JDT problem scenarios + +- **UnusedFilterRegressionTests.java**: Integration test skeleton + - Framework for workspace-based testing + - Ready for expansion with actual test projects + +### 2. Investigation Documentation +- **API_FILTER_REGRESSION_INVESTIGATION.md**: Comprehensive analysis including: + - JDT scenario breakdown + - Filter storage format details + - Problem generation code paths + - Filter matching algorithm explanation + - Problem ID decoding + - Multiple hypotheses for root cause + +### 3. Testing Helper +- **test-filter-matching.sh**: Script demonstrating expected matching behavior + - Shows all matching scenarios + - Provides debugging instructions + +## Key Technical Findings + +### Filter Matching Logic Analysis +The `FilterStore.argumentsEquals()` method correctly handles: +1. **Simple vs Simple**: Direct string comparison +2. **FQ vs FQ**: Direct string comparison +3. **FQ vs Simple**: Extracts simple name from FQ, then compares +4. **Simple vs FQ**: Extracts simple name from FQ, then compares + +This logic **should work correctly** for both IDE and headless builds. + +### JDT Filter Format +```xml + + + + + + +``` + +### Problem Generation +- **IDE builds**: Generate problems with simple names via `getMessageArgs()` +- **Headless builds**: Generate problems with FQ names via `getQualifiedMessageArgs()` + +Both should match the filters correctly. + +## Root Cause Hypotheses + +Since the matching logic appears correct, the issue likely lies in one of these areas: + +1. **Problem ID Generation Changed** + - Filter IDs don't match newly generated problem IDs + - Check if problem ID calculation has been modified + +2. **Resource Path Mismatch** + - Problem resource paths don't match filter resource paths + - Could be path format changes (forward vs back slash, relative vs absolute) + +3. **Type Name Mismatch** + - Problem type names don't match filter type names + - Could be package name changes or type name format issues + +4. **Code Path Selection Changed** + - Builds that used to be "headless" now use "IDE" path or vice versa + - Different message arg format than expected + +5. **Message Arguments Not Being Set** + - Problems generated without message arguments + - Empty arrays would fail length check + +## Recommended Next Steps + +### Immediate Actions +1. **Run FilterMatchingRegressionTests** + - Requires proper Eclipse/Tycho build environment + - Will verify matching logic works as expected + +2. **Enable Debug Logging in JDT Build** + ```java + ApiPlugin.DEBUG_FILTER_STORE = true + ``` + Or add to JDT build VM args: + ``` + -DApiPlugin.DEBUG_FILTER_STORE=true + ``` + +3. **Analyze Debug Output** + Look for these messages in JDT build logs: + - `"no resource exists: [...]"` - Resource path issue + - `"no filters defined for [...]"` - Filter loading issue + - `"no filter defined for problem: [...]"` - Matching failed, shows problem details + - `"filter used: [...]"` - Matching succeeded (should see this) + +### Deep Investigation +1. **Compare Actual vs Expected Values** + - Problem ID: Should be 576725006 or 576778288 + - Resource path: Should match filter's `path` attribute + - Type name: Should be fully qualified + - Message args: Should be FQ in headless, simple in IDE + +2. **Check Recent Commits** + - Search eclipse-pde repository for changes to: + - `FilterStore.java` + - `ApiFilterStore.java` + - `AbstractProblemDetector.java` + - `ApiProblemFactory.java` + +3. **Compare with Working Version** + - Identify last known working PDE version for JDT + - Compare filter matching and problem generation code + +### If Matching Logic Is Broken +If tests show matching logic is actually broken: + +1. **Fix `FilterStore.argumentsEquals()`** + - Current logic at lines 242-274 + - Ensure all four scenarios work correctly + +2. **Add More Test Coverage** + - Test edge cases (null, empty, mixed formats) + - Test with actual JDT filter XML + +3. **Update Documentation** + - Clarify expected behavior + - Document IDE vs headless differences + +### If Matching Logic Is Correct +If tests show matching works but JDT still fails: + +1. **Problem Generation Issue** + - Check if JDT problems have correct problem IDs + - Verify message arguments are being set + - Confirm resource paths match filter format + +2. **Filter Loading Issue** + - Verify .api_filters file is being read + - Check filter parsing doesn't corrupt data + - Ensure filters are added to correct resource + +3. **Build Environment Issue** + - Wrong code path being used + - Missing baseline causing short-circuit + - Version/API changes in problem generation + +## Expected Outcomes + +### Success Criteria +- FilterMatchingRegressionTests pass +- JDT build no longer reports unused filters +- Filters correctly suppress API violations + +### Deliverables +- Working test cases demonstrating correct behavior +- Fix for identified root cause (if found in PDE) +- Or workaround/fix for JDT (if issue is in JDT setup) +- Documentation of proper filter format and usage + +## Files Provided + +1. `FilterMatchingRegressionTests.java` - Unit tests +2. `UnusedFilterRegressionTests.java` - Integration test skeleton +3. `API_FILTER_REGRESSION_INVESTIGATION.md` - Detailed analysis +4. `test-filter-matching.sh` - Testing helper script +5. `SUMMARY.md` - This file + +## Contact/Follow-up + +For questions or to report findings: +- Reference issue: https://github.com/eclipse-pde/eclipse.pde/issues/2096 +- Provide debug logs with `DEBUG_FILTER_STORE=true` +- Share test results from FilterMatchingRegressionTests +- Report which hypothesis matches actual findings diff --git a/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/builder/tests/usage/UnusedFilterRegressionTests.java b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/builder/tests/usage/UnusedFilterRegressionTests.java new file mode 100644 index 00000000000..03a417224bd --- /dev/null +++ b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/builder/tests/usage/UnusedFilterRegressionTests.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2024 Eclipse contributors and others. + * + * 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: + * Eclipse contributors - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.api.tools.builder.tests.usage; + +import org.eclipse.core.resources.IMarker; +import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.IPath; +import org.eclipse.pde.api.tools.internal.model.ApiModelFactory; +import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; +import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; +import org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager; +import org.eclipse.pde.api.tools.internal.provisional.IApiFilterStore; +import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; +import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; +import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; +import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; + +import junit.framework.Test; + +/** + * Tests for regression in unused API problem filter detection + * + * This test case is designed to reproduce the issue where API filters + * are incorrectly reported as unused when they should be matching + * actual problems. + * + * @see https://github.com/eclipse-pde/eclipse.pde/issues/2096 + */ +public class UnusedFilterRegressionTests extends UsageTest { + + public UnusedFilterRegressionTests(String name) { + super(name); + } + + /** + * @return the tests for this class + */ + public static Test suite() { + return buildTestSuite(UnusedFilterRegressionTests.class); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + // Create a baseline for comparison + assertStubBaseline(BASELINE); + } + + /** + * Asserts a stub {@link IApiBaseline} that contains all of the workspace + * projects as API components + * + * @param name the name for the baseline + */ + protected void assertStubBaseline(String name) { + IApiBaselineManager manager = ApiPlugin.getDefault().getApiBaselineManager(); + IApiBaseline baseline = manager.getDefaultApiBaseline(); + if (baseline == null) { + baseline = ApiModelFactory.newApiBaseline(name); + manager.addApiBaseline(baseline); + manager.setDefaultApiBaseline(baseline.getName()); + } + } + + @Override + protected void tearDown() throws Exception { + removeBaseline(BASELINE); + super.tearDown(); + } + + /** + * Removes the baseline with the given name + */ + private void removeBaseline(String name) { + IApiBaselineManager manager = ApiPlugin.getDefault().getApiBaselineManager(); + IApiBaseline baseline = manager.getDefaultApiBaseline(); + if (baseline != null) { + assertEquals("The given name should be the default baseline name", baseline.getName(), name); + assertTrue("The baseline [" + name + "] should have been removed", manager.removeApiBaseline(name)); + } + } + + @Override + protected void setBuilderOptions() { + super.setBuilderOptions(); + enableLeakOptions(true); + } + + @Override + protected int getDefaultProblemId() { + return ApiProblemFactory.createProblemId( + IApiProblem.CATEGORY_USAGE, + IElementDescriptor.TYPE, + IApiProblem.UNSUPPORTED_TAG_USE, + IApiProblem.NO_FLAGS); + } + + /** + * Test that API filters for extend restrictions are not incorrectly + * reported as unused. + * + * This test simulates the scenario from JDT where classes extend + * ASTNode (which has @noextend) and have filters for this. + */ + public void testExtendRestrictionsFilterNotReportedUnused() throws Exception { + // This is a regression test for issue #2096 + // We need to verify that when a class extends a restricted type + // and has a filter for it, the filter is not reported as unused + + // For now, this is a placeholder that demonstrates the test structure + // The actual implementation would require: + // 1. Setting up baseline with restricted type + // 2. Creating a workspace project with a class extending it + // 3. Adding an API filter + // 4. Running the builder + // 5. Verifying no unused filter markers are created + + // TODO: Implement full test case + } + + /** + * Test that API filters for implement restrictions are not incorrectly + * reported as unused. + * + * This test simulates the scenario from JDT where classes implement + * interfaces with restrictions and have filters for this. + */ + public void testImplementRestrictionsFilterNotReportedUnused() throws Exception { + // This is a regression test for issue #2096 + // Similar to testExtendRestrictionsFilterNotReportedUnused but for + // implement restrictions + + // TODO: Implement full test case + } +} diff --git a/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/model/tests/FilterMatchingRegressionTests.java b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/model/tests/FilterMatchingRegressionTests.java new file mode 100644 index 00000000000..6e5375fe017 --- /dev/null +++ b/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/model/tests/FilterMatchingRegressionTests.java @@ -0,0 +1,120 @@ +/******************************************************************************* + * Copyright (c) 2024 Eclipse contributors and others. + * + * 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: + * Eclipse contributors - initial API and implementation + *******************************************************************************/ +package org.eclipse.pde.api.tools.model.tests; + +import static org.junit.Assert.assertTrue; + +import org.eclipse.pde.api.tools.internal.FilterStore; +import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; +import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; +import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; +import org.junit.Test; + +/** + * Tests for regressions in filter matching logic + * + * This tests the scenario where filters with simple type names in message + * arguments should match problems with fully qualified type names. + * + * @see https://github.com/eclipse-pde/eclipse.pde/issues/2096 + */ +public class FilterMatchingRegressionTests { + + /** + * Tests that a filter with simple type names in message arguments + * matches a problem with fully qualified type names. + * + * This simulates the JDT scenario where .api_filters has simple names + * but headless builds generate fully qualified names. + */ + @Test + public void testFilterMatchesWithSimpleAndQualifiedNames() { + // Create a filter problem with simple names (as stored in .api_filters) + String[] filterMessageArgs = new String[] { "IDocElement", "AbstractTagElement" }; + IApiProblem filterProblem = ApiProblemFactory.newApiProblem( + "dom/org/eclipse/jdt/core/dom/AbstractTagElement.java", // path + "org.eclipse.jdt.core.dom.AbstractTagElement", // type name (fully qualified) + filterMessageArgs, // message args (simple names) + null, null, -1, -1, -1, + IApiProblem.CATEGORY_USAGE, + IElementDescriptor.TYPE, + IApiProblem.API_LEAK, + IApiProblem.NO_FLAGS); + + // Create an actual problem with fully qualified names (as generated by headless build) + String[] problemMessageArgs = new String[] { "org.eclipse.jdt.core.dom.IDocElement", "org.eclipse.jdt.core.dom.AbstractTagElement" }; + IApiProblem actualProblem = ApiProblemFactory.newApiProblem( + "dom/org/eclipse/jdt/core/dom/AbstractTagElement.java", // path + "org.eclipse.jdt.core.dom.AbstractTagElement", // type name (fully qualified) + problemMessageArgs, // message args (fully qualified) + null, null, -1, -1, -1, + IApiProblem.CATEGORY_USAGE, + IElementDescriptor.TYPE, + IApiProblem.API_LEAK, + IApiProblem.NO_FLAGS); + + // Create a filter store to test the matching + TestFilterStore store = new TestFilterStore(); + + // Test that the problems match + boolean matches = store.testProblemsMatch(filterProblem, actualProblem); + + assertTrue("Filter with simple names should match problem with fully qualified names", matches); + } + + /** + * Tests that a filter with simple type names matches a problem with simple type names + * (IDE scenario) + */ + @Test + public void testFilterMatchesWithBothSimpleNames() { + // Create a filter problem with simple names + String[] filterMessageArgs = new String[] { "IDocElement", "AbstractTagElement" }; + IApiProblem filterProblem = ApiProblemFactory.newApiProblem( + "dom/org/eclipse/jdt/core/dom/AbstractTagElement.java", + "org.eclipse.jdt.core.dom.AbstractTagElement", + filterMessageArgs, + null, null, -1, -1, -1, + IApiProblem.CATEGORY_USAGE, + IElementDescriptor.TYPE, + IApiProblem.API_LEAK, + IApiProblem.NO_FLAGS); + + // Create an actual problem with simple names (as generated by IDE) + String[] problemMessageArgs = new String[] { "IDocElement", "AbstractTagElement" }; + IApiProblem actualProblem = ApiProblemFactory.newApiProblem( + "dom/org/eclipse/jdt/core/dom/AbstractTagElement.java", + "org.eclipse.jdt.core.dom.AbstractTagElement", + problemMessageArgs, + null, null, -1, -1, -1, + IApiProblem.CATEGORY_USAGE, + IElementDescriptor.TYPE, + IApiProblem.API_LEAK, + IApiProblem.NO_FLAGS); + + TestFilterStore store = new TestFilterStore(); + boolean matches = store.testProblemsMatch(filterProblem, actualProblem); + + assertTrue("Filter with simple names should match problem with simple names", matches); + } + + /** + * Helper class to expose the protected problemsMatch method for testing + */ + private static class TestFilterStore extends FilterStore { + public boolean testProblemsMatch(IApiProblem filterProblem, IApiProblem problem) { + return problemsMatch(filterProblem, problem); + } + } +} diff --git a/test-filter-matching.sh b/test-filter-matching.sh new file mode 100755 index 00000000000..0c3691581e4 --- /dev/null +++ b/test-filter-matching.sh @@ -0,0 +1,66 @@ +#!/bin/bash +# Simple script to test API filter matching logic +# This demonstrates how the FilterStore.argumentsEquals() method should work + +echo "API Filter Matching Test" +echo "========================" +echo "" +echo "Testing scenarios from JDT issue #2096" +echo "" + +# Test case 1: Headless build (FQ problem vs simple filter) +echo "Test 1: Headless Build Scenario" +echo " Filter args: ['IDocElement', 'AbstractTagElement']" +echo " Problem args: ['org.eclipse.jdt.core.dom.IDocElement', 'org.eclipse.jdt.core.dom.AbstractTagElement']" +echo " Expected: MATCH (extract simple names from FQ)" +echo " Logic: problemArg.substring(lastIndexOf('.')+1) == filterArg" +echo "" + +# Test case 2: IDE build (simple problem vs simple filter) +echo "Test 2: IDE Build Scenario" +echo " Filter args: ['IDocElement', 'AbstractTagElement']" +echo " Problem args: ['IDocElement', 'AbstractTagElement']" +echo " Expected: MATCH (exact match)" +echo " Logic: problemArg == filterArg" +echo "" + +# Test case 3: Both FQ and same +echo "Test 3: Both Fully Qualified (Same)" +echo " Filter args: ['org.eclipse.jdt.core.dom.IDocElement']" +echo " Problem args: ['org.eclipse.jdt.core.dom.IDocElement']" +echo " Expected: MATCH (exact match)" +echo " Logic: problemArg == filterArg" +echo "" + +# Test case 4: Both FQ but different +echo "Test 4: Both Fully Qualified (Different)" +echo " Filter args: ['org.eclipse.jdt.core.dom.IDocElement']" +echo " Problem args: ['org.eclipse.jdt.core.dom.OtherType']" +echo " Expected: NO MATCH (different types)" +echo " Logic: Both have dots, not equal -> return false" +echo "" + +# Test case 5: Filter FQ, problem simple +echo "Test 5: Filter FQ, Problem Simple" +echo " Filter args: ['org.eclipse.jdt.core.dom.IDocElement']" +echo " Problem args: ['IDocElement']" +echo " Expected: MATCH (extract simple name from filter)" +echo " Logic: filterArg.substring(lastIndexOf('.')+1) == problemArg" +echo "" + +echo "To run actual unit tests:" +echo " cd apitools/org.eclipse.pde.api.tools.tests" +echo " mvn test -Dtest=FilterMatchingRegressionTests" +echo "" +echo "To enable debug logging in Eclipse:" +echo " Set system property: -DApiPlugin.DEBUG_FILTER_STORE=true" +echo " Or set ApiPlugin.DEBUG_FILTER_STORE = true in code" +echo "" +echo "To analyze JDT build:" +echo " 1. Enable DEBUG_FILTER_STORE in JDT build" +echo " 2. Look for messages like:" +echo " - 'no resource exists: [...]'" +echo " - 'no filters defined for [...]'" +echo " - 'no filter defined for problem: [...]'" +echo " - 'filter used: [...]' <- should see this if matching works" +echo " 3. Compare actual vs expected problem IDs, paths, type names, message args"