mirror of
https://gitflic.ru/project/openide/openide.git
synced 2025-12-15 11:53:49 +07:00
[java-generation] IDEA-344399 generate annotation based on type_use option
- extracted new option - reuse this option in MissortedModifiersInspection - override uses this option too GitOrigin-RevId: 39f3f72991240753c86c7f80df865728aa9743ad
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f57df70730
commit
2023228d8c
@@ -1298,7 +1298,16 @@ extends.object.remove.quickfix=Remove redundant 'extends Object'
|
||||
implicit.call.to.super.ignore.option=Ignore for direct subclasses of 'java.lang.Object'
|
||||
implicit.call.to.super.make.explicit.quickfix=Make call to 'super()' explicit
|
||||
missorted.modifiers.require.option=Check annotation order
|
||||
missorted.modifiers.typeuse.before.type.option=Target TYPE_USE annotations always go before type
|
||||
missorted.modifiers.require.option.description=Use this option to report of misplaced annotations: \
|
||||
(annotations with <code>ElementType.TYPE_USE</code> <em>not</em> directly \
|
||||
before the type and after the modifier keywords, or \
|
||||
other annotations <em>not</em> before the modifier keywords). \
|
||||
When this option is disabled, any annotation can be positioned before or after the modifier keywords. \
|
||||
Modifier lists with annotations in between the modifier keywords will always be reported.
|
||||
missorted.modifiers.allowed.place=Target TYPE_USE annotations must be placed according to generation options
|
||||
missorted.modifiers.allowed.place.description=Target TYPE_USE annotations must be placed according to \
|
||||
<code>Settings->Editor->Code Style->Java->Code Generation->Generate annotations allowed for TYPE_USE directly before a type</code> option, \
|
||||
otherwise it can be placed in any allowed place
|
||||
missorted.modifiers.sort.quickfix=Sort modifiers
|
||||
nested.method.call.ignore.option=Ignore nested method calls in field initializers
|
||||
ignore.calls.to.static.methods=Ignore calls to static methods
|
||||
|
||||
@@ -0,0 +1,168 @@
|
||||
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.refactoring;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationTargetUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* The ModifierListUtil class provides utility methods for working with PsiModifierList objects.
|
||||
*/
|
||||
public final class ModifierListUtil {
|
||||
private ModifierListUtil() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param modifierList the original modifier list to create a sorted modifier list from
|
||||
* @param customAnnotationComparator the custom comparator to use for sorting annotations (nullable)
|
||||
* @param allowAnnotationTypeBeInAnyAllowedPlace if true annotation type can be in any allowed place
|
||||
* @return the sorted modifier (based on {@link ModifierListUtil#isTypeAnnotationAlwaysUseWithType(PsiElement)} and
|
||||
* {@link ModifierComparator}) list as a PsiModifierList object
|
||||
*/
|
||||
public static @Nullable PsiModifierList createSortedModifierList(@NotNull PsiModifierList modifierList,
|
||||
@Nullable Comparator<PsiElement> customAnnotationComparator,
|
||||
boolean allowAnnotationTypeBeInAnyAllowedPlace) {
|
||||
@NonNls final String text = String.join(" ", getSortedModifiers(modifierList, customAnnotationComparator, allowAnnotationTypeBeInAnyAllowedPlace));
|
||||
return createNewModifierList(modifierList, text);
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
private static PsiModifierList createNewModifierList(@NotNull PsiModifierList oldModifierList, @NotNull String newModifiersText) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(oldModifierList.getProject());
|
||||
PsiElement parent = oldModifierList.getParent();
|
||||
if (parent instanceof PsiRequiresStatement) {
|
||||
String text = ("requires " + newModifiersText + " x;").trim();
|
||||
PsiRequiresStatement statement = (PsiRequiresStatement)factory.createModuleStatementFromText(text, oldModifierList);
|
||||
return statement.getModifierList();
|
||||
}
|
||||
else if (parent instanceof PsiClass) {
|
||||
String text = (newModifiersText + " class X {}").trim();
|
||||
PsiDeclarationStatement declarationStatement =
|
||||
(PsiDeclarationStatement)factory.createStatementFromText(text, oldModifierList);
|
||||
return ((PsiClass)declarationStatement.getDeclaredElements()[0]).getModifierList();
|
||||
}
|
||||
else {
|
||||
String text = (newModifiersText + " void x() {}").trim();
|
||||
PsiMethod method = factory.createMethodFromText(text, oldModifierList);
|
||||
return method.getModifierList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param element the PsiElement to check
|
||||
* @return true if the element is a method with void return type, false otherwise
|
||||
*/
|
||||
public static boolean isMethodWithVoidReturnType(@NotNull PsiElement element) {
|
||||
return element instanceof PsiMethod && PsiTypes.voidType().equals(((PsiMethod)element).getReturnType());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param modifierList the original modifier list to create a sorted modifier list from
|
||||
* @param customAnnotationComparator the custom comparator to use for sorting annotations (nullable)
|
||||
* @param allowAnnotationTypeBeInAnyAllowedPlace if true annotation type can be in any allowed place
|
||||
* @return the sorted list of modifiers, represented as a list of strings
|
||||
* @see ModifierListUtil#createSortedModifierList(PsiModifierList, Comparator, boolean)
|
||||
*/
|
||||
public static List<String> getSortedModifiers(@NotNull PsiModifierList modifierList,
|
||||
@Nullable Comparator<PsiElement> customAnnotationComparator,
|
||||
boolean allowAnnotationTypeBeInAnyAllowedPlace) {
|
||||
final List<String> modifiers = new SmartList<>();
|
||||
final List<PsiAnnotation> typeAnnotations = new SmartList<>();
|
||||
final List<PsiAnnotation> annotations = new SmartList<>();
|
||||
for (PsiElement child : modifierList.getChildren()) {
|
||||
if (child instanceof PsiJavaToken) {
|
||||
modifiers.add(child.getText());
|
||||
}
|
||||
else if (child instanceof PsiAnnotation annotation) {
|
||||
if (PsiImplUtil.isTypeAnnotation(child) && !isMethodWithVoidReturnType(modifierList.getParent())) {
|
||||
final PsiAnnotation.TargetType[] targets = AnnotationTargetUtil.getTargetsForLocation(annotation.getOwner());
|
||||
PsiClass annotationClass = annotation.resolveAnnotationType();
|
||||
Set<PsiAnnotation.TargetType> annotationAllowedTypes = EnumSet.noneOf(PsiAnnotation.TargetType.class);
|
||||
if (annotationClass != null) {
|
||||
Set<PsiAnnotation.TargetType> annotationTargets = AnnotationTargetUtil.getAnnotationTargets(annotationClass);
|
||||
if (annotationTargets != null) {
|
||||
annotationAllowedTypes.addAll(annotationTargets);
|
||||
}
|
||||
}
|
||||
annotationAllowedTypes.removeAll(Arrays.stream(targets).collect(Collectors.toSet()));
|
||||
if (isTypeAnnotationAlwaysUseWithType(annotation) ||
|
||||
(allowAnnotationTypeBeInAnyAllowedPlace && !modifiers.isEmpty()) ||
|
||||
AnnotationTargetUtil.findAnnotationTarget(annotation, targets[0]) == PsiAnnotation.TargetType.UNKNOWN ||
|
||||
(annotationAllowedTypes.size() == 1 && annotationAllowedTypes.contains(PsiAnnotation.TargetType.TYPE_USE))) {
|
||||
typeAnnotations.add(annotation);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
annotations.add(annotation);
|
||||
}
|
||||
}
|
||||
modifiers.sort(new ModifierComparator());
|
||||
final List<String> result = new SmartList<>();
|
||||
if (customAnnotationComparator != null) {
|
||||
annotations.sort(customAnnotationComparator);
|
||||
typeAnnotations.sort(customAnnotationComparator);
|
||||
}
|
||||
result.addAll(ContainerUtil.map(annotations, a -> a.getText()));
|
||||
result.addAll(modifiers);
|
||||
result.addAll(ContainerUtil.map(typeAnnotations, a -> a.getText()));
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param psiElement the PsiElement to check
|
||||
* @return true if the type annotation should always be used direcly before the type, false otherwise
|
||||
*/
|
||||
public static boolean isTypeAnnotationAlwaysUseWithType(@NotNull PsiElement psiElement) {
|
||||
return JavaCodeStyleSettings.getInstance(psiElement.getContainingFile()).GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE;
|
||||
}
|
||||
|
||||
/**
|
||||
* A comparator for sorting keyword modifiers.
|
||||
*/
|
||||
public static class ModifierComparator implements Comparator<String> {
|
||||
|
||||
@NonNls private static final String[] s_modifierOrder =
|
||||
{
|
||||
PsiModifier.PUBLIC,
|
||||
PsiModifier.PROTECTED,
|
||||
PsiModifier.PRIVATE,
|
||||
PsiModifier.ABSTRACT,
|
||||
PsiModifier.DEFAULT,
|
||||
PsiModifier.STATIC,
|
||||
PsiModifier.FINAL,
|
||||
PsiModifier.TRANSIENT,
|
||||
PsiModifier.VOLATILE,
|
||||
PsiModifier.SYNCHRONIZED,
|
||||
PsiModifier.NATIVE,
|
||||
PsiModifier.STRICTFP,
|
||||
PsiModifier.TRANSITIVE,
|
||||
PsiModifier.SEALED,
|
||||
PsiModifier.NON_SEALED
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(String modifier1, String modifier2) {
|
||||
if (modifier1.equals(modifier2)) return 0;
|
||||
for (String modifier : s_modifierOrder) {
|
||||
if (modifier.equals(modifier1)) {
|
||||
return -1;
|
||||
}
|
||||
else if (modifier.equals(modifier2)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return modifier1.compareTo(modifier2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,12 +18,12 @@ package com.siyeh.ig.style;
|
||||
import com.intellij.codeInsight.AnnotationTargetUtil;
|
||||
import com.intellij.codeInspection.CleanupLocalInspectionTool;
|
||||
import com.intellij.codeInspection.LocalQuickFix;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.codeInspection.options.OptPane;
|
||||
import com.intellij.modcommand.ModPsiUpdater;
|
||||
import com.intellij.modcommand.PsiUpdateModCommandQuickFix;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.PsiImplUtil;
|
||||
import com.intellij.refactoring.ModifierListUtil;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.BaseInspection;
|
||||
@@ -31,9 +31,7 @@ import com.siyeh.ig.BaseInspectionVisitor;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Comparator;
|
||||
@@ -59,7 +57,7 @@ public final class MissortedModifiersInspection extends BaseInspection implement
|
||||
protected String buildErrorString(Object... infos) {
|
||||
final PsiModifierList modifierList = (PsiModifierList)infos[0];
|
||||
final List<String> modifiers = getModifiers(modifierList);
|
||||
final List<String> sortedModifiers = getSortedModifiers(modifierList);
|
||||
final List<String> sortedModifiers = ModifierListUtil.getSortedModifiers(modifierList, null, !typeUseWithType);
|
||||
final List<String> missortedModifiers = stripCommonPrefixSuffix(modifiers, sortedModifiers);
|
||||
return InspectionGadgetsBundle.message("missorted.modifiers.problem.descriptor", String.join(" ", missortedModifiers));
|
||||
}
|
||||
@@ -108,8 +106,11 @@ public final class MissortedModifiersInspection extends BaseInspection implement
|
||||
@Override
|
||||
public @NotNull OptPane getOptionsPane() {
|
||||
return pane(
|
||||
checkbox("m_requireAnnotationsFirst", InspectionGadgetsBundle.message("missorted.modifiers.require.option"),
|
||||
checkbox("typeUseWithType", InspectionGadgetsBundle.message("missorted.modifiers.typeuse.before.type.option"))));
|
||||
checkbox("m_requireAnnotationsFirst", InspectionGadgetsBundle.message("missorted.modifiers.require.option"))
|
||||
.description(InspectionGadgetsBundle.message("missorted.modifiers.require.option.description")),
|
||||
checkbox("typeUseWithType", InspectionGadgetsBundle.message("missorted.modifiers.allowed.place"))
|
||||
.description(InspectionGadgetsBundle.message("missorted.modifiers.allowed.place.description"))
|
||||
);
|
||||
}
|
||||
|
||||
private class SortModifiersFix extends PsiUpdateModCommandQuickFix {
|
||||
@@ -127,72 +128,23 @@ public final class MissortedModifiersInspection extends BaseInspection implement
|
||||
if (!(element instanceof PsiModifierList)) return;
|
||||
}
|
||||
final PsiModifierList modifierList = (PsiModifierList)element;
|
||||
@NonNls final String text = String.join(" ", getSortedModifiers(modifierList));
|
||||
PsiModifierList newModifierList = createNewModifierList(modifierList, text);
|
||||
PsiModifierList newModifierList = ModifierListUtil.createSortedModifierList(modifierList, null, !typeUseWithType);
|
||||
if (newModifierList != null) {
|
||||
new CommentTracker().replaceAndRestoreComments(modifierList, newModifierList);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static PsiModifierList createNewModifierList(@NotNull PsiModifierList oldModifierList, @NotNull String newModifiersText) {
|
||||
final PsiElementFactory factory = JavaPsiFacade.getElementFactory(oldModifierList.getProject());
|
||||
PsiElement parent = oldModifierList.getParent();
|
||||
if (parent instanceof PsiRequiresStatement) {
|
||||
String text = "requires " + newModifiersText + " x;";
|
||||
PsiRequiresStatement statement = (PsiRequiresStatement) factory.createModuleStatementFromText(text, oldModifierList);
|
||||
return statement.getModifierList();
|
||||
}
|
||||
else if (parent instanceof PsiClass) {
|
||||
PsiDeclarationStatement declarationStatement =
|
||||
(PsiDeclarationStatement)factory.createStatementFromText(newModifiersText + " class X {}", oldModifierList);
|
||||
return ((PsiClass)declarationStatement.getDeclaredElements()[0]).getModifierList();
|
||||
}
|
||||
else {
|
||||
PsiMethod method = factory.createMethodFromText(newModifiersText + " void x() {}", oldModifierList);
|
||||
return method.getModifierList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> getModifiers(PsiModifierList modifierList) {
|
||||
private static List<String> getModifiers(@NotNull PsiModifierList modifierList) {
|
||||
return Stream.of(modifierList.getChildren())
|
||||
.filter(e -> e instanceof PsiJavaToken || e instanceof PsiAnnotation)
|
||||
.map(PsiElement::getText)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private List<String> getSortedModifiers(PsiModifierList modifierList) {
|
||||
final List<String> modifiers = new SmartList<>();
|
||||
final List<String> typeAnnotations = new SmartList<>();
|
||||
final List<String> annotations = new SmartList<>();
|
||||
for (PsiElement child : modifierList.getChildren()) {
|
||||
if (child instanceof PsiJavaToken) {
|
||||
modifiers.add(child.getText());
|
||||
}
|
||||
else if (child instanceof PsiAnnotation annotation) {
|
||||
if (PsiImplUtil.isTypeAnnotation(child) && !isMethodWithVoidReturnType(modifierList.getParent())) {
|
||||
final PsiAnnotation.TargetType[] targets = AnnotationTargetUtil.getTargetsForLocation(annotation.getOwner());
|
||||
if (typeUseWithType || !modifiers.isEmpty() ||
|
||||
AnnotationTargetUtil.findAnnotationTarget(annotation, targets[0]) == PsiAnnotation.TargetType.UNKNOWN) {
|
||||
typeAnnotations.add(child.getText());
|
||||
continue;
|
||||
}
|
||||
}
|
||||
annotations.add(child.getText());
|
||||
}
|
||||
}
|
||||
modifiers.sort(new ModifierComparator());
|
||||
final List<String> result = new SmartList<>();
|
||||
result.addAll(annotations);
|
||||
result.addAll(modifiers);
|
||||
result.addAll(typeAnnotations);
|
||||
return result;
|
||||
}
|
||||
|
||||
private class MissortedModifiersVisitor extends BaseInspectionVisitor {
|
||||
|
||||
private final Comparator<String> modifierComparator = new ModifierComparator();
|
||||
private final Comparator<String> modifierComparator = new ModifierListUtil.ModifierComparator();
|
||||
|
||||
@Override
|
||||
public void visitClass(@NotNull PsiClass aClass) {
|
||||
@@ -271,10 +223,10 @@ public final class MissortedModifiersInspection extends BaseInspection implement
|
||||
}
|
||||
if (child instanceof PsiAnnotation annotation) {
|
||||
if (m_requireAnnotationsFirst) {
|
||||
if (AnnotationTargetUtil.isTypeAnnotation(annotation) && !isMethodWithVoidReturnType(modifierList.getParent())) {
|
||||
if (AnnotationTargetUtil.isTypeAnnotation(annotation) && !ModifierListUtil.isMethodWithVoidReturnType(modifierList.getParent())) {
|
||||
// type annotations go next to the type
|
||||
// see e.g. https://www.oracle.com/technical-resources/articles/java/ma14-architect-annotations.html
|
||||
if (typeUseWithType || !modifiers.isEmpty()) {
|
||||
if (ModifierListUtil.isTypeAnnotationAlwaysUseWithType(annotation) || !modifiers.isEmpty()) {
|
||||
typeAnnotation = annotation;
|
||||
}
|
||||
final PsiAnnotation.TargetType[] targets = AnnotationTargetUtil.getTargetsForLocation(annotation.getOwner());
|
||||
@@ -296,45 +248,4 @@ public final class MissortedModifiersInspection extends BaseInspection implement
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static boolean isMethodWithVoidReturnType(PsiElement element) {
|
||||
return element instanceof PsiMethod && PsiTypes.voidType().equals(((PsiMethod)element).getReturnType());
|
||||
}
|
||||
|
||||
private static class ModifierComparator implements Comparator<String> {
|
||||
|
||||
@NonNls private static final String[] s_modifierOrder =
|
||||
{
|
||||
PsiModifier.PUBLIC,
|
||||
PsiModifier.PROTECTED,
|
||||
PsiModifier.PRIVATE,
|
||||
PsiModifier.ABSTRACT,
|
||||
PsiModifier.DEFAULT,
|
||||
PsiModifier.STATIC,
|
||||
PsiModifier.FINAL,
|
||||
PsiModifier.TRANSIENT,
|
||||
PsiModifier.VOLATILE,
|
||||
PsiModifier.SYNCHRONIZED,
|
||||
PsiModifier.NATIVE,
|
||||
PsiModifier.STRICTFP,
|
||||
PsiModifier.TRANSITIVE,
|
||||
PsiModifier.SEALED,
|
||||
PsiModifier.NON_SEALED
|
||||
};
|
||||
|
||||
|
||||
@Override
|
||||
public int compare(String modifier1, String modifier2) {
|
||||
if (modifier1.equals(modifier2)) return 0;
|
||||
for (String modifier : s_modifierOrder) {
|
||||
if (modifier.equals(modifier1)) {
|
||||
return -1;
|
||||
}
|
||||
else if (modifier.equals(modifier2)) {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return modifier1.compareTo(modifier2);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -52,6 +52,7 @@ public class JavaCodeStyleSettings extends CustomCodeStyleSettings implements Im
|
||||
public String VISIBILITY = "public";
|
||||
|
||||
public boolean USE_EXTERNAL_ANNOTATIONS;
|
||||
public boolean GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = true;
|
||||
public boolean INSERT_OVERRIDE_ANNOTATION = true;
|
||||
|
||||
public boolean REPEAT_SYNCHRONIZED = true;
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.intellij.refactoring.extractMethod.newImpl
|
||||
import com.intellij.codeInsight.AnnotationUtil
|
||||
import com.intellij.codeInsight.Nullability
|
||||
import com.intellij.codeInsight.NullableNotNullManager
|
||||
import com.intellij.codeInsight.generation.GenerateMembersUtil
|
||||
import com.intellij.java.refactoring.JavaRefactoringBundle
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.psi.*
|
||||
@@ -212,6 +213,7 @@ internal fun updateMethodAnnotations(method: PsiMethod, inputParameters: List<In
|
||||
val returnedExpressions = PsiUtil.findReturnStatements(method).mapNotNull(PsiReturnStatement::getReturnValue)
|
||||
val resultNullability = CodeFragmentAnalyzer.inferNullability(returnedExpressions)
|
||||
ExtractMethodHelper.addNullabilityAnnotation(method.returnTypeElement, resultNullability)
|
||||
GenerateMembersUtil.sortModifiers(method, null)
|
||||
}
|
||||
val parameters = method.parameterList.parameters
|
||||
inputParameters
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<properties/>
|
||||
<border type="empty"/>
|
||||
<children>
|
||||
<grid id="452da" layout-manager="GridLayoutManager" row-count="5" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<grid id="452da" layout-manager="GridLayoutManager" row-count="6" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
<margin top="0" left="10" bottom="15" right="10"/>
|
||||
<constraints/>
|
||||
<properties/>
|
||||
@@ -218,7 +218,7 @@
|
||||
</grid>
|
||||
<vspacer id="91133">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
<grid row="5" column="0" row-span="1" col-span="1" vsize-policy="6" hsize-policy="1" anchor="0" fill="2" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
</vspacer>
|
||||
<grid id="83565" layout-manager="GridLayoutManager" row-count="4" column-count="1" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
|
||||
@@ -373,6 +373,15 @@
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
<component id="c89b" class="com.intellij.ui.components.JBCheckBox" binding="myGenerateTypeAnnotationBeforeType">
|
||||
<constraints>
|
||||
<grid row="4" column="0" row-span="1" col-span="1" vsize-policy="0" hsize-policy="3" anchor="8" fill="0" indent="0" use-parent-layout="false"/>
|
||||
</constraints>
|
||||
<properties>
|
||||
<text resource-bundle="messages/JavaBundle" key="generate.type.use.before.type"/>
|
||||
<toolTipText resource-bundle="messages/JavaBundle" key="generate.type.use.before.type.description"/>
|
||||
</properties>
|
||||
</component>
|
||||
</children>
|
||||
</grid>
|
||||
</children>
|
||||
|
||||
@@ -66,6 +66,7 @@ public class CodeStyleGenerationConfigurable implements CodeStyleConfigurable {
|
||||
private JCheckBox myCbGenerateFinalParameters;
|
||||
private JCheckBox myCbGenerateFinalLocals;
|
||||
private JCheckBox myCbUseExternalAnnotations;
|
||||
private JCheckBox myGenerateTypeAnnotationBeforeType;
|
||||
private JCheckBox myInsertOverrideAnnotationCheckBox;
|
||||
private JCheckBox myRepeatSynchronizedCheckBox;
|
||||
private JPanel myVisibilityPanel;
|
||||
@@ -137,6 +138,7 @@ public class CodeStyleGenerationConfigurable implements CodeStyleConfigurable {
|
||||
myCbDeclareVarType.setSelected(JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_VAR_TYPE);
|
||||
|
||||
myCbUseExternalAnnotations.setSelected(javaSettings.USE_EXTERNAL_ANNOTATIONS);
|
||||
myGenerateTypeAnnotationBeforeType.setSelected(javaSettings.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE);
|
||||
myInsertOverrideAnnotationCheckBox.setSelected(javaSettings.INSERT_OVERRIDE_ANNOTATION);
|
||||
myRepeatSynchronizedCheckBox.setSelected(javaSettings.REPEAT_SYNCHRONIZED);
|
||||
myJavaVisibilityPanel.setVisibility(javaSettings.VISIBILITY);
|
||||
@@ -179,6 +181,7 @@ public class CodeStyleGenerationConfigurable implements CodeStyleConfigurable {
|
||||
JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_VAR_TYPE = myCbDeclareVarType.isSelected();
|
||||
|
||||
javaSettings.USE_EXTERNAL_ANNOTATIONS = myCbUseExternalAnnotations.isSelected();
|
||||
javaSettings.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = myGenerateTypeAnnotationBeforeType.isSelected();
|
||||
javaSettings.INSERT_OVERRIDE_ANNOTATION = myInsertOverrideAnnotationCheckBox.isSelected();
|
||||
javaSettings.REPEAT_SYNCHRONIZED = myRepeatSynchronizedCheckBox.isSelected();
|
||||
|
||||
@@ -237,6 +240,7 @@ public class CodeStyleGenerationConfigurable implements CodeStyleConfigurable {
|
||||
isModified |= isCheckboxModified(myCbDeclareVarType, JavaRefactoringSettings.getInstance().INTRODUCE_LOCAL_CREATE_VAR_TYPE);
|
||||
|
||||
isModified |= isCheckboxModified(myCbUseExternalAnnotations, javaSettings.USE_EXTERNAL_ANNOTATIONS);
|
||||
isModified |= isCheckboxModified(myGenerateTypeAnnotationBeforeType, javaSettings.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE);
|
||||
isModified |= isCheckboxModified(myInsertOverrideAnnotationCheckBox, javaSettings.INSERT_OVERRIDE_ANNOTATION);
|
||||
isModified |= isCheckboxModified(myRepeatSynchronizedCheckBox, javaSettings.REPEAT_SYNCHRONIZED);
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.codeInsight.daemon.impl.quickfix.CreateFromUsageUtils;
|
||||
import com.intellij.codeInsight.intention.AddAnnotationPsiFix;
|
||||
import com.intellij.lang.ASTNode;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.modcommand.ModPsiNavigator;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -21,15 +22,18 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.codeStyle.*;
|
||||
import com.intellij.psi.impl.light.LightElement;
|
||||
import com.intellij.psi.impl.light.LightMethod;
|
||||
import com.intellij.psi.impl.light.LightTypeElement;
|
||||
import com.intellij.psi.impl.source.codeStyle.JavaCodeStyleManagerImpl;
|
||||
import com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl;
|
||||
import com.intellij.psi.javadoc.PsiDocComment;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.refactoring.ModifierListUtil;
|
||||
import com.intellij.util.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.UniqueNameGenerator;
|
||||
import com.siyeh.ig.psiutils.CommentTracker;
|
||||
import org.jetbrains.annotations.Contract;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -339,6 +343,7 @@ public final class GenerateMembersUtil {
|
||||
}
|
||||
}
|
||||
substituteThrows(factory, resultMethod.getThrowsList(), collisionResolvedSubstitutor, sourceMethod, thrownTypes);
|
||||
sortModifiers(resultMethod, sourceMethod);
|
||||
return resultMethod;
|
||||
}
|
||||
catch (IncorrectOperationException e) {
|
||||
@@ -347,6 +352,51 @@ public final class GenerateMembersUtil {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sorts the modifiers of the source method based on the order of the modifiers in the target method.
|
||||
*
|
||||
* @param targetMethod The target method whose modifier order will be used.
|
||||
* @param sourceMethod The source method whose modifiers will be sorted.
|
||||
*/
|
||||
public static void sortModifiers(@NotNull PsiMethod targetMethod, @Nullable PsiMethod sourceMethod) {
|
||||
if (targetMethod instanceof LightMethod ||
|
||||
!targetMethod.isWritable() ||
|
||||
targetMethod.getLanguage() != JavaLanguage.INSTANCE) {
|
||||
return;
|
||||
}
|
||||
PsiModifierList newList = targetMethod.getModifierList();
|
||||
Map<@NotNull String, @NotNull PsiAnnotation> oldAnnotations = new HashMap<>();
|
||||
if (sourceMethod != null) {
|
||||
PsiModifierList oldList = sourceMethod.getModifierList();
|
||||
for (@NotNull PsiElement child : oldList.getChildren()) {
|
||||
if (child instanceof PsiAnnotation psiAnnotation) {
|
||||
String annotationQualifiedName = psiAnnotation.getQualifiedName();
|
||||
if (annotationQualifiedName != null) {
|
||||
oldAnnotations.put(annotationQualifiedName, psiAnnotation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Comparator<PsiElement> comparator = (o1, o2) -> {
|
||||
if (!(o1 instanceof PsiAnnotation a1) || !(o2 instanceof PsiAnnotation a2)) {
|
||||
return 0;
|
||||
}
|
||||
String q1 = a1.getQualifiedName();
|
||||
String q2 = a2.getQualifiedName();
|
||||
if (q1 == null || q2 == null) return 0;
|
||||
PsiAnnotation oldA1 = oldAnnotations.get(q1);
|
||||
PsiAnnotation oldA2 = oldAnnotations.get(q2);
|
||||
if (oldA1 != null && oldA2 != null) {
|
||||
return oldA1.getTextRange().getStartOffset() - oldA2.getTextRange().getStartOffset();
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
PsiModifierList newList2 = ModifierListUtil.createSortedModifierList(newList, comparator, false);
|
||||
if (newList2 != null) {
|
||||
new CommentTracker().replace(newList, newList2);
|
||||
}
|
||||
}
|
||||
|
||||
private static void copyModifiers(@NotNull PsiModifierList sourceModifierList,
|
||||
@NotNull PsiModifierList targetModifierList) {
|
||||
VisibilityUtil.setVisibility(targetModifierList, VisibilityUtil.getVisibilityModifier(sourceModifierList));
|
||||
@@ -478,7 +528,7 @@ public final class GenerateMembersUtil {
|
||||
PsiParameter parameter = parameters[i];
|
||||
final PsiType parameterType = parameter.getType();
|
||||
PsiElement declarationScope = parameter.getDeclarationScope();
|
||||
PsiType substituted = declarationScope instanceof PsiTypeParameterListOwner ? substituteType(substitutor, parameterType, (PsiTypeParameterListOwner)declarationScope, parameter.getModifierList())
|
||||
PsiType substituted = declarationScope instanceof PsiTypeParameterListOwner ? substituteType(substitutor, parameterType, (PsiTypeParameterListOwner)declarationScope, parameter.getModifierList())
|
||||
: parameterType;
|
||||
String paramName = parameter.getName();
|
||||
boolean isBaseNameGenerated = true;
|
||||
|
||||
@@ -257,6 +257,7 @@ public final class OverrideImplementUtil extends OverrideImplementExploreUtil {
|
||||
AddAnnotationPsiFix.addPhysicalAnnotationIfAbsent(Override.class.getName(), PsiNameValuePair.EMPTY_ARRAY, method.getModifierList());
|
||||
}
|
||||
OverrideImplementsAnnotationsHandler.repeatAnnotationsFromSource(overridden, targetClass, method);
|
||||
GenerateMembersUtil.sortModifiers(method, overridden);
|
||||
}
|
||||
|
||||
public static void annotate(@NotNull PsiMethod result, @NotNull String fqn, String @NotNull ... annosToRemove) throws IncorrectOperationException {
|
||||
|
||||
@@ -15,25 +15,5 @@ preferred order (as stated in the Java Language Specification).
|
||||
}
|
||||
</code></pre>
|
||||
<!-- tooltip end -->
|
||||
<p>Use the inspection settings to:</p>
|
||||
<ul>
|
||||
<li>
|
||||
<p>
|
||||
toggle the reporting of misplaced annotations:
|
||||
(annotations with <code>ElementType.TYPE_USE</code> <em>not</em> directly
|
||||
before the type and after the modifier keywords, or
|
||||
other annotations <em>not</em> before the modifier keywords).
|
||||
When this option is disabled, any annotation can be positioned before or after the modifier keywords.
|
||||
Modifier lists with annotations in between the modifier keywords will always be reported.
|
||||
</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
specify whether the <code>ElementType.TYPE_USE</code> annotation should be positioned directly before
|
||||
a type, even when the annotation has other targets specified.
|
||||
</p>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -13,8 +13,7 @@ static class Y implements X {
|
||||
X x;
|
||||
|
||||
@Override
|
||||
@Foo
|
||||
public Map.@Foo Entry getString() {
|
||||
public @Foo Map.@Foo Entry getString() {
|
||||
return x.getString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,6 +102,7 @@
|
||||
"for_statement_wrap": "off",
|
||||
"generate_final_locals": false,
|
||||
"generate_final_parameters": false,
|
||||
"generate_use_type_annotation_before_type": true,
|
||||
"if_brace_force": "never",
|
||||
"imports_layout": [
|
||||
"*",
|
||||
|
||||
@@ -28,7 +28,7 @@ class TooComplexCode {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private X newMethod(@NotNull X x) {
|
||||
private TooComplexCode.X newMethod(@NotNull X x) {
|
||||
return x.get();
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ public class NullableCheckBreakDuplicate {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Pojo newMethod() {
|
||||
private NullableCheckBreakDuplicate.Pojo newMethod() {
|
||||
Pojo x = things.get(0);
|
||||
|
||||
if (x.it > 0) return null;
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
public class Test {
|
||||
public void test(){
|
||||
String a;
|
||||
<selection> if (1 == 0) {
|
||||
a = "1";
|
||||
} else {
|
||||
a = null;
|
||||
}</selection>
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Test {
|
||||
public void test(){
|
||||
String a;
|
||||
a = newMethod();
|
||||
return a;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String newMethod() {
|
||||
String a;
|
||||
if (1 == 0) {
|
||||
a = "1";
|
||||
} else {
|
||||
a = null;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
public class Test {
|
||||
public void test(){
|
||||
String a;
|
||||
<selection> if (1 == 0) {
|
||||
a = "1";
|
||||
} else {
|
||||
a = null;
|
||||
}</selection>
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class Test {
|
||||
public void test(){
|
||||
String a;
|
||||
a = newMethod();
|
||||
return a;
|
||||
}
|
||||
|
||||
private @Nullable String newMethod() {
|
||||
String a;
|
||||
if (1 == 0) {
|
||||
a = "1";
|
||||
} else {
|
||||
a = null;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiImplicitClass;
|
||||
import com.intellij.psi.PsiJavaFile;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
@@ -401,6 +402,270 @@ public class OverrideImplementTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
assertTrue(getFile().getText().contains("run()"));
|
||||
}
|
||||
|
||||
public void testTypeAnnotationsAfterKeyword() {
|
||||
OverrideImplementsAnnotationsHandler handler = new OverrideImplementsAnnotationsHandler() {
|
||||
@Override
|
||||
public String[] getAnnotations(@NotNull PsiFile file) { return new String[]{"TA"}; }
|
||||
};
|
||||
OverrideImplementsAnnotationsHandler.EP_NAME.getPoint().registerExtension(handler, getTestRootDisposable());
|
||||
|
||||
myFixture.addClass(
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE_USE, ElementType.METHOD})
|
||||
public @interface TA {
|
||||
}
|
||||
""".stripIndent());
|
||||
|
||||
myFixture.configureByText("test.java", """
|
||||
import java.util.*;
|
||||
|
||||
interface I {
|
||||
@TA
|
||||
public List<String> i(String p1, int[] p2) throws IllegalArgumentException;
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
<caret>
|
||||
}""".stripIndent());
|
||||
|
||||
invokeAction(true);
|
||||
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
import java.util.*;
|
||||
|
||||
interface I {
|
||||
@TA
|
||||
public List<String> i(String p1, int[] p2) throws IllegalArgumentException;
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
@Override
|
||||
public @TA List<String> i(String p1, int[] p2) throws IllegalArgumentException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}""".stripIndent());
|
||||
}
|
||||
|
||||
public void testTypeAnnotationsAfterKeywordWithGenerationBefore() {
|
||||
JavaCodeStyleSettings instance = JavaCodeStyleSettings.getInstance(getProject());
|
||||
boolean oldValue = instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE;
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = false;
|
||||
|
||||
try {
|
||||
OverrideImplementsAnnotationsHandler handler = new OverrideImplementsAnnotationsHandler() {
|
||||
@Override
|
||||
public String[] getAnnotations(@NotNull PsiFile file) { return new String[]{"TA"}; }
|
||||
};
|
||||
OverrideImplementsAnnotationsHandler.EP_NAME.getPoint().registerExtension(handler, getTestRootDisposable());
|
||||
|
||||
myFixture.addClass(
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
@Target({ElementType.TYPE_USE, ElementType.METHOD})
|
||||
public @interface TA {
|
||||
}
|
||||
""".stripIndent());
|
||||
|
||||
myFixture.configureByText("test.java", """
|
||||
import java.util.*;
|
||||
|
||||
interface I {
|
||||
@TA
|
||||
public List<String> i(String p1, int[] p2) throws IllegalArgumentException;
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
<caret>
|
||||
}""".stripIndent());
|
||||
|
||||
invokeAction(true);
|
||||
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
import java.util.*;
|
||||
|
||||
interface I {
|
||||
@TA
|
||||
public List<String> i(String p1, int[] p2) throws IllegalArgumentException;
|
||||
}
|
||||
|
||||
class C implements I {
|
||||
@TA
|
||||
@Override
|
||||
public List<String> i(String p1, int[] p2) throws IllegalArgumentException {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
}""".stripIndent());
|
||||
}finally {
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void testSeveralAnnotations() {
|
||||
OverrideImplementsAnnotationsHandler handler = new OverrideImplementsAnnotationsHandler() {
|
||||
@Override
|
||||
public String[] getAnnotations(@NotNull PsiFile file) { return new String[]{"R1", "R2", "R3", "R4"}; }
|
||||
};
|
||||
OverrideImplementsAnnotationsHandler.EP_NAME.getPoint().registerExtension(handler, getTestRootDisposable());
|
||||
|
||||
myFixture.configureByText("test.java", """
|
||||
import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R2 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R1 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R3 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R4 {
|
||||
boolean test();
|
||||
}
|
||||
abstract public class AAA {
|
||||
@R1(test = true)
|
||||
@R3(test = true)
|
||||
abstract public @R2(test = false) @R4(test = false) Object test();
|
||||
}
|
||||
class BBB extends AAA {
|
||||
<caret>
|
||||
}""".stripIndent());
|
||||
|
||||
invokeAction(true);
|
||||
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R2 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R1 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R3 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R4 {
|
||||
boolean test();
|
||||
}
|
||||
abstract public class AAA {
|
||||
@R1(test = true)
|
||||
@R3(test = true)
|
||||
abstract public @R2(test = false) @R4(test = false) Object test();
|
||||
}
|
||||
class BBB extends AAA {
|
||||
@Override
|
||||
public @R1(test = true) @R3(test = true) @R2(test = false) @R4(test = false) Object test() {
|
||||
return null;
|
||||
}
|
||||
}""".stripIndent());
|
||||
}
|
||||
|
||||
public void testSeveralAnnotationsWithGenerationBefore() {
|
||||
JavaCodeStyleSettings instance = JavaCodeStyleSettings.getInstance(getProject());
|
||||
boolean oldValue = instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE;
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = false;
|
||||
try {
|
||||
OverrideImplementsAnnotationsHandler handler = new OverrideImplementsAnnotationsHandler() {
|
||||
@Override
|
||||
public String[] getAnnotations(@NotNull PsiFile file) { return new String[]{"R1", "R2", "R3", "R4"}; }
|
||||
};
|
||||
OverrideImplementsAnnotationsHandler.EP_NAME.getPoint().registerExtension(handler, getTestRootDisposable());
|
||||
|
||||
myFixture.configureByText("test.java", """
|
||||
import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R2 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R1 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R3 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R4 {
|
||||
boolean test();
|
||||
}
|
||||
abstract public class AAA {
|
||||
@R1(test = true)
|
||||
@R3(test = true)
|
||||
abstract public @R2(test = false) @R4(test = false) Object test();
|
||||
}
|
||||
class BBB extends AAA {
|
||||
<caret>
|
||||
}""".stripIndent());
|
||||
|
||||
invokeAction(true);
|
||||
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
import java.lang.annotation.*;
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R2 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R1 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R3 {
|
||||
boolean test();
|
||||
}
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target({ElementType.METHOD, ElementType.TYPE_USE})
|
||||
@interface R4 {
|
||||
boolean test();
|
||||
}
|
||||
abstract public class AAA {
|
||||
@R1(test = true)
|
||||
@R3(test = true)
|
||||
abstract public @R2(test = false) @R4(test = false) Object test();
|
||||
}
|
||||
class BBB extends AAA {
|
||||
@R1(test = true)
|
||||
@R3(test = true)
|
||||
@R2(test = false)
|
||||
@R4(test = false)
|
||||
@Override
|
||||
public Object test() {
|
||||
return null;
|
||||
}
|
||||
}""".stripIndent());
|
||||
}finally {
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void doTest(boolean toImplement) {
|
||||
final String name = getTestName(false);
|
||||
myFixture.configureByFile("before" + name + ".java");
|
||||
|
||||
@@ -10,6 +10,9 @@ import com.intellij.ide.IdePopupManager
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.roots.ContentEntry
|
||||
import com.intellij.openapi.roots.ModifiableRootModel
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
@@ -22,6 +25,8 @@ import com.intellij.refactoring.listeners.RefactoringEventListener
|
||||
import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException
|
||||
import com.intellij.testFramework.IdeaTestUtil
|
||||
import com.intellij.testFramework.LightJavaCodeInsightTestCase
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor
|
||||
import com.intellij.ui.ChooserInterceptor
|
||||
import com.intellij.ui.UiInterceptors
|
||||
import com.intellij.util.ui.UIUtil
|
||||
@@ -568,6 +573,14 @@ class ExtractMethodAndDuplicatesInplaceTest: LightJavaCodeInsightTestCase() {
|
||||
} while (isVariableSwitched)
|
||||
}
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor {
|
||||
return object : SimpleLightProjectDescriptor(moduleTypeId, projectJDK) {
|
||||
override fun configureModule(module: Module, model: ModifiableRootModel, contentEntry: ContentEntry) {
|
||||
DefaultLightProjectDescriptor.addJetBrainsAnnotationsWithTypeUse(model)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
val settings = JavaRefactoringSettings.getInstance()
|
||||
|
||||
@@ -8,7 +8,10 @@ import com.intellij.codeInsight.NullableNotNullManager;
|
||||
import com.intellij.lang.java.JavaLanguage;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.editor.Editor;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.openapi.util.registry.Registry;
|
||||
import com.intellij.openapi.util.registry.RegistryValue;
|
||||
import com.intellij.pom.java.LanguageLevel;
|
||||
@@ -26,8 +29,9 @@ import com.intellij.refactoring.extractMethod.newImpl.ExtractException;
|
||||
import com.intellij.refactoring.extractMethod.newImpl.MethodExtractor;
|
||||
import com.intellij.refactoring.util.duplicates.Match;
|
||||
import com.intellij.testFramework.IdeaTestUtil;
|
||||
import com.intellij.testFramework.IndexingTestUtil;
|
||||
import com.intellij.testFramework.LightJavaCodeInsightTestCase;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
@@ -1694,6 +1698,30 @@ public class ExtractMethodNewTest extends LightJavaCodeInsightTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testSimpleWithNullableDirectlyBeforeType() throws Exception {
|
||||
JavaCodeStyleSettings instance = JavaCodeStyleSettings.getInstance(getProject());
|
||||
boolean oldValue = instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE;
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = true;
|
||||
try {
|
||||
doTest();
|
||||
}
|
||||
finally {
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
public void testSimpleWithNullableDirectlyBeforeKeyword() throws Exception {
|
||||
JavaCodeStyleSettings instance = JavaCodeStyleSettings.getInstance(getProject());
|
||||
boolean oldValue = instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE;
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = false;
|
||||
try {
|
||||
doTest();
|
||||
}
|
||||
finally {
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = oldValue;
|
||||
}
|
||||
}
|
||||
|
||||
private void doTestDisabledParam() throws PrepareFailedException {
|
||||
final CommonCodeStyleSettings settings = CodeStyle.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE);
|
||||
settings.ELSE_ON_NEW_LINE = true;
|
||||
@@ -1901,4 +1929,14 @@ public class ExtractMethodNewTest extends LightJavaCodeInsightTestCase {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return new SimpleLightProjectDescriptor(getModuleTypeId(), getProjectJDK()) {
|
||||
@Override
|
||||
protected void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) {
|
||||
DefaultLightProjectDescriptor.addJetBrainsAnnotationsWithTypeUse(model);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -5,10 +5,16 @@ import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.intention.IntentionAction;
|
||||
import com.intellij.codeInspection.RedundantSuppressInspection;
|
||||
import com.intellij.java.JavaBundle;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.roots.ContentEntry;
|
||||
import com.intellij.openapi.roots.LanguageLevelModuleExtension;
|
||||
import com.intellij.openapi.roots.ModifiableRootModel;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import static com.intellij.pom.java.LanguageLevel.JDK_21_PREVIEW;
|
||||
|
||||
public class ExtractMethodRecommenderInspectionTest extends LightJavaCodeInsightFixtureTestCase {
|
||||
public void testExtractMethodRecommender() {
|
||||
ExtractMethodRecommenderInspection inspection = new ExtractMethodRecommenderInspection();
|
||||
@@ -17,7 +23,7 @@ public class ExtractMethodRecommenderInspectionTest extends LightJavaCodeInsight
|
||||
myFixture.configureByFile(getTestName(false) + ".java");
|
||||
myFixture.checkHighlighting();
|
||||
}
|
||||
|
||||
|
||||
public void testImplicitClass() {
|
||||
ExtractMethodRecommenderInspection inspection = new ExtractMethodRecommenderInspection();
|
||||
myFixture.enableInspections(inspection);
|
||||
@@ -49,9 +55,9 @@ public class ExtractMethodRecommenderInspectionTest extends LightJavaCodeInsight
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on {@link ExtractMethodRecommenderInspectionTest#testExtractMethodRecommender()}
|
||||
* when the suggestion is placed on comments, even though this place is not really convenient.
|
||||
* This method checks that quickfix works even for this place
|
||||
* Based on {@link ExtractMethodRecommenderInspectionTest#testExtractMethodRecommender()}
|
||||
* when the suggestion is placed on comments, even though this place is not really convenient.
|
||||
* This method checks that quickfix works even for this place
|
||||
*/
|
||||
public void testCallExtractFirstNotDeclaration() {
|
||||
ExtractMethodRecommenderInspection inspection = new ExtractMethodRecommenderInspection();
|
||||
@@ -67,11 +73,17 @@ public class ExtractMethodRecommenderInspectionTest extends LightJavaCodeInsight
|
||||
|
||||
@Override
|
||||
protected @NotNull LightProjectDescriptor getProjectDescriptor() {
|
||||
return JAVA_21;
|
||||
return new ProjectDescriptor(JDK_21_PREVIEW){
|
||||
@Override
|
||||
public void configureModule(@NotNull Module module, @NotNull ModifiableRootModel model, @NotNull ContentEntry contentEntry) {
|
||||
model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(myLanguageLevel);
|
||||
addJetBrainsAnnotationsWithTypeUse(model);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBasePath() {
|
||||
return JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/extractMethodRecommender";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
package com.siyeh.ig.style;
|
||||
|
||||
import com.intellij.codeInspection.InspectionProfileEntry;
|
||||
import com.intellij.psi.codeStyle.JavaCodeStyleSettings;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.siyeh.InspectionGadgetsBundle;
|
||||
import com.siyeh.ig.LightJavaInspectionTestCase;
|
||||
@@ -55,14 +56,21 @@ public class MissortedModifiersInspectionTest extends LightJavaInspectionTestCas
|
||||
checkQuickFix(InspectionGadgetsBundle.message("missorted.modifiers.sort.quickfix"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
JavaCodeStyleSettings instance = JavaCodeStyleSettings.getInstance(getProject());
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = false;
|
||||
if (getTestName(false).contains("TypeUseWithType")) {
|
||||
instance.GENERATE_USE_TYPE_ANNOTATION_BEFORE_TYPE = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
protected InspectionProfileEntry getInspection() {
|
||||
MissortedModifiersInspection inspection = new MissortedModifiersInspection();
|
||||
if (getTestName(false).contains("TypeUseWithType")) {
|
||||
inspection.typeUseWithType = true;
|
||||
}
|
||||
else if (getTestName(false).contains("IgnoreAnnotations")) {
|
||||
if (getTestName(false).contains("IgnoreAnnotations")) {
|
||||
inspection.m_requireAnnotationsFirst = false;
|
||||
}
|
||||
return inspection;
|
||||
|
||||
@@ -1344,6 +1344,8 @@ unwrap.switch.statement=Unwrap 'switch' statement
|
||||
usage.target.exception=Exception
|
||||
usage.target.package.in.directory={0} (in {1})
|
||||
use.external.annotations=Use &external annotations
|
||||
generate.type.use.before.type=Generate annotations allowed for TYPE_USE directly &before a type
|
||||
generate.type.use.before.type.description=Annotations, containing TYPE_USE as target, will be placed right before a type. Otherwise, it will be tried to place before all modifiers.
|
||||
wrapping.annotation.enums=Enum field annotations
|
||||
wrapping.annotation.parameters=Annotation parameters
|
||||
wrapping.record.components=Record components
|
||||
|
||||
@@ -34,6 +34,7 @@ import static com.intellij.workspaceModel.ide.legacyBridge.impl.java.JavaModuleT
|
||||
|
||||
public class DefaultLightProjectDescriptor extends LightProjectDescriptor {
|
||||
private static final String JETBRAINS_ANNOTATIONS_COORDINATES = "org.jetbrains:annotations-java5:24.0.0";
|
||||
private static final String JETBRAINS_ANNOTATIONS_COORDINATES_JAVA_8 = "org.jetbrains:annotations:24.0.0";
|
||||
private @Nullable Supplier<? extends Sdk> customSdk;
|
||||
private final List<RequiredLibrary> mavenLibraries = new ArrayList<>();
|
||||
|
||||
@@ -86,6 +87,10 @@ public class DefaultLightProjectDescriptor extends LightProjectDescriptor {
|
||||
return withRepositoryLibrary(JETBRAINS_ANNOTATIONS_COORDINATES);
|
||||
}
|
||||
|
||||
public static void addJetBrainsAnnotationsWithTypeUse(ModifiableRootModel model) {
|
||||
MavenDependencyUtil.addFromMaven(model, JETBRAINS_ANNOTATIONS_COORDINATES_JAVA_8);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds old non-type-use JetBrains annotations as a module dependency.
|
||||
* Currently, many tests assume that this dependency is present.
|
||||
|
||||
@@ -636,7 +636,7 @@ public abstract class LightPlatformTestCase extends UsefulTestCase implements Da
|
||||
private @Nullable Sdk mySdk;
|
||||
private @NotNull Map<OrderRootType, List<String>> mySdkRoots;
|
||||
|
||||
SimpleLightProjectDescriptor(@NotNull String moduleTypeId, @Nullable Sdk sdk) {
|
||||
protected SimpleLightProjectDescriptor(@NotNull String moduleTypeId, @Nullable Sdk sdk) {
|
||||
myModuleTypeId = moduleTypeId;
|
||||
mySdk = sdk;
|
||||
mySdkRoots = new HashMap<>();
|
||||
|
||||
Reference in New Issue
Block a user