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
Original file line number Diff line number Diff line change
Expand Up @@ -371,4 +371,28 @@ private void x14(boolean inc) {
String typename = "Etest14"; //$NON-NLS-1$
deployLeakTest(typename + ".java", inc);//$NON-NLS-1$
}

/**
* Tests that a static inner class implementing its parent interface does NOT produce
* a leak warning (inner class referencing enclosing type should be excluded)
* using a full build
*/
public void testStaticInnerClassImplementsParent15F() {
x15(false);
}

/**
* Tests that a static inner class implementing its parent interface does NOT produce
* a leak warning (inner class referencing enclosing type should be excluded)
* using an incremental build
*/
public void testStaticInnerClassImplementsParent15I() {
x15(true);
}

private void x15(boolean inc) {
expectingNoProblems();
String typename = "Etest15"; //$NON-NLS-1$
deployLeakTest(typename + ".java", inc);//$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -252,4 +252,37 @@ public void testAnnotateStoreFromBundle() throws CoreException {
assertFalse("the new filter store must not be an instance of ApiFilterStore", store instanceof ApiFilterStore); //$NON-NLS-1$
assertTrue("the new filter store must be an instance of FilterStore", store instanceof FilterStore); //$NON-NLS-1$
}

/**
* Tests that filters work correctly when the resource path uses portable string format.
* This test verifies that problems created with toPortableString() paths are correctly
* matched with filters, which is important for Tycho builds where paths may differ from IDE builds.
*/
@Test
public void testFilterWithPortablePath() throws CoreException {
IApiBaseline profile = ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline();
IApiComponent component = profile.getApiComponent(TESTING_PLUGIN_PROJECT_NAME);
assertNotNull("the testing project api component must exist", component); //$NON-NLS-1$
IProject project = getTestingJavaProject(TESTING_PLUGIN_PROJECT_NAME).getProject();
IResource resource = project.findMember(IPath.fromOSString("src/x/y/z/C4.java")); //$NON-NLS-1$
assertNotNull("the resource src/x/y/z/C4.java must exist", resource); //$NON-NLS-1$

// Create a problem with portable path (as done in BaseApiAnalyzer)
String portablePath = resource.getProjectRelativePath().toPortableString();
IApiProblem problem = ApiProblemFactory.newApiProblem(portablePath,
null, null, null, null, -1, -1, -1, IApiProblem.CATEGORY_USAGE, 0, RestrictionModifiers.NO_IMPLEMENT,
IApiProblem.NO_FLAGS);

// Add filter
IApiFilterStore store = component.getFilterStore();
store.addFiltersFor(new IApiProblem[] { problem });

// Create another problem with the same portable path to test isFiltered
IApiProblem testProblem = ApiProblemFactory.newApiProblem(portablePath,
null, null, null, null, -1, -1, -1, IApiProblem.CATEGORY_USAGE, 0, RestrictionModifiers.NO_IMPLEMENT,
IApiProblem.NO_FLAGS);

// This should work with the fix (fromPortableString instead of fromOSString)
assertTrue("problem with portable path should be filtered", store.isFiltered(testProblem)); //$NON-NLS-1$
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
/*******************************************************************************
* Copyright (c) 2025 IBM Corporation 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:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package x.y.z;

/**
* Test case for static inner class implementing parent interface.
* This pattern should NOT produce a leak warning as the inner class
* is referencing its enclosing type which has the same visibility.
*/
public interface Etest15 {

/**
* Static inner class implementing the parent interface.
* This should not be reported as leaking non-API interface.
*/
static class Etest15Impl implements Etest15 {
// Implementation
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ private Image getApiProblemElementImage(IApiProblem problem) {
case IDelta.API_BASELINE_ELEMENT_TYPE:
return getBaselineImage();
case IDelta.API_COMPONENT_ELEMENT_TYPE: {
IPath path = IPath.fromOSString(problem.getResourcePath());
IPath path = IPath.fromPortableString(problem.getResourcePath());
// try to find the component via the resource handle
IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember(path);
if (res != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public synchronized void addFilters(IApiProblemFilter[] filters) {
if (resourcePath == null) {
continue;
}
IResource resource = fProject.getProject().findMember(IPath.fromOSString(resourcePath));
IResource resource = fProject.getProject().findMember(IPath.fromPortableString(resourcePath));
if (resource == null) {
continue;
}
Expand Down Expand Up @@ -247,7 +247,7 @@ public synchronized boolean isFiltered(IApiProblem problem) {
if (resourcePath == null) {
return false;
}
IResource resource = fProject.getProject().findMember(IPath.fromOSString(resourcePath));
IResource resource = fProject.getProject().findMember(IPath.fromPortableString(resourcePath));
if (resource == null) {
if (ApiPlugin.DEBUG_FILTER_STORE) {
System.out.println("no resource exists: [" + resourcePath + "]"); //$NON-NLS-1$ //$NON-NLS-2$
Expand Down Expand Up @@ -317,7 +317,7 @@ public synchronized boolean removeFilters(IApiProblemFilter[] filters) {
if (resourcePath == null) {
continue;
}
IResource resource = fProject.getProject().findMember(IPath.fromOSString(resourcePath));
IResource resource = fProject.getProject().findMember(IPath.fromPortableString(resourcePath));
if (resource == null) {
resource = fProject.getProject().getFile(resourcePath);
}
Expand Down Expand Up @@ -499,7 +499,7 @@ protected synchronized void internalAddFilters(IApiProblem[] problems, String[]
if (resourcePath == null) {
continue;
}
IResource resource = fProject.getProject().findMember(IPath.fromOSString(resourcePath));
IResource resource = fProject.getProject().findMember(IPath.fromPortableString(resourcePath));
if (resource == null) {
continue;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ protected boolean problemsMatch(IApiProblem filterProblem, IApiProblem problem)
// one is missing a path they may still be equal
String problemPath = problem.getResourcePath();
String filterProblemPath = filterProblem.getResourcePath();
if (problemPath != null && filterProblemPath != null && !(IPath.fromOSString(problemPath).equals(IPath.fromOSString(filterProblemPath)))) {
if (problemPath != null && filterProblemPath != null && !(IPath.fromPortableString(problemPath).equals(IPath.fromPortableString(filterProblemPath)))) {
return false;
}
String problemTypeName = problem.getTypeName();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1150,7 +1150,7 @@ IResource resolveResource(IApiProblem problem) {
if (resourcePath == null) {
return null;
}
IResource resource = currentproject.findMember(IPath.fromOSString(resourcePath));
IResource resource = currentproject.findMember(IPath.fromPortableString(resourcePath));
if (resource == null) {
// might be re-exported try to look it up
IJavaProject jp = JavaCore.create(currentproject);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,15 @@ protected boolean consider(Reference ref) {
}
return false;
}
// Also check the reverse: if this type is a member of the referenced type
// (e.g., inner class referencing its enclosing type)
if (fType.getName().startsWith(referencedTypeName)) {
int refLength = referencedTypeName.length();
if (fType.getName().length() > refLength && fType.getName().charAt(refLength) == '$') {
// This is a member type referencing its enclosing type - exclude it
return false;
}
}
return true;
}

Expand Down