From 2268c19ea706cdee5509ed02089dac0e8ed703e5 Mon Sep 17 00:00:00 2001 From: Tagir Valeev Date: Wed, 1 Jan 2020 16:15:47 +0700 Subject: [PATCH] OldJetBrainsAnnotationInspection: Converts Java 5 nullability annotations on arrays to Java 8 style GitOrigin-RevId: 8787ffe250d977f469e8342d1e4c5b59e76c004f --- .../devkit-core/resources/META-INF/plugin.xml | 5 + .../OldJetBrainsAnnotation.html | 8 ++ .../OldJetBrainsAnnotationInspection.java | 97 +++++++++++++++++++ 3 files changed, 110 insertions(+) create mode 100644 plugins/devkit/devkit-core/resources/inspectionDescriptions/OldJetBrainsAnnotation.html create mode 100644 plugins/devkit/devkit-core/src/inspections/OldJetBrainsAnnotationInspection.java diff --git a/plugins/devkit/devkit-core/resources/META-INF/plugin.xml b/plugins/devkit/devkit-core/resources/META-INF/plugin.xml index 12710de97987..1325dce49cff 100644 --- a/plugins/devkit/devkit-core/resources/META-INF/plugin.xml +++ b/plugins/devkit/devkit-core/resources/META-INF/plugin.xml @@ -123,6 +123,11 @@ enabledByDefault="true" level="WARNING" implementationClass="org.jetbrains.idea.devkit.inspections.PostfixTemplateDescriptionNotFoundInspection"/> + + +Converts Java 5 nullability annotations on arrays to Java 8 style +(e.g. @NotNull String[] arr → String @NotNull [] arr). Note that +after applying the fix the code will be non-compilable. To make it compilable you should replace +org.jetbrains:annotations-java5 to org.jetbrains:annotations in module dependencies. + + \ No newline at end of file diff --git a/plugins/devkit/devkit-core/src/inspections/OldJetBrainsAnnotationInspection.java b/plugins/devkit/devkit-core/src/inspections/OldJetBrainsAnnotationInspection.java new file mode 100644 index 000000000000..4490dca44a08 --- /dev/null +++ b/plugins/devkit/devkit-core/src/inspections/OldJetBrainsAnnotationInspection.java @@ -0,0 +1,97 @@ +// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. +package org.jetbrains.idea.devkit.inspections; + +import com.intellij.codeInsight.AnnotationTargetUtil; +import com.intellij.codeInspection.AbstractBaseJavaLocalInspectionTool; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; +import com.intellij.codeInspection.ProblemsHolder; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.util.ObjectUtils; +import org.jetbrains.annotations.Contract; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.Set; + +public class OldJetBrainsAnnotationInspection extends AbstractBaseJavaLocalInspectionTool { + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + @Override + public void visitAnnotation(PsiAnnotation annotation) { + PsiJavaCodeReferenceElement nameElement = annotation.getNameReferenceElement(); + if (nameElement == null) return; + String name = nameElement.getText(); + if (!name.equals("Nullable") && !name.equals("NotNull")) return; + PsiClass annotationClass = ObjectUtils.tryCast(nameElement.resolve(), PsiClass.class); + if (annotationClass == null) return; + String qualifiedName = annotationClass.getQualifiedName(); + if (qualifiedName == null || !"org.jetbrains.annotations".equals(StringUtil.getPackageName(qualifiedName))) return; + Set targets = AnnotationTargetUtil.getAnnotationTargets(annotationClass); + if (targets == null || targets.contains(PsiAnnotation.TargetType.TYPE_USE)) return; + PsiAnnotationOwner owner = annotation.getOwner(); + PsiTypeElement typeElement = findTypeElement(owner); + if (typeElement == null) return; + PsiType type = typeElement.getType(); + if (!(type instanceof PsiArrayType)) return; + PsiAnnotation.TargetType target = + AnnotationTargetUtil.findAnnotationTarget(annotation, AnnotationTargetUtil.getTargetsForLocation(owner)); + if (target == PsiAnnotation.TargetType.TYPE_USE) return; + holder.registerProblem(annotation, "Old-style array annotation", new OldJetBrainsAnnotationFix()); + } + }; + } + + @Contract("null -> null") + @Nullable + private static PsiTypeElement findTypeElement(PsiAnnotationOwner owner) { + if (!(owner instanceof PsiModifierList)) return null; + PsiElement parent = ((PsiModifierList)owner).getParent(); + PsiTypeElement typeElement; + if (parent instanceof PsiVariable) { + typeElement = ((PsiVariable)parent).getTypeElement(); + } + else if (parent instanceof PsiMethod) { + typeElement = ((PsiMethod)parent).getReturnTypeElement(); + } + else { + return null; + } + if (typeElement == null) return null; + return typeElement; + } + + private static class OldJetBrainsAnnotationFix implements LocalQuickFix { + @Nls(capitalization = Nls.Capitalization.Sentence) + @NotNull + @Override + public String getFamilyName() { + return "Convert to new-style annotation"; + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiAnnotation annotation = ObjectUtils.tryCast(descriptor.getStartElement(), PsiAnnotation.class); + if (annotation == null) return; + PsiAnnotationOwner owner = annotation.getOwner(); + PsiTypeElement typeElement = findTypeElement(owner); + if (typeElement == null) return; + PsiAnnotation copy = (PsiAnnotation)annotation.copy(); + PsiType newType = typeElement.getType().annotate(new TypeAnnotationProvider() { + @NotNull + @Override + public PsiAnnotation[] getAnnotations() { + return new PsiAnnotation[]{copy}; + } + }); + PsiTypeElement newTypeElement = JavaPsiFacade.getElementFactory(project).createTypeElementFromText(newType.getCanonicalText(true), typeElement); + typeElement.replace(newTypeElement); + annotation.delete(); + } + } +}