Revert "[java-inspection] IDEA-264141 Warn if compact constructor calls methods that access fields"

This reverts commit 30612508e6ae8a2bf3f7951880430144a2a7481a.

GitOrigin-RevId: 543b8b881b1a973000121d6f749a62fe2138a905
This commit is contained in:
Mikhail Pyltsin
2023-11-17 11:27:44 +00:00
committed by intellij-monorepo-bot
parent 51e1cc8b49
commit de0e95a46b
7 changed files with 0 additions and 234 deletions
@@ -1,131 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection;
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature;
import com.intellij.java.JavaBundle;
import com.intellij.modcommand.ActionContext;
import com.intellij.modcommand.ModCommand;
import com.intellij.modcommand.Presentation;
import com.intellij.modcommand.PsiBasedModCommandAction;
import com.intellij.openapi.util.Ref;
import com.intellij.psi.*;
import com.intellij.psi.impl.light.LightRecordMember;
import com.intellij.psi.util.JavaPsiRecordUtil;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.util.containers.MultiMap;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class CompactRecordConstructorAccessFieldsInspection extends AbstractBaseJavaLocalInspectionTool {
@Override
public boolean runForWholeFile() {
return true;
}
@Override
public @NotNull PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
if (!HighlightingFeature.RECORDS.isAvailable(holder.getFile())) {
return PsiElementVisitor.EMPTY_VISITOR;
}
return new JavaElementVisitor() {
@Override
public void visitClass(@NotNull PsiClass aClass) {
if (!aClass.isRecord()) {
return;
}
if (aClass.getRecordComponents().length == 0) {
return;
}
PsiMethod[] methods = aClass.getMethods();
for (PsiMethod method : methods) {
if (!JavaPsiRecordUtil.isCompactConstructor(method)) {
continue;
}
List<ProblemInfo> problemInfos = processCompactConstructor(method);
for (ProblemInfo problemInfo : problemInfos) {
if (isOnTheFly) {
LocalQuickFix fix = LocalQuickFix.from(new NavigateToUsageFix(problemInfo.reference));
holder.registerProblem(problemInfo.callExpression,
JavaBundle.message("inspection.record.compact.constructor.access.fields.display.name"),
fix);
}
else {
holder.registerProblem(problemInfo.callExpression,
JavaBundle.message("inspection.record.compact.constructor.access.fields.display.name"));
}
}
}
}
record ProblemInfo(@NotNull PsiCallExpression callExpression, @NotNull PsiReferenceExpression reference) {
}
private static List<ProblemInfo> processCompactConstructor(PsiMethod compactConstructor) {
List<ProblemInfo> result = new ArrayList<>();
PsiManager psiManager = compactConstructor.getManager();
PsiClass containingClass = compactConstructor.getContainingClass();
MultiMap<PsiMethod, PsiCallExpression> nestedMethods = new MultiMap<>();
final JavaRecursiveElementWalkingVisitor visitor = new JavaRecursiveElementWalkingVisitor() {
@Override
public void visitCallExpression(@NotNull PsiCallExpression callExpression) {
PsiMethod resolvedMethod = callExpression.resolveMethod();
if (resolvedMethod != null &&
!resolvedMethod.hasModifierProperty(PsiModifier.STATIC) &&
psiManager.areElementsEquivalent(resolvedMethod.getContainingClass(), containingClass)) {
nestedMethods.putValue(resolvedMethod, callExpression);
}
}
};
compactConstructor.accept(visitor);
for (PsiMethod method : nestedMethods.keySet()) {
Ref<PsiReferenceExpression> found = new Ref<>();
PsiTreeUtil.processElements(method.getBody(), e -> {
if (e instanceof PsiReferenceExpression referenceExpression) {
PsiElement resolved = referenceExpression.resolve();
if (resolved instanceof LightRecordMember) {
found.set(referenceExpression);
return false;
}
}
return true;
});
if (!found.isNull()) {
nestedMethods.get(method).forEach(expr -> {
result.add(new ProblemInfo(expr, found.get()));
});
}
}
return result;
}
};
}
private static class NavigateToUsageFix extends PsiBasedModCommandAction<PsiReferenceExpression> {
private NavigateToUsageFix(@NotNull PsiReferenceExpression reference) {
super(reference);
}
@Override
@NotNull
public String getFamilyName() {
return JavaBundle.message("inspection.record.compact.constructor.access.fields.navigate.usages.family");
}
@Override
protected @Nullable Presentation getPresentation(@NotNull ActionContext context, @NotNull PsiReferenceExpression reference) {
return Presentation.of(JavaBundle.message("inspection.record.compact.constructor.access.fields.navigate.usages.declaration.text",
reference.getCanonicalText()));
}
@Override
protected @NotNull ModCommand perform(@NotNull ActionContext context, @NotNull PsiReferenceExpression reference) {
return ModCommand.select(reference);
}
}
}
@@ -1607,10 +1607,6 @@
groupBundle="messages.InspectionsBundle" groupKey="group.names.probable.bugs"
bundle="messages.JavaBundle" key="inspection.meaningless.record.annotation.description"
implementationClass="com.intellij.codeInspection.MeaninglessRecordAnnotationInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="CompactRecordConstructorAccessFieldsInspection" enabledByDefault="true" level="WARNING"
groupBundle="messages.InspectionsBundle" groupKey="group.names.probable.bugs"
bundle="messages.JavaBundle" key="inspection.record.compact.constructor.access.fields.description"
implementationClass="com.intellij.codeInspection.CompactRecordConstructorAccessFieldsInspection"/>
<localInspection groupPath="Java" language="JAVA" shortName="RecordCanBeClass" enabledByDefault="true" level="INFORMATION"
groupBundle="messages.InspectionsBundle" groupKey="group.names.code.style.issues"
bundle="messages.JavaBundle" key="inspection.message.record.can.be.converted.to.class"
@@ -1,22 +0,0 @@
<html>
<body>
Reports method calls within record compact constructors if these methods access fields.
<p>These calls look suspicious because record fields may not be initialized at the time of these calls.</p>
<p><b>Example:</b></p>
<pre><code>
public record MyRecord(String name, int id) {
public MyRecord {
validateMyFields(); // it looks suspicious
}
private void validateMyFields() {
if (this.name.isEmpty()) {
throw new IllegalArgumentException();
}
}
}
</code></pre>
<!-- tooltip end -->
<p><small>New in 2024.1</small></p>
</body>
</html>
@@ -1,34 +0,0 @@
class RecordMain {
public record MyRecord(String name, int id) {
public MyRecord {
<warning descr="Calling method can access non-initialized fields">validateMyFields()</warning>;
<warning descr="Calling method can access non-initialized fields">validateMyFieldsWithGetter()</warning>;
nothing();
}
private void nothing() {
}
private static void t() {
}
private void validateMyFields() {
if (this.name.isEmpty()) {
throw new IllegalArgumentException();
}
}
private void validateMyFieldsWithGetter() {
if (this.name().isEmpty()) {
throw new IllegalArgumentException();
}
}
}
public static void main(String[] args) {
MyRecord myRecord = new MyRecord("s", 2);
System.out.println(myRecord);
}
}
@@ -1,26 +0,0 @@
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.java.codeInspection;
import com.intellij.JavaTestUtil;
import com.intellij.codeInspection.CompactRecordConstructorAccessFieldsInspection;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
public class CompactRecordConstructorAccessFieldsInspectionTest extends LightJavaCodeInsightFixtureTestCase {
public void testNestedCalls() { doTest(); }
private void doTest() {
myFixture.enableInspections(new CompactRecordConstructorAccessFieldsInspection());
myFixture.testHighlighting(getTestName(false) + ".java");
}
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_21;
}
@Override
protected String getBasePath() {
return JavaTestUtil.getRelativeJavaTestDataPath()+"/inspection/compactRecordConstructorAccessFields";
}
}
@@ -19,10 +19,7 @@ import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCa
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.SimplifyStreamApiCallChainsInspection;
import com.intellij.openapi.projectRoots.Sdk;
import com.intellij.pom.java.LanguageLevel;
import com.intellij.testFramework.IdeaTestUtil;
import com.intellij.testFramework.LightProjectDescriptor;
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
import org.jetbrains.annotations.NotNull;
public class SimplifyStreamApiCallChainsInspectionFixTest extends LightQuickFixParameterizedTestCase {
@@ -37,16 +34,6 @@ public class SimplifyStreamApiCallChainsInspectionFixTest extends LightQuickFixP
return IdeaTestUtil.getMockJdk11();
}
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_21;
}
@Override
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
return LightJavaCodeInsightFixtureTestCase.JAVA_21;
}
@Override
protected String getBasePath() {
return "/inspection/streamApiCallChains";
@@ -1705,10 +1705,6 @@ inspection.redundant.unmodifiable.call.description=Redundant usage of unmodifiab
inspection.redundant.unmodifiable.call.unwrap.argument.quickfix=Unwrap argument
completion.override.implement.methods=Override/Implement methods...
lambda.tree.node.presentation=Lambda
inspection.record.compact.constructor.access.fields.description=Compact constructor access components
inspection.record.compact.constructor.access.fields.display.name=Calling method can access non-initialized fields
inspection.record.compact.constructor.access.fields.navigate.usages.family=Navigate to a component usage
inspection.record.compact.constructor.access.fields.navigate.usages.declaration.text=Navigate to a component usage ''{0}''
inspection.meaningless.record.annotation.description=Meaningless record annotation
inspection.meaningless.record.annotation.message.method.and.parameter=Annotation has no effect: its targets are METHOD and PARAMETER but both accessor and canonical constructor are explicitly declared
inspection.meaningless.record.annotation.message.method=Annotation has no effect: its target is METHOD but the corresponding accessor is explicitly declared