mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-200005 Suggest to use Files.readString/writeString since Java 11
This commit is contained in:
@@ -734,6 +734,11 @@
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids9" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.java19api.Java9CollectionFactoryInspection"
|
||||
displayName="Immutable collection creation can be replaced with collection factory call"/>
|
||||
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" shortName="ReadWriteStringCanBeUsed"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids11" enabledByDefault="true" level="WARNING"
|
||||
implementationClass="com.intellij.codeInspection.ReadWriteStringCanBeUsedInspection"
|
||||
displayName="'Files.readString()' or 'Files.writeString()' can be used"/>
|
||||
<localInspection groupPath="Java,Java language level migration aids" language="JAVA" shortName="Java8MapForEach"
|
||||
groupBundle="messages.InspectionsBundle"
|
||||
groupKey="group.names.language.level.specific.issues.and.migration.aids8" enabledByDefault="true" level="WARNING"
|
||||
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
// Copyright 2000-2018 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.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import com.siyeh.ig.psiutils.ConstructionUtils;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.Nls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.tryCast;
|
||||
|
||||
public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalInspectionTool {
|
||||
private static final CallMatcher FILES_WRITE = CallMatcher.staticCall("java.nio.file.Files", "write")
|
||||
.parameterTypes("java.nio.file.Path", "byte[]", "java.nio.file.OpenOption...");
|
||||
private static final CallMatcher FILES_READ_ALL_BYTES = CallMatcher.staticCall("java.nio.file.Files", "readAllBytes")
|
||||
.parameterTypes("java.nio.file.Path");
|
||||
private static final CallMatcher STRING_GET_BYTES = CallMatcher.exactInstanceCall(CommonClassNames.JAVA_LANG_STRING, "getBytes")
|
||||
.parameterTypes("java.nio.charset.Charset");
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!PsiUtil.isLanguageLevel11OrHigher(holder.getFile())) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaElementVisitor() {
|
||||
@Override
|
||||
public void visitMethodCallExpression(PsiMethodCallExpression call) {
|
||||
if (FILES_WRITE.test(call)) {
|
||||
PsiMethodCallExpression bytesExpression = tryCast(ExpressionUtils.resolveExpression(call.getArgumentList().getExpressions()[1]), PsiMethodCallExpression.class);
|
||||
if (STRING_GET_BYTES.test(bytesExpression) && bytesExpression.getMethodExpression().getQualifierExpression() != null) {
|
||||
holder.registerProblem(call, "Can be replaced with 'Files.writeString()'",
|
||||
new ReplaceWithWriteStringFix());
|
||||
}
|
||||
} else if (FILES_READ_ALL_BYTES.test(call)) {
|
||||
PsiExpressionList expressionList = tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiExpressionList.class);
|
||||
if (expressionList != null) {
|
||||
PsiNewExpression newExpression = tryCast(expressionList.getParent(), PsiNewExpression.class);
|
||||
if (newExpression != null && newExpression.getAnonymousClass() == null &&
|
||||
ConstructionUtils.isReferenceTo(newExpression.getClassReference(), CommonClassNames.JAVA_LANG_STRING)) {
|
||||
PsiExpression[] args = expressionList.getExpressions();
|
||||
if (args.length == 2 && PsiTreeUtil.isAncestor(args[0], call, false) &&
|
||||
TypeUtils.typeEquals("java.nio.charset.Charset", args[1].getType())) {
|
||||
holder.registerProblem(newExpression, "Can be replaced with 'Files.readString()'",
|
||||
new ReplaceWithReadStringFix());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private static class ReplaceWithReadStringFix implements LocalQuickFix {
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", "Files.readString()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiNewExpression newExpression = tryCast(descriptor.getStartElement(), PsiNewExpression.class);
|
||||
if (newExpression == null) return;
|
||||
PsiExpressionList newArgList = newExpression.getArgumentList();
|
||||
if (newArgList == null) return;
|
||||
PsiExpression[] newArgs = newArgList.getExpressions();
|
||||
if (newArgs.length != 2) return;
|
||||
PsiMethodCallExpression readCall = tryCast(PsiUtil.skipParenthesizedExprDown(newArgs[0]), PsiMethodCallExpression.class);
|
||||
if (!FILES_READ_ALL_BYTES.test(readCall)) return;
|
||||
PsiExpression charsetExpression = newArgs[1];
|
||||
|
||||
CommentTracker ct = new CommentTracker();
|
||||
ExpressionUtils.bindCallTo(readCall, "readString");
|
||||
if (!isUtf8Charset(charsetExpression)) {
|
||||
readCall.getArgumentList().add(ct.markUnchanged(charsetExpression));
|
||||
}
|
||||
ct.replaceAndRestoreComments(newExpression, readCall);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReplaceWithWriteStringFix implements LocalQuickFix {
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getFamilyName() {
|
||||
return CommonQuickFixBundle.message("fix.replace.with.x", "Files.writeString()");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) {
|
||||
PsiMethodCallExpression writeCall = tryCast(descriptor.getStartElement(), PsiMethodCallExpression.class);
|
||||
if (!FILES_WRITE.test(writeCall)) return;
|
||||
PsiExpressionList argumentList = writeCall.getArgumentList();
|
||||
PsiExpression[] args = argumentList.getExpressions();
|
||||
if (args.length < 2) return;
|
||||
PsiExpression bytesArg = args[1];
|
||||
PsiMethodCallExpression bytesExpression = tryCast(ExpressionUtils.resolveExpression(bytesArg), PsiMethodCallExpression.class);
|
||||
if (!STRING_GET_BYTES.test(bytesExpression)) return;
|
||||
PsiExpression stringExpression = PsiUtil.skipParenthesizedExprDown(bytesExpression.getMethodExpression().getQualifierExpression());
|
||||
if (stringExpression == null) return;
|
||||
PsiExpression charsetExpression = bytesExpression.getArgumentList().getExpressions()[0];
|
||||
|
||||
CommentTracker ct = new CommentTracker();
|
||||
ExpressionUtils.bindCallTo(writeCall, "writeString");
|
||||
if (!isUtf8Charset(charsetExpression)) {
|
||||
argumentList.addAfter(ct.markUnchanged(charsetExpression), bytesArg);
|
||||
}
|
||||
PsiLocalVariable variable = ExpressionUtils.resolveLocalVariable(PsiUtil.skipParenthesizedExprDown(bytesArg));
|
||||
ct.replaceAndRestoreComments(bytesArg, stringExpression);
|
||||
if (variable != null) {
|
||||
ct = new CommentTracker();
|
||||
ct.markUnchanged(stringExpression);
|
||||
ct.deleteAndRestoreComments(variable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isUtf8Charset(PsiExpression expression) {
|
||||
PsiReferenceExpression ref = tryCast(PsiUtil.skipParenthesizedExprDown(expression), PsiReferenceExpression.class);
|
||||
if (ref == null) return false;
|
||||
if (!"UTF_8".equals(ref.getReferenceName())) {
|
||||
return false;
|
||||
}
|
||||
PsiField target = tryCast(ref.resolve(), PsiField.class);
|
||||
return target != null &&
|
||||
target.getContainingClass() != null &&
|
||||
"java.nio.charset.StandardCharsets".equals(target.getContainingClass().getQualifiedName());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<html>
|
||||
<body>
|
||||
Reports code fragments which could be replaced via methods <b>Files.readString</b> and <b>Files.writeString</b>
|
||||
which were introduced in Java 11.
|
||||
<!-- tooltip end -->
|
||||
<p><small>New in 2018.3</small></p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,21 @@
|
||||
// "Fix all ''Files.readString()' or 'Files.writeString()' can be used' problems in file" "true"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class Example {
|
||||
void testRead1() throws IOException {
|
||||
String s = Files.readString(Paths.get("/etc/passwd"), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
void testRead2() throws IOException {
|
||||
/*3*/
|
||||
/*4*/
|
||||
/*8*/
|
||||
/*9*/
|
||||
/*1*/
|
||||
/*2*/
|
||||
String s = Files/*5*/./*6*/readString(/*7*/Paths.get("/etc/passwd"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
// "Fix all ''Files.readString()' or 'Files.writeString()' can be used' problems in file" "true"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
void test1() throws IOException {
|
||||
/*1*/
|
||||
/*3*/
|
||||
/*4*/
|
||||
/*5*/
|
||||
Files.writeString(Paths.get("/etc/passwd"), "foo"/*2*/+"bar", StandardCharsets.ISO_8859_1/*6*/, StandardOpenOption.CREATE);
|
||||
}
|
||||
|
||||
void test2() throws IOException {
|
||||
/*3*/
|
||||
/*1*/
|
||||
/*2*/
|
||||
Files.writeString(Paths.get("/etc/passwd"), "foo");
|
||||
}
|
||||
|
||||
void test3() throws IOException {
|
||||
Files.writeString(Paths.get("/etc/passwd"), "foo");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Fix all ''Files.readString()' or 'Files.writeString()' can be used' problems in file" "true"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
|
||||
public class Example {
|
||||
void testRead1() throws IOException {
|
||||
String s = new String(Files<caret>.readAllBytes(Paths.get("/etc/passwd")), StandardCharsets.ISO_8859_1);
|
||||
}
|
||||
|
||||
void testRead2() throws IOException {
|
||||
String s = new /*3*/String(/*4*/Files/*5*/./*6*/readAllBytes(/*7*/Paths.get("/etc/passwd"))/*8*/, /*9*/(StandardCharsets./*1*/UTF_8/*2*/));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
// "Fix all ''Files.readString()' or 'Files.writeString()' can be used' problems in file" "true"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
void test1() throws IOException {
|
||||
byte[] /*1*/bytes = ("foo"/*2*/+"bar")./*3*/getBytes(StandardCharsets.ISO_8859_1/*4*/)/*5*/;
|
||||
Files.write<caret>(Paths.get("/etc/passwd"), bytes/*6*/, StandardOpenOption.CREATE);
|
||||
}
|
||||
|
||||
void test2() throws IOException {
|
||||
byte[] bytes = "foo".getBytes(StandardCharsets.UTF_8/*3*/);
|
||||
Files.write(Paths.get("/etc/passwd"), (/*1*/bytes/*2*/));
|
||||
}
|
||||
|
||||
void test3() throws IOException {
|
||||
Files.write(Paths.get("/etc/passwd"), (("foo").getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Copyright 2000-2018 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.codeInspection;
|
||||
|
||||
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
|
||||
import com.intellij.codeInspection.LocalInspectionTool;
|
||||
import com.intellij.codeInspection.ReadWriteStringCanBeUsedInspection;
|
||||
import com.intellij.codeInspection.java19api.Java9CollectionFactoryInspection;
|
||||
import com.intellij.openapi.projectRoots.Sdk;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
public class ReadWriteStringCanBeUsedInspectionTest extends LightQuickFixParameterizedTestCase {
|
||||
@Override
|
||||
protected LanguageLevel getDefaultLanguageLevel() {
|
||||
return LanguageLevel.JDK_11;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected LocalInspectionTool[] configureLocalInspectionTools() {
|
||||
return new LocalInspectionTool[]{new ReadWriteStringCanBeUsedInspection()};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Sdk getProjectJDK() {
|
||||
return IdeaTestUtil.getMockJdk9();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBasePath() {
|
||||
return "/inspection/filesReadWriteString";
|
||||
}
|
||||
}
|
||||
@@ -584,6 +584,7 @@ group.names.language.level.specific.issues.and.migration.aids8=Java 8
|
||||
group.names.language.level.specific.issues.and.migration.aids9=Java 9
|
||||
group.names.language.level.specific.issues.and.migration.aids5=Java 5
|
||||
group.names.language.level.specific.issues.and.migration.aids10=Java 10
|
||||
group.names.language.level.specific.issues.and.migration.aids11=Java 11
|
||||
group.names.language.level.specific.issues.and.migration.aids12=Java 12
|
||||
group.names.javabeans.issues=JavaBeans issues
|
||||
group.names.inheritance.issues=Inheritance issues
|
||||
|
||||
Reference in New Issue
Block a user