Improved type annotation support

PsiTypeElementImpl#addAnnotation implemented
AddAnnotationPsiFix, NullableNotNullManager adjusted
Fixes IDEA-232258 "Annotate overridden method parameters @NotNull" erroneously adds notNull to the param, not its type
Fixes IDEA-232565 Intention "Annotate overriding methods as NotNull" doesn't respect "type use"
Also, AddAnnotationPsiFix can be applied in batch now when annotations are not external.

GitOrigin-RevId: 0b652d3b032ed0d1c701beeda102c5e3c841762c
This commit is contained in:
Tagir Valeev
2020-02-12 12:32:19 +07:00
committed by intellij-monorepo-bot
parent ba85ab49e2
commit 2c486b2d0a
13 changed files with 456 additions and 79 deletions

View File

@@ -1,10 +1,7 @@
// Copyright 2000-2019 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.
package com.intellij.codeInsight.intention;
import com.intellij.codeInsight.AnnotationUtil;
import com.intellij.codeInsight.CodeInsightBundle;
import com.intellij.codeInsight.ExternalAnnotationsManager;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.*;
import com.intellij.codeInsight.daemon.impl.analysis.AnnotationsHighlightUtil;
import com.intellij.codeInsight.intention.impl.BaseIntentionAction;
import com.intellij.codeInspection.LocalQuickFixOnPsiElement;
@@ -34,6 +31,7 @@ public class AddAnnotationPsiFix extends LocalQuickFixOnPsiElement {
private final String[] myAnnotationsToRemove;
private final PsiNameValuePair[] myPairs; // not used when registering local quick fix
protected final String myText;
private final ExternalAnnotationsManager.AnnotationPlace myAnnotationPlace;
public AddAnnotationPsiFix(@NotNull String fqn,
@NotNull PsiModifierListOwner modifierListOwner,
@@ -46,6 +44,7 @@ public class AddAnnotationPsiFix extends LocalQuickFixOnPsiElement {
ObjectUtils.assertAllElementsNotNull(annotationsToRemove);
myAnnotationsToRemove = annotationsToRemove;
myText = calcText(modifierListOwner, myAnnotation);
myAnnotationPlace = choosePlace(modifierListOwner);
}
public static String calcText(PsiModifierListOwner modifierListOwner, @Nullable String annotation) {
@@ -131,7 +130,7 @@ public class AddAnnotationPsiFix extends LocalQuickFixOnPsiElement {
@Override
public boolean startInWriteAction() {
return false;
return myAnnotationPlace == ExternalAnnotationsManager.AnnotationPlace.IN_CODE;
}
@Override
@@ -141,48 +140,80 @@ public class AddAnnotationPsiFix extends LocalQuickFixOnPsiElement {
@NotNull PsiElement endElement) {
final PsiModifierListOwner myModifierListOwner = (PsiModifierListOwner)startElement;
PsiAnnotationOwner target = getTarget(project, myModifierListOwner);
if (target == null || target.hasAnnotation(myAnnotation)) return;
final ExternalAnnotationsManager annotationsManager = ExternalAnnotationsManager.getInstance(project);
final PsiModifierList modifierList = myModifierListOwner.getModifierList();
if (modifierList == null || modifierList.hasAnnotation(myAnnotation)) return;
PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(myAnnotation, myModifierListOwner.getResolveScope());
final ExternalAnnotationsManager.AnnotationPlace annotationAnnotationPlace;
if (aClass != null && BaseIntentionAction.canModify(myModifierListOwner) &&
(AnnotationsHighlightUtil.getRetentionPolicy(aClass) == RetentionPolicy.RUNTIME ||
!CommonClassNames.DEFAULT_PACKAGE.equals(StringUtil.getPackageName(myAnnotation)) &&
JavaPsiFacade.getInstance(project).getResolveHelper()//if class is already imported in current file
.resolveReferencedClass(StringUtil.getShortName(myAnnotation), myModifierListOwner) != null)) {
annotationAnnotationPlace = ExternalAnnotationsManager.AnnotationPlace.IN_CODE;
}
else {
annotationAnnotationPlace = annotationsManager.chooseAnnotationsPlace(myModifierListOwner);
}
if (annotationAnnotationPlace == ExternalAnnotationsManager.AnnotationPlace.NOWHERE) return;
if (annotationAnnotationPlace == ExternalAnnotationsManager.AnnotationPlace.EXTERNAL) {
for (String fqn : myAnnotationsToRemove) {
annotationsManager.deannotate(myModifierListOwner, fqn);
}
try {
annotationsManager.annotateExternally(myModifierListOwner, myAnnotation, file, myPairs);
}
catch (ExternalAnnotationsManager.CanceledConfigurationException ignored) {}
}
else {
final PsiFile containingFile = myModifierListOwner.getContainingFile();
WriteCommandAction.runWriteCommandAction(project, null, null, () -> {
removePhysicalAnnotations(myModifierListOwner, myAnnotationsToRemove);
ExternalAnnotationsManager.AnnotationPlace place = myAnnotationPlace == ExternalAnnotationsManager.AnnotationPlace.NEED_ASK_USER ?
annotationsManager.chooseAnnotationsPlace(myModifierListOwner) : myAnnotationPlace;
switch (place) {
case NOWHERE:
return;
case EXTERNAL:
for (String fqn : myAnnotationsToRemove) {
annotationsManager.deannotate(myModifierListOwner, fqn);
}
try {
annotationsManager.annotateExternally(myModifierListOwner, myAnnotation, file, myPairs);
}
catch (ExternalAnnotationsManager.CanceledConfigurationException ignored) {
}
break;
case IN_CODE:
final PsiFile containingFile = myModifierListOwner.getContainingFile();
WriteCommandAction.runWriteCommandAction(project, null, null, () -> {
removePhysicalAnnotations(myModifierListOwner, myAnnotationsToRemove);
PsiAnnotation inserted = addPhysicalAnnotation(myAnnotation, myPairs, modifierList);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(inserted);
}, containingFile);
PsiAnnotation inserted = addPhysicalAnnotation(myAnnotation, myPairs, target);
JavaCodeStyleManager.getInstance(project).shortenClassReferences(inserted);
}, containingFile);
if (containingFile != file) {
UndoUtil.markPsiFileForUndo(file);
}
if (containingFile != file) {
UndoUtil.markPsiFileForUndo(file);
}
break;
}
}
@NotNull
private ExternalAnnotationsManager.AnnotationPlace choosePlace(@NotNull PsiModifierListOwner modifierListOwner) {
Project project = modifierListOwner.getProject();
final ExternalAnnotationsManager annotationsManager = ExternalAnnotationsManager.getInstance(project);
PsiClass aClass = JavaPsiFacade.getInstance(project).findClass(myAnnotation, modifierListOwner.getResolveScope());
if (aClass != null && BaseIntentionAction.canModify(modifierListOwner) &&
(AnnotationsHighlightUtil.getRetentionPolicy(aClass) == RetentionPolicy.RUNTIME ||
!CommonClassNames.DEFAULT_PACKAGE.equals(StringUtil.getPackageName(myAnnotation)) &&
JavaPsiFacade.getInstance(project).getResolveHelper()//if class is already imported in current file
.resolveReferencedClass(StringUtil.getShortName(myAnnotation), modifierListOwner) != null)) {
return ExternalAnnotationsManager.AnnotationPlace.IN_CODE;
}
return annotationsManager.chooseAnnotationsPlaceNoUi(modifierListOwner);
}
private @Nullable PsiAnnotationOwner getTarget(@NotNull Project project, @NotNull PsiModifierListOwner modifierListOwner) {
PsiModifierList list = modifierListOwner.getModifierList();
if (list == null) return null;
PsiClass annotationClass = JavaPsiFacade.getInstance(project).findClass(myAnnotation, modifierListOwner.getResolveScope());
if (annotationClass != null &&
AnnotationTargetUtil.findAnnotationTarget(annotationClass, PsiAnnotation.TargetType.TYPE_USE) != null) {
PsiElement parent = list.getParent();
PsiTypeElement type = null;
if (parent instanceof PsiMethod) {
type = ((PsiMethod)parent).getReturnTypeElement();
}
else if (parent instanceof PsiVariable) {
type = ((PsiVariable)parent).getTypeElement();
}
if (type != null && !type.getType().equals(PsiType.VOID)) return type;
}
return list;
}
public static PsiAnnotation addPhysicalAnnotation(String fqn, PsiNameValuePair[] pairs, PsiModifierList modifierList) {
PsiAnnotation inserted = modifierList.addAnnotation(fqn);
return addPhysicalAnnotation(fqn, pairs, (PsiAnnotationOwner)modifierList);
}
public static PsiAnnotation addPhysicalAnnotation(String fqn, PsiNameValuePair[] pairs, PsiAnnotationOwner owner) {
PsiAnnotation inserted = owner.addAnnotation(fqn);
for (PsiNameValuePair pair : pairs) {
inserted.setDeclaredAttributeValue(pair.getName(), pair.getValue());
}

View File

@@ -76,6 +76,7 @@ import java.io.IOException;
import java.util.List;
import java.util.*;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
@@ -673,11 +674,25 @@ public final class ExternalAnnotationsManagerImpl extends ReadableExternalAnnota
}
}
@Override
@NotNull
public AnnotationPlace chooseAnnotationsPlaceNoUi(@NotNull final PsiElement element) {
return chooseAnnotationsPlace(element, () -> AnnotationPlace.NEED_ASK_USER);
}
@Override
@NotNull
public AnnotationPlace chooseAnnotationsPlace(@NotNull final PsiElement element) {
ApplicationManager.getApplication().assertIsDispatchThread();
if (!element.isPhysical() && !(element.getOriginalElement() instanceof PsiCompiledElement)) return AnnotationPlace.IN_CODE; //element just created
return chooseAnnotationsPlace(element, () -> confirmNewExternalAnnotationRoot(element));
}
@NotNull
private AnnotationPlace chooseAnnotationsPlace(@NotNull final PsiElement element,
@NotNull Supplier<AnnotationPlace> confirmNewExternalAnnotationRoot) {
if (!element.isPhysical() && !(element.getOriginalElement() instanceof PsiCompiledElement)) {
return AnnotationPlace.IN_CODE; //element just created
}
if (!element.getManager().isInProject(element)) return AnnotationPlace.EXTERNAL;
final Project project = myPsiManager.getProject();
@@ -699,48 +714,56 @@ public final class ExternalAnnotationsManagerImpl extends ReadableExternalAnnota
}
}
final MyExternalPromptDialog dialog = ApplicationManager.getApplication().isUnitTestMode() ||
ApplicationManager.getApplication().isHeadlessEnvironment() ? null : new MyExternalPromptDialog(project);
if (dialog != null && dialog.isToBeShown()) {
final PsiElement highlightElement = element instanceof PsiNameIdentifierOwner
? ((PsiNameIdentifierOwner)element).getNameIdentifier()
: element.getNavigationElement();
LOG.assertTrue(highlightElement != null);
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
final List<RangeHighlighter> highlighters = new ArrayList<>();
final boolean highlight =
editor != null && editor.getDocument() == PsiDocumentManager.getInstance(project).getDocument(containingFile);
try {
if (highlight) { //do not highlight for batch inspections
final EditorColorsManager colorsManager = EditorColorsManager.getInstance();
final TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
final TextRange textRange = highlightElement.getTextRange();
HighlightManager.getInstance(project).addRangeHighlight(editor,
textRange.getStartOffset(), textRange.getEndOffset(),
attributes, true, highlighters);
final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(textRange.getStartOffset());
editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.CENTER);
}
dialog.show();
if (dialog.getExitCode() == 2) {
return AnnotationPlace.EXTERNAL;
}
else if (dialog.getExitCode() == 1) {
return AnnotationPlace.NOWHERE;
}
return confirmNewExternalAnnotationRoot.get();
}
return AnnotationPlace.IN_CODE;
}
private static @NotNull AnnotationPlace confirmNewExternalAnnotationRoot(@NotNull PsiElement element) {
PsiFile containingFile = element.getContainingFile();
Project project = containingFile.getProject();
final MyExternalPromptDialog dialog = ApplicationManager.getApplication().isUnitTestMode() ||
ApplicationManager.getApplication().isHeadlessEnvironment()
? null
: new MyExternalPromptDialog(project);
if (dialog != null && dialog.isToBeShown()) {
final PsiElement highlightElement = element instanceof PsiNameIdentifierOwner
? ((PsiNameIdentifierOwner)element).getNameIdentifier()
: element.getNavigationElement();
LOG.assertTrue(highlightElement != null);
final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor();
final List<RangeHighlighter> highlighters = new ArrayList<>();
final boolean highlight =
editor != null && editor.getDocument() == PsiDocumentManager.getInstance(project).getDocument(containingFile);
try {
if (highlight) { //do not highlight for batch inspections
final EditorColorsManager colorsManager = EditorColorsManager.getInstance();
final TextAttributes attributes = colorsManager.getGlobalScheme().getAttributes(EditorColors.SEARCH_RESULT_ATTRIBUTES);
final TextRange textRange = highlightElement.getTextRange();
HighlightManager.getInstance(project).addRangeHighlight(editor,
textRange.getStartOffset(), textRange.getEndOffset(),
attributes, true, highlighters);
final LogicalPosition logicalPosition = editor.offsetToLogicalPosition(textRange.getStartOffset());
editor.getScrollingModel().scrollTo(logicalPosition, ScrollType.CENTER);
}
finally {
if (highlight) {
HighlightManager.getInstance(project).removeSegmentHighlighter(editor, highlighters.get(0));
}
dialog.show();
if (dialog.getExitCode() == 2) {
return AnnotationPlace.EXTERNAL;
}
else if (dialog.getExitCode() == 1) {
return AnnotationPlace.NOWHERE;
}
}
else if (dialog != null) {
dialog.close(DialogWrapper.OK_EXIT_CODE);
finally {
if (highlight) {
HighlightManager.getInstance(project).removeSegmentHighlighter(editor, highlighters.get(0));
}
}
}
else if (dialog != null) {
dialog.close(DialogWrapper.OK_EXIT_CODE);
}
return AnnotationPlace.IN_CODE;
}

View File

@@ -17,9 +17,28 @@ public abstract class ExternalAnnotationsManager {
public static final Topic<ExternalAnnotationsListener> TOPIC = Topic.create("external annotations", ExternalAnnotationsListener.class);
/**
* Describes where to place the new annotation
*/
public enum AnnotationPlace {
/**
* Annotation must be placed right in the code
*/
IN_CODE,
/**
* Annotation must be placed externally
*/
EXTERNAL,
/**
* User should be asked to decide whether they want to create new annotation root for external annotation.
* {@link ExternalAnnotationsManager#chooseAnnotationsPlace(PsiElement)} asks user automatically, so this result is never returned,
* but it requires EDT thread. On the other hand, {@link ExternalAnnotationsManager#chooseAnnotationsPlaceNoUi(PsiElement)}
* never display UI but may return this result.
*/
NEED_ASK_USER,
/**
* User actively cancelled the annotation addition, so it should not be added at all.
*/
NOWHERE
}
@@ -106,6 +125,18 @@ public abstract class ExternalAnnotationsManager {
@NotNull String annotationFQN,
PsiNameValuePair @Nullable [] value);
/**
* @param element element to add new annotation for
* @return place where the annotation must be added. No UI is displayed, so can be called inside any read-action.
* May return {@link AnnotationPlace#NEED_ASK_USER} if the user confirmation is necessary.
*/
@NotNull
public abstract AnnotationPlace chooseAnnotationsPlaceNoUi(@NotNull PsiElement element);
/**
* @param element element to add new annotation for
* @return place where the annotation must be added. Must be called in EDT.
*/
@NotNull
public abstract AnnotationPlace chooseAnnotationsPlace(@NotNull PsiElement element);

View File

@@ -315,6 +315,10 @@ public abstract class NullableNotNullManager {
? findAnnotationInHierarchy(owner, qualifiedNames)
: findAnnotation(owner, qualifiedNames);
PsiType type = getOwnerType(owner);
if (memberAnno != null && type instanceof PsiArrayType && AnnotationTargetUtil.isTypeAnnotation(memberAnno)) {
// Ambiguous TYPE_USE annotation on array type: we consider that it annotates an array component instead.
memberAnno = null;
}
if (memberAnno != null) {
PsiAnnotation annotation = preferTypeAnnotation(memberAnno, type);
if (annotation != memberAnno && !qualifiedNames.contains(annotation.getQualifiedName())) return null;

View File

@@ -399,6 +399,12 @@ public abstract class BaseExternalAnnotationsManager extends ExternalAnnotations
throw new UnsupportedOperationException();
}
@NotNull
@Override
public AnnotationPlace chooseAnnotationsPlaceNoUi(@NotNull PsiElement element) {
throw new UnsupportedOperationException();
}
@NotNull
@Override
public AnnotationPlace chooseAnnotationsPlace(@NotNull PsiElement element) {

View File

@@ -25,6 +25,7 @@ import com.intellij.psi.impl.PsiJavaParserFacadeImpl;
import com.intellij.psi.impl.source.tree.*;
import com.intellij.psi.scope.PsiScopeProcessor;
import com.intellij.psi.tree.IElementType;
import com.intellij.psi.tree.TokenSet;
import com.intellij.psi.util.*;
import com.intellij.util.IncorrectOperationException;
import com.intellij.util.SmartList;
@@ -321,7 +322,30 @@ public class PsiTypeElementImpl extends CompositePsiElement implements PsiTypeEl
@Override
@NotNull
public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
throw new UnsupportedOperationException();//todo
PsiAnnotation annotation = JavaPsiFacade.getElementFactory(getProject()).createAnnotationFromText('@' + qualifiedName, this);
PsiElement firstChild = getFirstChild();
for (PsiElement child = getLastChild(); child != firstChild; child = child.getPrevSibling()) {
if (PsiUtil.isJavaToken(child, JavaTokenType.LBRACKET) || PsiUtil.isJavaToken(child, JavaTokenType.ELLIPSIS)) {
return (PsiAnnotation)addBefore(annotation, child);
}
}
if (firstChild instanceof PsiJavaCodeReferenceElement) {
PsiIdentifier identifier = PsiTreeUtil.getChildOfType(firstChild, PsiIdentifier.class);
if (identifier != null) {
return (PsiAnnotation)firstChild.addBefore(annotation, identifier);
}
}
PsiElement parent = getParent();
if (parent instanceof PsiModifierListOwner) {
PsiModifierList modifierList = ((PsiModifierListOwner)parent).getModifierList();
if (modifierList != null) {
PsiTypeParameterList list = parent instanceof PsiTypeParameterListOwner ? ((PsiTypeParameterListOwner)parent).getTypeParameterList() : null;
if (list == null || list.textMatches("")) {
return (PsiAnnotation)modifierList.add(annotation);
}
}
}
return (PsiAnnotation)addBefore(annotation, firstChild);
}
@Override

View File

@@ -0,0 +1,36 @@
// "Fix all '@NotNull/@Nullable problems' problems in file" "true"
package typeUse;
import java.lang.annotation.*;
@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) public @interface NotNull { }
@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) public @interface Nullable { }
interface Foo {
void processString(@NotNull String s);
void processArray(String @NotNull [] arr);
void processArray2(String @NotNull [] arr);
void processString2(java.lang.@NotNull String qualified);
void processList(@NotNull List<@NotNull String> list);
void processList2(@NotNull List<String> finalList);
}
static class Bar implements Foo {
@Override
public void processString(@NotNull String s) { }
@Override
public void processArray(String @NotNull [] arr) { }
@Override
public void processArray2(@NotNull String @NotNull [] arr) { }
@Override
public void processString2(java.lang.@NotNull String qualified) { }
@Override
public void processList(@NotNull List<@NotNull String> list) { }
@Override
public void processList2(final @NotNull List<String> finalList) { }
}

View File

@@ -0,0 +1,42 @@
// "Fix all '@NotNull/@Nullable problems' problems in file" "true"
package typeUse;
import java.lang.annotation.*;
@Target(ElementType.TYPE_USE) public @interface NotNull { }
@Target(ElementType.TYPE_USE) public @interface Nullable { }
interface Foo {
@NotNull String getString();
String @NotNull [] getArray();
java.lang.@NotNull String getString2();
@NotNull List<@NotNull String> getList();
<T> @NotNull List<String> getList2();
}
class Bar implements Foo {
@Override
public @NotNull String getString() {
return "";
}
@Override
public String @NotNull [] getArray() {
return new String[0];
}
@Override
public java.lang.@NotNull String getString2() {
return "";
}
@Override
public @NotNull List<String> getList() {
return Collections.emptyList();
}
@Override
public <T> @NotNull List<String> getList2() {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,40 @@
// "Fix all '@NotNull/@Nullable problems' problems in file" "true"
package typeUse;
import java.lang.annotation.*;
@Target(ElementType.TYPE_USE) public @interface NotNull { }
@Target(ElementType.TYPE_USE) public @interface Nullable { }
interface Foo {
void processString(@NotNull String s);
void processArray(String @NotNull [] arr);
void processArray2(String @NotNull [] arr);
void processArray3(String @NotNull ... arr);
void processString2(java.lang.@NotNull String qualified);
void processList(@NotNull List<@NotNull String> list);
void processList2(@NotNull List<String> finalList);
}
static class Bar implements Foo {
@Override
public void processString(@NotNull String s) { }
@Override
public void processArray(String @NotNull [] arr) { }
@Override
public void processArray2(@NotNull String @NotNull [] arr) { }
@Override
public void processArray3(String @NotNull ... arr) { }
@Override
public void processString2(java.lang.@NotNull String qualified) { }
@Override
public void processList(@NotNull List<@NotNull String> list) { }
@Override
public void processList2(final @NotNull List<String> finalList) { }
}

View File

@@ -0,0 +1,36 @@
// "Fix all '@NotNull/@Nullable problems' problems in file" "true"
package typeUse;
import java.lang.annotation.*;
@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) public @interface NotNull { }
@Target({ElementType.TYPE_USE, ElementType.PARAMETER}) public @interface Nullable { }
interface Foo {
void processString(@NotNull String s);
void processArray(String @NotNull [] arr);
void processArray2(String @NotNull [] arr);
void processString2(java.lang.@NotNull String qualified);
void processList(@NotNull List<@NotNull String> list);
void processList2(@NotNull List<String> finalList);
}
static class Bar implements Foo {
@Override
public void processString(String <caret>s) { }
@Override
public void processArray(String[] arr) { }
@Override
public void processArray2(@NotNull String[] arr) { }
@Override
public void processString2(java.lang.String qualified) { }
@Override
public void processList(List<@NotNull String> list) { }
@Override
public void processList2(final List<String> finalList) { }
}

View File

@@ -0,0 +1,42 @@
// "Fix all '@NotNull/@Nullable problems' problems in file" "true"
package typeUse;
import java.lang.annotation.*;
@Target(ElementType.TYPE_USE) public @interface NotNull { }
@Target(ElementType.TYPE_USE) public @interface Nullable { }
interface Foo {
@NotNull String getString();
String @NotNull [] getArray();
java.lang.@NotNull String getString2();
@NotNull List<@NotNull String> getList();
<T> @NotNull List<String> getList2();
}
class Bar implements Foo {
@Override
public String g<caret>etString() {
return "";
}
@Override
public String[] getArray() {
return new String[0];
}
@Override
public java.lang.String getString2() {
return "";
}
@Override
public List<String> getList() {
return Collections.emptyList();
}
@Override
public <T> List<String> getList2() {
return Collections.emptyList();
}
}

View File

@@ -0,0 +1,40 @@
// "Fix all '@NotNull/@Nullable problems' problems in file" "true"
package typeUse;
import java.lang.annotation.*;
@Target(ElementType.TYPE_USE) public @interface NotNull { }
@Target(ElementType.TYPE_USE) public @interface Nullable { }
interface Foo {
void processString(@NotNull String s);
void processArray(String @NotNull [] arr);
void processArray2(String @NotNull [] arr);
void processArray3(String @NotNull ... arr);
void processString2(java.lang.@NotNull String qualified);
void processList(@NotNull List<@NotNull String> list);
void processList2(@NotNull List<String> finalList);
}
static class Bar implements Foo {
@Override
public void processString(String <caret>s) { }
@Override
public void processArray(String[] arr) { }
@Override
public void processArray2(@NotNull String[] arr) { }
@Override
public void processArray3(String... arr) { }
@Override
public void processString2(java.lang.String qualified) { }
@Override
public void processList(List<@NotNull String> list) { }
@Override
public void processList2(final List<String> finalList) { }
}

View File

@@ -15,9 +15,11 @@
*/
package com.intellij.java.codeInsight.daemon.quickFix;
import com.intellij.codeInsight.NullableNotNullManager;
import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase;
import com.intellij.codeInspection.LocalInspectionTool;
import com.intellij.codeInspection.nullable.NullableStuffInspection;
import com.intellij.openapi.util.Disposer;
import com.intellij.pom.java.LanguageLevel;
import org.jetbrains.annotations.NotNull;
@@ -27,6 +29,26 @@ public class AnnotateMethodTest extends LightQuickFixParameterizedTestCase {
return "/codeInsight/daemonCodeAnalyzer/quickFix/annotateMethod";
}
@Override
protected void setUp() throws Exception {
super.setUp();
if (getTestName(false).contains("TypeUse")) {
NullableNotNullManager nnnManager = NullableNotNullManager.getInstance(getProject());
String prevNullable = nnnManager.getDefaultNullable();
String prevNotNull = nnnManager.getDefaultNotNull();
nnnManager.setNotNulls("typeUse.NotNull");
nnnManager.setNullables("typeUse.Nullable");
nnnManager.setDefaultNotNull("typeUse.NotNull");
nnnManager.setDefaultNullable("typeUse.Nullable");
Disposer.register(getTestRootDisposable(), () -> {
nnnManager.setNotNulls();
nnnManager.setNullables();
nnnManager.setDefaultNotNull(prevNotNull);
nnnManager.setDefaultNullable(prevNullable);
});
}
}
@Override
protected LocalInspectionTool @NotNull [] configureLocalInspectionTools() {
return new LocalInspectionTool[]{new NullableStuffInspection()};
@@ -34,6 +56,6 @@ public class AnnotateMethodTest extends LightQuickFixParameterizedTestCase {
@Override
protected LanguageLevel getLanguageLevel() {
return LanguageLevel.JDK_1_5;
return LanguageLevel.JDK_1_8;
}
}