mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 02:59:33 +07:00
IDEA-231900 Support TYPE_USE Nls
GitOrigin-RevId: 40c7aedb9cc07d0a1902c2c5f6b46056fe8e34ad
This commit is contained in:
committed by
intellij-monorepo-bot
parent
2f4700ceea
commit
320de07c32
@@ -3,6 +3,7 @@ package com.intellij.codeInsight;
|
||||
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.*;
|
||||
@@ -280,6 +281,28 @@ public class AnnotationUtil {
|
||||
public static final int CHECK_INFERRED = 0x04;
|
||||
public static final int CHECK_TYPE = 0x08;
|
||||
|
||||
/**
|
||||
* @param type type to check
|
||||
* @param qualifiedNames annotation qualified names
|
||||
* @return found type annotation, or null if not found. For type parameter types upper bound annotations are also checked
|
||||
*/
|
||||
@Contract("null, _ -> null")
|
||||
public static @Nullable PsiAnnotation findTypeAnnotationInHierarchy(@Nullable PsiType type, @NotNull Set<String> qualifiedNames) {
|
||||
if (type == null) return null;
|
||||
Ref<PsiAnnotation> result = Ref.create(null);
|
||||
InheritanceUtil.processSuperTypes(type, true, eachType -> {
|
||||
for (PsiAnnotation annotation : eachType.getAnnotations()) {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
if (qualifiedNames.contains(qualifiedName)) {
|
||||
result.set(annotation);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !(eachType instanceof PsiClassType) || PsiUtil.resolveClassInClassTypeOnly(eachType) instanceof PsiTypeParameter;
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
|
||||
@MagicConstant(flags = {CHECK_HIERARCHY, CHECK_EXTERNAL, CHECK_INFERRED, CHECK_TYPE})
|
||||
@Target({ElementType.PARAMETER, ElementType.METHOD})
|
||||
private @interface Flags { }
|
||||
|
||||
@@ -5,7 +5,6 @@ import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.RecursionManager;
|
||||
import com.intellij.openapi.util.Ref;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.*;
|
||||
import org.jetbrains.annotations.ApiStatus;
|
||||
@@ -305,21 +304,8 @@ public abstract class NullableNotNullManager {
|
||||
if (annotation != memberAnno && !qualifiedNames.contains(annotation.getQualifiedName())) return null;
|
||||
return annotation;
|
||||
}
|
||||
if (type != null && !(type instanceof PsiPrimitiveType)) {
|
||||
Ref<PsiAnnotation> result = Ref.create(null);
|
||||
InheritanceUtil.processSuperTypes(type, true, eachType -> {
|
||||
for (PsiAnnotation annotation : eachType.getAnnotations()) {
|
||||
String qualifiedName = annotation.getQualifiedName();
|
||||
if (qualifiedNames.contains(qualifiedName)) {
|
||||
result.set(annotation);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !(type instanceof PsiClassType) || PsiUtil.resolveClassInClassTypeOnly(type) instanceof PsiTypeParameter;
|
||||
});
|
||||
return result.get();
|
||||
}
|
||||
return null;
|
||||
if (type instanceof PsiPrimitiveType) return null;
|
||||
return findTypeAnnotationInHierarchy(type, qualifiedNames);
|
||||
}
|
||||
|
||||
private static @NotNull PsiAnnotation preferTypeAnnotation(@NotNull PsiAnnotation memberAnno, @Nullable PsiType type) {
|
||||
|
||||
@@ -860,8 +860,9 @@ public class I18nInspection extends AbstractBaseUastLocalInspectionTool implemen
|
||||
|
||||
private static boolean isReturnedFromAnnotatedMethod(final ULiteralExpression expression,
|
||||
final String fqn,
|
||||
@Nullable final Set<? super PsiModifierListOwner> nonNlsTargets) {
|
||||
final @Nullable Set<? super PsiModifierListOwner> nonNlsTargets) {
|
||||
PsiMethod method;
|
||||
PsiType returnType = null;
|
||||
UNamedExpression nameValuePair = UastUtils.getParentOfType(expression, UNamedExpression.class);
|
||||
if (nameValuePair != null) {
|
||||
method = UastUtils.getAnnotationMethod(nameValuePair);
|
||||
@@ -873,12 +874,22 @@ public class I18nInspection extends AbstractBaseUastLocalInspectionTool implemen
|
||||
((UCallExpression)parent).getKind() == UastCallKind.NEW_ARRAY_WITH_INITIALIZER) {
|
||||
parent = parent.getUastParent();
|
||||
}
|
||||
if (parent == null) return false;
|
||||
final UElement returnStmt = UastUtils.getParentOfType(parent, UReturnExpression.class, false, UCallExpression.class, ULambdaExpression.class);
|
||||
if (!(returnStmt instanceof UReturnExpression)) {
|
||||
return false;
|
||||
}
|
||||
UMethod uMethod = UastUtils.getParentOfType(expression, UMethod.class);
|
||||
method = uMethod != null ? uMethod.getJavaPsi() : null;
|
||||
UElement jumpTarget = ((UReturnExpression)returnStmt).getJumpTarget();
|
||||
if (jumpTarget instanceof UMethod) {
|
||||
method = ((UMethod)jumpTarget).getJavaPsi();
|
||||
}
|
||||
else if (jumpTarget instanceof ULambdaExpression) {
|
||||
PsiType type = ((ULambdaExpression)jumpTarget).getFunctionalInterfaceType();
|
||||
returnType = LambdaUtil.getFunctionalInterfaceReturnType(type);
|
||||
if (type == null) return false;
|
||||
method = LambdaUtil.getFunctionalInterfaceMethod(type);
|
||||
}
|
||||
else return false;
|
||||
}
|
||||
if (method == null) return false;
|
||||
|
||||
@@ -891,6 +902,14 @@ public class I18nInspection extends AbstractBaseUastLocalInspectionTool implemen
|
||||
if (AnnotationUtil.isAnnotated(method, fqn, CHECK_HIERARCHY | CHECK_EXTERNAL)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (returnType != null) {
|
||||
PsiAnnotation typeAnnotation = AnnotationUtil.findTypeAnnotationInHierarchy(returnType, NON_NLS_NAMES);
|
||||
if (typeAnnotation != null) {
|
||||
return typeAnnotation.hasQualifiedName(fqn);
|
||||
}
|
||||
}
|
||||
|
||||
if (nonNlsTargets != null) {
|
||||
nonNlsTargets.add(method);
|
||||
}
|
||||
|
||||
@@ -128,8 +128,23 @@ public class JavaI18nUtil extends I18nUtil {
|
||||
if (!idx.isPresent()) return false;
|
||||
|
||||
PsiMethod method = callExpression.resolve();
|
||||
return method != null && isMethodParameterAnnotatedWith(method, idx.getAsInt(), null, annFqn, null, nonNlsTargets);
|
||||
|
||||
if (method == null) return false;
|
||||
if (isMethodParameterAnnotatedWith(method, idx.getAsInt(), null, annFqn, null, nonNlsTargets)) {
|
||||
return true;
|
||||
}
|
||||
PsiParameter parameter = method.getParameterList().getParameter(idx.getAsInt());
|
||||
if (parameter != null) {
|
||||
PsiType parameterType = parameter.getType();
|
||||
PsiType receiverType = callExpression.getReceiverType();
|
||||
if (receiverType instanceof PsiClassType) {
|
||||
PsiClassType.ClassResolveResult result = ((PsiClassType)receiverType).resolveGenerics();
|
||||
parameterType = result.getSubstitutor().substitute(parameterType);
|
||||
}
|
||||
if (AnnotationUtil.findTypeAnnotationInHierarchy(parameterType, Collections.singleton(annFqn)) != null) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
|
||||
33
plugins/java-i18n/testData/inspections/i18n/NlsTypeUse.java
Normal file
33
plugins/java-i18n/testData/inspections/i18n/NlsTypeUse.java
Normal file
@@ -0,0 +1,33 @@
|
||||
package org.jetbrains.annotations;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
import java.util.*;
|
||||
import java.util.function.*;
|
||||
import static java.lang.annotation.ElementType.*;
|
||||
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
@Target({METHOD, FIELD, PARAMETER, LOCAL_VARIABLE, TYPE_USE, TYPE, PACKAGE})
|
||||
@interface Nls {}
|
||||
|
||||
interface AnnotatedSam {
|
||||
@Nls String getString();
|
||||
}
|
||||
|
||||
class NlsTypeUse {
|
||||
native void foo(AnnotatedSam str);
|
||||
native void foo2(Supplier<@Nls String> str);
|
||||
native void foo3(Supplier<String> str);
|
||||
|
||||
Supplier<@Nls String> getSupplier() {
|
||||
return () -> <warning descr="Hardcoded string literal: \"foo\"">"foo"</warning>;
|
||||
}
|
||||
|
||||
void test(Map<String, @Nls String> map, BiConsumer<@Nls String, String> cons) {
|
||||
foo(() -> <warning descr="Hardcoded string literal: \"bar\"">"bar"</warning>);
|
||||
foo2(() -> <warning descr="Hardcoded string literal: \"bar\"">"bar"</warning>);
|
||||
foo3(() -> "bar");
|
||||
|
||||
map.put("foo", <warning descr="Hardcoded string literal: \"bar\"">"bar"</warning>);
|
||||
cons.accept(<warning descr="Hardcoded string literal: \"foo\"">"foo"</warning>, "bar");
|
||||
}
|
||||
}
|
||||
@@ -97,6 +97,16 @@ public class I18NInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public void testNlsTypeUse() {
|
||||
boolean old = myTool.setIgnoreForAllButNls(true);
|
||||
try {
|
||||
doTest();
|
||||
}
|
||||
finally {
|
||||
myTool.setIgnoreForAllButNls(old);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTestDataPath() {
|
||||
return PluginPathManager.getPluginHomePath("java-i18n") + "/testData/inspections";
|
||||
|
||||
@@ -50,6 +50,7 @@ fun UElement.skipParentOfType(strict: Boolean, vararg parentClasses: Class<out U
|
||||
}
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
fun <T : UElement> UElement.getParentOfType(
|
||||
parentClass: Class<out UElement>,
|
||||
strict: Boolean = true,
|
||||
|
||||
Reference in New Issue
Block a user