IDEA-231900 Support TYPE_USE Nls

GitOrigin-RevId: 40c7aedb9cc07d0a1902c2c5f6b46056fe8e34ad
This commit is contained in:
Tagir Valeev
2020-02-19 13:41:40 +07:00
committed by intellij-monorepo-bot
parent 2f4700ceea
commit 320de07c32
7 changed files with 108 additions and 21 deletions

View File

@@ -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 { }

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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

View 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");
}
}

View File

@@ -96,6 +96,16 @@ public class I18NInspectionTest extends LightJavaCodeInsightFixtureTestCase {
myTool.setIgnoreForEnumConstants(oldState);
}
}
public void testNlsTypeUse() {
boolean old = myTool.setIgnoreForAllButNls(true);
try {
doTest();
}
finally {
myTool.setIgnoreForAllButNls(old);
}
}
@Override
protected String getTestDataPath() {

View File

@@ -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,