From fa775da12a1dead5068775459a43ee1adca1809c Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 3 Dec 2025 22:21:11 +0300 Subject: [PATCH 1/2] Inspections localizing and refactoring (part 2). --- .../properties/impl/i18n/I18nInspection.java | 1586 +++++------ ...nusedMessageFormatParameterInspection.java | 151 +- .../FileHeaderChecker.java | 153 +- .../DependencyInspection.java | 56 +- .../emptyMethod/EmptyMethodInspection.java | 128 +- ...HasFrequentlyUsedInheritorsInspection.java | 59 +- .../javaDoc/JavaDocLocalInspection.java | 2373 +++++++++-------- .../javaDoc/JavaDocReferenceInspection.java | 603 +++-- .../SameReturnValueInspection.java | 31 +- .../ParameterCanBeLocalInspection.java | 71 +- ...ticMethodOnlyUsedInOneClassInspection.java | 10 +- .../MethodReturnAlwaysConstantInspection.java | 140 +- .../ClassIndependentOfModuleInspection.java | 43 +- .../ClassOnlyUsedInOneModuleInspection.java | 42 +- .../ClassOnlyUsedInOnePackageInspection.java | 49 +- .../ClassUnconnectedToPackageInspection.java | 45 +- ...ElementOnlyUsedFromTestCodeInspection.java | 140 +- 17 files changed, 2852 insertions(+), 2828 deletions(-) diff --git a/java-properties-impl/src/main/java/consulo/java/properties/impl/i18n/I18nInspection.java b/java-properties-impl/src/main/java/consulo/java/properties/impl/i18n/I18nInspection.java index 40845c090b..df81f45fda 100644 --- a/java-properties-impl/src/main/java/consulo/java/properties/impl/i18n/I18nInspection.java +++ b/java-properties-impl/src/main/java/consulo/java/properties/impl/i18n/I18nInspection.java @@ -33,6 +33,7 @@ import com.intellij.java.language.psi.util.PsiUtil; import com.intellij.java.language.util.TreeClassChooser; import com.intellij.java.language.util.TreeClassChooserFactory; +import com.siyeh.ig.junit.JUnitCommonClassNames; import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.document.Document; @@ -40,7 +41,6 @@ import consulo.language.Language; import consulo.language.editor.inspection.LocalQuickFix; import consulo.language.editor.inspection.ProblemDescriptor; -import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.localize.InspectionLocalize; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.intention.SuppressIntentionAction; @@ -52,6 +52,7 @@ import consulo.localize.LocalizeValue; import consulo.project.Project; import consulo.project.ProjectManager; +import consulo.ui.annotation.RequiredUIAccess; import consulo.ui.ex.awt.AddDeleteListPanel; import consulo.ui.ex.awt.DialogWrapper; import consulo.ui.ex.awt.FieldPanel; @@ -66,7 +67,6 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import javax.swing.*; import javax.swing.event.DocumentEvent; @@ -78,869 +78,869 @@ @ExtensionImpl public class I18nInspection extends BaseLocalInspectionTool { - public boolean ignoreForAssertStatements = true; - public boolean ignoreForExceptionConstructors = true; - @NonNls - public String ignoreForSpecifiedExceptionConstructors = ""; - public boolean ignoreForJUnitAsserts = true; - public boolean ignoreForClassReferences = true; - public boolean ignoreForPropertyKeyReferences = true; - public boolean ignoreForNonAlpha = true; - public boolean ignoreAssignedToConstants = false; - public boolean ignoreToString = false; - @NonNls - public String nonNlsCommentPattern = "NON-NLS"; - private boolean ignoreForEnumConstants = false; - - private static final LocalQuickFix I18N_QUICK_FIX = new I18nizeQuickFix(); - private static final I18nizeConcatenationQuickFix I18N_CONCATENATION_QUICK_FIX = new I18nizeConcatenationQuickFix(); - - @Nullable - private Pattern myCachedNonNlsPattern; - @NonNls - private static final String TO_STRING = "toString"; - - public I18nInspection() { - cacheNonNlsCommentPattern(); - } - - @Override - public SuppressIntentionAction[] getSuppressActions(PsiElement element) { - SuppressIntentionAction[] actions = {}; - if (myCachedNonNlsPattern != null) { - actions = new SuppressIntentionAction[]{new SuppressByCommentOutAction(nonNlsCommentPattern)}; - } - return ArrayUtil.mergeArrays(actions, super.getSuppressActions(element)); - } - - private static final String SKIP_FOR_ENUM = "ignoreForEnumConstant"; - - @Override - public void writeSettings(@Nonnull Element node) throws WriteExternalException { - super.writeSettings(node); - if (ignoreForEnumConstants) { - final Element e = new Element("option"); - e.setAttribute("name", SKIP_FOR_ENUM); - e.setAttribute("value", Boolean.toString(ignoreForEnumConstants)); - node.addContent(e); - } - } - - @Override - public void readSettings(@Nonnull Element node) throws InvalidDataException { - super.readSettings(node); - for (Object o : node.getChildren()) { - if (o instanceof Element && Comparing.strEqual(node.getAttributeValue("name"), SKIP_FOR_ENUM)) { - final String ignoreForConstantsAttr = node.getAttributeValue("value"); - if (ignoreForConstantsAttr != null) { - ignoreForEnumConstants = Boolean.parseBoolean(ignoreForConstantsAttr); - } - break; - } - } - cacheNonNlsCommentPattern(); - } - - @Override - @Nonnull - public LocalizeValue getGroupDisplayName() { - return InspectionLocalize.groupNamesInternationalizationIssues(); - } - - @Override - @Nonnull - public LocalizeValue getDisplayName() { - return CodeInsightLocalize.inspectionI18nDisplayName(); - } - - @Nullable - @Override - public Language getLanguage() { - return JavaLanguage.INSTANCE; - } - - @Override - @Nonnull - public String getShortName() { - return "HardCodedStringLiteral"; - } - - @Override - public JComponent createOptionsPanel() { - final GridBagLayout layout = new GridBagLayout(); - final JPanel panel = new JPanel(layout); - final JCheckBox assertStatementsCheckbox = - new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreAssert().get(), ignoreForAssertStatements); - assertStatementsCheckbox.addChangeListener(e -> ignoreForAssertStatements = assertStatementsCheckbox.isSelected()); - final JCheckBox exceptionConstructorCheck = - new JCheckBox( - CodeInsightLocalize.inspectionI18nOptionIgnoreForExceptionConstructorArguments().get(), - ignoreForExceptionConstructors - ); - exceptionConstructorCheck.addChangeListener(e -> ignoreForExceptionConstructors = exceptionConstructorCheck.isSelected()); - - final JTextField specifiedExceptions = new JTextField(ignoreForSpecifiedExceptionConstructors); - specifiedExceptions.getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - ignoreForSpecifiedExceptionConstructors = specifiedExceptions.getText(); - } - }); - - final JCheckBox junitAssertCheckbox = new JCheckBox( - CodeInsightLocalize.inspectionI18nOptionIgnoreForJunitAssertArguments().get(), - ignoreForJUnitAsserts - ); - junitAssertCheckbox.addChangeListener(e -> ignoreForJUnitAsserts = junitAssertCheckbox.isSelected()); - final JCheckBox classRef = - new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreQualifiedClassNames().get(), ignoreForClassReferences); - classRef.addChangeListener(e -> ignoreForClassReferences = classRef.isSelected()); - final JCheckBox propertyRef = - new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnorePropertyKeys().get(), ignoreForPropertyKeyReferences); - propertyRef.addChangeListener(e -> ignoreForPropertyKeyReferences = propertyRef.isSelected()); - final JCheckBox nonAlpha = - new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreNonalphanumerics().get(), ignoreForNonAlpha); - nonAlpha.addChangeListener(e -> ignoreForNonAlpha = nonAlpha.isSelected()); - final JCheckBox assignedToConstants = - new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreAssignedToConstants().get(), ignoreAssignedToConstants); - assignedToConstants.addChangeListener(e -> ignoreAssignedToConstants = assignedToConstants.isSelected()); - final JCheckBox chkToString = new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreTostring().get(), ignoreToString); - chkToString.addChangeListener(e -> ignoreToString = chkToString.isSelected()); - - final JCheckBox ignoreEnumConstants = new JCheckBox("Ignore enum constants", ignoreForEnumConstants); - ignoreEnumConstants.addChangeListener(e -> ignoreForEnumConstants = ignoreEnumConstants.isSelected()); - - final GridBagConstraints gc = new GridBagConstraints(); - gc.fill = GridBagConstraints.HORIZONTAL; - gc.insets.bottom = 2; - - gc.gridx = GridBagConstraints.REMAINDER; - gc.gridy = 0; - gc.weightx = 1; - gc.weighty = 0; - panel.add(assertStatementsCheckbox, gc); - - gc.gridy++; - panel.add(junitAssertCheckbox, gc); - - gc.gridy++; - panel.add(exceptionConstructorCheck, gc); - - gc.gridy++; - final Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); - panel.add( - new FieldPanel( - specifiedExceptions, - null, - CodeInsightLocalize.inspectionI18nOptionIgnoreForSpecifiedExceptionConstructorArguments().get(), - openProjects.length == 0 ? null : e -> createIgnoreExceptionsConfigurationDialog(openProjects[0], specifiedExceptions).show(), - null - ), - gc - ); - - gc.gridy++; - panel.add(classRef, gc); - - gc.gridy++; - panel.add(propertyRef, gc); - - gc.gridy++; - panel.add(assignedToConstants, gc); - - gc.gridy++; - panel.add(chkToString, gc); - - gc.gridy++; - panel.add(nonAlpha, gc); - - gc.gridy++; - panel.add(ignoreEnumConstants, gc); - - gc.gridy++; - gc.anchor = GridBagConstraints.NORTHWEST; - gc.weighty = 1; - final JTextField text = new JTextField(nonNlsCommentPattern); - final FieldPanel nonNlsCommentPatternComponent = - new FieldPanel( - text, - CodeInsightLocalize.inspectionI18nOptionIgnoreCommentPattern().get(), - CodeInsightLocalize.inspectionI18nOptionIgnoreCommentTitle().get(), - null, - () -> { - nonNlsCommentPattern = text.getText(); - cacheNonNlsCommentPattern(); - } - ); - panel.add(nonNlsCommentPatternComponent, gc); - - final JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(panel); - scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); - scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); - scrollPane.setBorder(null); - scrollPane.setPreferredSize(new Dimension( - panel.getPreferredSize().width + scrollPane.getVerticalScrollBar().getPreferredSize().width, - panel.getPreferredSize().height + scrollPane.getHorizontalScrollBar().getPreferredSize().height - )); - return scrollPane; - } - - @SuppressWarnings({"NonStaticInitializer"}) - private DialogWrapper createIgnoreExceptionsConfigurationDialog(final Project project, final JTextField specifiedExceptions) { - return new DialogWrapper(true) { - private AddDeleteListPanel myPanel; - - { - setTitle(CodeInsightLocalize.inspectionI18nOptionIgnoreForSpecifiedExceptionConstructorArguments()); - init(); - } - - @Override - protected JComponent createCenterPanel() { - final String[] ignored = ignoreForSpecifiedExceptionConstructors.split(","); - final List initialList = new ArrayList<>(); - if (ignored != null) { - for (String e : ignored) { - if (e.length() > 0) initialList.add(e); - } - } - myPanel = new AddDeleteListPanel<>(null, initialList) { - @Override - protected String findItemToAdd() { - final GlobalSearchScope scope = GlobalSearchScope.allScope(project); - TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project). - createInheritanceClassChooser( - CodeInsightLocalize.inspectionI18nOptionIgnoreForSpecifiedExceptionConstructorArguments().get(), - scope, - JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_THROWABLE, scope), - true, - true, - null - ); - chooser.showDialog(); - PsiClass selectedClass = chooser.getSelected(); - return selectedClass != null ? selectedClass.getQualifiedName() : null; - } - }; - return myPanel; - } - - @Override - protected void doOKAction() { - StringBuilder buf = new StringBuilder(); - final Object[] exceptions = myPanel.getListItems(); - for (Object exception : exceptions) { - buf.append(",").append(exception); - } - specifiedExceptions.setText(buf.length() > 0 ? buf.substring(1) : buf.toString()); - super.doOKAction(); - } - }; - } - - @Override - @Nullable - public ProblemDescriptor[] checkMethod(@Nonnull PsiMethod method, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - PsiClass containingClass = method.getContainingClass(); - if (containingClass == null || isClassNonNls(containingClass)) { - return null; - } - final PsiCodeBlock body = method.getBody(); - if (body != null) { - return checkElement(body, manager, isOnTheFly); - } - return null; - } - - @Override - @Nullable - public ProblemDescriptor[] checkClass(@Nonnull PsiClass aClass, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - if (isClassNonNls(aClass)) { - return null; - } - final PsiClassInitializer[] initializers = aClass.getInitializers(); - List result = new ArrayList<>(); - for (PsiClassInitializer initializer : initializers) { - final ProblemDescriptor[] descriptors = checkElement(initializer, manager, isOnTheFly); - if (descriptors != null) { - ContainerUtil.addAll(result, descriptors); - } - } - - return result.isEmpty() ? null : result.toArray(new ProblemDescriptor[result.size()]); - } - - @Override - @Nullable - public ProblemDescriptor[] checkField(@Nonnull PsiField field, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - PsiClass containingClass = field.getContainingClass(); - if (containingClass == null || isClassNonNls(containingClass)) { - return null; - } - if (AnnotationUtil.isAnnotated(field, AnnotationUtil.NON_NLS, false, false)) { - return null; - } - final PsiExpression initializer = field.getInitializer(); - if (initializer != null) return checkElement(initializer, manager, isOnTheFly); - - if (field instanceof PsiEnumConstant enumConstant) { - return checkElement(enumConstant.getArgumentList(), manager, isOnTheFly); - } - return null; - } - - private ProblemDescriptor[] checkElement(final PsiElement element, InspectionManager manager, boolean isOnTheFly) { - StringI18nVisitor visitor = new StringI18nVisitor(manager, isOnTheFly); - element.accept(visitor); - List problems = visitor.getProblems(); - return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]); - } - - private static LocalQuickFix createIntroduceConstantFix() { - return new LocalQuickFix() { - @Override - @Nonnull - public LocalizeValue getName() { - return RefactoringLocalize.introduceConstantTitle(); - } - - @Override - public void applyFix(@Nonnull final Project project, @Nonnull final ProblemDescriptor descriptor) { - //do it later because it is invoked from write action - project.getApplication().invokeLater( - () -> { - PsiElement element = descriptor.getPsiElement(); - if (!(element instanceof PsiExpression)) return; - - PsiExpression[] expressions = {(PsiExpression)element}; - - IntroduceConstantHandler handler = JavaRefactoringActionHandlerFactory.getInstance().createIntroduceConstantHandler(); - - handler.invoke(project, expressions); - }, - project.getDisposed() - ); - } - }; - } + public boolean ignoreForAssertStatements = true; + public boolean ignoreForExceptionConstructors = true; + public String ignoreForSpecifiedExceptionConstructors = ""; + public boolean ignoreForJUnitAsserts = true; + public boolean ignoreForClassReferences = true; + public boolean ignoreForPropertyKeyReferences = true; + public boolean ignoreForNonAlpha = true; + public boolean ignoreAssignedToConstants = false; + public boolean ignoreToString = false; + public String nonNlsCommentPattern = "NON-NLS"; + private boolean ignoreForEnumConstants = false; - private class StringI18nVisitor extends JavaRecursiveElementVisitor { - private final List myProblems = new ArrayList<>(); - private final InspectionManager myManager; - private final boolean myOnTheFly; + private static final LocalQuickFix I18N_QUICK_FIX = new I18nizeQuickFix(); + private static final I18nizeConcatenationQuickFix I18N_CONCATENATION_QUICK_FIX = new I18nizeConcatenationQuickFix(); - public StringI18nVisitor(final InspectionManager manager, boolean onTheFly) { - myManager = manager; - myOnTheFly = onTheFly; - } + @Nullable + private Pattern myCachedNonNlsPattern; + private static final String TO_STRING = "toString"; - @Override - public void visitAnonymousClass(PsiAnonymousClass aClass) { - final PsiExpressionList argumentList = aClass.getArgumentList(); - if (argumentList != null) { - argumentList.accept(this); - } + public I18nInspection() { + cacheNonNlsCommentPattern(); } @Override - public void visitClass(@Nonnull PsiClass aClass) { + public SuppressIntentionAction[] getSuppressActions(PsiElement element) { + SuppressIntentionAction[] actions = {}; + if (myCachedNonNlsPattern != null) { + actions = new SuppressIntentionAction[]{new SuppressByCommentOutAction(nonNlsCommentPattern)}; + } + return ArrayUtil.mergeArrays(actions, super.getSuppressActions(element)); } - @Override - public void visitField(@Nonnull PsiField field) { - } + private static final String SKIP_FOR_ENUM = "ignoreForEnumConstant"; @Override - public void visitMethod(@Nonnull PsiMethod method) { + public void writeSettings(@Nonnull Element node) throws WriteExternalException { + super.writeSettings(node); + if (ignoreForEnumConstants) { + Element e = new Element("option"); + e.setAttribute("name", SKIP_FOR_ENUM); + e.setAttribute("value", Boolean.toString(ignoreForEnumConstants)); + node.addContent(e); + } } @Override - public void visitLiteralExpression(PsiLiteralExpression expression) { - Object value = expression.getValue(); - if (!(value instanceof String)) return; - String stringValue = (String)value; - if (stringValue.trim().length() == 0) { - return; - } - - Set nonNlsTargets = new HashSet<>(); - if (canBeI18ned(myManager.getProject(), expression, stringValue, nonNlsTargets)) { - PsiField parentField = PsiTreeUtil.getParentOfType(expression, PsiField.class); - if (parentField != null) { - nonNlsTargets.add(parentField); - } - - final LocalizeValue description = CodeInsightLocalize.inspectionI18nMessageGeneralWithValue("#ref"); - - List fixes = new ArrayList<>(); - if (I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(expression) != null) { - fixes.add(I18N_CONCATENATION_QUICK_FIX); - } - fixes.add(I18N_QUICK_FIX); - - if (!isNotConstantFieldInitializer(expression)) { - fixes.add(createIntroduceConstantFix()); - } - - final Project project = expression.getManager().getProject(); - final JavaPsiFacade facade = JavaPsiFacade.getInstance(project); - if (PsiUtil.isLanguageLevel5OrHigher(expression)) { - for (PsiModifierListOwner element : nonNlsTargets) { - if (!AnnotationUtil.isAnnotated(element, AnnotationUtil.NLS, true, false)) { - if (!element.getManager().isInProject(element) || facade.findClass(AnnotationUtil.NON_NLS, - element.getResolveScope()) != null) { - fixes.add(new AddAnnotationFix(AnnotationUtil.NON_NLS, element)); - } + public void readSettings(@Nonnull Element node) throws InvalidDataException { + super.readSettings(node); + for (Object o : node.getChildren()) { + if (o instanceof Element && Comparing.strEqual(node.getAttributeValue("name"), SKIP_FOR_ENUM)) { + String ignoreForConstantsAttr = node.getAttributeValue("value"); + if (ignoreForConstantsAttr != null) { + ignoreForEnumConstants = Boolean.parseBoolean(ignoreForConstantsAttr); + } + break; } - } } - - LocalQuickFix[] farr = fixes.toArray(new LocalQuickFix[fixes.size()]); - final ProblemDescriptor problem = - myManager.createProblemDescriptor(expression, description.get(), myOnTheFly, farr, ProblemHighlightType.GENERIC_ERROR_OR_WARNING); - myProblems.add(problem); - } + cacheNonNlsCommentPattern(); } - private boolean isNotConstantFieldInitializer(final PsiExpression expression) { - PsiField parentField = expression.getParent() instanceof PsiField field ? field : null; - return parentField != null && expression == parentField.getInitializer() && - parentField.hasModifierProperty(PsiModifier.FINAL) && - parentField.hasModifierProperty(PsiModifier.STATIC); + @Override + @Nonnull + public LocalizeValue getGroupDisplayName() { + return InspectionLocalize.groupNamesInternationalizationIssues(); } - @Override - public void visitAnnotation(PsiAnnotation annotation) { - //prevent from @SuppressWarnings - if (!BatchSuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME.equals(annotation.getQualifiedName())) { - super.visitAnnotation(annotation); - } + @Nonnull + public LocalizeValue getDisplayName() { + return CodeInsightLocalize.inspectionI18nDisplayName(); } - public List getProblems() { - return myProblems; + @Nullable + @Override + public Language getLanguage() { + return JavaLanguage.INSTANCE; } - } - @RequiredReadAction - private boolean canBeI18ned( - @Nonnull Project project, - @Nonnull PsiLiteralExpression expression, - @Nonnull String value, - @Nonnull Set nonNlsTargets - ) { - if (ignoreForNonAlpha && !StringUtil.containsAlphaCharacters(value)) { - return false; + @Override + @Nonnull + public String getShortName() { + return "HardCodedStringLiteral"; } - if (JavaI18nUtil.isPassedToAnnotatedParam(project, expression, AnnotationUtil.NON_NLS, new HashMap<>(), nonNlsTargets)) { - return false; - } + @Override + public JComponent createOptionsPanel() { + GridBagLayout layout = new GridBagLayout(); + JPanel panel = new JPanel(layout); + JCheckBox assertStatementsCheckbox = + new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreAssert().get(), ignoreForAssertStatements); + assertStatementsCheckbox.addChangeListener(e -> ignoreForAssertStatements = assertStatementsCheckbox.isSelected()); + JCheckBox exceptionConstructorCheck = new JCheckBox( + CodeInsightLocalize.inspectionI18nOptionIgnoreForExceptionConstructorArguments().get(), + ignoreForExceptionConstructors + ); + exceptionConstructorCheck.addChangeListener(e -> ignoreForExceptionConstructors = exceptionConstructorCheck.isSelected()); - if (isInNonNlsCall(project, expression, nonNlsTargets)) { - return false; - } + final JTextField specifiedExceptions = new JTextField(ignoreForSpecifiedExceptionConstructors); + specifiedExceptions.getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + ignoreForSpecifiedExceptionConstructors = specifiedExceptions.getText(); + } + }); - if (isInNonNlsEquals(expression, nonNlsTargets)) { - return false; - } + JCheckBox junitAssertCheckbox = new JCheckBox( + CodeInsightLocalize.inspectionI18nOptionIgnoreForJunitAssertArguments().get(), + ignoreForJUnitAsserts + ); + junitAssertCheckbox.addChangeListener(e -> ignoreForJUnitAsserts = junitAssertCheckbox.isSelected()); + JCheckBox classRef = + new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreQualifiedClassNames().get(), ignoreForClassReferences); + classRef.addChangeListener(e -> ignoreForClassReferences = classRef.isSelected()); + JCheckBox propertyRef = + new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnorePropertyKeys().get(), ignoreForPropertyKeyReferences); + propertyRef.addChangeListener(e -> ignoreForPropertyKeyReferences = propertyRef.isSelected()); + JCheckBox nonAlpha = + new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreNonalphanumerics().get(), ignoreForNonAlpha); + nonAlpha.addChangeListener(e -> ignoreForNonAlpha = nonAlpha.isSelected()); + JCheckBox assignedToConstants = + new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreAssignedToConstants().get(), ignoreAssignedToConstants); + assignedToConstants.addChangeListener(e -> ignoreAssignedToConstants = assignedToConstants.isSelected()); + JCheckBox chkToString = new JCheckBox(CodeInsightLocalize.inspectionI18nOptionIgnoreTostring().get(), ignoreToString); + chkToString.addChangeListener(e -> ignoreToString = chkToString.isSelected()); + + JCheckBox ignoreEnumConstants = new JCheckBox("Ignore enum constants", ignoreForEnumConstants); + ignoreEnumConstants.addChangeListener(e -> ignoreForEnumConstants = ignoreEnumConstants.isSelected()); + + GridBagConstraints gc = new GridBagConstraints(); + gc.fill = GridBagConstraints.HORIZONTAL; + gc.insets.bottom = 2; + + gc.gridx = GridBagConstraints.REMAINDER; + gc.gridy = 0; + gc.weightx = 1; + gc.weighty = 0; + panel.add(assertStatementsCheckbox, gc); + + gc.gridy++; + panel.add(junitAssertCheckbox, gc); + + gc.gridy++; + panel.add(exceptionConstructorCheck, gc); + + gc.gridy++; + Project[] openProjects = ProjectManager.getInstance().getOpenProjects(); + panel.add( + new FieldPanel( + specifiedExceptions, + null, + CodeInsightLocalize.inspectionI18nOptionIgnoreForSpecifiedExceptionConstructorArguments().get(), + openProjects.length == 0 + ? null + : e -> createIgnoreExceptionsConfigurationDialog(openProjects[0], specifiedExceptions).show(), + null + ), + gc + ); - if (isPassedToNonNlsVariable(project, expression, nonNlsTargets)) { - return false; - } + gc.gridy++; + panel.add(classRef, gc); + + gc.gridy++; + panel.add(propertyRef, gc); + + gc.gridy++; + panel.add(assignedToConstants, gc); + + gc.gridy++; + panel.add(chkToString, gc); + + gc.gridy++; + panel.add(nonAlpha, gc); + + gc.gridy++; + panel.add(ignoreEnumConstants, gc); + + gc.gridy++; + gc.anchor = GridBagConstraints.NORTHWEST; + gc.weighty = 1; + JTextField text = new JTextField(nonNlsCommentPattern); + FieldPanel nonNlsCommentPatternComponent = + new FieldPanel( + text, + CodeInsightLocalize.inspectionI18nOptionIgnoreCommentPattern().get(), + CodeInsightLocalize.inspectionI18nOptionIgnoreCommentTitle().get(), + null, + () -> { + nonNlsCommentPattern = text.getText(); + cacheNonNlsCommentPattern(); + } + ); + panel.add(nonNlsCommentPatternComponent, gc); + + JScrollPane scrollPane = ScrollPaneFactory.createScrollPane(panel); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_AS_NEEDED); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setBorder(null); + scrollPane.setPreferredSize(new Dimension( + panel.getPreferredSize().width + scrollPane.getVerticalScrollBar().getPreferredSize().width, + panel.getPreferredSize().height + scrollPane.getHorizontalScrollBar().getPreferredSize().height + )); + return scrollPane; + } + + @SuppressWarnings({"NonStaticInitializer"}) + private DialogWrapper createIgnoreExceptionsConfigurationDialog(final Project project, final JTextField specifiedExceptions) { + return new DialogWrapper(true) { + private AddDeleteListPanel myPanel; + + { + setTitle(CodeInsightLocalize.inspectionI18nOptionIgnoreForSpecifiedExceptionConstructorArguments()); + init(); + } - if (JavaI18nUtil.mustBePropertyKey(project, expression, new HashMap<>())) { - return false; - } + @Override + protected JComponent createCenterPanel() { + String[] ignored = ignoreForSpecifiedExceptionConstructors.split(","); + final List initialList = new ArrayList<>(); + if (ignored != null) { + for (String e : ignored) { + if (e.length() > 0) { + initialList.add(e); + } + } + } + myPanel = new AddDeleteListPanel<>(null, initialList) { + @Override + @RequiredUIAccess + protected String findItemToAdd() { + GlobalSearchScope scope = GlobalSearchScope.allScope(project); + TreeClassChooser chooser = TreeClassChooserFactory.getInstance(project).createInheritanceClassChooser( + CodeInsightLocalize.inspectionI18nOptionIgnoreForSpecifiedExceptionConstructorArguments().get(), + scope, + JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_THROWABLE, scope), + true, + true, + null + ); + chooser.showDialog(); + PsiClass selectedClass = chooser.getSelected(); + return selectedClass != null ? selectedClass.getQualifiedName() : null; + } + }; + return myPanel; + } - if (isReturnedFromNonNlsMethod(expression, nonNlsTargets)) { - return false; - } - if (ignoreForAssertStatements && isArgOfAssertStatement(expression)) { - return false; - } - if (ignoreForExceptionConstructors && isArgOfExceptionConstructor(expression)) { - return false; - } - if (ignoreForEnumConstants && isArgOfEnumConstant(expression)) { - return false; + @Override + protected void doOKAction() { + StringBuilder buf = new StringBuilder(); + Object[] exceptions = myPanel.getListItems(); + for (Object exception : exceptions) { + buf.append(",").append(exception); + } + specifiedExceptions.setText(buf.length() > 0 ? buf.substring(1) : buf.toString()); + super.doOKAction(); + } + }; } - if (!ignoreForExceptionConstructors && isArgOfSpecifiedExceptionConstructor(expression, - ignoreForSpecifiedExceptionConstructors.split(","))) { - return false; + + @Nullable + @Override + @RequiredReadAction + public ProblemDescriptor[] checkMethod( + @Nonnull PsiMethod method, + @Nonnull InspectionManager manager, + boolean isOnTheFly, + Object state + ) { + PsiClass containingClass = method.getContainingClass(); + if (containingClass == null || isClassNonNls(containingClass)) { + return null; + } + PsiCodeBlock body = method.getBody(); + if (body != null) { + return checkElement(body, manager, isOnTheFly); + } + return null; } - if (ignoreForJUnitAsserts && isArgOfJUnitAssertion(expression)) { - return false; + + @Nullable + @Override + @RequiredReadAction + public ProblemDescriptor[] checkClass(@Nonnull PsiClass aClass, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { + if (isClassNonNls(aClass)) { + return null; + } + PsiClassInitializer[] initializers = aClass.getInitializers(); + List result = new ArrayList<>(); + for (PsiClassInitializer initializer : initializers) { + ProblemDescriptor[] descriptors = checkElement(initializer, manager, isOnTheFly); + if (descriptors != null) { + ContainerUtil.addAll(result, descriptors); + } + } + + return result.isEmpty() ? null : result.toArray(new ProblemDescriptor[result.size()]); } - if (ignoreForClassReferences && isClassRef(expression, value)) { - return false; + + @Nullable + @Override + @RequiredReadAction + public ProblemDescriptor[] checkField(@Nonnull PsiField field, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { + PsiClass containingClass = field.getContainingClass(); + if (containingClass == null || isClassNonNls(containingClass)) { + return null; + } + if (AnnotationUtil.isAnnotated(field, AnnotationUtil.NON_NLS, false, false)) { + return null; + } + PsiExpression initializer = field.getInitializer(); + if (initializer != null) { + return checkElement(initializer, manager, isOnTheFly); + } + + if (field instanceof PsiEnumConstant enumConstant) { + return checkElement(enumConstant.getArgumentList(), manager, isOnTheFly); + } + return null; } - if (ignoreForPropertyKeyReferences && JavaPropertiesUtil.isPropertyRef(expression, value, null)) { - return false; + + private ProblemDescriptor[] checkElement(PsiElement element, InspectionManager manager, boolean isOnTheFly) { + StringI18nVisitor visitor = new StringI18nVisitor(manager, isOnTheFly); + element.accept(visitor); + List problems = visitor.getProblems(); + return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]); } - if (ignoreToString && isToString(expression)) { - return false; + + private static LocalQuickFix createIntroduceConstantFix() { + return new LocalQuickFix() { + @Override + @Nonnull + public LocalizeValue getName() { + return RefactoringLocalize.introduceConstantTitle(); + } + + @Override + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + //do it later because it is invoked from write action + project.getApplication().invokeLater( + () -> { + if (!(descriptor.getPsiElement() instanceof PsiExpression expression)) { + return; + } + + IntroduceConstantHandler handler = + JavaRefactoringActionHandlerFactory.getInstance().createIntroduceConstantHandler(); + + handler.invoke(project, new PsiExpression[]{expression}); + }, + project.getDisposed() + ); + } + }; } - Pattern pattern = myCachedNonNlsPattern; - if (pattern != null) { - PsiFile file = expression.getContainingFile(); - Document document = PsiDocumentManager.getInstance(project).getDocument(file); - int line = document.getLineNumber(expression.getTextRange().getStartOffset()); - int lineStartOffset = document.getLineStartOffset(line); - CharSequence lineText = document.getCharsSequence().subSequence(lineStartOffset, document.getLineEndOffset(line)); + private class StringI18nVisitor extends JavaRecursiveElementVisitor { + private final List myProblems = new ArrayList<>(); + private final InspectionManager myManager; + private final boolean myOnTheFly; + + public StringI18nVisitor(InspectionManager manager, boolean onTheFly) { + myManager = manager; + myOnTheFly = onTheFly; + } + + @Override + public void visitAnonymousClass(PsiAnonymousClass aClass) { + PsiExpressionList argumentList = aClass.getArgumentList(); + if (argumentList != null) { + argumentList.accept(this); + } + } + + @Override + public void visitClass(@Nonnull PsiClass aClass) { + } + + @Override + public void visitField(@Nonnull PsiField field) { + } + + @Override + public void visitMethod(@Nonnull PsiMethod method) { + } + + @Override + @RequiredReadAction + public void visitLiteralExpression(PsiLiteralExpression expression) { + if (!(expression.getValue() instanceof String stringValue) || stringValue.isBlank()) { + return; + } + + Set nonNlsTargets = new HashSet<>(); + if (canBeI18ned(myManager.getProject(), expression, stringValue, nonNlsTargets)) { + PsiField parentField = PsiTreeUtil.getParentOfType(expression, PsiField.class); + if (parentField != null) { + nonNlsTargets.add(parentField); + } + + LocalizeValue description = CodeInsightLocalize.inspectionI18nMessageGeneralWithValue("#ref"); + + List fixes = new ArrayList<>(); + if (I18nizeConcatenationQuickFix.getEnclosingLiteralConcatenation(expression) != null) { + fixes.add(I18N_CONCATENATION_QUICK_FIX); + } + fixes.add(I18N_QUICK_FIX); + + if (!isNotConstantFieldInitializer(expression)) { + fixes.add(createIntroduceConstantFix()); + } + + Project project = expression.getManager().getProject(); + JavaPsiFacade facade = JavaPsiFacade.getInstance(project); + if (PsiUtil.isLanguageLevel5OrHigher(expression)) { + for (PsiModifierListOwner element : nonNlsTargets) { + if (!AnnotationUtil.isAnnotated(element, AnnotationUtil.NLS, true, false)) { + if (!element.getManager().isInProject(element) + || facade.findClass(AnnotationUtil.NON_NLS, element.getResolveScope()) != null) { + fixes.add(new AddAnnotationFix(AnnotationUtil.NON_NLS, element)); + } + } + } + } + + ProblemDescriptor problem = myManager.newProblemDescriptor(description) + .range(expression) + .onTheFly(myOnTheFly) + .withFixes(fixes) + .create(); + myProblems.add(problem); + } + } + + private boolean isNotConstantFieldInitializer(PsiExpression expression) { + PsiField parentField = expression.getParent() instanceof PsiField field ? field : null; + return parentField != null && expression == parentField.getInitializer() && parentField.isFinal() && parentField.isStatic(); + } + + @Override + public void visitAnnotation(PsiAnnotation annotation) { + //prevent from @SuppressWarnings + if (!BatchSuppressManager.SUPPRESS_INSPECTIONS_ANNOTATION_NAME.equals(annotation.getQualifiedName())) { + super.visitAnnotation(annotation); + } + } - Matcher matcher = pattern.matcher(lineText); - int start = 0; - while (matcher.find(start)) { - start = matcher.start(); - PsiElement element = file.findElementAt(lineStartOffset + start); - if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) return false; - if (start == lineText.length() - 1) break; - start++; - } + public List getProblems() { + return myProblems; + } } - return true; - } + @RequiredReadAction + private boolean canBeI18ned( + @Nonnull Project project, + @Nonnull PsiLiteralExpression expression, + @Nonnull String value, + @Nonnull Set nonNlsTargets + ) { + if (ignoreForNonAlpha && !StringUtil.containsAlphaCharacters(value)) { + return false; + } + + if (JavaI18nUtil.isPassedToAnnotatedParam(project, expression, AnnotationUtil.NON_NLS, new HashMap<>(), nonNlsTargets)) { + return false; + } + + if (isInNonNlsCall(project, expression, nonNlsTargets)) { + return false; + } + + if (isInNonNlsEquals(expression, nonNlsTargets)) { + return false; + } + + if (isPassedToNonNlsVariable(project, expression, nonNlsTargets)) { + return false; + } + + if (JavaI18nUtil.mustBePropertyKey(project, expression, new HashMap<>())) { + return false; + } + + if (isReturnedFromNonNlsMethod(expression, nonNlsTargets)) { + return false; + } + if (ignoreForAssertStatements && isArgOfAssertStatement(expression)) { + return false; + } + if (ignoreForExceptionConstructors && isArgOfExceptionConstructor(expression)) { + return false; + } + if (ignoreForEnumConstants && isArgOfEnumConstant(expression)) { + return false; + } + if (!ignoreForExceptionConstructors && isArgOfSpecifiedExceptionConstructor( + expression, + ignoreForSpecifiedExceptionConstructors.split(",") + )) { + return false; + } + if (ignoreForJUnitAsserts && isArgOfJUnitAssertion(expression)) { + return false; + } + if (ignoreForClassReferences && isClassRef(expression, value)) { + return false; + } + if (ignoreForPropertyKeyReferences && JavaPropertiesUtil.isPropertyRef(expression, value, null)) { + return false; + } + if (ignoreToString && isToString(expression)) { + return false; + } + + Pattern pattern = myCachedNonNlsPattern; + if (pattern != null) { + PsiFile file = expression.getContainingFile(); + Document document = PsiDocumentManager.getInstance(project).getDocument(file); + int line = document.getLineNumber(expression.getTextRange().getStartOffset()); + int lineStartOffset = document.getLineStartOffset(line); + CharSequence lineText = document.getCharsSequence().subSequence(lineStartOffset, document.getLineEndOffset(line)); + + Matcher matcher = pattern.matcher(lineText); + int start = 0; + while (matcher.find(start)) { + start = matcher.start(); + PsiElement element = file.findElementAt(lineStartOffset + start); + if (PsiTreeUtil.getParentOfType(element, PsiComment.class, false) != null) { + return false; + } + if (start == lineText.length() - 1) { + break; + } + start++; + } + } - private boolean isArgOfEnumConstant(PsiLiteralExpression expression) { - final PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class); - if (!(parent instanceof PsiExpressionList)) { - return false; + return true; } - final PsiElement grandparent = parent.getParent(); - return grandparent instanceof PsiEnumConstant; - } - public void cacheNonNlsCommentPattern() { - myCachedNonNlsPattern = nonNlsCommentPattern.trim().length() == 0 ? null : Pattern.compile(nonNlsCommentPattern); - } + private boolean isArgOfEnumConstant(PsiLiteralExpression expression) { + PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class); + return parent instanceof PsiExpressionList expressionList + && expressionList.getParent() instanceof PsiEnumConstant; + } - private static boolean isClassRef(final PsiLiteralExpression expression, String value) { - if (StringUtil.startsWithChar(value, '#')) { - value = value.substring(1); // A favor for JetBrains team to catch common Logger usage practice. + public void cacheNonNlsCommentPattern() { + myCachedNonNlsPattern = nonNlsCommentPattern.trim().length() == 0 ? null : Pattern.compile(nonNlsCommentPattern); } - return JavaPsiFacade.getInstance(expression.getProject()).findClass(value, GlobalSearchScope.allScope(expression.getProject())) != null; - } + private static boolean isClassRef(PsiLiteralExpression expression, String value) { + if (StringUtil.startsWithChar(value, '#')) { + value = value.substring(1); // A favor for JetBrains team to catch common Logger usage practice. + } - @Override - public boolean isEnabledByDefault() { - return false; - } + return JavaPsiFacade.getInstance(expression.getProject()) + .findClass(value, GlobalSearchScope.allScope(expression.getProject())) != null; + } - private static boolean isClassNonNls(@Nonnull PsiClass clazz) { - final PsiDirectory directory = clazz.getContainingFile().getContainingDirectory(); - return directory != null && isPackageNonNls(JavaDirectoryService.getInstance().getPackage(directory)); - } + @Override + public boolean isEnabledByDefault() { + return false; + } - @RequiredReadAction - public static boolean isPackageNonNls(final PsiJavaPackage psiPackage) { - if (psiPackage == null || psiPackage.getName() == null) { - return false; + @RequiredReadAction + private static boolean isClassNonNls(@Nonnull PsiClass clazz) { + PsiDirectory directory = clazz.getContainingFile().getContainingDirectory(); + return directory != null && isPackageNonNls(JavaDirectoryService.getInstance().getPackage(directory)); } - final PsiModifierList pkgModifierList = psiPackage.getAnnotationList(); - return pkgModifierList != null && pkgModifierList.findAnnotation(AnnotationUtil.NON_NLS) != null - || isPackageNonNls(psiPackage.getParentPackage()); - } - @RequiredReadAction - private boolean isPassedToNonNlsVariable( - @Nonnull Project project, - @Nonnull PsiLiteralExpression expression, - final Set nonNlsTargets - ) { - PsiExpression toplevel = JavaI18nUtil.getToplevelExpression(project, expression); - PsiVariable var = null; - if (toplevel instanceof PsiAssignmentExpression assignmentExpression) { - PsiExpression lExpression = assignmentExpression.getLExpression(); - while (lExpression instanceof PsiArrayAccessExpression arrayAccessExpression) { - lExpression = arrayAccessExpression.getArrayExpression(); - } - if (lExpression instanceof PsiReferenceExpression referenceExpression) { - final PsiElement resolved = referenceExpression.resolve(); - if (resolved instanceof PsiVariable variable) var = variable; - } - } - - if (var == null) { - PsiElement parent = toplevel.getParent(); - if (parent instanceof PsiVariable variable && toplevel.equals(variable.getInitializer())) { - var = variable; - } - } - - if (var != null) { - if (annotatedAsNonNls(var)) { - return true; - } - if (ignoreAssignedToConstants && - var.hasModifierProperty(PsiModifier.STATIC) && - var.hasModifierProperty(PsiModifier.FINAL)) { - return true; - } - nonNlsTargets.add(var); - } - return false; - } - - private static boolean annotatedAsNonNls(final PsiModifierListOwner parent) { - if (parent instanceof PsiParameter parameter) { - final PsiElement declarationScope = parameter.getDeclarationScope(); - if (declarationScope instanceof PsiMethod method) { - final int index = method.getParameterList().getParameterIndex(parameter); - return JavaI18nUtil.isMethodParameterAnnotatedWith(method, index, null, AnnotationUtil.NON_NLS, null, null); - } - } - return AnnotationUtil.isAnnotated(parent, AnnotationUtil.NON_NLS, false, false); - } - - private static boolean isInNonNlsEquals(PsiExpression expression, final Set nonNlsTargets) { - if (!(expression.getParent().getParent() instanceof PsiMethodCallExpression)) { - return false; - } - final PsiMethodCallExpression call = (PsiMethodCallExpression)expression.getParent().getParent(); - final PsiReferenceExpression methodExpression = call.getMethodExpression(); - final PsiExpression qualifier = methodExpression.getQualifierExpression(); - if (qualifier != expression) { - return false; - } - if (!"equals".equals(methodExpression.getReferenceName())) { - return false; - } - final PsiElement resolved = methodExpression.resolve(); - if (!(resolved instanceof PsiMethod)) { - return false; - } - PsiType objectType = PsiType.getJavaLangObject(resolved.getManager(), resolved.getResolveScope()); - MethodSignature equalsSignature = MethodSignatureUtil.createMethodSignature( - "equals", - new PsiType[]{objectType}, - PsiTypeParameter.EMPTY_ARRAY, - PsiSubstitutor.EMPTY - ); - if (!equalsSignature.equals(((PsiMethod)resolved).getSignature(PsiSubstitutor.EMPTY))) { - return false; - } - final PsiExpression[] expressions = call.getArgumentList().getExpressions(); - if (expressions.length != 1) { - return false; - } - final PsiExpression arg = expressions[0]; - PsiReferenceExpression ref = null; - if (arg instanceof PsiReferenceExpression referenceExpression) { - ref = referenceExpression; - } - else if (arg instanceof PsiMethodCallExpression methodCallExpression) ref = methodCallExpression.getMethodExpression(); - if (ref != null) { - final PsiElement resolvedEntity = ref.resolve(); - if (resolvedEntity instanceof PsiModifierListOwner modifierListOwner) { - if (annotatedAsNonNls(modifierListOwner)) { - return true; - } - nonNlsTargets.add(modifierListOwner); - } - } - return false; - } - - private static boolean isInNonNlsCall( - @Nonnull Project project, - @Nonnull PsiExpression expression, - final Set nonNlsTargets - ) { - expression = JavaI18nUtil.getToplevelExpression(project, expression); - final PsiElement parent = expression.getParent(); - if (parent instanceof PsiExpressionList) { - final PsiElement grParent = parent.getParent(); - if (grParent instanceof PsiMethodCallExpression methodCallExpression) { - return isNonNlsCall(methodCallExpression, nonNlsTargets); - } - else if (grParent instanceof PsiNewExpression) { - final PsiElement parentOfNew = grParent.getParent(); - if (parentOfNew instanceof PsiLocalVariable newVariable) { - if (annotatedAsNonNls(newVariable)) { - return true; - } - nonNlsTargets.add(newVariable); - return false; - } - else if (parentOfNew instanceof PsiAssignmentExpression assignmentExpression) { - final PsiExpression lExpression = assignmentExpression.getLExpression(); - if (lExpression instanceof PsiReferenceExpression referenceExpression) { - final PsiElement resolved = referenceExpression.resolve(); - if (resolved instanceof PsiModifierListOwner modifierListOwner) { - if (annotatedAsNonNls(modifierListOwner)) { + @RequiredReadAction + public static boolean isPackageNonNls(PsiJavaPackage psiPackage) { + if (psiPackage == null || psiPackage.getName() == null) { + return false; + } + PsiModifierList pkgModifierList = psiPackage.getAnnotationList(); + return pkgModifierList != null && pkgModifierList.findAnnotation(AnnotationUtil.NON_NLS) != null + || isPackageNonNls(psiPackage.getParentPackage()); + } + + @RequiredReadAction + private boolean isPassedToNonNlsVariable( + @Nonnull Project project, + @Nonnull PsiLiteralExpression expression, + Set nonNlsTargets + ) { + PsiExpression topLevel = JavaI18nUtil.getToplevelExpression(project, expression); + PsiVariable var = null; + if (topLevel instanceof PsiAssignmentExpression assignment) { + PsiExpression lExpression = assignment.getLExpression(); + while (lExpression instanceof PsiArrayAccessExpression arrayAccess) { + lExpression = arrayAccess.getArrayExpression(); + } + if (lExpression instanceof PsiReferenceExpression lRefExpr && lRefExpr.resolve() instanceof PsiVariable variable) { + var = variable; + } + } + + if (var == null && topLevel.getParent() instanceof PsiVariable variable && topLevel.equals(variable.getInitializer())) { + var = variable; + } + + if (var != null) { + if (annotatedAsNonNls(var)) { return true; - } - nonNlsTargets.add(modifierListOwner); - return false; } - } + if (ignoreAssignedToConstants + && var.hasModifierProperty(PsiModifier.STATIC) + && var.hasModifierProperty(PsiModifier.FINAL)) { + return true; + } + nonNlsTargets.add(var); } - } + return false; } - return false; - } + private static boolean annotatedAsNonNls(PsiModifierListOwner parent) { + if (parent instanceof PsiParameter param && param.getDeclarationScope() instanceof PsiMethod method) { + int index = method.getParameterList().getParameterIndex(param); + return JavaI18nUtil.isMethodParameterAnnotatedWith(method, index, null, AnnotationUtil.NON_NLS, null, null); + } + return AnnotationUtil.isAnnotated(parent, AnnotationUtil.NON_NLS, false, false); + } - private static boolean isNonNlsCall(PsiMethodCallExpression grParent, Set nonNlsTargets) { - final PsiReferenceExpression methodExpression = grParent.getMethodExpression(); - final PsiExpression qualifier = methodExpression.getQualifierExpression(); - if (qualifier instanceof PsiReferenceExpression referenceExpression) { - final PsiElement resolved = referenceExpression.resolve(); - if (resolved instanceof PsiModifierListOwner modifierListOwner) { - if (annotatedAsNonNls(modifierListOwner)) { - return true; + @RequiredReadAction + private static boolean isInNonNlsEquals(PsiExpression expression, Set nonNlsTargets) { + if (!(expression.getParent().getParent() instanceof PsiMethodCallExpression call)) { + return false; + } + PsiReferenceExpression methodExpression = call.getMethodExpression(); + PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier != expression) { + return false; + } + if (!"equals".equals(methodExpression.getReferenceName())) { + return false; + } + if (!(methodExpression.resolve() instanceof PsiMethod method)) { + return false; + } + PsiType objectType = PsiType.getJavaLangObject(method.getManager(), method.getResolveScope()); + MethodSignature equalsSignature = MethodSignatureUtil.createMethodSignature( + "equals", + new PsiType[]{objectType}, + PsiTypeParameter.EMPTY_ARRAY, + PsiSubstitutor.EMPTY + ); + if (!equalsSignature.equals(method.getSignature(PsiSubstitutor.EMPTY))) { + return false; + } + PsiExpression[] expressions = call.getArgumentList().getExpressions(); + if (expressions.length != 1) { + return false; + } + PsiExpression arg = expressions[0]; + PsiReferenceExpression ref = null; + if (arg instanceof PsiReferenceExpression refExpr) { + ref = refExpr; + } + else if (arg instanceof PsiMethodCallExpression callExpr) { + ref = callExpr.getMethodExpression(); + } + + if (ref != null && ref.resolve() instanceof PsiModifierListOwner modifierListOwner) { + if (annotatedAsNonNls(modifierListOwner)) { + return true; + } + nonNlsTargets.add(modifierListOwner); } - nonNlsTargets.add(modifierListOwner); - return false; - } - } - else if (qualifier instanceof PsiMethodCallExpression methodCallExpression) { - final PsiType type = qualifier.getType(); - if (type != null && type.equals(methodExpression.getType())) { - return isNonNlsCall(methodCallExpression, nonNlsTargets); - } - } - return false; - } - - private static boolean isReturnedFromNonNlsMethod(final PsiLiteralExpression expression, final Set nonNlsTargets) { - PsiElement parent = expression.getParent(); - PsiMethod method; - if (parent instanceof PsiNameValuePair nameValuePair) { - method = AnnotationUtil.getAnnotationMethod(nameValuePair); - } - else { - final PsiElement returnStmt = PsiTreeUtil.getParentOfType(expression, PsiReturnStatement.class, PsiMethodCallExpression.class); - if (returnStmt == null || !(returnStmt instanceof PsiReturnStatement)) { - return false; - } - method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); - } - if (method == null) return false; - - if (AnnotationUtil.isAnnotated(method, AnnotationUtil.NON_NLS, true, false)) { - return true; - } - nonNlsTargets.add(method); - return false; - } - - private static boolean isToString(final PsiLiteralExpression expression) { - final PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); - if (method == null) return false; - final PsiType returnType = method.getReturnType(); - return TO_STRING.equals(method.getName()) - && method.getParameterList().getParametersCount() == 0 - && returnType != null - && CommonClassNames.JAVA_LANG_STRING.equals(returnType.getCanonicalText()); - } - - private static boolean isArgOfJUnitAssertion(PsiExpression expression) { - final PsiElement parent = expression.getParent(); - if (!(parent instanceof PsiExpressionList)) { - return false; - } - final PsiElement grandparent = parent.getParent(); - if (!(grandparent instanceof PsiMethodCallExpression)) { - return false; - } - final PsiMethodCallExpression call = (PsiMethodCallExpression)grandparent; - final PsiReferenceExpression methodExpression = call.getMethodExpression(); - @NonNls final String methodName = methodExpression.getReferenceName(); - if (methodName == null) { - return false; - } - - if (!methodName.startsWith("assert") && !methodName.equals("fail")) { - return false; - } - final PsiMethod method = call.resolveMethod(); - if (method == null) { - return false; - } - final PsiClass containingClass = method.getContainingClass(); - if (containingClass == null) { - return false; - } - final Project project = expression.getProject(); - final GlobalSearchScope scope = GlobalSearchScope.allScope(project); - final PsiClass junitAssert = JavaPsiFacade.getInstance(project).findClass("junit.framework.Assert", scope); - return junitAssert != null && !containingClass.isInheritor(junitAssert, true); - } - - private static boolean isArgOfExceptionConstructor(PsiExpression expression) { - final PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class); - if (!(parent instanceof PsiExpressionList)) { - return false; - } - final PsiElement grandparent = parent.getParent(); - final PsiClass aClass; - if (RefactoringChangeUtil.isSuperOrThisMethodCall(grandparent)) { - final PsiMethod method = ((PsiMethodCallExpression)grandparent).resolveMethod(); - if (method != null) { - aClass = method.getContainingClass(); - } - else { return false; - } } - else { - if (!(grandparent instanceof PsiNewExpression)) { - return false; - } - final PsiJavaCodeReferenceElement reference = ((PsiNewExpression)grandparent).getClassReference(); - if (reference == null) { - return false; - } - final PsiElement referent = reference.resolve(); - if (!(referent instanceof PsiClass)) { + + @RequiredReadAction + private static boolean isInNonNlsCall( + @Nonnull Project project, + @Nonnull PsiExpression expression, + Set nonNlsTargets + ) { + expression = JavaI18nUtil.getToplevelExpression(project, expression); + if (expression.getParent() instanceof PsiExpressionList expressionList) { + PsiElement grParent = expressionList.getParent(); + if (grParent instanceof PsiMethodCallExpression methodCallExpression) { + return isNonNlsCall(methodCallExpression, nonNlsTargets); + } + else if (grParent instanceof PsiNewExpression newExpr) { + PsiElement parentOfNew = newExpr.getParent(); + if (parentOfNew instanceof PsiLocalVariable newVariable) { + if (annotatedAsNonNls(newVariable)) { + return true; + } + nonNlsTargets.add(newVariable); + return false; + } + else if (parentOfNew instanceof PsiAssignmentExpression assignment + && assignment.getLExpression() instanceof PsiReferenceExpression lRefExpr + && lRefExpr.resolve() instanceof PsiModifierListOwner modifierListOwner) { + if (annotatedAsNonNls(modifierListOwner)) { + return true; + } + nonNlsTargets.add(modifierListOwner); + return false; + } + } + } + return false; - } + } - aClass = (PsiClass)referent; + @RequiredReadAction + private static boolean isNonNlsCall(PsiMethodCallExpression grParent, Set nonNlsTargets) { + PsiReferenceExpression methodExpression = grParent.getMethodExpression(); + PsiExpression qualifier = methodExpression.getQualifierExpression(); + if (qualifier instanceof PsiReferenceExpression referenceExpression) { + if (referenceExpression.resolve() instanceof PsiModifierListOwner modifierListOwner) { + if (annotatedAsNonNls(modifierListOwner)) { + return true; + } + nonNlsTargets.add(modifierListOwner); + return false; + } + } + else if (qualifier instanceof PsiMethodCallExpression methodCallExpression) { + PsiType type = qualifier.getType(); + if (type != null && type.equals(methodExpression.getType())) { + return isNonNlsCall(methodCallExpression, nonNlsTargets); + } + } + return false; } - final Project project = expression.getProject(); - final GlobalSearchScope scope = GlobalSearchScope.allScope(project); - final PsiClass throwable = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_THROWABLE, scope); - return throwable != null && aClass.isInheritor(throwable, true); - } - private static boolean isArgOfSpecifiedExceptionConstructor(PsiExpression expression, String[] specifiedExceptions) { - if (specifiedExceptions.length == 0) return false; + private static boolean isReturnedFromNonNlsMethod(PsiLiteralExpression expression, Set nonNlsTargets) { + PsiMethod method; + if (expression.getParent() instanceof PsiNameValuePair nameValuePair) { + method = AnnotationUtil.getAnnotationMethod(nameValuePair); + } + else { + PsiElement returnStmt = PsiTreeUtil.getParentOfType(expression, PsiReturnStatement.class, PsiMethodCallExpression.class); + if (returnStmt == null || !(returnStmt instanceof PsiReturnStatement)) { + return false; + } + method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); + } + if (method == null) { + return false; + } - final PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class); - if (!(parent instanceof PsiExpressionList)) { - return false; - } - final PsiElement grandparent = parent.getParent(); - if (!(grandparent instanceof PsiNewExpression)) { - return false; + if (AnnotationUtil.isAnnotated(method, AnnotationUtil.NON_NLS, true, false)) { + return true; + } + nonNlsTargets.add(method); + return false; } - final PsiJavaCodeReferenceElement reference = - ((PsiNewExpression)grandparent).getClassReference(); - if (reference == null) { - return false; + + private static boolean isToString(PsiLiteralExpression expression) { + PsiMethod method = PsiTreeUtil.getParentOfType(expression, PsiMethod.class); + if (method == null) { + return false; + } + PsiType returnType = method.getReturnType(); + return TO_STRING.equals(method.getName()) + && method.getParameterList().getParametersCount() == 0 + && returnType != null + && CommonClassNames.JAVA_LANG_STRING.equals(returnType.getCanonicalText()); } - final PsiElement referent = reference.resolve(); - if (!(referent instanceof PsiClass)) { - return false; + + private static boolean isArgOfJUnitAssertion(PsiExpression expression) { + if (!(expression.getParent() instanceof PsiExpressionList expressionList)) { + return false; + } + if (!(expressionList.getParent() instanceof PsiMethodCallExpression call)) { + return false; + } + PsiReferenceExpression methodExpression = call.getMethodExpression(); + String methodName = methodExpression.getReferenceName(); + if (methodName == null) { + return false; + } + + if (!methodName.startsWith("assert") && !methodName.equals("fail")) { + return false; + } + PsiMethod method = call.resolveMethod(); + if (method == null) { + return false; + } + PsiClass containingClass = method.getContainingClass(); + if (containingClass == null) { + return false; + } + Project project = expression.getProject(); + GlobalSearchScope scope = GlobalSearchScope.allScope(project); + PsiClass junitAssert = JavaPsiFacade.getInstance(project).findClass(JUnitCommonClassNames.JUNIT_FRAMEWORK_ASSERT, scope); + return junitAssert != null && !containingClass.isInheritor(junitAssert, true); } - final PsiClass aClass = (PsiClass)referent; - for (String specifiedException : specifiedExceptions) { - if (specifiedException.equals(aClass.getQualifiedName())) return true; + @RequiredReadAction + private static boolean isArgOfExceptionConstructor(PsiExpression expression) { + PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class); + if (!(parent instanceof PsiExpressionList expressionList)) { + return false; + } + PsiElement grandparent = expressionList.getParent(); + PsiClass aClass; + if (RefactoringChangeUtil.isSuperOrThisMethodCall(grandparent)) { + PsiMethod method = ((PsiMethodCallExpression) grandparent).resolveMethod(); + if (method != null) { + aClass = method.getContainingClass(); + } + else { + return false; + } + } + else { + if (!(grandparent instanceof PsiNewExpression newExpr)) { + return false; + } + PsiJavaCodeReferenceElement reference = newExpr.getClassReference(); + if (reference == null) { + return false; + } + if (!(reference.resolve() instanceof PsiClass referentClass)) { + return false; + } + aClass = referentClass; + } + Project project = expression.getProject(); + GlobalSearchScope scope = GlobalSearchScope.allScope(project); + PsiClass throwable = JavaPsiFacade.getInstance(project).findClass(CommonClassNames.JAVA_LANG_THROWABLE, scope); + return throwable != null && aClass.isInheritor(throwable, true); } - return false; - } + @RequiredReadAction + private static boolean isArgOfSpecifiedExceptionConstructor(PsiExpression expression, String[] specifiedExceptions) { + if (specifiedExceptions.length == 0) { + return false; + } + + PsiElement parent = PsiTreeUtil.getParentOfType(expression, PsiExpressionList.class, PsiClass.class); + if (!(parent instanceof PsiExpressionList expressionList)) { + return false; + } + if (!(expressionList.getParent() instanceof PsiNewExpression newExpr)) { + return false; + } + PsiJavaCodeReferenceElement reference = newExpr.getClassReference(); + if (reference == null) { + return false; + } + if (!(reference.resolve() instanceof PsiClass aClass)) { + return false; + } + + for (String specifiedException : specifiedExceptions) { + if (specifiedException.equals(aClass.getQualifiedName())) { + return true; + } + } + + return false; + } - private static boolean isArgOfAssertStatement(PsiExpression expression) { - return PsiTreeUtil.getParentOfType(expression, PsiAssertStatement.class, PsiClass.class) instanceof PsiAssertStatement; - } + private static boolean isArgOfAssertStatement(PsiExpression expression) { + return PsiTreeUtil.getParentOfType(expression, PsiAssertStatement.class, PsiClass.class) instanceof PsiAssertStatement; + } } diff --git a/java-properties-impl/src/main/java/consulo/java/properties/impl/psi/UnusedMessageFormatParameterInspection.java b/java-properties-impl/src/main/java/consulo/java/properties/impl/psi/UnusedMessageFormatParameterInspection.java index 5ad41430eb..43dc50db8f 100644 --- a/java-properties-impl/src/main/java/consulo/java/properties/impl/psi/UnusedMessageFormatParameterInspection.java +++ b/java-properties-impl/src/main/java/consulo/java/properties/impl/psi/UnusedMessageFormatParameterInspection.java @@ -20,13 +20,13 @@ import com.intellij.lang.properties.PropertiesLanguage; import com.intellij.lang.properties.psi.PropertiesFile; import com.intellij.lang.properties.psi.Property; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.ide.impl.idea.codeInsight.daemon.impl.quickfix.RenameElementFix; import consulo.language.Language; import consulo.language.ast.ASTNode; import consulo.language.editor.inspection.LocalQuickFix; import consulo.language.editor.inspection.ProblemDescriptor; -import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.psi.PsiElement; import consulo.language.psi.PsiFile; @@ -41,78 +41,101 @@ import java.util.Set; /** - * User: anna - * Date: 07-Sep-2005 + * @since anna + * @since 2005-09-07 */ @ExtensionImpl public class UnusedMessageFormatParameterInspection extends BaseLocalInspectionTool { - public static final String REGEXP = "regexp"; + public static final String REGEXP = "regexp"; - @Nullable - @Override - public Language getLanguage() { - return PropertiesLanguage.INSTANCE; - } + @Nullable + @Override + public Language getLanguage() { + return PropertiesLanguage.INSTANCE; + } - @Nonnull - public LocalizeValue getDisplayName() { - return PropertiesLocalize.unusedMessageFormatParameterDisplayName(); - } + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return PropertiesLocalize.unusedMessageFormatParameterDisplayName(); + } - @Override - @Nonnull - public String getShortName() { - return "UnusedMessageFormatParameter"; - } + @Nonnull + @Override + public String getShortName() { + return "UnusedMessageFormatParameter"; + } - @Nullable - public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - if (!(file instanceof PropertiesFile)) return null; - PropertiesFile propertiesFile = (PropertiesFile) file; - final List properties = propertiesFile.getProperties(); - List problemDescriptors = new ArrayList(); - for (IProperty property : properties) { - String name = property.getName(); - if (name != null) { - if (name.startsWith("log4j")) continue; - if (name.startsWith(REGEXP + ".") || name.endsWith("." + REGEXP)) continue; - } - String value = property.getValue(); - Set parameters = new HashSet(); - if (value != null) { - int index = value.indexOf('{'); - while (index != -1) { - value = value.substring(index + 1); - final int comma = value.indexOf(','); - final int brace = value.indexOf('}'); - if (brace == -1) break; //misformatted string - if (comma == -1) { - index = brace; - } else { - index = Math.min(comma, brace); - } - try { - parameters.add(Integer.valueOf(value.substring(0, index))); - } catch (NumberFormatException e) { - break; - } - index = value.indexOf('{'); + @Nullable + @Override + @RequiredReadAction + public ProblemDescriptor[] checkFile(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { + if (!(file instanceof PropertiesFile)) { + return null; } - for (Integer integer : parameters) { - for (int i = 0; i < integer; i++) { - if (!parameters.contains(i)) { - ASTNode[] nodes = property.getPsiElement().getNode().getChildren(null); - PsiElement valElement = nodes.length < 3 ? property.getPsiElement() : nodes[2].getPsi(); - final String message = PropertiesLocalize.unusedMessageFormatParameterProblemDescriptor(integer.toString(), Integer.toString(i)).get(); - final String propertyKey = property.getKey(); - final LocalQuickFix[] fixes = isOnTheFly ? new LocalQuickFix[]{new RenameElementFix(((Property) property), propertyKey == null ? REGEXP : propertyKey + "." + REGEXP)} : null; - problemDescriptors.add(manager.createProblemDescriptor(valElement, message, isOnTheFly, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING)); - break; + PropertiesFile propertiesFile = (PropertiesFile) file; + List properties = propertiesFile.getProperties(); + List problemDescriptors = new ArrayList<>(); + for (IProperty property : properties) { + String name = property.getName(); + if (name != null) { + if (name.startsWith("log4j")) { + continue; + } + if (name.startsWith(REGEXP + ".") || name.endsWith("." + REGEXP)) { + continue; + } + } + String value = property.getValue(); + Set parameters = new HashSet<>(); + if (value != null) { + int index = value.indexOf('{'); + while (index != -1) { + value = value.substring(index + 1); + int comma = value.indexOf(','); + int brace = value.indexOf('}'); + if (brace == -1) { + break; //misformatted string + } + if (comma == -1) { + index = brace; + } + else { + index = Math.min(comma, brace); + } + try { + parameters.add(Integer.valueOf(value.substring(0, index))); + } + catch (NumberFormatException e) { + break; + } + index = value.indexOf('{'); + } + for (Integer integer : parameters) { + for (int i = 0; i < integer; i++) { + if (!parameters.contains(i)) { + ASTNode[] nodes = property.getPsiElement().getNode().getChildren(null); + PsiElement valElement = nodes.length < 3 ? property.getPsiElement() : nodes[2].getPsi(); + String propertyKey = property.getKey(); + LocalQuickFix fix = isOnTheFly + ? new RenameElementFix(((Property) property), propertyKey == null ? REGEXP : propertyKey + "." + REGEXP) + : null; + problemDescriptors.add( + manager.newProblemDescriptor(PropertiesLocalize.unusedMessageFormatParameterProblemDescriptor( + integer.toString(), + Integer.toString(i) + )) + .range(valElement) + .onTheFly(isOnTheFly) + .withOptionalFix(fix) + .create() + ); + break; + } + } + } } - } } - } + return problemDescriptors.isEmpty() ? null : problemDescriptors.toArray(new ProblemDescriptor[problemDescriptors.size()]); } - return problemDescriptors.isEmpty() ? null : problemDescriptors.toArray(new ProblemDescriptor[problemDescriptors.size()]); - } } diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java index 3f5eee4af0..51446b8069 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/defaultFileTemplateUsage/FileHeaderChecker.java @@ -17,13 +17,13 @@ import com.intellij.java.language.impl.JavaFileType; import com.intellij.java.language.impl.codeInsight.template.JavaTemplateUtil; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.access.RequiredWriteAction; import consulo.fileTemplate.FileTemplate; import consulo.fileTemplate.FileTemplateManager; import consulo.fileTemplate.FileTemplateUtil; import consulo.language.editor.inspection.LocalQuickFix; import consulo.language.editor.inspection.ProblemDescriptor; -import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.localize.InspectionLocalize; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.psi.PsiComment; @@ -47,85 +47,94 @@ * @author cdr */ public class FileHeaderChecker { - private static final Logger LOG = Logger.getInstance(FileHeaderChecker.class); - - static ProblemDescriptor checkFileHeader(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean onTheFly) { - IntObjectMap offsetToProperty = IntMaps.newIntObjectHashMap(); - FileTemplate defaultTemplate = FileTemplateManager.getInstance(file.getProject()).getDefaultTemplate(JavaTemplateUtil.FILE_HEADER_TEMPLATE_NAME); - Pattern pattern = FileTemplateUtil.getTemplatePattern(defaultTemplate, file.getProject(), offsetToProperty); - Matcher matcher = pattern.matcher(file.getViewProvider().getContents()); - if (!matcher.matches()) { - return null; - } - - PsiComment element = PsiTreeUtil.findElementOfClassAtRange(file, matcher.start(1), matcher.end(1), PsiComment.class); - if (element == null) { - return null; - } - - LocalQuickFix[] fixes = createQuickFix(matcher, offsetToProperty, file.getProject(), onTheFly); - String description = InspectionLocalize.defaultFileTemplateDescription().get(); - return manager.createProblemDescriptor(element, description, onTheFly, fixes, ProblemHighlightType.GENERIC_ERROR_OR_WARNING); - } - + private static final Logger LOG = Logger.getInstance(FileHeaderChecker.class); + + @RequiredReadAction + static ProblemDescriptor checkFileHeader(@Nonnull PsiFile file, @Nonnull InspectionManager manager, boolean onTheFly) { + IntObjectMap offsetToProperty = IntMaps.newIntObjectHashMap(); + FileTemplate defaultTemplate = + FileTemplateManager.getInstance(file.getProject()).getDefaultTemplate(JavaTemplateUtil.FILE_HEADER_TEMPLATE_NAME); + Pattern pattern = FileTemplateUtil.getTemplatePattern(defaultTemplate, file.getProject(), offsetToProperty); + Matcher matcher = pattern.matcher(file.getViewProvider().getContents()); + if (!matcher.matches()) { + return null; + } - private static Properties computeProperties(final Matcher matcher, final IntObjectMap offsetToProperty, Project project) { - Properties properties = new Properties(FileTemplateManager.getInstance(project).getDefaultProperties()); + PsiComment element = PsiTreeUtil.findElementOfClassAtRange(file, matcher.start(1), matcher.end(1), PsiComment.class); + if (element == null) { + return null; + } - int[] offsets = offsetToProperty.keys(); - Arrays.sort(offsets); - for (int i = 0; i < offsets.length; i++) { - final int offset = offsets[i]; - String propName = offsetToProperty.get(offset); - int groupNum = i + 2; // first group is whole doc comment - String propValue = matcher.group(groupNum); - properties.setProperty(propName, propValue); + return manager.newProblemDescriptor(InspectionLocalize.defaultFileTemplateDescription()) + .range(element) + .onTheFly(onTheFly) + .withFixes(createQuickFixes(matcher, offsetToProperty, file.getProject(), onTheFly)) + .create(); } - return properties; - } - - private static LocalQuickFix[] createQuickFix(final Matcher matcher, final IntObjectMap offsetToProperty, Project project, boolean onTheFly) { - final FileTemplate template = FileTemplateManager.getInstance(project).getPattern(JavaTemplateUtil.FILE_HEADER_TEMPLATE_NAME); - - ReplaceWithFileTemplateFix replaceTemplateFix = new ReplaceWithFileTemplateFix() { - @Override - @RequiredWriteAction - public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { - PsiElement element = descriptor.getPsiElement(); - if (element == null) { - return; + private static Properties computeProperties(Matcher matcher, IntObjectMap offsetToProperty, Project project) { + Properties properties = new Properties(FileTemplateManager.getInstance(project).getDefaultProperties()); + + int[] offsets = offsetToProperty.keys(); + Arrays.sort(offsets); + for (int i = 0; i < offsets.length; i++) { + int offset = offsets[i]; + String propName = offsetToProperty.get(offset); + int groupNum = i + 2; // first group is whole doc comment + String propValue = matcher.group(groupNum); + properties.setProperty(propName, propValue); } - String newText; - try { - newText = template.getText(computeProperties(matcher, offsetToProperty, project)).trim(); - } catch (IOException e) { - LOG.error(e); - return; - } + return properties; + } - if (!newText.isEmpty()) { - PsiElement parent = element.getParent(); - PsiFile tempFile = PsiFileFactory.getInstance(project).createFileFromText("template.java", JavaFileType.INSTANCE, newText); - for (PsiElement child : tempFile.getChildren()) { - if (child.getTextLength() > 0) { - parent.addBefore(child, element); + private static LocalQuickFix[] createQuickFixes( + final Matcher matcher, + final IntObjectMap offsetToProperty, + Project project, + boolean onTheFly + ) { + final FileTemplate template = FileTemplateManager.getInstance(project).getPattern(JavaTemplateUtil.FILE_HEADER_TEMPLATE_NAME); + + ReplaceWithFileTemplateFix replaceTemplateFix = new ReplaceWithFileTemplateFix() { + @Override + @RequiredWriteAction + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getPsiElement(); + if (element == null) { + return; + } + + String newText; + try { + newText = template.getText(computeProperties(matcher, offsetToProperty, project)).trim(); + } + catch (IOException e) { + LOG.error(e); + return; + } + + if (!newText.isEmpty()) { + PsiElement parent = element.getParent(); + PsiFile tempFile = + PsiFileFactory.getInstance(project).createFileFromText("template.java", JavaFileType.INSTANCE, newText); + for (PsiElement child : tempFile.getChildren()) { + if (child.getTextLength() > 0) { + parent.addBefore(child, element); + } + } + } + + element.delete(); } - } - } + }; - element.delete(); - } - }; - - if (onTheFly) { - LocalQuickFix editFileTemplateFix = DefaultFileTemplateUsageInspection.createEditFileTemplateFix(template, replaceTemplateFix); - return template.isDefault() ? new LocalQuickFix[]{editFileTemplateFix} : new LocalQuickFix[]{ - replaceTemplateFix, - editFileTemplateFix - }; + if (onTheFly) { + LocalQuickFix editFileTemplateFix = DefaultFileTemplateUsageInspection.createEditFileTemplateFix(template, replaceTemplateFix); + return template.isDefault() + ? new LocalQuickFix[]{editFileTemplateFix} + : new LocalQuickFix[]{replaceTemplateFix, editFileTemplateFix}; + } + return template.isDefault() ? LocalQuickFix.EMPTY_ARRAY : new LocalQuickFix[]{replaceTemplateFix}; } - return template.isDefault() ? null : new LocalQuickFix[]{replaceTemplateFix}; - } } diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java index f46630f251..185580e38c 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java @@ -17,6 +17,7 @@ import com.intellij.java.analysis.impl.codeInspection.ex.BaseLocalInspectionTool; import com.intellij.java.language.JavaLanguage; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.dataContext.DataManager; import consulo.ide.impl.idea.packageDependencies.DependenciesBuilder; @@ -25,7 +26,6 @@ import consulo.ide.setting.ShowSettingsUtil; import consulo.language.editor.inspection.LocalQuickFix; import consulo.language.editor.inspection.ProblemDescriptor; -import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.localize.InspectionLocalize; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.packageDependency.DependencyRule; @@ -42,7 +42,7 @@ import javax.swing.*; import java.awt.*; -import java.util.ArrayList; +import java.util.ArrayList;import java.util.List; /** * @author anna @@ -77,7 +77,7 @@ public String getShortName() { @Override public JComponent createOptionsPanel() { - final JButton editDependencies = new JButton(InspectionLocalize.inspectionDependencyConfigureButtonText().get()); + JButton editDependencies = new JButton(InspectionLocalize.inspectionDependencyConfigureButtonText().get()); editDependencies.addActionListener(e -> { Project project = DataManager.getInstance().getDataContext(editDependencies).getData(Project.KEY); if (project == null) { @@ -91,46 +91,47 @@ public JComponent createOptionsPanel() { return depPanel; } - @Override @Nullable + @Override + @RequiredReadAction public ProblemDescriptor[] checkFile( - @Nonnull final PsiFile file, - @Nonnull final InspectionManager manager, - final boolean isOnTheFly, + @Nonnull PsiFile file, + @Nonnull InspectionManager manager, + boolean isOnTheFly, Object state ) { - if (file == null) { - return null; - } if (file.getViewProvider().getPsi(JavaLanguage.INSTANCE) == null) { return null; } - final DependencyValidationManager validationManager = DependencyValidationManager.getInstance(file.getProject()); + DependencyValidationManager validationManager = DependencyValidationManager.getInstance(file.getProject()); if (!validationManager.hasRules()) { return null; } if (validationManager.getApplicableRules(file).length == 0) { return null; } - final ArrayList problems = new ArrayList<>(); + List problems = new ArrayList<>(); ForwardDependenciesBuilder builder = new ForwardDependenciesBuilder(file.getProject(), new AnalysisScope(file)); - DependenciesBuilder.analyzeFileDependencies(file, (place, dependency) -> { - PsiFile dependencyFile = dependency.getContainingFile(); - if (dependencyFile != null && dependencyFile.isPhysical() && dependencyFile.getVirtualFile() != null) { - final DependencyRule[] rule = validationManager.getViolatorDependencyRules(file, dependencyFile); - for (DependencyRule dependencyRule : rule) { - StringBuilder message = new StringBuilder(); - message.append(InspectionLocalize.inspectionDependencyViolatorProblemDescriptor(dependencyRule.getDisplayText())); - problems.add(manager.createProblemDescriptor( - place, - message.toString(), - isOnTheFly, - new LocalQuickFix[]{new EditDependencyRulesAction(dependencyRule)}, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING - )); + DependenciesBuilder.analyzeFileDependencies( + file, + (place, dependency) -> { + PsiFile dependencyFile = dependency.getContainingFile(); + if (dependencyFile != null && dependencyFile.isPhysical() && dependencyFile.getVirtualFile() != null) { + DependencyRule[] rule = validationManager.getViolatorDependencyRules(file, dependencyFile); + for (DependencyRule dependencyRule : rule) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionDependencyViolatorProblemDescriptor( + dependencyRule.getDisplayText() + )) + .range(place) + .onTheFly(isOnTheFly) + .withFix(new EditDependencyRulesAction(dependencyRule)) + .create() + ); + } } } - }); + ); return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]); } @@ -159,5 +160,4 @@ public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descri ShowSettingsUtil.getInstance().editConfigurable(project, new DependencyConfigurable(project)); } } - } diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java index 47d4ae9268..e87acc4432 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java @@ -28,7 +28,6 @@ import consulo.annotation.access.RequiredReadAction; import consulo.annotation.access.RequiredWriteAction; import consulo.annotation.component.ExtensionImpl; -import consulo.application.Application; import consulo.application.util.query.Query; import consulo.java.analysis.impl.localize.JavaQuickFixLocalize; import consulo.java.deadCodeNotWorking.OldStyleInspection; @@ -53,7 +52,6 @@ import consulo.util.xml.serializer.JDOMExternalizableStringList; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import org.jetbrains.annotations.NonNls; import javax.swing.*; import java.awt.*; @@ -66,29 +64,27 @@ */ @ExtensionImpl public class EmptyMethodInspection extends GlobalJavaInspectionTool implements OldStyleInspection { - @NonNls private static final String SHORT_NAME = "EmptyMethod"; - private final BidirectionalMap myQuickFixes = new BidirectionalMap(); + private final BidirectionalMap myQuickFixes = new BidirectionalMap<>(); public final JDOMExternalizableStringList EXCLUDE_ANNOS = new JDOMExternalizableStringList(); private static final Logger LOG = Logger.getInstance(EmptyMethodInspection.class); - @Override @Nullable + @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( @Nonnull RefEntity refEntity, @Nonnull AnalysisScope scope, @Nonnull InspectionManager manager, @Nonnull GlobalInspectionContext globalContext, @Nonnull ProblemDescriptionsProcessor processor, - Object state + @Nonnull Object state ) { - if (!(refEntity instanceof RefMethod)) { + if (!(refEntity instanceof RefMethod refMethod)) { return null; } - final RefMethod refMethod = (RefMethod) refEntity; - if (!isBodyEmpty(refMethod)) { return null; } @@ -105,75 +101,73 @@ public CommonProblemDescriptor[] checkElement( } } - String message = null; + LocalizeValue message = LocalizeValue.empty(); boolean needToDeleteHierarchy = false; if (refMethod.isOnlyCallsSuper() && !refMethod.isFinal()) { RefMethod refSuper = findSuperWithBody(refMethod); - final RefJavaUtil refUtil = RefJavaUtil.getInstance(); + RefJavaUtil refUtil = RefJavaUtil.getInstance(); if (refSuper != null && Comparing.strEqual(refMethod.getAccessModifier(), refSuper.getAccessModifier())) { - if (Comparing.strEqual(refSuper.getAccessModifier(), PsiModifier.PROTECTED) //protected modificator gives access to method in another package + //protected modificator gives access to method in another package + if (Comparing.strEqual(refSuper.getAccessModifier(), PsiModifier.PROTECTED) && !Comparing.strEqual(refUtil.getPackageName(refSuper), refUtil.getPackageName(refMethod))) { return null; } - final PsiModifierListOwner modifierListOwner = refMethod.getElement(); + PsiModifierListOwner modifierListOwner = refMethod.getElement(); if (modifierListOwner != null) { - final PsiModifierList list = modifierListOwner.getModifierList(); + PsiModifierList list = modifierListOwner.getModifierList(); if (list != null) { - final PsiModifierListOwner supMethod = refSuper.getElement(); - if (supMethod != null) { - final PsiModifierList superModifiedList = supMethod.getModifierList(); - LOG.assertTrue(superModifiedList != null); - if (list.hasModifierProperty(PsiModifier.SYNCHRONIZED) && !superModifiedList.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { - return null; - } + PsiModifierListOwner supMethod = refSuper.getElement(); + if (supMethod != null && list.hasModifierProperty(PsiModifier.SYNCHRONIZED) + && !supMethod.hasModifierProperty(PsiModifier.SYNCHRONIZED)) { + return null; } } } } if (refSuper == null || refUtil.compareAccess(refMethod.getAccessModifier(), refSuper.getAccessModifier()) <= 0) { - message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor().get(); + message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor(); } } else if (refMethod.hasBody() && hasEmptySuperImplementation(refMethod)) { - - message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor1().get(); + message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor1(); } else if (areAllImplementationsEmpty(refMethod)) { if (refMethod.hasBody()) { if (refMethod.getDerivedMethods().isEmpty()) { if (refMethod.getSuperMethods().isEmpty()) { - message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor2().get(); + message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor2(); } } else { needToDeleteHierarchy = true; - message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor3().get(); + message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor3(); } } else if (!refMethod.getDerivedMethods().isEmpty()) { needToDeleteHierarchy = true; - message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor4().get(); + message = InspectionLocalize.inspectionEmptyMethodProblemDescriptor4(); } } if (message != null) { - final ArrayList fixes = new ArrayList(); + List fixes = new ArrayList<>(); fixes.add(getFix(processor, needToDeleteHierarchy)); - SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes(refMethod.getElement(), qualifiedName -> { - fixes.add(SpecialAnnotationsUtilBase.createAddToSpecialAnnotationsListQuickFix( - JavaQuickFixLocalize.fixAddSpecialAnnotationText(qualifiedName), - JavaQuickFixLocalize.fixAddSpecialAnnotationFamily(), - EXCLUDE_ANNOS, qualifiedName, refMethod.getElement())); - return true; - }); - - final ProblemDescriptor descriptor = manager.createProblemDescriptor( - refMethod.getElement().getNavigationElement(), - message, - false, - fixes.toArray(new LocalQuickFix[fixes.size()]), - ProblemHighlightType.GENERIC_ERROR_OR_WARNING + SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( + refMethod.getElement(), + qualifiedName -> { + fixes.add(SpecialAnnotationsUtilBase.createAddToSpecialAnnotationsListQuickFix( + JavaQuickFixLocalize.fixAddSpecialAnnotationText(qualifiedName), + JavaQuickFixLocalize.fixAddSpecialAnnotationFamily(), + EXCLUDE_ANNOS, qualifiedName, refMethod.getElement() + )); + return true; + } ); + + ProblemDescriptor descriptor = manager.newProblemDescriptor(message) + .range(refMethod.getElement().getNavigationElement()) + .withFixes(fixes) + .create(); return new ProblemDescriptor[]{descriptor}; } @@ -181,15 +175,15 @@ else if (!refMethod.getDerivedMethods().isEmpty()) { } @RequiredReadAction - private boolean isBodyEmpty(final RefMethod refMethod) { + private boolean isBodyEmpty(RefMethod refMethod) { if (!refMethod.isBodyEmpty()) { return false; } - final PsiModifierListOwner owner = refMethod.getElement(); + PsiModifierListOwner owner = refMethod.getElement(); if (owner == null || AnnotationUtil.isAnnotated(owner, EXCLUDE_ANNOS)) { return false; } - for (ImplicitMethodBodyProvider provider : Application.get().getExtensionPoint(ImplicitMethodBodyProvider.class)) { + for (ImplicitMethodBodyProvider provider : owner.getApplication().getExtensionPoint(ImplicitMethodBodyProvider.class)) { if (provider.hasImplicitMethodBody(refMethod)) { return false; } @@ -208,6 +202,7 @@ private static RefMethod findSuperWithBody(RefMethod refMethod) { return null; } + @RequiredReadAction private boolean areAllImplementationsEmpty(RefMethod refMethod) { if (refMethod.hasBody() && !isBodyEmpty(refMethod)) { return false; @@ -222,6 +217,7 @@ private boolean areAllImplementationsEmpty(RefMethod refMethod) { return true; } + @RequiredReadAction private boolean hasEmptySuperImplementation(RefMethod refMethod) { for (RefMethod refSuper : refMethod.getSuperMethods()) { if (refSuper.hasBody() && isBodyEmpty(refSuper)) { @@ -234,7 +230,7 @@ private boolean hasEmptySuperImplementation(RefMethod refMethod) { @Override protected boolean queryExternalUsagesRequests( - @Nonnull final RefManager manager, + @Nonnull RefManager manager, @Nonnull final GlobalJavaInspectionContext context, @Nonnull final ProblemDescriptionsProcessor descriptionsProcessor, Object state @@ -245,7 +241,7 @@ public void visitElement(@Nonnull RefEntity refEntity) { if (refEntity instanceof RefElement && descriptionsProcessor.getDescriptions(refEntity) != null) { refEntity.accept(new RefJavaVisitor() { @Override - public void visitMethod(@Nonnull final RefMethod refMethod) { + public void visitMethod(@Nonnull RefMethod refMethod) { context.enqueueDerivedMethodsProcessor(refMethod, derivedMethod -> { PsiCodeBlock body = derivedMethod.getBody(); if (body == null || body.getStatements().length == 0 @@ -283,7 +279,7 @@ public String getShortName() { return SHORT_NAME; } - private LocalQuickFix getFix(final ProblemDescriptionsProcessor processor, final boolean needToDeleteHierarchy) { + private LocalQuickFix getFix(ProblemDescriptionsProcessor processor, boolean needToDeleteHierarchy) { QuickFix fix = myQuickFixes.get(needToDeleteHierarchy); if (fix == null) { fix = new DeleteMethodQuickFix(processor, needToDeleteHierarchy); @@ -294,8 +290,8 @@ private LocalQuickFix getFix(final ProblemDescriptionsProcessor processor, final } @Override - public String getHint(@Nonnull final QuickFix fix) { - final List list = myQuickFixes.getKeysByValue(fix); + public String getHint(@Nonnull QuickFix fix) { + List list = myQuickFixes.getKeysByValue(fix); if (list != null) { LOG.assertTrue(list.size() == 1); return String.valueOf(list.get(0)); @@ -303,21 +299,21 @@ public String getHint(@Nonnull final QuickFix fix) { return null; } - @Override @Nullable - public LocalQuickFix getQuickFix(final String hint) { + @Override + public LocalQuickFix getQuickFix(String hint) { return new DeleteMethodIntention(hint); } - @Override @Nullable + @Override public JComponent createOptionsPanel() { - final JPanel listPanel = SpecialAnnotationsUtil.createSpecialAnnotationsListControl( + JPanel listPanel = SpecialAnnotationsUtil.createSpecialAnnotationsListControl( EXCLUDE_ANNOS, InspectionLocalize.specialAnnotationsAnnotationsList().get() ); - final JPanel panel = new JPanel(new BorderLayout(2, 2)); + JPanel panel = new JPanel(new BorderLayout(2, 2)); panel.add(listPanel, BorderLayout.CENTER); return panel; } @@ -325,7 +321,7 @@ public JComponent createOptionsPanel() { private class DeleteMethodIntention implements LocalQuickFix { private final String myHint; - public DeleteMethodIntention(final String hint) { + public DeleteMethodIntention(String hint) { myHint = hint; } @@ -337,13 +333,13 @@ public LocalizeValue getName() { @Override @RequiredWriteAction - public void applyFix(@Nonnull final Project project, @Nonnull final ProblemDescriptor descriptor) { - final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethod.class, false); + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + PsiMethod psiMethod = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiMethod.class, false); if (psiMethod != null) { - final List psiElements = new ArrayList<>(); + List psiElements = new ArrayList<>(); psiElements.add(psiMethod); if (Boolean.valueOf(myHint)) { - final Query> query = AllOverridingMethodsSearch.search(psiMethod.getContainingClass()); + Query> query = AllOverridingMethodsSearch.search(psiMethod.getContainingClass()); query.forEach(pair -> { if (pair.first == psiMethod) { psiElements.add(pair.second); @@ -364,7 +360,7 @@ private class DeleteMethodQuickFix implements LocalQuickFix, BatchQuickFix(), null); } @@ -401,10 +397,10 @@ private void deleteMethod(RefMethod refMethod, List result) { @Override public void applyFix( - @Nonnull final Project project, - @Nonnull final CommonProblemDescriptor[] descriptors, - final List psiElementsToIgnore, - final Runnable refreshViews + @Nonnull Project project, + @Nonnull CommonProblemDescriptor[] descriptors, + List psiElementsToIgnore, + Runnable refreshViews ) { for (CommonProblemDescriptor descriptor : descriptors) { RefElement refElement = (RefElement) myProcessor.getElement(descriptor); diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/inheritance/SuperClassHasFrequentlyUsedInheritorsInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/inheritance/SuperClassHasFrequentlyUsedInheritorsInspection.java index 1223c92480..b80d3dbcfd 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/inheritance/SuperClassHasFrequentlyUsedInheritorsInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/inheritance/SuperClassHasFrequentlyUsedInheritorsInspection.java @@ -4,6 +4,7 @@ import com.intellij.java.impl.codeInspection.inheritance.search.InheritorsStatisticalDataSearch; import com.intellij.java.impl.codeInspection.inheritance.search.InheritorsStatisticsSearchResult; import com.intellij.java.language.psi.*; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.LocalQuickFix; import consulo.language.editor.inspection.ProblemDescriptor; @@ -47,35 +48,37 @@ public boolean isEnabledByDefault() { @Nullable @Override + @RequiredReadAction public ProblemDescriptor[] checkClass( - @Nonnull final PsiClass aClass, - @Nonnull final InspectionManager manager, - final boolean isOnTheFly, Object state + @Nonnull PsiClass aClass, + @Nonnull InspectionManager manager, + boolean isOnTheFly, + Object state ) { - if (aClass.isInterface() || - aClass instanceof PsiTypeParameter || - aClass.getMethods().length != 0 || - aClass.hasModifierProperty(PsiModifier.ABSTRACT)) { + if (aClass.isInterface() + || aClass instanceof PsiTypeParameter + || aClass.getMethods().length != 0 + || aClass.isAbstract()) { return null; } - final PsiClass superClass = getSuperIfUnique(aClass); + PsiClass superClass = getSuperIfUnique(aClass); if (superClass == null) { return null; } - final List topInheritors = + List topInheritors = InheritorsStatisticalDataSearch.search(superClass, aClass, aClass.getResolveScope(), MIN_PERCENT_RATIO); if (topInheritors.isEmpty()) { return null; } - final Collection topInheritorsQuickFix = new ArrayList(topInheritors.size()); + Collection topInheritorsQuickFix = new ArrayList<>(topInheritors.size()); boolean isFirst = true; - for (final InheritorsStatisticsSearchResult searchResult : topInheritors) { - final LocalQuickFix quickFix; + for (InheritorsStatisticsSearchResult searchResult : topInheritors) { + LocalQuickFix quickFix; if (isFirst) { quickFix = new ChangeSuperClassFix(searchResult.getPsiClass(), searchResult.getPercent(), superClass); isFirst = false; @@ -88,24 +91,26 @@ public ProblemDescriptor[] checkClass( break; } } - return new ProblemDescriptor[]{manager - .createProblemDescriptor( - aClass, - "Class may extend a commonly used base class instead of implementing interface or extending abstract class", - false, - topInheritorsQuickFix.toArray(new LocalQuickFix[topInheritorsQuickFix.size()]), - ProblemHighlightType.INFORMATION - )}; + return new ProblemDescriptor[]{ + manager.newProblemDescriptor(LocalizeValue.localizeTODO( + "Class may extend a commonly used base class instead of implementing interface or extending abstract class" + )) + .range(aClass) + .highlightType(ProblemHighlightType.INFORMATION) + .withFixes(topInheritorsQuickFix) + .create() + }; } @Nullable - private static PsiClass getSuperIfUnique(final @Nonnull PsiClass aClass) { - if (aClass instanceof PsiAnonymousClass) { - return (PsiClass) ((PsiAnonymousClass) aClass).getBaseClassReference().resolve(); + @RequiredReadAction + private static PsiClass getSuperIfUnique(@Nonnull PsiClass aClass) { + if (aClass instanceof PsiAnonymousClass anonymousClass) { + return (PsiClass) anonymousClass.getBaseClassReference().resolve(); } - final PsiReferenceList extendsList = aClass.getExtendsList(); + PsiReferenceList extendsList = aClass.getExtendsList(); if (extendsList != null) { - final PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements(); + PsiJavaCodeReferenceElement[] referenceElements = extendsList.getReferenceElements(); if (referenceElements.length == 1) { PsiClass returnClass = (PsiClass) referenceElements[0].resolve(); if (returnClass != null @@ -116,9 +121,9 @@ private static PsiClass getSuperIfUnique(final @Nonnull PsiClass aClass) { } } - final PsiReferenceList implementsList = aClass.getImplementsList(); + PsiReferenceList implementsList = aClass.getImplementsList(); if (implementsList != null) { - final PsiJavaCodeReferenceElement[] referenceElements = implementsList.getReferenceElements(); + PsiJavaCodeReferenceElement[] referenceElements = implementsList.getReferenceElements(); if (referenceElements.length == 1) { PsiClass returnClass = (PsiClass) referenceElements[0].resolve(); if (returnClass != null && returnClass.isInterface()) { diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocLocalInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocLocalInspection.java index d2aef8b7da..304014f601 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocLocalInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocLocalInspection.java @@ -13,17 +13,16 @@ import com.intellij.java.language.psi.util.InheritanceUtil; import com.intellij.java.language.psi.util.PropertyUtil; import consulo.annotation.access.RequiredReadAction; +import consulo.annotation.access.RequiredWriteAction; import consulo.annotation.component.ExtensionImpl; -import consulo.application.Application; import consulo.codeEditor.Editor; import consulo.codeEditor.ScrollType; +import consulo.component.extension.ExtensionPoint; import consulo.fileEditor.FileEditorManager; import consulo.java.analysis.impl.localize.JavaQuickFixLocalize; import consulo.language.ast.ASTNode; -import consulo.language.editor.inspection.InspectionsBundle; import consulo.language.editor.inspection.LocalQuickFix; import consulo.language.editor.inspection.ProblemDescriptor; -import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.localize.InspectionLocalize; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.inspection.scheme.InspectionProfile; @@ -47,7 +46,6 @@ import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; import org.jdom.Element; -import org.jetbrains.annotations.NonNls; import javax.swing.*; import javax.swing.event.ChangeEvent; @@ -56,1311 +54,1360 @@ import javax.swing.text.BadLocationException; import javax.swing.text.Document; import java.awt.*; +import java.util.List; import java.util.*; @ExtensionImpl public class JavaDocLocalInspection extends BaseLocalInspectionTool { - @NonNls private static final String NONE = "none"; - @NonNls private static final String PUBLIC = "public"; - @NonNls private static final String PROTECTED = "protected"; - @NonNls private static final String PACKAGE_LOCAL = "package"; - @NonNls private static final String PRIVATE = "private"; - @NonNls private static final Set ourUniqueTags = new HashSet<>(); - @NonNls public static final String SHORT_NAME = "JavaDoc"; - - static { - ourUniqueTags.add("return"); - ourUniqueTags.add("deprecated"); - ourUniqueTags.add("serial"); - ourUniqueTags.add("serialData"); - } - - private static final String IGNORE_ACCESSORS_ATTR_NAME = "IGNORE_ACCESSORS"; - - public static class Options implements JDOMExternalizable { - @NonNls public String ACCESS_JAVADOC_REQUIRED_FOR = NONE; - @NonNls public String REQUIRED_TAGS = ""; - - public Options() { - } + private static final String NONE = "none"; + private static final String PUBLIC = "public"; + private static final String PROTECTED = "protected"; + private static final String PACKAGE_LOCAL = "package"; + private static final String PRIVATE = "private"; + private static final Set ourUniqueTags = new HashSet<>(); + public static final String SHORT_NAME = "JavaDoc"; - public Options(String ACCESS_JAVADOC_REQUIRED_FOR, String REQUIRED_TAGS) { - this.ACCESS_JAVADOC_REQUIRED_FOR = ACCESS_JAVADOC_REQUIRED_FOR; - this.REQUIRED_TAGS = REQUIRED_TAGS; + static { + ourUniqueTags.add("return"); + ourUniqueTags.add("deprecated"); + ourUniqueTags.add("serial"); + ourUniqueTags.add("serialData"); } - @Override - public void readExternal(Element element) throws InvalidDataException { - DefaultJDOMExternalizer.readExternal(this, element); - } - - @Override - public void writeExternal(Element element) throws WriteExternalException { - DefaultJDOMExternalizer.writeExternal(this, element); - } - } + private static final String IGNORE_ACCESSORS_ATTR_NAME = "IGNORE_ACCESSORS"; - @NonNls public Options TOP_LEVEL_CLASS_OPTIONS = new Options("none", ""); - @NonNls public Options INNER_CLASS_OPTIONS = new Options("none", ""); - @NonNls public Options METHOD_OPTIONS = new Options("none", "@return@param@throws or @exception"); - @NonNls public Options FIELD_OPTIONS = new Options("none", ""); - public boolean IGNORE_DEPRECATED = false; - public boolean IGNORE_JAVADOC_PERIOD = true; - public boolean IGNORE_DUPLICATED_THROWS = false; - public boolean IGNORE_POINT_TO_ITSELF = false; - public String myAdditionalJavadocTags = ""; + public static class Options implements JDOMExternalizable { + public String ACCESS_JAVADOC_REQUIRED_FOR = NONE; + public String REQUIRED_TAGS = ""; - private boolean myIgnoreEmptyDescriptions = false; - private boolean myIgnoreSimpleAccessors = false; + public Options() { + } - public void setIgnoreSimpleAccessors(boolean ignoreSimpleAccessors) { - myIgnoreSimpleAccessors = ignoreSimpleAccessors; - } + public Options(String ACCESS_JAVADOC_REQUIRED_FOR, String REQUIRED_TAGS) { + this.ACCESS_JAVADOC_REQUIRED_FOR = ACCESS_JAVADOC_REQUIRED_FOR; + this.REQUIRED_TAGS = REQUIRED_TAGS; + } - private static final Logger LOG = Logger.getInstance(JavaDocLocalInspection.class); + @Override + public void readExternal(Element element) throws InvalidDataException { + DefaultJDOMExternalizer.readExternal(this, element); + } - private class OptionsPanel extends JPanel { - private JPanel createOptionsPanel(String[] modifiers, String[] tags, Options options) { - JPanel pane = new JPanel(new GridLayout(1, tags == null ? 1 : 2)); + @Override + public void writeExternal(Element element) throws WriteExternalException { + DefaultJDOMExternalizer.writeExternal(this, element); + } + } - pane.add(createScopePanel(modifiers, options)); - if (tags != null) { - pane.add(createTagsPanel(tags, options)); - } + public Options TOP_LEVEL_CLASS_OPTIONS = new Options("none", ""); + public Options INNER_CLASS_OPTIONS = new Options("none", ""); + public Options METHOD_OPTIONS = new Options("none", "@return@param@throws or @exception"); + public Options FIELD_OPTIONS = new Options("none", ""); + public boolean IGNORE_DEPRECATED = false; + public boolean IGNORE_JAVADOC_PERIOD = true; + public boolean IGNORE_DUPLICATED_THROWS = false; + public boolean IGNORE_POINT_TO_ITSELF = false; + public String myAdditionalJavadocTags = ""; - pane.validate(); + private boolean myIgnoreEmptyDescriptions = false; + private boolean myIgnoreSimpleAccessors = false; - return pane; + public void setIgnoreSimpleAccessors(boolean ignoreSimpleAccessors) { + myIgnoreSimpleAccessors = ignoreSimpleAccessors; } - private JPanel createTagsPanel(String[] tags, Options options) { - JPanel panel = new JPanel(new GridBagLayout()); - panel.setBorder(BorderFactory.createCompoundBorder( - IdeBorderFactory.createTitledBorder(InspectionLocalize.inspectionJavadocRequiredTagsOptionTitle().get(), true), - BorderFactory.createEmptyBorder(0, 3, 3, 3) - )); - - GridBagConstraints gc = new GridBagConstraints(); - gc.weightx = 1; - gc.weighty = 0; - gc.fill = GridBagConstraints.HORIZONTAL; - gc.anchor = GridBagConstraints.NORTHWEST; - - - for (int i = 0; i < tags.length; i++) { - JCheckBox box = new JCheckBox(tags[i]); - gc.gridy = i; - if (i == tags.length - 1) gc.weighty = 1; - panel.add(box, gc); - box.setSelected(isTagRequired(options, tags[i])); - box.addChangeListener(new MyChangeListener(box, options, tags[i])); - } - - return panel; - } + private static final Logger LOG = Logger.getInstance(JavaDocLocalInspection.class); - private class MyChangeListener implements ChangeListener { - private final JCheckBox myCheckBox; - private final Options myOptions; - private final String myTagName; + private class OptionsPanel extends JPanel { + private JPanel createOptionsPanel(String[] modifiers, String[] tags, Options options) { + JPanel pane = new JPanel(new GridLayout(1, tags == null ? 1 : 2)); - public MyChangeListener(JCheckBox checkBox, Options options, String tagName) { - myCheckBox = checkBox; - myOptions = options; - myTagName = tagName; - } + pane.add(createScopePanel(modifiers, options)); + if (tags != null) { + pane.add(createTagsPanel(tags, options)); + } - @Override - public void stateChanged(ChangeEvent e) { - if (myCheckBox.isSelected()) { - if (!isTagRequired(myOptions, myTagName)) { - myOptions.REQUIRED_TAGS += myTagName; - } - } - else { - myOptions.REQUIRED_TAGS = myOptions.REQUIRED_TAGS.replaceAll(myTagName, ""); + pane.validate(); + + return pane; } - } - } - private JPanel createScopePanel(final String[] modifiers, final Options options) { - JPanel panel = new JPanel(new BorderLayout()); - panel.setBorder(BorderFactory.createCompoundBorder( - IdeBorderFactory.createTitledBorder(InspectionLocalize.inspectionScopeForTitle().get(), true), - BorderFactory.createEmptyBorder(0, 3, 3, 3) - )); + private JPanel createTagsPanel(String[] tags, Options options) { + JPanel panel = new JPanel(new GridBagLayout()); + panel.setBorder(BorderFactory.createCompoundBorder( + IdeBorderFactory.createTitledBorder(InspectionLocalize.inspectionJavadocRequiredTagsOptionTitle().get(), true), + BorderFactory.createEmptyBorder(0, 3, 3, 3) + )); + + GridBagConstraints gc = new GridBagConstraints(); + gc.weightx = 1; + gc.weighty = 0; + gc.fill = GridBagConstraints.HORIZONTAL; + gc.anchor = GridBagConstraints.NORTHWEST; - final Hashtable sliderLabels = new Hashtable<>(); - for (int i = 0; i < modifiers.length; i++) { - sliderLabels.put(i + 1, new JLabel(modifiers[i])); - } - final JSlider slider = new JSlider(SwingConstants.VERTICAL, 1, modifiers.length, 1); + for (int i = 0; i < tags.length; i++) { + JCheckBox box = new JCheckBox(tags[i]); + gc.gridy = i; + if (i == tags.length - 1) { + gc.weighty = 1; + } + panel.add(box, gc); + box.setSelected(isTagRequired(options, tags[i])); + box.addChangeListener(new MyChangeListener(box, options, tags[i])); + } - slider.setLabelTable(sliderLabels); - slider.putClientProperty(UIUtil.JSLIDER_ISFILLED, Boolean.TRUE); - slider.setPreferredSize(new Dimension(80, 50)); - slider.setPaintLabels(true); - slider.setSnapToTicks(true); - slider.addChangeListener(e -> { - int value = slider.getValue(); - options.ACCESS_JAVADOC_REQUIRED_FOR = modifiers[value - 1]; - for (Integer key : sliderLabels.keySet()) { - sliderLabels.get(key).setForeground(key <= value ? Color.black : Gray._100); + return panel; } - }); - Color fore = Color.black; - for (int i = 0; i < modifiers.length; i++) { - sliderLabels.get(i + 1).setForeground(fore); + private class MyChangeListener implements ChangeListener { + private final JCheckBox myCheckBox; + private final Options myOptions; + private final String myTagName; + + public MyChangeListener(JCheckBox checkBox, Options options, String tagName) { + myCheckBox = checkBox; + myOptions = options; + myTagName = tagName; + } - if (modifiers[i].equals(options.ACCESS_JAVADOC_REQUIRED_FOR)) { - slider.setValue(i + 1); - fore = Gray._100; + @Override + public void stateChanged(ChangeEvent e) { + if (myCheckBox.isSelected()) { + if (!isTagRequired(myOptions, myTagName)) { + myOptions.REQUIRED_TAGS += myTagName; + } + } + else { + myOptions.REQUIRED_TAGS = myOptions.REQUIRED_TAGS.replaceAll(myTagName, ""); + } + } } - } - panel.add(slider, BorderLayout.WEST); + private JPanel createScopePanel(String[] modifiers, Options options) { + JPanel panel = new JPanel(new BorderLayout()); + panel.setBorder(BorderFactory.createCompoundBorder( + IdeBorderFactory.createTitledBorder(InspectionLocalize.inspectionScopeForTitle().get(), true), + BorderFactory.createEmptyBorder(0, 3, 3, 3) + )); - return panel; - } + Hashtable sliderLabels = new Hashtable<>(); + for (int i = 0; i < modifiers.length; i++) { + sliderLabels.put(i + 1, new JLabel(modifiers[i])); + } - public OptionsPanel() { - super(new GridBagLayout()); - GridBagConstraints gc = new GridBagConstraints( - 0, GridBagConstraints.RELATIVE, 2, 1, 1, 0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, - JBUI.emptyInsets(),0,0 - ); - add(createAdditionalJavadocTagsPanel(), gc); - JTabbedPane tabs = new JBTabbedPane(SwingConstants.BOTTOM); - @NonNls String[] tags = new String[]{"@author", "@version", "@since", "@param"}; - tabs.add( - InspectionLocalize.inspectionJavadocOptionTabTitle().get(), - createOptionsPanel(new String[]{NONE, PUBLIC, PACKAGE_LOCAL}, tags, TOP_LEVEL_CLASS_OPTIONS) - ); - tags = new String[]{"@return", "@param", InspectionLocalize.inspectionJavadocThrowsOrExceptionOption().get()}; - tabs.add( - InspectionLocalize.inspectionJavadocOptionTabTitleMethod().get(), - createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE}, tags, METHOD_OPTIONS) - ); - tabs.add( - InspectionLocalize.inspectionJavadocOptionTabTitleField().get(), - createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE}, null, FIELD_OPTIONS) - ); - tabs.add( - InspectionLocalize.inspectionJavadocOptionTabTitleInnerClass().get(), - createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE}, null, INNER_CLASS_OPTIONS) - ); - add(tabs, gc); - - final JCheckBox checkBox = new JCheckBox(InspectionLocalize.inspectionJavadocOptionIgnoreDeprecated().get(), IGNORE_DEPRECATED); - checkBox.addActionListener(e -> IGNORE_DEPRECATED = checkBox.isSelected()); - gc.gridwidth = 1; - add(checkBox, gc); - final JCheckBox periodCheckBox = new JCheckBox( - InspectionLocalize.inspectionJavadocOptionIgnorePeriod().get(), - IGNORE_JAVADOC_PERIOD - ); - periodCheckBox.addActionListener(e -> IGNORE_JAVADOC_PERIOD = periodCheckBox.isSelected()); - add(periodCheckBox, gc); - - final JCheckBox ignoreDuplicateThrowsCheckBox = new JCheckBox("Ignore duplicate throws tag", IGNORE_DUPLICATED_THROWS); - ignoreDuplicateThrowsCheckBox.addActionListener(e -> IGNORE_DUPLICATED_THROWS = ignoreDuplicateThrowsCheckBox.isSelected()); - add(ignoreDuplicateThrowsCheckBox, gc); - - final JCheckBox ignorePointToItselfCheckBox = new JCheckBox("Ignore javadoc pointing to itself", IGNORE_POINT_TO_ITSELF); - ignorePointToItselfCheckBox.addActionListener(e -> IGNORE_POINT_TO_ITSELF = ignorePointToItselfCheckBox.isSelected()); - add(ignorePointToItselfCheckBox, gc); - final JCheckBox ignoreSimpleAccessorsCheckBox = new JCheckBox("Ignore simple property accessors", myIgnoreSimpleAccessors); - ignoreSimpleAccessorsCheckBox.addActionListener(e -> myIgnoreSimpleAccessors = ignoreSimpleAccessorsCheckBox.isSelected()); - add(ignoreSimpleAccessorsCheckBox, gc); - } + JSlider slider = new JSlider(SwingConstants.VERTICAL, 1, modifiers.length, 1); + + slider.setLabelTable(sliderLabels); + slider.putClientProperty(UIUtil.JSLIDER_ISFILLED, Boolean.TRUE); + slider.setPreferredSize(new Dimension(80, 50)); + slider.setPaintLabels(true); + slider.setSnapToTicks(true); + slider.addChangeListener(e -> { + int value = slider.getValue(); + options.ACCESS_JAVADOC_REQUIRED_FOR = modifiers[value - 1]; + for (Integer key : sliderLabels.keySet()) { + sliderLabels.get(key).setForeground(key <= value ? Color.black : Gray._100); + } + }); - public FieldPanel createAdditionalJavadocTagsPanel(){ - FieldPanel additionalTagsPanel = new FieldPanel( - InspectionLocalize.inspectionJavadocLabelText().get(), - InspectionLocalize.inspectionJavadocDialogTitle().get(), - null, - null - ); - additionalTagsPanel.setPreferredSize(new Dimension(150, additionalTagsPanel.getPreferredSize().height)); - additionalTagsPanel.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { - @Override - protected void textChanged(DocumentEvent e) { - final Document document = e.getDocument(); - try { - final String text = document.getText(0, document.getLength()); - if (text != null) { - myAdditionalJavadocTags = text.trim(); + Color fore = Color.black; + for (int i = 0; i < modifiers.length; i++) { + sliderLabels.get(i + 1).setForeground(fore); + + if (modifiers[i].equals(options.ACCESS_JAVADOC_REQUIRED_FOR)) { + slider.setValue(i + 1); + fore = Gray._100; + } } - } - catch (BadLocationException e1) { - LOG.error(e1); - } - } - }); - additionalTagsPanel.setText(myAdditionalJavadocTags); - return additionalTagsPanel; - } - } - - @Override - public JComponent createOptionsPanel() { - return new OptionsPanel(); - } - - @Override - public void writeSettings(@Nonnull Element node) throws WriteExternalException { - super.writeSettings(node); - if (myIgnoreSimpleAccessors) { - final Element option = new Element(IGNORE_ACCESSORS_ATTR_NAME); - option.setAttribute("value", String.valueOf(true)); - node.addContent(option); - } - } - - @Override - public void readSettings(@Nonnull Element node) throws InvalidDataException { - super.readSettings(node); - final Element ignoreAccessorsTag = node.getChild(IGNORE_ACCESSORS_ATTR_NAME); - if (ignoreAccessorsTag != null) { - myIgnoreSimpleAccessors = Boolean.parseBoolean(ignoreAccessorsTag.getAttributeValue("value")); - } - } - - private static ProblemDescriptor createDescriptor( - @Nonnull PsiElement element, - String template, - InspectionManager manager, - boolean onTheFly - ) { - return manager.createProblemDescriptor(element, template, onTheFly, null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING); - } - - private static ProblemDescriptor createDescriptor( - @Nonnull PsiElement element, - String template, - @Nonnull LocalQuickFix fix, - InspectionManager manager, - boolean onTheFly - ) { - return manager.createProblemDescriptor(element, template, fix, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, onTheFly); - } - - private static class AddMissingTagFix implements LocalQuickFix { - private final String myTag; - private final String myValue; - - public AddMissingTagFix(@NonNls String tag, String value) { - myTag = tag; - myValue = value; + + panel.add(slider, BorderLayout.WEST); + + return panel; + } + + public OptionsPanel() { + super(new GridBagLayout()); + GridBagConstraints gc = new GridBagConstraints( + 0, GridBagConstraints.RELATIVE, 2, 1, 1, 0, GridBagConstraints.NORTH, GridBagConstraints.HORIZONTAL, + JBUI.emptyInsets(), 0, 0 + ); + add(createAdditionalJavadocTagsPanel(), gc); + JTabbedPane tabs = new JBTabbedPane(SwingConstants.BOTTOM); + String[] tags = new String[]{"@author", "@version", "@since", "@param"}; + tabs.add( + InspectionLocalize.inspectionJavadocOptionTabTitle().get(), + createOptionsPanel(new String[]{NONE, PUBLIC, PACKAGE_LOCAL}, tags, TOP_LEVEL_CLASS_OPTIONS) + ); + tags = new String[]{"@return", "@param", InspectionLocalize.inspectionJavadocThrowsOrExceptionOption().get()}; + tabs.add( + InspectionLocalize.inspectionJavadocOptionTabTitleMethod().get(), + createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE}, tags, METHOD_OPTIONS) + ); + tabs.add( + InspectionLocalize.inspectionJavadocOptionTabTitleField().get(), + createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE}, null, FIELD_OPTIONS) + ); + tabs.add( + InspectionLocalize.inspectionJavadocOptionTabTitleInnerClass().get(), + createOptionsPanel(new String[]{NONE, PUBLIC, PROTECTED, PACKAGE_LOCAL, PRIVATE}, null, INNER_CLASS_OPTIONS) + ); + add(tabs, gc); + + JCheckBox checkBox = new JCheckBox(InspectionLocalize.inspectionJavadocOptionIgnoreDeprecated().get(), IGNORE_DEPRECATED); + checkBox.addActionListener(e -> IGNORE_DEPRECATED = checkBox.isSelected()); + gc.gridwidth = 1; + add(checkBox, gc); + JCheckBox periodCheckBox = new JCheckBox( + InspectionLocalize.inspectionJavadocOptionIgnorePeriod().get(), + IGNORE_JAVADOC_PERIOD + ); + periodCheckBox.addActionListener(e -> IGNORE_JAVADOC_PERIOD = periodCheckBox.isSelected()); + add(periodCheckBox, gc); + + JCheckBox ignoreDuplicateThrowsCheckBox = new JCheckBox("Ignore duplicate throws tag", IGNORE_DUPLICATED_THROWS); + ignoreDuplicateThrowsCheckBox.addActionListener(e -> IGNORE_DUPLICATED_THROWS = ignoreDuplicateThrowsCheckBox.isSelected()); + add(ignoreDuplicateThrowsCheckBox, gc); + + JCheckBox ignorePointToItselfCheckBox = new JCheckBox("Ignore javadoc pointing to itself", IGNORE_POINT_TO_ITSELF); + ignorePointToItselfCheckBox.addActionListener(e -> IGNORE_POINT_TO_ITSELF = ignorePointToItselfCheckBox.isSelected()); + add(ignorePointToItselfCheckBox, gc); + JCheckBox ignoreSimpleAccessorsCheckBox = new JCheckBox("Ignore simple property accessors", myIgnoreSimpleAccessors); + ignoreSimpleAccessorsCheckBox.addActionListener(e -> myIgnoreSimpleAccessors = ignoreSimpleAccessorsCheckBox.isSelected()); + add(ignoreSimpleAccessorsCheckBox, gc); + } + + public FieldPanel createAdditionalJavadocTagsPanel() { + FieldPanel additionalTagsPanel = new FieldPanel( + InspectionLocalize.inspectionJavadocLabelText().get(), + InspectionLocalize.inspectionJavadocDialogTitle().get(), + null, + null + ); + additionalTagsPanel.setPreferredSize(new Dimension(150, additionalTagsPanel.getPreferredSize().height)); + additionalTagsPanel.getTextField().getDocument().addDocumentListener(new DocumentAdapter() { + @Override + protected void textChanged(DocumentEvent e) { + Document document = e.getDocument(); + try { + String text = document.getText(0, document.getLength()); + if (text != null) { + myAdditionalJavadocTags = text.trim(); + } + } + catch (BadLocationException e1) { + LOG.error(e1); + } + } + }); + additionalTagsPanel.setText(myAdditionalJavadocTags); + return additionalTagsPanel; + } } - public AddMissingTagFix(String tag) { - this(tag, ""); + + @Override + public JComponent createOptionsPanel() { + return new OptionsPanel(); } @Override - @Nonnull - public LocalizeValue getName() { - return InspectionLocalize.inspectionJavadocProblemAddTag(myTag, myValue); + public void writeSettings(@Nonnull Element node) throws WriteExternalException { + super.writeSettings(node); + if (myIgnoreSimpleAccessors) { + Element option = new Element(IGNORE_ACCESSORS_ATTR_NAME); + option.setAttribute("value", String.valueOf(true)); + node.addContent(option); + } } @Override - @RequiredReadAction - public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { - final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); - try { - final PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(descriptor.getEndElement(), PsiDocCommentOwner.class); - if (owner != null) { - if (!CodeInsightUtil.preparePsiElementsForWrite(owner)) return; - final PsiDocComment docComment = owner.getDocComment(); - final PsiDocTag tag = factory.createDocTagFromText("@" + myTag + " " + myValue); - if (docComment != null) { - PsiElement addedTag; - final PsiElement anchor = getAnchor(descriptor); - if (anchor != null) { - addedTag = docComment.addBefore(tag, anchor); + public void readSettings(@Nonnull Element node) throws InvalidDataException { + super.readSettings(node); + Element ignoreAccessorsTag = node.getChild(IGNORE_ACCESSORS_ATTR_NAME); + if (ignoreAccessorsTag != null) { + myIgnoreSimpleAccessors = Boolean.parseBoolean(ignoreAccessorsTag.getAttributeValue("value")); + } + } + + private static class AddMissingTagFix implements LocalQuickFix { + private final String myTag; + private final String myValue; + + public AddMissingTagFix(String tag, String value) { + myTag = tag; + myValue = value; + } + + public AddMissingTagFix(String tag) { + this(tag, ""); + } + + @Override + @Nonnull + public LocalizeValue getName() { + return InspectionLocalize.inspectionJavadocProblemAddTag(myTag, myValue); + } + + @Override + @RequiredWriteAction + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + try { + PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(descriptor.getEndElement(), PsiDocCommentOwner.class); + if (owner != null) { + if (!CodeInsightUtil.preparePsiElementsForWrite(owner)) { + return; + } + PsiDocComment docComment = owner.getDocComment(); + PsiDocTag tag = factory.createDocTagFromText("@" + myTag + " " + myValue); + if (docComment != null) { + PsiElement addedTag; + PsiElement anchor = getAnchor(descriptor); + if (anchor != null) { + addedTag = docComment.addBefore(tag, anchor); + } + else { + addedTag = docComment.add(tag); + } + moveCaretTo(addedTag); + } + } } - else { - addedTag = docComment.add(tag); + catch (IncorrectOperationException e) { + LOG.error(e); } - moveCaretTo(addedTag); - } } - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - } - @Nullable - protected PsiElement getAnchor(ProblemDescriptor descriptor) { - return null; + @Nullable + protected PsiElement getAnchor(ProblemDescriptor descriptor) { + return null; + } + + @RequiredReadAction + private static void moveCaretTo(PsiElement newCaretPosition) { + Project project = newCaretPosition.getProject(); + PsiFile psiFile = newCaretPosition.getContainingFile(); + Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); + if (editor != null && IJSwingUtilities.hasFocus(editor.getComponent())) { + PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); + if (file == psiFile) { + editor.getCaretModel().moveToOffset(newCaretPosition.getTextRange().getEndOffset()); + editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); + } + } + } } + @Override + @Nullable @RequiredReadAction - private static void moveCaretTo(final PsiElement newCaretPosition) { - Project project = newCaretPosition.getProject(); - final PsiFile psiFile = newCaretPosition.getContainingFile(); - final Editor editor = FileEditorManager.getInstance(project).getSelectedTextEditor(); - if (editor != null && IJSwingUtilities.hasFocus(editor.getComponent())) { - final PsiFile file = PsiDocumentManager.getInstance(project).getPsiFile(editor.getDocument()); - if (file == psiFile) { - editor.getCaretModel().moveToOffset(newCaretPosition.getTextRange().getEndOffset()); - editor.getScrollingModel().scrollToCaret(ScrollType.RELATIVE); - } - } - } - } - - @Override - @Nullable - @RequiredReadAction - public ProblemDescriptor[] checkClass(@Nonnull PsiClass psiClass, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - if (psiClass instanceof PsiAnonymousClass) return null; - // if (psiClass instanceof JspClass) return null; - if (psiClass instanceof PsiTypeParameter) return null; - if (IGNORE_DEPRECATED && psiClass.isDeprecated()) { - return null; - } - PsiDocComment docComment = psiClass.getDocComment(); - final PsiIdentifier nameIdentifier = psiClass.getNameIdentifier(); - final PsiElement elementToHighlight = nameIdentifier != null ? nameIdentifier : psiClass; - final boolean required = isJavaDocRequired(psiClass); - if (docComment == null) { - return required - ? new ProblemDescriptor[]{createDescriptor(elementToHighlight, - InspectionLocalize.inspectionJavadocProblemDescriptor().get(), manager, isOnTheFly)} - : null; - } + public ProblemDescriptor[] checkClass( + @Nonnull PsiClass psiClass, + @Nonnull InspectionManager manager, + boolean isOnTheFly, + Object state + ) { + if (psiClass instanceof PsiAnonymousClass) { + return null; + } + // if (psiClass instanceof JspClass) return null; + if (psiClass instanceof PsiTypeParameter) { + return null; + } + if (IGNORE_DEPRECATED && psiClass.isDeprecated()) { + return null; + } + PsiDocComment docComment = psiClass.getDocComment(); + PsiIdentifier nameIdentifier = psiClass.getNameIdentifier(); + PsiElement elementToHighlight = nameIdentifier != null ? nameIdentifier : psiClass; + boolean required = isJavaDocRequired(psiClass); + if (docComment == null) { + if (!required) { + return null; + } + return new ProblemDescriptor[]{ + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDescriptor()) + .range(elementToHighlight) + .onTheFly(isOnTheFly) + .create() + }; + } - PsiDocTag[] tags = docComment.getTags(); - @NonNls String[] tagsToCheck = {"author", "version", "since"}; - @NonNls String[] absentDescriptionKeys = { - "inspection.javadoc.problem.missing.author.description", - "inspection.javadoc.problem.missing.version.description", - "inspection.javadoc.problem.missing.since.description"}; - final ArrayList problems = new ArrayList<>(2); - if (required) { - boolean[] isTagRequired = new boolean[tagsToCheck.length]; - boolean[] isTagPresent = new boolean[tagsToCheck.length]; - - boolean someTagsAreRequired = false; - for (int i = 0; i < tagsToCheck.length; i++) { - final String tag = tagsToCheck[i]; - someTagsAreRequired |= isTagRequired[i] = isTagRequired(psiClass, tag); - } - - if (someTagsAreRequired) { + PsiDocTag[] tags = docComment.getTags(); + String[] tagsToCheck = {"author", "version", "since"}; + LocalizeValue[] absentDescriptions = { + InspectionLocalize.inspectionJavadocProblemMissingAuthorDescription(), + InspectionLocalize.inspectionJavadocProblemMissingVersionDescription(), + InspectionLocalize.inspectionJavadocProblemMissingSinceDescription() + }; + List problems = new ArrayList<>(2); + if (required) { + boolean[] isTagRequired = new boolean[tagsToCheck.length]; + boolean[] isTagPresent = new boolean[tagsToCheck.length]; + + boolean someTagsAreRequired = false; + for (int i = 0; i < tagsToCheck.length; i++) { + String tag = tagsToCheck[i]; + someTagsAreRequired |= isTagRequired[i] = isTagRequired(psiClass, tag); + } + + if (someTagsAreRequired) { + for (PsiDocTag tag : tags) { + String tagName = tag.getName(); + for (int i = 0; i < tagsToCheck.length; i++) { + String tagToCheck = tagsToCheck[i]; + if (tagToCheck.equals(tagName)) { + isTagPresent[i] = true; + } + } + } + } + + for (int i = 0; i < tagsToCheck.length; i++) { + String tagToCheck = tagsToCheck[i]; + if (isTagRequired[i] && !isTagPresent[i]) { + problems.add(createMissingTagDescriptor(elementToHighlight, tagToCheck, manager, isOnTheFly)); + } + } + } + + List tagProblems = getTagValuesProblems(psiClass, tags, manager, isOnTheFly); + if (tagProblems != null) { + problems.addAll(tagProblems); + } + checkForPeriodInDoc(docComment, problems, manager, isOnTheFly); + checkInlineTags(manager, problems, docComment.getDescriptionElements(), + JavadocManager.SERVICE.getInstance(docComment.getProject()), isOnTheFly + ); + checkForBadCharacters(docComment, problems, manager, isOnTheFly); for (PsiDocTag tag : tags) { - String tagName = tag.getName(); - for (int i = 0; i < tagsToCheck.length; i++) { - final String tagToCheck = tagsToCheck[i]; - if (tagToCheck.equals(tagName)) { - isTagPresent[i] = true; + for (int i = 0; i < tagsToCheck.length; i++) { + String tagToCheck = tagsToCheck[i]; + if (tagToCheck.equals(tag.getName()) && extractTagDescription(tag).length() == 0) { + problems.add( + manager.newProblemDescriptor(absentDescriptions[i]) + .range(elementToHighlight) + .onTheFly(isOnTheFly) + .create() + ); + } } - } } - } - for (int i = 0; i < tagsToCheck.length; i++) { - final String tagToCheck = tagsToCheck[i]; - if (isTagRequired[i] && !isTagPresent[i]) { - problems.add(createMissingTagDescriptor(elementToHighlight, tagToCheck, manager, isOnTheFly)); + checkDuplicateTags(tags, problems, manager, isOnTheFly); + + if (required && isTagRequired(psiClass, "param") && psiClass.hasTypeParameters() && nameIdentifier != null) { + List absentParameters = null; + PsiTypeParameter[] typeParameters = psiClass.getTypeParameters(); + for (PsiTypeParameter typeParameter : typeParameters) { + if (!isFound(tags, typeParameter)) { + if (absentParameters == null) { + absentParameters = new ArrayList<>(1); + } + absentParameters.add(typeParameter); + } + } + if (absentParameters != null) { + for (PsiTypeParameter psiTypeParameter : absentParameters) { + problems.add(createMissingParamTagDescriptor(nameIdentifier, psiTypeParameter, manager, isOnTheFly)); + } + } } - } - } - ArrayList tagProblems = getTagValuesProblems(psiClass, tags, manager, isOnTheFly); - if (tagProblems != null) { - problems.addAll(tagProblems); + return problems.isEmpty() + ? null + : problems.toArray(new ProblemDescriptor[problems.size()]); } - checkForPeriodInDoc(docComment, problems, manager, isOnTheFly); - checkInlineTags(manager, problems, docComment.getDescriptionElements(), - JavadocManager.SERVICE.getInstance(docComment.getProject()), isOnTheFly); - checkForBadCharacters(docComment, problems, manager, isOnTheFly); - for (PsiDocTag tag : tags) { - for (int i = 0; i < tagsToCheck.length; i++) { - final String tagToCheck = tagsToCheck[i]; - if (tagToCheck.equals(tag.getName()) && extractTagDescription(tag).length() == 0) { - problems.add(createDescriptor(elementToHighlight, InspectionsBundle.message(absentDescriptionKeys[i]), manager, isOnTheFly)); - } - } + + @RequiredReadAction + private static ProblemDescriptor createMissingParamTagDescriptor( + PsiIdentifier nameIdentifier, + PsiTypeParameter psiTypeParameter, + InspectionManager manager, + boolean isOnTheFly + ) { + return manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemMissingTag("@param")) + .range(nameIdentifier) + .onTheFly(isOnTheFly) + .withFix(new AddMissingTagFix("param", "<" + psiTypeParameter.getName() + ">")) + .create(); } - checkDuplicateTags(tags, problems, manager, isOnTheFly); + @Override + @Nullable + @RequiredReadAction + public ProblemDescriptor[] checkField( + @Nonnull PsiField psiField, + @Nonnull InspectionManager manager, + boolean isOnTheFly, + Object state + ) { + if (IGNORE_DEPRECATED && (psiField.isDeprecated() || psiField.getContainingClass().isDeprecated())) { + return null; + } - if (required && isTagRequired(psiClass, "param") && psiClass.hasTypeParameters() && nameIdentifier != null) { - ArrayList absentParameters = null; - final PsiTypeParameter[] typeParameters = psiClass.getTypeParameters(); - for (PsiTypeParameter typeParameter : typeParameters) { - if (!isFound(tags, typeParameter)) { - if (absentParameters == null) absentParameters = new ArrayList<>(1); - absentParameters.add(typeParameter); + PsiDocComment docComment = psiField.getDocComment(); + if (docComment == null) { + if (!isJavaDocRequired(psiField)) { + return null; + } + return new ProblemDescriptor[]{ + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDescriptor()) + .range(psiField.getNameIdentifier()) + .onTheFly(isOnTheFly) + .create() + }; } - } - if (absentParameters != null) { - for (PsiTypeParameter psiTypeParameter : absentParameters) { - problems.add(createMissingParamTagDescriptor(nameIdentifier, psiTypeParameter, manager, isOnTheFly)); + + List problems = new ArrayList<>(2); + List tagProblems = getTagValuesProblems(psiField, docComment.getTags(), manager, isOnTheFly); + if (tagProblems != null) { + problems.addAll(tagProblems); } - } + checkInlineTags( + manager, + problems, + docComment.getDescriptionElements(), + JavadocManager.SERVICE.getInstance(docComment.getProject()), + isOnTheFly + ); + checkForPeriodInDoc(docComment, problems, manager, isOnTheFly); + checkDuplicateTags(docComment.getTags(), problems, manager, isOnTheFly); + checkForBadCharacters(docComment, problems, manager, isOnTheFly); + return problems.isEmpty() + ? null + : problems.toArray(new ProblemDescriptor[problems.size()]); } - return problems.isEmpty() - ? null - : problems.toArray(new ProblemDescriptor[problems.size()]); - } - - @RequiredReadAction - private static ProblemDescriptor createMissingParamTagDescriptor( - final PsiIdentifier nameIdentifier, - final PsiTypeParameter psiTypeParameter, - final InspectionManager manager, boolean isOnTheFly - ) { - LocalizeValue message = InspectionLocalize.inspectionJavadocProblemMissingTag("@param"); - return createDescriptor( - nameIdentifier, - message.get(), - new AddMissingTagFix("param", "<" + psiTypeParameter.getName() + ">"), - manager, - isOnTheFly - ); - } - - @Override - @Nullable - @RequiredReadAction - public ProblemDescriptor[] checkField(@Nonnull PsiField psiField, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - if (IGNORE_DEPRECATED && (psiField.isDeprecated() || psiField.getContainingClass().isDeprecated())) { - return null; - } + @Override + @Nullable + @RequiredReadAction + public ProblemDescriptor[] checkMethod( + @Nonnull PsiMethod psiMethod, + @Nonnull InspectionManager manager, + boolean isOnTheFly, + Object state + ) { + //if (psiMethod instanceof JspHolderMethod) return null; + if (IGNORE_DEPRECATED && (psiMethod.isDeprecated() || psiMethod.getContainingClass().isDeprecated())) { + return null; + } + if (myIgnoreSimpleAccessors && PropertyUtil.isSimplePropertyAccessor(psiMethod)) { + return null; + } + PsiDocComment docComment = psiMethod.getDocComment(); + PsiMethod[] superMethods = psiMethod.findSuperMethods(); + boolean required = isJavaDocRequired(psiMethod); + if (docComment == null) { + if (required) { + if (superMethods.length > 0) { + return null; + } + ExtensionPoint filters = + psiMethod.getApplication().getExtensionPoint(JavaDocNotNecessaryFilter.class); + if (filters.anyMatchSafe(filter -> filter.isJavaDocNotNecessary(psiMethod))) { + return null; + } + PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier(); + if (nameIdentifier != null) { + return new ProblemDescriptor[]{ + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDescriptor()) + .range(nameIdentifier) + .onTheFly(isOnTheFly) + .create() + }; + } + } + return null; + } - PsiDocComment docComment = psiField.getDocComment(); - if (docComment == null) { - return isJavaDocRequired(psiField) - ? new ProblemDescriptor[]{createDescriptor(psiField.getNameIdentifier(), - InspectionLocalize.inspectionJavadocProblemDescriptor().get(), manager, isOnTheFly)} - : null; - } + PsiElement[] descriptionElements = docComment.getDescriptionElements(); + for (PsiElement descriptionElement : descriptionElements) { + if (descriptionElement instanceof PsiInlineDocTag inlineDocTag + && "inheritDoc".equals(inlineDocTag.getName())) { + return null; + } + } - final ArrayList problems = new ArrayList<>(2); - ArrayList tagProblems = getTagValuesProblems(psiField, docComment.getTags(), manager, isOnTheFly); - if (tagProblems != null) { - problems.addAll(tagProblems); - } - checkInlineTags( - manager, - problems, - docComment.getDescriptionElements(), - JavadocManager.SERVICE.getInstance(docComment.getProject()), - isOnTheFly - ); - checkForPeriodInDoc(docComment, problems, manager, isOnTheFly); - checkDuplicateTags(docComment.getTags(), problems, manager, isOnTheFly); - checkForBadCharacters(docComment, problems, manager, isOnTheFly); - return problems.isEmpty() - ? null - : problems.toArray(new ProblemDescriptor[problems.size()]); - } - - @Override - @Nullable - @RequiredReadAction - public ProblemDescriptor[] checkMethod(@Nonnull PsiMethod psiMethod, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - //if (psiMethod instanceof JspHolderMethod) return null; - if (IGNORE_DEPRECATED && (psiMethod.isDeprecated() || psiMethod.getContainingClass().isDeprecated())) { - return null; - } - if (myIgnoreSimpleAccessors && PropertyUtil.isSimplePropertyAccessor(psiMethod)) { - return null; - } - PsiDocComment docComment = psiMethod.getDocComment(); - final PsiMethod[] superMethods = psiMethod.findSuperMethods(); - final boolean required = isJavaDocRequired(psiMethod); - if (docComment == null) { - if (required) { - if (superMethods.length > 0) return null; - for (JavaDocNotNecessaryFilter addin : Application.get().getExtensionList(JavaDocNotNecessaryFilter.class)) { - if (addin.isJavaDocNotNecessary(psiMethod)) return null; - } - if (superMethods.length == 0) { - final PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier(); - return nameIdentifier != null - ? new ProblemDescriptor[] { createDescriptor(nameIdentifier, InspectionLocalize.inspectionJavadocProblemDescriptor().get(), manager, isOnTheFly)} : null; - } - else { - return null; - } - } - else { - return null; - } - } + List problems = new ArrayList<>(2); - final PsiElement[] descriptionElements = docComment.getDescriptionElements(); - for (PsiElement descriptionElement : descriptionElements) { - if (descriptionElement instanceof PsiInlineDocTag) { - if ("inheritDoc".equals(((PsiInlineDocTag)descriptionElement).getName())) return null; - } - } + checkInlineTags( + manager, + problems, + descriptionElements, + JavadocManager.SERVICE.getInstance(docComment.getProject()), + isOnTheFly + ); - final ArrayList problems = new ArrayList<>(2); - - checkInlineTags( - manager, - problems, - descriptionElements, - JavadocManager.SERVICE.getInstance(docComment.getProject()), - isOnTheFly - ); - - final PsiDocTag tagByName = docComment.findTagByName("inheritDoc"); - if (tagByName != null) { - final String tagName = tagByName.getName(); - final JavadocTagInfo tagInfo = JavadocManager.SERVICE.getInstance(tagByName.getProject()).getTagInfo(tagName); - if (tagInfo != null && tagInfo.isValidInContext(psiMethod)){ - return null; - } - } + PsiDocTag tagByName = docComment.findTagByName("inheritDoc"); + if (tagByName != null) { + String tagName = tagByName.getName(); + JavadocTagInfo tagInfo = JavadocManager.SERVICE.getInstance(tagByName.getProject()).getTagInfo(tagName); + if (tagInfo != null && tagInfo.isValidInContext(psiMethod)) { + return null; + } + } - PsiDocTag[] tags = docComment.getTags(); + PsiDocTag[] tags = docComment.getTags(); + + boolean isReturnRequired = false; + boolean isReturnAbsent = true; + if (superMethods.length == 0 && !psiMethod.isConstructor() && !PsiType.VOID.equals(psiMethod.getReturnType()) && isTagRequired( + psiMethod, + "return" + )) { + isReturnRequired = true; + for (PsiDocTag tag : tags) { + if ("return".equals(tag.getName())) { + isReturnAbsent = false; + break; + } + } + } + + List absentParameters = null; + if (required && superMethods.length == 0 && isTagRequired(psiMethod, "param")) { + PsiParameter[] params = psiMethod.getParameterList().getParameters(); + for (PsiParameter param : params) { + if (!isFound(tags, param)) { + if (absentParameters == null) { + absentParameters = new ArrayList<>(2); + } + absentParameters.add(param); + } + } + } - boolean isReturnRequired = false; - boolean isReturnAbsent = true; - if (superMethods.length == 0 && !psiMethod.isConstructor() && !PsiType.VOID.equals(psiMethod.getReturnType()) && isTagRequired(psiMethod, "return")) { - isReturnRequired = true; - for (PsiDocTag tag : tags) { - if ("return".equals(tag.getName())) { - isReturnAbsent = false; - break; + if (required && isReturnRequired && isReturnAbsent) { + PsiIdentifier psiIdentifier = psiMethod.getNameIdentifier(); + if (psiIdentifier != null) { + problems.add(createMissingTagDescriptor(psiIdentifier, "return", manager, isOnTheFly)); + } } - } - } - ArrayList absentParameters = null; - if (required && superMethods.length == 0 && isTagRequired(psiMethod, "param") ) { - PsiParameter[] params = psiMethod.getParameterList().getParameters(); - for (PsiParameter param : params) { - if (!isFound(tags, param)) { - if (absentParameters == null) absentParameters = new ArrayList<>(2); - absentParameters.add(param); + if (absentParameters != null) { + for (PsiParameter psiParameter : absentParameters) { + PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier(); + if (nameIdentifier != null) { + problems.add(createMissingParamTagDescriptor(nameIdentifier, psiParameter, manager, isOnTheFly)); + } + } } - } - } - if (required && isReturnRequired && isReturnAbsent) { - final PsiIdentifier psiIdentifier = psiMethod.getNameIdentifier(); - if (psiIdentifier != null) { - problems.add(createMissingTagDescriptor(psiIdentifier, "return", manager, isOnTheFly)); - } - } + if (!myIgnoreEmptyDescriptions) { + for (PsiDocTag tag : tags) { + if ("param".equals(tag.getName())) { + PsiElement[] dataElements = tag.getDataElements(); + PsiDocTagValue valueElement = tag.getValueElement(); + boolean hasProblemsWithTag = dataElements.length < 2; + if (!hasProblemsWithTag) { + StringBuilder buf = new StringBuilder(); + for (PsiElement element : dataElements) { + if (element != valueElement) { + buf.append(element.getText()); + } + } + hasProblemsWithTag = buf.toString().trim().length() == 0; + } + if (hasProblemsWithTag && valueElement != null) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription( + "@param " + valueElement.getText() + "" + )) + .range(valueElement) + .onTheFly(isOnTheFly) + .create() + ); + } + } + } + } + + if (required && superMethods.length == 0 && isTagRequired(psiMethod, "@throws") + && psiMethod.getThrowsList().getReferencedTypes().length > 0) { + Map declaredExceptions = new HashMap<>(); + PsiClassType[] classTypes = psiMethod.getThrowsList().getReferencedTypes(); + for (PsiClassType classType : classTypes) { + PsiClass psiClass = classType.resolve(); + if (psiClass != null) { + declaredExceptions.put(classType, psiClass); + } + } + processThrowsTags(tags, declaredExceptions, manager, problems, isOnTheFly); + if (!declaredExceptions.isEmpty()) { + for (PsiClassType declaredException : declaredExceptions.keySet()) { + problems.add(createMissingThrowsTagDescriptor(psiMethod, manager, declaredException, isOnTheFly)); + } + } + } - if (absentParameters != null) { - for (PsiParameter psiParameter : absentParameters) { - final PsiIdentifier nameIdentifier = psiMethod.getNameIdentifier(); - if (nameIdentifier != null) { - problems.add(createMissingParamTagDescriptor(nameIdentifier, psiParameter, manager, isOnTheFly)); + List tagProblems = getTagValuesProblems(psiMethod, tags, manager, isOnTheFly); + if (tagProblems != null) { + problems.addAll(tagProblems); } - } - } - if (!myIgnoreEmptyDescriptions) { - for (PsiDocTag tag : tags) { - if ("param".equals(tag.getName())) { - final PsiElement[] dataElements = tag.getDataElements(); - final PsiDocTagValue valueElement = tag.getValueElement(); - boolean hasProblemsWithTag = dataElements.length < 2; - if (!hasProblemsWithTag) { - final StringBuilder buf = new StringBuilder(); - for (PsiElement element : dataElements) { - if (element != valueElement){ - buf.append(element.getText()); - } + checkForPeriodInDoc(docComment, problems, manager, isOnTheFly); + checkForBadCharacters(docComment, problems, manager, isOnTheFly); + for (PsiDocTag tag : tags) { + if ("param".equals(tag.getName())) { + if (extractTagDescription(tag).length() == 0) { + PsiDocTagValue value = tag.getValueElement(); + if (value instanceof PsiDocParamRef paramRef) { + for (PsiParameter param : psiMethod.getParameterList().getParameters()) { + if (paramRef.getReference().isReferenceTo(param)) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocMethodProblemDescriptor( + "@param", + "" + param.getName() + "" + )) + .range(value) + .onTheFly(isOnTheFly) + .create() + ); + } + } + } + } } - hasProblemsWithTag = buf.toString().trim().length() == 0; - } - if (hasProblemsWithTag) { - if (valueElement != null) { - problems.add(createDescriptor( - valueElement, - InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription( - "@param " + valueElement.getText() + "" - ).get(), - manager, - isOnTheFly - )); + else if ("return".equals(tag.getName()) && !myIgnoreEmptyDescriptions && extractTagDescription(tag).length() == 0) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription( + "@return" + )) + .range(tag.getNameElement()) + .onTheFly(isOnTheFly) + .create() + ); } - } } - } - } - if (required && superMethods.length == 0 && isTagRequired(psiMethod, "@throws") - && psiMethod.getThrowsList().getReferencedTypes().length > 0) { - final Map declaredExceptions = new HashMap<>(); - final PsiClassType[] classTypes = psiMethod.getThrowsList().getReferencedTypes(); - for (PsiClassType classType : classTypes) { - final PsiClass psiClass = classType.resolve(); - if (psiClass != null){ - declaredExceptions.put(classType, psiClass); - } - } - processThrowsTags(tags, declaredExceptions, manager, problems, isOnTheFly); - if (!declaredExceptions.isEmpty()) { - for (PsiClassType declaredException : declaredExceptions.keySet()) { - problems.add(createMissingThrowsTagDescriptor(psiMethod, manager, declaredException, isOnTheFly)); - } - } - } + checkDuplicateTags(tags, problems, manager, isOnTheFly); - ArrayList tagProblems = getTagValuesProblems(psiMethod, tags, manager, isOnTheFly); - if (tagProblems != null) { - problems.addAll(tagProblems); + return problems.isEmpty() + ? null + : problems.toArray(new ProblemDescriptor[problems.size()]); } - checkForPeriodInDoc(docComment, problems, manager, isOnTheFly); - checkForBadCharacters(docComment, problems, manager, isOnTheFly); - for (PsiDocTag tag : tags) { - if ("param".equals(tag.getName())) { - if (extractTagDescription(tag).length() == 0) { - PsiDocTagValue value = tag.getValueElement(); - if (value instanceof PsiDocParamRef) { - PsiDocParamRef paramRef = (PsiDocParamRef)value; - PsiParameter[] params = psiMethod.getParameterList().getParameters(); - for (PsiParameter param : params) { - if (paramRef.getReference().isReferenceTo(param)) { - problems.add(createDescriptor( - value, - InspectionLocalize.inspectionJavadocMethodProblemDescriptor( - "@param", - "" + param.getName() + "" - ).get(), - manager, - isOnTheFly - )); - } + @RequiredReadAction + public static boolean isFound(PsiDocTag[] tags, PsiElement param) { + for (PsiDocTag tag : tags) { + if ("param".equals(tag.getName()) && tag.getValueElement() instanceof PsiDocParamRef paramRef) { + PsiReference psiReference = paramRef.getReference(); + if (psiReference != null && psiReference.isReferenceTo(param)) { + return true; + } } - } - } - } - else if ("return".equals(tag.getName()) && !myIgnoreEmptyDescriptions && extractTagDescription(tag).length() == 0) { - LocalizeValue message = InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription("@return"); - ProblemDescriptor descriptor = manager.createProblemDescriptor( - tag.getNameElement(), - message.get(), - (LocalQuickFix)null, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - isOnTheFly - ); - problems.add(descriptor); - } + } + return false; } - checkDuplicateTags(tags, problems, manager, isOnTheFly); - - return problems.isEmpty() - ? null - : problems.toArray(new ProblemDescriptor[problems.size()]); - } - - @RequiredReadAction - public static boolean isFound(final PsiDocTag[] tags, final PsiElement param) { - for (PsiDocTag tag : tags) { - if ("param".equals(tag.getName())) { - PsiDocTagValue value = tag.getValueElement(); - if (value instanceof PsiDocParamRef) { - PsiDocParamRef paramRef = (PsiDocParamRef)value; - final PsiReference psiReference = paramRef.getReference(); - if (psiReference != null && psiReference.isReferenceTo(param)) { - return true; - } - } - } - } - return false; - } - - @RequiredReadAction - private void processThrowsTags( - final PsiDocTag[] tags, - final Map declaredExceptions, - final InspectionManager mananger, - @Nonnull final ArrayList problems, - boolean isOnTheFly - ) { - for (PsiDocTag tag : tags) { - if ("throws".equals(tag.getName()) || "exception".equals(tag.getName())) { - final PsiDocTagValue value = tag.getValueElement(); - if (value == null) continue; - final PsiElement firstChild = value.getFirstChild(); - if (firstChild == null) continue; - final PsiElement psiElement = firstChild.getFirstChild(); - if (!(psiElement instanceof PsiJavaCodeReferenceElement)) continue; - final PsiJavaCodeReferenceElement ref = (PsiJavaCodeReferenceElement)psiElement; - final PsiElement element = ref.resolve(); - if (element instanceof PsiClass exceptionClass){ - for (Iterator it = declaredExceptions.keySet().iterator(); it.hasNext();) { - PsiClassType classType = it.next(); - final PsiClass psiClass = declaredExceptions.get(classType); - if (InheritanceUtil.isInheritorOrSelf(exceptionClass, psiClass, true)) { - if (!myIgnoreEmptyDescriptions && extractThrowsTagDescription(tag).length() == 0) { - problems.add(createDescriptor( - tag.getNameElement(), - InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription("" + tag.getName() + "").get(), - mananger, - isOnTheFly - )); - } - it.remove(); + @RequiredReadAction + private void processThrowsTags( + PsiDocTag[] tags, + Map declaredExceptions, + InspectionManager manager, + @Nonnull List problems, + boolean isOnTheFly + ) { + for (PsiDocTag tag : tags) { + if ("throws".equals(tag.getName()) || "exception".equals(tag.getName())) { + PsiDocTagValue value = tag.getValueElement(); + if (value == null) { + continue; + } + PsiElement firstChild = value.getFirstChild(); + if (firstChild != null + && firstChild.getFirstChild() instanceof PsiJavaCodeReferenceElement ref + && ref.resolve() instanceof PsiClass exceptionClass) { + for (Iterator it = declaredExceptions.keySet().iterator(); it.hasNext(); ) { + PsiClassType classType = it.next(); + PsiClass psiClass = declaredExceptions.get(classType); + if (InheritanceUtil.isInheritorOrSelf(exceptionClass, psiClass, true)) { + if (!myIgnoreEmptyDescriptions && extractThrowsTagDescription(tag).length() == 0) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription( + "" + tag.getName() + "" + )) + .range(tag.getNameElement()) + .onTheFly(isOnTheFly) + .create()); + } + it.remove(); + } + } + } } - } } - } } - } - - @Nullable - private static ProblemDescriptor createMissingThrowsTagDescriptor( - final PsiMethod method, - final InspectionManager manager, - final PsiClassType exceptionClassType, - boolean isOnTheFly - ) { - @NonNls String tag = "throws"; - LocalizeValue message = - InspectionLocalize.inspectionJavadocProblemMissingTag("@" + tag + " " + exceptionClassType.getCanonicalText()); - final String firstDeclaredException = exceptionClassType.getCanonicalText(); - final PsiIdentifier nameIdentifier = method.getNameIdentifier(); - return nameIdentifier != null ? createDescriptor( - nameIdentifier, - message.get(), - new AddMissingTagFix(tag, firstDeclaredException), - manager, - isOnTheFly - ) : null; - } - - private static ProblemDescriptor createMissingTagDescriptor( - PsiElement elementToHighlight, - @NonNls String tag, - final InspectionManager manager, - boolean isOnTheFly - ) { - LocalizeValue message = InspectionLocalize.inspectionJavadocProblemMissingTag("@" + tag + ""); - return createDescriptor(elementToHighlight, message.get(), new AddMissingTagFix(tag), manager, isOnTheFly); - } - - private static ProblemDescriptor createMissingParamTagDescriptor( - PsiElement elementToHighlight, - PsiParameter param, - final InspectionManager manager, - boolean isOnTheFly - ) { - LocalizeValue message = InspectionLocalize.inspectionJavadocMethodProblemMissingParamTag( - "@param", - "" + param.getName() + "" - ); - return createDescriptor(elementToHighlight, message.get(), new AddMissingParamTagFix(param.getName()), manager, isOnTheFly); - } - - private static class AddMissingParamTagFix extends AddMissingTagFix { - private final String myName; - - public AddMissingParamTagFix(String name) { - super("param", name); - myName = name; + + @Nullable + @RequiredReadAction + private static ProblemDescriptor createMissingThrowsTagDescriptor( + PsiMethod method, + InspectionManager manager, + PsiClassType exceptionClassType, + boolean isOnTheFly + ) { + String tag = "throws"; + LocalizeValue message = + InspectionLocalize.inspectionJavadocProblemMissingTag("@" + tag + " " + exceptionClassType.getCanonicalText()); + String firstDeclaredException = exceptionClassType.getCanonicalText(); + PsiIdentifier nameIdentifier = method.getNameIdentifier(); + if (nameIdentifier == null) { + return null; + } + return manager.newProblemDescriptor(message) + .range(nameIdentifier) + .onTheFly(isOnTheFly) + .withFix(new AddMissingTagFix(tag, firstDeclaredException)) + .create(); } - @Override - @Nonnull - public LocalizeValue getName() { - return InspectionLocalize.inspectionJavadocProblemAddParamTag(myName); + @RequiredReadAction + private static ProblemDescriptor createMissingTagDescriptor( + PsiElement elementToHighlight, + String tag, + InspectionManager manager, + boolean isOnTheFly + ) { + return manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemMissingTag("@" + tag + "")) + .range(elementToHighlight) + .onTheFly(isOnTheFly) + .withFix(new AddMissingTagFix(tag)) + .create(); } - @Override - @Nullable @RequiredReadAction - protected PsiElement getAnchor(ProblemDescriptor descriptor) { - PsiElement element = descriptor.getPsiElement(); - PsiElement parent = element == null ? null : element.getParent(); - if (!(parent instanceof PsiMethod)) return null; - PsiParameter[] parameters = ((PsiMethod)parent).getParameterList().getParameters(); - PsiParameter myParam = ContainerUtil.find(parameters, psiParameter -> myName.equals(psiParameter.getName())); - if (myParam == null) return null; - - final PsiMethod psiMethod = PsiTreeUtil.getParentOfType(myParam, PsiMethod.class); - LOG.assertTrue(psiMethod != null); - final PsiDocComment docComment = psiMethod.getDocComment(); - LOG.assertTrue(docComment != null); - PsiDocTag[] tags = docComment.findTagsByName("param"); - if (tags.length == 0) { //insert as first tag or append to description - tags = docComment.getTags(); - if (tags.length == 0) return null; - return tags[0]; - } - - PsiParameter nextParam = PsiTreeUtil.getNextSiblingOfType(myParam, PsiParameter.class); - while (nextParam != null) { - for (PsiDocTag tag : tags) { - if (matches(nextParam, tag)) { - return tag; - } + private static ProblemDescriptor createMissingParamTagDescriptor( + PsiElement elementToHighlight, + PsiParameter param, + InspectionManager manager, + boolean isOnTheFly + ) { + return manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocMethodProblemMissingParamTag( + "@param", + "" + param.getName() + "" + )) + .range(elementToHighlight) + .onTheFly(isOnTheFly) + .withFix(new AddMissingParamTagFix(param.getName())) + .create(); + } + + private static class AddMissingParamTagFix extends AddMissingTagFix { + private final String myName; + + public AddMissingParamTagFix(String name) { + super("param", name); + myName = name; } - nextParam = PsiTreeUtil.getNextSiblingOfType(nextParam, PsiParameter.class); - } - PsiParameter prevParam = PsiTreeUtil.getPrevSiblingOfType(myParam, PsiParameter.class); - while (prevParam != null) { - for (PsiDocTag tag : tags) { - if (matches(prevParam, tag)) { - return PsiTreeUtil.getNextSiblingOfType(tag, PsiDocTag.class); - } + @Override + @Nonnull + public LocalizeValue getName() { + return InspectionLocalize.inspectionJavadocProblemAddParamTag(myName); } - prevParam = PsiTreeUtil.getPrevSiblingOfType(prevParam, PsiParameter.class); - } - return null; + @Override + @Nullable + @RequiredReadAction + protected PsiElement getAnchor(ProblemDescriptor descriptor) { + if (!(descriptor.getPsiElement() instanceof PsiElement element && element.getParent() instanceof PsiMethod method)) { + return null; + } + PsiParameter[] parameters = method.getParameterList().getParameters(); + PsiParameter myParam = ContainerUtil.find(parameters, psiParameter -> myName.equals(psiParameter.getName())); + if (myParam == null) { + return null; + } + + PsiMethod psiMethod = PsiTreeUtil.getParentOfType(myParam, PsiMethod.class); + LOG.assertTrue(psiMethod != null); + PsiDocComment docComment = psiMethod.getDocComment(); + LOG.assertTrue(docComment != null); + PsiDocTag[] tags = docComment.findTagsByName("param"); + if (tags.length == 0) { //insert as first tag or append to description + tags = docComment.getTags(); + if (tags.length == 0) { + return null; + } + return tags[0]; + } + + PsiParameter nextParam = PsiTreeUtil.getNextSiblingOfType(myParam, PsiParameter.class); + while (nextParam != null) { + for (PsiDocTag tag : tags) { + if (matches(nextParam, tag)) { + return tag; + } + } + nextParam = PsiTreeUtil.getNextSiblingOfType(nextParam, PsiParameter.class); + } + + PsiParameter prevParam = PsiTreeUtil.getPrevSiblingOfType(myParam, PsiParameter.class); + while (prevParam != null) { + for (PsiDocTag tag : tags) { + if (matches(prevParam, tag)) { + return PsiTreeUtil.getNextSiblingOfType(tag, PsiDocTag.class); + } + } + prevParam = PsiTreeUtil.getPrevSiblingOfType(prevParam, PsiParameter.class); + } + + return null; + } + + @RequiredReadAction + private static boolean matches(PsiParameter param, PsiDocTag tag) { + PsiDocTagValue valueElement = tag.getValueElement(); + return valueElement != null && valueElement.getText().trim().startsWith(param.getName()); + } } @RequiredReadAction - private static boolean matches(final PsiParameter param, final PsiDocTag tag) { - final PsiDocTagValue valueElement = tag.getValueElement(); - return valueElement != null && valueElement.getText().trim().startsWith(param.getName()); - } - } - - @RequiredReadAction - private static String extractTagDescription(PsiDocTag tag) { - StringBuilder buf = new StringBuilder(); - PsiElement[] children = tag.getChildren(); - for (PsiElement child : children) { - if (child instanceof PsiDocToken) { - PsiDocToken token = (PsiDocToken)child; - if (token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) { - buf.append(token.getText()); - } - } - else if (child instanceof PsiDocTagValue) { - buf.append(child.getText()); - } else if (child instanceof PsiInlineDocTag) { - buf.append(child.getText()); - } - } + private static String extractTagDescription(PsiDocTag tag) { + StringBuilder buf = new StringBuilder(); + PsiElement[] children = tag.getChildren(); + for (PsiElement child : children) { + if (child instanceof PsiDocToken token) { + if (token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) { + buf.append(token.getText()); + } + } + else if (child instanceof PsiDocTagValue) { + buf.append(child.getText()); + } + else if (child instanceof PsiInlineDocTag) { + buf.append(child.getText()); + } + } - String s = buf.toString(); - return s.trim(); - } - - @RequiredReadAction - private static String extractThrowsTagDescription(PsiDocTag tag) { - StringBuilder buf = new StringBuilder(); - PsiElement[] children = tag.getChildren(); - for (PsiElement child : children) { - if (child instanceof PsiDocToken) { - PsiDocToken token = (PsiDocToken)child; - if (token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) { - buf.append(token.getText()); - } - } + String s = buf.toString(); + return s.trim(); } - return buf.toString().trim(); - } - - private void checkForBadCharacters(PsiDocComment docComment, - final ArrayList problems, - final InspectionManager manager, final boolean onTheFly) { - docComment.accept(new PsiRecursiveElementVisitor(){ - @Override - public void visitElement(PsiElement element) { - super.visitElement(element); - final ASTNode node = element.getNode(); - if (node != null) { - if (node.getElementType() == JavaDocTokenType.DOC_COMMENT_BAD_CHARACTER) { - problems.add(manager.createProblemDescriptor(element, "Illegal character", (LocalQuickFix)null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, onTheFly)); - } - } - } - }); - } - - @RequiredReadAction - private void checkForPeriodInDoc( - PsiDocComment docComment, - ArrayList problems, - InspectionManager manager, - boolean onTheFly - ) { - if (IGNORE_JAVADOC_PERIOD) return; - PsiDocTag[] tags = docComment.getTags(); - int dotIndex = docComment.getText().indexOf('.'); - int tagOffset = 0; - if (dotIndex >= 0) { //need to find first valid tag - final PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(docComment, PsiDocCommentOwner.class); - for (PsiDocTag tag : tags) { - final String tagName = tag.getName(); - final JavadocTagInfo tagInfo = JavadocManager.SERVICE.getInstance(tag.getProject()).getTagInfo(tagName); - if (tagInfo != null && tagInfo.isValidInContext(owner) && !tagInfo.isInline()) { - tagOffset = tag.getTextOffset(); - break; - } - } - } + @RequiredReadAction + private static String extractThrowsTagDescription(PsiDocTag tag) { + StringBuilder buf = new StringBuilder(); + PsiElement[] children = tag.getChildren(); + for (PsiElement child : children) { + if (child instanceof PsiDocToken token && token.getTokenType() == JavaDocTokenType.DOC_COMMENT_DATA) { + buf.append(token.getText()); + } + } - if (dotIndex == -1 || tagOffset > 0 && dotIndex + docComment.getTextOffset() > tagOffset) { - problems.add(manager.createProblemDescriptor( - docComment.getFirstChild(), - InspectionLocalize.inspectionJavadocProblemDescriptor1().get(), - null, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - onTheFly, - false - )); - } - } - - @Nullable - @RequiredReadAction - private ArrayList getTagValuesProblems( - PsiDocCommentOwner context, - PsiDocTag[] tags, - InspectionManager inspectionManager, - boolean isOnTheFly - ) { - final ArrayList problems = new ArrayList<>(2); - for (PsiDocTag tag : tags) { - final JavadocManager manager = JavadocManager.SERVICE.getInstance(tag.getProject()); - String tagName = tag.getName(); - JavadocTagInfo tagInfo = manager.getTagInfo(tagName); - - if ((tagInfo == null || !tagInfo.isValidInContext(context)) - && checkTagInfo(inspectionManager, tagInfo, tag, isOnTheFly, problems)) { - continue; - } - - PsiDocTagValue value = tag.getValueElement(); - final JavadocTagInfo info = manager.getTagInfo(tagName); - if (info != null && !info.isValidInContext(context)) continue; - String message = info == null ? null : info.checkTagValue(value); - - final PsiReference reference = value != null ? value.getReference() : null; - if (message == null && reference != null) { - PsiElement element = reference.resolve(); - if (element == null) { - final int textOffset = value.getTextOffset(); - - if (textOffset == value.getTextRange().getEndOffset()) { - problems.add(inspectionManager.createProblemDescriptor( - tag, - InspectionLocalize.inspectionJavadocProblemNameExpected().get(), - null, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - isOnTheFly, - true - )); - } - } - } - - if (message != null) { - final PsiDocTagValue valueElement = tag.getValueElement(); - if (valueElement == null){ - problems.add(inspectionManager.createProblemDescriptor( - tag, - InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription("" + tag.getName() + "").get(), - null, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - isOnTheFly, - true - )); - } else { - problems.add(createDescriptor(valueElement, message, inspectionManager, isOnTheFly)); - } - } - checkInlineTags(inspectionManager, problems, tag.getDataElements(), manager, isOnTheFly); + return buf.toString().trim(); + } + + private void checkForBadCharacters( + PsiDocComment docComment, + final List problems, + final InspectionManager manager, final boolean onTheFly + ) { + docComment.accept(new PsiRecursiveElementVisitor() { + @Override + @RequiredReadAction + public void visitElement(PsiElement element) { + super.visitElement(element); + ASTNode node = element.getNode(); + if (node != null && node.getElementType() == JavaDocTokenType.DOC_COMMENT_BAD_CHARACTER) { + problems.add( + manager.newProblemDescriptor(LocalizeValue.localizeTODO("Illegal character")) + .range(element) + .onTheFly(onTheFly) + .create() + ); + } + } + }); } - return problems.isEmpty() ? null : problems; - } + @RequiredReadAction + private void checkForPeriodInDoc( + PsiDocComment docComment, + List problems, + InspectionManager manager, + boolean onTheFly + ) { + if (IGNORE_JAVADOC_PERIOD) { + return; + } + PsiDocTag[] tags = docComment.getTags(); + int dotIndex = docComment.getText().indexOf('.'); + int tagOffset = 0; + if (dotIndex >= 0) { //need to find first valid tag + PsiDocCommentOwner owner = PsiTreeUtil.getParentOfType(docComment, PsiDocCommentOwner.class); + for (PsiDocTag tag : tags) { + String tagName = tag.getName(); + JavadocTagInfo tagInfo = JavadocManager.SERVICE.getInstance(tag.getProject()).getTagInfo(tagName); + if (tagInfo != null && tagInfo.isValidInContext(owner) && !tagInfo.isInline()) { + tagOffset = tag.getTextOffset(); + break; + } + } + } - private boolean checkTagInfo(InspectionManager inspectionManager, JavadocTagInfo tagInfo, PsiDocTag tag, boolean isOnTheFly, ArrayList problems) { - final String tagName = tag.getName(); - final StringTokenizer tokenizer = new StringTokenizer(myAdditionalJavadocTags, ", "); - while (tokenizer.hasMoreTokens()) { - if (Comparing.strEqual(tagName, tokenizer.nextToken())) return true; + if (dotIndex == -1 || tagOffset > 0 && dotIndex + docComment.getTextOffset() > tagOffset) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDescriptor1()) + .range(docComment.getFirstChild()) + .onTheFly(onTheFly) + .create() + ); + } } - final PsiElement nameElement = tag.getNameElement(); - if (nameElement != null) { - if (tagInfo == null) { - problems.add( - createDescriptor( - nameElement, - InspectionLocalize.inspectionJavadocProblemWrongTag("" + tagName + "").get(), - new AddUnknownTagToCustoms(tag.getName()), - inspectionManager, - isOnTheFly - )); - } - else { - problems.add(createDescriptor( - nameElement, - InspectionLocalize.inspectionJavadocProblemDisallowedTag("" + tagName + "").get(), - new AddUnknownTagToCustoms(tag.getName()), inspectionManager, isOnTheFly) - ); - } - } - return false; - } - - @RequiredReadAction - private void checkInlineTags( - final InspectionManager inspectionManager, - final ArrayList problems, - final PsiElement[] dataElements, - final JavadocManager manager, - boolean isOnTheFly - ) { - for (PsiElement dataElement : dataElements) { - if (dataElement instanceof PsiInlineDocTag) { - final PsiInlineDocTag inlineDocTag = (PsiInlineDocTag)dataElement; - final PsiElement nameElement = inlineDocTag.getNameElement(); - if (manager.getTagInfo(inlineDocTag.getName()) == null) { - checkTagInfo(inspectionManager, null, inlineDocTag, isOnTheFly, problems); - } - if (!IGNORE_POINT_TO_ITSELF) { - final PsiDocTagValue value = inlineDocTag.getValueElement(); - if (value != null) { - final PsiReference reference = value.getReference(); - if (reference != null) { - final PsiElement ref = reference.resolve(); - if (ref != null){ - if (PsiTreeUtil.getParentOfType(inlineDocTag, PsiDocCommentOwner.class) - == PsiTreeUtil.getParentOfType(ref, PsiDocCommentOwner.class, false)) { - if (nameElement != null) { - problems.add(createDescriptor( - nameElement, - InspectionLocalize.inspectionJavadocProblemPointingToItself().get(), - inspectionManager, - isOnTheFly - )); - } + @Nullable + @RequiredReadAction + private List getTagValuesProblems( + PsiDocCommentOwner context, + PsiDocTag[] tags, + InspectionManager inspectionManager, + boolean isOnTheFly + ) { + List problems = new ArrayList<>(2); + for (PsiDocTag tag : tags) { + JavadocManager manager = JavadocManager.SERVICE.getInstance(tag.getProject()); + String tagName = tag.getName(); + JavadocTagInfo tagInfo = manager.getTagInfo(tagName); + + if ((tagInfo == null || !tagInfo.isValidInContext(context)) + && checkTagInfo(inspectionManager, tagInfo, tag, isOnTheFly, problems)) { + continue; + } + + PsiDocTagValue value = tag.getValueElement(); + JavadocTagInfo info = manager.getTagInfo(tagName); + if (info != null && !info.isValidInContext(context)) { + continue; + } + String message = info == null ? null : info.checkTagValue(value); + + PsiReference reference = value != null ? value.getReference() : null; + if (message == null && reference != null) { + PsiElement element = reference.resolve(); + if (element == null) { + int textOffset = value.getTextOffset(); + + if (textOffset == value.getTextRange().getEndOffset()) { + problems.add( + inspectionManager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemNameExpected()) + .range(tag) + .afterEndOfLine(true) + .onTheFly(isOnTheFly) + .create() + ); + } } - } } - } + + if (message != null) { + PsiDocTagValue valueElement = tag.getValueElement(); + if (valueElement == null) { + problems.add( + inspectionManager.newProblemDescriptor(InspectionLocalize.inspectionJavadocMethodProblemMissingTagDescription( + "" + tag.getName() + "" + )) + .range(tag) + .afterEndOfLine(true) + .onTheFly(isOnTheFly) + .create() + ); + } + else { + problems.add( + inspectionManager.newProblemDescriptor(LocalizeValue.localizeTODO(message)) + .range(valueElement) + .onTheFly(isOnTheFly) + .create() + ); + } + } + checkInlineTags(inspectionManager, problems, tag.getDataElements(), manager, isOnTheFly); } - } + + return problems.isEmpty() ? null : problems; } - } - @SuppressWarnings({"SimplifiableIfStatement"}) - private boolean isTagRequired(PsiElement context, @NonNls String tag) { - if (context instanceof PsiClass) { - if (PsiTreeUtil.getParentOfType(context, PsiClass.class) != null) { - return isTagRequired(INNER_CLASS_OPTIONS, tag); - } + @RequiredReadAction + private boolean checkTagInfo( + InspectionManager inspectionManager, + JavadocTagInfo tagInfo, + PsiDocTag tag, + boolean isOnTheFly, + List problems + ) { + String tagName = tag.getName(); + StringTokenizer tokenizer = new StringTokenizer(myAdditionalJavadocTags, ", "); + while (tokenizer.hasMoreTokens()) { + if (Comparing.strEqual(tagName, tokenizer.nextToken())) { + return true; + } + } - return isTagRequired(TOP_LEVEL_CLASS_OPTIONS, tag); + PsiElement nameElement = tag.getNameElement(); + if (nameElement != null) { + problems.add( + inspectionManager.newProblemDescriptor( + tagInfo == null + ? InspectionLocalize.inspectionJavadocProblemWrongTag("" + tagName + "") + : InspectionLocalize.inspectionJavadocProblemDisallowedTag("" + tagName + "") + ) + .range(nameElement) + .onTheFly(isOnTheFly) + .withFix(new AddUnknownTagToCustoms(tag.getName())) + .create() + ); + } + return false; } - if (context instanceof PsiMethod) { - return isTagRequired(METHOD_OPTIONS, tag); + @RequiredReadAction + private void checkInlineTags( + InspectionManager inspectionManager, + List problems, + PsiElement[] dataElements, + JavadocManager manager, + boolean isOnTheFly + ) { + for (PsiElement dataElement : dataElements) { + if (dataElement instanceof PsiInlineDocTag inlineDocTag) { + PsiElement nameElement = inlineDocTag.getNameElement(); + if (manager.getTagInfo(inlineDocTag.getName()) == null) { + checkTagInfo(inspectionManager, null, inlineDocTag, isOnTheFly, problems); + } + if (!IGNORE_POINT_TO_ITSELF) { + PsiDocTagValue value = inlineDocTag.getValueElement(); + if (value != null) { + PsiReference reference = value.getReference(); + if (reference != null) { + PsiElement ref = reference.resolve(); + if (ref != null && PsiTreeUtil.getParentOfType(inlineDocTag, PsiDocCommentOwner.class) + == PsiTreeUtil.getParentOfType(ref, PsiDocCommentOwner.class, false) + && nameElement != null) { + problems.add( + inspectionManager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemPointingToItself()) + .range(nameElement) + .onTheFly(isOnTheFly) + .create() + ); + } + } + } + } + } + } } - if (context instanceof PsiField) { - return isTagRequired(FIELD_OPTIONS, tag); - } + @SuppressWarnings({"SimplifiableIfStatement"}) + private boolean isTagRequired(PsiElement context, String tag) { + if (context instanceof PsiClass) { + if (PsiTreeUtil.getParentOfType(context, PsiClass.class) != null) { + return isTagRequired(INNER_CLASS_OPTIONS, tag); + } - return false; - } + return isTagRequired(TOP_LEVEL_CLASS_OPTIONS, tag); + } - private static boolean isTagRequired(Options options, String tag) { - return options.REQUIRED_TAGS.contains(tag); - } + if (context instanceof PsiMethod) { + return isTagRequired(METHOD_OPTIONS, tag); + } - private boolean isJavaDocRequired(PsiModifierListOwner psiElement) { - final RefJavaUtil refUtil = RefJavaUtil.getInstance(); - int actualAccess = getAccessNumber(refUtil.getAccessModifier(psiElement)); - if (psiElement instanceof PsiClass) { - PsiClass psiClass = (PsiClass)psiElement; - if (PsiTreeUtil.getParentOfType(psiClass, PsiClass.class) != null) { - return actualAccess <= getAccessNumber(INNER_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); - } + if (context instanceof PsiField) { + return isTagRequired(FIELD_OPTIONS, tag); + } - return actualAccess <= getAccessNumber(TOP_LEVEL_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + return false; } - if (psiElement instanceof PsiMethod) { - psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); - while (psiElement != null) { - actualAccess = Math.max(actualAccess, getAccessNumber(refUtil.getAccessModifier(psiElement))); - psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); - } - - return actualAccess <= getAccessNumber(METHOD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + private static boolean isTagRequired(Options options, String tag) { + return options.REQUIRED_TAGS.contains(tag); } - if (psiElement instanceof PsiField) { - psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); - while (psiElement != null) { - actualAccess = Math.max(actualAccess, getAccessNumber(refUtil.getAccessModifier(psiElement))); - psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); - } + private boolean isJavaDocRequired(PsiModifierListOwner psiElement) { + RefJavaUtil refUtil = RefJavaUtil.getInstance(); + int actualAccess = getAccessNumber(refUtil.getAccessModifier(psiElement)); + if (psiElement instanceof PsiClass psiClass) { + if (PsiTreeUtil.getParentOfType(psiClass, PsiClass.class) != null) { + return actualAccess <= getAccessNumber(INNER_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + } - return actualAccess <= getAccessNumber(FIELD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + return actualAccess <= getAccessNumber(TOP_LEVEL_CLASS_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + } + + if (psiElement instanceof PsiMethod) { + psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); + while (psiElement != null) { + actualAccess = Math.max(actualAccess, getAccessNumber(refUtil.getAccessModifier(psiElement))); + psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); + } + + return actualAccess <= getAccessNumber(METHOD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + } + + if (psiElement instanceof PsiField) { + psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); + while (psiElement != null) { + actualAccess = Math.max(actualAccess, getAccessNumber(refUtil.getAccessModifier(psiElement))); + psiElement = PsiTreeUtil.getParentOfType(psiElement, PsiClass.class); + } + + return actualAccess <= getAccessNumber(FIELD_OPTIONS.ACCESS_JAVADOC_REQUIRED_FOR); + } + + return false; } - return false; - } - - @RequiredReadAction - private void checkDuplicateTags( - final PsiDocTag[] tags, - ArrayList problems, - final InspectionManager manager, - boolean isOnTheFly - ) { - Set documentedParamNames = null; - Set documentedExceptions = null; - Set uniqueTags = null; - for (PsiDocTag tag: tags) { - if ("param".equals(tag.getName())) { - PsiDocTagValue value = tag.getValueElement(); - if (value instanceof PsiDocParamRef) { - PsiDocParamRef paramRef = (PsiDocParamRef)value; - final PsiReference reference = paramRef.getReference(); - if (reference != null) { - final String paramName = reference.getCanonicalText(); - if (documentedParamNames == null) { - documentedParamNames = new HashSet<>(); + @RequiredReadAction + private void checkDuplicateTags( + PsiDocTag[] tags, + List problems, + InspectionManager manager, + boolean isOnTheFly + ) { + Set documentedParamNames = null; + Set documentedExceptions = null; + Set uniqueTags = null; + for (PsiDocTag tag : tags) { + if ("param".equals(tag.getName())) { + if (tag.getValueElement() instanceof PsiDocParamRef paramRef) { + PsiReference reference = paramRef.getReference(); + if (reference != null) { + String paramName = reference.getCanonicalText(); + if (documentedParamNames == null) { + documentedParamNames = new HashSet<>(); + } + if (documentedParamNames.contains(paramName)) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDuplicateParam(paramName)) + .range(tag.getNameElement()) + .onTheFly(isOnTheFly) + .create() + ); + } + documentedParamNames.add(paramName); + } + } } - if (documentedParamNames.contains(paramName)) { - problems.add(createDescriptor( - tag.getNameElement(), - InspectionLocalize.inspectionJavadocProblemDuplicateParam(paramName).get(), - manager, - isOnTheFly - )); + else if (!IGNORE_DUPLICATED_THROWS && ("throws".equals(tag.getName()) || "exception".equals(tag.getName()))) { + PsiDocTagValue value = tag.getValueElement(); + if (value != null) { + PsiElement firstChild = value.getFirstChild(); + if (firstChild != null + && firstChild.getFirstChild() instanceof PsiJavaCodeReferenceElement refElement + && refElement.resolve() instanceof PsiClass psiClass) { + String fqName = psiClass.getQualifiedName(); + if (documentedExceptions == null) { + documentedExceptions = new HashSet<>(); + } + if (documentedExceptions.contains(fqName)) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDuplicateThrows(fqName)) + .range(tag.getNameElement()) + .onTheFly(isOnTheFly) + .create() + ); + } + documentedExceptions.add(fqName); + } + } } - documentedParamNames.add(paramName); - } - } - } - else if (!IGNORE_DUPLICATED_THROWS && ("throws".equals(tag.getName()) || "exception".equals(tag.getName()))) { - PsiDocTagValue value = tag.getValueElement(); - if (value != null) { - final PsiElement firstChild = value.getFirstChild(); - if (firstChild != null && firstChild.getFirstChild() instanceof PsiJavaCodeReferenceElement) { - PsiJavaCodeReferenceElement refElement = (PsiJavaCodeReferenceElement) firstChild.getFirstChild(); - if (refElement != null) { - PsiElement element = refElement.resolve(); - if (element instanceof PsiClass) { - String fqName = ((PsiClass)element).getQualifiedName(); - if (documentedExceptions == null) { - documentedExceptions = new HashSet<>(); + else if (JavaDocLocalInspection.ourUniqueTags.contains(tag.getName())) { + if (uniqueTags == null) { + uniqueTags = new HashSet<>(); } - if (documentedExceptions.contains(fqName)) { - problems.add(createDescriptor( - tag.getNameElement(), - InspectionLocalize.inspectionJavadocProblemDuplicateThrows(fqName).get(), - manager, - isOnTheFly - )); + if (uniqueTags.contains(tag.getName())) { + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemDuplicateTag(tag.getName())) + .range(tag.getNameElement()) + .onTheFly(isOnTheFly) + .create() + ); } - documentedExceptions.add(fqName); - } + uniqueTags.add(tag.getName()); } - } } - } - else if (JavaDocLocalInspection.ourUniqueTags.contains(tag.getName())) { - if (uniqueTags == null) { - uniqueTags = new HashSet<>(); + } + + private static int getAccessNumber(String accessModifier) { + if (accessModifier.startsWith("none")) { + return 0; } - if (uniqueTags.contains(tag.getName())) { - problems.add(createDescriptor( - tag.getNameElement(), - InspectionLocalize.inspectionJavadocProblemDuplicateTag(tag.getName()).get(), - manager, - isOnTheFly - )); + if (accessModifier.startsWith("public")) { + return 1; + } + if (accessModifier.startsWith("protected")) { + return 2; + } + if (accessModifier.startsWith("package")) { + return 3; } - uniqueTags.add(tag.getName()); - } + if (accessModifier.startsWith("private")) { + return 4; + } + + return 5; } - } - - private static int getAccessNumber(@NonNls String accessModifier) { - if (accessModifier.startsWith("none")) return 0; - if (accessModifier.startsWith("public")) return 1; - if (accessModifier.startsWith("protected")) return 2; - if (accessModifier.startsWith("package")) return 3; - if (accessModifier.startsWith("private")) return 4; - - return 5; - } - - @Override - @Nonnull - public LocalizeValue getDisplayName() { - return InspectionLocalize.inspectionJavadocDisplayName(); - } - - @Override - @Nonnull - public LocalizeValue getGroupDisplayName() { - return InspectionLocalize.groupNamesJavadocIssues(); - } - - @Override - @Nonnull - public String getShortName() { - return SHORT_NAME; - } - - public void setIgnoreEmptyDescriptions(boolean ignoreEmptyDescriptions) { - myIgnoreEmptyDescriptions = ignoreEmptyDescriptions; - } - - private class AddUnknownTagToCustoms implements LocalQuickFix { - private final String myTag; - - public AddUnknownTagToCustoms(String tag) { - myTag = tag; + + @Override + @Nonnull + public LocalizeValue getDisplayName() { + return InspectionLocalize.inspectionJavadocDisplayName(); } @Override @Nonnull - public LocalizeValue getName() { - return JavaQuickFixLocalize.addDoctagToCustomTags(myTag); + public LocalizeValue getGroupDisplayName() { + return InspectionLocalize.groupNamesJavadocIssues(); } @Override - public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { - if (myTag == null) return; - if (myAdditionalJavadocTags.length() > 0) { - myAdditionalJavadocTags += "," + myTag; - } - else { - myAdditionalJavadocTags = myTag; - } - final InspectionProfile inspectionProfile = - InspectionProjectProfileManager.getInstance(project).getInspectionProfile(); - //correct save settings - InspectionProfileManager.getInstance().fireProfileChanged(inspectionProfile); - //TODO lesya - - /* - - try { - inspectionProfile.save(); - } - catch (IOException e) { - Messages.showErrorDialog(project, e.getMessage(), CommonBundle.getErrorTitle()); - } - - */ + @Nonnull + public String getShortName() { + return SHORT_NAME; + } + + public void setIgnoreEmptyDescriptions(boolean ignoreEmptyDescriptions) { + myIgnoreEmptyDescriptions = ignoreEmptyDescriptions; + } + + private class AddUnknownTagToCustoms implements LocalQuickFix { + private final String myTag; + + public AddUnknownTagToCustoms(String tag) { + myTag = tag; + } + + @Override + @Nonnull + public LocalizeValue getName() { + return JavaQuickFixLocalize.addDoctagToCustomTags(myTag); + } + + @Override + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + if (myTag == null) { + return; + } + if (myAdditionalJavadocTags.length() > 0) { + myAdditionalJavadocTags += "," + myTag; + } + else { + myAdditionalJavadocTags = myTag; + } + InspectionProfile inspectionProfile = + InspectionProjectProfileManager.getInstance(project).getInspectionProfile(); + //correct save settings + InspectionProfileManager.getInstance().fireProfileChanged(inspectionProfile); + //TODO lesya + /* + try { + inspectionProfile.save(); + } + catch (IOException e) { + Messages.showErrorDialog(project, e.getMessage(), CommonBundle.getErrorTitle()); + } + */ + } } - } } diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocReferenceInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocReferenceInspection.java index b01a41ba42..a6b58f0c5f 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocReferenceInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/javaDoc/JavaDocReferenceInspection.java @@ -29,7 +29,6 @@ import consulo.dataContext.DataManager; import consulo.document.util.TextRange; import consulo.ide.impl.ui.impl.PopupChooserBuilder; -import consulo.java.analysis.impl.JavaQuickFixBundle; import consulo.java.analysis.impl.localize.JavaQuickFixLocalize; import consulo.language.editor.FileModificationService; import consulo.language.editor.WriteCommandAction; @@ -51,353 +50,349 @@ import consulo.language.util.proximity.PsiProximityComparator; import consulo.localize.LocalizeValue; import consulo.project.Project; +import consulo.ui.annotation.RequiredUIAccess; import consulo.ui.ex.awt.JBList; import consulo.util.concurrent.AsyncResult; import consulo.util.lang.StringUtil; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; -import org.jetbrains.annotations.NonNls; import javax.swing.*; import java.util.*; @ExtensionImpl public class JavaDocReferenceInspection extends BaseLocalInspectionTool { - @NonNls - public static final String SHORT_NAME = "JavadocReference"; - - private static ProblemDescriptor createDescriptor( - @Nonnull PsiElement element, - String template, - InspectionManager manager, - boolean onTheFly - ) { - return manager.createProblemDescriptor(element, template, onTheFly, null, ProblemHighlightType.LIKE_UNKNOWN_SYMBOL); - } - - @Override - @Nullable - @RequiredReadAction - public ProblemDescriptor[] checkMethod(@Nonnull PsiMethod psiMethod, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - return checkMember(psiMethod, manager, isOnTheFly); - } - - @Override - @Nullable - @RequiredReadAction - public ProblemDescriptor[] checkField(@Nonnull PsiField field, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - return checkMember(field, manager, isOnTheFly); - } - - @Override - @Nullable - @RequiredReadAction - public ProblemDescriptor[] checkClass(@Nonnull PsiClass aClass, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { - return checkMember(aClass, manager, isOnTheFly); - } - - @Nullable - @RequiredReadAction - private ProblemDescriptor[] checkMember( - final PsiDocCommentOwner docCommentOwner, - final InspectionManager manager, - final boolean isOnTheFly - ) { - final ArrayList problems = new ArrayList<>(); - final PsiDocComment docComment = docCommentOwner.getDocComment(); - if (docComment == null) { - return null; - } + public static final String SHORT_NAME = "JavadocReference"; - final Set references = new HashSet<>(); - docComment.accept(getVisitor(references, docCommentOwner, problems, manager, isOnTheFly)); - for (PsiJavaCodeReferenceElement reference : references) { - final List classesToImport = new ImportClassFix(reference).getClassesToImport(); - final PsiElement referenceNameElement = reference.getReferenceNameElement(); - problems.add(manager.createProblemDescriptor( - referenceNameElement != null ? referenceNameElement : reference, - cannotResolveSymbolMessage("" + reference.getText() + ""), - !isOnTheFly || classesToImport.isEmpty() ? null : new AddQualifierFix(classesToImport), - ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, - isOnTheFly - )); + @Override + @Nullable + @RequiredReadAction + public ProblemDescriptor[] checkMethod( + @Nonnull PsiMethod psiMethod, + @Nonnull InspectionManager manager, + boolean isOnTheFly, + Object state + ) { + return checkMember(psiMethod, manager, isOnTheFly); } - return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]); - } - - private PsiElementVisitor getVisitor( - final Set references, - final PsiElement context, - final ArrayList problems, - final InspectionManager manager, - final boolean onTheFly - ) { - return new JavaElementVisitor() { - @Override - @RequiredReadAction - public void visitReferenceExpression(@Nonnull PsiReferenceExpression expression) { - visitElement(expression); - } - - @Override - public void visitReferenceElement(@Nonnull PsiJavaCodeReferenceElement reference) { - super.visitReferenceElement(reference); - JavaResolveResult result = reference.advancedResolve(false); - if (result.getElement() == null && !result.isPackagePrefixPackageReference()) { - references.add(reference); - } - } - - @Override - @RequiredReadAction - public void visitDocTag(@Nonnull PsiDocTag tag) { - super.visitDocTag(tag); - final JavadocManager javadocManager = JavadocManager.SERVICE.getInstance(tag.getProject()); - final JavadocTagInfo info = javadocManager.getTagInfo(tag.getName()); - if (info == null || !info.isInline()) { - visitRefInDocTag(tag, javadocManager, context, problems, manager, onTheFly); - } - } - - @Override - @RequiredReadAction - public void visitInlineDocTag(@Nonnull PsiInlineDocTag tag) { - super.visitInlineDocTag(tag); - final JavadocManager javadocManager = JavadocManager.SERVICE.getInstance(tag.getProject()); - visitRefInDocTag(tag, javadocManager, context, problems, manager, onTheFly); - } - - @Override - @RequiredReadAction - public void visitElement(PsiElement element) { - PsiElement[] children = element.getChildren(); - for (PsiElement child : children) { - //do not visit method javadoc twice - if (!(child instanceof PsiDocCommentOwner)) { - child.accept(this); - } - } - } - }; - } - - @RequiredReadAction - public static void visitRefInDocTag( - final PsiDocTag tag, - final JavadocManager manager, - final PsiElement context, - final ArrayList problems, - final InspectionManager inspectionManager, - final boolean onTheFly - ) { - final String tagName = tag.getName(); - final PsiDocTagValue value = tag.getValueElement(); - if (value == null) { - return; - } - final JavadocTagInfo info = manager.getTagInfo(tagName); - if (info != null && !info.isValidInContext(context)) { - return; - } - final String message = info == null || !info.isInline() ? null : info.checkTagValue(value); - if (message != null) { - problems.add(createDescriptor(value, message, inspectionManager, onTheFly)); + @Override + @Nullable + @RequiredReadAction + public ProblemDescriptor[] checkField(@Nonnull PsiField field, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { + return checkMember(field, manager, isOnTheFly); } - final PsiReference reference = value.getReference(); - if (reference == null) { - return; - } - final PsiElement element = reference.resolve(); - if (element != null) { - return; - } - final int textOffset = value.getTextOffset(); - if (textOffset == value.getTextRange().getEndOffset()) { - return; - } - final PsiDocTagValue valueElement = tag.getValueElement(); - if (valueElement == null) { - return; + @Override + @Nullable + @RequiredReadAction + public ProblemDescriptor[] checkClass(@Nonnull PsiClass aClass, @Nonnull InspectionManager manager, boolean isOnTheFly, Object state) { + return checkMember(aClass, manager, isOnTheFly); } - final CharSequence paramName = - value.getContainingFile().getViewProvider().getContents().subSequence(textOffset, value.getTextRange().getEndOffset()); - final String params = "" + paramName + ""; - final List fixes = new ArrayList<>(); - if (onTheFly && "param".equals(tagName)) { - final PsiDocCommentOwner commentOwner = PsiTreeUtil.getParentOfType(tag, PsiDocCommentOwner.class); - if (commentOwner instanceof PsiMethod method) { - final PsiParameter[] parameters = method.getParameterList().getParameters(); - final PsiDocTag[] tags = tag.getContainingComment().getTags(); - final Set unboundParams = new HashSet<>(); - for (PsiParameter parameter : parameters) { - if (!JavaDocLocalInspection.isFound(tags, parameter)) { - unboundParams.add(parameter.getName()); - } + @Nullable + @RequiredReadAction + private ProblemDescriptor[] checkMember( + PsiDocCommentOwner docCommentOwner, + InspectionManager manager, + boolean isOnTheFly + ) { + List problems = new ArrayList<>(); + PsiDocComment docComment = docCommentOwner.getDocComment(); + if (docComment == null) { + return null; } - if (!unboundParams.isEmpty()) { - fixes.add(new RenameReferenceQuickFix(unboundParams)); + + Set references = new HashSet<>(); + docComment.accept(getVisitor(references, docCommentOwner, problems, manager, isOnTheFly)); + for (PsiJavaCodeReferenceElement reference : references) { + List classesToImport = new ImportClassFix(reference).getClassesToImport(); + PsiElement referenceNameElement = reference.getReferenceNameElement(); + PsiElement psiElement = referenceNameElement != null ? referenceNameElement : reference; + problems.add( + manager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemCannotResolve("" + reference.getText() + "")) + .range(psiElement) + .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .onTheFly(isOnTheFly) + .withOptionalFix(!isOnTheFly || classesToImport.isEmpty() ? null : new AddQualifierFix(classesToImport)) + .create() + ); } - } - } - fixes.add(new RemoveTagFix(tagName, paramName)); - - problems.add(inspectionManager.createProblemDescriptor( - valueElement, - reference.getRangeInElement(), - cannotResolveSymbolMessage(params), - ProblemHighlightType.LIKE_UNKNOWN_SYMBOL, - onTheFly, - fixes.toArray(new LocalQuickFix[fixes.size()]) - )); - } - - private static String cannotResolveSymbolMessage(String params) { - return InspectionLocalize.inspectionJavadocProblemCannotResolve(params).get(); - } - - @Override - @Nonnull - public LocalizeValue getDisplayName() { - return InspectionLocalize.inspectionJavadocRefDisplayName(); - } - - @Override - @Nonnull - public LocalizeValue getGroupDisplayName() { - return InspectionLocalize.groupNamesJavadocIssues(); - } - - @Override - @Nonnull - public String getShortName() { - return SHORT_NAME; - } - - @Override - @Nonnull - public HighlightDisplayLevel getDefaultLevel() { - return HighlightDisplayLevel.ERROR; - } - - private class AddQualifierFix implements LocalQuickFix { - private final List originalClasses; - - public AddQualifierFix(final List originalClasses) { - this.originalClasses = originalClasses; + + return problems.isEmpty() ? null : problems.toArray(new ProblemDescriptor[problems.size()]); } - @Override - @Nonnull - public LocalizeValue getName() { - return JavaQuickFixLocalize.addQualifier(); + private PsiElementVisitor getVisitor( + final Set references, + final PsiElement context, + final List problems, + final InspectionManager manager, + final boolean onTheFly + ) { + return new JavaElementVisitor() { + @Override + @RequiredReadAction + public void visitReferenceExpression(@Nonnull PsiReferenceExpression expression) { + visitElement(expression); + } + + @Override + public void visitReferenceElement(@Nonnull PsiJavaCodeReferenceElement reference) { + super.visitReferenceElement(reference); + JavaResolveResult result = reference.advancedResolve(false); + if (result.getElement() == null && !result.isPackagePrefixPackageReference()) { + references.add(reference); + } + } + + @Override + @RequiredReadAction + public void visitDocTag(@Nonnull PsiDocTag tag) { + super.visitDocTag(tag); + JavadocManager javadocManager = JavadocManager.SERVICE.getInstance(tag.getProject()); + JavadocTagInfo info = javadocManager.getTagInfo(tag.getName()); + if (info == null || !info.isInline()) { + visitRefInDocTag(tag, javadocManager, context, problems, manager, onTheFly); + } + } + + @Override + @RequiredReadAction + public void visitInlineDocTag(@Nonnull PsiInlineDocTag tag) { + super.visitInlineDocTag(tag); + JavadocManager javadocManager = JavadocManager.SERVICE.getInstance(tag.getProject()); + visitRefInDocTag(tag, javadocManager, context, problems, manager, onTheFly); + } + + @Override + @RequiredReadAction + public void visitElement(PsiElement element) { + for (PsiElement child : element.getChildren()) { + //do not visit method javadoc twice + if (!(child instanceof PsiDocCommentOwner)) { + child.accept(this); + } + } + } + }; } - @Override - @RequiredWriteAction - public void applyFix(@Nonnull final Project project, @Nonnull final ProblemDescriptor descriptor) { - final PsiElement element = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiJavaCodeReferenceElement.class); - if (element instanceof PsiJavaCodeReferenceElement) { - final PsiJavaCodeReferenceElement referenceElement = (PsiJavaCodeReferenceElement)element; - Collections.sort(originalClasses, new PsiProximityComparator(referenceElement.getElement())); - final JList list = new JBList<>(originalClasses.toArray(new PsiClass[originalClasses.size()])); - list.setCellRenderer(new FQNameCellRenderer()); - final Runnable runnable = () -> { - if (!element.isValid()) { + @RequiredReadAction + public static void visitRefInDocTag( + PsiDocTag tag, + JavadocManager manager, + PsiElement context, + List problems, + InspectionManager inspectionManager, + boolean onTheFly + ) { + String tagName = tag.getName(); + PsiDocTagValue value = tag.getValueElement(); + if (value == null) { return; - } - final int index = list.getSelectedIndex(); - if (index < 0) { + } + JavadocTagInfo info = manager.getTagInfo(tagName); + if (info != null && !info.isValidInContext(context)) { return; - } - new WriteCommandAction(project, element.getContainingFile()) { - @Override - @RequiredWriteAction - protected void run(final Result result) throws Throwable { - final PsiClass psiClass = originalClasses.get(index); - if (psiClass.isValid()) { - PsiDocumentManager.getInstance(project).commitAllDocuments(); - referenceElement.bindToElement(psiClass); - } + } + String message = info == null || !info.isInline() ? null : info.checkTagValue(value); + if (message != null) { + problems.add(inspectionManager.newProblemDescriptor(LocalizeValue.of(message)) + .range(value) + .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .onTheFly(onTheFly) + .create()); + } + + PsiReference reference = value.getReference(); + if (reference == null) { + return; + } + PsiElement element = reference.resolve(); + if (element != null) { + return; + } + int textOffset = value.getTextOffset(); + if (textOffset == value.getTextRange().getEndOffset()) { + return; + } + PsiDocTagValue valueElement = tag.getValueElement(); + if (valueElement == null) { + return; + } + + CharSequence paramName = + value.getContainingFile().getViewProvider().getContents().subSequence(textOffset, value.getTextRange().getEndOffset()); + String params = "" + paramName + ""; + List fixes = new ArrayList<>(); + if (onTheFly && "param".equals(tagName)) { + PsiDocCommentOwner commentOwner = PsiTreeUtil.getParentOfType(tag, PsiDocCommentOwner.class); + if (commentOwner instanceof PsiMethod method) { + PsiParameter[] parameters = method.getParameterList().getParameters(); + PsiDocTag[] tags = tag.getContainingComment().getTags(); + Set unboundParams = new HashSet<>(); + for (PsiParameter parameter : parameters) { + if (!JavaDocLocalInspection.isFound(tags, parameter)) { + unboundParams.add(parameter.getName()); + } + } + if (!unboundParams.isEmpty()) { + fixes.add(new RenameReferenceQuickFix(unboundParams)); + } } - }.execute(); - }; - final AsyncResult asyncResult = DataManager.getInstance().getDataContextFromFocus(); - asyncResult.doWhenDone( - dataContext -> new PopupChooserBuilder(list) - .setTitle(JavaQuickFixBundle.message("add.qualifier.original.class.chooser.title")) - .setItemChoosenCallback(runnable) - .createPopup() - .showInBestPositionFor(dataContext) + } + fixes.add(new RemoveTagFix(tagName, paramName)); + + problems.add( + inspectionManager.newProblemDescriptor(InspectionLocalize.inspectionJavadocProblemCannotResolve(params)) + .range(valueElement, reference.getRangeInElement()) + .highlightType(ProblemHighlightType.LIKE_UNKNOWN_SYMBOL) + .onTheFly(onTheFly) + .withFixes(fixes) + .create() ); - } } - } - - private static class RenameReferenceQuickFix implements LocalQuickFix { - private final Set myUnboundParams; - public RenameReferenceQuickFix(Set unboundParams) { - myUnboundParams = unboundParams; + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return InspectionLocalize.inspectionJavadocRefDisplayName(); } + @Nonnull @Override + public LocalizeValue getGroupDisplayName() { + return InspectionLocalize.groupNamesJavadocIssues(); + } + @Nonnull - public LocalizeValue getName() { - return LocalizeValue.localizeTODO("Change to ..."); + @Override + public String getShortName() { + return SHORT_NAME; } + @Nonnull @Override - public void applyFix(@Nonnull final Project project, @Nonnull final ProblemDescriptor descriptor) { - final AsyncResult asyncResult = DataManager.getInstance().getDataContextFromFocus(); - asyncResult.doWhenDone(dataContext -> { - final Editor editor = dataContext.getData(Editor.KEY); - assert editor != null; - final TextRange textRange = ((ProblemDescriptorBase)descriptor).getTextRange(); - editor.getSelectionModel().setSelection(textRange.getStartOffset(), textRange.getEndOffset()); - - final String word = editor.getSelectionModel().getSelectedText(); - - if (word == null || StringUtil.isEmptyOrSpaces(word)) { - return; + public HighlightDisplayLevel getDefaultLevel() { + return HighlightDisplayLevel.ERROR; + } + + private class AddQualifierFix implements LocalQuickFix { + private final List originalClasses; + + public AddQualifierFix(List originalClasses) { + this.originalClasses = originalClasses; } - final List items = new ArrayList<>(); - for (String variant : myUnboundParams) { - items.add(LookupElementBuilder.create(variant)); + + @Nonnull + @Override + public LocalizeValue getName() { + return JavaQuickFixLocalize.addQualifier(); + } + + @Override + @RequiredWriteAction + public void applyFix(@Nonnull final Project project, @Nonnull ProblemDescriptor descriptor) { + PsiElement element = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiJavaCodeReferenceElement.class); + if (element instanceof PsiJavaCodeReferenceElement refElem) { + Collections.sort(originalClasses, new PsiProximityComparator(refElem.getElement())); + JList list = new JBList<>(originalClasses.toArray(new PsiClass[originalClasses.size()])); + list.setCellRenderer(new FQNameCellRenderer()); + @RequiredUIAccess + Runnable runnable = () -> { + if (!refElem.isValid()) { + return; + } + final int index = list.getSelectedIndex(); + if (index < 0) { + return; + } + new WriteCommandAction(project, refElem.getContainingFile()) { + @Override + @RequiredWriteAction + protected void run(Result result) throws Throwable { + PsiClass psiClass = originalClasses.get(index); + if (psiClass.isValid()) { + PsiDocumentManager.getInstance(project).commitAllDocuments(); + refElem.bindToElement(psiClass); + } + } + }.execute(); + }; + AsyncResult asyncResult = DataManager.getInstance().getDataContextFromFocus(); + asyncResult.doWhenDone( + dataContext -> new PopupChooserBuilder(list) + .setTitle(JavaQuickFixLocalize.addQualifierOriginalClassChooserTitle().get()) + .setItemChoosenCallback(runnable) + .createPopup() + .showInBestPositionFor(dataContext) + ); + } } - LookupManager.getInstance(project).showLookup(editor, items.toArray(new LookupElement[items.size()])); - }); } - } - private static class RemoveTagFix implements LocalQuickFix { - private final String myTagName; - private final CharSequence myParamName; + private static class RenameReferenceQuickFix implements LocalQuickFix { + private final Set myUnboundParams; - public RemoveTagFix(String tagName, CharSequence paramName) { - myTagName = tagName; - myParamName = paramName; - } + public RenameReferenceQuickFix(Set unboundParams) { + myUnboundParams = unboundParams; + } - @Override - @Nonnull - public LocalizeValue getName() { - return LocalizeValue.localizeTODO("Remove @" + myTagName + " " + myParamName); + @Nonnull + @Override + public LocalizeValue getName() { + return LocalizeValue.localizeTODO("Change to ..."); + } + + @Override + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + AsyncResult asyncResult = DataManager.getInstance().getDataContextFromFocus(); + asyncResult.doWhenDone(dataContext -> { + Editor editor = dataContext.getData(Editor.KEY); + assert editor != null; + TextRange textRange = ((ProblemDescriptorBase) descriptor).getTextRange(); + editor.getSelectionModel().setSelection(textRange.getStartOffset(), textRange.getEndOffset()); + + String word = editor.getSelectionModel().getSelectedText(); + + if (word == null || StringUtil.isEmptyOrSpaces(word)) { + return; + } + List items = new ArrayList<>(); + for (String variant : myUnboundParams) { + items.add(LookupElementBuilder.create(variant)); + } + LookupManager.getInstance(project).showLookup(editor, items.toArray(new LookupElement[items.size()])); + }); + } } - @Override - @RequiredWriteAction - public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { - final PsiDocTag myTag = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiDocTag.class); - if (myTag == null) { - return; - } - if (!FileModificationService.getInstance().preparePsiElementForWrite(myTag)) { - return; - } - myTag.delete(); + private static class RemoveTagFix implements LocalQuickFix { + private final String myTagName; + private final CharSequence myParamName; + + public RemoveTagFix(String tagName, CharSequence paramName) { + myTagName = tagName; + myParamName = paramName; + } + + @Nonnull + @Override + public LocalizeValue getName() { + return LocalizeValue.localizeTODO("Remove @" + myTagName + " " + myParamName); + } + + @Override + @RequiredWriteAction + public void applyFix(@Nonnull Project project, @Nonnull ProblemDescriptor descriptor) { + PsiDocTag myTag = PsiTreeUtil.getParentOfType(descriptor.getPsiElement(), PsiDocTag.class); + if (myTag == null) { + return; + } + if (!FileModificationService.getInstance().preparePsiElementForWrite(myTag)) { + return; + } + myTag.delete(); + } } - } } diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/sameReturnValue/SameReturnValueInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/sameReturnValue/SameReturnValueInspection.java index 1a9034f51e..4a11eed774 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/sameReturnValue/SameReturnValueInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/sameReturnValue/SameReturnValueInspection.java @@ -19,6 +19,7 @@ import com.intellij.java.analysis.codeInspection.GlobalJavaInspectionTool; import com.intellij.java.analysis.codeInspection.reference.RefJavaVisitor; import com.intellij.java.analysis.codeInspection.reference.RefMethod; +import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.localize.InspectionLocalize; @@ -36,8 +37,9 @@ */ @ExtensionImpl public class SameReturnValueInspection extends GlobalJavaInspectionTool { - @Override @Nullable + @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( @Nonnull RefEntity refEntity, @Nonnull AnalysisScope scope, @@ -53,7 +55,7 @@ public CommonProblemDescriptor[] checkElement( String returnValue = refMethod.getReturnValueIfSame(); if (returnValue != null) { - final LocalizeValue message; + LocalizeValue message; if (refMethod.getDerivedMethods().isEmpty()) { message = InspectionLocalize.inspectionSameReturnValueProblemDescriptor("" + returnValue + ""); } @@ -65,13 +67,9 @@ else if (refMethod.hasBody()) { } return new ProblemDescriptor[]{ - manager.createProblemDescriptor( - refMethod.getElement().getNavigationElement(), - message.get(), - false, - null, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING - ) + manager.newProblemDescriptor(message) + .range(refMethod.getElement().getNavigationElement()) + .create() }; } } @@ -82,7 +80,7 @@ else if (refMethod.hasBody()) { @Override protected boolean queryExternalUsagesRequests( - final RefManager manager, + RefManager manager, final GlobalJavaInspectionContext globalContext, final ProblemDescriptionsProcessor processor, Object state @@ -93,11 +91,14 @@ public void visitElement(@Nonnull RefEntity refEntity) { if (refEntity instanceof RefElement && processor.getDescriptions(refEntity) != null) { refEntity.accept(new RefJavaVisitor() { @Override - public void visitMethod(@Nonnull final RefMethod refMethod) { - globalContext.enqueueDerivedMethodsProcessor(refMethod, derivedMethod -> { - processor.ignoreElement(refMethod); - return false; - }); + public void visitMethod(@Nonnull RefMethod refMethod) { + globalContext.enqueueDerivedMethodsProcessor( + refMethod, + derivedMethod -> { + processor.ignoreElement(refMethod); + return false; + } + ); } }); } diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/varScopeCanBeNarrowed/ParameterCanBeLocalInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/varScopeCanBeNarrowed/ParameterCanBeLocalInspection.java index 314d464c2d..0cb5fc5dd1 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/varScopeCanBeNarrowed/ParameterCanBeLocalInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/varScopeCanBeNarrowed/ParameterCanBeLocalInspection.java @@ -32,6 +32,7 @@ import consulo.language.psi.PsiReference; import consulo.localize.LocalizeValue; import consulo.project.Project; +import consulo.ui.annotation.RequiredUIAccess; import consulo.usage.UsageInfo; import jakarta.annotation.Nonnull; import jakarta.annotation.Nullable; @@ -72,15 +73,15 @@ public ProblemDescriptor[] checkMethod( boolean isOnTheFly, Object state ) { - final Collection parameters = filterFinal(method.getParameterList().getParameters()); - final PsiCodeBlock body = method.getBody(); + Collection parameters = filterFinal(method.getParameterList().getParameters()); + PsiCodeBlock body = method.getBody(); if (body == null || parameters.isEmpty() || isOverrides(method)) { return ProblemDescriptor.EMPTY_ARRAY; } - final List result = new ArrayList<>(); + List result = new ArrayList<>(); for (PsiParameter parameter : getWriteBeforeRead(parameters, body)) { - final PsiIdentifier identifier = parameter.getNameIdentifier(); + PsiIdentifier identifier = parameter.getNameIdentifier(); if (identifier != null && identifier.isPhysical()) { result.add(createProblem(manager, identifier, isOnTheFly)); } @@ -90,7 +91,7 @@ public ProblemDescriptor[] checkMethod( @Nonnull private static List filterFinal(PsiParameter[] parameters) { - final List result = new ArrayList<>(parameters.length); + List result = new ArrayList<>(parameters.length); for (PsiParameter parameter : parameters) { if (!parameter.hasModifierProperty(PsiModifier.FINAL)) { result.add(parameter); @@ -99,36 +100,33 @@ private static List filterFinal(PsiParameter[] parameters) { return result; } - @Nonnull + @RequiredReadAction private static ProblemDescriptor createProblem( @Nonnull InspectionManager manager, @Nonnull PsiIdentifier identifier, boolean isOnTheFly ) { - return manager.createProblemDescriptor( - identifier, - InspectionLocalize.inspectionParameterCanBeLocalProblemDescriptor().get(), - true, - ProblemHighlightType.LIKE_UNUSED_SYMBOL, - isOnTheFly, - new ConvertParameterToLocalQuickFix() - ); + return manager.newProblemDescriptor(InspectionLocalize.inspectionParameterCanBeLocalProblemDescriptor()) + .range(identifier) + .highlightType(ProblemHighlightType.LIKE_UNUSED_SYMBOL) + .onTheFly(isOnTheFly) + .withFix(new ConvertParameterToLocalQuickFix()) + .create(); } @RequiredReadAction private static Collection getWriteBeforeRead(@Nonnull Collection parameters, @Nonnull PsiCodeBlock body) { - final ControlFlow controlFlow = getControlFlow(body); + ControlFlow controlFlow = getControlFlow(body); if (controlFlow == null) { return Collections.emptyList(); } - final Set result = filterParameters(controlFlow, parameters); + Set result = filterParameters(controlFlow, parameters); result.retainAll(ControlFlowUtil.getWrittenVariables(controlFlow, 0, controlFlow.getSize(), false)); - for (final PsiReferenceExpression readBeforeWrite : ControlFlowUtil.getReadBeforeWrite(controlFlow)) { - final PsiElement resolved = readBeforeWrite.resolve(); - if (resolved instanceof PsiParameter) { - result.remove(resolved); + for (PsiReferenceExpression readBeforeWrite : ControlFlowUtil.getReadBeforeWrite(controlFlow)) { + if (readBeforeWrite.resolve() instanceof PsiParameter param) { + result.remove(param); } } @@ -136,9 +134,9 @@ private static Collection getWriteBeforeRead(@Nonnull Collection

filterParameters(@Nonnull ControlFlow controlFlow, @Nonnull Collection parameters) { - final Set usedVars = new HashSet<>(ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize())); + Set usedVars = new HashSet<>(ControlFlowUtil.getUsedVariables(controlFlow, 0, controlFlow.getSize())); - final Set result = new HashSet<>(); + Set result = new HashSet<>(); for (PsiParameter parameter : parameters) { if (usedVars.contains(parameter)) { result.add(parameter); @@ -152,7 +150,7 @@ private static boolean isOverrides(PsiMethod method) { } @Nullable - private static ControlFlow getControlFlow(final PsiElement context) { + private static ControlFlow getControlFlow( PsiElement context) { try { return ControlFlowFactory.getInstance(context.getProject()) .getControlFlow(context, LocalsOrMyInstanceFieldsControlFlowPolicy.getInstance()); @@ -170,6 +168,7 @@ protected PsiParameter getVariable(@Nonnull ProblemDescriptor descriptor) { } @Override + @RequiredUIAccess protected PsiElement applyChanges( @Nonnull final Project project, @Nonnull final String localName, @@ -178,12 +177,10 @@ protected PsiElement applyChanges( @Nonnull final Collection references, @Nonnull final Function action ) { - final PsiElement scope = parameter.getDeclarationScope(); - if (scope instanceof PsiMethod) { - final PsiMethod method = (PsiMethod) scope; - final PsiParameter[] parameters = method.getParameterList().getParameters(); + if (parameter.getDeclarationScope() instanceof PsiMethod method) { + PsiParameter[] parameters = method.getParameterList().getParameters(); - final List info = new ArrayList<>(); + List info = new ArrayList<>(); for (int i = 0; i < parameters.length; i++) { PsiParameter psiParameter = parameters[i]; if (psiParameter == parameter) { @@ -193,14 +190,20 @@ protected PsiElement applyChanges( } final ParameterInfoImpl[] newParams = info.toArray(new ParameterInfoImpl[info.size()]); final String visibilityModifier = VisibilityUtil.getVisibilityModifier(method.getModifierList()); - final ChangeSignatureProcessor cp = new ChangeSignatureProcessor(project, method, false, visibilityModifier, - method.getName(), method.getReturnType(), newParams + ChangeSignatureProcessor cp = new ChangeSignatureProcessor( + project, + method, + false, + visibilityModifier, + method.getName(), + method.getReturnType(), + newParams ) { @Override - protected void performRefactoring(UsageInfo[] usages) { - final PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); - final PsiElement newDeclaration = - moveDeclaration(elementFactory, localName, parameter, initializer, action, references); + @RequiredReadAction + protected void performRefactoring(@Nonnull UsageInfo[] usages) { + PsiElementFactory elementFactory = JavaPsiFacade.getElementFactory(project); + PsiElement newDeclaration = moveDeclaration(elementFactory, localName, parameter, initializer, action, references); super.performRefactoring(usages); positionCaretToDeclaration(project, newDeclaration.getContainingFile(), newDeclaration); } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java index a64f70dd90..8ad137354b 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/abstraction/StaticMethodOnlyUsedInOneClassInspection.java @@ -35,7 +35,6 @@ import consulo.annotation.access.RequiredReadAction; import consulo.annotation.component.ExtensionImpl; import consulo.application.progress.ProgressManager; -import consulo.application.util.function.Processor; import consulo.application.util.query.Query; import consulo.dataContext.DataContext; import consulo.deadCodeNotWorking.impl.MultipleCheckboxOptionsPanel; @@ -58,6 +57,7 @@ import javax.swing.*; import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Predicate; @ExtensionImpl public class StaticMethodOnlyUsedInOneClassInspection extends BaseGlobalInspection { @@ -175,7 +175,9 @@ static ProblemDescriptor createProblemDescriptor(@Nonnull InspectionManager mana anonymousClass.getBaseClassReference().getText() ) : InspectionGadgetsLocalize.staticMethodOnlyUsedInOneClassProblemDescriptor(usageClass.getName()); - return manager.createProblemDescriptor(problemElement, message.get(), false, null, ProblemHighlightType.GENERIC_ERROR_OR_WARNING); + return manager.newProblemDescriptor(message) + .range(problemElement) + .create(); } @Override @@ -282,12 +284,12 @@ public boolean isAccessible() { } } - private static class UsageProcessor implements Processor { + private static class UsageProcessor implements Predicate { private final AtomicReference foundClass = new AtomicReference<>(); @Override @RequiredReadAction - public boolean process(PsiReference reference) { + public boolean test(PsiReference reference) { ProgressManager.checkCanceled(); PsiElement element = reference.getElement(); PsiClass usageClass = ClassUtils.getContainingClass(element); diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/classlayout/MethodReturnAlwaysConstantInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/classlayout/MethodReturnAlwaysConstantInspection.java index 094bdff0a0..f6f45290c4 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/classlayout/MethodReturnAlwaysConstantInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/classlayout/MethodReturnAlwaysConstantInspection.java @@ -21,10 +21,8 @@ import com.intellij.java.language.psi.*; import com.intellij.java.language.psi.util.PsiUtil; import com.siyeh.localize.InspectionGadgetsLocalize; -import consulo.language.editor.inspection.CommonProblemDescriptor; -import consulo.language.editor.inspection.GlobalInspectionContext; -import consulo.language.editor.inspection.ProblemDescriptor; -import consulo.language.editor.inspection.ProblemHighlightType; +import consulo.annotation.access.RequiredReadAction; +import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.reference.RefEntity; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.scope.AnalysisScope; @@ -37,80 +35,72 @@ import java.util.Set; public abstract class MethodReturnAlwaysConstantInspection extends BaseGlobalInspection { - private static final Key ALWAYS_CONSTANT = Key.create("ALWAYS_CONSTANT"); + private static final Key ALWAYS_CONSTANT = Key.create("ALWAYS_CONSTANT"); - @Nonnull - @Override - public LocalizeValue getDisplayName() { - return InspectionGadgetsLocalize.methodReturnAlwaysConstantDisplayName(); - } - - public CommonProblemDescriptor[] checkElement( - RefEntity refEntity, - AnalysisScope scope, - InspectionManager manager, - GlobalInspectionContext globalContext - ) { - if (!(refEntity instanceof RefMethod)) { - return null; - } - final RefMethod refMethod = (RefMethod) refEntity; - final Boolean alreadyProcessed = refMethod.getUserData(ALWAYS_CONSTANT); - if (alreadyProcessed != null && alreadyProcessed.booleanValue()) { - return null; - } - if (!(refMethod.getElement() instanceof PsiMethod)) { - return null; - } - final PsiMethod method = (PsiMethod) refMethod.getElement(); - if (method.getBody() == null) { - return null; //we'll catch it on another method - } - if (!alwaysReturnsConstant(method)) { - return null; + @Nonnull + @Override + public LocalizeValue getDisplayName() { + return InspectionGadgetsLocalize.methodReturnAlwaysConstantDisplayName(); } - final Set siblingMethods = MethodInheritanceUtils.calculateSiblingMethods(refMethod); - for (RefMethod siblingMethod : siblingMethods) { - final PsiMethod siblingPsiMethod = (PsiMethod) siblingMethod.getElement(); - if (method.getBody() != null && - !alwaysReturnsConstant(siblingPsiMethod)) { - return null; - } - } - final List out = new ArrayList(); - for (RefMethod siblingRefMethod : siblingMethods) { - final PsiMethod siblingMethod = (PsiMethod) siblingRefMethod.getElement(); - final PsiIdentifier identifier = siblingMethod.getNameIdentifier(); - if (identifier == null) { - continue; - } - out.add(manager.createProblemDescriptor( - identifier, - InspectionGadgetsLocalize.methodReturnAlwaysConstantProblemDescriptor().get(), - false, - null, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING - )); - siblingRefMethod.putUserData(ALWAYS_CONSTANT, Boolean.TRUE); - } - return out.toArray(new ProblemDescriptor[out.size()]); - } - private static boolean alwaysReturnsConstant(PsiMethod method) { - final PsiCodeBlock body = method.getBody(); - if (body == null) { - return false; + @RequiredReadAction + public CommonProblemDescriptor[] checkElement( + RefEntity refEntity, + AnalysisScope scope, + InspectionManager manager, + GlobalInspectionContext globalContext + ) { + if (!(refEntity instanceof RefMethod refMethod)) { + return null; + } + Boolean alreadyProcessed = refMethod.getUserData(ALWAYS_CONSTANT); + if (alreadyProcessed != null && alreadyProcessed) { + return null; + } + if (!(refMethod.getElement() instanceof PsiMethod method)) { + return null; + } + if (method.getBody() == null) { + return null; //we'll catch it on another method + } + if (!alwaysReturnsConstant(method)) { + return null; + } + Set siblingMethods = MethodInheritanceUtils.calculateSiblingMethods(refMethod); + for (RefMethod siblingMethod : siblingMethods) { + PsiMethod siblingPsiMethod = (PsiMethod) siblingMethod.getElement(); + if (method.getBody() != null && + !alwaysReturnsConstant(siblingPsiMethod)) { + return null; + } + } + List out = new ArrayList<>(); + for (RefMethod siblingRefMethod : siblingMethods) { + PsiMethod siblingMethod = (PsiMethod) siblingRefMethod.getElement(); + PsiIdentifier identifier = siblingMethod.getNameIdentifier(); + if (identifier == null) { + continue; + } + out.add( + manager.newProblemDescriptor(InspectionGadgetsLocalize.methodReturnAlwaysConstantProblemDescriptor()) + .range(identifier) + .create() + ); + siblingRefMethod.putUserData(ALWAYS_CONSTANT, Boolean.TRUE); + } + return out.toArray(new ProblemDescriptor[out.size()]); } - final PsiStatement[] statements = body.getStatements(); - if (statements.length != 1) { - return false; - } - final PsiStatement statement = statements[0]; - if (!(statement instanceof PsiReturnStatement)) { - return false; + + private static boolean alwaysReturnsConstant(PsiMethod method) { + PsiCodeBlock body = method.getBody(); + if (body == null) { + return false; + } + PsiStatement[] statements = body.getStatements(); + if (!(statements.length == 1 && statements[0] instanceof PsiReturnStatement returnStatement)) { + return false; + } + PsiExpression value = returnStatement.getReturnValue(); + return value != null && PsiUtil.isConstantExpression(value); } - final PsiReturnStatement returnStatement = (PsiReturnStatement) statement; - final PsiExpression value = returnStatement.getReturnValue(); - return value != null && PsiUtil.isConstantExpression(value); - } } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassIndependentOfModuleInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassIndependentOfModuleInspection.java index e14c8d6586..1e98351e92 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassIndependentOfModuleInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassIndependentOfModuleInspection.java @@ -22,9 +22,8 @@ import com.intellij.java.language.psi.PsiClass; import com.intellij.java.language.psi.PsiIdentifier; import com.siyeh.localize.InspectionGadgetsLocalize; -import consulo.language.editor.inspection.CommonProblemDescriptor; -import consulo.language.editor.inspection.GlobalInspectionContext; -import consulo.language.editor.inspection.ProblemHighlightType; +import consulo.annotation.access.RequiredReadAction; +import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.reference.RefEntity; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.scope.AnalysisScope; @@ -43,48 +42,42 @@ public LocalizeValue getDisplayName() { @Nullable @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( - RefEntity refEntity, - AnalysisScope scope, - InspectionManager manager, - GlobalInspectionContext globalContext, - Object state) { - if (!(refEntity instanceof RefClass)) { + @Nonnull RefEntity refEntity, + @Nonnull AnalysisScope scope, + @Nonnull InspectionManager manager, + @Nonnull GlobalInspectionContext globalContext, + @Nonnull Object state + ) { + if (!(refEntity instanceof RefClass refClass)) { return null; } - final RefClass refClass = (RefClass) refEntity; - final RefEntity owner = refClass.getOwner(); - if (!(owner instanceof RefPackage)) { + if (!(refClass.getOwner() instanceof RefPackage)) { return null; } - final Set dependencies = - DependencyUtils.calculateDependenciesForClass(refClass); + Set dependencies = DependencyUtils.calculateDependenciesForClass(refClass); for (RefClass dependency : dependencies) { if (inSameModule(refClass, dependency)) { return null; } } - final Set dependents = - DependencyUtils.calculateDependentsForClass(refClass); + Set dependents = DependencyUtils.calculateDependentsForClass(refClass); for (RefClass dependent : dependents) { if (inSameModule(refClass, dependent)) { return null; } } - final PsiClass aClass = refClass.getElement(); - final PsiIdentifier identifier = aClass.getNameIdentifier(); + PsiClass aClass = refClass.getElement(); + PsiIdentifier identifier = aClass.getNameIdentifier(); if (identifier == null) { return null; } return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor( - identifier, - InspectionGadgetsLocalize.classIndependentOfModuleProblemDescriptor().get(), - true, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - false - ) + manager.newProblemDescriptor(InspectionGadgetsLocalize.classIndependentOfModuleProblemDescriptor()) + .range(identifier) + .create() }; } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassOnlyUsedInOneModuleInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassOnlyUsedInOneModuleInspection.java index 9348c27734..f71b6fdd41 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassOnlyUsedInOneModuleInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/modularization/ClassOnlyUsedInOneModuleInspection.java @@ -22,9 +22,9 @@ import com.intellij.java.language.psi.PsiClass; import com.intellij.java.language.psi.PsiIdentifier; import com.siyeh.localize.InspectionGadgetsLocalize; +import consulo.annotation.access.RequiredReadAction; import consulo.language.editor.inspection.CommonProblemDescriptor; import consulo.language.editor.inspection.GlobalInspectionContext; -import consulo.language.editor.inspection.ProblemHighlightType; import consulo.language.editor.inspection.reference.RefEntity; import consulo.language.editor.inspection.reference.RefModule; import consulo.language.editor.inspection.scheme.InspectionManager; @@ -44,25 +44,24 @@ public LocalizeValue getDisplayName() { @Nullable @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( - RefEntity refEntity, - AnalysisScope scope, - InspectionManager manager, - GlobalInspectionContext globalContext, - Object state + @Nonnull RefEntity refEntity, + @Nonnull AnalysisScope scope, + @Nonnull InspectionManager manager, + @Nonnull GlobalInspectionContext globalContext, + @Nonnull Object state ) { - if (!(refEntity instanceof RefClass)) { + if (!(refEntity instanceof RefClass refClass)) { return null; } - final RefClass refClass = (RefClass) refEntity; - final RefEntity owner = refClass.getOwner(); - if (!(owner instanceof RefPackage)) { + if (!(refClass.getOwner() instanceof RefPackage)) { return null; } - final Set dependencies = DependencyUtils.calculateDependenciesForClass(refClass); + Set dependencies = DependencyUtils.calculateDependenciesForClass(refClass); RefModule otherModule = null; for (RefClass dependency : dependencies) { - final RefModule module = dependency.getModule(); + RefModule module = dependency.getModule(); if (refClass.getModule() == module) { return null; } @@ -75,9 +74,9 @@ public CommonProblemDescriptor[] checkElement( } } } - final Set dependents = DependencyUtils.calculateDependentsForClass(refClass); + Set dependents = DependencyUtils.calculateDependentsForClass(refClass); for (RefClass dependent : dependents) { - final RefModule module = dependent.getModule(); + RefModule module = dependent.getModule(); if (refClass.getModule() == module) { return null; } @@ -93,20 +92,15 @@ public CommonProblemDescriptor[] checkElement( if (otherModule == null) { return null; } - final PsiClass aClass = refClass.getElement(); - final PsiIdentifier identifier = aClass.getNameIdentifier(); + PsiClass aClass = refClass.getElement(); + PsiIdentifier identifier = aClass.getNameIdentifier(); if (identifier == null) { return null; } - final String moduleName = otherModule.getName(); return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor( - identifier, - InspectionGadgetsLocalize.classOnlyUsedInOneModuleProblemDescriptor(moduleName).get(), - true, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - false - ) + manager.newProblemDescriptor(InspectionGadgetsLocalize.classOnlyUsedInOneModuleProblemDescriptor(otherModule.getName())) + .range(identifier) + .create() }; } } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassOnlyUsedInOnePackageInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassOnlyUsedInOnePackageInspection.java index 5a48518557..b863906780 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassOnlyUsedInOnePackageInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassOnlyUsedInOnePackageInspection.java @@ -23,9 +23,8 @@ import com.intellij.java.language.psi.PsiClass; import com.intellij.java.language.psi.PsiIdentifier; import com.siyeh.localize.InspectionGadgetsLocalize; -import consulo.language.editor.inspection.CommonProblemDescriptor; -import consulo.language.editor.inspection.GlobalInspectionContext; -import consulo.language.editor.inspection.ProblemHighlightType; +import consulo.annotation.access.RequiredReadAction; +import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.reference.RefEntity; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.scope.AnalysisScope; @@ -44,26 +43,25 @@ public LocalizeValue getDisplayName() { @Nullable @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( - RefEntity refEntity, - AnalysisScope scope, - InspectionManager manager, - GlobalInspectionContext globalContext, - Object state + @Nonnull RefEntity refEntity, + @Nonnull AnalysisScope scope, + @Nonnull InspectionManager manager, + @Nonnull GlobalInspectionContext globalContext, + @Nonnull Object state ) { - if (!(refEntity instanceof RefClass)) { + if (!(refEntity instanceof RefClass refClass)) { return null; } - final RefClass refClass = (RefClass) refEntity; - final RefEntity owner = refClass.getOwner(); - if (!(owner instanceof RefPackage)) { + if (!(refClass.getOwner() instanceof RefPackage ownerPackage)) { return null; } - final Set dependencies = DependencyUtils.calculateDependenciesForClass(refClass); + Set dependencies = DependencyUtils.calculateDependenciesForClass(refClass); RefPackage otherPackage = null; for (RefClass dependency : dependencies) { - final RefPackage refPackage = RefJavaUtil.getPackage(dependency); - if (owner == refPackage) { + RefPackage refPackage = RefJavaUtil.getPackage(dependency); + if (ownerPackage == refPackage) { return null; } if (otherPackage != refPackage) { @@ -75,10 +73,10 @@ public CommonProblemDescriptor[] checkElement( } } } - final Set dependents = DependencyUtils.calculateDependentsForClass(refClass); + Set dependents = DependencyUtils.calculateDependentsForClass(refClass); for (RefClass dependent : dependents) { - final RefPackage refPackage = RefJavaUtil.getPackage(dependent); - if (owner == refPackage) { + RefPackage refPackage = RefJavaUtil.getPackage(dependent); + if (ownerPackage == refPackage) { return null; } if (otherPackage != refPackage) { @@ -93,20 +91,15 @@ public CommonProblemDescriptor[] checkElement( if (otherPackage == null) { return null; } - final PsiClass aClass = refClass.getElement(); - final PsiIdentifier identifier = aClass.getNameIdentifier(); + PsiClass aClass = refClass.getElement(); + PsiIdentifier identifier = aClass.getNameIdentifier(); if (identifier == null) { return null; } - final String packageName = otherPackage.getName(); return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor( - identifier, - InspectionGadgetsLocalize.classOnlyUsedInOnePackageProblemDescriptor(packageName).get(), - true, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - false - ) + manager.newProblemDescriptor(InspectionGadgetsLocalize.classOnlyUsedInOnePackageProblemDescriptor(otherPackage.getName())) + .range(identifier) + .create() }; } } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassUnconnectedToPackageInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassUnconnectedToPackageInspection.java index b70fe90f94..cb9f41e804 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassUnconnectedToPackageInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/packaging/ClassUnconnectedToPackageInspection.java @@ -22,9 +22,8 @@ import com.intellij.java.language.psi.PsiClass; import com.intellij.java.language.psi.PsiIdentifier; import com.siyeh.localize.InspectionGadgetsLocalize; -import consulo.language.editor.inspection.CommonProblemDescriptor; -import consulo.language.editor.inspection.GlobalInspectionContext; -import consulo.language.editor.inspection.ProblemHighlightType; +import consulo.annotation.access.RequiredReadAction; +import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.reference.RefEntity; import consulo.language.editor.inspection.scheme.InspectionManager; import consulo.language.editor.scope.AnalysisScope; @@ -41,50 +40,44 @@ public LocalizeValue getDisplayName() { return InspectionGadgetsLocalize.classUnconnectedToPackageDisplayName(); } - @Override @Nullable + @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( - RefEntity refEntity, - AnalysisScope analysisScope, - InspectionManager manager, - GlobalInspectionContext globalInspectionContext, - Object state) { - if (!(refEntity instanceof RefClass)) { + @Nonnull RefEntity refEntity, + @Nonnull AnalysisScope analysisScope, + @Nonnull InspectionManager manager, + @Nonnull GlobalInspectionContext globalInspectionContext, + @Nonnull Object state + ) { + if (!(refEntity instanceof RefClass refClass)) { return null; } - final RefClass refClass = (RefClass) refEntity; - final RefEntity owner = refClass.getOwner(); - if (!(owner instanceof RefPackage)) { + if (!(refClass.getOwner() instanceof RefPackage)) { return null; } - final Set dependencies = - DependencyUtils.calculateDependenciesForClass(refClass); + Set dependencies = DependencyUtils.calculateDependenciesForClass(refClass); for (RefClass dependency : dependencies) { if (inSamePackage(refClass, dependency)) { return null; } } - final Set dependents = - DependencyUtils.calculateDependentsForClass(refClass); + Set dependents = DependencyUtils.calculateDependentsForClass(refClass); for (RefClass dependent : dependents) { if (inSamePackage(refClass, dependent)) { return null; } } - final PsiClass aClass = refClass.getElement(); - final PsiIdentifier identifier = aClass.getNameIdentifier(); + PsiClass aClass = refClass.getElement(); + PsiIdentifier identifier = aClass.getNameIdentifier(); if (identifier == null) { return null; } return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor( - identifier, - InspectionGadgetsLocalize.classUnconnectedToPackageProblemDescriptor().get(), - true, - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - false - ) + manager.newProblemDescriptor(InspectionGadgetsLocalize.classUnconnectedToPackageProblemDescriptor()) + .range(identifier) + .create() }; } diff --git a/plugin/src/main/java/com/intellij/java/impl/ig/redundancy/ElementOnlyUsedFromTestCodeInspection.java b/plugin/src/main/java/com/intellij/java/impl/ig/redundancy/ElementOnlyUsedFromTestCodeInspection.java index 182cc0a160..73a76a1bd6 100644 --- a/plugin/src/main/java/com/intellij/java/impl/ig/redundancy/ElementOnlyUsedFromTestCodeInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/ig/redundancy/ElementOnlyUsedFromTestCodeInspection.java @@ -23,10 +23,8 @@ import com.intellij.java.language.psi.PsiIdentifier; import com.intellij.java.language.psi.PsiMethod; import com.siyeh.localize.InspectionGadgetsLocalize; -import consulo.language.editor.inspection.CommonProblemDescriptor; -import consulo.language.editor.inspection.GlobalInspectionContext; -import consulo.language.editor.inspection.ProblemDescriptionsProcessor; -import consulo.language.editor.inspection.ProblemHighlightType; +import consulo.annotation.access.RequiredReadAction; +import consulo.language.editor.inspection.*; import consulo.language.editor.inspection.reference.RefElement; import consulo.language.editor.inspection.reference.RefEntity; import consulo.language.editor.inspection.reference.RefGraphAnnotator; @@ -43,9 +41,7 @@ import jakarta.annotation.Nullable; public abstract class ElementOnlyUsedFromTestCodeInspection extends BaseGlobalInspection { - - private static final Key ONLY_USED_FROM_TEST_CODE = - Key.create("ONLY_USED_FROM_TEST_CODE"); + private static final Key ONLY_USED_FROM_TEST_CODE = Key.create("ONLY_USED_FROM_TEST_CODE"); @Nonnull @Override @@ -55,84 +51,81 @@ public LocalizeValue getDisplayName() { @Override @Nullable - public RefGraphAnnotator getAnnotator(RefManager refManager) { + public RefGraphAnnotator getAnnotator(@Nonnull RefManager refManager) { return new ElementOnlyUsedFromTestCodeAnnotator(); } @Nullable @Override + @RequiredReadAction public CommonProblemDescriptor[] checkElement( - RefEntity refEntity, AnalysisScope scope, InspectionManager manager, - GlobalInspectionContext globalContext, - ProblemDescriptionsProcessor processor, - Object state) { + @Nonnull RefEntity refEntity, + @Nonnull AnalysisScope scope, + @Nonnull InspectionManager manager, + @Nonnull GlobalInspectionContext globalContext, + @Nonnull ProblemDescriptionsProcessor processor, + @Nonnull Object state + ) { if (!isOnlyUsedFromTestCode(refEntity)) { return null; } - if (!(refEntity instanceof RefJavaElement)) { + if (!(refEntity instanceof RefJavaElement javaElement)) { return null; } - final RefJavaElement javaElement = (RefJavaElement) refEntity; if (!javaElement.isReferenced()) { return null; } - final PsiElement element = javaElement.getElement(); - if (element instanceof PsiClass) { - final PsiClass aClass = (PsiClass) element; - final PsiIdentifier identifier = aClass.getNameIdentifier(); + PsiElement element = javaElement.getElement(); + if (element instanceof PsiClass aClass) { + PsiIdentifier identifier = aClass.getNameIdentifier(); if (identifier == null) { return null; } return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor(identifier, - InspectionGadgetsLocalize.classOnlyUsedFromTestCodeProblemDescriptor().get(), - true, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false) + manager.newProblemDescriptor(InspectionGadgetsLocalize.classOnlyUsedFromTestCodeProblemDescriptor()) + .range(identifier) + .create() }; } - else if (element instanceof PsiMethod) { - final PsiMethod method = (PsiMethod) element; - final PsiIdentifier identifier = method.getNameIdentifier(); + else if (element instanceof PsiMethod method) { + PsiIdentifier identifier = method.getNameIdentifier(); if (identifier == null) { return null; } return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor(identifier, - InspectionGadgetsLocalize.methodOnlyUsedFromTestCodeProblemDescriptor().get(), - true, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false) + manager.newProblemDescriptor(InspectionGadgetsLocalize.methodOnlyUsedFromTestCodeProblemDescriptor()) + .range(identifier) + .create() }; } - else if (element instanceof PsiField) { - final PsiField field = (PsiField) element; - final PsiIdentifier identifier = field.getNameIdentifier(); + else if (element instanceof PsiField field) { return new CommonProblemDescriptor[]{ - manager.createProblemDescriptor(identifier, - InspectionGadgetsLocalize.fieldOnlyUsedFromTestCodeProblemDescriptor().get(), - true, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false) + manager.newProblemDescriptor(InspectionGadgetsLocalize.fieldOnlyUsedFromTestCodeProblemDescriptor()) + .range(field.getNameIdentifier()) + .create() }; } return null; } private static boolean isInsideTestClass(@Nonnull PsiElement e) { - final PsiClass aClass = getTopLevelParentClass(e); + PsiClass aClass = getTopLevelParentClass(e); return aClass != null && TestFrameworks.getInstance().isTestClass(aClass); } private static boolean isUnderTestSources(PsiElement e) { - final ProjectRootManager rootManager = - ProjectRootManager.getInstance(e.getProject()); - final VirtualFile file = e.getContainingFile().getVirtualFile(); - return file != null && - rootManager.getFileIndex().isInTestSourceContent(file); + ProjectRootManager rootManager = ProjectRootManager.getInstance(e.getProject()); + VirtualFile file = e.getContainingFile().getVirtualFile(); + return file != null && rootManager.getFileIndex().isInTestSourceContent(file); } @Nullable public static PsiClass getTopLevelParentClass(PsiElement e) { PsiClass result = null; PsiElement parent = e.getParent(); - while (parent != null && !(parent instanceof PsiFile)) { - if (parent instanceof PsiClass) { - result = (PsiClass) parent; + while (!(parent == null || parent instanceof PsiFile)) { + if (parent instanceof PsiClass psiClass) { + result = psiClass; } parent = parent.getParent(); } @@ -140,63 +133,50 @@ public static PsiClass getTopLevelParentClass(PsiElement e) { } private static boolean isOnlyUsedFromTestCode(RefEntity refElement) { - final Boolean usedFromTestCode = - refElement.getUserData(ONLY_USED_FROM_TEST_CODE); - return usedFromTestCode != null && usedFromTestCode.booleanValue(); + Boolean usedFromTestCode = refElement.getUserData(ONLY_USED_FROM_TEST_CODE); + return usedFromTestCode != null && usedFromTestCode; } - private static class ElementOnlyUsedFromTestCodeAnnotator - extends RefGraphAnnotator { - + private static class ElementOnlyUsedFromTestCodeAnnotator extends RefGraphAnnotator { @Override - public void onMarkReferenced(RefElement refWhat, RefElement refFrom, - boolean referencedFromClassInitializer) { - if (!(refWhat instanceof RefMethod) && - !(refWhat instanceof RefField) && - !(refWhat instanceof RefClass)) { + public void onMarkReferenced(RefElement refWhat, RefElement refFrom, boolean referencedFromClassInitializer) { + if (!(refWhat instanceof RefMethod) + && !(refWhat instanceof RefField) + && !(refWhat instanceof RefClass)) { return; } - if (referencedFromClassInitializer || - refFrom instanceof RefImplicitConstructor) { + if (referencedFromClassInitializer || refFrom instanceof RefImplicitConstructor) { return; } - final PsiElement whatElement = refWhat.getElement(); - if (isInsideTestClass(whatElement) || - isUnderTestSources(whatElement)) { + PsiElement whatElement = refWhat.getElement(); + if (isInsideTestClass(whatElement) || isUnderTestSources(whatElement)) { // test code itself is allowed to only be used from test code return; } - if (refFrom instanceof RefMethod && refWhat instanceof RefClass) { - final RefMethod method = (RefMethod) refFrom; - if (method.isConstructor() && - method.getOwnerClass() == refWhat) { - // don't count references to class from its own constructor - return; - } + if (refFrom instanceof RefMethod method + && refWhat instanceof RefClass + && method.isConstructor() + && method.getOwnerClass() == refWhat) { + // don't count references to class from its own constructor + return; } - final Boolean onlyUsedFromTestCode = - refWhat.getUserData(ONLY_USED_FROM_TEST_CODE); + Boolean onlyUsedFromTestCode = refWhat.getUserData(ONLY_USED_FROM_TEST_CODE); if (onlyUsedFromTestCode == null) { refWhat.putUserData(ONLY_USED_FROM_TEST_CODE, Boolean.TRUE); } - else if (!onlyUsedFromTestCode.booleanValue()) { + else if (!onlyUsedFromTestCode) { return; } - final PsiElement fromElement = refFrom.getElement(); - if (isInsideTestClass(fromElement) || - isUnderTestSources(fromElement)) { + PsiElement fromElement = refFrom.getElement(); + if (isInsideTestClass(fromElement) || isUnderTestSources(fromElement)) { return; } - if (refWhat instanceof RefMethod) { - final RefMethod what = (RefMethod) refWhat; - if (what.isConstructor()) { - final RefClass ownerClass = what.getOwnerClass(); - ownerClass.putUserData(ONLY_USED_FROM_TEST_CODE, - Boolean.FALSE); - // do count references to class from its own constructor - // when that constructor is used outside of test code. - } + if (refWhat instanceof RefMethod what && what.isConstructor()) { + RefClass ownerClass = what.getOwnerClass(); + ownerClass.putUserData(ONLY_USED_FROM_TEST_CODE, Boolean.FALSE); + // do count references to class from its own constructor + // when that constructor is used outside of test code. } refWhat.putUserData(ONLY_USED_FROM_TEST_CODE, Boolean.FALSE); } From 5e06da1e2d987c61e9669a537338e8dfd745a29a Mon Sep 17 00:00:00 2001 From: UNV Date: Wed, 10 Dec 2025 07:09:20 +0300 Subject: [PATCH 2/2] Fixes for DependencyInspection and EmptyMethodInspection. --- .../dependencyViolation/DependencyInspection.java | 3 ++- .../emptyMethod/EmptyMethodInspection.java | 12 ++++++------ 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java index 185580e38c..6b83e71a3a 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/dependencyViolation/DependencyInspection.java @@ -42,7 +42,8 @@ import javax.swing.*; import java.awt.*; -import java.util.ArrayList;import java.util.List; +import java.util.ArrayList; +import java.util.List; /** * @author anna diff --git a/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java b/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java index e87acc4432..2d9e0364c2 100644 --- a/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java +++ b/plugin/src/main/java/com/intellij/java/impl/codeInspection/emptyMethod/EmptyMethodInspection.java @@ -101,6 +101,7 @@ public CommonProblemDescriptor[] checkElement( } } + @Nonnull LocalizeValue message = LocalizeValue.empty(); boolean needToDeleteHierarchy = false; if (refMethod.isOnlyCallsSuper() && !refMethod.isFinal()) { @@ -149,7 +150,7 @@ else if (!refMethod.getDerivedMethods().isEmpty()) { } } - if (message != null) { + if (message != LocalizeValue.empty()) { List fixes = new ArrayList<>(); fixes.add(getFix(processor, needToDeleteHierarchy)); SpecialAnnotationsUtilBase.createAddToSpecialAnnotationFixes( @@ -260,21 +261,20 @@ public void visitMethod(@Nonnull RefMethod refMethod) { return false; } - - @Override @Nonnull + @Override public LocalizeValue getDisplayName() { return InspectionLocalize.inspectionEmptyMethodDisplayName(); } - @Override @Nonnull + @Override public LocalizeValue getGroupDisplayName() { return InspectionLocalize.groupNamesDeclarationRedundancy(); } - @Override @Nonnull + @Override public String getShortName() { return SHORT_NAME; } @@ -325,8 +325,8 @@ public DeleteMethodIntention(String hint) { myHint = hint; } - @Override @Nonnull + @Override public LocalizeValue getName() { return InspectionLocalize.inspectionEmptyMethodDeleteQuickfix(); }