diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/restriction/AnnotationContext.java b/java/java-analysis-impl/src/com/intellij/codeInspection/restriction/AnnotationContext.java index 36115f32809b..a19d033b6545 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/restriction/AnnotationContext.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/restriction/AnnotationContext.java @@ -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(); diff --git a/plugins/java-i18n/src/com/intellij/lang/properties/PropertiesUastReferenceContributor.java b/plugins/java-i18n/src/com/intellij/lang/properties/PropertiesUastReferenceContributor.java index c5b06f4b9be5..39204065b920 100644 --- a/plugins/java-i18n/src/com/intellij/lang/properties/PropertiesUastReferenceContributor.java +++ b/plugins/java-i18n/src/com/intellij/lang/properties/PropertiesUastReferenceContributor.java @@ -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 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 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; }); diff --git a/plugins/java-i18n/src/com/intellij/lang/properties/UastPropertiesReferenceProvider.java b/plugins/java-i18n/src/com/intellij/lang/properties/UastPropertiesReferenceProvider.java index 39549146a16c..e0c9343f593e 100644 --- a/plugins/java-i18n/src/com/intellij/lang/properties/UastPropertiesReferenceProvider.java +++ b/plugins/java-i18n/src/com/intellij/lang/properties/UastPropertiesReferenceProvider.java @@ -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; - } } diff --git a/plugins/kotlin/i18n/resources/kotlin.i18n.xml b/plugins/kotlin/i18n/resources/kotlin.i18n.xml index 94da8da0ea78..5668caa07e31 100644 --- a/plugins/kotlin/i18n/resources/kotlin.i18n.xml +++ b/plugins/kotlin/i18n/resources/kotlin.i18n.xml @@ -4,7 +4,6 @@ - ()?.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()?.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 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 { - 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 { - if (!(element is KtStringTemplateExpression && element.isPlain() && element.isBundleName())) return PsiReference.EMPTY_ARRAY - return arrayOf(ResourceBundleReference(element)) - } -} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyFileUsages.0.properties b/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyFileUsages.0.properties index ae40951daed1..f515cf6e2ba8 100644 --- a/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyFileUsages.0.properties +++ b/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyFileUsages.0.properties @@ -1,2 +1,4 @@ ## FIND_FILE_USAGES -foo.bar=test \ No newline at end of file +foo.bar=test + +// FIR_COMPARISON \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyUsages.0.properties b/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyUsages.0.properties index ffca5b232c01..c8bf7d74e550 100644 --- a/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyUsages.0.properties +++ b/plugins/kotlin/idea/tests/testData/findUsages/propertyFiles/propertyUsages.0.properties @@ -1 +1,3 @@ -foo.bar=test \ No newline at end of file +foo.bar=test + +// FIR_COMPARISON \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/multiFileInspections/invalidBundleOrProperty/expected.xml b/plugins/kotlin/idea/tests/testData/multiFileInspections/invalidBundleOrProperty/expected.xml index 7860806a395b..c4a309bf0aed 100644 --- a/plugins/kotlin/idea/tests/testData/multiFileInspections/invalidBundleOrProperty/expected.xml +++ b/plugins/kotlin/idea/tests/testData/multiFileInspections/invalidBundleOrProperty/expected.xml @@ -102,12 +102,4 @@ 15 5 - - unresolvedBundleReference.kt - 5 - testInvalidBundleOrProperty_InvalidBundleOrProperty - - Invalid property key - Invalid resource bundle reference 'TestBundle2' - diff --git a/plugins/kotlin/kotlin.searching/src/org/jetbrains/kotlin/idea/searching/usages/KotlinK2SearchUsagesSupport.kt b/plugins/kotlin/kotlin.searching/src/org/jetbrains/kotlin/idea/searching/usages/KotlinK2SearchUsagesSupport.kt index f5d4968bcee1..3adbc40a5b2f 100644 --- a/plugins/kotlin/kotlin.searching/src/org/jetbrains/kotlin/idea/searching/usages/KotlinK2SearchUsagesSupport.kt +++ b/plugins/kotlin/kotlin.searching/src/org/jetbrains/kotlin/idea/searching/usages/KotlinK2SearchUsagesSupport.kt @@ -182,6 +182,7 @@ internal class KotlinK2SearchUsagesSupport : KotlinSearchUsagesSupport { override fun forceResolveReferences(file: KtFile, elements: List) { + val i = 0; } override fun scriptDefinitionExists(file: PsiFile): Boolean {