mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-06 03:21:12 +07:00
[reactive-streams] IDEA-273877 Treat @Transactional like @Blocking if return type is not reactive streams Publisher
GitOrigin-RevId: 5e54b17510a88027b3fbffc330fc363b5845dd0b
This commit is contained in:
committed by
intellij-monorepo-bot
parent
95a94a545d
commit
4f044b833f
@@ -23,6 +23,10 @@ public interface BlockingMethodChecker {
|
||||
|
||||
boolean isMethodBlocking(@NotNull PsiMethod method);
|
||||
|
||||
default boolean isMethodNonBlocking(@NotNull PsiMethod method) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param element PsiElement (e.g. method call or reference) which is located in "non-blocking" code fragment
|
||||
* @return empty array if cannot provide any fixes, non-empty array of quick fixes otherwise
|
||||
|
||||
@@ -14,9 +14,11 @@ import static com.intellij.codeInspection.blockingCallsDetection.BlockingMethodI
|
||||
|
||||
public final class AnnotationBasedBlockingMethodChecker implements BlockingMethodChecker {
|
||||
private final List<String> myBlockingAnnotations;
|
||||
private final List<String> myNonBlockingAnnotations;
|
||||
|
||||
public AnnotationBasedBlockingMethodChecker(List<String> blockingAnnotations) {
|
||||
public AnnotationBasedBlockingMethodChecker(List<String> blockingAnnotations, List<String> nonBlockingAnnotations) {
|
||||
myBlockingAnnotations = blockingAnnotations;
|
||||
myNonBlockingAnnotations = nonBlockingAnnotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -31,4 +33,9 @@ public final class AnnotationBasedBlockingMethodChecker implements BlockingMetho
|
||||
public boolean isMethodBlocking(@NotNull PsiMethod method) {
|
||||
return AnnotationUtil.findAnnotation(method, myBlockingAnnotations, false) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isMethodNonBlocking(@NotNull PsiMethod method) {
|
||||
return AnnotationUtil.findAnnotation(method, myNonBlockingAnnotations, false) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,11 +18,9 @@ import com.intellij.psi.PsiElementVisitor;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.ui.components.panels.VerticalLayout;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.uast.UCallExpression;
|
||||
|
||||
import javax.swing.*;
|
||||
@@ -30,7 +28,7 @@ import java.awt.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class BlockingMethodInNonBlockingContextInspection extends AbstractBaseUastLocalInspectionTool {
|
||||
public final class BlockingMethodInNonBlockingContextInspection extends AbstractBaseUastLocalInspectionTool {
|
||||
|
||||
public static final List<String> DEFAULT_BLOCKING_ANNOTATIONS = List.of(
|
||||
"org.jetbrains.annotations.Blocking",
|
||||
@@ -46,7 +44,6 @@ public class BlockingMethodInNonBlockingContextInspection extends AbstractBaseUa
|
||||
public List<String> myBlockingAnnotations = new ArrayList<>(DEFAULT_BLOCKING_ANNOTATIONS);
|
||||
public List<String> myNonBlockingAnnotations = new ArrayList<>(DEFAULT_NONBLOCKING_ANNOTATIONS);
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public JComponent createOptionsPanel() {
|
||||
return new OptionsPanel();
|
||||
@@ -55,25 +52,29 @@ public class BlockingMethodInNonBlockingContextInspection extends AbstractBaseUa
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) {
|
||||
List<BlockingMethodChecker> blockingMethodCheckers =
|
||||
ContainerUtil.append(BlockingMethodChecker.EP_NAME.getExtensionList(),
|
||||
new AnnotationBasedBlockingMethodChecker(myBlockingAnnotations));
|
||||
List<NonBlockingContextChecker> nonBlockingContextCheckers = getNonBlockingContextCheckers(holder.getFile());
|
||||
if (nonBlockingContextCheckers.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
|
||||
|
||||
List<NonBlockingContextChecker> nonBlockingContextCheckers =
|
||||
ContainerUtil.append(NonBlockingContextChecker.EP_NAME.getExtensionList(),
|
||||
new AnnotationBasedNonBlockingContextChecker(myNonBlockingAnnotations));
|
||||
List<BlockingMethodChecker> blockingMethodCheckers = getBlockingMethodCheckers(holder.getFile());
|
||||
if (blockingMethodCheckers.isEmpty()) return PsiElementVisitor.EMPTY_VISITOR;
|
||||
|
||||
if (!isInspectionActive(holder.getFile(), blockingMethodCheckers, nonBlockingContextCheckers)) {
|
||||
return PsiElementVisitor.EMPTY_VISITOR;
|
||||
}
|
||||
return new BlockingMethodInNonBlockingContextVisitor(holder, blockingMethodCheckers, nonBlockingContextCheckers);
|
||||
}
|
||||
|
||||
private static boolean isInspectionActive(PsiFile file,
|
||||
List<BlockingMethodChecker> myBlockingMethodCheckers,
|
||||
List<NonBlockingContextChecker> myNonBlockingContextCheckers) {
|
||||
return myBlockingMethodCheckers.stream().anyMatch(extension -> extension.isApplicable(file)) &&
|
||||
myNonBlockingContextCheckers.stream().anyMatch(extension -> extension.isApplicable(file));
|
||||
@NotNull
|
||||
private List<NonBlockingContextChecker> getNonBlockingContextCheckers(@NotNull PsiFile file) {
|
||||
List<NonBlockingContextChecker> nonBlockingContextCheckers = new ArrayList<>(NonBlockingContextChecker.EP_NAME.getExtensionList());
|
||||
nonBlockingContextCheckers.add(new AnnotationBasedNonBlockingContextChecker(myNonBlockingAnnotations));
|
||||
nonBlockingContextCheckers.removeIf(checker -> !checker.isApplicable(file));
|
||||
return nonBlockingContextCheckers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<BlockingMethodChecker> getBlockingMethodCheckers(@NotNull PsiFile file) {
|
||||
List<BlockingMethodChecker> blockingMethodCheckers = new ArrayList<>(BlockingMethodChecker.EP_NAME.getExtensionList());
|
||||
blockingMethodCheckers.add(new AnnotationBasedBlockingMethodChecker(myBlockingAnnotations, myNonBlockingAnnotations));
|
||||
blockingMethodCheckers.removeIf(checker -> !checker.isApplicable(file));
|
||||
return blockingMethodCheckers;
|
||||
}
|
||||
|
||||
private final class OptionsPanel extends JPanel {
|
||||
@@ -146,6 +147,9 @@ public class BlockingMethodInNonBlockingContextInspection extends AbstractBaseUa
|
||||
|
||||
if (!isMethodOrSupersBlocking(referencedMethod, myBlockingMethodCheckers)) return;
|
||||
|
||||
// if some implementation believes this method is non-blocking then we don't report it
|
||||
if (isMethodNonBlocking(referencedMethod, myBlockingMethodCheckers)) return;
|
||||
|
||||
PsiElement elementToHighLight = AnalysisUastUtil.getMethodIdentifierSourcePsi(callExpression);
|
||||
if (elementToHighLight == null) return;
|
||||
|
||||
@@ -172,6 +176,14 @@ public class BlockingMethodInNonBlockingContextInspection extends AbstractBaseUa
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isMethodNonBlocking(PsiMethod method,
|
||||
List<? extends BlockingMethodChecker> blockingMethodCheckers) {
|
||||
return blockingMethodCheckers.stream().anyMatch(extension -> {
|
||||
ProgressIndicatorProvider.checkCanceled();
|
||||
return extension.isMethodNonBlocking(method);
|
||||
});
|
||||
}
|
||||
|
||||
private static boolean isContextNonBlockingFor(PsiElement element,
|
||||
List<? extends NonBlockingContextChecker> nonBlockingContextCheckers) {
|
||||
return nonBlockingContextCheckers.stream().anyMatch(extension -> {
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package org.jetbrains.kotlin.idea.inspections.blockingCallsDetection
|
||||
|
||||
import com.intellij.codeInspection.blockingCallsDetection.BlockingMethodChecker
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiMethod
|
||||
import org.jetbrains.kotlin.config.LanguageFeature
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.project.languageVersionSettings
|
||||
import org.jetbrains.kotlin.idea.util.projectStructure.module
|
||||
import org.jetbrains.kotlin.psi.KtFile
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.hasSuspendModifier
|
||||
import org.jetbrains.uast.toUElement
|
||||
|
||||
internal class CoroutineBlockingMethodChecker : BlockingMethodChecker {
|
||||
override fun isApplicable(file: PsiFile): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val languageVersionSettings = getLanguageVersionSettings(file)
|
||||
return languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
}
|
||||
|
||||
override fun isMethodBlocking(method: PsiMethod): Boolean = false
|
||||
|
||||
override fun isMethodNonBlocking(method: PsiMethod): Boolean {
|
||||
val uMethod = method.toUElement()
|
||||
val sourcePsi = uMethod?.sourcePsi ?: return false
|
||||
return sourcePsi is KtNamedFunction && sourcePsi.modifierList?.hasSuspendModifier() == true
|
||||
}
|
||||
|
||||
private fun getLanguageVersionSettings(psiElement: PsiElement): LanguageVersionSettings {
|
||||
return psiElement.module?.languageVersionSettings ?: psiElement.project.getLanguageVersionSettings()
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,8 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
class CoroutineNonBlockingContextChecker : NonBlockingContextChecker {
|
||||
|
||||
override fun isApplicable(file: PsiFile): Boolean {
|
||||
if (file !is KtFile) return false
|
||||
|
||||
val languageVersionSettings = getLanguageVersionSettings(file)
|
||||
return languageVersionSettings.supportsFeature(LanguageFeature.ReleaseCoroutines)
|
||||
}
|
||||
|
||||
@@ -137,6 +137,8 @@
|
||||
|
||||
<codeInsight.nonBlockingContextChecker
|
||||
implementation="org.jetbrains.kotlin.idea.inspections.blockingCallsDetection.CoroutineNonBlockingContextChecker"/>
|
||||
<codeInsight.blockingMethodChecker
|
||||
implementation="org.jetbrains.kotlin.idea.inspections.blockingCallsDetection.CoroutineBlockingMethodChecker"/>
|
||||
|
||||
<typeDeclarationProvider implementation="org.jetbrains.kotlin.idea.codeInsight.KotlinTypeDeclarationProvider"/>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user