Inline field/method: check for reflective usages

Fixes EA-126479 - CCE: InlineConstantFieldProcessor.inlineExpressionUsage
This commit is contained in:
Tagir Valeev
2018-09-07 16:32:43 +07:00
parent 812b56052e
commit 12b1c5a4e0
7 changed files with 64 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ import com.intellij.openapi.project.Project;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.searches.ReferencesSearch;
import com.intellij.psi.util.PsiTreeUtil;
@@ -42,6 +43,7 @@ import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.stream.Stream;
/**
* @author ven
@@ -201,6 +203,10 @@ public class InlineConstantFieldProcessor extends BaseRefactoringProcessor {
private void inlineExpressionUsage(PsiExpression expr,
PsiExpression initializer1,
Set<PsiAssignmentExpression> assignments) throws IncorrectOperationException {
if (expr instanceof PsiLiteralExpression) {
// Possible reflective usage
return;
}
if (myField.isWritable()) {
myField.normalizeDeclaration();
}
@@ -268,6 +274,10 @@ public class InlineConstantFieldProcessor extends BaseRefactoringProcessor {
conflicts.putValue(element, "Inlined field is used in javadoc");
}
}
if (element instanceof PsiLiteralExpression &&
Stream.of(element.getReferences()).anyMatch(JavaLangClassMemberReference.class::isInstance)) {
conflicts.putValue(element, "Inlined field is used reflectively");
}
}
}

View File

@@ -25,6 +25,7 @@ import com.intellij.psi.codeStyle.VariableKind;
import com.intellij.psi.controlFlow.*;
import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
import com.intellij.psi.impl.source.javadoc.PsiDocMethodOrFieldRef;
import com.intellij.psi.impl.source.resolve.reference.impl.JavaLangClassMemberReference;
import com.intellij.psi.infos.MethodCandidateInfo;
import com.intellij.psi.search.GlobalSearchScope;
import com.intellij.psi.search.LocalSearchScope;
@@ -53,6 +54,7 @@ import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class InlineMethodProcessor extends BaseRefactoringProcessor {
private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inline.InlineMethodProcessor");
@@ -210,6 +212,10 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor {
if (element instanceof PsiDocMethodOrFieldRef && !PsiTreeUtil.isAncestor(myMethod, element, false)) {
conflicts.putValue(element, "Inlined method is used in javadoc");
}
if (element instanceof PsiLiteralExpression &&
Stream.of(element.getReferences()).anyMatch(JavaLangClassMemberReference.class::isInstance)) {
conflicts.putValue(element, "Inlined method is used reflectively");
}
if (element instanceof PsiMethodReferenceExpression) {
final PsiExpression qualifierExpression = ((PsiMethodReferenceExpression)element).getQualifierExpression();
if (qualifierExpression != null) {

View File

@@ -0,0 +1,10 @@
class Test {
private static final int <caret>FIELD = 5;
/**
* Prints the value of {@link Test#FIELD}
*/
void test() {
System.out.println(FIELD);
}
}

View File

@@ -0,0 +1,8 @@
class Test {
private static final int <caret>FIELD = 5;
void test() {
System.out.println(FIELD);
System.out.println(Test.class.getDeclaredField("FIELD"));
}
}

View File

@@ -0,0 +1,7 @@
class InlineMethodTest {
public void f<caret>oo(){}
void test() throws Exception {
System.out.println(InlineMethodTest.class.getDeclaredMethod("foo"));
}
}

View File

@@ -20,6 +20,7 @@ import com.intellij.codeInsight.TargetElementUtil;
import com.intellij.java.refactoring.LightRefactoringTestCase;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.psi.*;
import com.intellij.refactoring.BaseRefactoringProcessor;
import com.intellij.refactoring.inline.InlineConstantFieldHandler;
import com.intellij.refactoring.inline.InlineConstantFieldProcessor;
import com.intellij.testFramework.IdeaTestUtil;
@@ -82,6 +83,14 @@ public class InlineConstantFieldTest extends LightRefactoringTestCase {
doTest();
}
public void testFieldUsedReflectively() {
doTestConflict("Inlined field is used reflectively");
}
public void testFieldUsedInJavadoc() {
doTestConflict("Inlined field is used in javadoc");
}
public void testMultipleInitializers() {
configureByFile("/refactoring/inlineConstantField/" + getTestName(false) + ".java");
PsiElement element = TargetElementUtil
@@ -107,4 +116,14 @@ public class InlineConstantFieldTest extends LightRefactoringTestCase {
new InlineConstantFieldProcessor(field, getProject(), refExpr, inlineThisOnly || element instanceof PsiCompiledElement).run();
checkResultByFile(fileName + ".after");
}
private void doTestConflict(final String conflict) {
try {
doTest();
fail("Conflict was not detected");
}
catch (BaseRefactoringProcessor.ConflictsInTestsException e) {
assertEquals(conflict, e.getMessage());
}
}
}

View File

@@ -231,6 +231,10 @@ public class InlineMethodTest extends LightRefactoringTestCase {
doTestConflict("Inlined method is used in javadoc");
}
public void testMethodUsedReflectively() {
doTestConflict("Inlined method is used reflectively");
}
public void testNotAStatement() {
doTestConflict("Inlined result would contain parse errors");
}