Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
154 changes: 154 additions & 0 deletions API_FILTER_REGRESSION_INVESTIGATION.md
Original file line number Diff line number Diff line change
@@ -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
<resource path="dom/org/eclipse/jdt/core/dom/AbstractTagElement.java" type="org.eclipse.jdt.core.dom.AbstractTagElement">
<filter id="576725006">
<message_arguments>
<message_argument value="IDocElement"/>
<message_argument value="AbstractTagElement"/>
</message_arguments>
</filter>
<filter id="576778288">
<message_arguments>
<message_argument value="ASTNode"/>
<message_argument value="AbstractTagElement"/>
</message_arguments>
</filter>
</resource>
```

### 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
187 changes: 187 additions & 0 deletions SUMMARY.md
Original file line number Diff line number Diff line change
@@ -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
<filter id="576725006">
<message_arguments>
<message_argument value="IDocElement"/> <!-- SIMPLE NAME -->
<message_argument value="AbstractTagElement"/> <!-- SIMPLE NAME -->
</message_arguments>
</filter>
```

### 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
Loading