IG: add options to ignore subclasses of and ignore annotated by (IDEA-90060)

This commit is contained in:
Bas Leijdekkers
2019-01-23 11:19:07 +01:00
parent 6a6ca3b0f2
commit fe56defeec
8 changed files with 105 additions and 38 deletions
@@ -1740,6 +1740,9 @@ log.method.name=Logging method name
log.condition.text=Log condition text
ignore.if.annotated.by=Ignore if annotated by
ignore.parameter.if.annotated.by=Ignore parameter if it is annotated by
ignore.classes.annotated.by=Ignore classes annotated by
ignored.class.hierarchies.border.title=Ignore subclasses of
choose.class.hierarchy.to.ignore.title=Choose Class Hierarchy to Ignore
unqualified.inner.class.access.display.name=Unqualified inner class access
unqualified.inner.class.access.problem.descriptor=<code>#ref</code> is not qualified with outer class #loc
unqualified.inner.class.access.quickfix=Qualify with outer class
@@ -1,5 +1,5 @@
/*
* Copyright 2003-2012 Dave Griffith, Bas Leijdekkers
* Copyright 2003-2019 Dave Griffith, Bas Leijdekkers
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -15,16 +15,20 @@
*/
package com.siyeh.ig.logging;
import com.intellij.codeInspection.ui.ListTable;
import com.intellij.codeInspection.ui.ListWrappingTableModel;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInspection.util.SpecialAnnotationsUtil;
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.PsiUtil;
import com.intellij.ui.ScrollPaneFactory;
import com.intellij.ui.components.JBTabbedPane;
import com.intellij.util.ui.CheckBox;
import com.siyeh.InspectionGadgetsBundle;
import com.siyeh.ig.BaseInspection;
import com.siyeh.ig.BaseInspectionVisitor;
import com.siyeh.ig.ui.ExternalizableStringSet;
import com.siyeh.ig.ui.UiUtils;
import org.jdom.Element;
import org.jetbrains.annotations.NonNls;
@@ -34,6 +38,7 @@ import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
public class ClassWithoutLoggerInspection extends BaseInspection {
@@ -51,19 +56,33 @@ public class ClassWithoutLoggerInspection extends BaseInspection {
*/
public boolean ignoreSuperLoggers = false;
@SuppressWarnings("PublicField") public final ExternalizableStringSet annotations = new ExternalizableStringSet();
@SuppressWarnings("PublicField") public final ExternalizableStringSet ignoredClasses = new ExternalizableStringSet("java.lang.Throwable");
public ClassWithoutLoggerInspection() {
parseString(this.loggerNamesString, this.loggerNames);
parseString(loggerNamesString, loggerNames);
}
@Override
public JComponent createOptionsPanel() {
final JComponent panel = new JPanel(new BorderLayout());
final ListTable table = new ListTable(new ListWrappingTableModel(loggerNames, InspectionGadgetsBundle.message("logger.class.name")));
final JPanel tablePanel = UiUtils.createAddRemoveTreeClassChooserPanel(table, InspectionGadgetsBundle.message("choose.logger.class"));
final JPanel loggerPanel = UiUtils.createTreeClassChooserList(loggerNames, InspectionGadgetsBundle.message("logger.class.name"),
InspectionGadgetsBundle.message("choose.logger.class"));
final JPanel annotationsListControl =
SpecialAnnotationsUtil.createSpecialAnnotationsListControl(annotations,
InspectionGadgetsBundle.message("ignore.classes.annotated.by"));
final JPanel ignoredClassesPanel =
UiUtils.createTreeClassChooserList(ignoredClasses, InspectionGadgetsBundle.message("ignored.class.hierarchies.border.title"),
InspectionGadgetsBundle.message("choose.class.hierarchy.to.ignore.title"));
final CheckBox checkBox = new CheckBox(InspectionGadgetsBundle.message("super.class.logger.option"), this, "ignoreSuperLoggers");
panel.add(tablePanel, BorderLayout.CENTER);
final JComponent panel = new JPanel(new BorderLayout());
panel.add(ignoredClassesPanel, BorderLayout.CENTER);
panel.add(checkBox, BorderLayout.SOUTH);
return panel;
final JBTabbedPane tabs = new JBTabbedPane(SwingConstants.TOP);
tabs.add("Loggers", ScrollPaneFactory.createScrollPane(loggerPanel, true));
tabs.add("Ignored classes", ScrollPaneFactory.createScrollPane(panel, true));
tabs.add("Annotations", ScrollPaneFactory.createScrollPane(annotationsListControl, true));
return tabs;
}
@Override
@@ -87,7 +106,9 @@ public class ClassWithoutLoggerInspection extends BaseInspection {
@Override
public void writeSettings(@NotNull Element element) throws WriteExternalException {
loggerNamesString = formatString(loggerNames);
super.writeSettings(element);
defaultWriteSettings(element, "annotations", "ignoredClasses");
annotations.writeSettings(element, "annotations");
ignoredClasses.writeSettings(element, "ignoredClasses");
}
@Override
@@ -99,31 +120,21 @@ public class ClassWithoutLoggerInspection extends BaseInspection {
@Override
public void visitClass(@NotNull PsiClass aClass) {
//no recursion to avoid drilldown
if (aClass.isInterface() || aClass.isEnum() ||
aClass.isAnnotationType()) {
if (aClass.isInterface() || aClass.isEnum() || aClass.isAnnotationType() || aClass.getContainingClass() != null) {
return;
}
if (aClass instanceof PsiTypeParameter ||
aClass instanceof PsiAnonymousClass) {
if (aClass instanceof PsiTypeParameter || aClass instanceof PsiAnonymousClass) {
return;
}
if (aClass.getContainingClass() != null) {
if (ignoredClasses.stream().anyMatch(ignoredClass -> InheritanceUtil.isInheritor(aClass, ignoredClass))) {
return;
}
final PsiField[] fields;
if (ignoreSuperLoggers) {
fields = aClass.getAllFields();
if (AnnotationUtil.isAnnotated(aClass, annotations, AnnotationUtil.CHECK_EXTERNAL | AnnotationUtil.CHECK_HIERARCHY)) {
return;
}
else {
fields = aClass.getFields();
}
for (PsiField field : fields) {
if (isLogger(field)) {
if (PsiUtil.isAccessible(field, aClass, aClass)) {
return;
}
}
final PsiField[] fields = ignoreSuperLoggers ? aClass.getAllFields() : aClass.getFields();
if (Stream.of(fields).anyMatch(field -> isLogger(field) && PsiUtil.isAccessible(field, aClass, aClass))) {
return;
}
registerClassError(aClass);
}
@@ -1,10 +0,0 @@
package com.siyeh.igtest.logging;
public class ClassWithoutLoggerInspection {
private ClassWithoutLoggerInspection() {
}
public static void main(String[] args) {
}
}
@@ -0,0 +1,4 @@
import test.*;
@Entity
class Annotation {}
@@ -0,0 +1,6 @@
import java.util.logging.*;
class ClassWithLogger {
public static final Logger LOG = Logger.getLogger("test");
}
@@ -0,0 +1,10 @@
package com.siyeh.igtest.logging;
public class <warning descr="Class 'ClassWithoutLogger' does not declare a logger">ClassWithoutLogger</warning> {
private ClassWithoutLogger() {
}
public static void main(String[] args) {
}
}
@@ -0,0 +1,3 @@
class MyException extends RuntimeException {
// no logger
}
@@ -0,0 +1,40 @@
// 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.
package com.siyeh.ig.logging;
import com.intellij.codeInspection.InspectionProfileEntry;
import com.siyeh.ig.LightInspectionTestCase;
import org.jetbrains.annotations.Nullable;
/**
* @author Bas Leijdekkers
*/
public class ClassWithoutLoggerInspectionTest extends LightInspectionTestCase {
public void testClassWithoutLogger() { doTest();}
public void testClassWithLogger() { doTest();}
public void testMyException() { doTest();}
public void testAnnotation() { doTest();}
@Nullable
@Override
protected InspectionProfileEntry getInspection() {
final ClassWithoutLoggerInspection inspection = new ClassWithoutLoggerInspection();
inspection.annotations.add("test.Entity");
return inspection;
}
@Override
protected String[] getEnvironmentClasses() {
return new String[] {
"package java.util.logging;" +
"public class Logger {" +
" public static Logger getLogger(String name) {" +
" return null;" +
" }" +
"}",
"package test;" +
"public @interface Entity {}"
};
}
}