This is an automated email from the ASF dual-hosted git repository.
dbalek pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/netbeans.git
The following commit(s) were added to refs/heads/master by this push:
new 0665eb1 Delete unused private/package private elements hint fix
added. (#3841)
0665eb1 is described below
commit 0665eb12aaa64cbe3b7bae657f46594a579a41cc
Author: Dusan Balek <[email protected]>
AuthorDate: Thu Mar 24 15:51:35 2022 +0100
Delete unused private/package private elements hint fix added. (#3841)
---
.../java/editor/base/semantic/UnusedDetector.java | 127 +++++++++++++++--
.../base/semantic/DetectorTest/testColorings1.pass | 2 +-
.../testImportDisambiguation203874.pass | 4 +-
.../DetectorTest/testMultiFields116520a.pass | 4 +-
.../DetectorTest/testMultiFields116520b.pass | 4 +-
.../DetectorTest/testSemanticInnerClasses.pass | 2 +-
.../DetectorTest/testStaticImport189226.pass | 4 +-
.../testTwoPackagePrivateConstructors.pass | 4 +-
.../semantic/DetectorTest/testUnusedImports.pass | 2 +-
.../DetectorTest/testUsedImport129988.pass | 2 +-
.../java/editor/base/semantic/DetectorTest.java | 14 +-
.../netbeans/modules/java/hints/bugs/Unused.java | 22 ++-
.../java/lsp/server/protocol/ServerTest.java | 8 +-
java/java.source.base/apichanges.xml | 12 ++
java/java.source.base/nbproject/project.properties | 2 +-
.../netbeans/api/java/source/TreeUtilities.java | 14 +-
java/spi.java.hints/nbproject/project.properties | 2 +-
.../netbeans/spi/java/hints/JavaFixUtilities.java | 151 ++++++++++++++++++++-
18 files changed, 334 insertions(+), 46 deletions(-)
diff --git
a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java
b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java
index 2f1a47a..35cfe12 100644
---
a/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java
+++
b/java/java.editor.base/src/org/netbeans/modules/java/editor/base/semantic/UnusedDetector.java
@@ -25,7 +25,6 @@ import com.sun.source.tree.EnhancedForLoopTree;
import com.sun.source.tree.IdentifierTree;
import com.sun.source.tree.MemberReferenceTree;
import com.sun.source.tree.MemberSelectTree;
-import com.sun.source.tree.MethodInvocationTree;
import com.sun.source.tree.MethodTree;
import com.sun.source.tree.NewClassTree;
import com.sun.source.tree.Tree;
@@ -33,6 +32,7 @@ import com.sun.source.tree.Tree.Kind;
import com.sun.source.tree.VariableTree;
import com.sun.source.util.TreePath;
import com.sun.source.util.TreePathScanner;
+import java.io.IOException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
@@ -44,6 +44,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
@@ -51,9 +52,13 @@ import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
+import org.netbeans.api.java.source.ClassIndex;
import org.netbeans.api.java.source.CompilationInfo;
import org.netbeans.api.java.source.ElementHandle;
+import org.netbeans.api.java.source.JavaSource;
import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
+import org.openide.filesystems.FileObject;
+import org.openide.util.Exceptions;
/**
*
@@ -101,25 +106,44 @@ public class UnusedDetector {
TreePath declaration = e.getValue();
Set<UseTypes> uses = uv.useTypes.getOrDefault(el,
Collections.emptySet());
boolean isPrivate = el.getModifiers().contains(Modifier.PRIVATE);
//TODO: effectivelly private!
- if (isLocalVariableClosure(el) || (el.getKind().isField() &&
isPrivate)) {
+ boolean isPkgPrivate = !isPrivate &&
!el.getModifiers().contains(Modifier.PUBLIC) &&
!el.getModifiers().contains(Modifier.PROTECTED);
+ if (isLocalVariableClosure(el)) {
+ boolean isWritten = uses.contains(UseTypes.WRITTEN);
+ boolean isRead = uses.contains(UseTypes.READ);
+ if (!isWritten && !isRead) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_WRITTEN_READ));
+ } else if (!isWritten) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_WRITTEN));
+ } else if (!isRead) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_READ));
+ }
+ } else if (el.getKind().isField() && (isPrivate || isPkgPrivate)) {
if (!isSerialSpecField(info, el)) {
boolean isWritten = uses.contains(UseTypes.WRITTEN);
boolean isRead = uses.contains(UseTypes.READ);
if (!isWritten && !isRead) {
- result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_WRITTEN_READ));
+ if (isPrivate || isUnusedInPkg(info, el)) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_WRITTEN_READ));
+ }
} else if (!isWritten) {
result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_WRITTEN));
} else if (!isRead) {
- result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_READ));
+ if (isPrivate || isUnusedInPkg(info, el)) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_READ));
+ }
}
}
- } else if ((el.getKind() == ElementKind.CONSTRUCTOR ||
el.getKind() == ElementKind.METHOD) && isPrivate) {
+ } else if ((el.getKind() == ElementKind.CONSTRUCTOR ||
el.getKind() == ElementKind.METHOD) && (isPrivate || isPkgPrivate)) {
if (!isSerializationMethod(info, (ExecutableElement)el) &&
!uses.contains(UseTypes.USED)) {
- result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_USED));
+ if (isPrivate || isUnusedInPkg(info, el)) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_USED));
+ }
}
- } else if ((el.getKind().isClass() || el.getKind().isInterface())
&& isPrivate) {
+ } else if ((el.getKind().isClass() || el.getKind().isInterface())
&& (isPrivate || isPkgPrivate)) {
if (!uses.contains(UseTypes.USED)) {
- result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_USED));
+ if (isPrivate || isUnusedInPkg(info, el)) {
+ result.add(new UnusedDescription(el, declaration,
UnusedReason.NOT_USED));
+ }
}
}
}
@@ -248,6 +272,88 @@ public class UnusedDetector {
LOCAL_VARIABLES.contains(el.getKind());
}
+ private static boolean isUnusedInPkg(CompilationInfo info, Element el) {
+ TypeElement typeElement;
+ Set<? extends String> packageSet =
Collections.singleton(info.getElements().getPackageOf(el).getQualifiedName().toString());
+ Set<ClassIndex.SearchKind> searchKinds;
+ Set<ClassIndex.SearchScopeType> scope = Collections.singleton(new
ClassIndex.SearchScopeType() {
+ @Override
+ public Set<? extends String> getPackages() {
+ return packageSet;
+ }
+
+ @Override
+ public boolean isSources() {
+ return true;
+ }
+
+ @Override
+ public boolean isDependencies() {
+ return false;
+ }
+ });
+ switch (el.getKind()) {
+ case FIELD:
+ typeElement =
info.getElementUtilities().enclosingTypeElement(el);
+ searchKinds =
EnumSet.of(ClassIndex.SearchKind.FIELD_REFERENCES);
+ break;
+ case METHOD:
+ case CONSTRUCTOR:
+ typeElement =
info.getElementUtilities().enclosingTypeElement(el);
+ searchKinds =
EnumSet.of(ClassIndex.SearchKind.METHOD_REFERENCES);
+ break;
+ case ANNOTATION_TYPE:
+ case CLASS:
+ case ENUM:
+ case INTERFACE:
+ List<? extends TypeElement> topLevelElements =
info.getTopLevelElements();
+ if (topLevelElements.size() == 1 && topLevelElements.get(0) ==
el) {
+ return false;
+ }
+ typeElement = (TypeElement) el;
+ searchKinds =
EnumSet.of(ClassIndex.SearchKind.TYPE_REFERENCES);
+ break;
+
+ default:
+ return true;
+ }
+ ElementHandle eh = ElementHandle.create(el);
+ Set<FileObject> res =
info.getClasspathInfo().getClassIndex().getResources(ElementHandle.create(typeElement),
searchKinds, scope);
+ for (FileObject fo : res) {
+ if (fo != info.getFileObject()) {
+ JavaSource js = JavaSource.forFileObject(fo);
+ if (js == null) {
+ return false;
+ }
+ AtomicBoolean found = new AtomicBoolean();
+ try {
+ js.runUserActionTask(cc -> {
+ cc.toPhase(JavaSource.Phase.RESOLVED);
+ new TreePathScanner<Void, Element>() {
+ @Override
+ public Void scan(Tree tree, Element p) {
+ if (!found.get() && tree != null) {
+ Element element =
cc.getTrees().getElement(new TreePath(getCurrentPath(), tree));
+ if (element != null &&
eh.signatureEquals(element)) {
+ found.set(true);
+ }
+ super.scan(tree, p);
+ }
+ return null;
+ }
+ }.scan(new TreePath(cc.getCompilationUnit()), el);
+ }, true);
+ } catch (IOException ex) {
+ Exceptions.printStackTrace(ex);
+ }
+ if (found.get()) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
private enum UseTypes {
READ, WRITTEN, USED;
}
@@ -295,8 +401,9 @@ public class UnusedDetector {
}
boolean isPrivate = el.getModifiers().contains(Modifier.PRIVATE);
//TODO: effectivelly private!
+ boolean isPkgPrivate = !isPrivate &&
!el.getModifiers().contains(Modifier.PUBLIC) &&
!el.getModifiers().contains(Modifier.PROTECTED);
- if (isLocalVariableClosure(el) || (el.getKind().isField() &&
isPrivate)) {
+ if (isLocalVariableClosure(el) || (el.getKind().isField() &&
(isPrivate | isPkgPrivate))) {
TreePath effectiveUse = getCurrentPath();
boolean isWrite = false;
boolean isRead = false;
@@ -343,7 +450,7 @@ public class UnusedDetector {
if (isRead) {
addUse(el, UseTypes.READ);
}
- } else if (isPrivate) {
+ } else if (isPrivate | isPkgPrivate) {
if (el.getKind() != ElementKind.METHOD || recursionDetector !=
el)
addUse(el, UseTypes.USED);
}
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testColorings1.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testColorings1.pass
index d86a957..b7fda67 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testColorings1.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testColorings1.pass
@@ -18,7 +18,7 @@
[PUBLIC, INTERFACE], 17:11-17:15
[PUBLIC, DEPRECATED, FIELD, DECLARATION], 17:16-17:21
[PUBLIC, INTERFACE], 19:11-19:15
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 19:16-19:21
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 19:16-19:21
[PUBLIC, INTERFACE], 21:14-21:18
[PROTECTED, FIELD, DECLARATION], 21:19-21:24
[STATIC, PUBLIC, METHOD, DECLARATION], 23:23-23:27
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testImportDisambiguation203874.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testImportDisambiguation203874.pass
index c340c4d..fb0784f 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testImportDisambiguation203874.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testImportDisambiguation203874.pass
@@ -1,7 +1,7 @@
[PUBLIC, INTERFACE], 2:17-2:21
[PUBLIC, CLASS, DECLARATION], 6:13-6:33
[PUBLIC, INTERFACE], 8:4-8:8
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 8:9-8:10
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 8:9-8:10
[PUBLIC, CONSTRUCTOR], 8:17-8:26
[ABSTRACT, PUBLIC, CLASS], 9:4-9:13
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 9:14-9:15
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 9:14-9:15
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520a.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520a.pass
index 428e5c4..6521042 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520a.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520a.pass
@@ -1,3 +1,3 @@
[PUBLIC, CLASS, DECLARATION], 2:13-2:24
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 4:9-4:11
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 4:13-4:15
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 4:9-4:11
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 4:13-4:15
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520b.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520b.pass
index 428e5c4..6521042 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520b.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testMultiFields116520b.pass
@@ -1,3 +1,3 @@
[PUBLIC, CLASS, DECLARATION], 2:13-2:24
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 4:9-4:11
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 4:13-4:15
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 4:9-4:11
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 4:13-4:15
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testSemanticInnerClasses.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testSemanticInnerClasses.pass
index 9135616..8f65897 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testSemanticInnerClasses.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testSemanticInnerClasses.pass
@@ -15,4 +15,4 @@
[PUBLIC, CONSTRUCTOR, DECLARATION], 34:15-34:21
[PRIVATE, CLASS, DECLARATION], 39:18-39:24
[PUBLIC, CONSTRUCTOR, DECLARATION], 40:15-40:21
-[PACKAGE_PRIVATE, CLASS, DECLARATION], 45:10-45:16
+[PACKAGE_PRIVATE, CLASS, UNUSED, DECLARATION], 45:10-45:16
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testStaticImport189226.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testStaticImport189226.pass
index fe329d2..cb284c1 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testStaticImport189226.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testStaticImport189226.pass
@@ -3,6 +3,6 @@
[PUBLIC, CLASS], 3:26-3:37
[PACKAGE_PRIVATE, CLASS, DECLARATION], 5:6-5:24
[ABSTRACT, PUBLIC, CLASS], 7:4-7:9
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 7:10-7:11
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 8:8-8:9
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 7:10-7:11
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 8:8-8:9
[STATIC, PUBLIC, FIELD], 8:12-8:26
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testTwoPackagePrivateConstructors.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testTwoPackagePrivateConstructors.pass
index 950b968..8c3a5b7 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testTwoPackagePrivateConstructors.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testTwoPackagePrivateConstructors.pass
@@ -1,4 +1,4 @@
[PUBLIC, CLASS, DECLARATION], 2:13-2:42
-[PACKAGE_PRIVATE, CONSTRUCTOR, DECLARATION], 3:4-3:33
-[PACKAGE_PRIVATE, CONSTRUCTOR, DECLARATION], 4:4-4:33
+[PACKAGE_PRIVATE, CONSTRUCTOR, UNUSED, DECLARATION], 3:4-3:33
+[PACKAGE_PRIVATE, CONSTRUCTOR, UNUSED, DECLARATION], 4:4-4:33
[PARAMETER, DECLARATION], 4:38-4:39
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUnusedImports.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUnusedImports.pass
index d946661..919d214 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUnusedImports.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUnusedImports.pass
@@ -32,7 +32,7 @@
[PUBLIC, CLASS], 19:41-19:47
[PUBLIC, INTERFACE], 19:59-19:71
[STATIC, PUBLIC, INTERFACE], 21:4-21:9
-[PACKAGE_PRIVATE, FIELD, DECLARATION], 21:10-21:11
+[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 21:10-21:11
[PUBLIC, INTERFACE], 23:12-23:16
[PRIVATE, FIELD, UNUSED, DECLARATION], 23:17-23:21
[PUBLIC, INTERFACE], 25:25-25:29
diff --git
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUsedImport129988.pass
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUsedImport129988.pass
index 1a4d812..0ef982c 100644
---
a/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUsedImport129988.pass
+++
b/java/java.editor.base/test/unit/data/goldenfiles/org/netbeans/modules/java/editor/base/semantic/DetectorTest/testUsedImport129988.pass
@@ -1,6 +1,6 @@
[PUBLIC, INTERFACE], 2:17-2:21
[PUBLIC, CLASS, DECLARATION], 4:13-4:29
-[PACKAGE_PRIVATE, METHOD, DECLARATION], 5:9-5:11
+[PACKAGE_PRIVATE, METHOD, UNUSED, DECLARATION], 5:9-5:11
[PUBLIC, CONSTRUCTOR], 6:12-6:28
[PUBLIC, INTERFACE], 6:29-6:33
[PUBLIC, CLASS], 6:34-6:40
diff --git
a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
index 4a00265..5440841 100644
---
a/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
+++
b/java/java.editor.base/test/unit/src/org/netbeans/modules/java/editor/base/semantic/DetectorTest.java
@@ -527,7 +527,7 @@ public class DetectorTest extends TestBase {
"[PUBLIC, RECORD, DECLARATION], 0:14-0:18",
"[PUBLIC, CLASS], 0:19-0:25",
"[PUBLIC, RECORD_COMPONENT, DECLARATION], 0:26-0:27",
- "[PACKAGE_PRIVATE, CLASS, DECLARATION], 1:6-1:7",
+ "[PACKAGE_PRIVATE, CLASS, UNUSED, DECLARATION], 1:6-1:7",
"[PUBLIC, CLASS], 2:11-2:17",
"[PUBLIC, METHOD, DECLARATION], 2:18-2:19",
"[PUBLIC, RECORD], 2:20-2:24",
@@ -591,7 +591,7 @@ public class DetectorTest extends TestBase {
"[PACKAGE_PRIVATE, CLASS, DECLARATION], 0:13-0:17",
"[KEYWORD], 1:0-1:3",
"[KEYWORD], 1:4-1:10",
- "[PACKAGE_PRIVATE, CLASS, DECLARATION], 1:17-1:22",
+ "[PACKAGE_PRIVATE, CLASS, UNUSED, DECLARATION], 1:17-1:22",
"[PACKAGE_PRIVATE, CLASS], 1:31-1:35");
}
@@ -638,7 +638,7 @@ public class DetectorTest extends TestBase {
"[PUBLIC, CLASS, DECLARATION], 0:13-0:30\n"
+ "[PUBLIC, CLASS], 1:4-1:10\n"
+ "[PACKAGE_PRIVATE, FIELD, DECLARATION], 1:11-1:19\n"
- + "[PACKAGE_PRIVATE, METHOD, DECLARATION], 2:9-2:11\n"
+ + "[PACKAGE_PRIVATE, METHOD, UNUSED, DECLARATION], 2:9-2:11\n"
+ "[PUBLIC, CLASS], 3:8-3:14\n"
+ "[LOCAL_VARIABLE, DECLARATION], 3:15-3:18\n"
+ "[LOCAL_VARIABLE], 4:16-4:19\n"
@@ -691,11 +691,11 @@ public class DetectorTest extends TestBase {
"}\n",
"[PUBLIC, CLASS, DECLARATION], 0:13-0:29",
"[PUBLIC, CLASS], 1:4-1:10",
- "[PACKAGE_PRIVATE, FIELD, DECLARATION], 1:11-1:13",
+ "[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 1:11-1:13",
"[UNINDENTED_TEXT_BLOCK], 2:13-2:27",
"[UNINDENTED_TEXT_BLOCK], 3:13-3:29",
"[PUBLIC, CLASS], 5:4-5:10",
- "[PACKAGE_PRIVATE, FIELD, DECLARATION], 5:11-5:13",
+ "[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 5:11-5:13",
"[UNINDENTED_TEXT_BLOCK], 6:16-6:27",
"[UNINDENTED_TEXT_BLOCK], 7:16-7:29");
}
@@ -739,7 +739,7 @@ public class DetectorTest extends TestBase {
"[PUBLIC, CLASS], 1:36-1:42",
"[PARAMETER, UNUSED, DECLARATION], 1:43-1:47",
"[PUBLIC, CLASS], 1:54-1:60",
- "[PACKAGE_PRIVATE, FIELD, DECLARATION], 1:61-1:65",
+ "[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 1:61-1:65",
"[PACKAGE_PRIVATE, CONSTRUCTOR], 2:19-2:25");
}
@@ -814,7 +814,7 @@ public class DetectorTest extends TestBase {
"}\n",
"[PUBLIC, CLASS, DECLARATION], 0:13-0:29",
"[PUBLIC, CLASS], 1:4-1:10",
- "[PACKAGE_PRIVATE, FIELD, DECLARATION], 1:11-1:13",
+ "[PACKAGE_PRIVATE, FIELD, UNUSED, DECLARATION], 1:11-1:13",
"[UNINDENTED_TEXT_BLOCK], 2:13-2:27",
"[UNINDENTED_TEXT_BLOCK], 6:13-6:29");
}
diff --git
a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java
b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java
index cc87f67..28b96bd 100644
--- a/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java
+++ b/java/java.hints/src/org/netbeans/modules/java/hints/bugs/Unused.java
@@ -25,9 +25,11 @@ import javax.lang.model.element.ElementKind;
import org.netbeans.modules.java.editor.base.semantic.UnusedDetector;
import
org.netbeans.modules.java.editor.base.semantic.UnusedDetector.UnusedDescription;
import org.netbeans.spi.editor.hints.ErrorDescription;
+import org.netbeans.spi.editor.hints.Fix;
import org.netbeans.spi.java.hints.ErrorDescriptionFactory;
import org.netbeans.spi.java.hints.Hint;
import org.netbeans.spi.java.hints.HintContext;
+import org.netbeans.spi.java.hints.JavaFixUtilities;
import org.netbeans.spi.java.hints.TriggerTreeKind;
import org.openide.util.NbBundle.Messages;
@@ -61,25 +63,37 @@ public class Unused {
"# {0} - element name",
"ERR_NotUsed={0} is never used",
"ERR_NotUsedConstructor=Constructor is never used",
+ "# {0} - element name",
+ "FIX_RemoveUsedElement=Remove unused \"{0}\"",
+ "FIX_RemoveUsedConstructor=Remove unused constructor",
})
private static ErrorDescription convertUnused(HintContext ctx,
UnusedDescription ud) {
//TODO: switch expression candidate!
String name = ud.unusedElement.getSimpleName().toString();
String message;
+ Fix fix = null;
switch (ud.reason) {
- case NOT_WRITTEN_READ: message =
Bundle.ERR_NeitherReadOrWritten(name); break;
- case NOT_WRITTEN: message = Bundle.ERR_NotWritten(name); break;
- case NOT_READ: message = Bundle.ERR_NotRead(name); break;
+ case NOT_WRITTEN_READ: message =
Bundle.ERR_NeitherReadOrWritten(name);
+ fix = JavaFixUtilities.removeFromParent(ctx,
Bundle.FIX_RemoveUsedElement(name), ud.unusedElementPath);
+ break;
+ case NOT_WRITTEN: message = Bundle.ERR_NotWritten(name);
+ break;
+ case NOT_READ: message = Bundle.ERR_NotRead(name);
+ fix = JavaFixUtilities.safelyRemoveFromParent(ctx,
Bundle.FIX_RemoveUsedElement(name), ud.unusedElementPath);
+ break;
case NOT_USED:
if (ud.unusedElement.getKind() == ElementKind.CONSTRUCTOR) {
message = Bundle.ERR_NotUsedConstructor();
+ fix = JavaFixUtilities.removeFromParent(ctx,
Bundle.FIX_RemoveUsedConstructor(), ud.unusedElementPath);
} else {
message = Bundle.ERR_NotUsed(name);
+ fix = JavaFixUtilities.removeFromParent(ctx,
Bundle.FIX_RemoveUsedElement(name), ud.unusedElementPath);
}
break;
default:
throw new IllegalStateException("Unknown unused type: " +
ud.reason);
}
- return ErrorDescriptionFactory.forName(ctx, ud.unusedElementPath,
message);
+ return fix != null ? ErrorDescriptionFactory.forName(ctx,
ud.unusedElementPath, message, fix)
+ : ErrorDescriptionFactory.forName(ctx,
ud.unusedElementPath, message);
}
}
diff --git
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
index 5a5acb1..0525b4a 100644
---
a/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
+++
b/java/java.lsp.server/test/unit/src/org/netbeans/modules/java/lsp/server/protocol/ServerTest.java
@@ -312,7 +312,7 @@ public class ServerTest extends NbTestCase {
public void testMain() throws Exception {
File src = new File(getWorkDir(), "Test.java");
src.getParentFile().mkdirs();
- String code = "public class Test { int i = \"\".hashCode(); public
void run() { this.test(); } /**Test.*/public void test() {} }";
+ String code = "public class Test { public int i = \"\".hashCode();
public void run() { this.test(); } /**Test.*/public void test() {} }";
try (Writer w = new FileWriter(src)) {
w.write(code);
}
@@ -331,8 +331,8 @@ public class ServerTest extends NbTestCase {
VersionedTextDocumentIdentifier id = new
VersionedTextDocumentIdentifier(1);
id.setUri(toURI(src));
server.getTextDocumentService().didChange(new
DidChangeTextDocumentParams(id, Arrays.asList(new
TextDocumentContentChangeEvent(new Range(new Position(0, hashCodeStart), new
Position(0, hashCodeStart + "hashCode".length())), "hashCode".length(),
"equ"))));
- assertDiags(diags, "Error:0:31-0:34");//errors
- assertDiags(diags, "Error:0:31-0:34");//hints
+ assertDiags(diags, "Error:0:38-0:41");//errors
+ assertDiags(diags, "Error:0:38-0:41");//hints
completion = server.getTextDocumentService().completion(new
CompletionParams(new TextDocumentIdentifier(toURI(src)), new Position(0,
hashCodeStart + 2))).get();
actualItems = completion.getRight().getItems().stream().map(ci ->
ci.getKind() + ":" + ci.getLabel()).collect(Collectors.toList());
if (jdk9Plus()) {
@@ -378,7 +378,7 @@ public class ServerTest extends NbTestCase {
assertEquals("(String) ", edit.getNewText());
server.getTextDocumentService().didChange(new
DidChangeTextDocumentParams(id, Arrays.asList(new
TextDocumentContentChangeEvent(new Range(new Position(0, closingBrace), new
Position(0, closingBrace)), 0, "public void assignToSelf(Object o) { o = o;
}"))));
assertDiags(diags, "Error:1:0-1:9");//errors
- assertDiags(diags, "Error:1:0-1:9", "Warning:0:148-0:153",
"Warning:0:152-0:153");//hints
+ assertDiags(diags, "Error:1:0-1:9", "Warning:0:155-0:160",
"Warning:0:159-0:160");//hints
}
/**
diff --git a/java/java.source.base/apichanges.xml
b/java/java.source.base/apichanges.xml
index 306d262..bf7cc2d 100644
--- a/java/java.source.base/apichanges.xml
+++ b/java/java.source.base/apichanges.xml
@@ -25,6 +25,18 @@
<apidef name="javasource_base">Java Source API</apidef>
</apidefs>
<changes>
+ <change id="TreeUtilities.isClassFile">
+ <api name="javasource_base" />
+ <summary>Adding
TreeUtilities.isExpressionStatement(ExpressionTree)</summary>
+ <version major="1" minor="2.56"/>
+ <date day="23" month="3" year="2021"/>
+ <author login="dbalek"/>
+ <compatibility addition="yes" binary="compatible" source="compatible"/>
+ <description>
+ Adding TreeUtilities.isExpressionStatement(ExpressionTree).
+ </description>
+ <class name="TreeUtilities" package="org.netbeans.api.java.source"/>
+ </change>
<change id="SourceUtils.isClassFile">
<api name="javasource_base" />
<summary>Adding SourceUtils.isClassFile(FileObject)</summary>
diff --git a/java/java.source.base/nbproject/project.properties
b/java/java.source.base/nbproject/project.properties
index e5b4d68..b62d7cf 100644
--- a/java/java.source.base/nbproject/project.properties
+++ b/java/java.source.base/nbproject/project.properties
@@ -23,7 +23,7 @@ javadoc.name=Java Source Base
javadoc.title=Java Source Base
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
-spec.version.base=2.55.0
+spec.version.base=2.56.0
test.qa-functional.cp.extra=${refactoring.java.dir}/modules/ext/nb-javac-api.jar
test.unit.run.cp.extra=${o.n.core.dir}/core/core.jar:\
${o.n.core.dir}/lib/boot.jar:\
diff --git
a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java
b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java
index 8aed857..73a7886 100644
--- a/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java
+++ b/java/java.source.base/src/org/netbeans/api/java/source/TreeUtilities.java
@@ -146,7 +146,7 @@ public final class TreeUtilities {
}
/**
- * Checks wheteher given variable tree represents an enum constant.
+ * Checks whether given variable tree represents an enum constant.
*/
public boolean isEnumConstant(VariableTree tree) {
return (((JCTree.JCModifiers) tree.getModifiers()).flags & Flags.ENUM)
!= 0;
@@ -161,7 +161,7 @@ public final class TreeUtilities {
}
/**
- * Checks wheteher given compilation unit represents a package-info.
+ * Checks whether given compilation unit represents a package-info.
* @since 2.23
*/
public boolean isPackageInfo(CompilationUnitTree tree) {
@@ -169,13 +169,21 @@ public final class TreeUtilities {
}
/**
- * Checks wheteher given compilation unit represents a module-info.
+ * Checks whether given compilation unit represents a module-info.
* @since 2.23
*/
public boolean isModuleInfo(CompilationUnitTree tree) {
return TreeInfo.isModuleInfo((JCTree.JCCompilationUnit)tree);
}
+ /**
+ * Checks whether given expression represents an expression statement.
+ * @since 2.56
+ */
+ public boolean isExpressionStatement(ExpressionTree tree) {
+ return TreeInfo.isExpressionStatement((JCTree.JCExpression)tree);
+ }
+
/**Returns whether or not the given tree is synthetic - generated by the
parser.
* Please note that this method does not check trees transitively - a
child of a syntetic tree
* may be considered non-syntetic.
diff --git a/java/spi.java.hints/nbproject/project.properties
b/java/spi.java.hints/nbproject/project.properties
index 84c91b9..5a5fb84 100644
--- a/java/spi.java.hints/nbproject/project.properties
+++ b/java/spi.java.hints/nbproject/project.properties
@@ -17,7 +17,7 @@
is.autoload=true
javac.source=1.8
javac.compilerargs=-Xlint -Xlint:-serial
-spec.version.base=1.47.0
+spec.version.base=1.48.0
requires.nb.javac=true
javadoc.arch=${basedir}/arch.xml
javadoc.apichanges=${basedir}/apichanges.xml
diff --git
a/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java
b/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java
index c632a57..748159d 100644
--- a/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java
+++ b/java/spi.java.hints/src/org/netbeans/spi/java/hints/JavaFixUtilities.java
@@ -58,6 +58,7 @@ import com.sun.source.tree.VariableTree;
import com.sun.source.tree.WhileLoopTree;
import com.sun.source.util.SourcePositions;
import com.sun.source.util.TreePath;
+import com.sun.source.util.TreePathScanner;
import com.sun.source.util.TreeScanner;
import org.netbeans.api.java.source.support.ErrorAwareTreePathScanner;
import org.netbeans.api.java.source.support.ErrorAwareTreeScanner;
@@ -78,6 +79,7 @@ import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Callable;
+import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
@@ -224,7 +226,21 @@ public class JavaFixUtilities {
* @return an editor fix that removes the give tree from the source code
*/
public static Fix removeFromParent(HintContext ctx, String displayName,
TreePath what) {
- return new RemoveFromParent(displayName, ctx.getInfo(),
what).toEditorFix();
+ return new RemoveFromParent(displayName, ctx.getInfo(), what,
false).toEditorFix();
+ }
+
+ /**Creates a fix that removes the given code corresponding to the given
tree
+ * node together with all its usages from the source code
+ *
+ * @param ctx basic context for which the fix should be created
+ * @param displayName the display name of the fix
+ * @param what the tree node that should be removed
+ * @return an editor fix that removes the give tree from the source code
+ *
+ * @since 1.48
+ */
+ public static Fix safelyRemoveFromParent(HintContext ctx, String
displayName, TreePath what) {
+ return RemoveFromParent.canSafelyRemove(ctx.getInfo(), what) ? new
RemoveFromParent(displayName, ctx.getInfo(), what, true).toEditorFix() : null;
}
private static String defaultFixDisplayName(CompilationInfo info,
Map<String, TreePath> variables, String replaceTarget) {
@@ -1612,10 +1628,12 @@ public class JavaFixUtilities {
private static final class RemoveFromParent extends JavaFix {
private final String displayName;
+ private final boolean safely;
- public RemoveFromParent(String displayName, CompilationInfo info,
TreePath toRemove) {
+ public RemoveFromParent(String displayName, CompilationInfo info,
TreePath toRemove, boolean safely) {
super(info, toRemove);
this.displayName = displayName;
+ this.safely = safely;
}
@Override
@@ -1629,6 +1647,24 @@ public class JavaFixUtilities {
TreePath tp = ctx.getPath();
doRemoveFromParent(wc, tp);
+ if (safely) {
+ Element el = wc.getTrees().getElement(tp);
+ if (el != null) {
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree tree, Void p) {
+ if (tree != null && tree != tp.getLeaf()) {
+ TreePath treePath = new
TreePath(getCurrentPath(), tree);
+ Element e = wc.getTrees().getElement(treePath);
+ if (el == e) {
+ doRemoveFromParent(wc, treePath);
+ }
+ }
+ return super.scan(tree, p);
+ }
+ }.scan(new TreePath(wc.getCompilationUnit()), null);
+ }
+ }
}
private void doRemoveFromParent(WorkingCopy wc, TreePath what) {
@@ -1768,12 +1804,123 @@ public class JavaFixUtilities {
case EXPRESSION_STATEMENT:
doRemoveFromParent(wc, what.getParentPath());
break;
+ case ASSIGNMENT:
+ AssignmentTree assignmentTree = (AssignmentTree)
parentLeaf;
+ if (leaf == assignmentTree.getVariable()) {
+ if
(wc.getTreeUtilities().isExpressionStatement(assignmentTree.getExpression())) {
+ wc.rewrite(parentLeaf,
assignmentTree.getExpression());
+ } else {
+ doRemoveFromParent(wc, what.getParentPath());
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ break;
+ case AND_ASSIGNMENT:
+ case DIVIDE_ASSIGNMENT:
+ case LEFT_SHIFT_ASSIGNMENT:
+ case MINUS_ASSIGNMENT:
+ case MULTIPLY_ASSIGNMENT:
+ case OR_ASSIGNMENT:
+ case PLUS_ASSIGNMENT:
+ case REMAINDER_ASSIGNMENT:
+ case RIGHT_SHIFT_ASSIGNMENT:
+ case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+ case XOR_ASSIGNMENT:
+ CompoundAssignmentTree compoundAssignmentTree =
(CompoundAssignmentTree) parentLeaf;
+ if (leaf == compoundAssignmentTree.getVariable()) {
+ if
(wc.getTreeUtilities().isExpressionStatement(compoundAssignmentTree.getExpression()))
{
+ wc.rewrite(parentLeaf,
compoundAssignmentTree.getExpression());
+ } else {
+ doRemoveFromParent(wc, what.getParentPath());
+ }
+ } else {
+ throw new UnsupportedOperationException();
+ }
+ break;
default:
wc.rewrite(what.getLeaf(),
make.Block(Collections.<StatementTree>emptyList(), false));
break;
}
}
+ private static boolean canSafelyRemove(CompilationInfo info, TreePath
tp) {
+ AtomicBoolean ret = new AtomicBoolean(true);
+ Element el = info.getTrees().getElement(tp);
+ if (el != null) {
+ new TreePathScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree tree, Void p) {
+ if (tree != null && tree != tp.getLeaf()) {
+ TreePath treePath = new TreePath(getCurrentPath(),
tree);
+ Element e = info.getTrees().getElement(treePath);
+ if (el == e) {
+ Tree parentLeaf =
treePath.getParentPath().getLeaf();
+ switch (parentLeaf.getKind()) {
+ case ASSIGNMENT:
+ AssignmentTree assignmentTree =
(AssignmentTree) parentLeaf;
+ if (tree ==
assignmentTree.getVariable()) {
+ if
(!info.getTreeUtilities().isExpressionStatement(assignmentTree.getExpression())
&& canHaveSideEffects(assignmentTree.getExpression())) {
+ ret.set(false);
+ }
+ } else {
+ ret.set(false);
+ }
+ break;
+ case AND_ASSIGNMENT:
+ case DIVIDE_ASSIGNMENT:
+ case LEFT_SHIFT_ASSIGNMENT:
+ case MINUS_ASSIGNMENT:
+ case MULTIPLY_ASSIGNMENT:
+ case OR_ASSIGNMENT:
+ case PLUS_ASSIGNMENT:
+ case REMAINDER_ASSIGNMENT:
+ case RIGHT_SHIFT_ASSIGNMENT:
+ case UNSIGNED_RIGHT_SHIFT_ASSIGNMENT:
+ case XOR_ASSIGNMENT:
+ CompoundAssignmentTree
compoundAssignmentTree = (CompoundAssignmentTree) parentLeaf;
+ if (tree ==
compoundAssignmentTree.getVariable()) {
+ if
(!info.getTreeUtilities().isExpressionStatement(compoundAssignmentTree.getExpression())
&& canHaveSideEffects(compoundAssignmentTree.getExpression())) {
+ ret.set(false);
+ }
+ } else {
+ ret.set(false);
+ }
+ break;
+ default:
+ ret.set(false);
+ }
+ }
+ }
+ return super.scan(tree, p);
+ }
+ }.scan(new TreePath(info.getCompilationUnit()), null);
+ }
+ return ret.get();
+ }
+
+ private static boolean canHaveSideEffects(Tree tree) {
+ AtomicBoolean ret = new AtomicBoolean();
+ new TreeScanner<Void, Void>() {
+ @Override
+ public Void scan(Tree tree, Void p) {
+ if (tree != null) {
+ switch (tree.getKind()) {
+ case METHOD_INVOCATION:
+ case NEW_CLASS:
+ case POSTFIX_DECREMENT:
+ case POSTFIX_INCREMENT:
+ case PREFIX_DECREMENT:
+ case PREFIX_INCREMENT:
+ ret.set(true);
+ break;
+ }
+ }
+ return super.scan(tree, p);
+ }
+ }.scan(tree, null);
+ return ret.get();
+ }
}
//TODO: from FileMovePlugin
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]
For further information about the NetBeans mailing lists, visit:
https://cwiki.apache.org/confluence/display/NETBEANS/Mailing+lists