From c84405476822f098bc7731afd8fc074a7f6bfea5 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Mon, 10 Oct 2016 15:19:08 +0700 Subject: [PATCH] IDEA-162190 Inspection to convert anonymous ThreadLocal subclass to ThreadLocal.withInitial --- .../AnonymousCanBeLambdaInspection.java | 183 ++++++++++-------- ...onymousHasLambdaAlternativeInspection.java | 152 +++++++++++++++ .../afterThread.java | 11 ++ .../afterThreadLocal.java | 5 + .../beforeThread.java | 14 ++ .../beforeThreadAnnotated.java | 18 ++ .../beforeThreadField.java | 13 ++ .../beforeThreadLocal.java | 10 + .../beforeThreadUseSuper.java | 12 ++ .../beforeThreadWrongMethod.java | 11 ++ ...ousHasLambdaAlternativeInspectionTest.java | 38 ++++ .../AnonymousHasLambdaAlternative.html | 12 ++ resources/src/META-INF/IdeaPlugin.xml | 3 + 13 files changed, 399 insertions(+), 83 deletions(-) create mode 100644 java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThread.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThreadLocal.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThread.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadAnnotated.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadField.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadLocal.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadUseSuper.java create mode 100644 java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadWrongMethod.java create mode 100644 java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AnonymousHasLambdaAlternativeInspectionTest.java create mode 100644 resources-en/src/inspectionDescriptions/AnonymousHasLambdaAlternative.html diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java index f1d19afeeafc..5d97a50ee127 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousCanBeLambdaInspection.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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. @@ -45,6 +45,7 @@ import javax.swing.*; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.util.*; +import java.util.function.UnaryOperator; /** * User: anna @@ -115,7 +116,7 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection }; } - private static boolean hasRuntimeAnnotations(PsiMethod method, @NotNull Set runtimeAnnotationsToIgnore) { + static boolean hasRuntimeAnnotations(PsiMethod method, @NotNull Set runtimeAnnotationsToIgnore) { PsiAnnotation[] annotations = method.getModifierList().getAnnotations(); for (PsiAnnotation annotation : annotations) { PsiJavaCodeReferenceElement ref = annotation.getNameReferenceElement(); @@ -242,91 +243,107 @@ public class AnonymousCanBeLambdaInspection extends BaseJavaBatchLocalInspection public static PsiExpression replacePsiElementWithLambda(@NotNull PsiElement element, final boolean ignoreEqualsMethod, boolean forceIgnoreTypeCast) { - if (element instanceof PsiNewExpression) { - if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return null; - final PsiAnonymousClass anonymousClass = ((PsiNewExpression)element).getAnonymousClass(); - - if (anonymousClass == null) return null; - - ChangeContextUtil.encodeContextInfo(anonymousClass, true); - final String canonicalText = anonymousClass.getBaseClassType().getCanonicalText(); - - final PsiMethod method; - if (ignoreEqualsMethod) { - final List methods = ContainerUtil.filter(anonymousClass.getMethods(), method1 -> !"equals".equals(method1.getName())); - method = methods.get(0); - } else { - method = anonymousClass.getMethods()[0]; - } - if (method == null) return null; - - final PsiCodeBlock body = method.getBody(); - if (body == null) return null; - - final Collection comments = collectCommentsOutsideMethodBody(anonymousClass, body); - final Project project = element.getProject(); - final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); - - final String withoutTypesDeclared = ReplaceWithLambdaFix.composeLambdaText(method); - - PsiLambdaExpression lambdaExpression = - (PsiLambdaExpression)elementFactory.createExpressionFromText(withoutTypesDeclared, anonymousClass); - - PsiElement lambdaBody = lambdaExpression.getBody(); - LOG.assertTrue(lambdaBody != null); - lambdaBody.replace(body); - final PsiNewExpression newExpression = (PsiNewExpression)anonymousClass.getParent(); - lambdaExpression = (PsiLambdaExpression)newExpression.replace(lambdaExpression); - - final Set variables = new HashSet<>(); - final Set usedLocalNames = new HashSet<>(); - - collectLocalVariablesDefinedInsideLambda(lambdaExpression, variables, usedLocalNames); - - ReplaceWithLambdaFix - .giveUniqueNames(project, elementFactory, lambdaExpression, - usedLocalNames, variables.toArray(new PsiVariable[variables.size()])); - - final PsiExpression singleExpr = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(lambdaExpression.getBody()); - if (singleExpr != null) { - lambdaExpression.getBody().replace(singleExpr); - } - ChangeContextUtil.decodeContextInfo(lambdaExpression, null, null); - restoreComments(comments, lambdaExpression); - - final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project); - if (forceIgnoreTypeCast) { - return (PsiExpression)javaCodeStyleManager.shortenClassReferences(lambdaExpression); - } - - PsiTypeCastExpression typeCast = (PsiTypeCastExpression)elementFactory - .createExpressionFromText("(" + canonicalText + ")" + withoutTypesDeclared, lambdaExpression); - final PsiExpression typeCastOperand = typeCast.getOperand(); - LOG.assertTrue(typeCastOperand instanceof PsiLambdaExpression); - final PsiElement fromText = ((PsiLambdaExpression)typeCastOperand).getBody(); - LOG.assertTrue(fromText != null); - lambdaBody = lambdaExpression.getBody(); - LOG.assertTrue(lambdaBody != null); - fromText.replace(lambdaBody); - ((PsiLambdaExpression)typeCastOperand).getParameterList().replace(lambdaExpression.getParameterList()); - typeCast = (PsiTypeCastExpression)lambdaExpression.replace(typeCast); - if (RedundantCastUtil.isCastRedundant(typeCast)) { - final PsiExpression operand = typeCast.getOperand(); - LOG.assertTrue(operand != null); - return (PsiExpression)typeCast.replace(operand); - } - return (PsiExpression)javaCodeStyleManager.shortenClassReferences(typeCast); + if (!(element instanceof PsiNewExpression) || !FileModificationService.getInstance().preparePsiElementForWrite(element)) { + return null; } - return null; + + final PsiNewExpression newExpression = (PsiNewExpression)element; + final PsiAnonymousClass anonymousClass = newExpression.getAnonymousClass(); + + if (anonymousClass == null) return null; + + final PsiMethod method; + if (ignoreEqualsMethod) { + final List methods = ContainerUtil.filter(anonymousClass.getMethods(), method1 -> !"equals".equals(method1.getName())); + method = methods.get(0); + } else { + method = anonymousClass.getMethods()[0]; + } + if (method == null || method.getBody() == null) return null; + + return generateLambdaByMethod(anonymousClass, method, lambda -> (PsiLambdaExpression)newExpression.replace(lambda), + forceIgnoreTypeCast); } - private static Collection collectCommentsOutsideMethodBody(PsiAnonymousClass anonymousClass, PsiCodeBlock body) { - final Collection psiComments = PsiTreeUtil.findChildrenOfType(anonymousClass, PsiComment.class); - for (Iterator iterator = psiComments.iterator(); iterator.hasNext(); ) { - if (PsiTreeUtil.isAncestor(body, iterator.next(), false)) { - iterator.remove(); - } + /** + * Try convert given method of given anonymous class into lambda and replace given element. + * + * @param anonymousClass physical anonymous class containing method + * @param method physical method to convert with non-empty body + * @param replacer an operator which actually inserts a lambda into the file (possibly removing anonymous class) + * and returns an inserted physical lambda + * @param forceIgnoreTypeCast if false, type cast might be added if necessary + * @return newly-generated lambda expression (possibly with typecast) + */ + @NotNull + static PsiExpression generateLambdaByMethod(PsiAnonymousClass anonymousClass, + PsiMethod method, + UnaryOperator replacer, + boolean forceIgnoreTypeCast) { + ChangeContextUtil.encodeContextInfo(anonymousClass, true); + final String canonicalText = anonymousClass.getBaseClassType().getCanonicalText(); + + final PsiCodeBlock body = method.getBody(); + LOG.assertTrue(body != null); + + final Collection comments = collectCommentsOutsideMethodBody(anonymousClass, body); + final Project project = anonymousClass.getProject(); + final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + + final String withoutTypesDeclared = ReplaceWithLambdaFix.composeLambdaText(method); + + PsiLambdaExpression lambdaExpression = + (PsiLambdaExpression)elementFactory.createExpressionFromText(withoutTypesDeclared, anonymousClass); + + PsiElement lambdaBody = lambdaExpression.getBody(); + LOG.assertTrue(lambdaBody != null); + lambdaBody.replace(body); + lambdaExpression = replacer.apply(lambdaExpression); + + final Set variables = new HashSet<>(); + final Set usedLocalNames = new HashSet<>(); + + collectLocalVariablesDefinedInsideLambda(lambdaExpression, variables, usedLocalNames); + + ReplaceWithLambdaFix + .giveUniqueNames(project, elementFactory, lambdaExpression, + usedLocalNames, variables.toArray(new PsiVariable[variables.size()])); + + final PsiExpression singleExpr = RedundantLambdaCodeBlockInspection.isCodeBlockRedundant(lambdaExpression.getBody()); + if (singleExpr != null) { + lambdaExpression.getBody().replace(singleExpr); } + ChangeContextUtil.decodeContextInfo(lambdaExpression, null, null); + restoreComments(comments, lambdaExpression); + + final JavaCodeStyleManager javaCodeStyleManager = JavaCodeStyleManager.getInstance(project); + if (forceIgnoreTypeCast) { + return (PsiExpression)javaCodeStyleManager.shortenClassReferences(lambdaExpression); + } + + PsiTypeCastExpression typeCast = (PsiTypeCastExpression)elementFactory + .createExpressionFromText("(" + canonicalText + ")" + withoutTypesDeclared, lambdaExpression); + final PsiExpression typeCastOperand = typeCast.getOperand(); + LOG.assertTrue(typeCastOperand instanceof PsiLambdaExpression); + final PsiElement fromText = ((PsiLambdaExpression)typeCastOperand).getBody(); + LOG.assertTrue(fromText != null); + lambdaBody = lambdaExpression.getBody(); + LOG.assertTrue(lambdaBody != null); + fromText.replace(lambdaBody); + ((PsiLambdaExpression)typeCastOperand).getParameterList().replace(lambdaExpression.getParameterList()); + typeCast = (PsiTypeCastExpression)lambdaExpression.replace(typeCast); + if (RedundantCastUtil.isCastRedundant(typeCast)) { + final PsiExpression operand = typeCast.getOperand(); + LOG.assertTrue(operand != null); + return (PsiExpression)typeCast.replace(operand); + } + return (PsiExpression)javaCodeStyleManager.shortenClassReferences(typeCast); + } + + @NotNull + static Collection collectCommentsOutsideMethodBody(PsiAnonymousClass anonymousClass, PsiCodeBlock body) { + final Collection psiComments = PsiTreeUtil.findChildrenOfType(anonymousClass, PsiComment.class); + psiComments.removeIf(comment -> PsiTreeUtil.isAncestor(body, comment, false)); return ContainerUtil.map(psiComments, (comment) -> (PsiComment)comment.copy()); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java new file mode 100644 index 000000000000..56ed803da99f --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/AnonymousHasLambdaAlternativeInspection.java @@ -0,0 +1,152 @@ +/* + * 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. + * 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.FileModificationService; +import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.TextRange; +import com.intellij.psi.*; +import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; + +import java.util.Collections; +import java.util.function.UnaryOperator; + +/** + * @author Tagir Valeev + */ +public class AnonymousHasLambdaAlternativeInspection extends BaseJavaBatchLocalInspectionTool { + public static final Logger LOG = Logger.getInstance(AnonymousHasLambdaAlternativeInspection.class); + + static final class AnonymousLambdaAlternative { + final String myClassName; + final String myMethodName; + final String myLambdaAlternative; + final String myReplacementMessage; + + public AnonymousLambdaAlternative(String className, String methodName, String lambdaAlternative, String replacementMessage) { + myClassName = className; + myMethodName = methodName; + myLambdaAlternative = lambdaAlternative; + myReplacementMessage = replacementMessage; + } + } + + private static AnonymousLambdaAlternative[] ALTERNATIVES = { + new AnonymousLambdaAlternative("java.lang.ThreadLocal", "initialValue", "java.lang.ThreadLocal.withInitial($lambda$)", + "ThreadLocal.withInitial"), + new AnonymousLambdaAlternative("java.lang.Thread", "run", "new java.lang.Thread($lambda$)", + "constructor accepting lambda") + }; + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) { + if (!PsiUtil.isLanguageLevel8OrHigher(holder.getFile())) { + return PsiElementVisitor.EMPTY_VISITOR; + } + return new JavaElementVisitor() { + @Override + public void visitAnonymousClass(final PsiAnonymousClass aClass) { + super.visitAnonymousClass(aClass); + PsiMethod[] methods = aClass.getMethods(); + if(methods.length == 1 && + aClass.getFields().length == 0 && + aClass.getInnerClasses().length == 0 && + aClass.getInitializers().length == 0) { + PsiExpressionList argumentList = aClass.getArgumentList(); + PsiMethod method = methods[0]; + if (argumentList != null && + argumentList.getExpressions().length == 0 && + method.getBody() != null && + method.getDocComment() == null && + !AnonymousCanBeLambdaInspection.hasRuntimeAnnotations(method, Collections.emptySet()) && + !method.hasModifierProperty(PsiModifier.SYNCHRONIZED) && + !AnonymousCanBeLambdaInspection.hasForbiddenRefsInsideBody(method, aClass)) { + PsiClassType type = aClass.getBaseClassType(); + AnonymousLambdaAlternative alternative = getAlternative(type.resolve(), method); + if(alternative != null) { + final PsiElement lBrace = aClass.getLBrace(); + LOG.assertTrue(lBrace != null); + final TextRange rangeInElement = new TextRange(0, lBrace.getStartOffsetInParent() + aClass.getStartOffsetInParent() - 1); + holder.registerProblem(aClass.getParent(), "Anonymous #ref #loc can be replaced with "+alternative.myReplacementMessage, + ProblemHighlightType.LIKE_UNUSED_SYMBOL, rangeInElement, new ReplaceWithLambdaAlternativeFix(alternative)); + } + } + } + } + + @Contract("null, _ -> null") + private AnonymousLambdaAlternative getAlternative(PsiClass type, PsiMethod method) { + if(type == null) return null; + for(AnonymousLambdaAlternative alternative : ALTERNATIVES) { + if(alternative.myClassName.equals(type.getQualifiedName()) && alternative.myMethodName.equals(method.getName())) { + return alternative; + } + } + return null; + } + }; + } + + static class ReplaceWithLambdaAlternativeFix implements LocalQuickFix { + private final @NotNull AnonymousLambdaAlternative myAlternative; + + public ReplaceWithLambdaAlternativeFix(@NotNull AnonymousLambdaAlternative alternative) { + myAlternative = alternative; + } + + @Nls + @NotNull + @Override + public String getName() { + return "Replace anonymous class with "+myAlternative.myReplacementMessage; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return "Replace anonymous class with lambda alternative"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getStartElement(); + if(!(element instanceof PsiNewExpression)) return; + PsiAnonymousClass aClass = ((PsiNewExpression)element).getAnonymousClass(); + if(aClass == null) return; + PsiMethod[] methods = aClass.getMethods(); + if(methods.length != 1) return; + PsiMethod method = methods[0]; + if(method.getBody() == null) return; + if (!FileModificationService.getInstance().preparePsiElementForWrite(element)) return; + UnaryOperator replacer = lambda -> { + PsiElementFactory factory = JavaPsiFacade.getElementFactory(project); + PsiElement replacement = element.replace(factory.createExpressionFromText(myAlternative.myLambdaAlternative, element)); + PsiElement[] lambdaPositions = + PsiTreeUtil.collectElements(replacement, e -> e instanceof PsiReference && e.textMatches("$lambda$")); + LOG.assertTrue(lambdaPositions.length == 1); + return (PsiLambdaExpression)lambdaPositions[0].replace(lambda); + }; + AnonymousCanBeLambdaInspection.generateLambdaByMethod(aClass, method, replacer, true); + } + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThread.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThread.java new file mode 100644 index 000000000000..eaba0042d306 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThread.java @@ -0,0 +1,11 @@ +// "Replace anonymous class with constructor accepting lambda" "true" +public class Main { + public void testThread() { + // Comment outside +// Ending comment + new Thread(() -> { + // Comment inside + System.out.println("Hello from thread!"); + }).start(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThreadLocal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThreadLocal.java new file mode 100644 index 000000000000..65f34abb52ef --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/afterThreadLocal.java @@ -0,0 +1,5 @@ +// "Replace anonymous class with ThreadLocal.withInitial" "true" +public class Main { + // comment + ThreadLocal tlr = ThreadLocal.withInitial(() -> "initial"); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThread.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThread.java new file mode 100644 index 000000000000..cc9961c24850 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThread.java @@ -0,0 +1,14 @@ +// "Replace anonymous class with constructor accepting lambda" "true" +public class Main { + public void testThread() { + new Thread() { + // Comment outside + @Override + public void run() { + // Comment inside + System.out.println("Hello from thread!"); + } + // Ending comment + }.start(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadAnnotated.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadAnnotated.java new file mode 100644 index 000000000000..dc5d95a97128 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadAnnotated.java @@ -0,0 +1,18 @@ +// "Replace anonymous class with constructor accepting lambda" "false" +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +public class Main { + @Retention(RetentionPolicy.RUNTIME) + @interface Anno{} + + public void testThread() { + new Thread() { + @Override + @Anno + public void run() { + System.out.println("Hello from thread! "+x); + } + }.start(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadField.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadField.java new file mode 100644 index 000000000000..64583db250d8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadField.java @@ -0,0 +1,13 @@ +// "Replace anonymous class with constructor accepting lambda" "false" +public class Main { + public void testThread() { + new Thread() { + int x = 5; + + @Override + public void run() { + System.out.println("Hello from thread! "+x); + } + }.start(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadLocal.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadLocal.java new file mode 100644 index 000000000000..c1338fb902fa --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadLocal.java @@ -0,0 +1,10 @@ +// "Replace anonymous class with ThreadLocal.withInitial" "true" +public class Main { + ThreadLocal tlr = new ThreadLocal() { + // comment + @Override + protected String initialValue() { + return "initial"; + } + }; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadUseSuper.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadUseSuper.java new file mode 100644 index 000000000000..65707993b287 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadUseSuper.java @@ -0,0 +1,12 @@ +// "Replace anonymous class with constructor accepting lambda" "false" +public class Main { + public void testThread() { + new Thread() { + @Override + public void run() { + System.out.println("Hello from thread!"); + super.run(); + } + }.start(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadWrongMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadWrongMethod.java new file mode 100644 index 000000000000..b90addb22912 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative/beforeThreadWrongMethod.java @@ -0,0 +1,11 @@ +// "Replace anonymous class with constructor accepting lambda" "false" +public class Main { + public void testThread() { + new Thread() { + @Override + public void start() { + System.out.println("Hello from thread!"); + } + }.start(); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AnonymousHasLambdaAlternativeInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AnonymousHasLambdaAlternativeInspectionTest.java new file mode 100644 index 000000000000..465db7b866cb --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/AnonymousHasLambdaAlternativeInspectionTest.java @@ -0,0 +1,38 @@ +/* + * 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. + * 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.codeInsight.daemon.quickFix; + +import com.intellij.codeInspection.AnonymousHasLambdaAlternativeInspection; +import com.intellij.codeInspection.LocalInspectionTool; +import org.jetbrains.annotations.NotNull; + + +public class AnonymousHasLambdaAlternativeInspectionTest extends LightQuickFixParameterizedTestCase { + @NotNull + @Override + protected LocalInspectionTool[] configureLocalInspectionTools() { + return new LocalInspectionTool[]{ + new AnonymousHasLambdaAlternativeInspection(), + }; + } + + public void test() throws Exception { doAllTests(); } + + @Override + protected String getBasePath() { + return "/codeInsight/daemonCodeAnalyzer/quickFix/anonymous2lambdaAlternative"; + } +} \ No newline at end of file diff --git a/resources-en/src/inspectionDescriptions/AnonymousHasLambdaAlternative.html b/resources-en/src/inspectionDescriptions/AnonymousHasLambdaAlternative.html new file mode 100644 index 000000000000..cee9457eeef6 --- /dev/null +++ b/resources-en/src/inspectionDescriptions/AnonymousHasLambdaAlternative.html @@ -0,0 +1,12 @@ + + +Reports anonymous classes which could be transformed to constructor or factory method call accepting lambda expression. + +

The following classes are reported by this inspection:

+
    +
  • ThreadLocal anonymous classes having initialValue() method (could be replaced with ThreadLocal.withInitial)
  • +
  • Thread anonymous classes having run() method (could be replaced with new Thread(Runnable)
  • +
+New in 2016.3 + + \ No newline at end of file diff --git a/resources/src/META-INF/IdeaPlugin.xml b/resources/src/META-INF/IdeaPlugin.xml index 4de90ce9f8e3..51ecafc90b9a 100644 --- a/resources/src/META-INF/IdeaPlugin.xml +++ b/resources/src/META-INF/IdeaPlugin.xml @@ -812,6 +812,9 @@ groupKey="group.names.performance.issues" enabledByDefault="false" level="WARNING" implementationClass="com.intellij.codeInspection.CollectionAddAllCanBeReplacedWithConstructorInspection" displayName="Collection.addAll() can be replaced with parametrized constructor"/> +