Skip to content

Commit

Permalink
Incorrect unused import warning
Browse files Browse the repository at this point in the history
Fixes #1808
The issue happens for imports used in the permits clause when that import is
- declared as a nested class
- is used only in a permits clause
The fix checks if member types are imported and marks the import as used.

Signed-off-by: Snjezana Peco <[email protected]>
  • Loading branch information
snjeza committed Feb 15, 2024
1 parent 6eab603 commit 9e72aa5
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1701,6 +1701,28 @@ private ReferenceBinding findPermittedtype(TypeReference typeReference) {
}
typeReference.bits |= ASTNode.IgnoreRawTypeCheck;
ReferenceBinding permittedType = (ReferenceBinding) typeReference.resolveType(this);
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1808#issuecomment-1918455864
if (permittedType != null && permittedType.isMemberType() && permittedType.isPrivate()) {
String name = CharOperation.toString(permittedType.compoundName);
name = name.replace("$", "."); //$NON-NLS-1$ //$NON-NLS-2$
char[][] compoundName = CharOperation.splitOn('.', name.toCharArray());
ImportBinding[] imports = unitScope.imports;
boolean resolved = false;
for (ImportBinding importBinding: imports) {
if (CharOperation.equals(compoundName, importBinding.compoundName)) {
resolved = true;
break;
}
}
if (!resolved) {
MissingTypeBinding miss = new MissingTypeBinding(permittedType.fPackage, compoundName,
env);
typeReference.resolvedType = miss;
typeReference.resolveType(this);
problemReporter().invalidType(typeReference, miss);
return miss;
}
}
return permittedType;
} catch (AbortCompilation e) {
SourceTypeBinding sourceType = this.referenceContext.binding;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3457,7 +3457,7 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
if (memberType.problemId() == ProblemReasons.Ambiguous) {
if (foundType == null || foundType.problemId() == ProblemReasons.NotVisible)
// supercedes any potential InheritedNameHidesEnclosingName problem
return memberType;
return checkUsed(name, scope, memberType);
// make the user qualify the type, likely wants the first inherited type
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
}
Expand All @@ -3468,7 +3468,7 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
// found a valid type in the 'immediate' scope (i.e. not inherited)
// OR in 1.4 mode (inherited visible shadows enclosing)
if (foundType == null || (inheritedHasPrecedence && foundType.problemId() == ProblemReasons.NotVisible))
return memberType;
return checkUsed(name, scope, memberType);
// if a valid type was found, complain when another is found in an 'immediate' enclosing type (i.e. not inherited)
if (foundType.isValidBinding() && TypeBinding.notEquals(foundType, memberType))
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
Expand Down Expand Up @@ -3656,6 +3656,26 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
return foundType;
}

private Binding checkUsed(char[] name, Scope scope, ReferenceBinding memberType) {
CompilationUnitScope unitScope = scope.compilationUnitScope();
ImportBinding[] imports = unitScope.imports;
HashtableOfObject typeOrPackageCache = unitScope.typeOrPackageCache;
if (imports != null && typeOrPackageCache == null) { // walk single type imports since faultInImports() has not run yet
for (int i = 0, length = imports.length; i < length; i++) {
ImportBinding importBinding = imports[i];
if (!importBinding.onDemand && CharOperation.equals(importBinding.getSimpleName(), name)) {
Binding resolvedImport = unitScope.resolveSingleImport(importBinding, Binding.TYPE);
if (resolvedImport instanceof TypeBinding) {
ImportReference importReference = importBinding.reference;
if (importReference != null && !isUnnecessarySamePackageImport(importBinding.resolvedImport, unitScope))
importReference.bits |= ASTNode.Used;
}
}
}
}
return memberType;
}

private boolean isUnnecessarySamePackageImport(Binding resolvedImport, Scope unitScope) {
if (resolvedImport instanceof ReferenceBinding) {
ReferenceBinding referenceBinding = (ReferenceBinding) resolvedImport;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,46 @@ boolean match(String err) {

return "StdErr: " + error + " StdOut: " + output;
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1808
public void testGH1808() {
this.runConformTest(
new String[] {
"p/X.java",
"""
package foo;
import foo.X.B;
public sealed interface X permits B {
record B(int data) implements X {}
}
"""
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -17 -g -preserveAllLocals -proceedOnError -referenceInfo ",
"",
"",
true);
}
public void testGH1808a() {
this.runNegativeTest(
new String[] {
"p/X.java",
"""
package foo;
public sealed interface X permits B {
record B(int data) {}
}
"""
},
"\"" + OUTPUT_DIR + File.separator + "p" + File.separator + "X.java\""
+ " -17 -g -preserveAllLocals -proceedOnError -referenceInfo ",
"",
"----------\n"
+ "1. ERROR in ---OUTPUT_DIR_PLACEHOLDER---/p/X.java (at line 2)\n"
+ " public sealed interface X permits B {\n"
+ " ^\n"
+ "B cannot be resolved to a type\n"
+ "----------\n"
+ "1 problem (1 error)\n",
true);
}
}

0 comments on commit 9e72aa5

Please sign in to comment.