Merge remote-tracking branch 'origin/master'

This commit is contained in:
Konstantin Bulenkov
2017-03-19 17:18:07 +01:00
1473 changed files with 32389 additions and 17573 deletions
+1 -1
View File
@@ -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" />
+4 -21
View File
@@ -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>
+3
View File
@@ -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>
+3 -1
View File
@@ -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>
+1 -1
View File
@@ -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>
+2 -2
View File
@@ -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>
+2 -2
View File
@@ -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 />
+3 -1
View File
@@ -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>
-1
View File
@@ -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>
-1
View File
@@ -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 />
+2 -2
View File
@@ -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>
+1 -1
View File
@@ -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" />
+5 -5
View File
@@ -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>
+3 -1
View File
@@ -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>
+3 -1
View File
@@ -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>
+1 -2
View File
@@ -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
View File
@@ -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" />
+1 -1
View File
@@ -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
+9 -1
View File
@@ -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)
}
}
}
-1
View File
@@ -36,7 +36,6 @@ target(compile: "Compile project") {
}
if (!isDefined("avoid.project.rebuild")) {
clearBuildCaches()
projectBuilder.cleanOutput()
projectBuilder.buildAll()
}
-150
View File
@@ -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")
}
}
}
-4
View File
@@ -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")
+30 -18
View File
@@ -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 -47
View File
@@ -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
View File
@@ -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" />
+1 -1
View File
@@ -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)) {
@@ -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 -1
View File
@@ -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();
@@ -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
@@ -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);
@@ -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)
}
}
@@ -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();
@@ -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);
}
@@ -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;
}
@@ -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();
@@ -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();
}
@@ -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));
}
@@ -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();
}
@@ -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;
}
@@ -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;
@@ -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;
@@ -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);
@@ -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;
@@ -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[]{
@@ -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;
@@ -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();
}
@@ -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());
@@ -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);
@@ -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;
}
}
@@ -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()) {
@@ -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();
@@ -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);
@@ -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();
@@ -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();
@@ -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;
}
}
@@ -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();
}
@@ -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);
}
}
@@ -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));
}
}
@@ -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);
}
}
@@ -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();
@@ -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);
}
}
}
@@ -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
@@ -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;
@@ -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();
}
}
}
@@ -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;
}
@@ -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));
}
}
}
@@ -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;
}
@@ -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;
}
}
@@ -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;
}
@@ -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