RedundantUnmodifiableInspection created: IDEA-202309

GitOrigin-RevId: d5a5e1f45ba0f6e0af786ca588caaa192cd4b554
This commit is contained in:
Andrey.Cherkasov
2020-09-25 14:16:22 +03:00
committed by intellij-monorepo-bot
parent a89e02f7f0
commit 6fbd744430
17 changed files with 454 additions and 0 deletions

View File

@@ -1389,6 +1389,12 @@
bundle="messages.JavaBundle"
key="inspection.redundant.file.creation.display.name"
implementationClass="com.intellij.codeInspection.RedundantFileCreationInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="RedundantUnmodifiable"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.verbose.or.redundant.code.constructs" enabledByDefault="true" level="WARNING"
bundle="messages.JavaBundle"
key="inspection.redundant.unmodifiable.call.description"
implementationClass="com.intellij.codeInspection.RedundantUnmodifiableInspection"/>
<localInspection groupPathKey="group.path.names.java.language.level.specific.issues.and.migration.aids" language="JAVA" shortName="TextBlockMigration"
groupBundle="messages.InspectionsBundle"
groupKey="group.names.language.level.specific.issues.and.migration.aids15" enabledByDefault="true" level="WARNING"

View File

@@ -0,0 +1,97 @@
// Copyright 2000-2020 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.intellij.codeInspection;
import com.intellij.codeInspection.dataFlow.CommonDataflow;
import com.intellij.codeInspection.dataFlow.Mutability;
import com.intellij.codeInspection.dataFlow.types.DfType;
import com.intellij.java.JavaBundle;
import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.TextRange;
import com.intellij.psi.*;
import com.intellij.util.ArrayUtil;
import com.intellij.util.ObjectUtils;
import com.siyeh.ig.callMatcher.CallMatcher;
import com.siyeh.ig.psiutils.CommentTracker;
import org.jetbrains.annotations.NotNull;
import static com.intellij.psi.CommonClassNames.JAVA_UTIL_COLLECTIONS;
import static com.siyeh.ig.callMatcher.CallMatcher.anyOf;
import static com.siyeh.ig.callMatcher.CallMatcher.staticCall;
public class RedundantUnmodifiableInspection extends AbstractBaseJavaLocalInspectionTool {
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_COLLECTION =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableCollection").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_LIST =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableList").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_SET =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableSet").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_MAP =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableMap").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_SORTED_SET =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableSortedSet").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_SORTED_MAP =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableSortedMap").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_NAVIGABLE_MAP =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableNavigableMap").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE_NAVIGABLE_SET =
staticCall(JAVA_UTIL_COLLECTIONS, "unmodifiableNavigableSet").parameterCount(1);
private static final CallMatcher COLLECTIONS_UNMODIFIABLE =
anyOf(COLLECTIONS_UNMODIFIABLE_COLLECTION, COLLECTIONS_UNMODIFIABLE_SET,
COLLECTIONS_UNMODIFIABLE_MAP, COLLECTIONS_UNMODIFIABLE_LIST,
COLLECTIONS_UNMODIFIABLE_SORTED_SET, COLLECTIONS_UNMODIFIABLE_SORTED_MAP,
COLLECTIONS_UNMODIFIABLE_NAVIGABLE_MAP, COLLECTIONS_UNMODIFIABLE_NAVIGABLE_SET);
@NotNull
@Override
public PsiElementVisitor buildVisitor(@NotNull final ProblemsHolder holder, boolean isOnTheFly) {
return new JavaElementVisitor() {
@Override
public void visitMethodCallExpression(PsiMethodCallExpression call) {
super.visitMethodCallExpression(call);
if (COLLECTIONS_UNMODIFIABLE.test(call)) {
PsiExpression arg = ArrayUtil.getFirstElement(call.getArgumentList().getExpressions());
if (arg == null) return;
DfType dfType = CommonDataflow.getDfType(arg);
if (!Mutability.fromDfType(dfType).isUnmodifiable()) return;
String methodName = call.getMethodExpression().getReferenceName();
if (methodName == null) return;
holder.registerProblem(call,
JavaBundle.message("inspection.redundant.unmodifiable.call.display.name", methodName),
ProblemHighlightType.LIKE_UNUSED_SYMBOL,
new TextRange(0, call.getArgumentList().getStartOffsetInParent()),
new UnwrapUnmodifiableFix());
}
}
};
}
private static class UnwrapUnmodifiableFix implements LocalQuickFix {
@NotNull
@Override
public String getFamilyName() {
return JavaBundle.message("inspection.redundant.unmodifiable.call.replace.with.arg.quickfix");
}
@Override
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
final PsiElement element = descriptor.getPsiElement();
PsiMethodCallExpression call = ObjectUtils.tryCast(element, PsiMethodCallExpression.class);
if (call == null) return;
final PsiExpressionList argList = call.getArgumentList();
final PsiExpression[] args = argList.getExpressions();
if (args.length != 1) return;
CommentTracker commentTracker = new CommentTracker();
commentTracker.replaceAndRestoreComments(element, commentTracker.text(args[0]));
}
}
}

View File

@@ -0,0 +1,14 @@
<html>
<body>
Reports redundant calls to unmodifiable collection factories within the Collections class.
If the argument that is passed to an unmodifiable collection factory is already immutable, such wrapping becomes pointless.
<p>Example:</p>
<pre>
<code>Collections.unmodifiableList(Collections.singletonList("abc"));
</code>
</pre>
In order to detect the methods that return unmodifiable collections, the inspection uses the <code>@Unmodifiable</code> annotation. Use this annotation if you want to extend the inspection to your own unmodifiable collection factories.
<!-- tooltip end -->
<p><small>New in 2020.3</small></p>
</body>
</html>

View File

@@ -0,0 +1,35 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
class Main {
public static void main(String[] args) {
getEmptyList();
getEmptyList();
getEmptySet();
getEmptyMap();
getEmptySet();
getEmptyMap();
getEmptySet();
getEmptyMap();
}
static List getEmptyList() {
return Collections.emptyList();
}
static Set getEmptySet() {
return Collections.emptySet();
}
static Map getEmptyMap() {
return Collections.emptyMap();
}
}

View File

@@ -0,0 +1,29 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.*;
class Main {
public static void main(String[] args) {
List list = new ArrayList();
list = Collections.emptyList();
Set set = new HashSet();
set = Collections.emptySet();
Map map = new HashMap();
map = Collections.emptyMap();
list;
list;
set;
map;
set;
map;
set;
map;
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
Collections.EMPTY_LIST;
Collections.EMPTY_LIST;
Collections.EMPTY_SET;
Collections.EMPTY_MAP;
Collections.EMPTY_SET;
Collections.EMPTY_MAP;
Collections.EMPTY_SET;
Collections.EMPTY_MAP;
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
Collections.emptyList();
Collections.emptyList();
Collections.emptySet();
Collections.emptyMap();
Collections.emptySet();
Collections.emptyMap();
Collections.emptySet();
Collections.emptyMap();
}
}

View File

@@ -0,0 +1,44 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptyList();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptyList();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptySet();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptyMap();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptySet();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptyMap();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptySet();
/*empty*/
/*empty too*/
/*blah blah blah*/
Collections.emptyMap();
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
((Collections.emptyList()));
((Collections.emptyList()));
((Collections.emptySet()));
((Collections.emptyMap()));
((Collections.emptySet()));
((Collections.emptyMap()));
((Collections.emptySet()));
((Collections.emptyMap()));
}
}

View File

@@ -0,0 +1,35 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
class Main {
public static void main(String[] args) {
Collections.unmodifiableCollecti<caret>on(getEmptyList());
Collections.unmodifiableList(getEmptyList());
Collections.unmodifiableSet(getEmptySet());
Collections.unmodifiableMap(getEmptyMap());
Collections.unmodifiableSortedSet(getEmptySet());
Collections.unmodifiableSortedMap(getEmptyMap());
Collections.unmodifiableNavigableSet(getEmptySet());
Collections.unmodifiableNavigableMap(getEmptyMap());
}
static List getEmptyList() {
return Collections.emptyList();
}
static Set getEmptySet() {
return Collections.emptySet();
}
static Map getEmptyMap() {
return Collections.emptyMap();
}
}

View File

@@ -0,0 +1,29 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.*;
class Main {
public static void main(String[] args) {
List list = new ArrayList();
list = Collections.emptyList();
Set set = new HashSet();
set = Collections.emptySet();
Map map = new HashMap();
map = Collections.emptyMap();
Collections.unmodifiableCollecti<caret>on(list);
Collections.unmodifiableList(list);
Collections.unmodifiableSet(set);
Collections.unmodifiableMap(map);
Collections.unmodifiableSortedSet(set);
Collections.unmodifiableSortedMap(map);
Collections.unmodifiableNavigableSet(set);
Collections.unmodifiableNavigableMap(map);
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
Collections.unmodifiableCollecti<caret>on(Collections.EMPTY_LIST);
Collections.unmodifiableList(Collections.EMPTY_LIST);
Collections.unmodifiableSet(Collections.EMPTY_SET);
Collections.unmodifiableMap(Collections.EMPTY_MAP);
Collections.unmodifiableSortedSet(Collections.EMPTY_SET);
Collections.unmodifiableSortedMap(Collections.EMPTY_MAP);
Collections.unmodifiableNavigableSet(Collections.EMPTY_SET);
Collections.unmodifiableNavigableMap(Collections.EMPTY_MAP);
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
Collections.unmodifiableCollecti<caret>on(Collections.emptyList());
Collections.unmodifiableList(Collections.emptyList());
Collections.unmodifiableSet(Collections.emptySet());
Collections.unmodifiableMap(Collections.emptyMap());
Collections.unmodifiableSortedSet(Collections.emptySet());
Collections.unmodifiableSortedMap(Collections.emptyMap());
Collections.unmodifiableNavigableSet(Collections.emptySet());
Collections.unmodifiableNavigableMap(Collections.emptyMap());
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
Collections/*empty*/./*empty too*/unmodifiableCollecti<caret>on/*blah blah blah*/(Collections.emptyList());
Collections/*empty*/./*empty too*/unmodifiableList/*blah blah blah*/(Collections.emptyList());
Collections/*empty*/./*empty too*/unmodifiableSet/*blah blah blah*/(Collections.emptySet());
Collections/*empty*/./*empty too*/unmodifiableMap/*blah blah blah*/(Collections.emptyMap());
Collections/*empty*/./*empty too*/unmodifiableSortedSet/*blah blah blah*/(Collections.emptySet());
Collections/*empty*/./*empty too*/unmodifiableSortedMap/*blah blah blah*/(Collections.emptyMap());
Collections/*empty*/./*empty too*/unmodifiableNavigableSet/*blah blah blah*/(Collections.emptySet());
Collections/*empty*/./*empty too*/unmodifiableNavigableMap/*blah blah blah*/(Collections.emptyMap());
}
}

View File

@@ -0,0 +1,20 @@
// "Fix all 'Redundant usage of unmodifiable collection factories' problems in file" "true"
import java.util.Collections;
class Main {
public static void main(String[] args) {
Collections.unmodifiableCollecti<caret>on(((Collections.emptyList())));
Collections.unmodifiableList(((Collections.emptyList())));
Collections.unmodifiableSet(((Collections.emptySet())));
Collections.unmodifiableMap(((Collections.emptyMap())));
Collections.unmodifiableSortedSet(((Collections.emptySet())));
Collections.unmodifiableSortedMap(((Collections.emptyMap())));
Collections.unmodifiableNavigableSet(((Collections.emptySet())));
Collections.unmodifiableNavigableMap(((Collections.emptyMap())));
}
}

View File

@@ -0,0 +1,22 @@
// Copyright 2000-2020 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.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.RedundantUnmodifiableInspection;
import org.jetbrains.annotations.NotNull;
public class RedundantUnmodifiableInspectionTest extends LightQuickFixParameterizedTestCase {
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{
new RedundantUnmodifiableInspection()
};
}
@Override
protected String getBasePath() {
return "/codeInsight/daemonCodeAnalyzer/quickFix/redundantUnmodifiable";
}
}

View File

@@ -1551,3 +1551,6 @@ postfix.template.condition.boolean.name=boolean
postfix.template.condition.number.name=number
postfix.template.condition.not.primitive.type.name=not primitive type
postfix.template.condition.array.name=array
inspection.redundant.unmodifiable.call.display.name=Redundant usage of the ''{0}'' factory
inspection.redundant.unmodifiable.call.description=Redundant usage of unmodifiable collection factories
inspection.redundant.unmodifiable.call.replace.with.arg.quickfix=Unwrap argument