[kotlin] fix common uast properties ref contributor to get rid of dedicated kotlin one

^ KTIJ-22356
also enables search for properties bundle/properties key for k2 (part of KTIJ-21662)

GitOrigin-RevId: e3ec5803a2de593e1b3fd742f3ed7ae97a3e6d42
This commit is contained in:
Anna Kozlova
2022-12-16 22:08:16 +00:00
committed by intellij-monorepo-bot
parent 5ab4f3c510
commit ddb2d075d9
10 changed files with 50 additions and 202 deletions
@@ -1,4 +1,4 @@
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.codeInspection.restriction;
import com.intellij.lang.java.JavaLanguage;
@@ -112,6 +112,8 @@ public final class AnnotationContext {
if (context != EMPTY) return context;
context = fromArgument(expression);
if (context != EMPTY) return context;
context = fromInfixMethod(expression);
if (context != EMPTY) return context;
return fromInitializer(expression);
}
@@ -223,6 +225,18 @@ public final class AnnotationContext {
return fromModifierListOwner(parameter).withType(parameterType);
}
private static @NotNull AnnotationContext fromInfixMethod(@NotNull UExpression expression) {
UBinaryExpression parent = ObjectUtils.tryCast(expression.getUastParent(), UBinaryExpression.class);
PsiMethod method = parent != null ? parent.resolveOperator() : null;
if (method == null) return EMPTY;
PsiParameter[] parameters = method.getParameterList().getParameters();
if (parameters.length != 2) return EMPTY;
PsiParameter parameter = UastUtils.isPsiAncestor(expression, parent.getRightOperand()) ? parameters[1] : parameters[0];
if (parameter == null) return EMPTY;
PsiType parameterType = parameter.getType();
return fromModifierListOwner(parameter).withType(parameterType);
}
@NotNull
private static AnnotationContext fromInitializer(UExpression expression) {
UElement parent = expression.getUastParent();
@@ -1,18 +1,4 @@
/*
* Copyright 2000-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang.properties;
import com.intellij.codeInsight.AnnotationUtil;
@@ -20,11 +6,11 @@ import com.intellij.openapi.diagnostic.Logger;
import com.intellij.psi.*;
import com.intellij.psi.search.LocalSearchScope;
import com.intellij.psi.search.searches.AnnotatedElementsSearch;
import com.intellij.util.ObjectUtils;
import com.intellij.util.ProcessingContext;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UField;
import org.jetbrains.uast.*;
import java.util.ArrayList;
import java.util.Collections;
@@ -63,37 +49,34 @@ public class PropertiesUastReferenceContributor extends PsiReferenceContributor
if (!(parent instanceof UField)) {
return PsiReference.EMPTY_ARRAY;
}
PsiElement elementSource = uExpression.getSourcePsi();
if (elementSource == null) return PsiReference.EMPTY_ARRAY;
final UField field = (UField)parent;
UExpression initializer = field.getUastInitializer();
if (initializer == null) return PsiReference.EMPTY_ARRAY;
PsiElement initializerSource = initializer.getSourcePsi();
if (initializerSource == null) return PsiReference.EMPTY_ARRAY;
PsiElement elementSource = uExpression.getSourcePsi();
if (initializerSource != elementSource ||
!field.isFinal() ||
if (!field.isFinal() ||
!field.getType().equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
return PsiReference.EMPTY_ARRAY;
}
List<PsiReference> references = new ArrayList<>();
final PsiClass propertyKeyAnnotation =
JavaPsiFacade.getInstance(initializerSource.getProject())
JavaPsiFacade.getInstance(elementSource.getProject())
.findClass(AnnotationUtil.PROPERTY_KEY, elementSource.getResolveScope());
if (propertyKeyAnnotation != null) {
LOG.assertTrue(propertyKeyAnnotation.isAnnotationType());
AnnotatedElementsSearch.searchPsiParameters(propertyKeyAnnotation, new LocalSearchScope(elementSource.getContainingFile()))
.forEach(parameter -> {
final PsiModifierList list = parameter.getModifierList();
LOG.assertTrue(list != null);
final PsiAnnotation annotation = list.findAnnotation(AnnotationUtil.PROPERTY_KEY);
LOG.assertTrue(annotation != null);
for (PsiNameValuePair pair : annotation.getParameterList().getAttributes()) {
if (AnnotationUtil.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER.equals(pair.getName())) {
final PsiAnnotationMemberValue value = pair.getValue();
if (value instanceof PsiReferenceExpression && ((PsiReferenceExpression)value).resolve() == field.getSourcePsi()) {
Collections.addAll(references, myUnderlying.getReferencesForInjectionHost(uExpression, host, context));
return false;
}
}
UParameter uParameter = ObjectUtils.tryCast(UastContextKt.toUElement(parameter), UParameter.class);
if (uParameter == null) return true;
List<UAnnotation> annotations = uParameter.getUAnnotations();
UAnnotation uAnnotation =
ContainerUtil.find(annotations, anno -> AnnotationUtil.PROPERTY_KEY.equals(anno.getQualifiedName()));
if (uAnnotation == null) return true;
UExpression attributeValue = uAnnotation.findAttributeValue(AnnotationUtil.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER);
if (attributeValue instanceof UResolvable &&
field.equals(UastContextKt.toUElement(((UResolvable)attributeValue).resolve()))) {
Collections.addAll(references, myUnderlying.getReferencesForInjectionHost(uExpression, host, context));
return false;
}
return true;
});
@@ -1,4 +1,4 @@
// Copyright 2000-2020 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.
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package com.intellij.lang.properties;
import com.intellij.codeInspection.i18n.JavaI18nUtil;
@@ -13,10 +13,7 @@ import com.intellij.psi.PsiReference;
import com.intellij.psi.UastInjectionHostReferenceProvider;
import com.intellij.util.ProcessingContext;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.uast.UElement;
import org.jetbrains.uast.UExpression;
import org.jetbrains.uast.UPolyadicExpression;
import org.jetbrains.uast.UastBinaryOperator;
import org.jetbrains.uast.*;
class UastPropertiesReferenceProvider extends UastInjectionHostReferenceProvider {
@@ -37,7 +34,10 @@ class UastPropertiesReferenceProvider extends UastInjectionHostReferenceProvider
@NotNull PsiLanguageInjectionHost host,
@NotNull ProcessingContext context) {
UExpression parent = StringFlowUtil.goUp(element, false, NlsInfo.factory());
if (!canBePropertyKeyRef(parent)) {
UElement gParent = parent.getUastParent();
if (gParent instanceof UPolyadicExpression &&
((UPolyadicExpression)gParent).getOperator() != UastBinaryOperator.ASSIGN &&
(!(gParent instanceof UBinaryExpression) || ((UBinaryExpression)gParent).resolveOperator() == null)) {
return PsiReference.EMPTY_ARRAY;
}
Object value = element.evaluate();
@@ -63,15 +63,13 @@ class UastPropertiesReferenceProvider extends UastInjectionHostReferenceProvider
}
}
else {
if (gParent instanceof UBinaryExpression) {
return PsiReference.EMPTY_ARRAY;
}
soft = myDefaultSoft;
bundleName = null;
}
PsiReference reference = new PropertyReference(text, host, bundleName, soft);
return new PsiReference[]{reference};
}
private static boolean canBePropertyKeyRef(@NotNull UExpression element) {
UElement parent = element.getUastParent();
return !(parent instanceof UPolyadicExpression) || ((UPolyadicExpression)parent).getOperator() == UastBinaryOperator.ASSIGN;
}
}
@@ -4,7 +4,6 @@
</dependencies>
<extensions defaultExtensionNs="com.intellij">
<psi.referenceContributor language="kotlin" implementation="org.jetbrains.kotlin.idea.i18n.KotlinPropertiesReferenceContributor"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.i18n.KotlinInvalidBundleOrPropertyInspection"
groupBundle="messages.KotlinBundle" groupKey="group.names.kotlin"
@@ -1,21 +0,0 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.i18n
import com.intellij.patterns.PlatformPatterns
import com.intellij.psi.*
import org.jetbrains.kotlin.psi.KtStringTemplateExpression
class KotlinPropertiesReferenceContributor : PsiReferenceContributor() {
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) {
registrar.registerReferenceProvider(
PlatformPatterns.psiElement(KtStringTemplateExpression::class.java),
KotlinPropertyKeyReferenceProvider
)
registrar.registerReferenceProvider(
PlatformPatterns.psiElement(KtStringTemplateExpression::class.java),
KotlinResourceBundleNameReferenceProvider
)
}
}
@@ -1,122 +0,0 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.i18n
import com.intellij.codeInsight.AnnotationUtil
import com.intellij.lang.properties.ResourceBundleReference
import com.intellij.lang.properties.references.PropertyReference
import com.intellij.psi.ElementManipulators
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiReferenceProvider
import com.intellij.psi.search.LocalSearchScope
import com.intellij.util.ProcessingContext
import org.jetbrains.annotations.PropertyKey
import org.jetbrains.kotlin.base.fe10.analysis.findAnnotation
import org.jetbrains.kotlin.base.fe10.analysis.getStringValue
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.caches.resolve.safeAnalyzeNonSourceRootCode
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.anyDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.isPlain
import org.jetbrains.kotlin.resolve.calls.util.getParentResolvedCall
import org.jetbrains.kotlin.resolve.calls.util.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
private val PROPERTY_KEY = FqName(AnnotationUtil.PROPERTY_KEY)
private val PROPERTY_KEY_RESOURCE_BUNDLE = Name.identifier(AnnotationUtil.PROPERTY_KEY_RESOURCE_BUNDLE_PARAMETER)
private fun Annotated.getBundleNameByAnnotation(): String? {
return findAnnotation<PropertyKey>()?.getStringValue(PropertyKey::resourceBundle)
}
private fun KtExpression.getBundleNameByContext(): String? {
val expression = KtPsiUtil.safeDeparenthesize(this)
val parent = expression.parent
if (parent is KtProperty) {
return parent.resolveToDescriptorIfAny()?.getBundleNameByAnnotation()
}
val bindingContext = expression.safeAnalyzeNonSourceRootCode(BodyResolveMode.PARTIAL)
val resolvedCall = if (parent is KtQualifiedExpression && expression == parent.receiverExpression) {
parent.selectorExpression.getResolvedCall(bindingContext)
} else {
expression.getParentResolvedCall(bindingContext)
} ?: return null
val callable = resolvedCall.resultingDescriptor
if ((resolvedCall.extensionReceiver as? ExpressionReceiver)?.expression == expression) {
return callable.extensionReceiverParameter?.getBundleNameByAnnotation()
}
return resolvedCall.valueArguments.entries
.singleOrNull { it.value.arguments.any { it.getArgumentExpression() == expression } }
?.key
?.getBundleNameByAnnotation()
}
private fun KtAnnotationEntry.getPropertyKeyResolvedCall(): ResolvedCall<*>? {
val resolvedCall = resolveToCall() ?: return null
val klass = (resolvedCall.resultingDescriptor as? ClassConstructorDescriptor)?.containingDeclaration ?: return null
if (klass.kind != ClassKind.ANNOTATION_CLASS || klass.importableFqName != PROPERTY_KEY) return null
return resolvedCall
}
private fun KtStringTemplateExpression.isBundleName(): Boolean {
when (val parent = KtPsiUtil.safeDeparenthesize(this).parent) {
is KtValueArgument -> {
val resolvedCall = parent.getStrictParentOfType<KtAnnotationEntry>()?.getPropertyKeyResolvedCall() ?: return false
val valueParameter = (resolvedCall.getArgumentMapping(parent) as? ArgumentMatch)?.valueParameter ?: return false
if (valueParameter.name != PROPERTY_KEY_RESOURCE_BUNDLE) return false
return true
}
is KtProperty -> {
val contexts = (parent.useScope as? LocalSearchScope)?.scope ?: arrayOf(parent.containingFile)
return contexts.any {
it.anyDescendantOfType<KtAnnotationEntry> f@{ entry ->
if (!entry.valueArguments.any { it.getArgumentName()?.asName == PROPERTY_KEY_RESOURCE_BUNDLE }) return@f false
val resolvedCall = entry.getPropertyKeyResolvedCall() ?: return@f false
val parameter =
resolvedCall.resultingDescriptor.valueParameters.singleOrNull { it.name == PROPERTY_KEY_RESOURCE_BUNDLE }
?: return@f false
val valueArgument = resolvedCall.valueArguments[parameter] as? ExpressionValueArgument ?: return@f false
val bundleNameExpression = valueArgument.valueArgument?.getArgumentExpression() ?: return@f false
bundleNameExpression is KtSimpleNameExpression && bundleNameExpression.mainReference.resolve() == parent
}
}
}
}
return false
}
object KotlinPropertyKeyReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<out PsiReference> {
if (!(element is KtStringTemplateExpression && element.isPlain())) return PsiReference.EMPTY_ARRAY
val bundleName = element.getBundleNameByContext() ?: return PsiReference.EMPTY_ARRAY
return arrayOf(PropertyReference(ElementManipulators.getValueText(element), element, bundleName, false))
}
}
object KotlinResourceBundleNameReferenceProvider : PsiReferenceProvider() {
override fun getReferencesByElement(element: PsiElement, context: ProcessingContext): Array<out PsiReference> {
if (!(element is KtStringTemplateExpression && element.isPlain() && element.isBundleName())) return PsiReference.EMPTY_ARRAY
return arrayOf(ResourceBundleReference(element))
}
}
@@ -1,2 +1,4 @@
## FIND_FILE_USAGES
foo.bar=test
foo.bar=test
// FIR_COMPARISON
@@ -1 +1,3 @@
foo<caret>.bar=test
foo<caret>.bar=test
// FIR_COMPARISON
@@ -102,12 +102,4 @@
<offset>15</offset>
<length>5</length>
</problem>
<problem>
<file>unresolvedBundleReference.kt</file>
<line>5</line>
<module>testInvalidBundleOrProperty_InvalidBundleOrProperty</module>
<entry_point TYPE="method" FQNAME="UnresolvedBundleReferenceKt java.lang.String message(java.lang.String key, java.lang.Object... args)"/>
<problem_class severity="ERROR" attribute_key="WRONG_REFERENCES_ATTRIBUTES">Invalid property key</problem_class>
<description>Invalid resource bundle reference 'TestBundle2'</description>
</problem>
</problems>
@@ -182,6 +182,7 @@ internal class KotlinK2SearchUsagesSupport : KotlinSearchUsagesSupport {
override fun forceResolveReferences(file: KtFile, elements: List<KtElement>) {
val i = 0;
}
override fun scriptDefinitionExists(file: PsiFile): Boolean {