[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:
Mikhail Pyltsin
2024-06-10 19:18:09 +02:00
committed by intellij-monorepo-bot
parent f57df70730
commit 2023228d8c
26 changed files with 681 additions and 142 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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": [
"*",

View File

@@ -28,7 +28,7 @@ class TooComplexCode {
}
@NotNull
private X newMethod(@NotNull X x) {
private TooComplexCode.X newMethod(@NotNull X x) {
return x.get();
}
}

View File

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

View File

@@ -0,0 +1,11 @@
public class Test {
public void test(){
String a;
<selection> if (1 == 0) {
a = "1";
} else {
a = null;
}</selection>
return a;
}
}

View File

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

View File

@@ -0,0 +1,11 @@
public class Test {
public void test(){
String a;
<selection> if (1 == 0) {
a = "1";
} else {
a = null;
}</selection>
return a;
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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