diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties index 184b76bdbfbc..284e38e0f2ba 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/InspectionGadgetsBundle.properties @@ -2030,7 +2030,8 @@ package.info.without.package.quickfix=add ''package {0};'' package.info.without.package.family.quickfix=add package statement auto.closeable.resource.display.name=AutoCloseable used without 'try'-with-resources auto.closeable.resource.problem.descriptor=''{0}'' used without ''try''-with-resources statement #loc -auto.closeable.resource.returned.option=Ignore AutoCloseable instances returned from method calls +auto.closeable.resource.quickfix=Ignore 'AutoCloseable' returned by this method +auto.closeable.resource.returned.option=Ignore AutoCloseable instances returned from all method calls problematic.varargs.method.display.name=Non-varargs method overrides varargs method problematic.varargs.method.override.problem.descriptor=Non-varargs method #ref() overrides varargs method #loc negatively.named.boolean.variable.problem.descriptor=Boolean variable #ref is negatively named #loc diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/MethodMatcher.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/MethodMatcher.java index 6c1e5ce16f23..40c7c7a2f6b7 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/MethodMatcher.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/psiutils/MethodMatcher.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -60,6 +60,44 @@ public class MethodMatcher { return this; } + public void add(@NotNull PsiMethodCallExpression expression) { + final PsiMethod method = expression.resolveMethod(); + if (method != null) { + add(method); + } + } + + public void add(@NotNull PsiMethod method) { + final PsiClass aClass = method.getContainingClass(); + if (aClass == null) { + return; + } + final String fqName = aClass.getQualifiedName(); + final int index = myClassNames.indexOf(fqName); + final String methodName = method.getName(); + if (index < 0) { + myClassNames.add(fqName); + myMethodNamePatterns.add(methodName); + } + else { + final String pattern = myMethodNamePatterns.get(index); + if (pattern.isEmpty()) { + myMethodNamePatterns.set(index, methodName); + return; + } + else if (".*".equals(pattern)) { + return; + } + final String[] names = pattern.split("\\|"); + for (String name : names) { + if (methodName.equals(name)) { + return; + } + } + myMethodNamePatterns.set(index, pattern + '|' + methodName); + } + } + @NotNull protected String getOptionName() { return myOptionName; diff --git a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/resources/AutoCloseableResourceInspectionBase.java b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/resources/AutoCloseableResourceInspectionBase.java index 3212844c0957..20b5d837c8fb 100644 --- a/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/resources/AutoCloseableResourceInspectionBase.java +++ b/plugins/InspectionGadgets/InspectionGadgetsAnalysis/src/com/siyeh/ig/resources/AutoCloseableResourceInspectionBase.java @@ -15,18 +15,23 @@ */ package com.siyeh.ig.resources; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.openapi.project.Project; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.psi.*; import com.intellij.psi.util.InheritanceUtil; +import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.BaseInspectionVisitor; -import com.siyeh.ig.psiutils.MethodCallUtils; +import com.siyeh.ig.InspectionGadgetsFix; +import com.siyeh.ig.psiutils.MethodMatcher; import com.siyeh.ig.psiutils.TypeUtils; import org.jdom.Element; import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.Arrays; @@ -43,6 +48,16 @@ public class AutoCloseableResourceInspectionBase extends ResourceInspection { public boolean ignoreFromMethodCall = false; final List ignoredTypes = new ArrayList(DEFAULT_IGNORED_TYPES); + protected final MethodMatcher myMethodMatcher; + + public AutoCloseableResourceInspectionBase() { + myMethodMatcher = new MethodMatcher() + .add("java.util.Formatter", "format") + .add("java.io.Writer", "append") + .add("com.google.common.base.Preconditions", "checkNotNull") + .add("org.hibernate.Session", "close") + .finishDefault(); + } @Nls @NotNull @@ -65,6 +80,43 @@ public class AutoCloseableResourceInspectionBase extends ResourceInspection { return InspectionGadgetsBundle.message("auto.closeable.resource.problem.descriptor", text); } + @Nullable + @Override + protected InspectionGadgetsFix buildFix(Object... infos) { + final boolean buildQuickfix = ((Boolean)infos[1]).booleanValue(); + if (!buildQuickfix) { + return null; + } + return new AutoCloseableResourceFix(); + } + + private class AutoCloseableResourceFix extends InspectionGadgetsFix { + + @Nls + @NotNull + @Override + public String getName() { + return InspectionGadgetsBundle.message("auto.closeable.resource.quickfix"); + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return getName(); + } + + @Override + protected void doFix(Project project, ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PsiMethodCallExpression methodCallExpression = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); + if (methodCallExpression == null) { + return; + } + myMethodMatcher.add(methodCallExpression); + } + } + @Override public void readSettings(@NotNull Element node) throws InvalidDataException { super.readSettings(node); @@ -78,6 +130,7 @@ public class AutoCloseableResourceInspectionBase extends ResourceInspection { } } } + myMethodMatcher.readSettings(node); } @Override @@ -88,6 +141,7 @@ public class AutoCloseableResourceInspectionBase extends ResourceInspection { final String ignoredTypesString = formatString(ignoredTypes); node.addContent(new Element("option").setAttribute("name", "ignoredTypes").setAttribute("value", ignoredTypesString)); } + myMethodMatcher.writeSettings(node); } @Override @@ -114,25 +168,16 @@ public class AutoCloseableResourceInspectionBase extends ResourceInspection { if (!isNotSafelyClosedResource(expression)) { return; } - registerNewExpressionError(expression, expression.getType()); + registerNewExpressionError(expression, expression.getType(), Boolean.FALSE); } @Override public void visitMethodCallExpression(PsiMethodCallExpression expression) { super.visitMethodCallExpression(expression); - if (ignoreFromMethodCall) { + if (ignoreFromMethodCall || myMethodMatcher.matches(expression) || !isNotSafelyClosedResource(expression)) { return; } - if (MethodCallUtils.isCallToMethod(expression, "java.util.Formatter", null, "format", null) || - MethodCallUtils.isCallToMethod(expression, "java.io.Writer", null, "append", null) || - MethodCallUtils.isCallToMethod(expression, "com.google.common.base.Preconditions", null, "checkNotNull", null) || - MethodCallUtils.isCallToMethod(expression, "org.hibernate.Session", null, "close")) { - return; - } - if (!isNotSafelyClosedResource(expression)) { - return; - } - registerMethodCallError(expression, expression.getType()); + registerMethodCallError(expression, expression.getType(), Boolean.TRUE); } @Override @@ -150,7 +195,7 @@ public class AutoCloseableResourceInspectionBase extends ResourceInspection { return; } } - registerError(expression, type); + registerError(expression, type, Boolean.FALSE); } private boolean isNotSafelyClosedResource(PsiExpression expression) { diff --git a/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java b/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java index d66e2f25dece..64117ddfe710 100644 --- a/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java +++ b/plugins/InspectionGadgets/src/com/siyeh/ig/resources/AutoCloseableResourceInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2015 JetBrains s.r.o. + * Copyright 2000-2016 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. @@ -17,31 +17,53 @@ package com.siyeh.ig.resources; import com.intellij.codeInspection.ui.ListTable; import com.intellij.codeInspection.ui.ListWrappingTableModel; -import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; +import com.intellij.ui.IdeBorderFactory; +import com.intellij.ui.components.panels.VerticalLayout; +import com.intellij.util.ui.CheckBox; import com.siyeh.InspectionGadgetsBundle; import com.siyeh.ig.ui.UiUtils; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; +import java.util.Arrays; /** * @author Bas Leijdekkers */ public class AutoCloseableResourceInspection extends AutoCloseableResourceInspectionBase { + @NotNull @Override public JComponent createOptionsPanel() { - final JComponent panel = new JPanel(new BorderLayout()); + final JComponent panel = new JPanel(new VerticalLayout(2)); final ListTable table = new ListTable(new ListWrappingTableModel(ignoredTypes, InspectionGadgetsBundle.message("ignored.autocloseable.types.column.label"))); final JPanel tablePanel = UiUtils.createAddRemoveTreeClassChooserPanel(table, InspectionGadgetsBundle.message("choose.autocloseable.type.to.ignore.title"), "java.lang.AutoCloseable"); - final MultipleCheckboxOptionsPanel checkboxPanel = new MultipleCheckboxOptionsPanel(this); - checkboxPanel.addCheckbox(InspectionGadgetsBundle.message("auto.closeable.resource.returned.option"), "ignoreFromMethodCall"); - checkboxPanel.addCheckbox(InspectionGadgetsBundle.message("any.method.may.close.resource.argument"), "anyMethodMayClose"); - panel.add(tablePanel, BorderLayout.CENTER); - panel.add(checkboxPanel, BorderLayout.SOUTH); + final ListTable table2 = new ListTable( + new ListWrappingTableModel(Arrays.asList(myMethodMatcher.getClassNames(), myMethodMatcher.getMethodNamePatterns()), + InspectionGadgetsBundle.message("result.of.method.call.ignored.class.column.title"), + InspectionGadgetsBundle.message("method.name.regex"))) { + @Override + public void setEnabled(boolean enabled) { + // hack to display correctly on initial opening of + // inspection settings (otherwise it is always enabled) + super.setEnabled(enabled && !ignoreFromMethodCall); + } + }; + final JPanel tablePanel2 = UiUtils.createAddRemoveTreeClassChooserPanel(table2, "Choose class"); + final JPanel wrapperPanel = new JPanel(new BorderLayout()); + wrapperPanel.setBorder(IdeBorderFactory.createTitledBorder("Ignore AutoCloseable instances returned from these methods", false)); + wrapperPanel.add(tablePanel2); + panel.add(tablePanel); + panel.add(wrapperPanel); + final CheckBox checkBox = + new CheckBox(InspectionGadgetsBundle.message("auto.closeable.resource.returned.option"), this, "ignoreFromMethodCall"); + checkBox.addChangeListener(e -> table2.setEnabled(!ignoreFromMethodCall)); + panel.add(checkBox); + panel.add(new CheckBox(InspectionGadgetsBundle.message("any.method.may.close.resource.argument"), this, "anyMethodMayClose")); return panel; } } diff --git a/plugins/InspectionGadgets/src/inspectionDescriptions/AutoCloseableResource.html b/plugins/InspectionGadgets/src/inspectionDescriptions/AutoCloseableResource.html index 57010958fa0b..9a58ab92b4cd 100644 --- a/plugins/InspectionGadgets/src/inspectionDescriptions/AutoCloseableResource.html +++ b/plugins/InspectionGadgets/src/inspectionDescriptions/AutoCloseableResource.html @@ -1,14 +1,16 @@ -Reports AutoCloseable instances which are not used in a try-with-resources statement, also known as +Reports AutoCloseable instances which are not used in a try-with-resources statement, also known as Automatic Resource Management. This means that the open resource before/in try, close in finally style which was used before -try-with-resources was available is also reported. +try-with-resources was available is also reported. This inspection is meant to replace all opened but not safely closed inspections when developing in Java 7 and higher.

-Use the table below to specify which AutoCloseable subclasses should be ignored by this inspection. +Use the first table below to specify which AutoCloseable subclasses should be ignored by this inspection. Specify AutoCloseable subclasses here which do not need to be closed.

+Use the second table below to specify which methods returning AutoCloseable will be ignored when called. +

Use the first checkbox below to ignore an AutoCloseable if it the result of a method call. When enabled, the results of factory methods will also be ignored.