mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[java-analysis] ClassDataIndexer: provide an extension to register custom file suppressors
Should help BAZEL-852 IntelliJ incorrectly infers "Contract(pure = true)" annotations for methods from ijar-s produced by Bazel GitOrigin-RevId: 2fdfc9166737d0aba84372a5a2d127aeeb747f9d
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f618ebaf5a
commit
fff1c6af0c
@@ -42,6 +42,8 @@
|
||||
beanClass="com.intellij.codeInsight.daemon.impl.analysis.InjectedLanguageJavaReferenceSupplier" dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.lang.jvm.annotations.marker.suppressor"
|
||||
interface="com.intellij.codeInsight.NonCodeAnnotationsMarkerSuppressor" dynamic="true"/>
|
||||
<extensionPoint qualifiedName="com.intellij.lang.jvm.bytecodeAnalysisSuppressor"
|
||||
interface="com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisSuppressor" dynamic="true"/>
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
|
||||
+29
@@ -0,0 +1,29 @@
|
||||
// Copyright 2000-2025 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* An extension point interface to allow suppressing the bytecode analysis for specific class files.
|
||||
* It's typically used when the class file is known to be a stub, which is replaced with an actual
|
||||
* implementation at runtime. Analyzing such stubs may produce incorrect results
|
||||
* (e.g., incorrect purity contract may be inferred for impure methods).
|
||||
*/
|
||||
public interface BytecodeAnalysisSuppressor {
|
||||
ExtensionPointName<BytecodeAnalysisSuppressor> EP_NAME = ExtensionPointName.create("com.intellij.lang.jvm.bytecodeAnalysisSuppressor");
|
||||
|
||||
/**
|
||||
* @return suppressor version. Override and increase the number every time the suppression algorithm changes.
|
||||
*/
|
||||
default int getVersion() {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param file file to check. It points to a class file, usually inside a jar
|
||||
* @return true if the analysis for a given class file should be suppressed.
|
||||
*/
|
||||
boolean shouldSuppress(@NotNull VirtualFile file);
|
||||
}
|
||||
+7
-2
@@ -7,6 +7,7 @@ import com.intellij.openapi.progress.ProcessCanceledException;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Key;
|
||||
import com.intellij.openapi.util.text.StringHash;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.util.CachedValue;
|
||||
@@ -37,6 +38,7 @@ import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.BinaryOperator;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Direction.*;
|
||||
import static com.intellij.codeInspection.bytecodeAnalysis.Effects.VOLATILE_EFFECTS;
|
||||
@@ -60,7 +62,9 @@ public final class ClassDataIndexer implements VirtualFileGist.GistCalculator<Ma
|
||||
|
||||
private static final int VERSION = 19; // change when inference algorithm changes
|
||||
private static final int VERSION_MODIFIER = HardCodedPurity.AGGRESSIVE_HARDCODED_PURITY ? 1 : 0;
|
||||
private static final int FINAL_VERSION = VERSION * 2 + VERSION_MODIFIER;
|
||||
private static final int FINAL_VERSION = VERSION * 2 + VERSION_MODIFIER + StringHash.murmur(
|
||||
BytecodeAnalysisSuppressor.EP_NAME.getExtensionList().stream().map(ep -> String.valueOf(ep.getVersion()))
|
||||
.collect(Collectors.joining("-")));
|
||||
private static final VirtualFileGist<Map<HMember, Equations>> ourGist = GistManager.getInstance().newVirtualFileGist(
|
||||
"BytecodeAnalysisIndex", FINAL_VERSION, new BytecodeAnalysisIndex.EquationsExternalizer(), new ClassDataIndexer());
|
||||
|
||||
@@ -101,7 +105,8 @@ public final class ClassDataIndexer implements VirtualFileGist.GistCalculator<Ma
|
||||
// Methods of GenericModel.class in Play framework throw UnsupportedOperationException
|
||||
// However, it looks like they are replaced with something meaningful during compilation/runtime
|
||||
// See IDEA-285334.
|
||||
path.endsWith("!/play/db/jpa/GenericModel.class");
|
||||
path.endsWith("!/play/db/jpa/GenericModel.class") ||
|
||||
ContainerUtil.exists(BytecodeAnalysisSuppressor.EP_NAME.getExtensionList(), ep -> ep.shouldSuppress(file));
|
||||
}
|
||||
|
||||
private static final Pattern ANDROID_JAR_PATH = Pattern.compile(
|
||||
|
||||
-129
@@ -79,135 +79,6 @@
|
||||
<item name='org.apache.velocity.runtime.parser.ParserConstants tokenImage'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager ParserTokenManager(org.apache.velocity.runtime.parser.CharStream)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager boolean jjCanMove_0(int, int, int, long, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopAtPos(int, int)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_0(int, long, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_1(int, long, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_10(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_11(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_2(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_3(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_4(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_5(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_6(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_7(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_8(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager int jjStopStringLiteralDfa_9(int, long)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjbitVec0'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjbitVec2'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjnewLexState'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjnextStates'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjstrLiteralImages'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjtoMore'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjtoSkip'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjtoSpecial'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager jjtoToken'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager lexStateNames'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager org.apache.velocity.runtime.parser.Token jjFillToken()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val val=""->new""/>
|
||||
</annotation>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager void SkipLexicalActions(org.apache.velocity.runtime.parser.Token) 0'>
|
||||
<annotation name='org.jetbrains.annotations.Nullable'/>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager void SwitchTo(int)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager void setDebugStream(java.io.PrintStream)'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="mutates" val=""this""/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTokenManager.ParserState ParserState()'>
|
||||
<annotation name='org.jetbrains.annotations.Contract'>
|
||||
<val name="pure" val="true"/>
|
||||
</annotation>
|
||||
</item>
|
||||
<item name='org.apache.velocity.runtime.parser.ParserTreeConstants jjtNodeName'>
|
||||
<annotation name='org.jetbrains.annotations.NotNull'/>
|
||||
</item>
|
||||
|
||||
+9
@@ -5,9 +5,11 @@ import com.intellij.codeInsight.*;
|
||||
import com.intellij.codeInsight.daemon.GutterMark;
|
||||
import com.intellij.codeInsight.daemon.LineMarkerSettings;
|
||||
import com.intellij.codeInsight.daemon.impl.LineMarkerSettingsImpl;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.BytecodeAnalysisSuppressor;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.ClassDataIndexer;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.ProjectBytecodeAnalysis;
|
||||
import com.intellij.codeInspection.dataFlow.JavaMethodContractUtil;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.application.ex.PathManagerEx;
|
||||
import com.intellij.openapi.command.WriteCommandAction;
|
||||
import com.intellij.openapi.editor.Document;
|
||||
@@ -28,6 +30,7 @@ import com.intellij.psi.util.PsiFormatUtil;
|
||||
import com.intellij.psi.xml.XmlFile;
|
||||
import com.intellij.testFramework.LightProjectDescriptor;
|
||||
import com.intellij.testFramework.PsiTestUtil;
|
||||
import com.intellij.testFramework.ServiceContainerUtil;
|
||||
import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
|
||||
import com.intellij.testFramework.fixtures.LightJavaCodeInsightFixtureTestCase;
|
||||
import com.intellij.testFramework.fixtures.MavenDependencyUtil;
|
||||
@@ -46,6 +49,8 @@ public class BytecodeAnalysisIntegrationTest extends LightJavaCodeInsightFixture
|
||||
private static final String INFERRED_TEST_METHOD =
|
||||
"org.apache.velocity.util.ExceptionUtils java.lang.Throwable createWithCause(java.lang.Class, java.lang.String, java.lang.Throwable)";
|
||||
private static final String EXTERNAL_TEST_METHOD = "java.lang.String String(java.lang.String)";
|
||||
|
||||
private static final BytecodeAnalysisSuppressor TEST_SUPPRESSOR = file -> file.getPath().endsWith("/ParserTokenManager.class");
|
||||
|
||||
private static final DefaultLightProjectDescriptor PROJECT_DESCRIPTOR = new DefaultLightProjectDescriptor() {
|
||||
@Override
|
||||
@@ -133,6 +138,8 @@ public class BytecodeAnalysisIntegrationTest extends LightJavaCodeInsightFixture
|
||||
|
||||
public void testSdkAndLibAnnotations() {
|
||||
PsiPackage rootPackage = JavaPsiFacade.getInstance(getProject()).findPackage("");
|
||||
ServiceContainerUtil.registerExtension(ApplicationManager.getApplication(), BytecodeAnalysisSuppressor.EP_NAME, TEST_SUPPRESSOR,
|
||||
getTestRootDisposable());
|
||||
assertNotNull(rootPackage);
|
||||
|
||||
List<String> diffs = new ArrayList<>();
|
||||
@@ -212,6 +219,8 @@ public class BytecodeAnalysisIntegrationTest extends LightJavaCodeInsightFixture
|
||||
public void _testExportInferredAnnotations() {
|
||||
PsiPackage rootPackage = JavaPsiFacade.getInstance(getProject()).findPackage("");
|
||||
assertNotNull(rootPackage);
|
||||
ServiceContainerUtil.registerExtension(ApplicationManager.getApplication(), BytecodeAnalysisSuppressor.EP_NAME, TEST_SUPPRESSOR,
|
||||
getTestRootDisposable());
|
||||
|
||||
VirtualFile annotationsRoot = getAnnotationsRoot();
|
||||
JavaRecursiveElementVisitor visitor = new PackageVisitor(GlobalSearchScope.moduleWithLibrariesScope(getModule())) {
|
||||
|
||||
Reference in New Issue
Block a user