[K2] Properly report diagnostics on const properties with Java usages
#KT-63752 #KT-59894 #KT-61920 Fixed
This commit is contained in:
+4
-6
@@ -18,9 +18,6 @@ package org.jetbrains.kotlin.load.java.structure.impl;
|
||||
|
||||
import com.intellij.psi.PsiEnumConstant;
|
||||
import com.intellij.psi.PsiField;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiVariable;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaField;
|
||||
@@ -28,12 +25,13 @@ import org.jetbrains.kotlin.load.java.structure.JavaType;
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementPsiSource;
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.source.JavaElementSourceFactory;
|
||||
|
||||
import static org.jetbrains.kotlin.util.ConstUtils.isCompileTimeConstant;
|
||||
|
||||
public class JavaFieldImpl extends JavaMemberImpl<PsiField> implements JavaField {
|
||||
public JavaFieldImpl(@NotNull JavaElementPsiSource<PsiField> psiFieldSource) {
|
||||
super(psiFieldSource);
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unused") // used in KSP
|
||||
public JavaFieldImpl(PsiField psiField) {
|
||||
this(JavaElementSourceFactory.getInstance(psiField.getProject()).createPsiSource(psiField));
|
||||
@@ -59,7 +57,7 @@ public class JavaFieldImpl extends JavaMemberImpl<PsiField> implements JavaField
|
||||
@Override
|
||||
public boolean getHasConstantNotNullInitializer() {
|
||||
// PsiUtil.isCompileTimeConstant returns false for null-initialized fields,
|
||||
// see com.intellij.psi.util.IsConstantExpressionVisitor.visitLiteralExpression()
|
||||
return PsiUtil.isCompileTimeConstant((PsiVariable) getPsi());
|
||||
// see IsConstantExpressionVisitor.visitLiteralExpression()
|
||||
return isCompileTimeConstant(getPsi());
|
||||
}
|
||||
}
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.load.java.structure.impl
|
||||
|
||||
interface NotEvaluatedConstAware {
|
||||
fun isNotYetComputed(): Boolean
|
||||
}
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.util;
|
||||
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.compiled.ClsFieldImpl;
|
||||
import com.intellij.psi.impl.java.stubs.PsiFieldStub;
|
||||
import com.intellij.psi.impl.source.PsiFieldImpl;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.load.java.structure.impl.NotEvaluatedConstAware;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class ConstUtils {
|
||||
// Copy paste from com.intellij.psi.util.PsiUtil.isCompileTimeConstant
|
||||
public static boolean isCompileTimeConstant(@NotNull PsiVariable field) {
|
||||
if (!field.hasModifierProperty(PsiModifier.FINAL)) return false;
|
||||
PsiType type = field.getType();
|
||||
return (TypeConversionUtil.isPrimitiveAndNotNull(type) || type.equalsToText(CommonClassNames.JAVA_LANG_STRING))
|
||||
&& field.hasInitializer()
|
||||
&& isConstantExpression(field.getInitializer());
|
||||
}
|
||||
|
||||
// Copy paste from com.intellij.psi.util.PsiUtil.isConstantExpression
|
||||
private static boolean isConstantExpression(@Nullable PsiExpression expression) {
|
||||
if (expression == null) return false;
|
||||
IsConstantExpressionVisitor visitor = new IsConstantExpressionVisitor();
|
||||
expression.accept(visitor);
|
||||
return visitor.isConstant();
|
||||
}
|
||||
}
|
||||
|
||||
// Copy of `com.intellij.psi.util.IsConstantExpressionVisitor`.
|
||||
// This copy is required to be able to handle K2 constants without triggering constant evaluation.
|
||||
// The only change is done in `visitReferenceExpression` where we check for constant expression without triggering evaluation.
|
||||
final class IsConstantExpressionVisitor extends JavaElementVisitor {
|
||||
private boolean myIsConstant;
|
||||
private final Map<PsiVariable, Boolean> varIsConst = new HashMap<>();
|
||||
|
||||
public boolean isConstant() {
|
||||
return myIsConstant;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitExpression(PsiExpression expression) {
|
||||
myIsConstant = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLiteralExpression(PsiLiteralExpression expression) {
|
||||
myIsConstant = !"null".equals(expression.getText());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitClassObjectAccessExpression(PsiClassObjectAccessExpression expression) {
|
||||
myIsConstant = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitParenthesizedExpression(PsiParenthesizedExpression expression) {
|
||||
PsiExpression expr = expression.getExpression();
|
||||
if (expr != null) {
|
||||
expr.accept(this);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitTypeCastExpression(PsiTypeCastExpression expression) {
|
||||
PsiExpression operand = expression.getOperand();
|
||||
if (operand == null) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
operand.accept(this);
|
||||
if (!myIsConstant) return;
|
||||
PsiTypeElement element = expression.getCastType();
|
||||
if (element == null) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
PsiType type = element.getType();
|
||||
if (type instanceof PsiPrimitiveType) return;
|
||||
if (type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) return;
|
||||
myIsConstant = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPrefixExpression(PsiPrefixExpression expression) {
|
||||
PsiExpression operand = expression.getOperand();
|
||||
if (operand == null) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
operand.accept(this);
|
||||
if (!myIsConstant) return;
|
||||
IElementType opType = expression.getOperationTokenType();
|
||||
if (opType == JavaTokenType.PLUS || opType == JavaTokenType.MINUS || opType == JavaTokenType.TILDE || opType == JavaTokenType.EXCL) {
|
||||
return;
|
||||
}
|
||||
myIsConstant = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPolyadicExpression(PsiPolyadicExpression expression) {
|
||||
for (PsiExpression operand : expression.getOperands()) {
|
||||
operand.accept(this);
|
||||
if (!myIsConstant) return;
|
||||
final PsiType type = operand.getType();
|
||||
if (type != null && !(type instanceof PsiPrimitiveType) && !type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitConditionalExpression(PsiConditionalExpression expression) {
|
||||
PsiExpression thenExpr = expression.getThenExpression();
|
||||
PsiExpression elseExpr = expression.getElseExpression();
|
||||
if (thenExpr == null || elseExpr == null) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
|
||||
expression.getCondition().accept(this);
|
||||
if (!myIsConstant) return;
|
||||
thenExpr.accept(this);
|
||||
if (!myIsConstant) return;
|
||||
elseExpr.accept(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitReferenceExpression(PsiReferenceExpression expression) {
|
||||
PsiExpression qualifierExpression = expression.getQualifierExpression();
|
||||
if (qualifierExpression != null && !(qualifierExpression instanceof PsiReferenceExpression)) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
PsiElement refElement = expression.resolve();
|
||||
if (!(refElement instanceof PsiVariable)) {
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
PsiVariable variable = (PsiVariable)refElement;
|
||||
Boolean isConst = varIsConst.get(variable);
|
||||
if (isConst != null) {
|
||||
myIsConstant &= isConst.booleanValue();
|
||||
return;
|
||||
}
|
||||
if (variable instanceof PsiEnumConstant) {
|
||||
myIsConstant = true;
|
||||
varIsConst.put(variable, Boolean.TRUE);
|
||||
return;
|
||||
}
|
||||
varIsConst.put(variable, Boolean.FALSE);
|
||||
if (!variable.hasModifierProperty(PsiModifier.FINAL)){
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// This block is the only difference with the original `IsConstantExpressionVisitor`
|
||||
if (variable instanceof ClsFieldImpl) {
|
||||
PsiFieldStub stub = ((ClsFieldImpl) variable).getStub();
|
||||
if (stub instanceof NotEvaluatedConstAware && ((NotEvaluatedConstAware) stub).isNotYetComputed()) {
|
||||
myIsConstant = true;
|
||||
varIsConst.put(variable, Boolean.TRUE);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// END
|
||||
|
||||
variable.hasInitializer();
|
||||
PsiExpression initializer = PsiFieldImpl.getDetachedInitializer(variable);
|
||||
if (initializer == null){
|
||||
myIsConstant = false;
|
||||
return;
|
||||
}
|
||||
initializer.accept(this);
|
||||
varIsConst.put(variable, myIsConstant);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user