From 71e60700a216a2c184809d7f7973cde8189c1d51 Mon Sep 17 00:00:00 2001 From: Anna Kozlova Date: Wed, 1 Oct 2014 17:43:26 +0200 Subject: [PATCH] api usage inspection: warn if old interface method was made default and class doesn't override it --- .../Java15APIUsageInspectionBase.java | 34 +++++++++++++++++++ .../usage1.5/defaultMethods/expected.xml | 10 ++++++ .../usage1.5/defaultMethods/src/Test.java | 29 ++++++++++++++++ .../JavaAPIUsagesInspectionTest.java | 15 ++++++++ .../src/messages/InspectionsBundle.properties | 2 ++ 5 files changed, 90 insertions(+) create mode 100644 java/java-tests/testData/inspection/usage1.5/defaultMethods/expected.xml create mode 100644 java/java-tests/testData/inspection/usage1.5/defaultMethods/src/Test.java diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java index 006e5d946c22..fdeb517f5a1d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java @@ -18,6 +18,7 @@ package com.intellij.codeInspection.java15api; import com.intellij.ToolExtensionPoints; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.daemon.GroupNames; +import com.intellij.codeInsight.intention.QuickFixFactory; import com.intellij.codeInspection.*; import com.intellij.openapi.extensions.ExtensionPoint; import com.intellij.openapi.extensions.ExtensionPointName; @@ -25,6 +26,8 @@ import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.EffectiveLanguageLevelUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.JavaVersionService; import com.intellij.openapi.util.InvalidDataException; import com.intellij.openapi.util.WriteExternalException; import com.intellij.openapi.vfs.CharsetToolkit; @@ -48,6 +51,8 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.lang.ref.Reference; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.Set; @@ -80,6 +85,11 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo ourGenerifiedClasses.add("javax.swing.ListModel"); ourGenerifiedClasses.add("javax.swing.JList"); } + + private static final Set ourDefaultMethods = new HashSet(); + static { + ourDefaultMethods.add("java.util.Iterator#remove()"); + } protected LanguageLevel myEffectiveLanguageLevel = null; @@ -192,6 +202,30 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo @Override public void visitClass(PsiClass aClass) { // Don't go into classes (anonymous, locals). + if (!aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + final LanguageLevel effectiveLanguageLevel = getEffectiveLanguageLevel(ModuleUtilCore.findModuleForPsiElement(aClass)); + if (!effectiveLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8) && + JavaVersionService.getInstance().getJavaSdkVersion(aClass).isAtLeast(JavaSdkVersion.JDK_1_8)) { + final List methods = new ArrayList(); + for (HierarchicalMethodSignature methodSignature : aClass.getVisibleSignatures()) { + final PsiMethod method = methodSignature.getMethod(); + if (ourDefaultMethods.contains(getSignature(method))) { + methods.add(method); + } + } + + if (!methods.isEmpty()) { + PsiElement element2Highlight = aClass.getNameIdentifier(); + if (element2Highlight == null) { + element2Highlight = aClass; + } + myHolder.registerProblem(element2Highlight, + methods.size() == 1 ? InspectionsBundle.message("inspection.1.8.problem.single.descriptor", methods.get(0).getName(), getJdkName(effectiveLanguageLevel)) + : InspectionsBundle.message("inspection.1.8.problem.descriptor", methods.size(), getJdkName(effectiveLanguageLevel)), + QuickFixFactory.getInstance().createImplementMethodsFix(aClass)); + } + } + } } @Override public void visitReferenceExpression(PsiReferenceExpression expression) { diff --git a/java/java-tests/testData/inspection/usage1.5/defaultMethods/expected.xml b/java/java-tests/testData/inspection/usage1.5/defaultMethods/expected.xml new file mode 100644 index 000000000000..f6d068d07ada --- /dev/null +++ b/java/java-tests/testData/inspection/usage1.5/defaultMethods/expected.xml @@ -0,0 +1,10 @@ + + + + Test.java + 3 + Usages of API documented as @since 1.5 (1.6|1.7) + Default method 'remove' is not overridden. It would cause compilation problems with JDK 6 + + + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/usage1.5/defaultMethods/src/Test.java b/java/java-tests/testData/inspection/usage1.5/defaultMethods/src/Test.java new file mode 100644 index 000000000000..df38656b66c9 --- /dev/null +++ b/java/java-tests/testData/inspection/usage1.5/defaultMethods/src/Test.java @@ -0,0 +1,29 @@ +import java.util.Iterator; + +public class Test implements Iterator { + @Override + public boolean hasNext() { + return false; + } + + @Override + public String next() { + return null; + } + + static class T implements Iterator { + @Override + public boolean hasNext() { + return false; + } + + @Override + public String next() { + return null; + } + + @Override + public void remove() {} + } +} + diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/JavaAPIUsagesInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/JavaAPIUsagesInspectionTest.java index 18b96564cc61..cb9b29ac62e1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/JavaAPIUsagesInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/JavaAPIUsagesInspectionTest.java @@ -23,6 +23,7 @@ package com.intellij.codeInspection; import com.intellij.JavaTestUtil; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.codeInspection.java15api.Java15APIUsageInspection; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.pom.java.LanguageLevel; import com.intellij.testFramework.IdeaTestUtil; import com.intellij.testFramework.InspectionTestCase; @@ -60,6 +61,20 @@ public class JavaAPIUsagesInspectionTest extends InspectionTestCase { }); } + public void testDefaultMethods() throws Exception { + IdeaTestUtil.withLevel(getModule(), LanguageLevel.JDK_1_6, new Runnable() { + @Override + public void run() { + doTest(); + } + }); + } + + @Override + protected Sdk getTestProjectSdk() { + return IdeaTestUtil.getMockJdk18(); + } + //generate apiXXX.txt /* public void testCollectSinceApiUsages() { diff --git a/platform/platform-resources-en/src/messages/InspectionsBundle.properties b/platform/platform-resources-en/src/messages/InspectionsBundle.properties index 758ac72680a3..d365dff90108 100644 --- a/platform/platform-resources-en/src/messages/InspectionsBundle.properties +++ b/platform/platform-resources-en/src/messages/InspectionsBundle.properties @@ -122,6 +122,8 @@ inspection.equals.hashcode.generate.hashcode.quickfix=Generate 'hashCode()' inspection.1.5.display.name=Usages of API documented as @since 1.5 (1.6|1.7) inspection.1.5.problem.descriptor=Usage of API documented as @since {0}+ inspection.1.7.problem.descriptor=Usage of generified after 1.6 API which would cause compilation problems with JDK {0} +inspection.1.8.problem.descriptor=Default {0, choice, 0#|1#method is|2# methods are} not overridden. It would cause compilation problems with JDK {1} +inspection.1.8.problem.single.descriptor=Default method ''{0}'' is not overridden. It would cause compilation problems with JDK {1} inspection.local.can.be.final.display.name=Local variable or parameter can be final inspection.local.can.be.final.option=Report local variables