mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-201515 IDEA suggests to use Java 11 writeString which is buggy for UTF-8 charset (until 11.0.2)
This commit is contained in:
+71
-12
@@ -2,6 +2,7 @@
|
||||
package com.intellij.codeInspection;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -22,11 +23,14 @@ public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
.parameterTypes("java.nio.file.Path");
|
||||
private static final CallMatcher STRING_GET_BYTES = CallMatcher.exactInstanceCall(CommonClassNames.JAVA_LANG_STRING, "getBytes")
|
||||
.parameterTypes("java.nio.charset.Charset");
|
||||
private static final CallMatcher CHARSET_FOR_NAME = CallMatcher.staticCall("java.nio.charset.Charset", "forName")
|
||||
.parameterTypes(CommonClassNames.JAVA_LANG_STRING);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
if (!PsiUtil.isLanguageLevel11OrHigher(holder.getFile())) {
|
||||
LanguageLevel level = PsiUtil.getLanguageLevel(holder.getFile());
|
||||
if (level.isLessThan(LanguageLevel.JDK_11)) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new JavaElementVisitor() {
|
||||
@@ -35,8 +39,15 @@ public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
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());
|
||||
// Do not suggest for UTF-8 before Java 12 due to JDK-8209576
|
||||
ProblemHighlightType highlight;
|
||||
String message = "Can be replaced with 'Files.writeString()'";
|
||||
if (level.isAtLeast(LanguageLevel.JDK_12) || isNonUtf8Charset(bytesExpression.getArgumentList().getExpressions()[0])) {
|
||||
highlight = ProblemHighlightType.WARNING;
|
||||
} else {
|
||||
highlight = ProblemHighlightType.INFORMATION;
|
||||
}
|
||||
holder.registerProblem(call, message, highlight, new ReplaceWithWriteStringFix(highlight == ProblemHighlightType.INFORMATION));
|
||||
}
|
||||
} else if (FILES_READ_ALL_BYTES.test(call)) {
|
||||
PsiExpressionList expressionList = tryCast(PsiUtil.skipParenthesizedExprUp(call.getParent()), PsiExpressionList.class);
|
||||
@@ -58,6 +69,7 @@ public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
}
|
||||
|
||||
private static class ReplaceWithReadStringFix implements LocalQuickFix {
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -87,6 +99,19 @@ public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
}
|
||||
|
||||
private static class ReplaceWithWriteStringFix implements LocalQuickFix {
|
||||
private final boolean myMayNotWork;
|
||||
|
||||
private ReplaceWithWriteStringFix(boolean mayNotWork) {
|
||||
myMayNotWork = mayNotWork;
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
public String getName() {
|
||||
return myMayNotWork ? getFamilyName() + " (may not work before JDK 11.0.2)" : getFamilyName();
|
||||
}
|
||||
|
||||
@Nls(capitalization = Nls.Capitalization.Sentence)
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -122,16 +147,50 @@ public class ReadWriteStringCanBeUsedInspection extends AbstractBaseJavaLocalIns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
expression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (expression instanceof PsiReferenceExpression) {
|
||||
PsiReferenceExpression ref = (PsiReferenceExpression)expression;
|
||||
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());
|
||||
}
|
||||
PsiField target = tryCast(ref.resolve(), PsiField.class);
|
||||
return target != null &&
|
||||
target.getContainingClass() != null &&
|
||||
"java.nio.charset.StandardCharsets".equals(target.getContainingClass().getQualifiedName());
|
||||
if (expression instanceof PsiMethodCallExpression && CHARSET_FOR_NAME.test((PsiMethodCallExpression)expression)) {
|
||||
PsiExpression arg = ((PsiMethodCallExpression)expression).getArgumentList().getExpressions()[0];
|
||||
Object value = ExpressionUtils.computeConstantExpression(arg);
|
||||
return value instanceof String && ((String)value).equalsIgnoreCase("utf-8");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static boolean isNonUtf8Charset(PsiExpression expression) {
|
||||
for (int i = 0; i < 3; i++) {
|
||||
expression = PsiUtil.skipParenthesizedExprDown(expression);
|
||||
if (expression instanceof PsiReferenceExpression) {
|
||||
PsiField target = tryCast(((PsiReferenceExpression)expression).resolve(), PsiField.class);
|
||||
if (target != null) {
|
||||
if ("UTF_8".equals(target.getName())) return false;
|
||||
if (target.getContainingClass() != null &&
|
||||
"java.nio.charset.StandardCharsets".equals(target.getContainingClass().getQualifiedName())) {
|
||||
return true;
|
||||
}
|
||||
if (target.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
expression = target.getInitializer();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (expression instanceof PsiMethodCallExpression && CHARSET_FOR_NAME.test((PsiMethodCallExpression)expression)) {
|
||||
PsiExpression arg = ((PsiMethodCallExpression)expression).getArgumentList().getExpressions()[0];
|
||||
Object value = ExpressionUtils.computeConstantExpression(arg);
|
||||
return value instanceof String && !((String)value).equalsIgnoreCase("utf-8");
|
||||
}
|
||||
break;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
private static final Charset CHARSET = Charset.forName("UTF-16");
|
||||
|
||||
void test1(String str) throws IOException {
|
||||
Files.writeString(Paths.get("/etc/passwd"), str, CHARSET, StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_16;
|
||||
|
||||
void test1(String str) throws IOException {
|
||||
Files.writeString(Paths.get("/etc/passwd"), str, CHARSET, StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
// "Replace with 'Files.writeString()' (may not work before JDK 11.0.2)" "INFORMATION"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
void test1(String str) throws IOException {
|
||||
Files.writeString(Paths.get("/etc/passwd"), str, CHARSET, StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
private static final Charset CHARSET = Charset.forName("UTF-16");
|
||||
|
||||
void test1(String str) throws IOException {
|
||||
byte[] bytes = str.getBytes(CHARSET);
|
||||
Files.write<caret>(Paths.get("/etc/passwd"), bytes, StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Replace with 'Files.writeString()'" "WARNING"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_16;
|
||||
|
||||
void test1(String str) throws IOException {
|
||||
byte[] bytes = str.getBytes(CHARSET);
|
||||
Files.write<caret>(Paths.get("/etc/passwd"), bytes, StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
// "Replace with 'Files.writeString()' (may not work before JDK 11.0.2)" "INFORMATION"
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Paths;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
|
||||
public class Example {
|
||||
private static final Charset CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
void test1(String str) throws IOException {
|
||||
byte[] bytes = str.getBytes(CHARSET);
|
||||
Files.write<caret>(Paths.get("/etc/passwd"), bytes, StandardOpenOption.CREATE);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user