mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
Generated
+1
-1
@@ -1,6 +1,6 @@
|
||||
<component name="ArtifactManager">
|
||||
<artifact name="standalone-jps">
|
||||
<output-path>$PROJECT_DIR$/out/artifacts/standalone_jps</output-path>
|
||||
<output-path>$PROJECT_DIR$/build/lib/jps</output-path>
|
||||
<root id="root">
|
||||
<element id="archive" name="util.jar">
|
||||
<element id="module-output" name="util" />
|
||||
|
||||
Generated
+4
-21
@@ -1,29 +1,12 @@
|
||||
<component name="libraryTable">
|
||||
<library name="Ant">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-commons-net.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-jmf.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-apache-resolver.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-jai.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-apache-bsf.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-commons-logging.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-junit.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-jsch.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-apache-bcel.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-netrexx.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-apache-oro.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-antlr.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-jdepend.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-launcher.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-apache-regexp.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-apache-log4j.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-swing.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/lib/ant-javamail.jar!/" />
|
||||
<root url="file://$PROJECT_DIR$/lib/ant/lib"/>
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<JAVADOC/>
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/src/bundled_ant_src.zip!/bundled_ant_src/main" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/ant/src/bundled_ant_src.zip!/bundled_ant_src/main"/>
|
||||
</SOURCES>
|
||||
<jarDirectory url="file://$PROJECT_DIR$/lib/ant/lib" recursive="false"/>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+3
@@ -1,5 +1,8 @@
|
||||
<component name="libraryTable">
|
||||
<library name="JDOM">
|
||||
<ANNOTATIONS>
|
||||
<root url="file://$PROJECT_DIR$/java/jdkAnnotations"/>
|
||||
</ANNOTATIONS>
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/jdom.jar!/" />
|
||||
</CLASSES>
|
||||
|
||||
Generated
+3
-1
@@ -4,6 +4,8 @@
|
||||
<root url="jar://$PROJECT_DIR$/lib/junit.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/junit3_8_1.jar!/"/>
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+1
-1
@@ -5,7 +5,7 @@
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/log4j.zip!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/log4j.zip!/java" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+2
-2
@@ -4,11 +4,11 @@
|
||||
<root url="file://$PROJECT_DIR$/lib/annotations/netty"/>
|
||||
</ANNOTATIONS>
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/netty-all-4.1.6.Final.Patched.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/netty-all-4.1.9.Final.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/netty-all-4.1.6.Final.Patched-sources.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/netty-all-4.1.9.Final-sources.jar!/" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+2
-2
@@ -1,9 +1,9 @@
|
||||
<component name="libraryTable">
|
||||
<library name="XmlBeans">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsr173_1.0_api.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/xbean.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/resolver.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/xbean.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsr173_1.0_api.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
Generated
+3
-1
@@ -4,6 +4,8 @@
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-codec-1.9.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/commons-codec-1.9-sources.jar!/"/>
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
-1
@@ -6,7 +6,6 @@
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/commons-compress-1.10-src.zip!/commons-compress-1.10-src/src/main/java" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/commons-compress-1.10-src.zip!/commons-compress-1.10-src/src/test/java" />
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
-1
@@ -2,7 +2,6 @@
|
||||
<library name="cucumber-core">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/gherkin-2.12.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/cucumber-jvm-deps-1.0.3.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/cucumber-core-1.2.4.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
|
||||
Generated
+2
-2
@@ -4,10 +4,10 @@
|
||||
<root url="file://$PROJECT_DIR$/lib/annotations/http-client"/>
|
||||
</ANNOTATIONS>
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/httpcore-4.4.5.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/httpmime-4.5.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/httpclient-4.5.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/httpcore-4.4.5.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/fluent-hc-4.5.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/httpmime-4.5.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
|
||||
Generated
+1
-1
@@ -1,8 +1,8 @@
|
||||
<component name="libraryTable">
|
||||
<library name="http-client-3.1">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-logging-1.2.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-httpclient-3.1-patched.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/commons-logging-1.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/commons-httpclient-3.1-src-patched.zip!/commons-httpclient-3.1/docs" />
|
||||
|
||||
Generated
+5
-5
@@ -1,12 +1,12 @@
|
||||
<component name="libraryTable">
|
||||
<library name="jsch-agent-proxy">
|
||||
<CLASSES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.usocket-nc.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.usocket-jna.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.sshagent.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.pageant.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.core.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.connector-factory.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.core.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.pageant.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.sshagent.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.usocket-jna.jar!/" />
|
||||
<root url="jar://$PROJECT_DIR$/lib/jsch.agentproxy.usocket-nc.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES>
|
||||
|
||||
Generated
+3
-1
@@ -4,6 +4,8 @@
|
||||
<root url="jar://$PROJECT_DIR$/lib/proxy-vole_20131209.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/proxy-vole-20131209-src.zip!/"/>
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+3
-1
@@ -4,6 +4,8 @@
|
||||
<root url="jar://$PROJECT_DIR$/lib/swingx-core-1.6.2.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
<SOURCES>
|
||||
<root url="jar://$PROJECT_DIR$/lib/src/swingx-1.6.2-src.zip!/java"/>
|
||||
</SOURCES>
|
||||
</library>
|
||||
</component>
|
||||
Generated
+1
-2
@@ -51,7 +51,6 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/impl/compiler-impl.iml" filepath="$PROJECT_DIR$/java/compiler/impl/compiler-impl.iml" group="java/compiler" />
|
||||
<module fileurl="file://$PROJECT_DIR$/java/compiler/openapi/compiler-openapi.iml" filepath="$PROJECT_DIR$/java/compiler/openapi/compiler-openapi.iml" group="java/compiler" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/configuration-store-impl/configuration-store-impl.iml" filepath="$PROJECT_DIR$/platform/configuration-store-impl/configuration-store-impl.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/configuration-store-impl/testSrc/configuration-store-tests.iml" filepath="$PROJECT_DIR$/platform/configuration-store-impl/testSrc/configuration-store-tests.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/copyright/copyright.iml" filepath="$PROJECT_DIR$/plugins/copyright/copyright.iml" group="plugins" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/core-api/core-api.iml" filepath="$PROJECT_DIR$/platform/core-api/core-api.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/core-impl/core-impl.iml" filepath="$PROJECT_DIR$/platform/core-impl/core-impl.iml" group="platform" />
|
||||
@@ -255,7 +254,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/structuralsearch/structuralsearch-tests.iml" filepath="$PROJECT_DIR$/platform/structuralsearch/structuralsearch-tests.iml" group="plugins/structuralsearch" />
|
||||
<module fileurl="file://$PROJECT_DIR$/platform/structure-view-impl/structure-view-impl.iml" filepath="$PROJECT_DIR$/platform/structure-view-impl/structure-view-impl.iml" group="platform" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/educational-core/student/student.iml" filepath="$PROJECT_DIR$/python/educational-core/student/student.iml" group="python/educational/educational-core" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/educational-python/student-python/student-python.iml" filepath="$PROJECT_DIR$/python/educational-python/student-python/student-python.iml" group="python/educational/educational-python" />
|
||||
<module fileurl="file://$PROJECT_DIR$/python/educational-python/Edu-Python/Edu-Python.iml" filepath="$PROJECT_DIR$/python/educational-python/Edu-Python/Edu-Python.iml" group="python/educational/educational-python" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/svn4idea/svn4idea.iml" filepath="$PROJECT_DIR$/plugins/svn4idea/svn4idea.iml" group="plugins/VCS" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/svn4idea/svn4idea-tests.iml" filepath="$PROJECT_DIR$/plugins/svn4idea/svn4idea-tests.iml" group="plugins/VCS" />
|
||||
<module fileurl="file://$PROJECT_DIR$/plugins/tasks/tasks-api/tasks-api.iml" filepath="$PROJECT_DIR$/plugins/tasks/tasks-api/tasks-api.iml" group="plugins/tasks" />
|
||||
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class RegExpBraceMatcher implements PairedBraceMatcher {
|
||||
@NotNull
|
||||
@Override
|
||||
public BracePair[] getPairs() {
|
||||
return new BracePair[]{
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+1
-2
@@ -4,12 +4,11 @@
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/scripts" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/groovy" type="java-resource" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/jdk" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/restApiDocGenerator/node_modules" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" exported="" name="Groovy" level="project" />
|
||||
<orderEntry type="library" exported="" name="Ant" level="project" />
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
1.8 =>
|
||||
|
||||
1.3 => lib/idea_rt.jar
|
||||
1.4 => lib/forms_rt.jar
|
||||
1.6 => lib/forms_rt.jar
|
||||
|
||||
1.6 => lib/util.jar
|
||||
|
||||
|
||||
@@ -903,7 +903,15 @@ skip_ipr:
|
||||
WriteRegDWORD SHCTX "Software\Microsoft\Windows\CurrentVersion\Uninstall\${PRODUCT_WITH_VER}" \
|
||||
"NoRepair" 1
|
||||
|
||||
ExecWait "$INSTDIR\jre\jre\bin\javaw.exe -Xshare:dump"
|
||||
; Regenerating the Shared Archives for java x64 and x86 bit.
|
||||
; http://docs.oracle.com/javase/8/docs/technotes/guides/vm/class-data-sharing.html
|
||||
IfFileExists $INSTDIR\jre\bin\javaw.exe 0 java64
|
||||
ExecWait "$INSTDIR\jre\bin\javaw.exe -Xshare:dump"
|
||||
java64:
|
||||
IfFileExists $INSTDIR\jre64\bin\javaw.exe 0 skip_regeneration_shared_archive_for_java_64
|
||||
ExecWait "$INSTDIR\jre64\bin\javaw.exe -Xshare:dump"
|
||||
|
||||
skip_regeneration_shared_archive_for_java_64:
|
||||
SetOutPath $INSTDIR\bin
|
||||
; set the current time for installation files under $INSTDIR\bin
|
||||
ExecDos::exec 'copy "$INSTDIR\bin\*.*s" +,,'
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.intellij.build
|
||||
|
||||
import org.jetbrains.intellij.build.impl.LayoutBuilder
|
||||
|
||||
/**
|
||||
* Creates JARs containing classes required to run the external build for IDEA project without IDE.
|
||||
*
|
||||
* @author nik
|
||||
*/
|
||||
class CommunityStandaloneJpsBuilder {
|
||||
private final BuildContext buildContext
|
||||
|
||||
CommunityStandaloneJpsBuilder(BuildContext buildContext) {
|
||||
this.buildContext = buildContext
|
||||
}
|
||||
|
||||
void layoutJps(String targetDir, String buildNumber, @DelegatesTo(LayoutBuilder.LayoutSpec) Closure additionalJars) {
|
||||
AntBuilder ant = buildContext.ant
|
||||
String home = buildContext.paths.communityHome
|
||||
new LayoutBuilder(ant, buildContext.project, false).layout(targetDir) {
|
||||
zip("standalone-jps-${buildNumber}.zip") {
|
||||
jar("util.jar") {
|
||||
module("annotations-common")
|
||||
module("annotations")
|
||||
module("util-rt")
|
||||
module("util")
|
||||
}
|
||||
|
||||
jar("jps-launcher.jar") {
|
||||
module("jps-launcher")
|
||||
}
|
||||
|
||||
jar("jps-model.jar") {
|
||||
module("jps-model-api")
|
||||
module("jps-model-impl")
|
||||
module("jps-model-serialization")
|
||||
}
|
||||
jar("jps-builders.jar") {
|
||||
module("forms_rt")
|
||||
module("forms-compiler")
|
||||
module("instrumentation-util")
|
||||
module("instrumentation-util-8")
|
||||
module("javac-ref-scanner-8")
|
||||
module("jps-builders")
|
||||
module("jps-standalone-builder")
|
||||
}
|
||||
jar("idea_rt.jar") {
|
||||
module("java-runtime")
|
||||
}
|
||||
jar("jps-builders-6.jar") {
|
||||
module("jps-builders-6")
|
||||
}
|
||||
//layout of groovy jars must be consistent with GroovyBuilder.getGroovyRtRoots method
|
||||
jar("groovy-jps-plugin.jar") {
|
||||
module("groovy-jps-plugin")
|
||||
}
|
||||
jar("groovy_rt.jar") {
|
||||
module("groovy_rt")
|
||||
}
|
||||
jar("groovy-rt-constants.jar") {
|
||||
module("groovy-rt-constants")
|
||||
}
|
||||
jar("ui-designer-jps-plugin.jar") { module("ui-designer-jps-plugin") }
|
||||
|
||||
|
||||
jar("maven-jps-plugin.jar") { module("maven-jps-plugin") }
|
||||
jar("aether-dependency-resolver.jar") { module("aether-dependency-resolver") }
|
||||
ant.fileset(dir: "$home/plugins/maven/lib") {
|
||||
include(name: "aether-*.jar")
|
||||
include(name: "maven-aether-provider-*.jar")
|
||||
}
|
||||
jar("gradle-jps-plugin.jar") { module("gradle-jps-plugin") }
|
||||
ant.fileset(dir: "$home/plugins/maven/maven30-server-impl/lib/maven3/lib") { include(name: "plexus-utils-*.jar") }
|
||||
|
||||
jar("eclipse-jps-plugin.jar") {
|
||||
module("common-eclipse-util")
|
||||
module("eclipse-jps-plugin")
|
||||
}
|
||||
jar("devkit-jps-plugin.jar") { module("devkit-jps-plugin") }
|
||||
jar("intellilang-jps-plugin.jar") { module("intellilang-jps-plugin") }
|
||||
ant.fileset(dir: "$home/lib") {
|
||||
include(name: "jdom.jar")
|
||||
include(name: "jna.jar")
|
||||
include(name: "jna-platform.jar")
|
||||
include(name: "oromatcher.jar")
|
||||
include(name: "trove4j.jar")
|
||||
include(name: "asm-all.jar")
|
||||
include(name: "nanoxml-*.jar")
|
||||
include(name: "protobuf-*.jar")
|
||||
include(name: "cli-parser-*.jar")
|
||||
include(name: "log4j.jar")
|
||||
include(name: "jgoodies-forms.jar")
|
||||
include(name: "ecj*.jar")
|
||||
include(name: "netty-all-*.jar")
|
||||
include(name: "snappy-in-java-*.jar")
|
||||
include(name: "xercesImpl.jar")
|
||||
include(name: "xml-apis.jar")
|
||||
}
|
||||
ant.fileset(dir: "$home/jps/lib") {
|
||||
include(name: "optimizedFileManager.jar")
|
||||
}
|
||||
jar("ant-jps-plugin.jar") { module("ant-jps-plugin") }
|
||||
include(additionalJars)
|
||||
}
|
||||
jar("jps-build-test-${buildNumber}.jar") {
|
||||
moduleTests("jps-builders")
|
||||
moduleTests("jps-model-tests")
|
||||
moduleTests("jps-serialization-tests")
|
||||
}
|
||||
}
|
||||
buildContext.notifyArtifactBuilt(targetDir)
|
||||
}
|
||||
}
|
||||
@@ -16,7 +16,6 @@
|
||||
package org.jetbrains.intellij.build
|
||||
|
||||
import org.codehaus.gant.GantBinding
|
||||
|
||||
/**
|
||||
* @author nik
|
||||
*/
|
||||
@@ -42,21 +41,23 @@ class IdeaCommunityBuilder {
|
||||
|
||||
void buildIntelliJCore() {
|
||||
buildContext.projectBuilder.targetFolder = buildContext.options.outputRootPath
|
||||
BuildTasks.create(buildContext).compileModules(binding["analysisApiModules"] + binding["analysisImplModules"])
|
||||
|
||||
def layouts = binding["includeFile"]("$buildContext.paths.communityHome/build/scripts/layouts.gant")
|
||||
layoutIntelliJCore(layouts)
|
||||
def builder = new IntelliJCoreArtifactsBuilder(buildContext)
|
||||
builder.compileModules()
|
||||
builder.layoutIntelliJCore()
|
||||
}
|
||||
|
||||
void buildDistJars() {
|
||||
BuildTasks.create(buildContext).buildDistributions()
|
||||
layoutAdditionalArtifacts()
|
||||
layoutCoreArtifacts()
|
||||
}
|
||||
|
||||
void buildDistributions() {
|
||||
def tasks = BuildTasks.create(buildContext)
|
||||
tasks.buildDistributions()
|
||||
layoutAdditionalArtifacts(true)
|
||||
buildContext.messages.block("Build standalone JPS") {
|
||||
String jpsArtifactDir = "$buildContext.paths.artifacts/jps"
|
||||
new CommunityStandaloneJpsBuilder(buildContext).layoutJps(jpsArtifactDir, buildContext.fullBuildNumber, {})
|
||||
}
|
||||
tasks.buildUpdaterJar()
|
||||
}
|
||||
|
||||
@@ -64,30 +65,7 @@ class IdeaCommunityBuilder {
|
||||
BuildTasks.create(buildContext).buildUnpackedDistribution(targetDirectory)
|
||||
}
|
||||
|
||||
void layoutAdditionalArtifacts(boolean buildJps = false) {
|
||||
def layouts = binding["includeFile"]("$buildContext.paths.communityHome/build/scripts/layouts.gant")
|
||||
layoutIntelliJCore(layouts)
|
||||
if (buildJps) {
|
||||
buildContext.messages.block("Build standalone JPS") {
|
||||
String jpsArtifactDir = "$buildContext.paths.artifacts/jps"
|
||||
layouts.layoutJps(buildContext.paths.communityHome, jpsArtifactDir, buildContext.fullBuildNumber, {})
|
||||
buildContext.notifyArtifactBuilt(jpsArtifactDir)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void layoutIntelliJCore(layouts) {
|
||||
buildContext.messages.block("Build intellij-core") {
|
||||
String coreArtifactDir = "$buildContext.paths.artifacts/core"
|
||||
buildContext.ant.mkdir(dir: coreArtifactDir)
|
||||
layouts.layout_core(buildContext.paths.communityHome, coreArtifactDir)
|
||||
buildContext.notifyArtifactBuilt(coreArtifactDir)
|
||||
|
||||
def intellijCoreZip = "${buildContext.paths.artifacts}/intellij-core-${buildContext.buildNumber}.zip"
|
||||
buildContext.ant.zip(destfile: intellijCoreZip) {
|
||||
fileset(dir: coreArtifactDir)
|
||||
}
|
||||
buildContext.notifyArtifactBuilt(intellijCoreZip)
|
||||
}
|
||||
void layoutCoreArtifacts() {
|
||||
new IntelliJCoreArtifactsBuilder(buildContext).layoutIntelliJCore()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jetbrains.intellij.build
|
||||
|
||||
import org.jetbrains.intellij.build.impl.LayoutBuilder
|
||||
|
||||
/**
|
||||
* Builds artifacts which are used in Kotlin Compiler and UpSource
|
||||
*
|
||||
* @author nik
|
||||
*/
|
||||
class IntelliJCoreArtifactsBuilder {
|
||||
private static final List<String> ANALYSIS_API_MODULES = [
|
||||
"analysis-api",
|
||||
"boot",
|
||||
"core-api",
|
||||
"duplicates-analysis",
|
||||
"editor-ui-api",
|
||||
"editor-ui-ex",
|
||||
"extensions",
|
||||
"indexing-api",
|
||||
"java-analysis-api",
|
||||
"java-indexing-api",
|
||||
"java-psi-api",
|
||||
"java-structure-view",
|
||||
"jps-model-api",
|
||||
"jps-model-serialization",
|
||||
"projectModel-api",
|
||||
"util",
|
||||
"util-rt",
|
||||
"xml-analysis-api",
|
||||
"xml-psi-api",
|
||||
"xml-structure-view-api",
|
||||
]
|
||||
private static final List<String> ANALYSIS_IMPL_MODULES = [
|
||||
"analysis-impl",
|
||||
"core-impl",
|
||||
"indexing-impl",
|
||||
"java-analysis-impl",
|
||||
"java-indexing-impl",
|
||||
"java-psi-impl",
|
||||
"projectModel-impl",
|
||||
"structure-view-impl",
|
||||
"xml-analysis-impl",
|
||||
"xml-psi-impl",
|
||||
"xml-structure-view-impl",
|
||||
]
|
||||
private final BuildContext buildContext
|
||||
|
||||
IntelliJCoreArtifactsBuilder(BuildContext buildContext) {
|
||||
this.buildContext = buildContext
|
||||
}
|
||||
|
||||
void compileModules() {
|
||||
BuildTasks.create(buildContext).compileModules(ANALYSIS_API_MODULES + ANALYSIS_IMPL_MODULES)
|
||||
}
|
||||
|
||||
void layoutIntelliJCore() {
|
||||
buildContext.messages.block("Build intellij-core") {
|
||||
String coreArtifactDir = "$buildContext.paths.artifacts/core"
|
||||
AntBuilder ant = buildContext.ant
|
||||
ant.mkdir(dir: coreArtifactDir)
|
||||
String home = buildContext.paths.communityHome
|
||||
List<String> analysisModules = ANALYSIS_API_MODULES + ANALYSIS_IMPL_MODULES
|
||||
new LayoutBuilder(ant, buildContext.project, false).layout(coreArtifactDir) {
|
||||
jar("intellij-core.jar") {
|
||||
module("util-rt")
|
||||
module("util")
|
||||
module("core-api")
|
||||
module("core-impl")
|
||||
module("extensions")
|
||||
module("java-psi-api")
|
||||
module("java-psi-impl")
|
||||
}
|
||||
|
||||
jar("annotations.jar") {
|
||||
module("annotations-common")
|
||||
module("annotations")
|
||||
}
|
||||
|
||||
jar("intellij-core-analysis.jar") {
|
||||
analysisModules.each { module it }
|
||||
}
|
||||
|
||||
ant.fileset(dir: "$home/lib") {
|
||||
include(name: "asm-all.jar")
|
||||
include(name: "guava-19.0.jar")
|
||||
include(name: "picocontainer.jar")
|
||||
include(name: "trove4j.jar")
|
||||
include(name: "cli-parser-1.1.jar")
|
||||
include(name: "snappy-in-java-0.5.1.jar")
|
||||
include(name: "jayatana-1.2.4.jar")
|
||||
include(name: "imgscalr-lib-4.2.jar")
|
||||
include(name: "batik-all.jar")
|
||||
include(name: "xmlgraphics-commons-1.5.jar")
|
||||
include(name: "xml-apis-ext.jar")
|
||||
}
|
||||
}
|
||||
buildContext.notifyArtifactBuilt(coreArtifactDir)
|
||||
|
||||
def intellijCoreZip = "${buildContext.paths.artifacts}/intellij-core-${buildContext.buildNumber}.zip"
|
||||
ant.zip(destfile: intellijCoreZip) {
|
||||
fileset(dir: coreArtifactDir)
|
||||
}
|
||||
buildContext.notifyArtifactBuilt(intellijCoreZip)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,6 @@ target(compile: "Compile project") {
|
||||
}
|
||||
|
||||
if (!isDefined("avoid.project.rebuild")) {
|
||||
clearBuildCaches()
|
||||
projectBuilder.cleanOutput()
|
||||
projectBuilder.buildAll()
|
||||
}
|
||||
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
|
||||
|
||||
includeTargets << new File("${guessHome(this)}/build/scripts/utils.gant")
|
||||
|
||||
def layoutJps(String home, String targetDir, String buildNumber, Closure additionalJars) {
|
||||
layout(targetDir) {
|
||||
zip("standalone-jps-${buildNumber}.zip") {
|
||||
jar("util.jar") {
|
||||
module("annotations-common")
|
||||
module("annotations")
|
||||
module("util-rt")
|
||||
module("util")
|
||||
}
|
||||
|
||||
jar("jps-launcher.jar") {
|
||||
module("jps-launcher")
|
||||
}
|
||||
|
||||
jar("jps-model.jar") {
|
||||
module("jps-model-api")
|
||||
module("jps-model-impl")
|
||||
module("jps-model-serialization")
|
||||
}
|
||||
jar("jps-builders.jar") {
|
||||
module("forms_rt")
|
||||
module("forms-compiler")
|
||||
module("instrumentation-util")
|
||||
module("instrumentation-util-8")
|
||||
module("javac-ref-scanner-8")
|
||||
module("jps-builders")
|
||||
module("jps-standalone-builder")
|
||||
}
|
||||
jar("idea_rt.jar") {
|
||||
module("java-runtime")
|
||||
}
|
||||
jar("jps-builders-6.jar") {
|
||||
module("jps-builders-6")
|
||||
}
|
||||
//layout of groovy jars must be consistent with GroovyBuilder.getGroovyRtRoots method
|
||||
jar("groovy-jps-plugin.jar") {
|
||||
module("groovy-jps-plugin")
|
||||
}
|
||||
jar("groovy_rt.jar") {
|
||||
module("groovy_rt")
|
||||
}
|
||||
jar("groovy-rt-constants.jar") {
|
||||
module("groovy-rt-constants")
|
||||
}
|
||||
jar("ui-designer-jps-plugin.jar") { module("ui-designer-jps-plugin") }
|
||||
|
||||
|
||||
jar("maven-jps-plugin.jar") { module("maven-jps-plugin") }
|
||||
jar("aether-dependency-resolver.jar") { module("aether-dependency-resolver") }
|
||||
fileset(dir: "$home/plugins/maven/lib") {
|
||||
include(name: "aether-*.jar")
|
||||
include(name: "maven-aether-provider-*.jar")
|
||||
}
|
||||
jar("gradle-jps-plugin.jar") { module("gradle-jps-plugin") }
|
||||
fileset(dir: "$home/plugins/maven/maven30-server-impl/lib/maven3/lib") {include(name: "plexus-utils-*.jar")}
|
||||
|
||||
jar("eclipse-jps-plugin.jar") {
|
||||
module("common-eclipse-util")
|
||||
module("eclipse-jps-plugin")
|
||||
}
|
||||
jar("devkit-jps-plugin.jar") { module("devkit-jps-plugin") }
|
||||
jar("intellilang-jps-plugin.jar") { module("intellilang-jps-plugin") }
|
||||
fileset(dir: "$home/lib") {
|
||||
include(name: "jdom.jar")
|
||||
include(name: "jna.jar")
|
||||
include(name: "jna-platform.jar")
|
||||
include(name: "oromatcher.jar")
|
||||
include(name: "trove4j.jar")
|
||||
include(name: "asm-all.jar")
|
||||
include(name: "nanoxml-*.jar")
|
||||
include(name: "protobuf-*.jar")
|
||||
include(name: "cli-parser-*.jar")
|
||||
include(name: "log4j.jar")
|
||||
include(name: "jgoodies-forms.jar")
|
||||
include(name: "ecj*.jar")
|
||||
include(name: "netty-all-*.jar")
|
||||
include(name: "snappy-in-java-*.jar")
|
||||
include(name: "xercesImpl.jar")
|
||||
include(name: "xml-apis.jar")
|
||||
}
|
||||
fileset(dir: "$home/jps/lib") {
|
||||
include(name: "optimizedFileManager.jar")
|
||||
}
|
||||
jar("ant-jps-plugin.jar") { module("ant-jps-plugin") }
|
||||
additionalJars()
|
||||
}
|
||||
jar("jps-build-test-${buildNumber}.jar") {
|
||||
moduleTests("jps-builders")
|
||||
moduleTests("jps-model-tests")
|
||||
moduleTests("jps-serialization-tests")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def layout_core(String home, String target) {
|
||||
layout(target) {
|
||||
jar("intellij-core.jar") {
|
||||
module("util-rt")
|
||||
module("util")
|
||||
module("core-api")
|
||||
module("core-impl")
|
||||
module("extensions")
|
||||
module("java-psi-api")
|
||||
module("java-psi-impl")
|
||||
}
|
||||
|
||||
jar("annotations.jar") {
|
||||
module("annotations-common")
|
||||
module("annotations")
|
||||
}
|
||||
|
||||
jar("intellij-core-analysis.jar") {
|
||||
analysisApiModules.each { module it; }
|
||||
analysisImplModules.each { module it; }
|
||||
}
|
||||
|
||||
fileset(dir: "$home/lib") {
|
||||
include(name: "asm-all.jar")
|
||||
include(name: "guava-19.0.jar")
|
||||
include(name: "picocontainer.jar")
|
||||
include(name: "trove4j.jar")
|
||||
include(name: "cli-parser-1.1.jar")
|
||||
include(name: "snappy-in-java-0.5.1.jar")
|
||||
include(name: "jayatana-1.2.4.jar")
|
||||
include(name: "imgscalr-lib-4.2.jar")
|
||||
include(name: "batik-all.jar")
|
||||
include(name: "xmlgraphics-commons-1.5.jar")
|
||||
include(name: "xml-apis-ext.jar")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -19,10 +19,6 @@ def home = guessHome(this)
|
||||
|
||||
includeTargets << new File("${home}/build/scripts/common_tests.gant")
|
||||
|
||||
if ("GIT_TESTS".equalsIgnoreCase(System.getProperty("idea.test.group"))) {
|
||||
setProperty("cucumber_test_dirs", [["git4idea", "Idea", "${home}/plugins/git4idea/test-features/git4idea", "git4idea"]])
|
||||
}
|
||||
|
||||
def args = []
|
||||
|
||||
def mem = System.getProperty("test.jvm.memory")
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
|
||||
import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
|
||||
import com.intellij.openapi.util.SystemInfoRt
|
||||
import com.intellij.openapi.util.io.FileUtilRt
|
||||
import org.apache.tools.ant.BuildException
|
||||
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
@@ -19,14 +18,13 @@ import static org.jetbrains.jps.idea.IdeaProjectLoader.guessHome
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
includeTargets << new File("${guessHome(this)}/build/scripts/utils.gant")
|
||||
|
||||
target(default: "Unpack JDKs") {
|
||||
unpackJdks()
|
||||
}
|
||||
|
||||
def unpackJdks() {
|
||||
def osDirName = SystemInfo.isWindows ? "win" : SystemInfo.isMac ? "mac" : "linux"
|
||||
String home = guessHome(this)
|
||||
def osDirName = SystemInfoRt.isWindows ? "win" : SystemInfoRt.isMac ? "mac" : "linux"
|
||||
String basePath
|
||||
if (new File(home).getName().equals("community")) {
|
||||
basePath = new File(home).getParent()
|
||||
@@ -38,7 +36,7 @@ def unpackJdks() {
|
||||
def jdkDir = new File(basePath, "build/jdk")
|
||||
def osDir = new File(jdkDir, osDirName)
|
||||
if (!osDir.isDirectory()) {
|
||||
projectBuilder.info("Cannot unpack JDK: $osDir not found")
|
||||
ant.project.log("Cannot unpack JDK: $osDir not found")
|
||||
return
|
||||
}
|
||||
|
||||
@@ -48,28 +46,28 @@ def unpackJdks() {
|
||||
|
||||
private void unpackJdk(File osDir, File jdkDir, String archiveFileNamePrefix, String targetDirectoryName) {
|
||||
def jdkFiles = osDir.listFiles().findAll { it.name.startsWith(archiveFileNamePrefix) }
|
||||
if (SystemInfo.isWindows) {
|
||||
if (SystemInfoRt.isWindows) {
|
||||
def arch = System.getenv("ProgramFiles(x86)") != null ? "x64" : "x86"
|
||||
jdkFiles = jdkFiles.findAll { it.name.endsWith("_${arch}.tar.gz") }
|
||||
}
|
||||
if (SystemInfo.isLinux) {
|
||||
if (SystemInfoRt.isLinux) {
|
||||
jdkFiles = jdkFiles.findAll { it.name.endsWith("64.tar.gz") }
|
||||
}
|
||||
if (jdkFiles.empty) {
|
||||
projectBuilder.error("Cannot unpack JDK: $archiveFileNamePrefix not found in $osDir")
|
||||
throw new BuildException("Cannot unpack JDK: $archiveFileNamePrefix not found in $osDir")
|
||||
}
|
||||
if (jdkFiles.size() > 1) {
|
||||
projectBuilder.error("Cannot unpack JDK: several matching files are found ($jdkFiles)")
|
||||
throw new BuildException("Cannot unpack JDK: several matching files are found ($jdkFiles)")
|
||||
}
|
||||
|
||||
File jdkFile = jdkFiles.first()
|
||||
def destDir = new File(jdkDir, targetDirectoryName)
|
||||
FileUtil.delete(destDir)
|
||||
projectBuilder.info("Extracting $jdkFile to $destDir")
|
||||
FileUtilRt.delete(destDir)
|
||||
ant.project.log("Extracting $jdkFile to $destDir")
|
||||
ant.mkdir(dir: destDir)
|
||||
|
||||
if (jdkFile.name.endsWith(".tar.gz")) {
|
||||
if (SystemInfo.isWindows) {
|
||||
if (SystemInfoRt.isWindows) {
|
||||
ant.untar(src: jdkFile, dest: destDir, compression: 'gzip')
|
||||
}
|
||||
else {
|
||||
@@ -81,7 +79,7 @@ private void unpackJdk(File osDir, File jdkDir, String archiveFileNamePrefix, St
|
||||
arg(value: destDir)
|
||||
}
|
||||
def jdkContents = new File(destDir, "jdk/Contents")
|
||||
if (SystemInfo.isMac && jdkContents.exists()) {
|
||||
if (SystemInfoRt.isMac && jdkContents.exists()) {
|
||||
jdkContents.listFiles().each { file ->
|
||||
ant.exec(executable: "mv", dir: destDir) {
|
||||
arg(value: file.absolutePath)
|
||||
@@ -92,6 +90,20 @@ private void unpackJdk(File osDir, File jdkDir, String archiveFileNamePrefix, St
|
||||
}
|
||||
}
|
||||
else {
|
||||
projectBuilder.error("Cannot unpack JDK: unknown extension of file '${jdkFile.name}'")
|
||||
throw new BuildException("Cannot unpack JDK: unknown extension of file '${jdkFile.name}'")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static String guessHome(Script script) {
|
||||
File home = new File(script["gant.file"].substring("file:".length()))
|
||||
|
||||
while (home != null) {
|
||||
if (home.isDirectory()) {
|
||||
if (new File(home, ".idea").exists()) return home.getCanonicalPath()
|
||||
}
|
||||
|
||||
home = home.getParentFile()
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -204,14 +204,6 @@ binding.setVariable("prepareOutputFolder", {
|
||||
}
|
||||
})
|
||||
|
||||
binding.setVariable("clearBuildCaches", {
|
||||
//todo[nik] this is temporary solution until we update bootstrap jps-builders jars to the new version where cleaning is performed in JpsGantProjectBuilder#cleanOutput
|
||||
def storageRoot = projectBuilder.dataStorageRoot
|
||||
if (storageRoot != null) {
|
||||
FileUtil.delete(storageRoot)
|
||||
}
|
||||
})
|
||||
|
||||
binding.setVariable("findModule", {String name ->
|
||||
project.modules.find { it.name == name }
|
||||
})
|
||||
@@ -349,46 +341,8 @@ binding.setVariable("buildModulesAndCollectUsedJars", { List modules, List appro
|
||||
def modulesToBuild = []
|
||||
def modulesNames = modules.contains(javaRT) ? modules : [*modules, javaRT]
|
||||
def usedJars = collectUsedJars(modulesNames, approvedJars, forbiddenJars, modulesToBuild)
|
||||
clearBuildCaches()
|
||||
projectBuilder.cleanOutput()
|
||||
projectBuilder.buildModules(modulesToBuild)
|
||||
|
||||
return usedJars
|
||||
})
|
||||
|
||||
// modules used in Upsource and in Kotlin as an API to IDEA
|
||||
binding.setVariable("analysisApiModules", [
|
||||
"analysis-api",
|
||||
"boot",
|
||||
"core-api",
|
||||
"duplicates-analysis",
|
||||
"editor-ui-api",
|
||||
"editor-ui-ex",
|
||||
"extensions",
|
||||
"indexing-api",
|
||||
"java-analysis-api",
|
||||
"java-indexing-api",
|
||||
"java-psi-api",
|
||||
"java-structure-view",
|
||||
"jps-model-api",
|
||||
"jps-model-serialization",
|
||||
"projectModel-api",
|
||||
"util",
|
||||
"util-rt",
|
||||
"xml-analysis-api",
|
||||
"xml-psi-api",
|
||||
"xml-structure-view-api",
|
||||
])
|
||||
binding.setVariable("analysisImplModules", [
|
||||
"analysis-impl",
|
||||
"core-impl",
|
||||
"indexing-impl",
|
||||
"java-analysis-impl",
|
||||
"java-indexing-impl",
|
||||
"java-psi-impl",
|
||||
"projectModel-impl",
|
||||
"structure-view-impl",
|
||||
"xml-analysis-impl",
|
||||
"xml-psi-impl",
|
||||
"xml-structure-view-impl",
|
||||
])
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="module" module-name="annotations" />
|
||||
<orderEntry type="module" module-name="ant" />
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="true" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -9,6 +9,13 @@ com.intellij.tasks.live.*
|
||||
git4idea.*
|
||||
org.jetbrains.plugins.github.*
|
||||
|
||||
[VCS_TESTS]
|
||||
hg4idea.*
|
||||
com.intellij.openapi.vcs.*
|
||||
com.intellij.testFramework.vcs.*
|
||||
com.intellij.tasks.vcs.*
|
||||
com.intellij.vcs.*
|
||||
|
||||
[ANDROID_SDK_TOOLS_TESTS]
|
||||
com.android.dvlib.*
|
||||
com.android.ide.common.*
|
||||
@@ -139,7 +146,7 @@ com.android.tools.idea.editors.strings.StringResourceDataTest
|
||||
com.android.tools.idea.rendering.ClassConverterTest
|
||||
com.android.tools.idea.gradle.service.notification.errors.UnknownHostErrorHandlerTest
|
||||
com.android.tools.idea.wizard.AsyncValidatorTest
|
||||
org.jetbrains.jps.android.AndroidBuilderTest
|
||||
org.jetbrains.android.jps.AndroidBuilderTest
|
||||
com.android.tools.idea.gradle.service.notification.hyperlink.CustomNotificationListenerTest
|
||||
com.android.repository.impl.installer.BasicInstallerTest
|
||||
org.jetbrains.android.AndroidLintTest
|
||||
@@ -172,4 +179,8 @@ com.intellij.junit4.JUnit4IntegrationTest
|
||||
com.intellij.testGuiFramework.tests.*
|
||||
|
||||
[GROOVY_TESTS]
|
||||
org.jetbrains.plugins.groovy.*
|
||||
org.jetbrains.plugins.groovy.*
|
||||
|
||||
[COMPILER_AND_DEBUGGER_TESTS]
|
||||
com.intellij.compiler.*
|
||||
org.jetbrains.jps.*
|
||||
@@ -27,6 +27,7 @@ import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.openapi.wm.IdeFocusManager;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFile;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.ui.IdeBorderFactory;
|
||||
import com.intellij.ui.PopupHandler;
|
||||
@@ -78,7 +79,8 @@ final class ImageEditorUI extends JPanel implements DataProvider, CopyProvider,
|
||||
@NonNls
|
||||
private static final String ZOOM_FACTOR_PROP = "ImageEditor.zoomFactor";
|
||||
|
||||
private final @Nullable ImageEditor editor;
|
||||
@Nullable
|
||||
private final ImageEditor editor;
|
||||
private final DeleteProvider deleteProvider;
|
||||
private final CopyPasteSupport copyPasteSupport;
|
||||
|
||||
@@ -483,33 +485,47 @@ final class ImageEditorUI extends JPanel implements DataProvider, CopyProvider,
|
||||
|
||||
@Nullable
|
||||
public Object getData(String dataId) {
|
||||
|
||||
if (CommonDataKeys.PROJECT.is(dataId)) {
|
||||
return editor != null ? editor.getProject() : null;
|
||||
} else if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
|
||||
}
|
||||
else if (CommonDataKeys.VIRTUAL_FILE.is(dataId)) {
|
||||
return editor != null ? editor.getFile() : null;
|
||||
} else if (CommonDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) {
|
||||
}
|
||||
else if (CommonDataKeys.VIRTUAL_FILE_ARRAY.is(dataId)) {
|
||||
return editor != null ? new VirtualFile[]{editor.getFile()} : VirtualFile.EMPTY_ARRAY;
|
||||
} else if (CommonDataKeys.PSI_FILE.is(dataId)) {
|
||||
return getData(CommonDataKeys.PSI_ELEMENT.getName());
|
||||
} else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
|
||||
VirtualFile file = editor != null ? editor.getFile() : null;
|
||||
return file != null && file.isValid() ? PsiManager.getInstance(editor.getProject()).findFile(file) : null;
|
||||
} else if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
|
||||
return editor != null ? new PsiElement[]{(PsiElement)getData(CommonDataKeys.PSI_ELEMENT.getName())} : PsiElement.EMPTY_ARRAY;
|
||||
} else if (PlatformDataKeys.COPY_PROVIDER.is(dataId) && copyPasteSupport != null) {
|
||||
}
|
||||
else if (CommonDataKeys.PSI_FILE.is(dataId)) {
|
||||
return findPsiFile();
|
||||
}
|
||||
else if (CommonDataKeys.PSI_ELEMENT.is(dataId)) {
|
||||
return findPsiFile();
|
||||
}
|
||||
else if (LangDataKeys.PSI_ELEMENT_ARRAY.is(dataId)) {
|
||||
PsiElement psi = findPsiFile();
|
||||
return psi != null ? new PsiElement[]{psi} : PsiElement.EMPTY_ARRAY;
|
||||
}
|
||||
else if (PlatformDataKeys.COPY_PROVIDER.is(dataId) && copyPasteSupport != null) {
|
||||
return this;
|
||||
} else if (PlatformDataKeys.CUT_PROVIDER.is(dataId) && copyPasteSupport != null) {
|
||||
}
|
||||
else if (PlatformDataKeys.CUT_PROVIDER.is(dataId) && copyPasteSupport != null) {
|
||||
return copyPasteSupport.getCutProvider();
|
||||
} else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) {
|
||||
}
|
||||
else if (PlatformDataKeys.DELETE_ELEMENT_PROVIDER.is(dataId)) {
|
||||
return deleteProvider;
|
||||
} else if (ImageComponentDecorator.DATA_KEY.is(dataId)) {
|
||||
}
|
||||
else if (ImageComponentDecorator.DATA_KEY.is(dataId)) {
|
||||
return editor != null ? editor : this;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private PsiFile findPsiFile() {
|
||||
VirtualFile file = editor != null ? editor.getFile() : null;
|
||||
return file != null && file.isValid() ? PsiManager.getInstance(editor.getProject()).findFile(file) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void performCopy(@NotNull DataContext dataContext) {
|
||||
ImageDocument document = imageComponent.getDocument();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module relativePaths="true" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_4" inherit-compiler-output="true">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -154,7 +154,7 @@ public class CompilerErrorTreeView extends NewErrorTreeViewPanel {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean use15Suppressions(@NotNull final PsiDocCommentOwner container) {
|
||||
protected boolean use15Suppressions(@NotNull final PsiJavaDocumentedElement container) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
@@ -166,7 +166,7 @@ public class CompilerErrorTreeView extends NewErrorTreeViewPanel {
|
||||
protected SuppressFix getSuppressAction(@NotNull final String id) {
|
||||
return new SuppressForClassFix(id){
|
||||
@Override
|
||||
protected boolean use15Suppressions(@NotNull final PsiDocCommentOwner container) {
|
||||
protected boolean use15Suppressions(@NotNull final PsiJavaDocumentedElement container) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -253,7 +253,7 @@ public class BuildManager implements Disposable {
|
||||
projectManager.addProjectManagerListener(new ProjectWatcher());
|
||||
|
||||
final MessageBusConnection conn = application.getMessageBus().connect();
|
||||
conn.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() {
|
||||
conn.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() {
|
||||
@Override
|
||||
public void after(@NotNull List<? extends VFileEvent> events) {
|
||||
if (!IS_UNIT_TEST_MODE && shouldTriggerMake(events)) {
|
||||
|
||||
+7
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,10 +21,14 @@ import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.packaging.artifacts.ArtifactManager
|
||||
|
||||
internal class ArtifactWebServerRootsProvider : PrefixlessWebServerRootsProvider() {
|
||||
override fun resolve(path: String, project: Project, resolver: FileResolver): PathInfo? {
|
||||
override fun resolve(path: String, project: Project, resolver: FileResolver, pathQuery: PathQuery): PathInfo? {
|
||||
if (!pathQuery.searchInArtifacts) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (artifact in ArtifactManager.getInstance(project).artifacts) {
|
||||
val root = artifact.outputFile ?: continue
|
||||
return resolver.resolve(path, root)
|
||||
return resolver.resolve(path, root, pathQuery = pathQuery)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module relativePaths="false" type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_5" inherit-compiler-output="true">
|
||||
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_6" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
|
||||
@@ -236,6 +236,9 @@ public class JavaExecutionStack extends XExecutionStack {
|
||||
myContainer.addStackFrames(Collections.emptyList(), true);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
((JavaStackFrame)frame).setInsertCapturePoint(StackCapturingLineBreakpoint.getMatchingDisabledInsertionPoint(frameProxy));
|
||||
}
|
||||
}
|
||||
|
||||
myDebugProcess.getManagerThread().schedule(
|
||||
|
||||
@@ -29,6 +29,8 @@ import com.intellij.debugger.jdi.DecompiledLocalVariable;
|
||||
import com.intellij.debugger.jdi.LocalVariableProxyImpl;
|
||||
import com.intellij.debugger.jdi.LocalVariablesUtil;
|
||||
import com.intellij.debugger.jdi.StackFrameProxyImpl;
|
||||
import com.intellij.debugger.memory.utils.StackFrameItem;
|
||||
import com.intellij.debugger.settings.CapturePoint;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.debugger.ui.breakpoints.Breakpoint;
|
||||
@@ -45,6 +47,7 @@ import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.ui.ColoredTextContainer;
|
||||
import com.intellij.ui.SimpleTextAttributes;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.text.CharArrayUtil;
|
||||
import com.intellij.xdebugger.XDebugSession;
|
||||
@@ -68,6 +71,8 @@ import java.util.*;
|
||||
*/
|
||||
public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProvider {
|
||||
private static final Logger LOG = Logger.getInstance(JavaStackFrame.class);
|
||||
public static final DummyMessageValueNode LOCAL_VARIABLES_INFO_UNAVAILABLE_MESSAGE_NODE =
|
||||
new DummyMessageValueNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(), XDebuggerUIConstants.INFORMATION_MESSAGE_ICON);
|
||||
|
||||
private final DebugProcessImpl myDebugProcess;
|
||||
@Nullable private final XSourcePosition myXSourcePosition;
|
||||
@@ -76,6 +81,7 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
|
||||
private static final JavaFramesListRenderer FRAME_RENDERER = new JavaFramesListRenderer();
|
||||
private JavaDebuggerEvaluator myEvaluator = null;
|
||||
private final String myEqualityObject;
|
||||
private CapturePoint myInsertCapturePoint;
|
||||
|
||||
public JavaStackFrame(@NotNull StackFrameDescriptorImpl descriptor, boolean update) {
|
||||
myDescriptor = descriptor;
|
||||
@@ -123,6 +129,9 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
|
||||
}
|
||||
}
|
||||
FRAME_RENDERER.customizePresentation(myDescriptor, component, selectedDescriptor);
|
||||
if (myInsertCapturePoint != null) {
|
||||
component.setIcon(XDebuggerUIConstants.INFORMATION_MESSAGE_ICON);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -137,6 +146,12 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
|
||||
@Override
|
||||
public void threadAction() {
|
||||
if (node.isObsolete()) return;
|
||||
if (myInsertCapturePoint != null) {
|
||||
node.setMessage("Async stacktrace from " +
|
||||
myInsertCapturePoint.myClassName + "." + myInsertCapturePoint.myMethodName +
|
||||
" could be available here, enable in", XDebuggerUIConstants.INFORMATION_MESSAGE_ICON,
|
||||
SimpleTextAttributes.REGULAR_ATTRIBUTES, StackFrameItem.CAPTURE_SETTINGS_OPENER);
|
||||
}
|
||||
XValueChildrenList children = new XValueChildrenList();
|
||||
buildVariablesThreadAction(getFrameDebuggerContext(getDebuggerContext()), children, node);
|
||||
node.addChildren(children, true);
|
||||
@@ -321,7 +336,7 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
if (e.getCause() instanceof AbsentInformationException) {
|
||||
children.add(new DummyMessageValueNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(), XDebuggerUIConstants.INFORMATION_MESSAGE_ICON));
|
||||
children.add(LOCAL_VARIABLES_INFO_UNAVAILABLE_MESSAGE_NODE);
|
||||
// trying to collect values from variable slots
|
||||
try {
|
||||
for (Map.Entry<DecompiledLocalVariable, Value> entry : LocalVariablesUtil.fetchValues(getStackFrameProxy(), debugProcess, true).entrySet()) {
|
||||
@@ -389,6 +404,11 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myMessage;
|
||||
}
|
||||
}
|
||||
|
||||
protected void superBuildVariables(final EvaluationContextImpl evaluationContext, XValueChildrenList children) throws EvaluateException {
|
||||
@@ -669,6 +689,10 @@ public class JavaStackFrame extends XStackFrame implements JVMStackFrameInfoProv
|
||||
return rangeRef.get();
|
||||
}
|
||||
|
||||
public void setInsertCapturePoint(CapturePoint insertCapturePoint) {
|
||||
myInsertCapturePoint = insertCapturePoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSynthetic() {
|
||||
return myDescriptor.isSynthetic();
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.intellij.debugger.SourcePosition;
|
||||
import com.intellij.debugger.impl.DebuggerContextImpl;
|
||||
import com.intellij.debugger.ui.tree.NodeDescriptor;
|
||||
import com.intellij.openapi.extensions.ExtensionPointName;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -41,10 +42,15 @@ public abstract class SourcePositionProvider {
|
||||
@NotNull DebuggerContextImpl context,
|
||||
boolean nearest
|
||||
) {
|
||||
return StreamEx.of(EP_NAME.getExtensions())
|
||||
.map(provider -> provider.computeSourcePosition(descriptor, project, context, nearest))
|
||||
.nonNull()
|
||||
.findFirst().orElse(null);
|
||||
try {
|
||||
return StreamEx.of(EP_NAME.getExtensions())
|
||||
.map(provider -> provider.computeSourcePosition(descriptor, project, context, nearest))
|
||||
.nonNull()
|
||||
.findFirst().orElse(null);
|
||||
}
|
||||
catch (IndexNotReadyException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -41,7 +41,7 @@ public abstract class DebuggerCommandImpl extends DebuggerTaskImpl {
|
||||
}
|
||||
}
|
||||
|
||||
@Debugger.Insert
|
||||
@Debugger.Insert(group = "com.intellij.debugger.impl.InvokeThread.schedule")
|
||||
public final void run() throws Exception{
|
||||
try {
|
||||
action();
|
||||
|
||||
@@ -322,14 +322,16 @@ public class ClassesFilteredView extends BorderLayoutPanel implements Disposable
|
||||
}
|
||||
|
||||
private void updateClassesAndCounts() {
|
||||
final XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession();
|
||||
if (debugSession != null) {
|
||||
final DebugProcess debugProcess = DebuggerManager.getInstance(myProject)
|
||||
.getDebugProcess(debugSession.getDebugProcess().getProcessHandler());
|
||||
if (debugProcess.isAttached()) {
|
||||
mySingleAlarm.cancelAndRequest();
|
||||
ApplicationManager.getApplication().invokeLater(() -> {
|
||||
final XDebugSession debugSession = XDebuggerManager.getInstance(myProject).getCurrentSession();
|
||||
if (debugSession != null) {
|
||||
final DebugProcess debugProcess = DebuggerManager.getInstance(myProject)
|
||||
.getDebugProcess(debugSession.getDebugProcess().getProcessHandler());
|
||||
if (debugProcess != null && debugProcess.isAttached()) {
|
||||
mySingleAlarm.cancelAndRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, x -> myProject.isDisposed());
|
||||
}
|
||||
|
||||
private static ActionPopupMenu createContextMenu() {
|
||||
|
||||
@@ -24,7 +24,6 @@ import com.intellij.debugger.settings.CaptureConfigurable;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.debugger.settings.NodeRendererSettings;
|
||||
import com.intellij.debugger.ui.breakpoints.StackCapturingLineBreakpoint;
|
||||
import com.intellij.debugger.ui.impl.watch.MessageDescriptor;
|
||||
import com.intellij.debugger.ui.tree.render.ClassRenderer;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
@@ -61,9 +60,14 @@ public class StackFrameItem {
|
||||
private static final List<XNamedValue> VARS_NOT_CAPTURED = Collections.singletonList(
|
||||
JavaStackFrame.createMessageNode(DebuggerBundle.message("message.node.local.variables.not.captured"),
|
||||
XDebuggerUIConstants.INFORMATION_MESSAGE_ICON));
|
||||
private static final XNamedValue VAR_NO_DEBUG_INFO =
|
||||
JavaStackFrame.createMessageNode(MessageDescriptor.LOCAL_VARIABLES_INFO_UNAVAILABLE.getLabel(),
|
||||
XDebuggerUIConstants.INFORMATION_MESSAGE_ICON);
|
||||
|
||||
public static final XDebuggerTreeNodeHyperlink CAPTURE_SETTINGS_OPENER = new XDebuggerTreeNodeHyperlink(" settings") {
|
||||
@Override
|
||||
public void onClick(MouseEvent event) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(null, CaptureConfigurable.class);
|
||||
event.consume();
|
||||
}
|
||||
};
|
||||
|
||||
private final Location myLocation;
|
||||
private final List<XNamedValue> myVariables;
|
||||
@@ -125,7 +129,7 @@ public class StackFrameItem {
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
if (e.getCause() instanceof AbsentInformationException) {
|
||||
vars.add(VAR_NO_DEBUG_INFO);
|
||||
vars.add(JavaStackFrame.LOCAL_VARIABLES_INFO_UNAVAILABLE_MESSAGE_NODE);
|
||||
// only args for frames w/o debug info for now
|
||||
try {
|
||||
for (Map.Entry<DecompiledLocalVariable, Value> entry : LocalVariablesUtil
|
||||
@@ -176,6 +180,11 @@ public class StackFrameItem {
|
||||
return new VariableItem(name, type, valueText, varType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return myLocation.toString();
|
||||
}
|
||||
|
||||
private static class VariableItem extends XNamedValue {
|
||||
enum VarType {PARAM, OBJECT}
|
||||
|
||||
@@ -263,14 +272,6 @@ public class StackFrameItem {
|
||||
}
|
||||
}
|
||||
|
||||
private static final XDebuggerTreeNodeHyperlink CAPTURE_SETTINGS_OPENER = new XDebuggerTreeNodeHyperlink(" settings") {
|
||||
@Override
|
||||
public void onClick(MouseEvent event) {
|
||||
ShowSettingsUtil.getInstance().showSettingsDialog(null, CaptureConfigurable.class);
|
||||
event.consume();
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
public void computeChildren(@NotNull XCompositeNode node) {
|
||||
if (myVariables == VARS_CAPTURE_DISABLED) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import com.intellij.debugger.jdi.DecompiledLocalVariable;
|
||||
import com.intellij.debugger.ui.JavaDebuggerSupport;
|
||||
import com.intellij.icons.AllIcons;
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent;
|
||||
import com.intellij.openapi.actionSystem.CustomShortcutSet;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.fileChooser.FileChooser;
|
||||
import com.intellij.openapi.fileChooser.FileChooserDescriptor;
|
||||
@@ -30,6 +31,8 @@ import com.intellij.openapi.fileTypes.FileTypes;
|
||||
import com.intellij.openapi.fileTypes.StdFileTypes;
|
||||
import com.intellij.openapi.options.ConfigurationException;
|
||||
import com.intellij.openapi.options.SearchableConfigurable;
|
||||
import com.intellij.openapi.project.DumbAwareAction;
|
||||
import com.intellij.openapi.project.IndexNotReadyException;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.ui.Messages;
|
||||
import com.intellij.openapi.util.JDOMUtil;
|
||||
@@ -42,6 +45,7 @@ import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.search.searches.AnnotatedElementsSearch;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.table.JBTable;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.PlatformIcons;
|
||||
import com.intellij.util.ui.ItemRemovable;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
@@ -59,6 +63,8 @@ import org.jetbrains.annotations.Nullable;
|
||||
import javax.swing.*;
|
||||
import javax.swing.table.AbstractTableModel;
|
||||
import javax.swing.table.TableColumnModel;
|
||||
import java.awt.event.KeyEvent;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
@@ -123,8 +129,8 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
selectedCapturePoints(table).forEach(c -> {
|
||||
try {
|
||||
myTableModel.add(c.clone());
|
||||
table.getSelectionModel().setSelectionInterval(table.getRowCount() - 1, table.getRowCount() - 1);
|
||||
int idx = myTableModel.add(c.clone());
|
||||
table.getSelectionModel().setSelectionInterval(idx, idx);
|
||||
}
|
||||
catch (CloneNotSupportedException ex) {
|
||||
LOG.error(ex);
|
||||
@@ -133,10 +139,48 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
}
|
||||
});
|
||||
|
||||
decorator.addExtraAction(new DumbAwareActionButton("Enable Selected", "Enable Selected", PlatformIcons.SELECT_ALL_ICON) {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return table.getSelectedRowCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
selectedCapturePoints(table).forEach(c -> c.myEnabled = true);
|
||||
table.repaint();
|
||||
}
|
||||
});
|
||||
decorator.addExtraAction(new DumbAwareActionButton("Disable Selected", "Disable Selected", PlatformIcons.UNSELECT_ALL_ICON) {
|
||||
@Override
|
||||
public boolean isEnabled() {
|
||||
return table.getSelectedRowCount() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull AnActionEvent e) {
|
||||
selectedCapturePoints(table).forEach(c -> c.myEnabled = false);
|
||||
table.repaint();
|
||||
}
|
||||
});
|
||||
|
||||
new DumbAwareAction("Toggle") {
|
||||
@Override
|
||||
public void update(@NotNull AnActionEvent e) {
|
||||
e.getPresentation().setEnabled(table.getSelectedRowCount() == 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
selectedCapturePoints(table).forEach(c -> c.myEnabled = !c.myEnabled);
|
||||
table.repaint();
|
||||
}
|
||||
}.registerCustomShortcutSet(new CustomShortcutSet(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0)), table);
|
||||
|
||||
decorator.addExtraAction(new DumbAwareActionButton("Import", "Import", AllIcons.Actions.Install) {
|
||||
@Override
|
||||
public void actionPerformed(@NotNull final AnActionEvent e) {
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, true, false, true, false) {
|
||||
FileChooserDescriptor descriptor = new FileChooserDescriptor(true, false, true, false, true, true) {
|
||||
@Override
|
||||
public boolean isFileVisible(VirtualFile file, boolean showHiddenFiles) {
|
||||
return super.isFileVisible(file, showHiddenFiles) &&
|
||||
@@ -151,19 +195,24 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
descriptor.setDescription("Please select a file to import.");
|
||||
descriptor.setTitle("Import Capture Points");
|
||||
|
||||
VirtualFile file = FileChooser.chooseFile(descriptor, e.getProject(), null);
|
||||
if (file == null) return;
|
||||
try {
|
||||
Document document = JDOMUtil.loadDocument(file.getInputStream());
|
||||
table.getSelectionModel().clearSelection();
|
||||
int start = table.getRowCount();
|
||||
List<Element> children = document.getRootElement().getChildren();
|
||||
children.forEach(element -> myTableModel.add(XmlSerializer.deserialize(element, CapturePoint.class)));
|
||||
table.getSelectionModel().addSelectionInterval(start, table.getRowCount() - 1);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
final String msg = ex.getLocalizedMessage();
|
||||
Messages.showErrorDialog(e.getProject(), msg != null && msg.length() > 0 ? msg : ex.toString(), "Export Failed");
|
||||
VirtualFile[] files = FileChooser.chooseFiles(descriptor, e.getProject(), null);
|
||||
if (ArrayUtil.isEmpty(files)) return;
|
||||
|
||||
table.getSelectionModel().clearSelection();
|
||||
|
||||
for (VirtualFile file : files) {
|
||||
try {
|
||||
Document document = JDOMUtil.loadDocument(file.getInputStream());
|
||||
List<Element> children = document.getRootElement().getChildren();
|
||||
children.forEach(element -> {
|
||||
int idx = myTableModel.addIfNeeded(XmlSerializer.deserialize(element, CapturePoint.class));
|
||||
table.getSelectionModel().addSelectionInterval(idx, idx);
|
||||
});
|
||||
}
|
||||
catch (Exception ex) {
|
||||
final String msg = ex.getLocalizedMessage();
|
||||
Messages.showErrorDialog(e.getProject(), msg != null && msg.length() > 0 ? msg : ex.toString(), "Export Failed");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -233,12 +282,15 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
|
||||
private void scanPoints() {
|
||||
if (Registry.is("debugger.capture.points.annotations")) {
|
||||
scanPointsInt(true);
|
||||
scanPointsInt(false);
|
||||
List<CapturePoint> capturePointsFromAnnotations = new ArrayList<>();
|
||||
scanPointsInt(true, capturePointsFromAnnotations);
|
||||
scanPointsInt(false, capturePointsFromAnnotations);
|
||||
|
||||
capturePointsFromAnnotations.forEach(this::addIfNeeded);
|
||||
}
|
||||
}
|
||||
|
||||
private void scanPointsInt(boolean capture) {
|
||||
private static void scanPointsInt(boolean capture, List<CapturePoint> capturePointsFromAnnotations) {
|
||||
try {
|
||||
String annotationName = (capture ? Debugger.Capture.class : Debugger.Insert.class).getName().replace("$", ".");
|
||||
Project project = JavaDebuggerSupport.getContextProjectForEditorFieldsInDebuggerConfigurables();
|
||||
@@ -247,28 +299,31 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
if (annotationClass != null) {
|
||||
AnnotatedElementsSearch.searchElements(annotationClass, allScope, PsiMethod.class, PsiParameter.class).forEach(e -> {
|
||||
if (e instanceof PsiMethod) {
|
||||
addCapturePointIfNeeded(e, (PsiMethod)e, annotationName, "this", capture);
|
||||
addCapturePointIfNeeded(e, (PsiMethod)e, annotationName, "this", capture, capturePointsFromAnnotations);
|
||||
}
|
||||
else if (e instanceof PsiParameter) {
|
||||
PsiParameter psiParameter = (PsiParameter)e;
|
||||
PsiMethod psiMethod = (PsiMethod)psiParameter.getDeclarationScope();
|
||||
addCapturePointIfNeeded(psiParameter, psiMethod, annotationName,
|
||||
DecompiledLocalVariable.PARAM_PREFIX + psiMethod.getParameterList().getParameterIndex(psiParameter),
|
||||
capture);
|
||||
capture, capturePointsFromAnnotations);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
catch (IndexNotReadyException ignore) {
|
||||
}
|
||||
catch (Exception e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
}
|
||||
|
||||
private void addCapturePointIfNeeded(PsiModifierListOwner psiElement,
|
||||
PsiMethod psiMethod,
|
||||
String annotationName,
|
||||
String defaultExpression,
|
||||
boolean capture) {
|
||||
private static void addCapturePointIfNeeded(PsiModifierListOwner psiElement,
|
||||
PsiMethod psiMethod,
|
||||
String annotationName,
|
||||
String defaultExpression,
|
||||
boolean capture,
|
||||
List<CapturePoint> capturePointsFromAnnotations) {
|
||||
CapturePoint capturePoint = new CapturePoint();
|
||||
capturePoint.myEnabled = false;
|
||||
if (capture) {
|
||||
@@ -284,8 +339,8 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
if (modifierList != null) {
|
||||
PsiAnnotation annotation = modifierList.findAnnotation(annotationName);
|
||||
if (annotation != null) {
|
||||
PsiAnnotationMemberValue attributeValue = annotation.findAttributeValue("keyExpression");
|
||||
String keyExpression = attributeValue != null ? StringUtil.unquoteString(attributeValue.getText()) : null;
|
||||
PsiAnnotationMemberValue keyExpressionValue = annotation.findAttributeValue("keyExpression");
|
||||
String keyExpression = keyExpressionValue != null ? StringUtil.unquoteString(keyExpressionValue.getText()) : null;
|
||||
if (StringUtil.isEmpty(keyExpression)) {
|
||||
keyExpression = defaultExpression;
|
||||
}
|
||||
@@ -295,20 +350,24 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
else {
|
||||
capturePoint.myInsertKeyExpression = keyExpression;
|
||||
}
|
||||
|
||||
PsiAnnotationMemberValue groupValue = annotation.findAttributeValue("group");
|
||||
String group = groupValue != null ? StringUtil.unquoteString(groupValue.getText()) : null;
|
||||
if (!StringUtil.isEmpty(group)) {
|
||||
for (CapturePoint capturePointsFromAnnotation : capturePointsFromAnnotations) {
|
||||
if (StringUtil.startsWith(group, capturePointsFromAnnotation.myClassName) &&
|
||||
StringUtil.endsWith(group, capturePointsFromAnnotation.myMethodName)) {
|
||||
capturePointsFromAnnotation.myInsertClassName = capturePoint.myInsertClassName;
|
||||
capturePointsFromAnnotation.myInsertMethodName = capturePoint.myInsertMethodName;
|
||||
capturePointsFromAnnotation.myInsertKeyExpression = capturePoint.myInsertKeyExpression;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CapturePoint clone = capturePoint;
|
||||
try {
|
||||
clone = capturePoint.clone();
|
||||
clone.myEnabled = !clone.myEnabled;
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
if (!myCapturePoints.contains(capturePoint) && !myCapturePoints.contains(clone)) {
|
||||
myCapturePoints.add(capturePoint);
|
||||
}
|
||||
capturePointsFromAnnotations.add(capturePoint);
|
||||
}
|
||||
|
||||
public String getColumnName(int column) {
|
||||
@@ -388,10 +447,30 @@ public class CaptureConfigurable implements SearchableConfigurable {
|
||||
return myCapturePoints.get(idx);
|
||||
}
|
||||
|
||||
public void add(CapturePoint p) {
|
||||
int add(CapturePoint p) {
|
||||
myCapturePoints.add(p);
|
||||
int lastRow = getRowCount() - 1;
|
||||
fireTableRowsInserted(lastRow, lastRow);
|
||||
return lastRow;
|
||||
}
|
||||
|
||||
int addIfNeeded(CapturePoint p) {
|
||||
CapturePoint clone = p;
|
||||
try {
|
||||
clone = p.clone();
|
||||
clone.myEnabled = !clone.myEnabled;
|
||||
}
|
||||
catch (CloneNotSupportedException e) {
|
||||
LOG.error(e);
|
||||
}
|
||||
int idx = myCapturePoints.indexOf(p);
|
||||
if (idx < 0) {
|
||||
idx = myCapturePoints.indexOf(clone);
|
||||
}
|
||||
if (idx < 0) {
|
||||
idx = add(p);
|
||||
}
|
||||
return idx;
|
||||
}
|
||||
|
||||
public void addRow() {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -24,6 +24,7 @@ import com.intellij.debugger.impl.DebuggerSession;
|
||||
import com.intellij.debugger.impl.HotSwapFile;
|
||||
import com.intellij.debugger.impl.HotSwapManager;
|
||||
import com.intellij.debugger.settings.DebuggerSettings;
|
||||
import com.intellij.internal.statistic.UsageTrigger;
|
||||
import com.intellij.notification.NotificationType;
|
||||
import com.intellij.openapi.application.Application;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
@@ -257,6 +258,7 @@ public class HotSwapUIImpl extends HotSwapUI implements ProjectComponent {
|
||||
}
|
||||
|
||||
private static void reloadModifiedClasses(final Map<DebuggerSession, Map<String, HotSwapFile>> modifiedClasses, final HotSwapProgressImpl progress) {
|
||||
UsageTrigger.trigger("debugger.reload.classes");
|
||||
ProgressManager.getInstance().runProcess(() -> {
|
||||
HotSwapManager.reloadModifiedClasses(modifiedClasses, progress);
|
||||
progress.finished();
|
||||
|
||||
+48
-31
@@ -196,42 +196,59 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<StackFrameItem> getRelatedStack(@Nullable StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) {
|
||||
if (frame != null) {
|
||||
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
Map<Object, List<StackFrameItem>> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS);
|
||||
if (ContainerUtil.isEmpty(capturedStacks)) {
|
||||
return null;
|
||||
}
|
||||
List<StackCapturingLineBreakpoint> captureBreakpoints = debugProcess.getUserData(CAPTURE_BREAKPOINTS);
|
||||
if (ContainerUtil.isEmpty(captureBreakpoints)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Location location = frame.location();
|
||||
String className = location.declaringType().name();
|
||||
String methodName = location.method().name();
|
||||
public static CapturePoint getMatchingDisabledInsertionPoint(@NotNull StackFrameProxyImpl frame) {
|
||||
try {
|
||||
Location location = frame.location();
|
||||
String className = location.declaringType().name();
|
||||
String methodName = location.method().name();
|
||||
|
||||
for (StackCapturingLineBreakpoint b : captureBreakpoints) {
|
||||
String insertClassName = b.myCapturePoint.myInsertClassName;
|
||||
if ((StringUtil.isEmpty(insertClassName) || StringUtil.equals(insertClassName, className)) &&
|
||||
StringUtil.equals(b.myCapturePoint.myInsertMethodName, methodName)) {
|
||||
try {
|
||||
Value key = b.myInsertEvaluator.evaluate(new EvaluationContextImpl(suspendContext, frame));
|
||||
if (key instanceof ObjectReference) {
|
||||
return capturedStacks.get(getKey((ObjectReference)key));
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.debug(e);
|
||||
debugProcess.printToConsole(DebuggerBundle.message("error.unable.to.evaluate.insert.expression", e.getMessage()) + "\n");
|
||||
for (CapturePoint c : DebuggerSettings.getInstance().getCapturePoints()) {
|
||||
if (!c.myEnabled && StringUtil.equals(c.myInsertClassName, className) && StringUtil.equals(c.myInsertMethodName, methodName)) {
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static List<StackFrameItem> getRelatedStack(@NotNull StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) {
|
||||
DebugProcessImpl debugProcess = suspendContext.getDebugProcess();
|
||||
Map<Object, List<StackFrameItem>> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS);
|
||||
if (ContainerUtil.isEmpty(capturedStacks)) {
|
||||
return null;
|
||||
}
|
||||
List<StackCapturingLineBreakpoint> captureBreakpoints = debugProcess.getUserData(CAPTURE_BREAKPOINTS);
|
||||
if (ContainerUtil.isEmpty(captureBreakpoints)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
Location location = frame.location();
|
||||
String className = location.declaringType().name();
|
||||
String methodName = location.method().name();
|
||||
|
||||
for (StackCapturingLineBreakpoint b : captureBreakpoints) {
|
||||
String insertClassName = b.myCapturePoint.myInsertClassName;
|
||||
if ((StringUtil.isEmpty(insertClassName) || StringUtil.equals(insertClassName, className)) &&
|
||||
StringUtil.equals(b.myCapturePoint.myInsertMethodName, methodName)) {
|
||||
try {
|
||||
Value key = b.myInsertEvaluator.evaluate(new EvaluationContextImpl(suspendContext, frame));
|
||||
if (key instanceof ObjectReference) {
|
||||
return capturedStacks.get(getKey((ObjectReference)key));
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.debug(e);
|
||||
debugProcess.printToConsole(DebuggerBundle.message("error.unable.to.evaluate.insert.expression", e.getMessage()) + "\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
}
|
||||
catch (EvaluateException e) {
|
||||
LOG.debug(e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -35,29 +35,34 @@ public class BinaryRenderer extends NodeRendererImpl {
|
||||
public String calcLabel(ValueDescriptor valueDescriptor, EvaluationContext evaluationContext, DescriptorLabelListener listener)
|
||||
throws EvaluateException {
|
||||
Value value = valueDescriptor.getValue();
|
||||
StringBuilder buf = new StringBuilder();
|
||||
|
||||
if (value == null) {
|
||||
return "null";
|
||||
}
|
||||
else {
|
||||
String prefix = "0b";
|
||||
buf.append(prefix);
|
||||
if (value instanceof LongValue) {
|
||||
buf.append(Long.toBinaryString(((LongValue)value).longValue()));
|
||||
}
|
||||
else if (value instanceof PrimitiveValue) {
|
||||
buf.append(Integer.toBinaryString(((PrimitiveValue)value).intValue()));
|
||||
}
|
||||
else {
|
||||
LOG.assertTrue(false);
|
||||
}
|
||||
// group by 8
|
||||
for (int i = buf.length() - 8; i > prefix.length(); i -= 8) {
|
||||
buf.insert(i, '_');
|
||||
}
|
||||
return buf.toString();
|
||||
|
||||
StringBuilder buf = new StringBuilder("0b");
|
||||
int prefixLength = buf.length();
|
||||
if (value instanceof ByteValue) {
|
||||
buf.append(Integer.toBinaryString(0xff & ((ByteValue)value).byteValue()));
|
||||
}
|
||||
else if (value instanceof ShortValue) {
|
||||
buf.append(Integer.toBinaryString(0xffff & ((ShortValue)value).shortValue()));
|
||||
}
|
||||
else if (value instanceof IntegerValue) {
|
||||
buf.append(Integer.toBinaryString(((PrimitiveValue)value).intValue()));
|
||||
}
|
||||
else if (value instanceof LongValue) {
|
||||
buf.append(Long.toBinaryString(((LongValue)value).longValue()));
|
||||
}
|
||||
else {
|
||||
LOG.error("Unsupported value " + value);
|
||||
}
|
||||
|
||||
// group by 8
|
||||
for (int i = buf.length() - 8; i > prefixLength; i -= 8) {
|
||||
buf.insert(i, '_');
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -42,7 +42,6 @@ import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.StringBuilderSpinAllocator;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.sun.jdi.*;
|
||||
import org.jdom.Element;
|
||||
@@ -201,17 +200,9 @@ public abstract class DebuggerUtils {
|
||||
|
||||
public static String translateStringValue(final String str) {
|
||||
int length = str.length();
|
||||
final StringBuilder buffer = StringBuilderSpinAllocator.alloc();
|
||||
try {
|
||||
StringUtil.escapeStringCharacters(length, str, buffer);
|
||||
if (str.length() > length) {
|
||||
buffer.append("...");
|
||||
}
|
||||
return buffer.toString();
|
||||
}
|
||||
finally {
|
||||
StringBuilderSpinAllocator.dispose(buffer);
|
||||
}
|
||||
final StringBuilder buffer = new StringBuilder();
|
||||
StringUtil.escapeStringCharacters(length, str, buffer);
|
||||
return buffer.toString();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
+3
-2
@@ -112,7 +112,8 @@ public abstract class AbstractInClassConfigurationProducer<T extends JavaTestCon
|
||||
break;
|
||||
}
|
||||
else if (element instanceof PsiMember) {
|
||||
psiClass = ((PsiMember)element).getContainingClass();
|
||||
psiClass = contextLocation instanceof MethodLocation ? ((MethodLocation)contextLocation).getContainingClass()
|
||||
: ((PsiMember)element).getContainingClass();
|
||||
if (isTestClass(psiClass)) {
|
||||
break;
|
||||
}
|
||||
@@ -138,7 +139,7 @@ public abstract class AbstractInClassConfigurationProducer<T extends JavaTestCon
|
||||
PsiMethod method = PsiTreeUtil.getParentOfType(context.getPsiLocation(), PsiMethod.class, false);
|
||||
while (method != null) {
|
||||
if (isTestMethod(false, method)) {
|
||||
configuration.beMethodConfiguration(PsiLocation.fromPsiElement(project, method));
|
||||
configuration.beMethodConfiguration(MethodLocation.elementInClass(method, psiClass));
|
||||
psiElement = method;
|
||||
}
|
||||
method = PsiTreeUtil.getParentOfType(method, PsiMethod.class);
|
||||
|
||||
@@ -23,7 +23,6 @@ import com.intellij.openapi.components.ServiceManager;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
@@ -33,7 +32,6 @@ import java.util.Set;
|
||||
public class ProjectCategoryUsagesCollector extends UsagesCollector {
|
||||
|
||||
private final FactoryMap<String, UsageDescriptor> myUsageDescriptors = new FactoryMap<String, UsageDescriptor>() {
|
||||
@Nullable
|
||||
@Override
|
||||
protected UsageDescriptor create(String key) {
|
||||
return new UsageDescriptor(key, 0);
|
||||
|
||||
@@ -168,7 +168,7 @@ public class JdkComboBox extends ComboBoxWithWidePopup<JdkComboBox.JdkComboBoxIt
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
DefaultActionGroup group = new DefaultActionGroup();
|
||||
jdksModel.createAddActions(group, JdkComboBox.this, jdk -> {
|
||||
jdksModel.createAddActions(group, JdkComboBox.this, getSelectedJdk(), jdk -> {
|
||||
if (project != null) {
|
||||
final JdkListConfigurable configurable = JdkListConfigurable.getInstance(project);
|
||||
configurable.addJdkNode(jdk, false);
|
||||
|
||||
+7
-11
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -25,10 +25,7 @@ import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.progress.ProgressIndicator
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.pom.java.LanguageLevel
|
||||
import com.intellij.psi.JavaRecursiveElementVisitor
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.psi.PsiJavaFile
|
||||
import com.intellij.psi.PsiKeyword
|
||||
import com.intellij.psi.*
|
||||
|
||||
class JavaSoftKeywordHighlightingPassFactory(project: Project, registrar: TextEditorHighlightingPassRegistrar) :
|
||||
AbstractProjectComponent(project), TextEditorHighlightingPassFactory {
|
||||
@@ -37,9 +34,10 @@ class JavaSoftKeywordHighlightingPassFactory(project: Project, registrar: TextEd
|
||||
registrar.registerTextEditorHighlightingPass(this, null, null, false, -1)
|
||||
}
|
||||
|
||||
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? =
|
||||
if (file is PsiJavaFile && file.languageLevel.isAtLeast(LanguageLevel.JDK_1_9)) JavaSoftKeywordHighlightingPass(file, editor.document)
|
||||
else null
|
||||
override fun createHighlightingPass(file: PsiFile, editor: Editor): TextEditorHighlightingPass? {
|
||||
val visit = file is PsiJavaFile && file.name == PsiJavaModule.MODULE_INFO_FILE && file.languageLevel.isAtLeast(LanguageLevel.JDK_1_9)
|
||||
return if (visit) JavaSoftKeywordHighlightingPass(file as PsiJavaFile, editor.document) else null
|
||||
}
|
||||
}
|
||||
|
||||
private class JavaSoftKeywordHighlightingPass(private val file: PsiJavaFile, document: Document) :
|
||||
@@ -52,9 +50,7 @@ private class JavaSoftKeywordHighlightingPass(private val file: PsiJavaFile, doc
|
||||
}
|
||||
|
||||
override fun doApplyInformationToEditor() {
|
||||
if (results.isNotEmpty()) {
|
||||
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument!!, 0, file.textLength, results, colorsScheme, id)
|
||||
}
|
||||
UpdateHighlightersUtil.setHighlightersToEditor(myProject, myDocument!!, 0, file.textLength, results, colorsScheme, id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -43,8 +43,8 @@ public class SuppressAllForClassFix extends SuppressFix {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiDocCommentOwner getContainer(final PsiElement element) {
|
||||
PsiDocCommentOwner container = super.getContainer(element);
|
||||
public PsiJavaDocumentedElement getContainer(final PsiElement element) {
|
||||
PsiJavaDocumentedElement container = super.getContainer(element);
|
||||
if (container == null) {
|
||||
return null;
|
||||
}
|
||||
@@ -55,7 +55,7 @@ public class SuppressAllForClassFix extends SuppressFix {
|
||||
}
|
||||
container = parentClass;
|
||||
}
|
||||
return container;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,7 +72,7 @@ public class SuppressAllForClassFix extends SuppressFix {
|
||||
|
||||
@Override
|
||||
public void invoke(@NotNull final Project project, @NotNull final PsiElement element) throws IncorrectOperationException {
|
||||
final PsiDocCommentOwner container = getContainer(element);
|
||||
final PsiJavaDocumentedElement container = getContainer(element);
|
||||
LOG.assertTrue(container != null);
|
||||
if (use15Suppressions(container)) {
|
||||
final PsiModifierList modifierList = container.getModifierList();
|
||||
|
||||
+21
-12
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2013 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -59,7 +59,7 @@ public class SuppressFix extends AbstractBatchSuppressByNoInspectionCommentFix {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiDocCommentOwner getContainer(final PsiElement context) {
|
||||
public PsiJavaDocumentedElement getContainer(final PsiElement context) {
|
||||
if (context == null || !context.getManager().isInProject(context)) {
|
||||
return null;
|
||||
}
|
||||
@@ -72,23 +72,32 @@ public class SuppressFix extends AbstractBatchSuppressByNoInspectionCommentFix {
|
||||
return null;
|
||||
}
|
||||
PsiElement container = context;
|
||||
while (container instanceof PsiAnonymousClass || !(container instanceof PsiDocCommentOwner) || container instanceof PsiTypeParameter) {
|
||||
container = PsiTreeUtil.getParentOfType(container, PsiDocCommentOwner.class);
|
||||
while (container instanceof PsiAnonymousClass || !(container instanceof PsiJavaDocumentedElement) || container instanceof PsiTypeParameter) {
|
||||
container = PsiTreeUtil.getParentOfType(container, PsiJavaDocumentedElement.class);
|
||||
if (container == null) return null;
|
||||
}
|
||||
return (PsiDocCommentOwner)container;
|
||||
return (PsiJavaDocumentedElement)container;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAvailable(@NotNull final Project project, @NotNull final PsiElement context) {
|
||||
PsiDocCommentOwner container = getContainer(context);
|
||||
PsiJavaDocumentedElement container = getContainer(context);
|
||||
boolean isValid = container != null && !(container instanceof PsiMethod && container instanceof SyntheticElement);
|
||||
if (!isValid) {
|
||||
return false;
|
||||
}
|
||||
setText(container instanceof PsiClass
|
||||
? InspectionsBundle.message("suppress.inspection.class")
|
||||
: container instanceof PsiMethod ? InspectionsBundle.message("suppress.inspection.method") : InspectionsBundle.message("suppress.inspection.field"));
|
||||
if (container instanceof PsiJavaModule) {
|
||||
setText(InspectionsBundle.message("suppress.inspection.module"));
|
||||
}
|
||||
else if (container instanceof PsiClass) {
|
||||
setText(InspectionsBundle.message("suppress.inspection.class"));
|
||||
}
|
||||
else if (container instanceof PsiMethod) {
|
||||
setText(InspectionsBundle.message("suppress.inspection.method"));
|
||||
}
|
||||
else {
|
||||
setText(InspectionsBundle.message("suppress.inspection.field"));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -111,7 +120,7 @@ public class SuppressFix extends AbstractBatchSuppressByNoInspectionCommentFix {
|
||||
return InspectionsBundle.message("suppress.inspection.member");
|
||||
}
|
||||
|
||||
private boolean doSuppress(@NotNull Project project, PsiDocCommentOwner container) {
|
||||
private boolean doSuppress(@NotNull Project project, PsiJavaDocumentedElement container) {
|
||||
assert container != null;
|
||||
if (use15Suppressions(container)) {
|
||||
final PsiModifierList modifierList = container.getModifierList();
|
||||
@@ -125,7 +134,7 @@ public class SuppressFix extends AbstractBatchSuppressByNoInspectionCommentFix {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void suppressByDocComment(@NotNull Project project, PsiDocCommentOwner container) {
|
||||
private void suppressByDocComment(@NotNull Project project, PsiJavaDocumentedElement container) {
|
||||
PsiDocComment docComment = container.getDocComment();
|
||||
PsiManager manager = PsiManager.getInstance(project);
|
||||
if (docComment == null) {
|
||||
@@ -147,7 +156,7 @@ public class SuppressFix extends AbstractBatchSuppressByNoInspectionCommentFix {
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean use15Suppressions(@NotNull PsiDocCommentOwner container) {
|
||||
protected boolean use15Suppressions(@NotNull PsiJavaDocumentedElement container) {
|
||||
return JavaSuppressionUtil.canHave15Suppressions(container) &&
|
||||
!JavaSuppressionUtil.alreadyHas14Suppressions(container);
|
||||
}
|
||||
|
||||
+4
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -17,10 +17,7 @@ package com.intellij.codeInsight.daemon.impl.actions;
|
||||
|
||||
import com.intellij.codeInsight.daemon.HighlightDisplayKey;
|
||||
import com.intellij.codeInspection.InspectionsBundle;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiDeclarationStatement;
|
||||
import com.intellij.psi.PsiDocCommentOwner;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -40,8 +37,8 @@ public class SuppressForClassFix extends SuppressFix {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public PsiDocCommentOwner getContainer(final PsiElement element) {
|
||||
PsiDocCommentOwner container = super.getContainer(element);
|
||||
public PsiJavaDocumentedElement getContainer(final PsiElement element) {
|
||||
PsiJavaDocumentedElement container = super.getContainer(element);
|
||||
if (container == null || container instanceof PsiClass){
|
||||
return null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -784,7 +784,7 @@ public class HighlightClassUtil {
|
||||
if (!PsiUtil.isInnerClass(base)) return;
|
||||
|
||||
if (resolve == resolved && baseClass != null && (!PsiTreeUtil.isAncestor(baseClass, extendRef, true) || aClass.hasModifierProperty(PsiModifier.STATIC)) &&
|
||||
!InheritanceUtil.hasEnclosingInstanceInScope(baseClass, extendRef, PsiUtil.isInnerClass(aClass) && !aClass.hasModifierProperty(PsiModifier.STATIC), true) &&
|
||||
!InheritanceUtil.hasEnclosingInstanceInScope(baseClass, extendRef, psiClass -> psiClass != aClass, true) &&
|
||||
!qualifiedNewCalledInConstructors(aClass)) {
|
||||
String description = JavaErrorMessages.message("no.enclosing.instance.in.scope", HighlightUtil.formatClass(baseClass));
|
||||
infos[0] = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(extendRef).descriptionAndTooltip(description).create();
|
||||
|
||||
+54
-54
@@ -63,23 +63,19 @@ import com.intellij.refactoring.util.RefactoringChangeUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ArrayUtilRt;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.hash.HashSet;
|
||||
import com.intellij.util.ui.UIUtil;
|
||||
import com.intellij.xml.util.XmlStringUtil;
|
||||
import gnu.trove.THashMap;
|
||||
import org.intellij.lang.annotations.Language;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.annotations.PropertyKey;
|
||||
import org.jetbrains.annotations.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import static com.intellij.util.ObjectUtils.notNull;
|
||||
|
||||
/**
|
||||
* @author cdr
|
||||
* @since Jul 30, 2002
|
||||
@@ -87,18 +83,20 @@ import static com.intellij.util.ObjectUtils.notNull;
|
||||
public class HighlightUtil extends HighlightUtilBase {
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.daemon.impl.analysis.HighlightUtil");
|
||||
|
||||
@NotNull private static final Map<String, Set<String>> ourInterfaceIncompatibleModifiers = new THashMap<>(7);
|
||||
@NotNull private static final Map<String, Set<String>> ourMethodIncompatibleModifiers = new THashMap<>(11);
|
||||
@NotNull private static final Map<String, Set<String>> ourFieldIncompatibleModifiers = new THashMap<>(8);
|
||||
@NotNull private static final Map<String, Set<String>> ourClassIncompatibleModifiers = new THashMap<>(8);
|
||||
@NotNull private static final Map<String, Set<String>> ourClassInitializerIncompatibleModifiers = new THashMap<>(1);
|
||||
@NotNull private static final Set<String> ourConstructorNotAllowedModifiers =
|
||||
ContainerUtil.newTroveSet(PsiModifier.ABSTRACT, PsiModifier.STATIC, PsiModifier.NATIVE, PsiModifier.FINAL, PsiModifier.STRICTFP, PsiModifier.SYNCHRONIZED);
|
||||
|
||||
@NonNls private static final String SERIAL_PERSISTENT_FIELDS_FIELD_NAME = "serialPersistentFields";
|
||||
private static final QuickFixFactory QUICK_FIX_FACTORY = QuickFixFactory.getInstance();
|
||||
|
||||
private HighlightUtil() { }
|
||||
private static final Map<String, Set<String>> ourInterfaceIncompatibleModifiers = new THashMap<>(7);
|
||||
private static final Map<String, Set<String>> ourMethodIncompatibleModifiers = new THashMap<>(11);
|
||||
private static final Map<String, Set<String>> ourFieldIncompatibleModifiers = new THashMap<>(8);
|
||||
private static final Map<String, Set<String>> ourClassIncompatibleModifiers = new THashMap<>(8);
|
||||
private static final Map<String, Set<String>> ourClassInitializerIncompatibleModifiers = new THashMap<>(1);
|
||||
private static final Map<String, Set<String>> ourModuleIncompatibleModifiers = new THashMap<>(1);
|
||||
private static final Map<String, Set<String>> ourRequiresIncompatibleModifiers = new THashMap<>(2);
|
||||
|
||||
private static final Set<String> ourConstructorNotAllowedModifiers =
|
||||
ContainerUtil.newTroveSet(PsiModifier.ABSTRACT, PsiModifier.STATIC, PsiModifier.NATIVE, PsiModifier.FINAL, PsiModifier.STRICTFP, PsiModifier.SYNCHRONIZED);
|
||||
|
||||
private static final String SERIAL_PERSISTENT_FIELDS_FIELD_NAME = "serialPersistentFields";
|
||||
|
||||
static {
|
||||
ourClassIncompatibleModifiers.put(PsiModifier.ABSTRACT, ContainerUtil.newTroveSet(PsiModifier.FINAL));
|
||||
@@ -118,7 +116,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
ourInterfaceIncompatibleModifiers.put(PsiModifier.STRICTFP, Collections.emptySet());
|
||||
ourInterfaceIncompatibleModifiers.put(PsiModifier.STATIC, Collections.emptySet());
|
||||
|
||||
ourMethodIncompatibleModifiers.put(PsiModifier.ABSTRACT, ContainerUtil.newTroveSet(PsiModifier.NATIVE, PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PRIVATE, PsiModifier.STRICTFP, PsiModifier.SYNCHRONIZED, PsiModifier.DEFAULT));
|
||||
ourMethodIncompatibleModifiers.put(PsiModifier.ABSTRACT, ContainerUtil.newTroveSet(
|
||||
PsiModifier.NATIVE, PsiModifier.STATIC, PsiModifier.FINAL, PsiModifier.PRIVATE, PsiModifier.STRICTFP, PsiModifier.SYNCHRONIZED, PsiModifier.DEFAULT));
|
||||
ourMethodIncompatibleModifiers.put(PsiModifier.NATIVE, ContainerUtil.newTroveSet(PsiModifier.ABSTRACT, PsiModifier.STRICTFP));
|
||||
ourMethodIncompatibleModifiers.put(PsiModifier.PACKAGE_LOCAL, ContainerUtil.newTroveSet(PsiModifier.PRIVATE, PsiModifier.PUBLIC, PsiModifier.PROTECTED));
|
||||
ourMethodIncompatibleModifiers.put(PsiModifier.PRIVATE, ContainerUtil.newTroveSet(PsiModifier.PACKAGE_LOCAL, PsiModifier.PUBLIC, PsiModifier.PROTECTED));
|
||||
@@ -140,8 +139,15 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
ourFieldIncompatibleModifiers.put(PsiModifier.VOLATILE, ContainerUtil.newTroveSet(PsiModifier.FINAL));
|
||||
|
||||
ourClassInitializerIncompatibleModifiers.put(PsiModifier.STATIC, Collections.emptySet());
|
||||
|
||||
ourModuleIncompatibleModifiers.put(PsiModifier.OPEN, Collections.emptySet());
|
||||
|
||||
ourRequiresIncompatibleModifiers.put(PsiModifier.STATIC, Collections.emptySet());
|
||||
ourRequiresIncompatibleModifiers.put(PsiModifier.TRANSITIVE, Collections.emptySet());
|
||||
}
|
||||
|
||||
private HighlightUtil() { }
|
||||
|
||||
@Nullable
|
||||
private static String getIncompatibleModifier(String modifier,
|
||||
@Nullable PsiModifierList modifierList,
|
||||
@@ -707,7 +713,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
}
|
||||
if (proc.size() > 0) {
|
||||
isIncorrect = true;
|
||||
} else if (declarationScope instanceof PsiLambdaExpression) {
|
||||
}
|
||||
else if (declarationScope instanceof PsiLambdaExpression) {
|
||||
isIncorrect = checkSameNames(variable);
|
||||
}
|
||||
}
|
||||
@@ -738,13 +745,13 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean checkSameNames(PsiVariable variable) {
|
||||
private static boolean checkSameNames(@NotNull PsiVariable variable) {
|
||||
PsiElement scope = variable.getParent();
|
||||
PsiElement[] children = scope.getChildren();
|
||||
for (PsiElement child : children) {
|
||||
if (child instanceof PsiVariable) {
|
||||
if (child.equals(variable)) continue;
|
||||
if (variable.getName().equals(((PsiVariable)child).getName())) {
|
||||
if (Objects.equals(variable.getName(), ((PsiVariable)child).getName())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -783,7 +790,7 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
static String formatField(@NotNull PsiField field) {
|
||||
private static String formatField(@NotNull PsiField field) {
|
||||
return PsiFormatUtil.formatVariable(field, PsiFormatUtilBase.SHOW_CONTAINING_CLASS | PsiFormatUtilBase.SHOW_NAME, PsiSubstitutor.EMPTY);
|
||||
}
|
||||
|
||||
@@ -889,39 +896,33 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static Map<String, Set<String>> getIncompatibleModifierMap(@NotNull PsiModifierList modifierList) {
|
||||
PsiElement parent = modifierList.getParent();
|
||||
if (parent == null || PsiUtilCore.hasErrorElementChild(parent)) return null;
|
||||
return parent instanceof PsiClass
|
||||
? ((PsiClass)parent).isInterface() ? ourInterfaceIncompatibleModifiers : ourClassIncompatibleModifiers
|
||||
: parent instanceof PsiMethod
|
||||
? ourMethodIncompatibleModifiers
|
||||
: parent instanceof PsiVariable
|
||||
? ourFieldIncompatibleModifiers
|
||||
: parent instanceof PsiClassInitializer ? ourClassInitializerIncompatibleModifiers : null;
|
||||
@Contract("null -> null")
|
||||
private static Map<String, Set<String>> getIncompatibleModifierMap(@Nullable PsiElement modifierListOwner) {
|
||||
if (modifierListOwner == null || PsiUtilCore.hasErrorElementChild(modifierListOwner)) return null;
|
||||
if (modifierListOwner instanceof PsiClass) {
|
||||
return ((PsiClass)modifierListOwner).isInterface() ? ourInterfaceIncompatibleModifiers : ourClassIncompatibleModifiers;
|
||||
}
|
||||
if (modifierListOwner instanceof PsiMethod) return ourMethodIncompatibleModifiers;
|
||||
if (modifierListOwner instanceof PsiVariable) return ourFieldIncompatibleModifiers;
|
||||
if (modifierListOwner instanceof PsiClassInitializer) return ourClassInitializerIncompatibleModifiers;
|
||||
if (modifierListOwner instanceof PsiJavaModule) return ourModuleIncompatibleModifiers;
|
||||
if (modifierListOwner instanceof PsiRequiresStatement) return ourRequiresIncompatibleModifiers;
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static String getIncompatibleModifier(String modifier, @NotNull PsiModifierList modifierList) {
|
||||
PsiElement parent = modifierList.getParent();
|
||||
if (parent == null || PsiUtilCore.hasErrorElementChild(parent)) return null;
|
||||
final Map<String, Set<String>> incompatibleModifierMap = getIncompatibleModifierMap(modifierList);
|
||||
if (incompatibleModifierMap == null) return null;
|
||||
return getIncompatibleModifier(modifier, modifierList, incompatibleModifierMap);
|
||||
Map<String, Set<String>> incompatibleModifierMap = getIncompatibleModifierMap(modifierList.getParent());
|
||||
return incompatibleModifierMap != null ? getIncompatibleModifier(modifier, modifierList, incompatibleModifierMap) : null;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkNotAllowedModifier(@NotNull PsiKeyword keyword, @NotNull PsiModifierList modifierList) {
|
||||
PsiElement modifierOwner = modifierList.getParent();
|
||||
if (modifierOwner == null) return null;
|
||||
if (PsiUtilCore.hasErrorElementChild(modifierOwner)) return null;
|
||||
|
||||
@PsiModifier.ModifierConstant String modifier = keyword.getText();
|
||||
final Map<String, Set<String>> incompatibleModifierMap = getIncompatibleModifierMap(modifierList);
|
||||
Map<String, Set<String>> incompatibleModifierMap = getIncompatibleModifierMap(modifierOwner);
|
||||
if (incompatibleModifierMap == null) return null;
|
||||
|
||||
@PsiModifier.ModifierConstant String modifier = keyword.getText();
|
||||
Set<String> incompatibles = incompatibleModifierMap.get(modifier);
|
||||
PsiElement modifierOwnerParent = modifierOwner instanceof PsiMember ? ((PsiMember)modifierOwner).getContainingClass() : modifierOwner.getParent();
|
||||
if (modifierOwnerParent == null) modifierOwnerParent = modifierOwner.getParent();
|
||||
@@ -1004,12 +1005,11 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
isAllowed &= incompatibles != null;
|
||||
if (!isAllowed) {
|
||||
String message = JavaErrorMessages.message("modifier.not.allowed", modifier);
|
||||
|
||||
HighlightInfo highlightInfo =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(keyword).descriptionAndTooltip(message).create();
|
||||
HighlightInfo highlightInfo = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(keyword).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(highlightInfo, QUICK_FIX_FACTORY.createModifierListFix(modifierList, modifier, false, false));
|
||||
return highlightInfo;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -1208,8 +1208,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
|
||||
if (isInt) {
|
||||
int start = 0;
|
||||
int end = text.length();
|
||||
if (text.startsWith(PsiLiteralExpressionImpl.HEX_PREFIX) || text.startsWith(PsiLiteralExpressionImpl.BIN_PREFIX)) start += 2;
|
||||
int end = text.length();
|
||||
if (StringUtil.endsWithChar(text, 'l')) --end;
|
||||
parts = new String[]{text.substring(start, end)};
|
||||
}
|
||||
@@ -1689,7 +1689,7 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
|
||||
@NotNull
|
||||
static String buildProblemWithAccessDescription(@NotNull final PsiElement reference, @NotNull final JavaResolveResult result) {
|
||||
return buildProblemWithAccessDescription(reference, result, notNull(result.getElement()));
|
||||
return buildProblemWithAccessDescription(reference, result, ObjectUtils.notNull(result.getElement()));
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -2730,8 +2730,8 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
JavaResolveResult[] results = ref.multiResolve(true);
|
||||
String description;
|
||||
if (results.length > 1) {
|
||||
String t1 = format(notNull(results[0].getElement()));
|
||||
String t2 = format(notNull(results[1].getElement()));
|
||||
String t1 = format(ObjectUtils.notNull(results[0].getElement()));
|
||||
String t2 = format(ObjectUtils.notNull(results[1].getElement()));
|
||||
description = JavaErrorMessages.message("ambiguous.reference", refName.getText(), t1, t2);
|
||||
}
|
||||
else {
|
||||
@@ -2772,10 +2772,10 @@ public class HighlightUtil extends HighlightUtilBase {
|
||||
if ((resolved instanceof PsiLocalVariable || resolved instanceof PsiParameter) && !(resolved instanceof ImplicitVariable)) {
|
||||
return HighlightControlFlowUtil.checkVariableMustBeFinal((PsiVariable)resolved, ref, languageLevel);
|
||||
}
|
||||
else if (resolved instanceof PsiClass &&
|
||||
((PsiClass)resolved).getContainingClass() == null &&
|
||||
PsiTreeUtil.getParentOfType(ref, PsiImportStatementBase.class) != null &&
|
||||
PsiUtil.isFromDefaultPackage((PsiClass)resolved)) {
|
||||
if (resolved instanceof PsiClass &&
|
||||
((PsiClass)resolved).getContainingClass() == null &&
|
||||
PsiTreeUtil.getParentOfType(ref, PsiImportStatementBase.class) != null &&
|
||||
PsiUtil.isFromDefaultPackage((PsiClass)resolved)) {
|
||||
String description = JavaErrorMessages.message("cannot.resolve.symbol", refName.getText());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refName).descriptionAndTooltip(description).create();
|
||||
}
|
||||
|
||||
+7
-2
@@ -984,7 +984,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
super.visitPackageStatement(statement);
|
||||
myHolder.add(AnnotationsHighlightUtil.checkPackageAnnotationContainingFile(statement, myFile));
|
||||
if (myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_9)) {
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkPackageStatement(statement, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkPackageStatement(statement, myFile, myJavaModule));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1295,6 +1295,10 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
myHolder.add(GenericsHighlightUtil.areSupersAccessible(psiClass, expression));
|
||||
}
|
||||
}
|
||||
|
||||
if (!myHolder.hasErrorResults() && resolved != null && myJavaModule != null) {
|
||||
myHolder.add(ModuleHighlightUtil.checkPackageAccessibility(expression, resolved, myJavaModule));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1369,7 +1373,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (errorMessage != null) {
|
||||
final HighlightInfo info =
|
||||
HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(expression).descriptionAndTooltip(errorMessage).create();
|
||||
if (method instanceof PsiMethod && !((PsiMethod)method).isConstructor() &&
|
||||
if (method instanceof PsiMethod && !((PsiMethod)method).isConstructor() &&
|
||||
!((PsiMethod)method).hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
final boolean shouldHave = !((PsiMethod)method).hasModifierProperty(PsiModifier.STATIC);
|
||||
final LocalQuickFixAndIntentionActionOnPsiElement fixStaticModifier =
|
||||
@@ -1673,6 +1677,7 @@ public class HighlightVisitorImpl extends JavaElementVisitor implements Highligh
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkFileName(module, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkFileDuplicates(module, myFile));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(ModuleHighlightUtil.checkDuplicateStatements(module));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkClashingReads(module));
|
||||
if (!myHolder.hasErrorResults()) myHolder.addAll(ModuleHighlightUtil.checkUnusedServices(module));
|
||||
if (!myHolder.hasErrorResults()) myHolder.add(ModuleHighlightUtil.checkFileLocation(module, myFile));
|
||||
}
|
||||
|
||||
+77
-9
@@ -18,8 +18,10 @@ package com.intellij.codeInsight.daemon.impl.analysis;
|
||||
import com.intellij.openapi.module.Module;
|
||||
import com.intellij.openapi.module.ModuleManager;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import com.intellij.psi.impl.source.PsiJavaModuleReference;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.util.CachedValueProvider.Result;
|
||||
@@ -29,12 +31,12 @@ import com.intellij.util.containers.MultiMap;
|
||||
import com.intellij.util.graph.DFSTBuilder;
|
||||
import com.intellij.util.graph.Graph;
|
||||
import com.intellij.util.graph.GraphGenerator;
|
||||
import com.intellij.util.graph.OutboundSemiGraph;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.intellij.psi.PsiJavaModule.MODULE_INFO_FILE;
|
||||
@@ -70,10 +72,17 @@ public class JavaModuleGraphUtil {
|
||||
}
|
||||
|
||||
public static boolean reads(@NotNull PsiJavaModule source, @NotNull PsiJavaModule destination) {
|
||||
Project project = source.getProject();
|
||||
RequiresGraph graph = CachedValuesManager.getManager(project).getCachedValue(project, () ->
|
||||
Result.create(buildRequiresGraph(project), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
|
||||
return graph.reads(source, destination);
|
||||
return getRequiresGraph(source).reads(source, destination);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static Trinity<String, PsiJavaModule, PsiJavaModule> findConflict(@NotNull PsiJavaModule module) {
|
||||
return getRequiresGraph(module).findConflict(module);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static PsiJavaModule findOrigin(@NotNull PsiJavaModule module, @NotNull String packageName) {
|
||||
return getRequiresGraph(module).findOrigin(module, packageName);
|
||||
}
|
||||
|
||||
// Looks for cycles between Java modules in the project sources.
|
||||
@@ -124,8 +133,14 @@ public class JavaModuleGraphUtil {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static RequiresGraph getRequiresGraph(PsiJavaModule module) {
|
||||
Project project = module.getProject();
|
||||
return CachedValuesManager.getManager(project).getCachedValue(project, () ->
|
||||
Result.create(buildRequiresGraph(project), OUT_OF_CODE_BLOCK_MODIFICATION_COUNT));
|
||||
}
|
||||
|
||||
// Starting from source modules, collects all module dependencies in the project.
|
||||
// The resulting graph is used for tracing readability.
|
||||
// The resulting graph is used for tracing readability and checking package conflicts.
|
||||
private static RequiresGraph buildRequiresGraph(Project project) {
|
||||
MultiMap<PsiJavaModule, PsiJavaModule> relations = MultiMap.create();
|
||||
Set<String> transitiveEdges = ContainerUtil.newTroveSet();
|
||||
@@ -144,21 +159,28 @@ public class JavaModuleGraphUtil {
|
||||
private static void visit(PsiJavaModule module, MultiMap<PsiJavaModule, PsiJavaModule> relations, Set<String> transitiveEdges) {
|
||||
if (!relations.containsKey(module)) {
|
||||
relations.putValues(module, Collections.emptyList());
|
||||
boolean explicitJavaBase = false;
|
||||
for (PsiRequiresStatement statement : module.getRequires()) {
|
||||
for (PsiJavaModule dependency : PsiJavaModuleReference.multiResolve(statement, statement.getModuleName(), false)) {
|
||||
String moduleName = statement.getModuleName();
|
||||
if (PsiJavaModule.JAVA_BASE.equals(moduleName)) explicitJavaBase = true;
|
||||
for (PsiJavaModule dependency : PsiJavaModuleReference.multiResolve(statement, moduleName, false)) {
|
||||
relations.putValue(module, dependency);
|
||||
if (statement.hasModifierProperty(PsiModifier.TRANSITIVE)) transitiveEdges.add(RequiresGraph.key(dependency, module));
|
||||
visit(dependency, relations, transitiveEdges);
|
||||
}
|
||||
}
|
||||
if (!explicitJavaBase && !(module instanceof LightJavaModule)) {
|
||||
PsiJavaModule javaBase = PsiJavaModuleReference.resolve(module, PsiJavaModule.JAVA_BASE, false);
|
||||
if (javaBase != null) relations.putValue(module, javaBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class RequiresGraph {
|
||||
private final OutboundSemiGraph<PsiJavaModule> myGraph;
|
||||
private final Graph<PsiJavaModule> myGraph;
|
||||
private final Set<String> myTransitiveEdges;
|
||||
|
||||
public RequiresGraph(OutboundSemiGraph<PsiJavaModule> graph, Set<String> transitiveEdges) {
|
||||
public RequiresGraph(Graph<PsiJavaModule> graph, Set<String> transitiveEdges) {
|
||||
myGraph = graph;
|
||||
myTransitiveEdges = transitiveEdges;
|
||||
}
|
||||
@@ -177,6 +199,52 @@ public class JavaModuleGraphUtil {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Trinity<String, PsiJavaModule, PsiJavaModule> findConflict(PsiJavaModule source) {
|
||||
Map<String, PsiJavaModule> exports = ContainerUtil.newHashMap();
|
||||
return processExports(source, (pkg, m) -> {
|
||||
PsiJavaModule existing = exports.put(pkg, m);
|
||||
return existing != null ? new Trinity<>(pkg, existing, m) : null;
|
||||
});
|
||||
}
|
||||
|
||||
public PsiJavaModule findOrigin(PsiJavaModule module, String packageName) {
|
||||
return processExports(module, (pkg, m) -> packageName.equals(pkg) ? m : null);
|
||||
}
|
||||
|
||||
private <T> T processExports(PsiJavaModule start, BiFunction<String, PsiJavaModule, T> processor) {
|
||||
return myGraph.getNodes().contains(start) ? processExports(start.getName(), start, 0, ContainerUtil.newHashSet(), processor) : null;
|
||||
}
|
||||
|
||||
private <T> T processExports(String name,
|
||||
PsiJavaModule module,
|
||||
int layer,
|
||||
Set<PsiJavaModule> visited,
|
||||
BiFunction<String, PsiJavaModule, T> processor) {
|
||||
if (visited.add(module)) {
|
||||
if (layer == 1) {
|
||||
for (PsiPackageAccessibilityStatement statement : module.getExports()) {
|
||||
List<String> exportTargets = statement.getModuleNames();
|
||||
if (exportTargets.isEmpty() || exportTargets.contains(name)) {
|
||||
T result = processor.apply(statement.getPackageName(), module);
|
||||
if (result != null) return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer < 2) {
|
||||
Iterator<PsiJavaModule> iterator = myGraph.getIn(module);
|
||||
while (iterator.hasNext()) {
|
||||
PsiJavaModule dependency = iterator.next();
|
||||
if (layer == 0 || myTransitiveEdges.contains(key(dependency, module))) {
|
||||
T result = processExports(name, dependency, 1, visited, processor);
|
||||
if (result != null) return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static String key(PsiJavaModule module, PsiJavaModule exporter) {
|
||||
return module.getName() + '/' + exporter.getName();
|
||||
}
|
||||
|
||||
+84
-43
@@ -27,6 +27,7 @@ import com.intellij.openapi.module.ModuleUtilCore;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.roots.ProjectFileIndex;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.Trinity;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.JarFileSystem;
|
||||
import com.intellij.openapi.vfs.VirtualFile;
|
||||
@@ -34,6 +35,7 @@ import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiPackageAccessibilityStatement.Role;
|
||||
import com.intellij.psi.impl.light.LightJavaModule;
|
||||
import com.intellij.psi.search.FilenameIndex;
|
||||
import com.intellij.psi.util.ClassUtil;
|
||||
import com.intellij.psi.util.InheritanceUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
@@ -93,14 +95,25 @@ public class ModuleHighlightUtil {
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
static HighlightInfo checkPackageStatement(@NotNull PsiPackageStatement statement, @NotNull PsiFile file) {
|
||||
static HighlightInfo checkPackageStatement(@NotNull PsiPackageStatement statement, @NotNull PsiFile file, @Nullable PsiJavaModule module) {
|
||||
if (PsiUtil.isModuleFile(file)) {
|
||||
String message = JavaErrorMessages.message("module.no.package");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createDeleteFix(statement));
|
||||
return info;
|
||||
}
|
||||
|
||||
if (module != null) {
|
||||
String packageName = statement.getPackageName();
|
||||
if (packageName != null) {
|
||||
PsiJavaModule origin = JavaModuleGraphUtil.findOrigin(module, packageName);
|
||||
if (origin != null) {
|
||||
String message = JavaErrorMessages.message("module.conflicting.packages", packageName, origin.getName());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -108,7 +121,7 @@ public class ModuleHighlightUtil {
|
||||
static HighlightInfo checkFileName(@NotNull PsiJavaModule element, @NotNull PsiFile file) {
|
||||
if (!MODULE_INFO_FILE.equals(file.getName())) {
|
||||
String message = JavaErrorMessages.message("module.file.wrong.name");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(element)).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(element)).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createRenameFileFix(MODULE_INFO_FILE));
|
||||
return info;
|
||||
}
|
||||
@@ -124,7 +137,7 @@ public class ModuleHighlightUtil {
|
||||
Collection<VirtualFile> others = FilenameIndex.getVirtualFilesByName(project, MODULE_INFO_FILE, module.getModuleScope(false));
|
||||
if (others.size() > 1) {
|
||||
String message = JavaErrorMessages.message("module.file.duplicate");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(element)).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(element)).descriptionAndTooltip(message).create();
|
||||
others.stream().map(f -> PsiManager.getInstance(project).findFile(f)).filter(f -> f != file).findFirst().ifPresent(
|
||||
duplicate -> QuickFixAction.registerQuickFixAction(info, new GoToSymbolFix(duplicate, JavaErrorMessages.message("module.open.duplicate.text")))
|
||||
);
|
||||
@@ -148,7 +161,13 @@ public class ModuleHighlightUtil {
|
||||
checkDuplicateRefs(
|
||||
module.getExports(),
|
||||
st -> Optional.ofNullable(st.getPackageReference()).map(ModuleHighlightUtil::refText),
|
||||
"module.duplicate.export",
|
||||
"module.duplicate.exports",
|
||||
results);
|
||||
|
||||
checkDuplicateRefs(
|
||||
module.getOpens(),
|
||||
st -> Optional.ofNullable(st.getPackageReference()).map(ModuleHighlightUtil::refText),
|
||||
"module.duplicate.opens",
|
||||
results);
|
||||
|
||||
checkDuplicateRefs(
|
||||
@@ -175,7 +194,7 @@ public class ModuleHighlightUtil {
|
||||
String refText = ref.apply(statement).orElse(null);
|
||||
if (refText != null && !filter.add(refText)) {
|
||||
String message = JavaErrorMessages.message(key, refText);
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createDeleteFix(statement));
|
||||
results.add(info);
|
||||
}
|
||||
@@ -198,7 +217,7 @@ public class ModuleHighlightUtil {
|
||||
String className = refText(ref), packageName = StringUtil.getPackageName(className);
|
||||
if (!exports.contains(packageName) && !uses.contains(className)) {
|
||||
String message = JavaErrorMessages.message("module.service.unused");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(range(ref)).description(message).create());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(range(ref)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -218,7 +237,7 @@ public class ModuleHighlightUtil {
|
||||
VirtualFile root = ProjectFileIndex.SERVICE.getInstance(file.getProject()).getSourceRootForFile(vFile);
|
||||
if (root != null && !root.equals(vFile.getParent())) {
|
||||
String message = JavaErrorMessages.message("module.file.wrong.location");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(range(element)).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(range(element)).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, new MoveFileFix(vFile, root, QuickFixBundle.message("move.file.to.source.root.text")));
|
||||
return info;
|
||||
}
|
||||
@@ -238,7 +257,7 @@ public class ModuleHighlightUtil {
|
||||
}
|
||||
else if (target == container) {
|
||||
String message = JavaErrorMessages.message("module.cyclic.dependence", container.getName());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
else {
|
||||
Collection<PsiJavaModule> cycle = JavaModuleGraphUtil.findCycle((PsiJavaModule)target);
|
||||
@@ -246,7 +265,7 @@ public class ModuleHighlightUtil {
|
||||
Stream<String> stream = cycle.stream().map(PsiJavaModule::getName);
|
||||
if (ApplicationManager.getApplication().isUnitTestMode()) stream = stream.sorted();
|
||||
String message = JavaErrorMessages.message("module.cyclic.dependence", stream.collect(Collectors.joining(", ")));
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -261,8 +280,9 @@ public class ModuleHighlightUtil {
|
||||
(parent = statement.getParent()) instanceof PsiJavaModule &&
|
||||
((PsiJavaModule)parent).hasModifierProperty(PsiModifier.OPEN)) {
|
||||
String message = JavaErrorMessages.message("module.opens.in.weak.module");
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(statement).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createModifierListFix((PsiModifierListOwner)parent, PsiModifier.OPEN, false, false));
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createDeleteFix(statement));
|
||||
return info;
|
||||
}
|
||||
|
||||
@@ -281,11 +301,11 @@ public class ModuleHighlightUtil {
|
||||
HighlightInfoType type = statement.getRole() == Role.OPENS ? HighlightInfoType.WARNING : HighlightInfoType.ERROR;
|
||||
if (directories == null || directories.length == 0) {
|
||||
String message = JavaErrorMessages.message("package.not.found", packageName);
|
||||
return HighlightInfo.newHighlightInfo(type).range(refElement).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(type).range(refElement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
if (PsiUtil.isPackageEmpty(directories, packageName)) {
|
||||
String message = JavaErrorMessages.message("package.is.empty", packageName);
|
||||
return HighlightInfo.newHighlightInfo(type).range(refElement).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(type).range(refElement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,12 +322,15 @@ public class ModuleHighlightUtil {
|
||||
PsiPolyVariantReference ref = refElement.getReference();
|
||||
assert ref != null : statement;
|
||||
if (!targets.add(refText)) {
|
||||
String message = JavaErrorMessages.message("module.duplicate.export", refText);
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).description(message).create());
|
||||
boolean exports = statement.getRole() == Role.EXPORTS;
|
||||
String message = JavaErrorMessages.message(exports ? "module.duplicate.exports.target" : "module.duplicate.opens.target", refText);
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(refElement).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createDeleteFix(refElement, QuickFixBundle.message("delete.reference.fix.text")));
|
||||
results.add(info);
|
||||
}
|
||||
else if (ref.multiResolve(true).length == 0) {
|
||||
String message = JavaErrorMessages.message("module.not.found", refElement.getReferenceText());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).description(message).create());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).descriptionAndTooltip(message).create());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -320,11 +343,11 @@ public class ModuleHighlightUtil {
|
||||
PsiElement target = refElement.resolve();
|
||||
if (target == null) {
|
||||
String message = JavaErrorMessages.message("cannot.resolve.symbol", refElement.getReferenceName());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(refElement)).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(refElement)).descriptionAndTooltip(message).create();
|
||||
}
|
||||
else if (target instanceof PsiClass && ((PsiClass)target).isEnum()) {
|
||||
String message = JavaErrorMessages.message("module.service.enum", ((PsiClass)target).getName());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(refElement)).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(refElement)).descriptionAndTooltip(message).create();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -345,7 +368,7 @@ public class ModuleHighlightUtil {
|
||||
String refText = refText(implRef);
|
||||
if (!filter.add(refText)) {
|
||||
String message = JavaErrorMessages.message("module.duplicate.impl", refText);
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(implRef).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(implRef).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, factory().createDeleteFix(implRef, QuickFixBundle.message("delete.reference.fix.text")));
|
||||
results.add(info);
|
||||
continue;
|
||||
@@ -356,29 +379,40 @@ public class ModuleHighlightUtil {
|
||||
PsiElement implTarget = implRef.resolve();
|
||||
if (implTarget instanceof PsiClass) {
|
||||
PsiClass implClass = (PsiClass)implTarget;
|
||||
PsiMethod provider;
|
||||
|
||||
if (InheritanceUtil.isInheritorOrSelf(implClass, (PsiClass)intTarget, true)) {
|
||||
if (implClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
String message = JavaErrorMessages.message("module.service.abstract", implClass.getName());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).description(message).create());
|
||||
}
|
||||
else if (!PsiUtil.hasDefaultConstructor(implClass)) {
|
||||
String message = JavaErrorMessages.message("module.service.no.ctor", implClass.getName());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).description(message).create());
|
||||
}
|
||||
if (findModule(statement) != findModule(implClass)) {
|
||||
String message = JavaErrorMessages.message("module.service.alien");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
else if ((provider = findProvider(implClass)) != null) {
|
||||
|
||||
PsiMethod provider = ContainerUtil.find(
|
||||
implClass.findMethodsByName("provider", false),
|
||||
m -> m.hasModifierProperty(PsiModifier.PUBLIC) && m.hasModifierProperty(PsiModifier.STATIC) && m.getParameterList().getParametersCount() == 0);
|
||||
if (provider != null) {
|
||||
PsiType type = provider.getReturnType();
|
||||
PsiClass typeClass = type instanceof PsiClassType ? ((PsiClassType)type).resolve() : null;
|
||||
if (!InheritanceUtil.isInheritorOrSelf(typeClass, (PsiClass)intTarget, true)) {
|
||||
String message = JavaErrorMessages.message("module.service.provider.type", implClass.getName());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).description(message).create());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
}
|
||||
else if (InheritanceUtil.isInheritorOrSelf(implClass, (PsiClass)intTarget, true)) {
|
||||
if (implClass.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
String message = JavaErrorMessages.message("module.service.abstract", implClass.getName());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
else if (!(ClassUtil.isTopLevelClass(implClass) || implClass.hasModifierProperty(PsiModifier.STATIC))) {
|
||||
String message = JavaErrorMessages.message("module.service.inner", implClass.getName());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
else if (!PsiUtil.hasDefaultConstructor(implClass)) {
|
||||
String message = JavaErrorMessages.message("module.service.no.ctor", implClass.getName());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
}
|
||||
else {
|
||||
String message = JavaErrorMessages.message("module.service.impl");
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).description(message).create());
|
||||
results.add(HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(implRef)).descriptionAndTooltip(message).create());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -386,11 +420,6 @@ public class ModuleHighlightUtil {
|
||||
return results;
|
||||
}
|
||||
|
||||
private static PsiMethod findProvider(PsiClass implClass) {
|
||||
return JBIterable.of(implClass.findMethodsByName("provider", false))
|
||||
.find(p -> p.hasModifierProperty(PsiModifier.PUBLIC) && p.hasModifierProperty(PsiModifier.STATIC) && p.getParameterList().getParametersCount() == 0);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkPackageAccessibility(@NotNull PsiJavaCodeReferenceElement ref,
|
||||
@NotNull PsiElement target,
|
||||
@@ -428,19 +457,19 @@ public class ModuleHighlightUtil {
|
||||
if (!refModule.equals(targetModule)) {
|
||||
if (targetModule == null) {
|
||||
String message = JavaErrorMessages.message("module.package.on.classpath");
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).descriptionAndTooltip(message).create();
|
||||
}
|
||||
|
||||
String refModuleName = refModule.getName();
|
||||
String requiredName = targetModule.getName();
|
||||
if (!(targetModule instanceof LightJavaModule || JavaModuleGraphUtil.exports(targetModule, packageName, refModule))) {
|
||||
String message = JavaErrorMessages.message("module.package.not.exported", requiredName, packageName, refModuleName);
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).descriptionAndTooltip(message).create();
|
||||
}
|
||||
|
||||
if (!(PsiJavaModule.JAVA_BASE.equals(requiredName) || JavaModuleGraphUtil.reads(refModule, targetModule))) {
|
||||
String message = JavaErrorMessages.message("module.not.in.requirements", refModuleName, requiredName);
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(ref).descriptionAndTooltip(message).create();
|
||||
QuickFixAction.registerQuickFixAction(info, new AddRequiredModuleFix(refModule, requiredName));
|
||||
return info;
|
||||
}
|
||||
@@ -449,6 +478,18 @@ public class ModuleHighlightUtil {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
static HighlightInfo checkClashingReads(@NotNull PsiJavaModule module) {
|
||||
Trinity<String, PsiJavaModule, PsiJavaModule> conflict = JavaModuleGraphUtil.findConflict(module);
|
||||
if (conflict != null) {
|
||||
String message = JavaErrorMessages.message(
|
||||
"module.conflicting.reads", module.getName(), conflict.first, conflict.second.getName(), conflict.third.getName());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.ERROR).range(range(module)).descriptionAndTooltip(message).create();
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Module findModule(PsiElement element) {
|
||||
return Optional.ofNullable(element.getContainingFile())
|
||||
.map(PsiFile::getVirtualFile)
|
||||
@@ -459,15 +500,15 @@ public class ModuleHighlightUtil {
|
||||
private static HighlightInfo moduleResolveError(PsiJavaModuleReferenceElement refElement, PsiPolyVariantReference ref) {
|
||||
if (ref.multiResolve(true).length == 0) {
|
||||
String message = JavaErrorMessages.message("module.not.found", refElement.getReferenceText());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
else if (ref.multiResolve(false).length > 1) {
|
||||
String message = JavaErrorMessages.message("module.ambiguous", refElement.getReferenceText());
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).description(message).create();
|
||||
return HighlightInfo.newHighlightInfo(HighlightInfoType.WARNING).range(refElement).descriptionAndTooltip(message).create();
|
||||
}
|
||||
else {
|
||||
String message = JavaErrorMessages.message("module.not.on.path", refElement.getReferenceText());
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).description(message).create();
|
||||
HighlightInfo info = HighlightInfo.newHighlightInfo(HighlightInfoType.WRONG_REF).range(refElement).descriptionAndTooltip(message).create();
|
||||
factory().registerOrderEntryFixes(new QuickFixActionRegistrarImpl(info), ref);
|
||||
return info;
|
||||
}
|
||||
|
||||
+4
-4
@@ -21,6 +21,7 @@ import com.intellij.openapi.util.Comparing;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.infos.CandidateInfo;
|
||||
import com.intellij.psi.util.PsiTypesUtil;
|
||||
import com.intellij.util.IncorrectOperationException;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -54,11 +55,11 @@ public abstract class ArgumentFixerActionFactory {
|
||||
PsiMethod method = (PsiMethod) candidate.getElement();
|
||||
PsiSubstitutor substitutor = candidate.getSubstitutor();
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
if (expressions.length != parameters.length) {
|
||||
if (expressions.length != parameters.length && !method.isVarArgs()) {
|
||||
methodCandidates.remove(i);
|
||||
continue;
|
||||
}
|
||||
for (int j = 0; j < parameters.length; j++) {
|
||||
for (int j = 0; j < Math.min(parameters.length, expressions.length); j++) {
|
||||
PsiParameter parameter = parameters[j];
|
||||
PsiExpression expression = expressions[j];
|
||||
// check if we can cast to this method
|
||||
@@ -84,8 +85,7 @@ public abstract class ArgumentFixerActionFactory {
|
||||
for (CandidateInfo candidate : methodCandidates) {
|
||||
PsiMethod method = (PsiMethod)candidate.getElement();
|
||||
PsiSubstitutor substitutor = candidate.getSubstitutor();
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
PsiType originalParameterType = parameters[i].getType();
|
||||
PsiType originalParameterType = PsiTypesUtil.getParameterType(method.getParameterList().getParameters(), i, true);
|
||||
PsiType parameterType = substitutor.substitute(originalParameterType);
|
||||
if (parameterType instanceof PsiWildcardType) continue;
|
||||
if (!GenericsUtil.isFromExternalTypeLanguage(parameterType)) continue;
|
||||
|
||||
+5
-1
@@ -67,7 +67,11 @@ public class CastMethodArgumentFix extends MethodArgumentFix implements HighPrio
|
||||
parameterType = PsiPrimitiveType.getUnboxedType(parameterType);
|
||||
if (parameterType == null) return false;
|
||||
}
|
||||
return parameterType.isConvertibleFrom(exprType);
|
||||
if (parameterType.isConvertibleFrom(exprType)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return parameterType instanceof PsiEllipsisType && areTypesConvertible(exprType, ((PsiEllipsisType)parameterType).getComponentType(), context);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,8 +31,10 @@ public abstract class GuessManager {
|
||||
return ServiceManager.getService(project, GuessManager.class);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public abstract PsiType[] guessContainerElementType(PsiExpression containerExpr, TextRange rangeToIgnore);
|
||||
|
||||
@NotNull
|
||||
public abstract PsiType[] guessTypeToCast(PsiExpression expr);
|
||||
|
||||
@NotNull
|
||||
|
||||
@@ -79,6 +79,7 @@ public class GuessManagerImpl extends GuessManager {
|
||||
myProject = project;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiType[] guessContainerElementType(PsiExpression containerExpr, TextRange rangeToIgnore) {
|
||||
HashSet<PsiType> typesSet = new HashSet<>();
|
||||
@@ -117,6 +118,7 @@ public class GuessManagerImpl extends GuessManager {
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
public PsiType[] guessTypeToCast(PsiExpression expr) { //TODO : make better guess based on control flow
|
||||
LinkedHashSet<PsiType> types = new LinkedHashSet<>();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -47,7 +47,7 @@ import java.util.regex.Matcher;
|
||||
|
||||
public class JavaSuppressionUtil {
|
||||
public static final String SUPPRESS_INSPECTIONS_ANNOTATION_NAME = "java.lang.SuppressWarnings";
|
||||
public static boolean alreadyHas14Suppressions(@NotNull PsiDocCommentOwner commentOwner) {
|
||||
public static boolean alreadyHas14Suppressions(@NotNull PsiJavaDocumentedElement commentOwner) {
|
||||
final PsiDocComment docComment = commentOwner.getDocComment();
|
||||
return docComment != null && docComment.findTagByName(SuppressionUtilCore.SUPPRESS_INSPECTIONS_TAG_NAME) != null;
|
||||
}
|
||||
@@ -117,12 +117,12 @@ public class JavaSuppressionUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static PsiElement getElementMemberSuppressedIn(@NotNull PsiDocCommentOwner owner, @NotNull String inspectionToolID) {
|
||||
public static PsiElement getElementMemberSuppressedIn(@NotNull PsiJavaDocumentedElement owner, @NotNull String inspectionToolID) {
|
||||
PsiElement element = getDocCommentToolSuppressedIn(owner, inspectionToolID);
|
||||
if (element != null) return element;
|
||||
element = getAnnotationMemberSuppressedIn(owner, inspectionToolID);
|
||||
if (element != null) return element;
|
||||
PsiDocCommentOwner classContainer = PsiTreeUtil.getParentOfType(owner, PsiDocCommentOwner.class);
|
||||
PsiJavaDocumentedElement classContainer = PsiTreeUtil.getParentOfType(owner, PsiJavaDocumentedElement.class);
|
||||
while (classContainer != null) {
|
||||
element = getDocCommentToolSuppressedIn(classContainer, inspectionToolID);
|
||||
if (element != null) return element;
|
||||
@@ -130,7 +130,7 @@ public class JavaSuppressionUtil {
|
||||
element = getAnnotationMemberSuppressedIn(classContainer, inspectionToolID);
|
||||
if (element != null) return element;
|
||||
|
||||
classContainer = PsiTreeUtil.getParentOfType(classContainer, PsiDocCommentOwner.class);
|
||||
classContainer = PsiTreeUtil.getParentOfType(classContainer, PsiJavaDocumentedElement.class);
|
||||
}
|
||||
|
||||
final PsiJavaFile file = PsiTreeUtil.getParentOfType(owner, PsiJavaFile.class);
|
||||
@@ -158,7 +158,7 @@ public class JavaSuppressionUtil {
|
||||
return AnnotationUtil.findAnnotation(owner, Generated.class.getName());
|
||||
}
|
||||
|
||||
static PsiElement getDocCommentToolSuppressedIn(@NotNull PsiDocCommentOwner owner, @NotNull String inspectionToolID) {
|
||||
static PsiElement getDocCommentToolSuppressedIn(@NotNull PsiJavaDocumentedElement owner, @NotNull String inspectionToolID) {
|
||||
PsiDocComment docComment = owner.getDocComment();
|
||||
if (docComment == null && owner.getParent() instanceof PsiDeclarationStatement) {
|
||||
final PsiElement el = PsiTreeUtil.skipSiblingsBackward(owner.getParent(), PsiWhiteSpace.class);
|
||||
@@ -195,8 +195,8 @@ public class JavaSuppressionUtil {
|
||||
return matcher.group(1).trim();
|
||||
}
|
||||
}
|
||||
if (element instanceof PsiDocCommentOwner) {
|
||||
PsiDocComment docComment = ((PsiDocCommentOwner)element).getDocComment();
|
||||
if (element instanceof PsiJavaDocumentedElement) {
|
||||
PsiDocComment docComment = ((PsiJavaDocumentedElement)element).getDocComment();
|
||||
if (docComment != null) {
|
||||
PsiDocTag inspectionTag = docComment.findTagByName(SuppressionUtilCore.SUPPRESS_INSPECTIONS_TAG_NAME);
|
||||
if (inspectionTag != null) {
|
||||
@@ -226,9 +226,9 @@ public class JavaSuppressionUtil {
|
||||
return statement;
|
||||
}
|
||||
|
||||
PsiModifierListOwner up = PsiTreeUtil.getNonStrictParentOfType(place, PsiVariable.class, PsiDocCommentOwner.class);
|
||||
if (up instanceof PsiDocCommentOwner && up.getModifierList() == null) {
|
||||
up = PsiTreeUtil.getParentOfType(up, PsiVariable.class, PsiDocCommentOwner.class);
|
||||
PsiModifierListOwner up = PsiTreeUtil.getNonStrictParentOfType(place, PsiVariable.class, PsiJavaDocumentedElement.class);
|
||||
if (up instanceof PsiJavaDocumentedElement && up.getModifierList() == null) {
|
||||
up = PsiTreeUtil.getParentOfType(up, PsiVariable.class, PsiJavaDocumentedElement.class);
|
||||
}
|
||||
if (up instanceof PsiVariable) {
|
||||
PsiVariable local = (PsiVariable)up;
|
||||
@@ -238,18 +238,18 @@ public class JavaSuppressionUtil {
|
||||
}
|
||||
}
|
||||
|
||||
PsiDocCommentOwner container = up == null || up instanceof PsiDocCommentOwner
|
||||
? (PsiDocCommentOwner)up : PsiTreeUtil.getNonStrictParentOfType(up, PsiDocCommentOwner.class);
|
||||
PsiJavaDocumentedElement container = up == null || up instanceof PsiJavaDocumentedElement
|
||||
? (PsiJavaDocumentedElement)up : PsiTreeUtil.getNonStrictParentOfType(up, PsiJavaDocumentedElement.class);
|
||||
while (true) {
|
||||
if (!(container instanceof PsiTypeParameter)) break;
|
||||
container = PsiTreeUtil.getParentOfType(container, PsiDocCommentOwner.class);
|
||||
container = PsiTreeUtil.getParentOfType(container, PsiJavaDocumentedElement.class);
|
||||
}
|
||||
|
||||
if (container != null) {
|
||||
PsiElement element = getElementMemberSuppressedIn(container, toolId);
|
||||
if (element != null) return element;
|
||||
}
|
||||
PsiDocCommentOwner classContainer = PsiTreeUtil.getParentOfType(container, PsiDocCommentOwner.class, true);
|
||||
PsiJavaDocumentedElement classContainer = PsiTreeUtil.getParentOfType(container, PsiJavaDocumentedElement.class, true);
|
||||
if (classContainer != null) {
|
||||
PsiElement element = getElementMemberSuppressedIn(classContainer, toolId);
|
||||
if (element != null) return element;
|
||||
|
||||
+35
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -84,35 +84,38 @@ public class RedundantSuppressInspectionBase extends GlobalInspectionTool {
|
||||
@NotNull final GlobalInspectionContext globalContext,
|
||||
@NotNull final ProblemDescriptionsProcessor problemDescriptionsProcessor) {
|
||||
globalContext.getRefManager().iterate(new RefJavaVisitor() {
|
||||
@Override
|
||||
public void visitJavaModule(@NotNull RefJavaModule javaModule) {
|
||||
visitElement(javaModule);
|
||||
}
|
||||
|
||||
@Override public void visitClass(@NotNull RefClass refClass) {
|
||||
if (!globalContext.shouldCheck(refClass, RedundantSuppressInspectionBase.this)) return;
|
||||
CommonProblemDescriptor[] descriptors = checkElement(refClass, manager);
|
||||
if (descriptors != null) {
|
||||
for (CommonProblemDescriptor descriptor : descriptors) {
|
||||
if (descriptor instanceof ProblemDescriptor) {
|
||||
final PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement();
|
||||
final PsiMember member = PsiTreeUtil.getParentOfType(psiElement, PsiMember.class);
|
||||
final RefElement refElement = globalContext.getRefManager().getReference(member);
|
||||
if (refElement != null) {
|
||||
problemDescriptionsProcessor.addProblemElement(refElement, descriptor);
|
||||
continue;
|
||||
}
|
||||
visitElement(refClass);
|
||||
}
|
||||
|
||||
private void visitElement(@NotNull RefElement refElement) {
|
||||
if (!globalContext.shouldCheck(refElement, RedundantSuppressInspectionBase.this)) return;
|
||||
final PsiElement element = refElement.getElement();
|
||||
if (element == null) return;
|
||||
final CommonProblemDescriptor[] descriptors = checkElement(element, manager);
|
||||
for (CommonProblemDescriptor descriptor : descriptors) {
|
||||
if (descriptor instanceof ProblemDescriptor) {
|
||||
final PsiElement psiElement = ((ProblemDescriptor)descriptor).getPsiElement();
|
||||
final PsiMember member = PsiTreeUtil.getParentOfType(psiElement, PsiMember.class);
|
||||
final RefElement reference = globalContext.getRefManager().getReference(member);
|
||||
if (reference != null) {
|
||||
problemDescriptionsProcessor.addProblemElement(reference, descriptor);
|
||||
continue;
|
||||
}
|
||||
problemDescriptionsProcessor.addProblemElement(refClass, descriptor);
|
||||
}
|
||||
problemDescriptionsProcessor.addProblemElement(refElement, descriptor);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private CommonProblemDescriptor[] checkElement(@NotNull RefClass refEntity, @NotNull InspectionManager manager) {
|
||||
final PsiClass psiClass = refEntity.getElement();
|
||||
if (psiClass == null) return null;
|
||||
return checkElement(psiClass, manager);
|
||||
}
|
||||
|
||||
public CommonProblemDescriptor[] checkElement(@NotNull final PsiElement psiElement, @NotNull final InspectionManager manager) {
|
||||
@NotNull
|
||||
public ProblemDescriptor[] checkElement(@NotNull final PsiElement psiElement, @NotNull final InspectionManager manager) {
|
||||
final Map<PsiElement, Collection<String>> suppressedScopes = new THashMap<>();
|
||||
psiElement.accept(new JavaRecursiveElementWalkingVisitor() {
|
||||
@Override public void visitModifierList(PsiModifierList list) {
|
||||
@@ -156,7 +159,7 @@ public class RedundantSuppressInspectionBase extends GlobalInspectionTool {
|
||||
}
|
||||
});
|
||||
|
||||
if (suppressedScopes.values().isEmpty()) return null;
|
||||
if (suppressedScopes.values().isEmpty()) return ProblemDescriptor.EMPTY_ARRAY;
|
||||
// have to visit all file from scratch since inspections can be written in any pervasive way including checkFile() overriding
|
||||
Map<InspectionToolWrapper, String> suppressedTools = new THashMap<>();
|
||||
InspectionToolWrapper[] toolWrappers = getInspectionTools(psiElement, manager);
|
||||
@@ -246,13 +249,13 @@ public class RedundantSuppressInspectionBase extends GlobalInspectionTool {
|
||||
for (PsiElement suppressedScope : suppressedScopes.keySet()) {
|
||||
Collection<String> suppressedIds = suppressedScopes.get(suppressedScope);
|
||||
for (String toolId : suppressedIds) {
|
||||
PsiMember psiMember;
|
||||
PsiJavaDocumentedElement psiMember;
|
||||
String problemLine = null;
|
||||
if (suppressedScope instanceof PsiMember) {
|
||||
psiMember = (PsiMember)suppressedScope;
|
||||
if (suppressedScope instanceof PsiJavaDocumentedElement) {
|
||||
psiMember = (PsiJavaDocumentedElement)suppressedScope;
|
||||
}
|
||||
else {
|
||||
psiMember = PsiTreeUtil.getParentOfType(suppressedScope, PsiDocCommentOwner.class);
|
||||
psiMember = PsiTreeUtil.getParentOfType(suppressedScope, PsiJavaDocumentedElement.class);
|
||||
final PsiStatement statement = PsiTreeUtil.getNextSiblingOfType(suppressedScope, PsiStatement.class);
|
||||
problemLine = statement != null ? statement.getText() : null;
|
||||
}
|
||||
@@ -265,18 +268,12 @@ public class RedundantSuppressInspectionBase extends GlobalInspectionTool {
|
||||
fix = new RemoveSuppressWarningAction(toolId, problemLine);
|
||||
myQuickFixes.put(key, fix);
|
||||
}
|
||||
PsiElement identifier = null;
|
||||
if (!(suppressedScope instanceof PsiMember)) {
|
||||
PsiElement identifier;
|
||||
if (!(suppressedScope instanceof PsiJavaDocumentedElement)) {
|
||||
identifier = suppressedScope;
|
||||
}
|
||||
else if (psiMember instanceof PsiMethod) {
|
||||
identifier = ((PsiMethod)psiMember).getNameIdentifier();
|
||||
}
|
||||
else if (psiMember instanceof PsiField) {
|
||||
identifier = ((PsiField)psiMember).getNameIdentifier();
|
||||
}
|
||||
else if (psiMember instanceof PsiClass) {
|
||||
identifier = ((PsiClass)psiMember).getNameIdentifier();
|
||||
else {
|
||||
identifier = psiMember.getNameIdentifier();
|
||||
}
|
||||
if (identifier == null) {
|
||||
identifier = psiMember;
|
||||
@@ -299,6 +296,7 @@ public class RedundantSuppressInspectionBase extends GlobalInspectionTool {
|
||||
return new GlobalInspectionContextBase(file.getProject());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected InspectionToolWrapper[] getInspectionTools(PsiElement psiElement, @NotNull InspectionManager manager) {
|
||||
// todo for what we create modifiable model here?
|
||||
return new InspectionProfileModifiableModel(InspectionProjectProfileManager.getInstance(manager.getProject()).getCurrentProfile()).getInspectionTools(psiElement);
|
||||
|
||||
+2
@@ -18,6 +18,7 @@ package com.intellij.codeInspection.bytecodeAnalysis;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.AnalyzerExt;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.InterpreterExt;
|
||||
import com.intellij.codeInspection.bytecodeAnalysis.asm.LiteAnalyzerExt;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||
@@ -165,6 +166,7 @@ class NullableMethodAnalysis {
|
||||
return FinalBot;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static int[] mapOrigins(boolean[] origins) {
|
||||
int[] originsMapping = new int[origins.length];
|
||||
int mapped = 0;
|
||||
|
||||
+2
@@ -151,6 +151,7 @@ public class ProjectBytecodeAnalysis {
|
||||
* @param methodAnnotations inferred annotations
|
||||
* @return Psi annotations
|
||||
*/
|
||||
@NotNull
|
||||
private PsiAnnotation[] toPsi(HKey primaryKey, MethodAnnotations methodAnnotations) {
|
||||
boolean notNull = methodAnnotations.notNulls.contains(primaryKey);
|
||||
boolean nullable = methodAnnotations.nullables.contains(primaryKey);
|
||||
@@ -201,6 +202,7 @@ public class ProjectBytecodeAnalysis {
|
||||
* @param parameterAnnotations inferred parameter annotations
|
||||
* @return Psi annotations
|
||||
*/
|
||||
@NotNull
|
||||
private PsiAnnotation[] toPsi(ParameterAnnotations parameterAnnotations) {
|
||||
if (parameterAnnotations.notNull) {
|
||||
return new PsiAnnotation[]{
|
||||
|
||||
+19
@@ -145,6 +145,25 @@ public class JavacQuirksInspectionVisitor extends JavaElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitBinaryExpression(PsiBinaryExpression expression) {
|
||||
super.visitBinaryExpression(expression);
|
||||
if (myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_7) && !myLanguageLevel.isAtLeast(LanguageLevel.JDK_1_8)) {
|
||||
PsiType ltype = expression.getLOperand().getType();
|
||||
PsiExpression rOperand = expression.getROperand();
|
||||
if (rOperand != null) {
|
||||
PsiType rtype = rOperand.getType();
|
||||
if (ltype != null && rtype != null &&
|
||||
(ltype.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) ^ rtype.equalsToText(CommonClassNames.JAVA_LANG_OBJECT)) &&
|
||||
(TypeConversionUtil.isPrimitiveAndNotNull(ltype) ^ TypeConversionUtil.isPrimitiveAndNotNull(rtype)) &&
|
||||
TypeConversionUtil.isBinaryOperatorApplicable(expression.getOperationTokenType(), ltype, rtype, false) &&
|
||||
TypeConversionUtil.areTypesConvertible(rtype, ltype)) {
|
||||
myHolder.registerProblem(expression.getOperationSign(), "Comparision between Object and primitive is illegal and is accepted in java 7 only", ProblemHighlightType.GENERIC_ERROR_OR_WARNING);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ReplaceAssignmentOperatorWithAssignmentFix implements LocalQuickFix {
|
||||
private final String myOperationSign;
|
||||
|
||||
|
||||
+10
-9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -21,7 +21,10 @@ import com.intellij.psi.*;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
@@ -33,20 +36,18 @@ class ContractChecker extends DataFlowRunner {
|
||||
private final Set<PsiElement> myNonViolations = ContainerUtil.newHashSet();
|
||||
private final Set<PsiElement> myFailures = ContainerUtil.newHashSet();
|
||||
|
||||
private ContractChecker(PsiMethod method, MethodContract contract, final boolean onTheFly) {
|
||||
super(false, true, onTheFly);
|
||||
private ContractChecker(PsiMethod method, MethodContract contract) {
|
||||
super(false, true);
|
||||
myMethod = method;
|
||||
myContract = contract;
|
||||
}
|
||||
|
||||
static Map<PsiElement, String> checkContractClause(PsiMethod method,
|
||||
MethodContract contract,
|
||||
boolean ignoreAssertions, final boolean onTheFly) {
|
||||
static Map<PsiElement, String> checkContractClause(PsiMethod method, MethodContract contract, boolean ignoreAssertions) {
|
||||
|
||||
PsiCodeBlock body = method.getBody();
|
||||
if (body == null) return Collections.emptyMap();
|
||||
|
||||
ContractChecker checker = new ContractChecker(method, contract, onTheFly);
|
||||
ContractChecker checker = new ContractChecker(method, contract);
|
||||
|
||||
PsiParameter[] parameters = method.getParameterList().getParameters();
|
||||
final DfaMemoryState initialState = checker.createMemoryState();
|
||||
@@ -61,7 +62,7 @@ class ContractChecker extends DataFlowRunner {
|
||||
}
|
||||
}
|
||||
|
||||
checker.analyzeMethod(body, new StandardInstructionVisitor(), ignoreAssertions, Arrays.asList(initialState));
|
||||
checker.analyzeMethod(body, new StandardInstructionVisitor(), ignoreAssertions, Collections.singletonList(initialState));
|
||||
return checker.getErrors();
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -40,7 +40,7 @@ public class ContractInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
@Override
|
||||
public void visitMethod(PsiMethod method) {
|
||||
for (MethodContract contract : ControlFlowAnalyzer.getMethodContracts(method)) {
|
||||
Map<PsiElement, String> errors = ContractChecker.checkContractClause(method, contract, false, isOnTheFly);
|
||||
Map<PsiElement, String> errors = ContractChecker.checkContractClause(method, contract, false);
|
||||
for (Map.Entry<PsiElement, String> entry : errors.entrySet()) {
|
||||
PsiElement element = entry.getKey();
|
||||
holder.registerProblem(element, entry.getValue());
|
||||
|
||||
+1
-12
@@ -52,7 +52,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
private FList<Trap> myTrapStack = FList.emptyList();
|
||||
private final ExceptionTransfer myRuntimeException;
|
||||
private final ExceptionTransfer myError;
|
||||
private final PsiType myNpe;
|
||||
private final PsiType myAssertionError;
|
||||
|
||||
ControlFlowAnalyzer(final DfaValueFactory valueFactory, @NotNull PsiElement codeFragment, boolean ignoreAssertions) {
|
||||
@@ -63,7 +62,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
GlobalSearchScope scope = codeFragment.getResolveScope();
|
||||
myRuntimeException = new ExceptionTransfer(myFactory.createTypeValue(createClassType(scope, JAVA_LANG_RUNTIME_EXCEPTION), Nullness.NOT_NULL));
|
||||
myError = new ExceptionTransfer(myFactory.createTypeValue(createClassType(scope, JAVA_LANG_ERROR), Nullness.NOT_NULL));
|
||||
myNpe = createClassType(scope, JAVA_LANG_NULL_POINTER_EXCEPTION);
|
||||
myAssertionError = createClassType(scope, JAVA_LANG_ASSERTION_ERROR);
|
||||
}
|
||||
|
||||
@@ -708,16 +706,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
exception.accept(this);
|
||||
|
||||
addConditionalRuntimeThrow();
|
||||
addInstruction(new DupInstruction());
|
||||
addInstruction(new PushInstruction(myFactory.getConstFactory().getNull(), null));
|
||||
addInstruction(new BinopInstruction(JavaTokenType.EQEQ, null, myProject));
|
||||
ConditionalGotoInstruction gotoInstruction = new ConditionalGotoInstruction(null, true, null);
|
||||
addInstruction(gotoInstruction);
|
||||
|
||||
addInstruction(new FieldReferenceInstruction(exception, "thrown exception"));
|
||||
throwException(myNpe, statement);
|
||||
|
||||
gotoInstruction.setOffset(myCurrentFlow.getInstructionCount());
|
||||
throwException(exception.getType(), statement);
|
||||
}
|
||||
|
||||
@@ -1131,7 +1120,7 @@ public class ControlFlowAnalyzer extends JavaElementVisitor {
|
||||
|
||||
PopInstruction pop = new PopInstruction();
|
||||
addInstruction(pop);
|
||||
DfaConstValue constValue = and ? myFactory.getConstFactory().getFalse() : myFactory.getConstFactory().getTrue();
|
||||
DfaConstValue constValue = myFactory.getBoolean(!and);
|
||||
PushInstruction pushSuccess = new PushInstruction(constValue, null);
|
||||
addInstruction(pushSuccess);
|
||||
|
||||
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.psi.PsiMethodCallExpression;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.siyeh.ig.callMatcher.CallMapper;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import static com.intellij.psi.CommonClassNames.*;
|
||||
import static com.intellij.psi.JavaTokenType.*;
|
||||
import static com.siyeh.ig.callMatcher.CallMatcher.*;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class CustomMethodHandlers {
|
||||
interface CustomMethodHandler {
|
||||
List<DfaMemoryState> handle(DfaValue qualifier, DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory);
|
||||
}
|
||||
|
||||
private static final CallMapper<CustomMethodHandler> CUSTOM_METHOD_HANDLERS = new CallMapper<CustomMethodHandler>()
|
||||
.register(instanceCall(JAVA_LANG_STRING, "isEmpty").parameterCount(0),
|
||||
(qualifier, args, memState, factory) -> stringIsEmpty(qualifier, memState, factory))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "indexOf", "lastIndexOf"),
|
||||
(qualifier, args, memState, factory) -> stringIndexOf(qualifier, memState, factory))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "equals").parameterCount(1),
|
||||
(qualifier, args, memState, factory) -> stringEquals(qualifier, args, memState, factory, false))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "equalsIgnoreCase").parameterCount(1),
|
||||
(qualifier, args, memState, factory) -> stringEquals(qualifier, args, memState, factory, true))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "startsWith").parameterCount(1),
|
||||
(qualifier, args, memState, factory) -> stringStartsEnds(qualifier, args, memState, factory, false))
|
||||
.register(instanceCall(JAVA_LANG_STRING, "endsWith").parameterCount(1),
|
||||
(qualifier, args, memState, factory) -> stringStartsEnds(qualifier, args, memState, factory, true))
|
||||
.register(anyOf(staticCall(JAVA_LANG_MATH, "max").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_MATH, "max").parameterTypes("long", "long"),
|
||||
staticCall(JAVA_LANG_INTEGER, "max").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_LONG, "max").parameterTypes("long", "long")),
|
||||
(qualifier, args, memState, factory) -> mathMinMax(args, memState, factory, true))
|
||||
.register(anyOf(staticCall(JAVA_LANG_MATH, "min").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_MATH, "min").parameterTypes("long", "long"),
|
||||
staticCall(JAVA_LANG_INTEGER, "min").parameterTypes("int", "int"),
|
||||
staticCall(JAVA_LANG_LONG, "min").parameterTypes("long", "long")),
|
||||
(qualifier, args, memState, factory) -> mathMinMax(args, memState, factory, false))
|
||||
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("int"),
|
||||
(qualifier, args, memState, factory) -> mathAbs(args, memState, factory, false))
|
||||
.register(staticCall(JAVA_LANG_MATH, "abs").parameterTypes("long"),
|
||||
(qualifier, args, memState, factory) -> mathAbs(args, memState, factory, true));
|
||||
|
||||
public static CustomMethodHandler find(PsiMethodCallExpression call) {
|
||||
return CUSTOM_METHOD_HANDLERS.mapFirst(call);
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> stringStartsEnds(DfaValue qualifier,
|
||||
DfaValue[] args,
|
||||
DfaMemoryState memState,
|
||||
DfaValueFactory factory,
|
||||
boolean ends) {
|
||||
DfaValue arg = ArrayUtil.getFirstElement(args);
|
||||
if (arg == null) return Collections.emptyList();
|
||||
String leftConst = ObjectUtils.tryCast(getConstantValue(memState, qualifier), String.class);
|
||||
String rightConst = ObjectUtils.tryCast(getConstantValue(memState, arg), String.class);
|
||||
if (leftConst != null && rightConst != null) {
|
||||
return singleResult(memState, factory.getBoolean(ends ? leftConst.endsWith(rightConst) : leftConst.startsWith(rightConst)));
|
||||
}
|
||||
DfaValue leftLength = memState.getStringLength(qualifier);
|
||||
DfaValue rightLength = memState.getStringLength(arg);
|
||||
DfaRelationValue trueRelation = factory.getRelationFactory().createRelation(leftLength, rightLength, GE, false);
|
||||
DfaRelationValue falseRelation = factory.getRelationFactory().createRelation(leftLength, rightLength, LT, false);
|
||||
return applyCondition(memState, trueRelation, DfaUnknownValue.getInstance(), falseRelation, factory.getBoolean(false));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> stringEquals(DfaValue qualifier,
|
||||
DfaValue[] args,
|
||||
DfaMemoryState memState,
|
||||
DfaValueFactory factory,
|
||||
boolean ignoreCase) {
|
||||
DfaValue arg = ArrayUtil.getFirstElement(args);
|
||||
if (arg == null) return Collections.emptyList();
|
||||
String leftConst = ObjectUtils.tryCast(getConstantValue(memState, qualifier), String.class);
|
||||
String rightConst = ObjectUtils.tryCast(getConstantValue(memState, arg), String.class);
|
||||
if (leftConst != null && rightConst != null) {
|
||||
return singleResult(memState, factory.getBoolean(ignoreCase ? leftConst.equalsIgnoreCase(rightConst) : leftConst.equals(rightConst)));
|
||||
}
|
||||
DfaValue leftLength = memState.getStringLength(qualifier);
|
||||
DfaValue rightLength = memState.getStringLength(arg);
|
||||
DfaRelationValue trueRelation = factory.getRelationFactory().createRelation(leftLength, rightLength, EQ, false);
|
||||
DfaRelationValue falseRelation = factory.getRelationFactory().createRelation(leftLength, rightLength, NE, false);
|
||||
return applyCondition(memState, trueRelation, DfaUnknownValue.getInstance(), falseRelation, factory.getBoolean(false));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> stringIndexOf(DfaValue qualifier,
|
||||
DfaMemoryState memState,
|
||||
DfaValueFactory factory) {
|
||||
DfaValue length = memState.getStringLength(qualifier);
|
||||
LongRangeSet range = memState.getRange(length);
|
||||
long maxLen = range == null || range.isEmpty() ? Integer.MAX_VALUE : range.max();
|
||||
return singleResult(memState, factory.getRangeFactory().create(LongRangeSet.range(-1, maxLen - 1)));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> stringIsEmpty(DfaValue qualifier, DfaMemoryState memState, DfaValueFactory factory) {
|
||||
DfaValue length = memState.getStringLength(qualifier);
|
||||
if (length == DfaUnknownValue.getInstance()) {
|
||||
return singleResult(memState, DfaUnknownValue.getInstance());
|
||||
}
|
||||
DfaConstValue zero = factory.getConstFactory().createFromValue(0, PsiType.INT, null);
|
||||
DfaRelationValue trueRelation = factory.getRelationFactory().createRelation(length, zero, EQEQ, false);
|
||||
DfaRelationValue falseRelation = factory.getRelationFactory().createRelation(length, zero, NE, false);
|
||||
return applyCondition(memState, trueRelation, factory.getBoolean(true), falseRelation, factory.getBoolean(false));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> mathMinMax(DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory, boolean max) {
|
||||
if(args == null || args.length != 2) return Collections.emptyList();
|
||||
LongRangeSet first = memState.getRange(args[0]);
|
||||
LongRangeSet second = memState.getRange(args[1]);
|
||||
if (first == null || second == null || first.isEmpty() || second.isEmpty()) return Collections.emptyList();
|
||||
LongRangeSet domain = max ? LongRangeSet.range(Math.max(first.min(), second.min()), Long.MAX_VALUE)
|
||||
: LongRangeSet.range(Long.MIN_VALUE, Math.min(first.max(), second.max()));
|
||||
LongRangeSet result = first.union(second).intersect(domain);
|
||||
return singleResult(memState, factory.getRangeFactory().create(result));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> mathAbs(DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory, boolean isLong) {
|
||||
DfaValue arg = ArrayUtil.getFirstElement(args);
|
||||
if(arg == null) return Collections.emptyList();
|
||||
LongRangeSet range = memState.getRange(arg);
|
||||
if (range == null) return Collections.emptyList();
|
||||
return singleResult(memState, factory.getRangeFactory().create(range.abs(isLong)));
|
||||
}
|
||||
|
||||
private static List<DfaMemoryState> singleResult(DfaMemoryState state, DfaValue value) {
|
||||
state.push(value);
|
||||
return Collections.singletonList(state);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<DfaMemoryState> applyCondition(DfaMemoryState memState,
|
||||
DfaRelationValue trueRelation,
|
||||
DfaValue trueResult,
|
||||
DfaRelationValue falseRelation,
|
||||
DfaValue falseResult) {
|
||||
DfaMemoryState falseState = memState.createCopy();
|
||||
List<DfaMemoryState> result = new ArrayList<>(2);
|
||||
if (memState.applyCondition(trueRelation)) {
|
||||
memState.push(trueResult);
|
||||
result.add(memState);
|
||||
}
|
||||
if (falseState.applyCondition(falseRelation)) {
|
||||
falseState.push(falseResult);
|
||||
result.add(falseState);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private static Object getConstantValue(DfaMemoryState memoryState, DfaValue value) {
|
||||
if (value instanceof DfaVariableValue) {
|
||||
value = memoryState.getConstantValue((DfaVariableValue)value);
|
||||
}
|
||||
if (value instanceof DfaConstValue) {
|
||||
return ((DfaConstValue)value).getValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+15
-30
@@ -173,8 +173,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
if (containingClass != null && PsiUtil.isLocalOrAnonymousClass(containingClass) && !(containingClass instanceof PsiEnumConstantInitializer)) return;
|
||||
|
||||
final StandardDataFlowRunner dfaRunner =
|
||||
new StandardDataFlowRunner(TREAT_UNKNOWN_MEMBERS_AS_NULLABLE,
|
||||
!isInsideConstructorOrInitializer(scope), onTheFly);
|
||||
new StandardDataFlowRunner(TREAT_UNKNOWN_MEMBERS_AS_NULLABLE, !isInsideConstructorOrInitializer(scope));
|
||||
analyzeDfaWithNestedClosures(scope, holder, dfaRunner, Collections.singletonList(dfaRunner.createMemoryState()), onTheFly);
|
||||
}
|
||||
|
||||
@@ -356,30 +355,24 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
private void reportUncheckedOptionalGet(ProblemsHolder holder,
|
||||
Map<PsiMethodCallExpression, ThreeState> calls,
|
||||
List<PsiExpression> qualifiers) {
|
||||
if (!REPORT_UNCHECKED_OPTIONALS) return;
|
||||
for (Map.Entry<PsiMethodCallExpression, ThreeState> entry : calls.entrySet()) {
|
||||
ThreeState state = entry.getValue();
|
||||
if (state == ThreeState.YES || state == ThreeState.UNSURE && !REPORT_UNCHECKED_OPTIONALS) {
|
||||
continue;
|
||||
}
|
||||
if (state != ThreeState.UNSURE) continue;
|
||||
PsiMethodCallExpression call = entry.getKey();
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null) continue;
|
||||
PsiClass optionalClass = method.getContainingClass();
|
||||
if (optionalClass == null) continue;
|
||||
if (state == ThreeState.NO) {
|
||||
holder.registerProblem(getElementToHighlight(call),
|
||||
InspectionsBundle.message("dataflow.message.optional.get.definitely.absent", optionalClass.getName()));
|
||||
} else if (state == ThreeState.UNSURE) {
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression());
|
||||
if (qualifier instanceof PsiMethodCallExpression &&
|
||||
qualifiers.stream().anyMatch(q -> PsiEquivalenceUtil.areElementsEquivalent(q, qualifier))) {
|
||||
// Conservatively do not report methodCall().get() cases if methodCall().isPresent() was found in the same method
|
||||
// without deep correspondence analysis
|
||||
continue;
|
||||
}
|
||||
holder.registerProblem(getElementToHighlight(call),
|
||||
InspectionsBundle.message("dataflow.message.optional.get.without.is.present", optionalClass.getName()));
|
||||
PsiExpression qualifier = PsiUtil.skipParenthesizedExprDown(call.getMethodExpression().getQualifierExpression());
|
||||
if (qualifier instanceof PsiMethodCallExpression &&
|
||||
qualifiers.stream().anyMatch(q -> PsiEquivalenceUtil.areElementsEquivalent(q, qualifier))) {
|
||||
// Conservatively do not report methodCall().get() cases if methodCall().isPresent() was found in the same method
|
||||
// without deep correspondence analysis
|
||||
continue;
|
||||
}
|
||||
holder.registerProblem(getElementToHighlight(call),
|
||||
InspectionsBundle.message("dataflow.message.optional.get.without.is.present", optionalClass.getName()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -981,7 +974,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
if ("isPresent".equals(methodName) && qualifier instanceof PsiMethodCallExpression) {
|
||||
myOptionalQualifiers.add(qualifier);
|
||||
}
|
||||
else if (isOptionalGetMethodName(methodName)) {
|
||||
else if (DfaOptionalSupport.isOptionalGetMethodName(methodName)) {
|
||||
ThreeState state = memState.checkOptional(memState.peek());
|
||||
myOptionalCalls.merge(call, state, (s1, s2) -> s1 == s2 ? s1 : ThreeState.UNSURE);
|
||||
}
|
||||
@@ -995,18 +988,13 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
return states;
|
||||
}
|
||||
|
||||
private static boolean isOptionalGetMethodName(String name) {
|
||||
return "get".equals(name) || "getAsDouble".equals(name) || "getAsInt".equals(name) || "getAsLong".equals(name);
|
||||
}
|
||||
|
||||
private static boolean hasNonTrivialFailingContracts(MethodCallInstruction instruction) {
|
||||
List<MethodContract> contracts = instruction.getContracts();
|
||||
return !contracts.isEmpty() && contracts.stream().allMatch(DataFlowInstructionVisitor::isNonTrivialFailingContract);
|
||||
return !contracts.isEmpty() && contracts.stream().anyMatch(DataFlowInstructionVisitor::isNonTrivialFailingContract);
|
||||
}
|
||||
|
||||
private static boolean isNonTrivialFailingContract(MethodContract contract) {
|
||||
return contract.returnValue == MethodContract.ValueConstraint.THROW_EXCEPTION &&
|
||||
Arrays.stream(contract.arguments).anyMatch(v -> v != MethodContract.ValueConstraint.ANY_VALUE);
|
||||
return contract.returnValue == MethodContract.ValueConstraint.THROW_EXCEPTION && !contract.isTrivial();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1020,10 +1008,7 @@ public class DataFlowInspectionBase extends BaseJavaBatchLocalInspectionTool {
|
||||
myProblems.putValue(problem, anchor);
|
||||
}
|
||||
Pair<NullabilityProblem, PsiElement> key = Pair.create(problem, anchor);
|
||||
StateInfo info = myStateInfos.get(key);
|
||||
if (info == null) {
|
||||
myStateInfos.put(key, info = new StateInfo());
|
||||
}
|
||||
StateInfo info = myStateInfos.computeIfAbsent(key, k -> new StateInfo());
|
||||
if (state.isEphemeral() && !ok) {
|
||||
info.ephemeralNpe = true;
|
||||
} else if (!state.isEphemeral()) {
|
||||
|
||||
+21
-16
@@ -26,7 +26,6 @@ package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.openapi.application.ApplicationManager;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Key;
|
||||
@@ -54,17 +53,15 @@ public class DataFlowRunner {
|
||||
private final List<DfaMemoryState> myStackTopClosures = new ArrayList<>();
|
||||
@NotNull
|
||||
private final DfaValueFactory myValueFactory;
|
||||
private final boolean myShouldCheckLimitTime;
|
||||
// Maximum allowed attempts to process instruction. Fail as too complex to process if certain instruction
|
||||
// is executed more than this limit times.
|
||||
static final int MAX_STATES_PER_BRANCH = 300;
|
||||
|
||||
protected DataFlowRunner() {
|
||||
this(false, true, false);
|
||||
this(false, true);
|
||||
}
|
||||
|
||||
protected DataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers, boolean shouldCheckLimitTime) {
|
||||
myShouldCheckLimitTime = shouldCheckLimitTime;
|
||||
protected DataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers) {
|
||||
myValueFactory = new DfaValueFactory(honorFieldInitializers, unknownMembersAreNullable);
|
||||
}
|
||||
|
||||
@@ -153,14 +150,13 @@ public class DataFlowRunner {
|
||||
MultiMap<BranchingInstruction, DfaMemoryState> processedStates = MultiMap.createSet();
|
||||
MultiMap<BranchingInstruction, DfaMemoryState> incomingStates = MultiMap.createSet();
|
||||
|
||||
long msLimit = Registry.intValue(shouldCheckTimeLimit() ? "ide.dfa.time.limit.online" : "ide.dfa.time.limit.offline");
|
||||
WorkingTimeMeasurer measurer = new WorkingTimeMeasurer(msLimit * 1000 * 1000);
|
||||
int stateLimit = Registry.intValue("ide.dfa.state.limit");
|
||||
int count = 0;
|
||||
while (!queue.isEmpty()) {
|
||||
List<DfaInstructionState> states = queue.getNextInstructionStates(joinInstructions);
|
||||
for (DfaInstructionState instructionState : states) {
|
||||
if (count++ % 1024 == 0 && measurer.isTimeOver()) {
|
||||
LOG.trace("Too complex because the analysis took too long");
|
||||
if (count++ > stateLimit) {
|
||||
LOG.trace("Too complex data flow: too many instruction states processed");
|
||||
psiBlock.putUserData(TOO_EXPENSIVE_HASH, psiBlock.getText().hashCode());
|
||||
return RunnerResult.TOO_COMPLEX;
|
||||
}
|
||||
@@ -177,7 +173,7 @@ public class DataFlowRunner {
|
||||
if (instruction instanceof BranchingInstruction) {
|
||||
BranchingInstruction branching = (BranchingInstruction)instruction;
|
||||
Collection<DfaMemoryState> processed = processedStates.get(branching);
|
||||
if (processed.contains(instructionState.getMemoryState())) {
|
||||
if (containsState(processed, instructionState)) {
|
||||
continue;
|
||||
}
|
||||
if (processed.size() > MAX_STATES_PER_BRANCH) {
|
||||
@@ -198,8 +194,8 @@ public class DataFlowRunner {
|
||||
handleStepOutOfLoop(instruction, nextInstruction, loopNumber, processedStates, incomingStates, states, after, queue);
|
||||
if (nextInstruction instanceof BranchingInstruction) {
|
||||
BranchingInstruction branching = (BranchingInstruction)nextInstruction;
|
||||
if (processedStates.get(branching).contains(state.getMemoryState()) ||
|
||||
incomingStates.get(branching).contains(state.getMemoryState())) {
|
||||
if (containsState(processedStates.get(branching), state) ||
|
||||
containsState(incomingStates.get(branching), state)) {
|
||||
continue;
|
||||
}
|
||||
if (loopNumber[branching.getIndex()] != 0) {
|
||||
@@ -221,6 +217,19 @@ public class DataFlowRunner {
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean containsState(Collection<DfaMemoryState> processed,
|
||||
DfaInstructionState instructionState) {
|
||||
if (processed.contains(instructionState.getMemoryState())) {
|
||||
return true;
|
||||
}
|
||||
for (DfaMemoryState state : processed) {
|
||||
if (((DfaMemoryStateImpl)state).isSuperStateOf((DfaMemoryStateImpl)instructionState.getMemoryState())) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void handleStepOutOfLoop(@NotNull final Instruction prevInstruction,
|
||||
@NotNull Instruction nextInstruction,
|
||||
@NotNull final int[] loopNumber,
|
||||
@@ -271,10 +280,6 @@ public class DataFlowRunner {
|
||||
return loopNumber[nextInstruction.getIndex()] == loopNumber[prevInstruction.getIndex()];
|
||||
}
|
||||
|
||||
protected boolean shouldCheckTimeLimit() {
|
||||
return myShouldCheckLimitTime && !ApplicationManager.getApplication().isUnitTestMode();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected DfaInstructionState[] acceptInstruction(@NotNull InstructionVisitor visitor, @NotNull DfaInstructionState instructionState) {
|
||||
Instruction instruction = instructionState.getInstruction();
|
||||
|
||||
+3
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -26,7 +26,6 @@ package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.instructions.Instruction;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.Processor;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
@@ -125,7 +124,8 @@ class StateQueue {
|
||||
|
||||
StateMerger merger = new StateMerger();
|
||||
while (true) {
|
||||
List<DfaMemoryStateImpl> nextStates = merger.mergeByFacts(group);
|
||||
List<DfaMemoryStateImpl> nextStates = merger.mergeByRanges(group);
|
||||
if (nextStates == null) nextStates = merger.mergeByFacts(group);
|
||||
if (nextStates == null) nextStates = merger.mergeByNullability(group);
|
||||
if (nextStates == null) nextStates = merger.mergeByUnknowns(group);
|
||||
if (nextStates == null) break;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaRelationValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
@@ -53,6 +54,11 @@ public interface DfaMemoryState {
|
||||
|
||||
ThreeState checkOptional(DfaValue value);
|
||||
|
||||
@Nullable
|
||||
LongRangeSet getRange(DfaValue value);
|
||||
|
||||
DfaValue getStringLength(DfaValue value);
|
||||
|
||||
void flushFields();
|
||||
|
||||
void flushVariable(DfaVariableValue variable);
|
||||
|
||||
+130
-10
@@ -24,15 +24,14 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.UnorderedPair;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiEnumConstant;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
@@ -40,6 +39,7 @@ import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.Stack;
|
||||
import gnu.trove.*;
|
||||
import one.util.streamex.StreamEx;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -207,12 +207,9 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
|
||||
if (!myDistinctClasses.isEmpty()) {
|
||||
result.append("\n distincts: ");
|
||||
List<String> distincts = new ArrayList<>();
|
||||
for (UnorderedPair<EqClass> pair : getDistinctClassPairs()) {
|
||||
distincts.add("{" + pair.first + ", " + pair.second + "}");
|
||||
}
|
||||
Collections.sort(distincts);
|
||||
result.append(StringUtil.join(distincts, " "));
|
||||
String distincts =
|
||||
StreamEx.of(getDistinctClassPairs()).map(pair -> "{" + pair.first + ", " + pair.second + "}").sorted().joining(" ");
|
||||
result.append(distincts);
|
||||
}
|
||||
|
||||
if (!myStack.isEmpty()) {
|
||||
@@ -361,6 +358,28 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
myIdToEqClassesIndices.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if current state describes all possible concrete program states described by {@code that} state.
|
||||
*
|
||||
* @param that a sub-state candidate
|
||||
* @return true if current state is a super-state of the supplied state.
|
||||
*/
|
||||
public boolean isSuperStateOf(DfaMemoryStateImpl that) {
|
||||
if (!equalsSuperficially(that) ||
|
||||
!equalsByUnknownVariables(that) ||
|
||||
!getNonTrivialEqClasses().equals(that.getNonTrivialEqClasses()) ||
|
||||
!that.getDistinctClassPairs().containsAll(getDistinctClassPairs())) {
|
||||
return false;
|
||||
}
|
||||
if(myVariableStates.size() != that.myVariableStates.size()) return false;
|
||||
for (Map.Entry<DfaVariableValue, DfaVariableState> entry : myVariableStates.entrySet()) {
|
||||
DfaVariableState thisState = entry.getValue();
|
||||
DfaVariableState thatState = that.myVariableStates.get(entry.getKey());
|
||||
if(Objects.equals(thisState, thatState)) continue;
|
||||
if(thatState == null || thisState == null || !thisState.isSuperStateOf(thatState)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean canBeInRelation(@NotNull DfaValue dfaValue) {
|
||||
DfaValue unwrapped = unwrap(dfaValue);
|
||||
@@ -640,6 +659,27 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
}
|
||||
}
|
||||
|
||||
void setRange(DfaVariableValue target, LongRangeSet range) {
|
||||
if (!isUnknownState(target)) {
|
||||
setVariableState(target, getVariableState(target).withRange(range));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean applyRange(LongRangeSet range, DfaVariableValue target) {
|
||||
if (!isUnknownState(target) && range != null) {
|
||||
DfaVariableState state = getVariableState(target);
|
||||
LongRangeSet oldRange = state.getRange();
|
||||
if (oldRange == null) {
|
||||
oldRange = LongRangeSet.fromType(target.getVariableType());
|
||||
if (oldRange == null) return true;
|
||||
}
|
||||
LongRangeSet newRange = oldRange.intersect(range);
|
||||
if (newRange.isEmpty()) return false;
|
||||
setVariableState(target, state.withRange(newRange));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static DfaValue unwrap(DfaValue value) {
|
||||
if (value instanceof DfaBoxedValue) {
|
||||
return ((DfaBoxedValue)value).getWrappedValue();
|
||||
@@ -683,6 +723,25 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
if (dfaLeft instanceof DfaUnknownValue || dfaRight instanceof DfaUnknownValue) return true;
|
||||
|
||||
boolean isNegated = dfaRelation.isNegated();
|
||||
|
||||
if (dfaLeft instanceof DfaVariableValue) {
|
||||
LongRangeSet right = getRange(dfaRight);
|
||||
if (right != null) {
|
||||
if (!applyRange(right.fromRelation(dfaRelation.getComparisonOperation()), (DfaVariableValue)dfaLeft)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (dfaRight instanceof DfaVariableValue) {
|
||||
LongRangeSet left = getRange(dfaLeft);
|
||||
if (left != null) {
|
||||
if (!applyRange(left.fromRelation(DfaRelationValue.getSymmetricOperation(dfaRelation.getComparisonOperation())),
|
||||
(DfaVariableValue)dfaRight)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dfaLeft instanceof DfaTypeValue && ((DfaTypeValue)dfaLeft).isNotNull() && dfaRight == myFactory.getConstFactory().getNull()) {
|
||||
return isNegated;
|
||||
}
|
||||
@@ -946,6 +1005,67 @@ public class DfaMemoryStateImpl implements DfaMemoryState {
|
||||
return value instanceof DfaOptionalValue ? ThreeState.fromBoolean(((DfaOptionalValue)value).isPresent()) : ThreeState.UNSURE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns range of possible values for given DfaValue if possible
|
||||
*
|
||||
* @param value value to get the range from
|
||||
* @return possible range or null if range is not known/non-applicable. Empty range indicates that no exact value is possible
|
||||
* for given DfaValue (likely impossible code path).
|
||||
*/
|
||||
@Nullable
|
||||
@Override
|
||||
public LongRangeSet getRange(DfaValue value) {
|
||||
if (value instanceof DfaVariableValue) {
|
||||
DfaVariableValue var = (DfaVariableValue)value;
|
||||
DfaVariableState state = getVariableState(var);
|
||||
LongRangeSet range = state.getRange();
|
||||
if (range == null) {
|
||||
DfaConstValue constValue = getConstantValue(var);
|
||||
if (constValue != null) {
|
||||
return LongRangeSet.fromConstant(constValue.getValue());
|
||||
}
|
||||
return LongRangeSet.fromType(var.getVariableType());
|
||||
}
|
||||
return range;
|
||||
}
|
||||
if (value instanceof DfaRangeValue) {
|
||||
return ((DfaRangeValue)value).getValue();
|
||||
}
|
||||
if (value instanceof DfaConstValue) {
|
||||
return LongRangeSet.fromConstant(((DfaConstValue)value).getValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public DfaValue getStringLength(DfaValue value) {
|
||||
if (value instanceof DfaVariableValue) {
|
||||
DfaVariableValue variableValue = (DfaVariableValue)value;
|
||||
DfaConstValue constValue = getConstantValue(variableValue);
|
||||
if(constValue != null) {
|
||||
value = constValue;
|
||||
} else {
|
||||
PsiType type = variableValue.getVariableType();
|
||||
if (type != null && type.equalsToText(CommonClassNames.JAVA_LANG_STRING)) {
|
||||
PsiClass psiClass = PsiUtil.resolveClassInClassTypeOnly(type);
|
||||
if (psiClass != null) {
|
||||
PsiMethod[] lengthMethods = psiClass.findMethodsByName("length", false);
|
||||
if (lengthMethods.length == 1) {
|
||||
return getFactory().getVarFactory().createVariableValue(lengthMethods[0], PsiType.INT, false, variableValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(value instanceof DfaConstValue) {
|
||||
Object str = ((DfaConstValue)value).getValue();
|
||||
if(str instanceof String) {
|
||||
return getFactory().getRangeFactory().create(LongRangeSet.point(((String)str).length()));
|
||||
}
|
||||
}
|
||||
return DfaUnknownValue.getInstance();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DfaRelationValue compareToNull(DfaValue dfaVar, boolean negated) {
|
||||
DfaConstValue dfaNull = myFactory.getConstFactory().getNull();
|
||||
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -89,6 +89,10 @@ public class DfaOptionalSupport {
|
||||
return null;
|
||||
}
|
||||
|
||||
static boolean isOptionalGetMethodName(String name) {
|
||||
return "get".equals(name) || "getAsDouble".equals(name) || "getAsInt".equals(name) || "getAsLong".equals(name);
|
||||
}
|
||||
|
||||
private static class ReplaceOptionalCallFix implements LocalQuickFix {
|
||||
private final String myTargetMethodName;
|
||||
private final boolean myClearArguments;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -209,7 +209,7 @@ public class DfaPsiUtil {
|
||||
public Result<Set<PsiField>> compute() {
|
||||
final PsiCodeBlock body = constructor.getBody();
|
||||
final Map<PsiField, Boolean> map = ContainerUtil.newHashMap();
|
||||
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false, false, false) {
|
||||
final StandardDataFlowRunner dfaRunner = new StandardDataFlowRunner(false, false) {
|
||||
|
||||
private boolean isCallExposingNonInitializedFields(Instruction instruction) {
|
||||
if (!(instruction instanceof MethodCallInstruction) ||
|
||||
|
||||
@@ -39,7 +39,6 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
* @author Gregory.Shrago
|
||||
*/
|
||||
public class DfaUtil {
|
||||
|
||||
@Nullable("null means DFA analysis has failed (too complex to analyze)")
|
||||
public static Collection<PsiExpression> getCachedVariableValues(@Nullable final PsiVariable variable, @Nullable final PsiElement context) {
|
||||
if (variable == null || context == null) return Collections.emptyList();
|
||||
@@ -53,7 +52,7 @@ public class DfaUtil {
|
||||
ValuableInstructionVisitor.PlaceResult placeResult = value.get(context);
|
||||
final Collection<FList<PsiExpression>> concatenations = placeResult == null ? null : placeResult.myValues.get(variable);
|
||||
if (concatenations != null) {
|
||||
return ContainerUtil.map(concatenations, expressions -> concatenateExpressions(expressions));
|
||||
return ContainerUtil.map(concatenations, DfaUtil::concatenateExpressions);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -83,23 +82,23 @@ public class DfaUtil {
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Collection<? extends PsiElement> getPossibleInitializationElements(final PsiElement qualifierExpression) {
|
||||
public static Collection<PsiExpression> getPossibleInitializationElements(@NotNull PsiElement qualifierExpression) {
|
||||
if (qualifierExpression instanceof PsiMethodCallExpression) {
|
||||
return Collections.singletonList(qualifierExpression);
|
||||
return Collections.singletonList((PsiMethodCallExpression)qualifierExpression);
|
||||
}
|
||||
if (qualifierExpression instanceof PsiReferenceExpression) {
|
||||
final PsiElement targetElement = ((PsiReferenceExpression)qualifierExpression).resolve();
|
||||
if (!(targetElement instanceof PsiVariable)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final Collection<? extends PsiElement> variableValues = getCachedVariableValues((PsiVariable)targetElement, qualifierExpression);
|
||||
Collection<PsiExpression> variableValues = getCachedVariableValues((PsiVariable)targetElement, qualifierExpression);
|
||||
if (variableValues == null || variableValues.isEmpty()) {
|
||||
return DfaPsiUtil.getVariableAssignmentsInFile((PsiVariable)targetElement, false, qualifierExpression);
|
||||
}
|
||||
return variableValues;
|
||||
}
|
||||
if (qualifierExpression instanceof PsiLiteralExpression) {
|
||||
return Collections.singletonList(qualifierExpression);
|
||||
return Collections.singletonList((PsiLiteralExpression)qualifierExpression);
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
@@ -180,10 +179,7 @@ public class DfaUtil {
|
||||
public DfaInstructionState[] visitPush(PushInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
PsiExpression place = instruction.getPlace();
|
||||
if (place != null) {
|
||||
PlaceResult result = myResults.get(place);
|
||||
if (result == null) {
|
||||
myResults.put(place, result = new PlaceResult());
|
||||
}
|
||||
PlaceResult result = myResults.computeIfAbsent(place, __ -> new PlaceResult());
|
||||
final Map<DfaVariableValue,DfaVariableState> map = ((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableStates();
|
||||
for (Map.Entry<DfaVariableValue, DfaVariableState> entry : map.entrySet()) {
|
||||
ValuableDataFlowRunner.ValuableDfaVariableState state = (ValuableDataFlowRunner.ValuableDfaVariableState)entry.getValue();
|
||||
|
||||
+61
-20
@@ -24,44 +24,68 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaTypeValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaVariableValue;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.*;
|
||||
|
||||
class DfaVariableState {
|
||||
@NotNull final Set<DfaPsiType> myInstanceofValues;
|
||||
@NotNull final Set<DfaPsiType> myNotInstanceofValues;
|
||||
@NotNull final Nullness myNullability;
|
||||
@NotNull final ThreeState myOptionalPresence;
|
||||
@Nullable final LongRangeSet myRange;
|
||||
private final int myHash;
|
||||
|
||||
DfaVariableState(@NotNull DfaVariableValue dfaVar) {
|
||||
this(Collections.emptySet(), Collections.emptySet(), dfaVar.getInherentNullability(), ThreeState.UNSURE);
|
||||
this(Collections.emptySet(), Collections.emptySet(), dfaVar.getInherentNullability(), ThreeState.UNSURE, getInitialRange(dfaVar));
|
||||
}
|
||||
|
||||
public boolean isSuperStateOf(DfaVariableState that) {
|
||||
if(!myNotInstanceofValues.equals(that.myNotInstanceofValues)) return false;
|
||||
if(!myInstanceofValues.equals(that.myNotInstanceofValues)) return false;
|
||||
if(!myNullability.equals(that.myNullability)) return false;
|
||||
if(!myOptionalPresence.equals(that.myOptionalPresence)) return false;
|
||||
if(Objects.equals(myRange, that.myRange)) return true;
|
||||
return myRange != null && that.myRange != null && myRange.contains(that.myRange);
|
||||
}
|
||||
|
||||
private static LongRangeSet getInitialRange(DfaVariableValue var) {
|
||||
DfaVariableValue qualifier = var.getQualifier();
|
||||
if(qualifier != null) {
|
||||
PsiModifierListOwner owner = var.getPsiVariable();
|
||||
boolean stringLength = owner instanceof PsiMethod && MethodUtils.isStringLength((PsiMethod)owner);
|
||||
boolean arrayLength =
|
||||
owner instanceof PsiField && "length".equals(((PsiField)owner).getName()) && qualifier.getVariableType() instanceof PsiArrayType;
|
||||
if(stringLength || arrayLength) {
|
||||
return LongRangeSet.indexRange();
|
||||
}
|
||||
}
|
||||
return LongRangeSet.fromType(var.getVariableType());
|
||||
}
|
||||
|
||||
DfaVariableState(@NotNull Set<DfaPsiType> instanceofValues,
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull Nullness nullability,
|
||||
@NotNull ThreeState optionalPresence) {
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull Nullness nullability,
|
||||
@NotNull ThreeState optionalPresence,
|
||||
@Nullable LongRangeSet range) {
|
||||
myInstanceofValues = instanceofValues;
|
||||
myNotInstanceofValues = notInstanceofValues;
|
||||
myNullability = nullability;
|
||||
myOptionalPresence = optionalPresence;
|
||||
myHash = ((myInstanceofValues.hashCode() * 31 + myNotInstanceofValues.hashCode()) * 31 + myNullability.hashCode()) * 31 +
|
||||
myOptionalPresence.hashCode();
|
||||
myRange = range;
|
||||
myHash = Objects.hash(myInstanceofValues, myNotInstanceofValues, myNullability, myOptionalPresence, range);
|
||||
}
|
||||
|
||||
public boolean isNullable() {
|
||||
@@ -101,7 +125,7 @@ class DfaVariableState {
|
||||
HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.removeAll(moreGeneric);
|
||||
newInstanceof.add(dfaType.getDfaType());
|
||||
result = createCopy(newInstanceof, myNotInstanceofValues, result.myNullability, myOptionalPresence);
|
||||
result = createCopy(newInstanceof, myNotInstanceofValues, result.myNullability, myOptionalPresence, myRange);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -129,7 +153,7 @@ class DfaVariableState {
|
||||
HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
|
||||
newNotInstanceof.removeAll(moreSpecific);
|
||||
newNotInstanceof.add(dfaType.getDfaType());
|
||||
return createCopy(myInstanceofValues, newNotInstanceof, myNullability, myOptionalPresence);
|
||||
return createCopy(myInstanceofValues, newNotInstanceof, myNullability, myOptionalPresence, myRange);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -137,12 +161,12 @@ class DfaVariableState {
|
||||
if (myInstanceofValues.contains(type)) {
|
||||
HashSet<DfaPsiType> newInstanceof = ContainerUtil.newHashSet(myInstanceofValues);
|
||||
newInstanceof.remove(type);
|
||||
return createCopy(newInstanceof, myNotInstanceofValues, myNullability, myOptionalPresence);
|
||||
return createCopy(newInstanceof, myNotInstanceofValues, myNullability, myOptionalPresence, myRange);
|
||||
}
|
||||
if (myNotInstanceofValues.contains(type)) {
|
||||
HashSet<DfaPsiType> newNotInstanceof = ContainerUtil.newHashSet(myNotInstanceofValues);
|
||||
newNotInstanceof.remove(type);
|
||||
return createCopy(myInstanceofValues, newNotInstanceof, myNullability, myOptionalPresence);
|
||||
return createCopy(myInstanceofValues, newNotInstanceof, myNullability, myOptionalPresence, myRange);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@@ -159,14 +183,17 @@ class DfaVariableState {
|
||||
myNullability == aState.myNullability &&
|
||||
myOptionalPresence == aState.myOptionalPresence &&
|
||||
myInstanceofValues.equals(aState.myInstanceofValues) &&
|
||||
myNotInstanceofValues.equals(aState.myNotInstanceofValues);
|
||||
myNotInstanceofValues.equals(aState.myNotInstanceofValues) &&
|
||||
Objects.equals(myRange, aState.myRange);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
protected DfaVariableState createCopy(@NotNull Set<DfaPsiType> instanceofValues,
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull Nullness nullability, ThreeState optionalPresent) {
|
||||
return new DfaVariableState(instanceofValues, notInstanceofValues, nullability, optionalPresent);
|
||||
@NotNull Nullness nullability,
|
||||
ThreeState optionalPresent,
|
||||
LongRangeSet range) {
|
||||
return new DfaVariableState(instanceofValues, notInstanceofValues, nullability, optionalPresent, range);
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
@@ -184,6 +211,9 @@ class DfaVariableState {
|
||||
if (myOptionalPresence != ThreeState.UNSURE) {
|
||||
buf.append(myOptionalPresence == ThreeState.YES ? " Optional with value" : " empty Optional");
|
||||
}
|
||||
if (myRange != null) {
|
||||
buf.append(" ").append(myRange);
|
||||
}
|
||||
return buf.toString();
|
||||
}
|
||||
|
||||
@@ -198,7 +228,7 @@ class DfaVariableState {
|
||||
|
||||
@NotNull
|
||||
DfaVariableState withNullability(@NotNull Nullness nullness) {
|
||||
return myNullability == nullness ? this : createCopy(myInstanceofValues, myNotInstanceofValues, nullness, myOptionalPresence);
|
||||
return myNullability == nullness ? this : createCopy(myInstanceofValues, myNotInstanceofValues, nullness, myOptionalPresence, myRange);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -209,10 +239,16 @@ class DfaVariableState {
|
||||
DfaVariableState withOptionalPresense(final boolean presense) {
|
||||
ThreeState optionalPresent = ThreeState.fromBoolean(presense);
|
||||
return myOptionalPresence != optionalPresent
|
||||
? createCopy(myInstanceofValues, myNotInstanceofValues, myNullability, optionalPresent)
|
||||
? createCopy(myInstanceofValues, myNotInstanceofValues, myNullability, optionalPresent, myRange)
|
||||
: this;
|
||||
}
|
||||
|
||||
DfaVariableState withRange(@Nullable LongRangeSet range) {
|
||||
return Objects.equals(range, myRange)
|
||||
? this
|
||||
: createCopy(myInstanceofValues, myNotInstanceofValues, myNullability, myOptionalPresence, range);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaVariableState withValue(DfaValue value) {
|
||||
return this;
|
||||
@@ -234,4 +270,9 @@ class DfaVariableState {
|
||||
public ThreeState getOptionalPresense() {
|
||||
return myOptionalPresence;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public LongRangeSet getRange() {
|
||||
return myRange;
|
||||
}
|
||||
}
|
||||
|
||||
+53
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,14 +15,18 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.lang.injection.InjectedLanguageManager;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.ExpressionUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
@@ -33,6 +37,43 @@ import static com.intellij.codeInspection.dataFlow.MethodContract.createConstrai
|
||||
* @author peter
|
||||
*/
|
||||
public class HardcodedContracts {
|
||||
static class OptionalPresenceContract extends MethodContract.QualifierBasedContract {
|
||||
private final boolean myPresent;
|
||||
|
||||
public OptionalPresenceContract(boolean mustPresent, ValueConstraint[] valueConstraints, ValueConstraint returnValue) {
|
||||
super(valueConstraints, returnValue);
|
||||
myPresent = mustPresent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass() || !super.equals(o)) return false;
|
||||
return myPresent == ((OptionalPresenceContract)o).myPresent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 31 * super.hashCode() + (myPresent ? 1 : 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean applyContract(boolean matches, DfaValue qualifier, DfaMemoryState memoryState) {
|
||||
boolean present = !matches ^ myPresent;
|
||||
ThreeState state = memoryState.checkOptional(qualifier);
|
||||
if(state == ThreeState.fromBoolean(!present)) return false;
|
||||
if(state == ThreeState.UNSURE) {
|
||||
memoryState.applyIsPresentCheck(present, qualifier);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "[" + (myPresent ? "present" : "absent") + "] " + super.toString();
|
||||
}
|
||||
}
|
||||
|
||||
public static List<MethodContract> getHardcodedContracts(@NotNull PsiMethod method, @Nullable PsiMethodCallExpression call) {
|
||||
PsiClass owner = method.getContainingClass();
|
||||
if (owner == null ||
|
||||
@@ -85,6 +126,17 @@ public class HardcodedContracts {
|
||||
className.startsWith("org.assertj.core.api.")) {
|
||||
return handleTestFrameworks(paramCount, className, methodName, call);
|
||||
}
|
||||
else if (TypeUtils.isOptional(owner)) {
|
||||
MethodContract.ValueConstraint[] constraints = createConstraintArray(paramCount);
|
||||
if (DfaOptionalSupport.isOptionalGetMethodName(methodName) || "orElseThrow".equals(methodName)) {
|
||||
return Arrays.asList(new OptionalPresenceContract(false, constraints, THROW_EXCEPTION),
|
||||
new OptionalPresenceContract(true, constraints, NOT_NULL_VALUE));
|
||||
}
|
||||
else if ("isPresent".equals(methodName)) {
|
||||
return Arrays.asList(new OptionalPresenceContract(false, constraints, FALSE_VALUE),
|
||||
new OptionalPresenceContract(true, constraints, TRUE_VALUE));
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
+24
-2
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2014 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaConstValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
@@ -24,6 +25,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* @author peter
|
||||
@@ -49,7 +51,7 @@ public class MethodContract {
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (this == o) return true;
|
||||
if (!(o instanceof MethodContract)) return false;
|
||||
if (o == null || o.getClass() != getClass()) return false;
|
||||
|
||||
MethodContract contract = (MethodContract)o;
|
||||
|
||||
@@ -74,6 +76,13 @@ public class MethodContract {
|
||||
return StringUtil.join(arguments, constraint -> constraint.toString(), ", ") + " -> " + returnValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if this contract result does not depend on arguments
|
||||
*/
|
||||
boolean isTrivial() {
|
||||
return Arrays.stream(this.arguments).allMatch(Predicate.isEqual(ValueConstraint.ANY_VALUE));
|
||||
}
|
||||
|
||||
public enum ValueConstraint {
|
||||
ANY_VALUE("_"), NULL_VALUE("null"), NOT_NULL_VALUE("!null"), TRUE_VALUE("true"), FALSE_VALUE("false"), THROW_EXCEPTION("fail");
|
||||
private final String myPresentableName;
|
||||
@@ -138,4 +147,17 @@ public class MethodContract {
|
||||
}
|
||||
}
|
||||
|
||||
abstract static class QualifierBasedContract extends MethodContract {
|
||||
public QualifierBasedContract(@NotNull ValueConstraint[] arguments,
|
||||
@NotNull ValueConstraint returnValue) {
|
||||
super(arguments, returnValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean isTrivial() {
|
||||
return false;
|
||||
}
|
||||
|
||||
abstract boolean applyContract(boolean matches, DfaValue qualifier, DfaMemoryState memoryState);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2016 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -52,8 +52,8 @@ class NullParameterConstraintChecker extends DataFlowRunner {
|
||||
private final Set<PsiParameter> myPossiblyViolatedParameters;
|
||||
private final Set<PsiParameter> myUsedParameters;
|
||||
|
||||
private NullParameterConstraintChecker(Collection<PsiParameter> parameters, boolean isOnTheFly) {
|
||||
super(false, true, isOnTheFly);
|
||||
private NullParameterConstraintChecker(Collection<PsiParameter> parameters) {
|
||||
super(false, true);
|
||||
myPossiblyViolatedParameters = new THashSet<>(parameters);
|
||||
myUsedParameters = new THashSet<>();
|
||||
}
|
||||
@@ -75,7 +75,7 @@ class NullParameterConstraintChecker extends DataFlowRunner {
|
||||
}
|
||||
if (nullableParameters.isEmpty()) return PsiParameter.EMPTY_ARRAY;
|
||||
|
||||
final NullParameterConstraintChecker checker = new NullParameterConstraintChecker(nullableParameters, true);
|
||||
final NullParameterConstraintChecker checker = new NullParameterConstraintChecker(nullableParameters);
|
||||
checker.analyzeMethod(method.getBody(), new StandardInstructionVisitor());
|
||||
|
||||
return checker.myPossiblyViolatedParameters.stream().filter(checker.myUsedParameters::contains).toArray(PsiParameter[]::new);
|
||||
@@ -130,7 +130,7 @@ class NullParameterConstraintChecker extends DataFlowRunner {
|
||||
super(factory);
|
||||
for (PsiParameter parameter : myPossiblyViolatedParameters) {
|
||||
setVariableState(getFactory().getVarFactory().createVariableValue(parameter, false),
|
||||
new DfaVariableState(Collections.emptySet(), Collections.emptySet(), Nullness.NULLABLE, ThreeState.UNSURE));
|
||||
new DfaVariableState(Collections.emptySet(), Collections.emptySet(), Nullness.NULLABLE, ThreeState.UNSURE, null));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -27,9 +27,9 @@ package com.intellij.codeInspection.dataFlow;
|
||||
public class StandardDataFlowRunner extends DataFlowRunner {
|
||||
|
||||
public StandardDataFlowRunner() {
|
||||
this(false, true, false);
|
||||
this(false, true);
|
||||
}
|
||||
public StandardDataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers, boolean shouldCheckLimitTime) {
|
||||
super(unknownMembersAreNullable, honorFieldInitializers, shouldCheckLimitTime);
|
||||
public StandardDataFlowRunner(boolean unknownMembersAreNullable, boolean honorFieldInitializers) {
|
||||
super(unknownMembersAreNullable, honorFieldInitializers);
|
||||
}
|
||||
}
|
||||
|
||||
+152
-96
@@ -15,7 +15,10 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.codeInspection.dataFlow.MethodContract.QualifierBasedContract;
|
||||
import com.intellij.codeInspection.dataFlow.instructions.*;
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
@@ -25,10 +28,12 @@ import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.psi.util.TypeConversionUtil;
|
||||
import com.intellij.util.ObjectUtils;
|
||||
import com.intellij.util.ThreeState;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.FactoryMap;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.callMatcher.CallMapper;
|
||||
import com.siyeh.ig.callMatcher.CallMatcher;
|
||||
import com.siyeh.ig.psiutils.ComparisonUtils;
|
||||
import com.siyeh.ig.psiutils.TypeUtils;
|
||||
import gnu.trove.THashSet;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -42,11 +47,19 @@ import static com.intellij.psi.JavaTokenType.*;
|
||||
* @author peter
|
||||
*/
|
||||
public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
private static final Set<String> OPTIONAL_METHOD_NAMES =
|
||||
ContainerUtil.set("isPresent", "of", "ofNullable", "fromNullable", "empty", "absent",
|
||||
"or", "orElseGet", "ifPresent", "map", "flatMap", "filter", "transform");
|
||||
private static final Logger LOG = Logger.getInstance("#com.intellij.codeInspection.dataFlow.StandardInstructionVisitor");
|
||||
private static final Object ANY_VALUE = new Object();
|
||||
|
||||
private static final Set<String> OPTIONAL_METHOD_NAMES = ContainerUtil
|
||||
.set("of", "ofNullable", "fromNullable", "empty", "absent", "or", "orElseGet", "ifPresent", "map", "flatMap", "filter", "transform");
|
||||
private static final CallMapper<LongRangeSet> KNOWN_METHOD_RANGES = new CallMapper<LongRangeSet>()
|
||||
.register(CallMatcher.instanceCall("java.time.LocalDateTime", "getHour"), LongRangeSet.range(0, 23))
|
||||
.register(CallMatcher.instanceCall("java.time.LocalDateTime", "getMinute", "getSecond"), LongRangeSet.range(0, 59))
|
||||
.register(CallMatcher.staticCall(CommonClassNames.JAVA_LANG_LONG, "numberOfLeadingZeros", "numberOfTrailingZeros", "bitCount"),
|
||||
LongRangeSet.range(0, Long.SIZE))
|
||||
.register(CallMatcher.staticCall(CommonClassNames.JAVA_LANG_INTEGER, "numberOfLeadingZeros", "numberOfTrailingZeros", "bitCount"),
|
||||
LongRangeSet.range(0, Integer.SIZE));
|
||||
|
||||
private final Set<BinopInstruction> myReachable = new THashSet<>();
|
||||
private final Set<BinopInstruction> myCanBeNullInInstanceof = new THashSet<>();
|
||||
private final MultiMap<PushInstruction, Object> myPossibleVariableValues = MultiMap.createSet();
|
||||
@@ -185,15 +198,16 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
public DfaInstructionState[] visitMethodCall(final MethodCallInstruction instruction, final DataFlowRunner runner, final DfaMemoryState memState) {
|
||||
Set<DfaMemoryState> finalStates = ContainerUtil.newLinkedHashSet();
|
||||
finalStates.addAll(handleOptionalMethods(instruction, runner, memState));
|
||||
finalStates.addAll(handleKnownMethods(instruction, runner, memState));
|
||||
|
||||
if (finalStates.isEmpty()) {
|
||||
DfaValue[] argValues = popCallArguments(instruction, runner, memState);
|
||||
DfaValue[] argValues = popCallArguments(instruction, runner, memState, true);
|
||||
final DfaValue qualifier = popQualifier(instruction, runner, memState);
|
||||
|
||||
LinkedHashSet<DfaMemoryState> currentStates = ContainerUtil.newLinkedHashSet(memState);
|
||||
if (argValues != null) {
|
||||
for (MethodContract contract : instruction.getContracts()) {
|
||||
currentStates = addContractResults(argValues, contract, currentStates, instruction, runner.getFactory(), finalStates);
|
||||
currentStates = addContractResults(qualifier, argValues, contract, currentStates, instruction, runner.getFactory(), finalStates);
|
||||
if (currentStates.size() + finalStates.size() > DataFlowRunner.MAX_STATES_PER_BRANCH) {
|
||||
if (LOG.isDebugEnabled()) {
|
||||
LOG.debug("Too complex contract on " + instruction.getContext() + ", skipping contract processing");
|
||||
@@ -221,6 +235,23 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return result;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<DfaMemoryState> handleKnownMethods(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
PsiMethodCallExpression call = ObjectUtils.tryCast(instruction.getCallExpression(), PsiMethodCallExpression.class);
|
||||
CustomMethodHandlers.CustomMethodHandler handler = CustomMethodHandlers.find(call);
|
||||
if (handler == null) return Collections.emptyList();
|
||||
DfaValue[] arguments = popCallArguments(instruction, runner, memState, false);
|
||||
DfaValue qualifier = popQualifier(instruction, runner, memState);
|
||||
List<DfaMemoryState> states =
|
||||
arguments == null ? Collections.emptyList() :
|
||||
handler.handle(qualifier, arguments, memState, runner.getFactory());
|
||||
if (states.isEmpty()) {
|
||||
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
|
||||
return Collections.singletonList(memState);
|
||||
}
|
||||
return states;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private List<DfaMemoryState> handleOptionalMethods(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
PsiMethodCallExpression call = ObjectUtils.tryCast(instruction.getCallExpression(), PsiMethodCallExpression.class);
|
||||
@@ -230,37 +261,20 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method == null || !TypeUtils.isOptional(method.getContainingClass())) return Collections.emptyList();
|
||||
List<DfaMemoryState> closures = runner.getStackTopClosures();
|
||||
DfaValue[] argValues = popCallArguments(instruction, runner, memState);
|
||||
final DfaValue qualifier = popQualifier(instruction, runner, memState);
|
||||
DfaValue[] argValues = popCallArguments(instruction, runner, memState, false);
|
||||
DfaValue qualifier = popQualifier(instruction, runner, memState);
|
||||
DfaValue result = null;
|
||||
switch (methodName) {
|
||||
case "isPresent": {
|
||||
ThreeState state = memState.checkOptional(qualifier);
|
||||
DfaConstValue.Factory constFactory = runner.getFactory().getConstFactory();
|
||||
if (state == ThreeState.UNSURE) {
|
||||
DfaMemoryState falseState = memState.createCopy();
|
||||
memState.push(constFactory.getTrue());
|
||||
memState.applyIsPresentCheck(true, qualifier);
|
||||
falseState.push(constFactory.getFalse());
|
||||
falseState.applyIsPresentCheck(false, qualifier);
|
||||
return Arrays.asList(memState, falseState);
|
||||
}
|
||||
else {
|
||||
memState.push(state == ThreeState.YES ? constFactory.getTrue() : constFactory.getFalse());
|
||||
}
|
||||
break;
|
||||
}
|
||||
case "of":
|
||||
case "ofNullable":
|
||||
case "fromNullable":
|
||||
if ("of".equals(methodName) || (argValues != null && argValues.length == 1 && memState.isNotNull(argValues[0]))) {
|
||||
memState.push(runner.getFactory().getOptionalFactory().getOptional(true));
|
||||
} else {
|
||||
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
|
||||
result = runner.getFactory().getOptionalFactory().getOptional(true);
|
||||
}
|
||||
break;
|
||||
case "empty":
|
||||
case "absent":
|
||||
memState.push(runner.getFactory().getOptionalFactory().getOptional(false));
|
||||
result = runner.getFactory().getOptionalFactory().getOptional(false);
|
||||
break;
|
||||
case "filter":
|
||||
case "flatMap":
|
||||
@@ -272,23 +286,24 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
for (DfaMemoryState closure : closures) {
|
||||
closure.applyIsPresentCheck(!methodName.startsWith("or"), qualifier);
|
||||
}
|
||||
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
|
||||
break;
|
||||
default:
|
||||
memState.push(getMethodResultValue(instruction, qualifier, runner.getFactory()));
|
||||
break;
|
||||
}
|
||||
memState.push(result == null ? getMethodResultValue(instruction, qualifier, runner.getFactory()) : result);
|
||||
return Collections.singletonList(memState);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private DfaValue[] popCallArguments(MethodCallInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
@Nullable
|
||||
private DfaValue[] popCallArguments(MethodCallInstruction instruction,
|
||||
DataFlowRunner runner,
|
||||
DfaMemoryState memState,
|
||||
boolean contractOnly) {
|
||||
final PsiExpression[] args = instruction.getArgs();
|
||||
|
||||
PsiMethod method = instruction.getTargetMethod();
|
||||
boolean varargCall = instruction.isVarArgCall();
|
||||
DfaValue[] argValues;
|
||||
if (method == null || instruction.getContracts().isEmpty()) {
|
||||
if (method == null || (contractOnly && instruction.getContracts().isEmpty())) {
|
||||
argValues = null;
|
||||
} else {
|
||||
PsiParameterList paramList = method.getParameterList();
|
||||
@@ -335,12 +350,13 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return qualifier;
|
||||
}
|
||||
|
||||
private LinkedHashSet<DfaMemoryState> addContractResults(DfaValue[] argValues,
|
||||
MethodContract contract,
|
||||
LinkedHashSet<DfaMemoryState> states,
|
||||
MethodCallInstruction instruction,
|
||||
DfaValueFactory factory,
|
||||
Set<DfaMemoryState> finalStates) {
|
||||
private LinkedHashSet<DfaMemoryState> addContractResults(DfaValue qualifier,
|
||||
DfaValue[] argValues,
|
||||
MethodContract contract,
|
||||
LinkedHashSet<DfaMemoryState> states,
|
||||
MethodCallInstruction instruction,
|
||||
DfaValueFactory factory,
|
||||
Set<DfaMemoryState> finalStates) {
|
||||
DfaConstValue.Factory constFactory = factory.getConstFactory();
|
||||
LinkedHashSet<DfaMemoryState> falseStates = ContainerUtil.newLinkedHashSet();
|
||||
for (int i = 0; i < argValues.length; i++) {
|
||||
@@ -388,6 +404,21 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
states = nextStates;
|
||||
}
|
||||
|
||||
if (contract instanceof QualifierBasedContract) {
|
||||
LinkedHashSet<DfaMemoryState> nextStates = ContainerUtil.newLinkedHashSet();
|
||||
QualifierBasedContract qualifierBasedContract = (QualifierBasedContract)contract;
|
||||
for (DfaMemoryState state : states) {
|
||||
DfaMemoryState falseCopy = state.createCopy();
|
||||
if (qualifierBasedContract.applyContract(true, qualifier, state)) {
|
||||
nextStates.add(state);
|
||||
}
|
||||
if (qualifierBasedContract.applyContract(false, qualifier, falseCopy)) {
|
||||
falseStates.add(falseCopy);
|
||||
}
|
||||
}
|
||||
states = nextStates;
|
||||
}
|
||||
|
||||
for (DfaMemoryState state : states) {
|
||||
state.push(getDfaContractReturnValue(contract, instruction, factory));
|
||||
finalStates.add(state);
|
||||
@@ -451,6 +482,23 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
}
|
||||
return factory.createTypeValue(type, nullability);
|
||||
}
|
||||
DfaRangeValue rangeValue = factory.getRangeFactory().create(type);
|
||||
if (rangeValue != null) {
|
||||
PsiCall call = instruction.getCallExpression();
|
||||
if (call instanceof PsiMethodCallExpression) {
|
||||
LongRangeSet range = KNOWN_METHOD_RANGES.mapFirst((PsiMethodCallExpression)call);
|
||||
if (range == null) {
|
||||
PsiMethod method = call.resolveMethod();
|
||||
if (method != null && AnnotationUtil.isAnnotated(method, "javax.annotation.Nonnegative", false)) {
|
||||
range = LongRangeSet.range(0, Long.MAX_VALUE);
|
||||
}
|
||||
}
|
||||
if (range != null) {
|
||||
return rangeValue.intersect(range);
|
||||
}
|
||||
}
|
||||
return rangeValue;
|
||||
}
|
||||
return DfaUnknownValue.getInstance();
|
||||
}
|
||||
|
||||
@@ -474,28 +522,35 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaValue dfaLeft = memState.pop();
|
||||
|
||||
final IElementType opSign = instruction.getOperationSign();
|
||||
if (opSign != null) {
|
||||
if (ComparisonUtils.isComparisonOperation(opSign) || opSign == INSTANCEOF_KEYWORD) {
|
||||
DfaInstructionState[] states = handleConstantComparison(instruction, runner, memState, dfaRight, dfaLeft, opSign);
|
||||
if (states == null) {
|
||||
states = handleRangeComparison(instruction, runner, memState, dfaRight, dfaLeft, opSign);
|
||||
}
|
||||
if (states == null) {
|
||||
states = handleRelationBinop(instruction, runner, memState, dfaRight, dfaLeft);
|
||||
}
|
||||
if (states != null) {
|
||||
return states;
|
||||
}
|
||||
|
||||
if (PLUS == opSign) {
|
||||
memState.push(instruction.getNonNullStringValue(runner.getFactory()));
|
||||
}
|
||||
else {
|
||||
if (instruction instanceof InstanceofInstruction) {
|
||||
handleInstanceof((InstanceofInstruction)instruction, dfaRight, dfaLeft);
|
||||
}
|
||||
memState.push(DfaUnknownValue.getInstance());
|
||||
}
|
||||
DfaValue result = null;
|
||||
if (AND == opSign) {
|
||||
LongRangeSet left = memState.getRange(dfaLeft);
|
||||
LongRangeSet right = memState.getRange(dfaRight);
|
||||
if(left != null && right != null) {
|
||||
result = runner.getFactory().getRangeFactory().create(left.bitwiseAnd(right));
|
||||
}
|
||||
}
|
||||
else if (PLUS == opSign) {
|
||||
result = instruction.getNonNullStringValue(runner.getFactory());
|
||||
}
|
||||
else {
|
||||
memState.push(DfaUnknownValue.getInstance());
|
||||
if (instruction instanceof InstanceofInstruction) {
|
||||
handleInstanceof((InstanceofInstruction)instruction, dfaRight, dfaLeft);
|
||||
}
|
||||
}
|
||||
memState.push(result == null ? DfaUnknownValue.getInstance() : result);
|
||||
|
||||
instruction.setTrueReachable(); // Not a branching instruction actually.
|
||||
instruction.setFalseReachable();
|
||||
@@ -557,6 +612,27 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
myUsefulInstanceofs.add(instruction);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DfaInstructionState[] handleRangeComparison(BinopInstruction instruction,
|
||||
DataFlowRunner runner,
|
||||
DfaMemoryState state,
|
||||
DfaValue right,
|
||||
DfaValue left, IElementType sign) {
|
||||
LongRangeSet leftRange = state.getRange(left);
|
||||
if (leftRange == null) return null;
|
||||
LongRangeSet rightRange = state.getRange(right);
|
||||
if (rightRange == null) return null;
|
||||
LongRangeSet constraint = rightRange.fromRelation(sign);
|
||||
if (constraint != null && !constraint.intersects(leftRange)) {
|
||||
return alwaysFalse(instruction, runner, state);
|
||||
}
|
||||
LongRangeSet revConstraint = rightRange.fromRelation(ComparisonUtils.getNegatedComparisonTokenType(sign));
|
||||
if (revConstraint != null && !revConstraint.intersects(leftRange)) {
|
||||
return alwaysTrue(instruction, runner, state);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DfaInstructionState[] handleConstantComparison(BinopInstruction instruction,
|
||||
DataFlowRunner runner,
|
||||
@@ -608,26 +684,11 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
DfaMemoryState memState,
|
||||
DfaVariableValue var,
|
||||
IElementType opSign, Number comparedWith) {
|
||||
Object knownValue = getKnownNumberValue(memState, var);
|
||||
Number knownValue = getKnownNumberValue(memState, var);
|
||||
if (knownValue != null) {
|
||||
return checkComparisonWithKnownValue(instruction, runner, memState, opSign, (Number)knownValue, comparedWith);
|
||||
return checkComparisonWithKnownValue(instruction, runner, memState, opSign, knownValue, comparedWith);
|
||||
}
|
||||
|
||||
PsiType varType = var.getVariableType();
|
||||
if (!(varType instanceof PsiPrimitiveType)) return null;
|
||||
|
||||
if (PsiType.FLOAT.equals(varType) || PsiType.DOUBLE.equals(varType)) return null;
|
||||
|
||||
double minValue = PsiType.BYTE.equals(varType) ? Byte.MIN_VALUE : PsiType.SHORT.equals(varType)
|
||||
? Short.MIN_VALUE : PsiType.INT.equals(varType)
|
||||
? Integer.MIN_VALUE : PsiType.CHAR.equals(varType) ? Character.MIN_VALUE :
|
||||
Long.MIN_VALUE;
|
||||
double maxValue = PsiType.BYTE.equals(varType) ? Byte.MAX_VALUE : PsiType.SHORT.equals(varType)
|
||||
? Short.MAX_VALUE : PsiType.INT.equals(varType)
|
||||
? Integer.MAX_VALUE : PsiType.CHAR.equals(varType) ? Character.MAX_VALUE :
|
||||
Long.MAX_VALUE;
|
||||
|
||||
return checkComparisonWithKnownRange(instruction, runner, memState, opSign, comparedWith, minValue, maxValue);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -642,7 +703,28 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
IElementType opSign,
|
||||
Number leftValue,
|
||||
Number rightValue) {
|
||||
return checkComparisonWithKnownRange(instruction, runner, memState, opSign, rightValue, leftValue, leftValue);
|
||||
int cmp = compare(leftValue, rightValue);
|
||||
Boolean result = null;
|
||||
if (cmp < 0 || cmp > 0) {
|
||||
if(opSign == EQEQ) result = false;
|
||||
else if (opSign == NE) result = true;
|
||||
}
|
||||
if (opSign == LT) {
|
||||
result = cmp < 0;
|
||||
}
|
||||
else if (opSign == GT) {
|
||||
result = cmp > 0;
|
||||
}
|
||||
else if (opSign == LE) {
|
||||
result = cmp <= 0;
|
||||
}
|
||||
else if (opSign == GE) {
|
||||
result = cmp >= 0;
|
||||
}
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return result ? alwaysTrue(instruction, runner, memState) : alwaysFalse(instruction, runner, memState);
|
||||
}
|
||||
|
||||
private static int compare(Number a, Number b) {
|
||||
@@ -653,32 +735,6 @@ public class StandardInstructionVisitor extends InstructionVisitor {
|
||||
return Double.compare(a.doubleValue(), b.doubleValue());
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static DfaInstructionState[] checkComparisonWithKnownRange(BinopInstruction instruction,
|
||||
DataFlowRunner runner,
|
||||
DfaMemoryState memState,
|
||||
IElementType opSign,
|
||||
Number comparedWith,
|
||||
Number rangeMin,
|
||||
Number rangeMax) {
|
||||
if (compare(comparedWith, rangeMin) < 0 || compare(comparedWith, rangeMax) > 0) {
|
||||
if (opSign == EQEQ) return alwaysFalse(instruction, runner, memState);
|
||||
if (opSign == NE) return alwaysTrue(instruction, runner, memState);
|
||||
}
|
||||
|
||||
if (opSign == LT && compare(comparedWith, rangeMin) <= 0) return alwaysFalse(instruction, runner, memState);
|
||||
if (opSign == LT && compare(comparedWith, rangeMax) > 0) return alwaysTrue(instruction, runner, memState);
|
||||
if (opSign == LE && compare(comparedWith, rangeMax) >= 0) return alwaysTrue(instruction, runner, memState);
|
||||
if (opSign == LE && compare(comparedWith, rangeMin) < 0) return alwaysFalse(instruction, runner, memState);
|
||||
|
||||
if (opSign == GT && compare(comparedWith, rangeMax) >= 0) return alwaysFalse(instruction, runner, memState);
|
||||
if (opSign == GT && compare(comparedWith, rangeMin) < 0) return alwaysTrue(instruction, runner, memState);
|
||||
if (opSign == GE && compare(comparedWith, rangeMin) <= 0) return alwaysTrue(instruction, runner, memState);
|
||||
if (opSign == GE && compare(comparedWith, rangeMax) > 0) return alwaysFalse(instruction, runner, memState);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private static DfaInstructionState[] alwaysFalse(BinopInstruction instruction, DataFlowRunner runner, DfaMemoryState memState) {
|
||||
memState.push(runner.getFactory().getConstFactory().getFalse());
|
||||
instruction.setFalseReachable();
|
||||
|
||||
+172
-37
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -15,13 +15,14 @@
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.*;
|
||||
import com.intellij.openapi.progress.ProgressManager;
|
||||
import com.intellij.openapi.util.Condition;
|
||||
import com.intellij.openapi.util.UnorderedPair;
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
@@ -32,6 +33,7 @@ import java.util.*;
|
||||
* @author peter
|
||||
*/
|
||||
class StateMerger {
|
||||
public static final int MAX_RANGE_STATES = 100;
|
||||
private final Map<DfaMemoryStateImpl, Set<Fact>> myFacts = ContainerUtil.newIdentityHashMap();
|
||||
private final Map<DfaMemoryState, Map<DfaVariableValue, DfaMemoryStateImpl>> myCopyCache = ContainerUtil.newIdentityHashMap();
|
||||
|
||||
@@ -92,7 +94,13 @@ class StateMerger {
|
||||
|
||||
@NotNull
|
||||
private LinkedHashSet<Fact> getUnrelatedFacts(@NotNull final Fact fact, @NotNull DfaMemoryStateImpl state) {
|
||||
return new LinkedHashSet<>(ContainerUtil.filter(getFacts(state), another -> !fact.invalidatesFact(another)));
|
||||
final LinkedHashSet<Fact> result = new LinkedHashSet<>();
|
||||
for (Fact other : getFacts(state)) {
|
||||
if (!fact.invalidatesFact(other)) {
|
||||
result.add(other);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private void restoreOtherInequalities(@NotNull Fact removedFact, @NotNull Collection<DfaMemoryStateImpl> mergedGroup, @NotNull DfaMemoryStateImpl state) {
|
||||
@@ -203,6 +211,122 @@ class StateMerger {
|
||||
return replacements.getMergeResult();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
List<DfaMemoryStateImpl> mergeByRanges(List<DfaMemoryStateImpl> states) {
|
||||
// If the same variable has different range A and B in different memState and range A contains range B
|
||||
// then range A is replaced with range B
|
||||
Map<DfaVariableValue, Map<LongRangeSet, LongRangeSet>> ranges = createRangeMap(states);
|
||||
boolean changed = false;
|
||||
for (Map<LongRangeSet, LongRangeSet> map : ranges.values()) {
|
||||
for (Map.Entry<LongRangeSet, LongRangeSet> entry : map.entrySet()) {
|
||||
for(LongRangeSet candidate : map.values()) {
|
||||
if(!entry.getValue().equals(candidate) && candidate.contains(entry.getValue())) {
|
||||
entry.setValue(candidate);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if(changed) {
|
||||
changed = false;
|
||||
for (DfaMemoryStateImpl state : states) {
|
||||
for (Map.Entry<DfaVariableValue, Map<LongRangeSet, LongRangeSet>> entry : ranges.entrySet()) {
|
||||
DfaVariableState variableState = state.getVariableState(entry.getKey());
|
||||
LongRangeSet range = variableState.getRange();
|
||||
LongRangeSet boundingRange = entry.getValue().get(range);
|
||||
if (boundingRange != null && !boundingRange.equals(range)) {
|
||||
state.setRange(entry.getKey(), boundingRange);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(changed) {
|
||||
return new ArrayList<>(new LinkedHashSet<>(states));
|
||||
}
|
||||
}
|
||||
List<DfaMemoryStateImpl> merged = mergeIndependentRanges(states, ranges);
|
||||
if(merged != null) return merged;
|
||||
return dropExcessRangeInfo(states, ranges.keySet());
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static Map<DfaVariableValue, Map<LongRangeSet, LongRangeSet>> createRangeMap(List<DfaMemoryStateImpl> states) {
|
||||
Map<DfaVariableValue, Map<LongRangeSet, LongRangeSet>> ranges = new LinkedHashMap<>();
|
||||
for (DfaMemoryStateImpl state : states) {
|
||||
ProgressManager.checkCanceled();
|
||||
Map<DfaVariableValue, DfaVariableState> variableStates = state.getVariableStates();
|
||||
for (Map.Entry<DfaVariableValue, DfaVariableState> entry : variableStates.entrySet()) {
|
||||
LongRangeSet range = entry.getValue().getRange();
|
||||
if (range != null) {
|
||||
ranges.computeIfAbsent(entry.getKey(), k -> new HashMap<>()).put(range, range);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<DfaMemoryStateImpl> mergeIndependentRanges(List<DfaMemoryStateImpl> states, Map<DfaVariableValue, Map<LongRangeSet, LongRangeSet>> ranges) {
|
||||
boolean changed = false;
|
||||
// For every variable with more than one range, try to union range info and see if some states could be merged after that
|
||||
for (Map.Entry<DfaVariableValue, Map<LongRangeSet, LongRangeSet>> entry : ranges.entrySet()) {
|
||||
if (entry.getValue().size() > 1) {
|
||||
class Record {
|
||||
final DfaMemoryStateImpl myState;
|
||||
final LongRangeSet myRange;
|
||||
final boolean myMerged;
|
||||
|
||||
Record(DfaMemoryStateImpl state, LongRangeSet range, boolean merged) {
|
||||
myState = state;
|
||||
myRange = range;
|
||||
myMerged = merged;
|
||||
}
|
||||
|
||||
Record union(Record other) {
|
||||
return new Record(myState, myRange.union(other.myRange), true);
|
||||
}
|
||||
}
|
||||
|
||||
ProgressManager.checkCanceled();
|
||||
Map<DfaMemoryStateImpl, Record> merged = new LinkedHashMap<>();
|
||||
DfaVariableValue var = entry.getKey();
|
||||
for (DfaMemoryStateImpl state : states) {
|
||||
DfaVariableState variableState = state.getVariableState(var);
|
||||
LongRangeSet range = variableState.getRange();
|
||||
if (range == null) {
|
||||
range = Objects.requireNonNull(LongRangeSet.fromType(var.getVariableType()));
|
||||
}
|
||||
merged.merge(copyWithoutVar(state, var), new Record(state, range, false), Record::union);
|
||||
}
|
||||
if(merged.size() < states.size()) {
|
||||
List<DfaMemoryStateImpl> updated = new ArrayList<>(merged.size());
|
||||
for (Record record : merged.values()) {
|
||||
DfaMemoryStateImpl state = record.myState;
|
||||
if(record.myMerged) {
|
||||
state.flushVariable(var);
|
||||
state.setRange(var, record.myRange);
|
||||
}
|
||||
updated.add(state);
|
||||
}
|
||||
states = updated;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return changed ? states : null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static List<DfaMemoryStateImpl> dropExcessRangeInfo(List<DfaMemoryStateImpl> states, Set<DfaVariableValue> rangeVariables) {
|
||||
if (states.size() <= MAX_RANGE_STATES || rangeVariables.isEmpty()) return null;
|
||||
// If there are too many states, try to drop range information from some variable
|
||||
DfaVariableValue lastVar = Collections.max(rangeVariables, Comparator.comparingInt(DfaVariableValue::getID));
|
||||
for (DfaMemoryStateImpl state : states) {
|
||||
state.setRange(lastVar, null);
|
||||
}
|
||||
return new ArrayList<>(new HashSet<>(states));
|
||||
}
|
||||
|
||||
private static boolean mergeUnknowns(@NotNull Replacements replacements, @NotNull List<DfaMemoryStateImpl> complementary) {
|
||||
if (complementary.size() < 2) return false;
|
||||
|
||||
@@ -220,10 +344,7 @@ class StateMerger {
|
||||
|
||||
@NotNull
|
||||
private DfaMemoryStateImpl copyWithoutVar(@NotNull DfaMemoryStateImpl state, @NotNull DfaVariableValue var) {
|
||||
Map<DfaVariableValue, DfaMemoryStateImpl> map = myCopyCache.get(state);
|
||||
if (map == null) {
|
||||
myCopyCache.put(state, map = ContainerUtil.newIdentityHashMap());
|
||||
}
|
||||
Map<DfaVariableValue, DfaMemoryStateImpl> map = myCopyCache.computeIfAbsent(state, k -> ContainerUtil.newIdentityHashMap());
|
||||
DfaMemoryStateImpl copy = map.get(var);
|
||||
if (copy == null) {
|
||||
copy = state.createCopy();
|
||||
@@ -245,46 +366,52 @@ class StateMerger {
|
||||
}
|
||||
|
||||
result = ContainerUtil.newLinkedHashSet();
|
||||
|
||||
IdentityHashMap<EqClass, EqClassInfo> classInfo = new IdentityHashMap<>();
|
||||
|
||||
for (EqClass eqClass : state.getNonTrivialEqClasses()) {
|
||||
DfaValue constant = eqClass.findConstant(true);
|
||||
List<DfaVariableValue> vars = eqClass.getVariables(false);
|
||||
for (DfaVariableValue var : vars) {
|
||||
EqClassInfo info = classInfo.computeIfAbsent(eqClass, EqClassInfo::new);
|
||||
DfaValue constant = info.constant;
|
||||
List<DfaVariableValue> vars = info.vars;
|
||||
int size = vars.size();
|
||||
for (int i = 0; i < size; i++) {
|
||||
DfaVariableValue var = vars.get(i);
|
||||
if (constant != null) {
|
||||
result.add(Fact.createEqualityFact(var, constant, true));
|
||||
}
|
||||
for (DfaVariableValue eqVar : vars) {
|
||||
if (var != eqVar) {
|
||||
result.add(Fact.createEqualityFact(var, eqVar, true));
|
||||
}
|
||||
for (int j = i + 1; j < size; j++) {
|
||||
DfaVariableValue eqVar = vars.get(j);
|
||||
result.add(Fact.createEqualityFact(var, eqVar, true));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
for (UnorderedPair<EqClass> classPair : state.getDistinctClassPairs()) {
|
||||
List<DfaVariableValue> vars1 = classPair.first.getVariables(false);
|
||||
List<DfaVariableValue> vars2 = classPair.second.getVariables(false);
|
||||
|
||||
LinkedHashSet<DfaValue> firstSet = new LinkedHashSet<>(vars1);
|
||||
ContainerUtil.addIfNotNull(firstSet, classPair.first.findConstant(true));
|
||||
EqClassInfo info1 = classInfo.computeIfAbsent(classPair.first, EqClassInfo::new);
|
||||
EqClassInfo info2 = classInfo.computeIfAbsent(classPair.second, EqClassInfo::new);
|
||||
|
||||
LinkedHashSet<DfaValue> secondSet = new LinkedHashSet<>(vars2);
|
||||
ContainerUtil.addIfNotNull(secondSet, classPair.second.findConstant(true));
|
||||
|
||||
for (DfaVariableValue var : vars1) {
|
||||
for (DfaValue value : secondSet) {
|
||||
result.add(new Fact(FactType.equality, var, false, value));
|
||||
for (DfaVariableValue var1 : info1.vars) {
|
||||
for (DfaVariableValue var2 : info2.vars) {
|
||||
result.add(new Fact(FactType.equality, var1, false, var2));
|
||||
result.add(new Fact(FactType.equality, var2, false, var1));
|
||||
}
|
||||
}
|
||||
for (DfaVariableValue var : vars2) {
|
||||
for (DfaValue value : firstSet) {
|
||||
result.add(new Fact(FactType.equality, var, false, value));
|
||||
if(info1.constant != null) {
|
||||
for (DfaVariableValue var2 : info2.vars) {
|
||||
result.add(new Fact(FactType.equality, var2, false, info1.constant));
|
||||
}
|
||||
}
|
||||
if(info2.constant != null) {
|
||||
for (DfaVariableValue var1 : info1.vars) {
|
||||
result.add(new Fact(FactType.equality, var1, false, info2.constant));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Map<DfaVariableValue, DfaVariableState> states = state.getVariableStates();
|
||||
for (DfaVariableValue var : states.keySet()) {
|
||||
DfaVariableState variableState = states.get(var);
|
||||
for (Map.Entry<DfaVariableValue, DfaVariableState> entry : states.entrySet()) {
|
||||
DfaVariableValue var = entry.getKey();
|
||||
DfaVariableState variableState = entry.getValue();
|
||||
for (DfaPsiType type : variableState.getInstanceofValues()) {
|
||||
result.add(new Fact(FactType.instanceOf, var, true, type));
|
||||
}
|
||||
@@ -305,12 +432,14 @@ class StateMerger {
|
||||
private final boolean myPositive;
|
||||
@NotNull
|
||||
private final Object myArg; // DfaValue for equality fact, DfaPsiType for instanceOf fact
|
||||
private final int myHash;
|
||||
|
||||
private Fact(@NotNull FactType type, @NotNull DfaVariableValue var, boolean positive, @NotNull Object arg) {
|
||||
myType = type;
|
||||
myVar = var;
|
||||
myPositive = positive;
|
||||
myArg = arg;
|
||||
myHash = Objects.hash(myType, myVar, myPositive, myArg);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -320,9 +449,10 @@ class StateMerger {
|
||||
|
||||
Fact fact = (Fact)o;
|
||||
|
||||
if (myHash != fact.myHash) return false;
|
||||
if (myPositive != fact.myPositive) return false;
|
||||
if (!myArg.equals(fact.myArg)) return false;
|
||||
if (myType != fact.myType) return false;
|
||||
if (!myArg.equals(fact.myArg)) return false;
|
||||
if (!myVar.equals(fact.myVar)) return false;
|
||||
|
||||
return true;
|
||||
@@ -330,11 +460,7 @@ class StateMerger {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = myType.hashCode();
|
||||
result = 31 * result + myVar.hashCode();
|
||||
result = 31 * result + (myPositive ? 1 : 0);
|
||||
result = 31 * result + myArg.hashCode();
|
||||
return result;
|
||||
return myHash;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -431,4 +557,13 @@ class StateMerger {
|
||||
}
|
||||
}
|
||||
|
||||
static final class EqClassInfo {
|
||||
final List<DfaVariableValue> vars;
|
||||
final DfaValue constant;
|
||||
|
||||
EqClassInfo(EqClass eqClass) {
|
||||
vars = eqClass.getVariables(false);
|
||||
constant = eqClass.findConstant(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
-7
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package com.intellij.codeInspection.dataFlow;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaPsiType;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
@@ -78,8 +79,10 @@ class ValuableDataFlowRunner extends DataFlowRunner {
|
||||
private ValuableDfaVariableState(Set<DfaPsiType> instanceofValues,
|
||||
Set<DfaPsiType> notInstanceofValues,
|
||||
Nullness nullability, DfaValue value,
|
||||
@NotNull FList<PsiExpression> concatenation, ThreeState optionalPresence) {
|
||||
super(instanceofValues, notInstanceofValues, nullability, optionalPresence);
|
||||
@NotNull FList<PsiExpression> concatenation,
|
||||
ThreeState optionalPresence,
|
||||
LongRangeSet range) {
|
||||
super(instanceofValues, notInstanceofValues, nullability, optionalPresence, range);
|
||||
myValue = value;
|
||||
myConcatenation = concatenation;
|
||||
}
|
||||
@@ -89,8 +92,10 @@ class ValuableDataFlowRunner extends DataFlowRunner {
|
||||
protected DfaVariableState createCopy(@NotNull Set<DfaPsiType> instanceofValues,
|
||||
@NotNull Set<DfaPsiType> notInstanceofValues,
|
||||
@NotNull Nullness nullability,
|
||||
ThreeState optionalPresence) {
|
||||
return new ValuableDfaVariableState(instanceofValues, notInstanceofValues, nullability, myValue, myConcatenation, optionalPresence);
|
||||
ThreeState optionalPresence,
|
||||
LongRangeSet range) {
|
||||
return new ValuableDfaVariableState(instanceofValues, notInstanceofValues, nullability, myValue, myConcatenation, optionalPresence,
|
||||
range);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -98,13 +103,13 @@ class ValuableDataFlowRunner extends DataFlowRunner {
|
||||
public DfaVariableState withValue(@Nullable final DfaValue value) {
|
||||
if (value == myValue) return this;
|
||||
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, value, myConcatenation,
|
||||
myOptionalPresence);
|
||||
myOptionalPresence, myRange);
|
||||
}
|
||||
|
||||
ValuableDfaVariableState withExpression(@NotNull final FList<PsiExpression> concatenation) {
|
||||
if (concatenation == myConcatenation) return this;
|
||||
return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, myValue, concatenation,
|
||||
myOptionalPresence);
|
||||
myOptionalPresence, myRange);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+6
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -28,7 +28,10 @@ import com.intellij.codeInspection.dataFlow.*;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValue;
|
||||
import com.intellij.codeInspection.dataFlow.value.DfaValueFactory;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.PsiClassType;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiManager;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.psi.tree.TokenSet;
|
||||
@@ -38,7 +41,7 @@ import org.jetbrains.annotations.Nullable;
|
||||
import static com.intellij.psi.JavaTokenType.*;
|
||||
|
||||
public class BinopInstruction extends BranchingInstruction {
|
||||
private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS);
|
||||
private static final TokenSet ourSignificantOperations = TokenSet.create(EQEQ, NE, LT, GT, LE, GE, INSTANCEOF_KEYWORD, PLUS, AND);
|
||||
private final IElementType myOperationSign;
|
||||
private final Project myProject;
|
||||
|
||||
|
||||
+853
@@ -0,0 +1,853 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow.rangeSet;
|
||||
|
||||
import com.intellij.psi.JavaTokenType;
|
||||
import com.intellij.psi.PsiPrimitiveType;
|
||||
import com.intellij.psi.PsiType;
|
||||
import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.ThreeState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.stream.IntStream;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
/**
|
||||
* An immutable set of long values optimized for small number of ranges.
|
||||
*
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public abstract class LongRangeSet {
|
||||
LongRangeSet() {}
|
||||
|
||||
/**
|
||||
* Subtracts given set from the current
|
||||
*
|
||||
* @param other set to subtract
|
||||
* @return a new set
|
||||
*/
|
||||
public abstract LongRangeSet subtract(LongRangeSet other);
|
||||
|
||||
public LongRangeSet without(long value) {
|
||||
return subtract(point(value));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if set is empty
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return this == Empty.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Intersects current set with other
|
||||
*
|
||||
* @param other other set to intersect with
|
||||
* @return a new set
|
||||
*/
|
||||
public abstract LongRangeSet intersect(LongRangeSet other);
|
||||
|
||||
/**
|
||||
* Merge current set with other
|
||||
*
|
||||
* @param other other set to merge with
|
||||
* @return a new set
|
||||
*/
|
||||
public LongRangeSet union(LongRangeSet other) {
|
||||
if(other.isEmpty() || other == this) return this;
|
||||
if(other.contains(this)) return other;
|
||||
// TODO: optimize
|
||||
return Range.LONG_RANGE.subtract(Range.LONG_RANGE.subtract(this).intersect(Range.LONG_RANGE.subtract(other)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a minimal value contained in the set
|
||||
* @throws NoSuchElementException if set is empty
|
||||
*/
|
||||
public abstract long min();
|
||||
|
||||
/**
|
||||
* @return a maximal value contained in the set
|
||||
* @throws NoSuchElementException if set is empty
|
||||
*/
|
||||
public abstract long max();
|
||||
|
||||
/**
|
||||
* Checks if current set and other set have at least one common element
|
||||
*
|
||||
* @param other other set to check whether intersection exists
|
||||
* @return true if this set intersects other set
|
||||
*/
|
||||
public abstract boolean intersects(LongRangeSet other);
|
||||
|
||||
/**
|
||||
* Checks whether current set contains given value
|
||||
*
|
||||
* @param value value to find
|
||||
* @return true if current set contains given value
|
||||
*/
|
||||
public abstract boolean contains(long value);
|
||||
|
||||
/**
|
||||
* Checks whether current set contains all the values from other set
|
||||
*
|
||||
* @param other a sub-set candidate
|
||||
* @return true if current set contains all the values from other
|
||||
*/
|
||||
public abstract boolean contains(LongRangeSet other);
|
||||
|
||||
/**
|
||||
* Creates a new set which contains all possible values satisfying given predicate regarding the current set.
|
||||
* <p>
|
||||
* E.g. if current set is {0..10} and relation is "GT", then result will be {1..Long.MAX_VALUE} (values which can be greater than
|
||||
* some value from the current set)
|
||||
*
|
||||
* @param relation relation to be applied to current set (JavaTokenType.EQEQ/NE/GT/GE/LT/LE)
|
||||
* @return new set or null if relation is unsupported
|
||||
*/
|
||||
public LongRangeSet fromRelation(IElementType relation) {
|
||||
if (isEmpty()) return null;
|
||||
if (JavaTokenType.EQEQ.equals(relation)) {
|
||||
return this;
|
||||
}
|
||||
if (JavaTokenType.NE.equals(relation)) {
|
||||
long min = min();
|
||||
if (min == max()) return all().without(min);
|
||||
return all();
|
||||
}
|
||||
if (JavaTokenType.GT.equals(relation)) {
|
||||
long min = min();
|
||||
return min == Long.MAX_VALUE ? empty() : range(min + 1, Long.MAX_VALUE);
|
||||
}
|
||||
if (JavaTokenType.GE.equals(relation)) {
|
||||
return range(min(), Long.MAX_VALUE);
|
||||
}
|
||||
if (JavaTokenType.LE.equals(relation)) {
|
||||
return range(Long.MIN_VALUE, max());
|
||||
}
|
||||
if (JavaTokenType.LT.equals(relation)) {
|
||||
long max = max();
|
||||
return max == Long.MIN_VALUE ? empty() : range(Long.MIN_VALUE, max - 1);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@link Math#abs(int)} or {@link Math#abs(long)}
|
||||
* to the values from this set
|
||||
*
|
||||
* @param isLong whether {@link Math#abs(long)} is applied
|
||||
* @return a new range
|
||||
*/
|
||||
public abstract LongRangeSet abs(boolean isLong);
|
||||
|
||||
/**
|
||||
* Returns a range which represents all the possible values after applying {@code x & y} operation for
|
||||
* all {@code x} from this set and for all {@code y} from the other set. The resulting set may contain
|
||||
* some more values.
|
||||
*
|
||||
* @param other other set to perform bitwise-and with
|
||||
* @return a new range
|
||||
*/
|
||||
public LongRangeSet bitwiseAnd(LongRangeSet other) {
|
||||
if (this.isEmpty() || other.isEmpty()) return empty();
|
||||
long[] left = splitAtZero(asRanges());
|
||||
long[] right = splitAtZero(other.asRanges());
|
||||
// More than three intervals --> convert to single interval to make result more compact (though probably less precise)
|
||||
if (left.length > 6) {
|
||||
left = splitAtZero(new long[]{left[0], left[left.length - 1]});
|
||||
}
|
||||
if (right.length > 6) {
|
||||
right = splitAtZero(new long[]{right[0], right[right.length - 1]});
|
||||
}
|
||||
LongRangeSet result = all();
|
||||
for (int i = 0; i < left.length; i += 2) {
|
||||
for (int j = 0; j < right.length; j += 2) {
|
||||
result = result.subtract(bitwiseAnd(left[i], left[i + 1], right[j], right[j + 1]));
|
||||
}
|
||||
}
|
||||
return all().subtract(result);
|
||||
}
|
||||
|
||||
private static long[] splitAtZero(long[] ranges) {
|
||||
for (int i = 0; i < ranges.length; i += 2) {
|
||||
if (ranges[i] < 0 && ranges[i + 1] >= 0) {
|
||||
long[] result = new long[ranges.length + 2];
|
||||
System.arraycopy(ranges, 0, result, 0, i + 1);
|
||||
result[i + 1] = -1;
|
||||
System.arraycopy(ranges, i + 1, result, i + 3, ranges.length - i - 1);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return ranges;
|
||||
}
|
||||
|
||||
private static LongRangeSet bitwiseAnd(long leftFrom, long leftTo, long rightFrom, long rightTo) {
|
||||
if (leftFrom == leftTo && rightFrom == rightTo) {
|
||||
return point(leftFrom & rightFrom);
|
||||
}
|
||||
ThreeState[] leftBits = bits(leftFrom, leftTo);
|
||||
ThreeState[] rightBits = bits(rightFrom, rightTo);
|
||||
ThreeState[] resultBits = new ThreeState[Long.SIZE];
|
||||
for (int i = 0; i < Long.SIZE; i++) {
|
||||
if (leftBits[i] == ThreeState.NO || rightBits[i] == ThreeState.NO) {
|
||||
resultBits[i] = ThreeState.NO;
|
||||
}
|
||||
else if (leftBits[i] == ThreeState.UNSURE || rightBits[i] == ThreeState.UNSURE) {
|
||||
resultBits[i] = ThreeState.UNSURE;
|
||||
}
|
||||
else {
|
||||
resultBits[i] = ThreeState.YES;
|
||||
}
|
||||
}
|
||||
return fromBits(resultBits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set which contains all the numbers satisfying the supplied bit vector.
|
||||
* Vector format is the same as returned by {@link #bits(long, long)}. The resulting set may
|
||||
* contain more values than necessary.
|
||||
*
|
||||
* @param bits a bit vector
|
||||
* @return a new LongRangeSet
|
||||
*/
|
||||
private static LongRangeSet fromBits(ThreeState[] bits) {
|
||||
long from = 0;
|
||||
int i = 0;
|
||||
while (i < Long.SIZE && bits[i] != ThreeState.UNSURE) {
|
||||
if (bits[i] == ThreeState.YES) {
|
||||
from |= (1L << (Long.SIZE - 1 - i));
|
||||
}
|
||||
i++;
|
||||
}
|
||||
long to = ((1L << (Long.SIZE - i)) - 1) | from;
|
||||
int j = Long.SIZE - 1;
|
||||
while(j > i && bits[j] != ThreeState.UNSURE) {
|
||||
if (bits[j] == ThreeState.NO) {
|
||||
to &= ~(1L << Long.SIZE - 1 - j);
|
||||
}
|
||||
j--;
|
||||
}
|
||||
if(i == j) {
|
||||
return point(from).union(point(to));
|
||||
}
|
||||
return from < to ? range(from, to) : range(to, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a bit vector for values between from and to.
|
||||
*
|
||||
* @param from lower bound
|
||||
* @param to upper bound
|
||||
* @return an array of 64 ThreeState values (NO = zero bit for all values, YES = one bit for all values,
|
||||
* UNSURE = both one and zero possible)
|
||||
*/
|
||||
private static ThreeState[] bits(long from, long to) {
|
||||
ThreeState[] bits = new ThreeState[Long.SIZE];
|
||||
Arrays.setAll(bits, idx -> ThreeState.NO);
|
||||
while (true) {
|
||||
int fromBit = Long.numberOfLeadingZeros(from);
|
||||
int toBit = Long.numberOfLeadingZeros(to);
|
||||
if (fromBit != toBit) {
|
||||
for (int i = Math.min(fromBit, toBit); i < Long.SIZE; i++) {
|
||||
bits[i] = ThreeState.UNSURE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (fromBit == 64) break;
|
||||
bits[fromBit] = ThreeState.YES;
|
||||
long clearMask = ~(1L << (Long.SIZE - 1 - fromBit));
|
||||
from &= clearMask;
|
||||
to &= clearMask;
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a stream of all values from this range. Be careful: could be huge
|
||||
*
|
||||
* @return a new stream
|
||||
*/
|
||||
public abstract LongStream stream();
|
||||
|
||||
/**
|
||||
* @return an empty set
|
||||
*/
|
||||
public static LongRangeSet empty() {
|
||||
return Empty.EMPTY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a set containing all possible long values
|
||||
*/
|
||||
public static LongRangeSet all() {
|
||||
return Range.LONG_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set containing single given value
|
||||
*
|
||||
* @param value a value to be included into the set
|
||||
* @return a new set
|
||||
*/
|
||||
public static LongRangeSet point(long value) {
|
||||
return new Point(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a set containing single value which is equivalent to supplied boxed constant (if its type is supported)
|
||||
*
|
||||
* @param val constant to create a set from
|
||||
* @return new LongRangeSet or null if constant type is unsupported
|
||||
*/
|
||||
@Nullable
|
||||
public static LongRangeSet fromConstant(Object val) {
|
||||
if (val instanceof Byte || val instanceof Short || val instanceof Integer || val instanceof Long) {
|
||||
return point(((Number)val).longValue());
|
||||
}
|
||||
else if (val instanceof Character) {
|
||||
return point(((Character)val).charValue());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new set which contains all the numbers between from (inclusive) and to (inclusive)
|
||||
*
|
||||
* @param from lower bound
|
||||
* @param to upper bound (must be greater or equal to {@code from})
|
||||
* @return a new LongRangeSet
|
||||
*/
|
||||
public static LongRangeSet range(long from, long to) {
|
||||
return from == to ? new Point(from) : new Range(from, to);
|
||||
}
|
||||
|
||||
abstract long[] asRanges();
|
||||
|
||||
static String toString(long from, long to) {
|
||||
return from == to ? String.valueOf(from) : from + (to - from == 1 ? ", " : "..") + to;
|
||||
}
|
||||
|
||||
static long minValue(boolean isLong) {
|
||||
return isLong ? Long.MIN_VALUE : Integer.MIN_VALUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LongRangeSet describing possible array or string indices (from 0 to Integer.MAX_VALUE)
|
||||
*/
|
||||
public static LongRangeSet indexRange() {
|
||||
return Range.INDEX_RANGE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a range for given type (for primitives and boxed: values range)
|
||||
*
|
||||
* @param type type to create a range for
|
||||
* @return a range or null if type is not supported
|
||||
*/
|
||||
@Nullable
|
||||
public static LongRangeSet fromType(PsiType type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
type = PsiPrimitiveType.getOptionallyUnboxedType(type);
|
||||
if (type != null) {
|
||||
if (type.equals(PsiType.BYTE)) {
|
||||
return Range.BYTE_RANGE;
|
||||
}
|
||||
if (type.equals(PsiType.CHAR)) {
|
||||
return Range.CHAR_RANGE;
|
||||
}
|
||||
if (type.equals(PsiType.SHORT)) {
|
||||
return Range.SHORT_RANGE;
|
||||
}
|
||||
if (type.equals(PsiType.INT)) {
|
||||
return Range.INT_RANGE;
|
||||
}
|
||||
if (type.equals(PsiType.LONG)) {
|
||||
return all();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static LongRangeSet fromRanges(long[] ranges, int bound) {
|
||||
if (bound == 0) {
|
||||
return Empty.EMPTY;
|
||||
}
|
||||
else if (bound == 2) {
|
||||
return range(ranges[0], ranges[1]);
|
||||
}
|
||||
else {
|
||||
return new RangeSet(Arrays.copyOfRange(ranges, 0, bound));
|
||||
}
|
||||
}
|
||||
|
||||
static final class Empty extends LongRangeSet {
|
||||
static final LongRangeSet EMPTY = new Empty();
|
||||
|
||||
@Override
|
||||
public LongRangeSet subtract(LongRangeSet other) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet intersect(LongRangeSet other) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet union(LongRangeSet other) {
|
||||
return other;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long min() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long max() {
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(LongRangeSet other) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(LongRangeSet other) {
|
||||
return other.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return LongStream.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return new long[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return 2154231;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{}";
|
||||
}
|
||||
}
|
||||
|
||||
static final class Point extends LongRangeSet {
|
||||
final long myValue;
|
||||
|
||||
Point(long value) {
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet subtract(LongRangeSet other) {
|
||||
return other.contains(myValue) ? Empty.EMPTY : this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet intersect(LongRangeSet other) {
|
||||
return other.contains(myValue) ? this : Empty.EMPTY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long min() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long max() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(LongRangeSet other) {
|
||||
return other.contains(myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long value) {
|
||||
return myValue == value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(LongRangeSet other) {
|
||||
return other.isEmpty() || equals(other);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
return myValue >= 0 || myValue == minValue(isLong) ? this : point(-myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return LongStream.of(myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return new long[] {myValue, myValue};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(myValue);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
return o != null && o instanceof Point && myValue == ((Point)o).myValue;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + myValue + "}";
|
||||
}
|
||||
}
|
||||
|
||||
static final class Range extends LongRangeSet {
|
||||
static final Range BYTE_RANGE = new Range(Byte.MIN_VALUE, Byte.MAX_VALUE);
|
||||
static final Range CHAR_RANGE = new Range(Character.MIN_VALUE, Character.MAX_VALUE);
|
||||
static final Range SHORT_RANGE = new Range(Short.MIN_VALUE, Short.MAX_VALUE);
|
||||
static final Range INT_RANGE = new Range(Integer.MIN_VALUE, Integer.MAX_VALUE);
|
||||
static final Range LONG_RANGE = new Range(Long.MIN_VALUE, Long.MAX_VALUE);
|
||||
static final Range INDEX_RANGE = new Range(0, Integer.MAX_VALUE);
|
||||
|
||||
final long myFrom; // inclusive
|
||||
final long myTo; // inclusive
|
||||
|
||||
Range(long from, long to) {
|
||||
if (to <= from) { // to == from => must be Point
|
||||
throw new IllegalArgumentException(to + "<=" + from);
|
||||
}
|
||||
myFrom = from;
|
||||
myTo = to;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet subtract(LongRangeSet other) {
|
||||
if (other.isEmpty()) return this;
|
||||
if (other == this) return Empty.EMPTY;
|
||||
if (other instanceof Point) {
|
||||
long value = ((Point)other).myValue;
|
||||
if (value < myFrom || value > myTo) return this;
|
||||
if (value == myFrom) return range(myFrom + 1, myTo);
|
||||
if (value == myTo) return range(myFrom, myTo - 1);
|
||||
return new RangeSet(new long[]{myFrom, value - 1, value + 1, myTo});
|
||||
}
|
||||
if (other instanceof Range) {
|
||||
long from = ((Range)other).myFrom;
|
||||
long to = ((Range)other).myTo;
|
||||
if (to < myFrom || from > myTo) return this;
|
||||
if (from <= myFrom && to >= myTo) return Empty.EMPTY;
|
||||
if (from > myFrom && to < myTo) {
|
||||
return new RangeSet(new long[]{myFrom, from - 1, to + 1, myTo});
|
||||
}
|
||||
if (from <= myFrom) {
|
||||
return range(to + 1, myTo);
|
||||
}
|
||||
if (to >= myTo) {
|
||||
return range(myFrom, from - 1);
|
||||
}
|
||||
throw new InternalError("Impossible: " + this + ":" + other);
|
||||
}
|
||||
long[] ranges = ((RangeSet)other).myRanges;
|
||||
LongRangeSet result = this;
|
||||
for (int i = 0; i < ranges.length; i += 2) {
|
||||
result = result.subtract(range(ranges[i], ranges[i + 1]));
|
||||
if (result.isEmpty()) return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet intersect(LongRangeSet other) {
|
||||
if (other == this) return this;
|
||||
if (other.isEmpty()) return other;
|
||||
if (other instanceof Point) {
|
||||
return other.intersect(this);
|
||||
}
|
||||
if (other instanceof Range) {
|
||||
long from = ((Range)other).myFrom;
|
||||
long to = ((Range)other).myTo;
|
||||
if (from <= myFrom && to >= myTo) return this;
|
||||
if (from >= myFrom && to <= myTo) return other;
|
||||
if (from < myFrom) {
|
||||
from = myFrom;
|
||||
}
|
||||
if (to > myTo) {
|
||||
to = myTo;
|
||||
}
|
||||
return from <= to ? range(from, to) : Empty.EMPTY;
|
||||
}
|
||||
long[] ranges = ((RangeSet)other).myRanges;
|
||||
long[] result = new long[ranges.length];
|
||||
int index = 0;
|
||||
for (int i = 0; i < ranges.length; i += 2) {
|
||||
long[] res = intersect(range(ranges[i], ranges[i + 1])).asRanges();
|
||||
System.arraycopy(res, 0, result, index, res.length);
|
||||
index += res.length;
|
||||
}
|
||||
return fromRanges(result, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long min() {
|
||||
return myFrom;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long max() {
|
||||
return myTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(LongRangeSet other) {
|
||||
if (other.isEmpty()) return false;
|
||||
if (other instanceof RangeSet) {
|
||||
return other.intersects(this);
|
||||
}
|
||||
return myTo >= other.min() && myFrom <= other.max();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long value) {
|
||||
return myFrom <= value && myTo >= value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(LongRangeSet other) {
|
||||
return other.isEmpty() || other.min() >= myFrom && other.max() <= myTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
if (myFrom >= 0) return this;
|
||||
long minValue = minValue(isLong);
|
||||
long low = myFrom, hi = myTo;
|
||||
if (low <= minValue) {
|
||||
low = minValue + 1;
|
||||
}
|
||||
if (myTo <= 0) {
|
||||
hi = -low;
|
||||
low = -myTo;
|
||||
}
|
||||
else {
|
||||
hi = Math.max(-low, hi);
|
||||
low = 0;
|
||||
}
|
||||
if (myFrom <= minValue) {
|
||||
return new RangeSet(new long[]{minValue, minValue, low, hi});
|
||||
}
|
||||
else {
|
||||
return new Range(low, hi);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return LongStream.rangeClosed(myFrom, myTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return new long[] {myFrom, myTo};
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Long.hashCode(myFrom) * 1337 + Long.hashCode(myTo);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
return o != null && o instanceof Range && myFrom == ((Range)o).myFrom && myTo == ((Range)o).myTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "{" + toString(myFrom, myTo) + "}";
|
||||
}
|
||||
}
|
||||
|
||||
static final class RangeSet extends LongRangeSet {
|
||||
final long[] myRanges;
|
||||
|
||||
RangeSet(long[] ranges) {
|
||||
if (ranges.length < 4 || ranges.length % 2 != 0) {
|
||||
// 0 ranges = Empty; 1 range = Range
|
||||
throw new IllegalArgumentException("Bad length: " + ranges.length + " " + Arrays.toString(ranges));
|
||||
}
|
||||
for (int i = 0; i < ranges.length; i += 2) {
|
||||
if (ranges[i + 1] < ranges[i]) {
|
||||
throw new IllegalArgumentException("Bad sub-range #" + (i / 2) + " " + Arrays.toString(ranges));
|
||||
}
|
||||
if (i > 0 && (ranges[i - 1] == Long.MAX_VALUE || 1 + ranges[i - 1] > ranges[i])) {
|
||||
throw new IllegalArgumentException("Bad sub-ranges #" + (i / 2 - 1) + " and #" + (i / 2) + " " + Arrays.toString(ranges));
|
||||
}
|
||||
}
|
||||
myRanges = ranges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet subtract(LongRangeSet other) {
|
||||
if (other.isEmpty()) return this;
|
||||
if (other == this) return Empty.EMPTY;
|
||||
long[] result = new long[myRanges.length + other.asRanges().length];
|
||||
int index = 0;
|
||||
for (int i = 0; i < myRanges.length; i += 2) {
|
||||
LongRangeSet res = range(myRanges[i], myRanges[i + 1]).subtract(other);
|
||||
long[] ranges = res.asRanges();
|
||||
System.arraycopy(ranges, 0, result, index, ranges.length);
|
||||
index += ranges.length;
|
||||
}
|
||||
return fromRanges(result, index);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet intersect(LongRangeSet other) {
|
||||
if (other == this) return this;
|
||||
if (other.isEmpty()) return other;
|
||||
if (other instanceof Point || other instanceof Range) {
|
||||
return other.intersect(this);
|
||||
}
|
||||
return subtract(all().subtract(other));
|
||||
}
|
||||
|
||||
@Override
|
||||
public long min() {
|
||||
return myRanges[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public long max() {
|
||||
return myRanges[myRanges.length - 1];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean intersects(LongRangeSet other) {
|
||||
if (other.isEmpty()) return false;
|
||||
if (other instanceof Point) {
|
||||
return contains(((Point)other).myValue);
|
||||
}
|
||||
long[] otherRanges = other.asRanges();
|
||||
int a = 0, b = 0;
|
||||
while (true) {
|
||||
long aFrom = myRanges[a];
|
||||
long aTo = myRanges[a + 1];
|
||||
long bFrom = otherRanges[b];
|
||||
long bTo = otherRanges[b + 1];
|
||||
if (aFrom <= bTo && bFrom <= aTo) return true;
|
||||
if (aFrom > bTo) {
|
||||
b += 2;
|
||||
if (b >= otherRanges.length) return false;
|
||||
}
|
||||
else {
|
||||
a += 2;
|
||||
if (a >= myRanges.length) return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(long value) {
|
||||
for (int i = 0; i < myRanges.length; i += 2) {
|
||||
if (value >= myRanges[i] && value <= myRanges[i + 1]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(LongRangeSet other) {
|
||||
if (other.isEmpty() || other == this) return true;
|
||||
return other.subtract(this).isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongRangeSet abs(boolean isLong) {
|
||||
LongRangeSet result = all();
|
||||
for (int i = 0; i < myRanges.length; i += 2) {
|
||||
result = result.subtract(range(myRanges[i], myRanges[i + 1]).abs(isLong));
|
||||
}
|
||||
return all().subtract(result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LongStream stream() {
|
||||
return IntStream.range(0, myRanges.length / 2)
|
||||
.mapToObj(idx -> LongStream.rangeClosed(myRanges[idx * 2], myRanges[idx * 2 + 1]))
|
||||
.reduce(LongStream::concat).orElseGet(LongStream::empty);
|
||||
}
|
||||
|
||||
@Override
|
||||
long[] asRanges() {
|
||||
return myRanges;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Arrays.hashCode(myRanges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
if (o == this) return true;
|
||||
return o != null && o instanceof RangeSet && Arrays.equals(myRanges, ((RangeSet)o).myRanges);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("{");
|
||||
for (int i = 0; i < myRanges.length; i += 2) {
|
||||
if (i > 0) sb.append(", ");
|
||||
sb.append(LongRangeSet.toString(myRanges[i], myRanges[i + 1]));
|
||||
}
|
||||
sb.append("}");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import com.intellij.psi.util.PropertyUtil;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.PsiUtil;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -174,6 +175,9 @@ public class DfaExpressionFactory {
|
||||
return method;
|
||||
}
|
||||
}
|
||||
if (MethodUtils.isStringLength(method)) {
|
||||
return method;
|
||||
}
|
||||
if (AnnotationUtil.findAnnotation(method.getContainingClass(), "javax.annotation.concurrent.Immutable") != null) {
|
||||
return method;
|
||||
}
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.intellij.codeInspection.dataFlow.value;
|
||||
|
||||
import com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet;
|
||||
import com.intellij.psi.PsiType;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Tagir Valeev
|
||||
*/
|
||||
public class DfaRangeValue extends DfaValue {
|
||||
private final LongRangeSet myValue;
|
||||
|
||||
DfaRangeValue(DfaValueFactory factory, @NotNull LongRangeSet value) {
|
||||
super(factory);
|
||||
myValue = value;
|
||||
}
|
||||
|
||||
public DfaRangeValue intersect(LongRangeSet value) {
|
||||
return myFactory.getRangeFactory().create(myValue.intersect(value));
|
||||
}
|
||||
|
||||
public LongRangeSet getValue() {
|
||||
return myValue;
|
||||
}
|
||||
|
||||
public static class Factory {
|
||||
private Map<LongRangeSet, DfaRangeValue> myValues = new HashMap<>();
|
||||
private DfaValueFactory myFactory;
|
||||
|
||||
Factory(DfaValueFactory factory) {
|
||||
myFactory = factory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Any value of given type (if type is supported)
|
||||
*
|
||||
* @param type type to create a range-value from
|
||||
* @return DfaRangeValue representing range of given type
|
||||
*/
|
||||
@Nullable
|
||||
public DfaRangeValue create(PsiType type) {
|
||||
LongRangeSet domain = LongRangeSet.fromType(type);
|
||||
return domain == null ? null : create(domain);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaRangeValue create(LongRangeSet value) {
|
||||
return myValues.computeIfAbsent(value, val -> new DfaRangeValue(myFactory, val));
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
-1
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2009 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -29,6 +29,7 @@ import com.intellij.psi.tree.IElementType;
|
||||
import com.intellij.util.containers.HashMap;
|
||||
import org.jetbrains.annotations.NonNls;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -168,6 +169,23 @@ public class DfaRelationValue extends DfaValue {
|
||||
return myRelation == EQEQ && myIsNegated || myRelation == GT && !myIsNegated || myRelation == GE && myIsNegated;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return comparison operation (GT, GE, LE, LT, EQEQ, NE) if this relation represents comparison, null otherwise
|
||||
*/
|
||||
@Nullable
|
||||
public IElementType getComparisonOperation() {
|
||||
if(myRelation == GT) {
|
||||
return myIsNegated ? LE : GT;
|
||||
}
|
||||
if(myRelation == GE) {
|
||||
return myIsNegated ? LT : GE;
|
||||
}
|
||||
if(myRelation == EQEQ) {
|
||||
return myIsNegated ? NE : EQEQ;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isInstanceOf() {
|
||||
return myRelation == INSTANCEOF_KEYWORD;
|
||||
}
|
||||
|
||||
+12
-4
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2000-2015 JetBrains s.r.o.
|
||||
* Copyright 2000-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -60,6 +60,7 @@ public class DfaValueFactory {
|
||||
myRelationFactory = new DfaRelationValue.Factory(this);
|
||||
myExpressionFactory = new DfaExpressionFactory(this);
|
||||
myOptionalFactory = new DfaOptionalValue.Factory(this);
|
||||
myRangeFactory = new DfaRangeValue.Factory(this);
|
||||
}
|
||||
|
||||
public boolean isHonorFieldInitializers() {
|
||||
@@ -101,12 +102,13 @@ public class DfaValueFactory {
|
||||
|
||||
@Nullable
|
||||
public DfaValue createLiteralValue(PsiLiteralExpression literal) {
|
||||
if (literal.getValue() instanceof String) {
|
||||
return createTypeValue(literal.getType(), Nullness.NOT_NULL); // Non-null string literal.
|
||||
}
|
||||
return getConstFactory().create(literal);
|
||||
}
|
||||
|
||||
public DfaConstValue getBoolean(boolean value) {
|
||||
return value ? getConstFactory().getTrue() : getConstFactory().getFalse();
|
||||
}
|
||||
|
||||
public static boolean isEffectivelyUnqualified(PsiReferenceExpression refExpression) {
|
||||
PsiExpression qualifier = refExpression.getQualifierExpression();
|
||||
if (qualifier == null) {
|
||||
@@ -137,6 +139,7 @@ public class DfaValueFactory {
|
||||
private final DfaRelationValue.Factory myRelationFactory;
|
||||
private final DfaExpressionFactory myExpressionFactory;
|
||||
private final DfaOptionalValue.Factory myOptionalFactory;
|
||||
private final DfaRangeValue.Factory myRangeFactory;
|
||||
|
||||
@NotNull
|
||||
public DfaVariableValue.Factory getVarFactory() {
|
||||
@@ -166,4 +169,9 @@ public class DfaValueFactory {
|
||||
public DfaOptionalValue.Factory getOptionalFactory() {
|
||||
return myOptionalFactory;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public DfaRangeValue.Factory getRangeFactory() {
|
||||
return myRangeFactory;
|
||||
}
|
||||
}
|
||||
|
||||
+2
@@ -39,6 +39,7 @@ import com.intellij.psi.search.searches.ReferencesSearch;
|
||||
import com.intellij.psi.util.*;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.MultiMap;
|
||||
import com.siyeh.ig.psiutils.MethodUtils;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -270,6 +271,7 @@ public class DfaVariableValue extends DfaValue {
|
||||
if (myVariable instanceof PsiVariable && myVariable.hasModifierProperty(PsiModifier.FINAL)) {
|
||||
return myQualifier != null && myQualifier.isFlushableByCalls();
|
||||
}
|
||||
if (myVariable instanceof PsiMethod && MethodUtils.isStringLength((PsiMethod)myVariable)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ public class DeprecationInspection extends BaseJavaBatchLocalInspectionTool {
|
||||
public static final String DISPLAY_NAME = DeprecationUtil.DEPRECATION_DISPLAY_NAME;
|
||||
public static final String IGNORE_METHODS_OF_DEPRECATED_NAME = "IGNORE_METHODS_OF_DEPRECATED";
|
||||
|
||||
public boolean IGNORE_INSIDE_DEPRECATED;
|
||||
public boolean IGNORE_INSIDE_DEPRECATED = true;
|
||||
public boolean IGNORE_ABSTRACT_DEPRECATED_OVERRIDES = true;
|
||||
public boolean IGNORE_IMPORT_STATEMENTS = true;
|
||||
public boolean IGNORE_METHODS_OF_DEPRECATED = true;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user