redundant throws inspection:

1. global and local inspections are merged to one
2. local inspection reports "non-final" methods (if cheap enough) IDEA-177230
This commit is contained in:
Dmitry Batkovich
2017-08-09 12:14:26 +03:00
parent 259f33f9df
commit 52cf50c461
31 changed files with 353 additions and 284 deletions
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2009 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -20,6 +20,7 @@ import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
import com.intellij.openapi.command.undo.UndoUtil;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.*;
import com.intellij.psi.codeStyle.JavaCodeStyleManager;
import com.intellij.psi.util.PsiFormatUtil;
@@ -49,7 +50,7 @@ public class MethodThrowsFix extends LocalQuickFixOnPsiElement {
@Override
public String getText() {
return QuickFixBundle.message(myShouldThrow ? "fix.throws.list.add.exception" : "fix.throws.list.remove.exception",
myThrowsCanonicalText,
StringUtil.getShortName(myThrowsCanonicalText),
myMethodName);
}
@@ -1,60 +0,0 @@
/*
* Copyright 2000-2009 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.
*/
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.QuickFixBundle;
import com.intellij.codeInsight.daemon.impl.quickfix.MethodThrowsFix;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiClassType;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiFile;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.util.PsiTreeUtil;
import org.jetbrains.annotations.NotNull;
/**
* @author cdr
*/
public class DeleteThrowsFix implements LocalQuickFix {
private final MethodThrowsFix myQuickFix;
public DeleteThrowsFix(@NotNull PsiMethod method, PsiClassType exceptionClass) {
myQuickFix = new MethodThrowsFix(method, exceptionClass, false, false);
}
@Override
@NotNull
public String getName() {
return myQuickFix.getText();
}
@Override
@NotNull
public String getFamilyName() {
return QuickFixBundle.message("fix.throws.list.family");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
PsiElement element = descriptor.getPsiElement();
PsiMethod method = PsiTreeUtil.getParentOfType(element, PsiMethod.class);
if (method == null) return;
final PsiFile psiFile = method.getContainingFile();
if (myQuickFix.isAvailable(project, psiFile, method, method)) {
myQuickFix.invoke(project, psiFile, method, method);
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2016 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -16,6 +16,7 @@
package com.intellij.codeInspection.duplicateThrows;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.impl.quickfix.MethodThrowsFix;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel;
import com.intellij.psi.*;
@@ -82,7 +83,7 @@ public class DuplicateThrowsInspection extends BaseJavaBatchLocalInspectionTool
}
}
if (problem != null) {
holder.registerProblem(ref, problem, ProblemHighlightType.LIKE_UNUSED_SYMBOL, new DeleteThrowsFix(method, type));
holder.registerProblem(ref, problem, ProblemHighlightType.LIKE_UNUSED_SYMBOL, new MethodThrowsFix(method, type, false, false));
}
}
}
@@ -1,126 +0,0 @@
/*
* Copyright 2000-2013 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.
*/
package com.intellij.codeInspection.unneededThrows;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
import com.intellij.codeInspection.*;
import com.intellij.psi.*;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
/**
* @author anna
* @since 15-Nov-2005
*/
public class RedundantThrowsDeclaration extends BaseJavaBatchLocalInspectionTool implements CleanupLocalInspectionTool {
@Override
@NotNull
public String getGroupDisplayName() {
return GroupNames.DECLARATION_REDUNDANCY;
}
@Override
@NotNull
public String getDisplayName() {
return InspectionsBundle.message("redundant.throws.declaration");
}
@Override
@NotNull
@NonNls
public String getShortName() {
return "RedundantThrowsDeclaration";
}
@Override
@Nullable
public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull final InspectionManager manager, final boolean isOnTheFly) {
final Set<ProblemDescriptor> problems = new HashSet<>();
file.accept(new JavaRecursiveElementWalkingVisitor() {
@Override public void visitReferenceElement(PsiJavaCodeReferenceElement reference) {
final ProblemDescriptor descriptor = checkExceptionsNeverThrown(reference, manager, isOnTheFly);
if (descriptor != null) {
problems.add(descriptor);
}
}
});
return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]);
}
private static ProblemDescriptor checkExceptionsNeverThrown(PsiJavaCodeReferenceElement referenceElement,
InspectionManager inspectionManager,
boolean onTheFly) {
if (!(referenceElement.getParent() instanceof PsiReferenceList)) return null;
PsiReferenceList referenceList = (PsiReferenceList)referenceElement.getParent();
if (!(referenceList.getParent() instanceof PsiMethod)) return null;
PsiMethod method = (PsiMethod)referenceList.getParent();
if (referenceList != method.getThrowsList()) return null;
PsiClass containingClass = method.getContainingClass();
if (containingClass == null) return null;
PsiManager manager = referenceElement.getManager();
PsiClassType exceptionType = JavaPsiFacade.getInstance(manager.getProject()).getElementFactory().createType(referenceElement);
if (ExceptionUtil.isUncheckedExceptionOrSuperclass(exceptionType)) return null;
PsiCodeBlock body = method.getBody();
if (body == null) return null;
PsiModifierList modifierList = method.getModifierList();
if (!modifierList.hasModifierProperty(PsiModifier.PRIVATE)
&& !modifierList.hasModifierProperty(PsiModifier.STATIC)
&& !modifierList.hasModifierProperty(PsiModifier.FINAL)
&& !method.isConstructor()
&& !(containingClass instanceof PsiAnonymousClass)
&& !containingClass.hasModifierProperty(PsiModifier.FINAL)) {
return null;
}
Collection<PsiClassType> types = ExceptionUtil.collectUnhandledExceptions(body, method, false);
Collection<PsiClassType> unhandled = new HashSet<>(types);
if (method.isConstructor()) {
// there may be field initializer throwing exception
// that exception must be caught in the constructor
PsiField[] fields = containingClass.getFields();
for (final PsiField field : fields) {
if (field.hasModifierProperty(PsiModifier.STATIC)) continue;
PsiExpression initializer = field.getInitializer();
if (initializer == null) continue;
unhandled.addAll(ExceptionUtil.collectUnhandledExceptions(initializer, field));
}
}
for (PsiClassType unhandledException : unhandled) {
if (unhandledException.isAssignableFrom(exceptionType) || exceptionType.isAssignableFrom(unhandledException)) {
return null;
}
}
if (JavaHighlightUtil.isSerializationRelatedMethod(method, containingClass)) return null;
String description = JavaErrorMessages.message("exception.is.never.thrown", JavaHighlightUtil.formatType(exceptionType));
LocalQuickFix quickFixes = new DeleteThrowsFix(method, exceptionType);
return inspectionManager.createProblemDescriptor(referenceElement, description, quickFixes, ProblemHighlightType.LIKE_UNUSED_SYMBOL, onTheFly);
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
* Copyright 2000-2017 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.
@@ -18,7 +18,6 @@ package com.intellij.codeInspection.unneededThrows;
import com.intellij.analysis.AnalysisScope;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.FileModificationService;
import com.intellij.codeInsight.daemon.GroupNames;
import com.intellij.codeInspection.*;
import com.intellij.codeInspection.reference.*;
import com.intellij.openapi.diagnostic.Logger;
@@ -31,7 +30,7 @@ import com.intellij.psi.search.searches.AllOverridingMethodsSearch;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.Query;
import org.jetbrains.annotations.NonNls;
import org.jetbrains.annotations.Nls;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -41,10 +40,17 @@ import java.util.List;
/**
* @author max
*/
public class RedundantThrows extends GlobalJavaBatchInspectionTool {
public class RedundantThrowsDeclarationInspection extends GlobalJavaBatchInspectionTool {
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.unneededThrows.RedundantThrows");
private static final String DISPLAY_NAME = InspectionsBundle.message("inspection.redundant.throws.display.name");
@NonNls private static final String SHORT_NAME = "RedundantThrows";
private final RedundantThrowsDeclarationLocalInspection myLocalInspection = new RedundantThrowsDeclarationLocalInspection(this);
@Nls
@NotNull
@Override
public String getDisplayName() {
return InspectionsBundle.message("inspection.redundant.throws.display.name");
}
@Override
@Nullable
@@ -143,24 +149,6 @@ public class RedundantThrows extends GlobalJavaBatchInspectionTool {
return false;
}
@Override
@NotNull
public String getDisplayName() {
return DISPLAY_NAME;
}
@Override
@NotNull
public String getGroupDisplayName() {
return GroupNames.DECLARATION_REDUNDANCY;
}
@Override
@NotNull
public String getShortName() {
return SHORT_NAME;
}
@Override
@Nullable
public QuickFix getQuickFix(String hint) {
@@ -287,4 +275,10 @@ public class RedundantThrows extends GlobalJavaBatchInspectionTool {
}
}
}
@Nullable
@Override
public LocalInspectionTool getSharedLocalInspectionTool() {
return myLocalInspection;
}
}
@@ -0,0 +1,166 @@
/*
* Copyright 2000-2017 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.
*/
package com.intellij.codeInspection.unneededThrows;
import com.intellij.codeInsight.ExceptionUtil;
import com.intellij.codeInsight.daemon.JavaErrorMessages;
import com.intellij.codeInsight.daemon.impl.analysis.JavaHighlightUtil;
import com.intellij.codeInsight.daemon.impl.quickfix.MethodThrowsFix;
import com.intellij.codeInspection.*;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.PsiClassReferenceType;
import com.intellij.psi.impl.source.PsiImmediateClassType;
import com.intellij.util.ArrayUtil;
import com.siyeh.ig.JavaOverridingMethodUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.annotations.TestOnly;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
/**
* @author anna
* @since 15-Nov-2005
*/
public class RedundantThrowsDeclarationLocalInspection extends BaseJavaBatchLocalInspectionTool implements CleanupLocalInspectionTool {
private final RedundantThrowsDeclarationInspection myGlobalTool;
@TestOnly
public RedundantThrowsDeclarationLocalInspection() {this(new RedundantThrowsDeclarationInspection());}
public RedundantThrowsDeclarationLocalInspection(@NotNull RedundantThrowsDeclarationInspection tool) {myGlobalTool = tool;}
@Override
@NotNull
public String getGroupDisplayName() {
return myGlobalTool.getGroupDisplayName();
}
@Override
@NotNull
public String getDisplayName() {
return myGlobalTool.getDisplayName();
}
@Override
@NotNull
public String getShortName() {
return myGlobalTool.getShortName();
}
@Nullable
@Override
public ProblemDescriptor[] checkMethod(@NotNull PsiMethod method, @NotNull InspectionManager manager, boolean isOnTheFly) {
return checkExceptionsNeverThrown(method, manager);
}
@Nullable
private static ProblemDescriptor[] checkExceptionsNeverThrown(PsiMethod method,
InspectionManager inspectionManager) {
PsiClass containingClass = method.getContainingClass();
if (containingClass == null || JavaHighlightUtil.isSerializationRelatedMethod(method, containingClass)) return null;
PsiCodeBlock body = method.getBody();
if (body == null) return null;
ReferenceAndType[] thrownExceptions = getThrownCheckedExceptions(method);
if (thrownExceptions.length == 0) return null;
PsiModifierList modifierList = method.getModifierList();
boolean needCheckOverridingMethods = !(modifierList.hasModifierProperty(PsiModifier.PRIVATE) ||
modifierList.hasModifierProperty(PsiModifier.STATIC) ||
modifierList.hasModifierProperty(PsiModifier.FINAL) ||
method.isConstructor() ||
containingClass instanceof PsiAnonymousClass ||
containingClass.hasModifierProperty(PsiModifier.FINAL));
Collection<PsiClassType> types = ExceptionUtil.collectUnhandledExceptions(body, method, false);
Collection<PsiClassType> unhandled = new HashSet<>(types);
if (method.isConstructor()) {
// there may be field initializer throwing exception
// that exception must be caught in the constructor
PsiField[] fields = containingClass.getFields();
for (final PsiField field : fields) {
if (field.hasModifierProperty(PsiModifier.STATIC)) continue;
PsiExpression initializer = field.getInitializer();
if (initializer == null) continue;
unhandled.addAll(ExceptionUtil.collectUnhandledExceptions(initializer, field));
}
}
List<ReferenceAndType> candidates = Arrays.stream(thrownExceptions)
.filter(refAndType -> unhandled.stream().noneMatch(unhandledException -> unhandledException.isAssignableFrom(refAndType.type) || refAndType.type.isAssignableFrom(unhandledException)))
.collect(Collectors.toList());
if (candidates.isEmpty()) return null;
if (needCheckOverridingMethods) {
Set<String> thrownExceptionShortNames = candidates.stream().map(refAndType -> refAndType.type.getClassName()).collect(Collectors.toSet());
Predicate<PsiMethod> methodContainsThrownExceptions = m -> Arrays.stream(m.getThrowsList().getReferencedTypes())
.map(PsiClassType::getClassName)
.anyMatch(thrownExceptionShortNames::contains);
Stream<PsiMethod> overridingMethods = JavaOverridingMethodUtil.getOverridingMethodsIfCheapEnough(method, null, methodContainsThrownExceptions);
if (overridingMethods == null) return null;
Iterator<PsiMethod> overridingMethodIt = overridingMethods.iterator();
while (overridingMethodIt.hasNext()) {
PsiMethod m = overridingMethodIt.next();
PsiClassType[] overridingMethodThrownException = m.getThrowsList().getReferencedTypes();
candidates.removeIf(refAndType -> {
PsiClassType type = refAndType.type;
return ArrayUtil.contains(type, overridingMethodThrownException);
});
if (candidates.isEmpty()) return null;
}
}
return candidates.stream().map(exceptionType -> {
PsiJavaCodeReferenceElement reference = exceptionType.ref;
String description = JavaErrorMessages.message("exception.is.never.thrown", JavaHighlightUtil.formatType(exceptionType.type));
LocalQuickFix quickFix = new MethodThrowsFix(method, exceptionType.type, false, false);
return inspectionManager.createProblemDescriptor(reference, description, quickFix, ProblemHighlightType.LIKE_UNUSED_SYMBOL, true);
}).toArray(ProblemDescriptor[]::new);
}
private static ReferenceAndType[] getThrownCheckedExceptions(PsiMethod method) {
return Stream
.of(method.getThrowsList().getReferenceElements())
.map(ref -> {
PsiElement resolved = ref.resolve();
if (resolved instanceof PsiClass) {
return new ReferenceAndType(ref, (PsiClass)resolved);
}
return null;
})
.filter(Objects::nonNull)
.toArray(ReferenceAndType[]::new);
}
private static class ReferenceAndType {
private final PsiJavaCodeReferenceElement ref;
private final PsiClassType type;
private ReferenceAndType(@NotNull PsiJavaCodeReferenceElement ref, @NotNull PsiClass aClass) {
this.ref = ref;
type = new PsiImmediateClassType(aClass, PsiSubstitutor.EMPTY);
}
}
}
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2017 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.
*/
package com.intellij.codeInspection.unneededThrows;
import com.intellij.codeInspection.ex.InspectionElementsMerger;
import org.jetbrains.annotations.NotNull;
public class RedundantThrowsInspectionMerger extends InspectionElementsMerger {
@NotNull
@Override
public String getMergedToolName() {
return "RedundantThrows";
}
@NotNull
@Override
public String[] getSourceToolNames() {
return new String[] {"RedundantThrowsDeclaration"};
}
@NotNull
@Override
public String[] getSuppressIds() {
return getSourceToolNames();
}
}
@@ -6,7 +6,7 @@ class Test {
abstract void call(String[] f) throws FileNotFoundException, IOException;
}
void use(Target target) throws IOException {
void use(Target target) throws <warning descr="Exception 'java.io.IOException' is never thrown in the method">IOException</warning> {
try {
target.call("");
} catch (FileNotFoundException e) {
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "true"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Add 'java.io.IOException' to 'a.f' throws list" "true"
// "Add 'IOException' to 'a.f' throws list" "true"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "true"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -0,0 +1,13 @@
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
void f() throws IOException {
}
}
class b extends a {
void f() {
}
}
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "true"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "true"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Add 'java.io.IOException' to 'a.f' throws list" "true"
// "Add 'IOException' to 'a.f' throws list" "true"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "true"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "false"
// "Remove 'IOException' from 'f' throws list" "false"
import java.io.*;
class a {
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "false"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -0,0 +1,13 @@
// "Remove 'IOException' from 'f' throws list" "false"
import java.io.*;
class a {
void f() throws <caret>IOException {
}
}
class b extends a {
void f() throws IOException {
}
}
@@ -1,4 +1,4 @@
// "Remove 'java.io.IOException' from 'f' throws list" "true"
// "Remove 'IOException' from 'f' throws list" "true"
import java.io.*;
class a {
@@ -0,0 +1,8 @@
// "Suppress for method" "true"
import java.io.FileNotFoundException;
import java.io.IOException;
class Main {
@SuppressWarnings("RedundantThrowsDeclaration")
public void test() throws Exception, FileNotFoundException, IOException {}
}
@@ -1,4 +1,4 @@
// "Suppress for method" "false"
// "Suppress for method" "true"
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -30,7 +30,7 @@ import com.intellij.codeInspection.reference.EntryPoint;
import com.intellij.codeInspection.reference.RefElement;
import com.intellij.codeInspection.sillyAssignment.SillyAssignmentInspection;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclaration;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclarationLocalInspection;
import com.intellij.codeInspection.unusedImport.UnusedImportLocalInspection;
import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspectionBase;
import com.intellij.lang.Language;
@@ -100,7 +100,7 @@ public class LightAdvHighlightingTest extends LightDaemonAnalyzerTestCase {
new SillyAssignmentInspection(),
new AccessStaticViaInstance(),
new DeprecationInspection(),
new RedundantThrowsDeclaration(),
new RedundantThrowsDeclarationLocalInspection(),
new UnusedImportLocalInspection(),
new UncheckedWarningLocalInspection()
};
@@ -17,7 +17,7 @@ package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclaration;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclarationLocalInspection;
import org.jetbrains.annotations.NotNull;
@@ -25,7 +25,7 @@ public class MethodThrowsTest extends LightQuickFixParameterizedTestCase {
@NotNull
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[] {new RedundantThrowsDeclaration()};
return new LocalInspectionTool[] {new RedundantThrowsDeclarationLocalInspection()};
}
public void test() throws Exception { doAllTests(); }
@@ -24,7 +24,7 @@ import com.intellij.codeInspection.duplicateThrows.DuplicateThrowsInspection;
import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection;
import com.intellij.codeInspection.sillyAssignment.SillyAssignmentInspection;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclaration;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclarationLocalInspection;
import org.jetbrains.annotations.NotNull;
@@ -39,7 +39,7 @@ public class Suppress15InspectionsTest extends LightQuickFixParameterizedTestCas
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new RedundantThrowsDeclaration(),
new RedundantThrowsDeclarationLocalInspection(),
new SillyAssignmentInspection(),
new AccessStaticViaInstance(),
new DeprecationInspection(),
@@ -23,7 +23,7 @@ import com.intellij.codeInspection.deprecation.DeprecationInspection;
import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection;
import com.intellij.codeInspection.sillyAssignment.SillyAssignmentInspection;
import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclaration;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclarationLocalInspection;
import com.intellij.pom.java.LanguageLevel;
import org.jetbrains.annotations.NotNull;
@@ -44,7 +44,7 @@ public class SuppressNonInspectionsTest extends LightQuickFixParameterizedTestCa
@Override
protected LocalInspectionTool[] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new RedundantThrowsDeclaration(),
new RedundantThrowsDeclarationLocalInspection(),
new SillyAssignmentInspection(),
new AccessStaticViaInstance(),
new DeprecationInspection(),
@@ -16,7 +16,7 @@
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.unneededThrows.RedundantThrows;
import com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclarationInspection;
import com.intellij.testFramework.InspectionTestCase;
public class RedundantThrowTest extends InspectionTestCase {
@@ -30,7 +30,7 @@ public class RedundantThrowTest extends InspectionTestCase {
}
private void doTest(boolean checkRange) throws Exception {
final RedundantThrows tool = new RedundantThrows();
final RedundantThrowsDeclarationInspection tool = new RedundantThrowsDeclarationInspection();
doTest("redundantThrow/" + getTestName(false), tool, checkRange);
}
@@ -45,7 +45,7 @@ public class RedundantThrowTest extends InspectionTestCase {
public void testRemote() throws Exception { doTest(); }
public void testEntryPoint() throws Exception {
final RedundantThrows tool = new RedundantThrows();
final RedundantThrowsDeclarationInspection tool = new RedundantThrowsDeclarationInspection();
doTest("redundantThrow/" + getTestName(true), tool, false, true);
}
@@ -0,0 +1,61 @@
/*
* Copyright 2000-2017 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.
*/
package com.siyeh.ig;
import com.intellij.openapi.project.Project;
import com.intellij.psi.PsiMethod;
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopeUtil;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.util.PsiSuperMethodUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class JavaOverridingMethodUtil {
private static final int MAX_OVERRIDDEN_METHOD_SEARCH = 20;
@Nullable
public static Stream<PsiMethod> getOverridingMethodsIfCheapEnough(@NotNull PsiMethod method,
@Nullable GlobalSearchScope searchScope,
@NotNull Predicate<PsiMethod> preFilter) {
Project project = method.getProject();
String name = method.getName();
SearchScope useScope = method.getUseScope();
GlobalSearchScope effectiveSearchScope = GlobalSearchScopeUtil.toGlobalSearchScope(useScope, project);
if (searchScope != null) {
effectiveSearchScope = effectiveSearchScope.intersectWith(searchScope);
}
PsiMethod[] methods =
StubIndex.getElements(JavaStubIndexKeys.METHODS, name, project, effectiveSearchScope, PsiMethod.class)
.stream()
.filter(m -> m != method)
.filter(preFilter)
.limit(MAX_OVERRIDDEN_METHOD_SEARCH + 1)
.toArray(PsiMethod[]::new);
// search should be deterministic
if (methods.length > MAX_OVERRIDDEN_METHOD_SEARCH) {
return null;
}
return Stream.of(methods).filter(candidate -> PsiSuperMethodUtil.isSuperMethod(candidate, method));
}
}
@@ -27,28 +27,21 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.text.StringUtil;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.psi.*;
import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.GlobalSearchScopeUtil;
import com.intellij.psi.search.SearchScope;
import com.intellij.psi.stubs.StubIndex;
import com.intellij.psi.util.InheritanceUtil;
import com.intellij.psi.util.PsiSuperMethodUtil;
import com.intellij.psi.util.PsiUtil;
import com.intellij.util.ThreeState;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.JavaOverridingMethodUtil;
import com.siyeh.ig.psiutils.MethodUtils;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import javax.swing.*;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class MissingOverrideAnnotationInspection extends BaseJavaBatchLocalInspectionTool implements CleanupLocalInspectionTool{
private static final int MAX_OVERRIDDEN_METHOD_SEARCH = 20;
private static final String OVERRIDE_SHORT_NAME = StringUtil.getShortName(CommonClassNames.JAVA_LANG_OVERRIDE);
@SuppressWarnings({"PublicField"})
@@ -138,7 +131,8 @@ public class MissingOverrideAnnotationInspection extends BaseJavaBatchLocalInspe
Project project = method.getProject();
LanguageLevel minimal = Objects.requireNonNull(method.getContainingClass()).isInterface() ? LanguageLevel.JDK_1_6 : LanguageLevel.JDK_1_5;
Stream<PsiMethod> overridingMethods = getOverridingMethodsIfCheapEnough(method, getLanguageLevelScope(minimal, project), m -> {
Stream<PsiMethod> overridingMethods = JavaOverridingMethodUtil
.getOverridingMethodsIfCheapEnough(method, getLanguageLevelScope(minimal, project), m -> {
for (PsiAnnotation annotation : m.getModifierList().getAnnotations()) {
PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement();
if (ref != null && OVERRIDE_SHORT_NAME.equals(ref.getReferenceName())) {
@@ -231,29 +225,4 @@ public class MissingOverrideAnnotationInspection extends BaseJavaBatchLocalInspe
.map(Module::getModuleScope)
.toArray(GlobalSearchScope[]::new));
}
@Nullable
private static Stream<PsiMethod> getOverridingMethodsIfCheapEnough(@NotNull PsiMethod method,
@NotNull GlobalSearchScope searchScope,
@NotNull Predicate<PsiMethod> preFilter) {
Project project = method.getProject();
String name = method.getName();
SearchScope useScope = method.getUseScope();
GlobalSearchScope effectiveSearchScope = GlobalSearchScopeUtil.toGlobalSearchScope(useScope, project).intersectWith(searchScope);
PsiMethod[] methods =
StubIndex.getElements(JavaStubIndexKeys.METHODS, name, project, effectiveSearchScope, PsiMethod.class)
.stream()
.filter(m -> m != method)
.filter(preFilter)
.limit(MAX_OVERRIDDEN_METHOD_SEARCH + 1)
.toArray(PsiMethod[]::new);
// search should be deterministic
if (methods.length > MAX_OVERRIDDEN_METHOD_SEARCH) {
return null;
}
return Stream.of(methods).filter(candidate -> PsiSuperMethodUtil.isSuperMethod(candidate, method));
}
}
@@ -1,8 +0,0 @@
<html>
<body>
This inspection reports exceptions that are declared in a method's signature but never thrown by the method itself.
Only final, private or static methods are analyzed. For other methods, global code analysis is required.
For batch inspections, this can be achieved using
<nobr><i>Declaration Redundancy | Redundant throws clause</i>.</nobr>
</body>
</html>
+3 -5
View File
@@ -616,6 +616,7 @@
<externalProjectDataService implementation="com.intellij.externalSystem.JavaProjectDataService"/>
<inspectionElementsMerger implementation="com.intellij.codeInspection.deadCode.UnusedDeclarationInspectionMerger"/>
<inspectionElementsMerger implementation="com.intellij.codeInspection.java18api.Java8MapApiInspectionMerger"/>
<inspectionElementsMerger implementation="com.intellij.codeInspection.unneededThrows.RedundantThrowsInspectionMerger"/>
<globalInspection groupPath="Java" language="JAVA" shortName="unused" displayName="Unused declaration" groupBundle="messages.InspectionsBundle"
groupKey="group.names.declaration.redundancy" enabledByDefault="true" level="WARNING"
@@ -645,9 +646,9 @@
implementationClass="com.intellij.codeInspection.sameReturnValue.SameReturnValueInspection"/>
<globalInspection groupPath="Java" language="JAVA" shortName="EmptyMethod" displayName="Empty method" groupKey="group.names.declaration.redundancy" enabledByDefault="true" groupBundle="messages.InspectionsBundle"
level="WARNING" implementationClass="com.intellij.codeInspection.emptyMethod.EmptyMethodInspection"/>
<globalInspection groupPath="Java" language="JAVA" shortName="RedundantThrows" displayName="Redundant throws clause" groupKey="group.names.declaration.redundancy" groupBundle="messages.InspectionsBundle"
<globalInspection groupPath="Java" language="JAVA" shortName="RedundantThrows" cleanupTool="true" displayName="Redundant throws clause" groupKey="group.names.declaration.redundancy" groupBundle="messages.InspectionsBundle"
enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.unneededThrows.RedundantThrows"/>
implementationClass="com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclarationInspection"/>
<globalInspection groupPath="Java" language="JAVA" shortName="Java9RedundantRequiresStatement"
groupBundle="messages.InspectionsBundle" groupKey="group.names.declaration.redundancy"
enabledByDefault="true" level="WARNING" displayName="Redundant 'requires' statement in module-info"
@@ -740,9 +741,6 @@
<localInspection groupPath="Java" language="JAVA" shortName="DefaultAnnotationParam" bundle="messages.InspectionsBundle" key="inspection.default.annotation.param" groupKey="group.names.declaration.redundancy"
enabledByDefault="true" level="WARNING"
implementationClass="com.intellij.codeInspection.DefaultAnnotationParamInspection" />
<localInspection groupPath="Java" language="JAVA" shortName="RedundantThrowsDeclaration" bundle="messages.InspectionsBundle" key="redundant.throws.declaration"
groupKey="group.names.declaration.redundancy" enabledByDefault="false" level="WARNING" cleanupTool="true"
implementationClass="com.intellij.codeInspection.unneededThrows.RedundantThrowsDeclaration" />
<localInspection groupPath="Java" language="JAVA" shortName="AccessStaticViaInstance" bundle="messages.InspectionsBundle" key="access.static.via.instance" groupKey="group.names.declaration.redundancy"
enabledByDefault="true" level="WARNING" cleanupTool="true" alternativeId="static-access"
implementationClass="com.intellij.codeInspection.accessStaticViaInstance.AccessStaticViaInstance" />