diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java index 84d09bed80d0..31167933dcd3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionBase.java @@ -152,7 +152,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { } } - private static boolean isExternalizableNoParameterConstructor(@NotNull UMethod method, RefClass refClass) { + private static boolean isExternalizableNoParameterConstructor(@NotNull UMethod method, @Nullable RefClass refClass) { if (!method.isConstructor()) return false; if (method.getVisibility() != UastVisibility.PUBLIC) return false; final List parameterList = method.getUastParameters(); @@ -191,7 +191,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { return !(aClass != null && !isSerializable(aClass, refClass)); } - private static boolean isWriteReplaceMethod(@NotNull UMethod method, RefClass refClass) { + private static boolean isWriteReplaceMethod(@NotNull UMethod method, @Nullable RefClass refClass) { final String name = method.getName(); if (!"writeReplace".equals(name)) return false; List parameters = method.getUastParameters(); @@ -202,7 +202,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { return !(aClass != null && !isSerializable(aClass, refClass)); } - private static boolean isReadResolveMethod(@NotNull UMethod method, RefClass refClass) { + private static boolean isReadResolveMethod(@NotNull UMethod method, @Nullable RefClass refClass) { final String name = method.getName(); if (!"readResolve".equals(name)) return false; List parameters = method.getUastParameters(); @@ -426,14 +426,16 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { processedSuspicious.add(refMethod); if (refMethod instanceof RefImplicitConstructor) { RefClass ownerClass = refMethod.getOwnerClass(); - LOG.assertTrue(ownerClass != null); - visitClass(ownerClass); + if (ownerClass != null) { + visitClass(ownerClass); + } return; } if (refMethod.isConstructor()) { RefClass ownerClass = refMethod.getOwnerClass(); - LOG.assertTrue(ownerClass != null); - queryQualifiedNameUsages(ownerClass); + if (ownerClass != null) { + queryQualifiedNameUsages(ownerClass); + } } UMethod uMethod = (UMethod)refMethod.getUastElement(); if (uMethod != null && (isSerializablePatternMethod(uMethod, refMethod.getOwnerClass()) || @@ -504,7 +506,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { return true; } - private static boolean isSerializablePatternMethod(@NotNull UMethod psiMethod, RefClass refClass) { + private static boolean isSerializablePatternMethod(@NotNull UMethod psiMethod, @Nullable RefClass refClass) { return isReadObjectMethod(psiMethod, refClass) || isWriteObjectMethod(psiMethod, refClass) || isReadResolveMethod(psiMethod, refClass) || isWriteReplaceMethod(psiMethod, refClass) || isExternalizableNoParameterConstructor(psiMethod, refClass); } @@ -598,14 +600,6 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { else if (methodOwnerClass != null) { addInstantiatedClass(methodOwnerClass); } - else { - LOG.error("owner class is null for " + method.getPsiElement() - + " is static ? " + method.isStatic() - + "; is abstract ? " + method.isAbstract() - + "; is main method ? " + method.isAppMain() - + "; is constructor " + method.isConstructor() - + "; containing file " + method.getPointer().getVirtualFile().getFileType()); - } myProcessedMethods.add(method); makeContentReachable((RefJavaElementImpl)method); makeClassInitializersReachable(methodOwnerClass); @@ -616,7 +610,7 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { makeContentReachable((RefJavaElementImpl)method); } else { - addDelayedMethod(method); + addDelayedMethod(method, methodOwnerClass); } for (RefMethod refSub : method.getDerivedMethods()) { @@ -676,8 +670,8 @@ public class UnusedDeclarationInspectionBase extends GlobalInspectionTool { } } - private void addDelayedMethod(@NotNull RefMethod refMethod) { - Set methods = myClassIDtoMethods.computeIfAbsent(refMethod.getOwnerClass(), __ -> new HashSet<>()); + private void addDelayedMethod(@NotNull RefMethod refMethod, @NotNull RefClass ownerClass) { + Set methods = myClassIDtoMethods.computeIfAbsent(ownerClass, __ -> new HashSet<>()); methods.add(refMethod); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java index 9d0d57e7c81d..58b54714cb43 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/ex/EntryPointsManagerBase.java @@ -1,4 +1,4 @@ -// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInspection.ex; import com.intellij.codeInsight.AnnotationUtil; @@ -233,7 +233,7 @@ public abstract class EntryPointsManagerBase extends EntryPointsManager implemen if (newEntryPoint instanceof RefClass || newEntryPoint instanceof RefMethod) { RefClass refClass = newEntryPoint instanceof RefMethod ? ((RefMethod)newEntryPoint).getOwnerClass() : (RefClass)newEntryPoint; - if (!refClass.isAnonymous()) { + if (refClass != null && !refClass.isAnonymous()) { final ClassPattern classPattern = new ClassPattern(); classPattern.pattern = new SmartRefElementPointerImpl(refClass, true).getFQName(); if (newEntryPoint instanceof RefMethod && !(newEntryPoint instanceof RefImplicitConstructor)) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java index 74cce78468ca..d1282d37ee5b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefImplicitConstructorImpl.java @@ -1,18 +1,4 @@ -/* - * Copyright 2000-2015 JetBrains s.r.o. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInspection.reference; @@ -32,6 +18,11 @@ public class RefImplicitConstructorImpl extends RefMethodImpl implements RefImpl super(JavaAnalysisBundle.message("inspection.reference.implicit.constructor.name", ownerClass.getName()), ownerClass); } + @Override + public @NotNull RefClass getOwnerClass() { + return Objects.requireNonNull(super.getOwnerClass()); + } + @Override public void buildReferences() { getRefManager().fireBuildReferences(this); @@ -47,7 +38,6 @@ public class RefImplicitConstructorImpl extends RefMethodImpl implements RefImpl public String getName() { if (isValid()) { RefClass ownerClass = getOwnerClass(); - LOG.assertTrue(ownerClass != null); return JavaAnalysisBundle.message("inspection.reference.implicit.constructor.name", ownerClass.getName()); } return super.getName(); @@ -60,8 +50,7 @@ public class RefImplicitConstructorImpl extends RefMethodImpl implements RefImpl @Override public boolean isValid() { - RefClass ownerClass = getOwnerClass(); - return ownerClass != null && ReadAction.compute(ownerClass::isValid).booleanValue(); + return ReadAction.compute(getOwnerClass()::isValid).booleanValue(); } @NotNull @@ -88,8 +77,7 @@ public class RefImplicitConstructorImpl extends RefMethodImpl implements RefImpl @Nullable @Override public PsiElement getPsiElement() { - RefClass ownerClass = getOwnerClass(); - return ownerClass == null ? null : ownerClass.getPsiElement(); + return getOwnerClass().getPsiElement(); } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java index d3fe03f3ceab..ba98731b151c 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/reference/RefMethodImpl.java @@ -228,7 +228,13 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { @Override public boolean hasBody() { - return !isAbstract() && !getOwnerClass().isInterface() || !isBodyEmpty(); + if (!isAbstract()) { + RefClass ownerClass = getOwnerClass(); + if (ownerClass != null && !ownerClass.isInterface()) { + return true; + } + } + return !isBodyEmpty(); } private void initializeSuperMethods(PsiMethod method) { @@ -416,11 +422,7 @@ public class RefMethodImpl extends RefJavaElementImpl implements RefMethod { @Nullable @Override public RefClass getOwnerClass() { - WritableRefEntity owner = getOwner(); - if (owner != null && !(owner instanceof RefClass)) { - LOG.error("Unexpected owner: " + owner.getExternalName()); - } - return ObjectUtils.tryCast(owner, RefClass.class); + return ObjectUtils.tryCast(getOwner(), RefClass.class); } @NotNull diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java index 4934c5d416fd..e69740b99773 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/DeadHTMLComposer.java @@ -1,4 +1,4 @@ -// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. package com.intellij.codeInspection.deadCode; @@ -14,6 +14,7 @@ import org.jetbrains.annotations.NotNull; import java.util.Collections; import java.util.HashSet; +import java.util.Objects; import java.util.Set; public class DeadHTMLComposer extends HTMLComposerImpl { @@ -145,7 +146,7 @@ public class DeadHTMLComposer extends HTMLComposerImpl { @Override public void visitMethod(@NotNull RefMethod method) { RefClass refClass = method.getOwnerClass(); if (method.isExternalOverride()) { - String classOrInterface = HTMLJavaHTMLComposer.getClassOrInterface(refClass, false); + String classOrInterface = HTMLJavaHTMLComposer.getClassOrInterface(Objects.requireNonNull(refClass), false); buf.append(AnalysisBundle.message("inspection.dead.code.problem.synopsis22", classOrInterface)); } else if (method.isStatic() || method.isConstructor()) { int nRefs = method.getInReferences().size(); diff --git a/java/java-tests/testData/inspection/jvm/deadCode/topLevelFunction/expected.xml b/java/java-tests/testData/inspection/jvm/deadCode/topLevelFunction/expected.xml new file mode 100644 index 000000000000..5383140aacf2 --- /dev/null +++ b/java/java-tests/testData/inspection/jvm/deadCode/topLevelFunction/expected.xml @@ -0,0 +1,9 @@ + + + + TC.kt + 1 + + Method is never used. + + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/jvm/deadCode/topLevelFunction/src/TC.kt b/java/java-tests/testData/inspection/jvm/deadCode/topLevelFunction/src/TC.kt new file mode 100644 index 000000000000..1a0d5af2ce34 --- /dev/null +++ b/java/java-tests/testData/inspection/jvm/deadCode/topLevelFunction/src/TC.kt @@ -0,0 +1,2 @@ +inline fun runApplication(vararg args: String) {} +fun main(args: Array) {} \ No newline at end of file diff --git a/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationKtTest.java b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationKtTest.java index af9d77b705f1..f30ce3d2b6ff 100644 --- a/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationKtTest.java +++ b/jvm/jvm-analysis-kotlin-tests/testSrc/com/intellij/codeInspection/UnusedDeclarationKtTest.java @@ -87,4 +87,8 @@ public class UnusedDeclarationKtTest extends AbstractUnusedDeclarationTest { public void testClassLiteralRef() { doTest(); } + + public void testTopLevelFunction() { + doTest(); + } }