diff --git a/.idea/artifacts/standalone_jps.xml b/.idea/artifacts/standalone_jps.xml index 27cf1f36d9f2..8f26221c67fd 100644 --- a/.idea/artifacts/standalone_jps.xml +++ b/.idea/artifacts/standalone_jps.xml @@ -1,6 +1,6 @@ - $PROJECT_DIR$/out/artifacts/standalone_jps + $PROJECT_DIR$/build/lib/jps diff --git a/.idea/libraries/Ant.xml b/.idea/libraries/Ant.xml index fca1abd1e774..da2e071db2cf 100644 --- a/.idea/libraries/Ant.xml +++ b/.idea/libraries/Ant.xml @@ -1,29 +1,12 @@ - - - - - - - - - - - - - - - - - - - + - + - + + \ No newline at end of file diff --git a/.idea/libraries/JDOM.xml b/.idea/libraries/JDOM.xml index 17d2e7336796..00fe634c11b9 100644 --- a/.idea/libraries/JDOM.xml +++ b/.idea/libraries/JDOM.xml @@ -1,5 +1,8 @@ + + + diff --git a/.idea/libraries/JUnit3.xml b/.idea/libraries/JUnit3.xml index c8e5da0d1ec9..3a853d475fbb 100644 --- a/.idea/libraries/JUnit3.xml +++ b/.idea/libraries/JUnit3.xml @@ -4,6 +4,8 @@ - + + + \ No newline at end of file diff --git a/.idea/libraries/Log4J.xml b/.idea/libraries/Log4J.xml index 6e3948c15fc9..588f22548b1e 100644 --- a/.idea/libraries/Log4J.xml +++ b/.idea/libraries/Log4J.xml @@ -5,7 +5,7 @@ - + \ No newline at end of file diff --git a/.idea/libraries/Netty.xml b/.idea/libraries/Netty.xml index caa3be98dcaf..e3e6b240934d 100644 --- a/.idea/libraries/Netty.xml +++ b/.idea/libraries/Netty.xml @@ -4,11 +4,11 @@ - + - + \ No newline at end of file diff --git a/.idea/libraries/XmlBeans.xml b/.idea/libraries/XmlBeans.xml index 9ba2a85a0698..75b6e3cf603c 100644 --- a/.idea/libraries/XmlBeans.xml +++ b/.idea/libraries/XmlBeans.xml @@ -1,9 +1,9 @@ - - + + diff --git a/.idea/libraries/commons_codec.xml b/.idea/libraries/commons_codec.xml index 260da8de3c82..572492fefd61 100644 --- a/.idea/libraries/commons_codec.xml +++ b/.idea/libraries/commons_codec.xml @@ -4,6 +4,8 @@ - + + + \ No newline at end of file diff --git a/.idea/libraries/commons_compress.xml b/.idea/libraries/commons_compress.xml index 7d2018ea643a..e79002e7d931 100644 --- a/.idea/libraries/commons_compress.xml +++ b/.idea/libraries/commons_compress.xml @@ -6,7 +6,6 @@ - \ No newline at end of file diff --git a/.idea/libraries/cucumber_core.xml b/.idea/libraries/cucumber_core.xml index 6974c004732a..211b733da500 100644 --- a/.idea/libraries/cucumber_core.xml +++ b/.idea/libraries/cucumber_core.xml @@ -2,7 +2,6 @@ - diff --git a/.idea/libraries/http_client.xml b/.idea/libraries/http_client.xml index 0f632a7142f1..fbcaa33fd353 100644 --- a/.idea/libraries/http_client.xml +++ b/.idea/libraries/http_client.xml @@ -4,10 +4,10 @@ - - + + diff --git a/.idea/libraries/http_client_3_1.xml b/.idea/libraries/http_client_3_1.xml index d5f7f1ff0c8b..420565fc52de 100644 --- a/.idea/libraries/http_client_3_1.xml +++ b/.idea/libraries/http_client_3_1.xml @@ -1,8 +1,8 @@ - + diff --git a/.idea/libraries/jsch_agent_proxy.xml b/.idea/libraries/jsch_agent_proxy.xml index aafd08d8ef48..e3675a6d4608 100644 --- a/.idea/libraries/jsch_agent_proxy.xml +++ b/.idea/libraries/jsch_agent_proxy.xml @@ -1,12 +1,12 @@ - - - - - + + + + + diff --git a/.idea/libraries/proxy_vole.xml b/.idea/libraries/proxy_vole.xml index 6cb9a2e19b64..71fba7085c65 100644 --- a/.idea/libraries/proxy_vole.xml +++ b/.idea/libraries/proxy_vole.xml @@ -4,6 +4,8 @@ - + + + \ No newline at end of file diff --git a/.idea/libraries/swingx.xml b/.idea/libraries/swingx.xml index aada475d160f..9da1c0f6130a 100644 --- a/.idea/libraries/swingx.xml +++ b/.idea/libraries/swingx.xml @@ -4,6 +4,8 @@ - + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml index 13115cca44e6..11a95cfece80 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -51,7 +51,6 @@ - @@ -255,7 +254,7 @@ - + diff --git a/RegExpSupport/src/org/intellij/lang/regexp/RegExpBraceMatcher.java b/RegExpSupport/src/org/intellij/lang/regexp/RegExpBraceMatcher.java index 72a92d6b4994..8a773d327165 100644 --- a/RegExpSupport/src/org/intellij/lang/regexp/RegExpBraceMatcher.java +++ b/RegExpSupport/src/org/intellij/lang/regexp/RegExpBraceMatcher.java @@ -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[]{ diff --git a/bin/WinLauncher/WinLauncher.exe b/bin/WinLauncher/WinLauncher.exe index ae38036efcde..cc0124e6ffdd 100644 Binary files a/bin/WinLauncher/WinLauncher.exe and b/bin/WinLauncher/WinLauncher.exe differ diff --git a/bin/WinLauncher/WinLauncher64.exe b/bin/WinLauncher/WinLauncher64.exe index 8bb8a862c4b2..cc48b50acc5b 100644 Binary files a/bin/WinLauncher/WinLauncher64.exe and b/bin/WinLauncher/WinLauncher64.exe differ diff --git a/bin/win/runnerw.exe b/bin/win/runnerw.exe index 47ae098d81f6..0fc194cc49b4 100644 Binary files a/bin/win/runnerw.exe and b/bin/win/runnerw.exe differ diff --git a/build/build.iml b/build/build.iml index 7f6d23f7d97e..075656c3d6a6 100644 --- a/build/build.iml +++ b/build/build.iml @@ -4,12 +4,11 @@ - - + diff --git a/build/conf/classVersions.txt b/build/conf/classVersions.txt index 3585d4fb1f5a..1f1b7d14df85 100644 --- a/build/conf/classVersions.txt +++ b/build/conf/classVersions.txt @@ -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 diff --git a/build/conf/nsis/idea.nsi b/build/conf/nsis/idea.nsi index c764c707fb41..a19d4fce5c5f 100644 --- a/build/conf/nsis/idea.nsi +++ b/build/conf/nsis/idea.nsi @@ -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" +,,' diff --git a/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy new file mode 100644 index 000000000000..0210d1a775aa --- /dev/null +++ b/build/groovy/org/jetbrains/intellij/build/CommunityStandaloneJpsBuilder.groovy @@ -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) + } +} diff --git a/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy index 676a83e4104d..59a7623a7a55 100644 --- a/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy +++ b/build/groovy/org/jetbrains/intellij/build/IdeaCommunityBuilder.groovy @@ -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() } } \ No newline at end of file diff --git a/build/groovy/org/jetbrains/intellij/build/IntelliJCoreArtifactsBuilder.groovy b/build/groovy/org/jetbrains/intellij/build/IntelliJCoreArtifactsBuilder.groovy new file mode 100644 index 000000000000..0decc28762eb --- /dev/null +++ b/build/groovy/org/jetbrains/intellij/build/IntelliJCoreArtifactsBuilder.groovy @@ -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 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 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 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) + } + } +} diff --git a/build/scripts/common_tests.gant b/build/scripts/common_tests.gant index 855ba9bc7e9e..a6778a2b3113 100644 --- a/build/scripts/common_tests.gant +++ b/build/scripts/common_tests.gant @@ -36,7 +36,6 @@ target(compile: "Compile project") { } if (!isDefined("avoid.project.rebuild")) { - clearBuildCaches() projectBuilder.cleanOutput() projectBuilder.buildAll() } diff --git a/build/scripts/layouts.gant b/build/scripts/layouts.gant deleted file mode 100644 index fa96333f7ccb..000000000000 --- a/build/scripts/layouts.gant +++ /dev/null @@ -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") - } - } -} diff --git a/build/scripts/tests.gant b/build/scripts/tests.gant index d23155dfd888..7bba14ef69e0 100644 --- a/build/scripts/tests.gant +++ b/build/scripts/tests.gant @@ -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") diff --git a/build/scripts/unpackJdks.gant b/build/scripts/unpackJdks.gant index f4856042c635..356b40f3f0bb 100644 --- a/build/scripts/unpackJdks.gant +++ b/build/scripts/unpackJdks.gant @@ -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}'") } -} \ No newline at end of file +} + +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 +} diff --git a/build/scripts/utils.gant b/build/scripts/utils.gant index 63272135cc90..ac61c1b1c04c 100644 --- a/build/scripts/utils.gant +++ b/build/scripts/utils.gant @@ -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", -]) diff --git a/community-main.iml b/community-main.iml index 2e77b4c5f704..2d6c5c64c1bf 100644 --- a/community-main.iml +++ b/community-main.iml @@ -3,7 +3,7 @@ - + diff --git a/community-tests/community-tests.iml b/community-tests/community-tests.iml index 142051034725..ee1899fd5ff9 100644 --- a/community-tests/community-tests.iml +++ b/community-tests/community-tests.iml @@ -5,7 +5,7 @@ - + \ No newline at end of file diff --git a/community-tests/src/tests/testGroups.properties b/community-tests/src/tests/testGroups.properties index 8d0bc68bf3e4..33cab98b4005 100644 --- a/community-tests/src/tests/testGroups.properties +++ b/community-tests/src/tests/testGroups.properties @@ -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.* \ No newline at end of file +org.jetbrains.plugins.groovy.* + +[COMPILER_AND_DEBUGGER_TESTS] +com.intellij.compiler.* +org.jetbrains.jps.* \ No newline at end of file diff --git a/images/src/org/intellij/images/editor/impl/ImageEditorUI.java b/images/src/org/intellij/images/editor/impl/ImageEditorUI.java index 072f8199c7a3..39886b604b7f 100644 --- a/images/src/org/intellij/images/editor/impl/ImageEditorUI.java +++ b/images/src/org/intellij/images/editor/impl/ImageEditorUI.java @@ -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(); diff --git a/java/compiler/forms-compiler/forms-compiler.iml b/java/compiler/forms-compiler/forms-compiler.iml index 9eec06a77713..27d957e047a0 100644 --- a/java/compiler/forms-compiler/forms-compiler.iml +++ b/java/compiler/forms-compiler/forms-compiler.iml @@ -1,6 +1,6 @@ - + diff --git a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java index 7965114323b9..13f01113cbb2 100644 --- a/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java +++ b/java/compiler/impl/src/com/intellij/compiler/impl/CompilerErrorTreeView.java @@ -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; } }; diff --git a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java index fb14dee65b5c..2ccfc4450c8a 100644 --- a/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java +++ b/java/compiler/impl/src/com/intellij/compiler/server/BuildManager.java @@ -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 events) { if (!IS_UNIT_TEST_MODE && shouldTriggerMake(events)) { diff --git a/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactWebServerRootsProvider.kt b/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactWebServerRootsProvider.kt index 963a53c7cec4..0b5f5eedb4c2 100644 --- a/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactWebServerRootsProvider.kt +++ b/java/compiler/impl/src/org/jetbrains/builtInWebServer/ArtifactWebServerRootsProvider.kt @@ -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 } diff --git a/java/compiler/javac2/javac2.iml b/java/compiler/javac2/javac2.iml index 5376b3eff57f..4698c6a785ca 100644 --- a/java/compiler/javac2/javac2.iml +++ b/java/compiler/javac2/javac2.iml @@ -1,6 +1,6 @@ - + diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java index 90d46aec3570..d5886c161605 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaExecutionStack.java @@ -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( diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java index 33baee466e43..ee817cd7ef17 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/JavaStackFrame.java @@ -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 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(); diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java b/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java index a23cb058b1e3..8aa3190984cd 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/SourcePositionProvider.java @@ -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 diff --git a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java index 16d5c1e6534e..6c442896a75a 100644 --- a/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/engine/events/DebuggerCommandImpl.java @@ -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(); diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java index 51c59147a5c9..13e54ce4e4c4 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/ui/ClassesFilteredView.java @@ -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() { diff --git a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java index d800af800e42..c23eb3499415 100644 --- a/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java +++ b/java/debugger/impl/src/com/intellij/debugger/memory/utils/StackFrameItem.java @@ -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 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 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 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) { diff --git a/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java b/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java index f6e8a99a688d..d5dd77232aeb 100644 --- a/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java +++ b/java/debugger/impl/src/com/intellij/debugger/settings/CaptureConfigurable.java @@ -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 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 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 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 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 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() { diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java index fbb9a5459a8d..920387526892 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/HotSwapUIImpl.java @@ -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> modifiedClasses, final HotSwapProgressImpl progress) { + UsageTrigger.trigger("debugger.reload.classes"); ProgressManager.getInstance().runProcess(() -> { HotSwapManager.reloadModifiedClasses(modifiedClasses, progress); progress.finished(); diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java index b74626fec330..6c6bacb6d978 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/breakpoints/StackCapturingLineBreakpoint.java @@ -196,42 +196,59 @@ public class StackCapturingLineBreakpoint extends WildcardMethodBreakpoint { } @Nullable - public static List getRelatedStack(@Nullable StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) { - if (frame != null) { - DebugProcessImpl debugProcess = suspendContext.getDebugProcess(); - Map> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS); - if (ContainerUtil.isEmpty(capturedStacks)) { - return null; - } - List 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 getRelatedStack(@NotNull StackFrameProxyImpl frame, @NotNull SuspendContextImpl suspendContext) { + DebugProcessImpl debugProcess = suspendContext.getDebugProcess(); + Map> capturedStacks = debugProcess.getUserData(CAPTURED_STACKS); + if (ContainerUtil.isEmpty(capturedStacks)) { + return null; + } + List 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; } diff --git a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BinaryRenderer.java b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BinaryRenderer.java index fed98cda14d8..6c22965b2d59 100644 --- a/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BinaryRenderer.java +++ b/java/debugger/impl/src/com/intellij/debugger/ui/tree/render/BinaryRenderer.java @@ -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 diff --git a/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java b/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java index 3605f6016c6d..ef55ee92e099 100644 --- a/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java +++ b/java/debugger/openapi/src/com/intellij/debugger/engine/DebuggerUtils.java @@ -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 diff --git a/java/execution/impl/src/com/intellij/execution/testframework/AbstractInClassConfigurationProducer.java b/java/execution/impl/src/com/intellij/execution/testframework/AbstractInClassConfigurationProducer.java index 249042e47c2a..fad949607d47 100644 --- a/java/execution/impl/src/com/intellij/execution/testframework/AbstractInClassConfigurationProducer.java +++ b/java/execution/impl/src/com/intellij/execution/testframework/AbstractInClassConfigurationProducer.java @@ -112,7 +112,8 @@ public abstract class AbstractInClassConfigurationProducer myUsageDescriptors = new FactoryMap() { - @Nullable @Override protected UsageDescriptor create(String key) { return new UsageDescriptor(key, 0); diff --git a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java index 904fe063669f..d12fea7e226c 100644 --- a/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java +++ b/java/idea-ui/src/com/intellij/openapi/roots/ui/configuration/JdkComboBox.java @@ -168,7 +168,7 @@ public class JdkComboBox extends ComboBoxWithWidePopup { + jdksModel.createAddActions(group, JdkComboBox.this, getSelectedJdk(), jdk -> { if (project != null) { final JdkListConfigurable configurable = JdkListConfigurable.getInstance(project); configurable.addJdkNode(jdk, false); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaSoftKeywordHighlighting.kt b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaSoftKeywordHighlighting.kt index 2118bf920b16..de77df2d9d58 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaSoftKeywordHighlighting.kt +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/JavaSoftKeywordHighlighting.kt @@ -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) } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressAllForClassFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressAllForClassFix.java index c690de59b5ce..4bea5a4761e5 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressAllForClassFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressAllForClassFix.java @@ -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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java index cfc60ef5666d..adfe3b75f76e 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressFix.java @@ -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); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressForClassFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressForClassFix.java index 614d852e7645..3695918e758a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressForClassFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/actions/SuppressForClassFix.java @@ -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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java index 6552e5aa2c7c..00ca113a49de 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightClassUtil.java @@ -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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java index b3bb3cacedc6..467f71c2a18d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightUtil.java @@ -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> ourInterfaceIncompatibleModifiers = new THashMap<>(7); - @NotNull private static final Map> ourMethodIncompatibleModifiers = new THashMap<>(11); - @NotNull private static final Map> ourFieldIncompatibleModifiers = new THashMap<>(8); - @NotNull private static final Map> ourClassIncompatibleModifiers = new THashMap<>(8); - @NotNull private static final Map> ourClassInitializerIncompatibleModifiers = new THashMap<>(1); - @NotNull private static final Set 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> ourInterfaceIncompatibleModifiers = new THashMap<>(7); + private static final Map> ourMethodIncompatibleModifiers = new THashMap<>(11); + private static final Map> ourFieldIncompatibleModifiers = new THashMap<>(8); + private static final Map> ourClassIncompatibleModifiers = new THashMap<>(8); + private static final Map> ourClassInitializerIncompatibleModifiers = new THashMap<>(1); + private static final Map> ourModuleIncompatibleModifiers = new THashMap<>(1); + private static final Map> ourRequiresIncompatibleModifiers = new THashMap<>(2); + + private static final Set 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> 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> 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> incompatibleModifierMap = getIncompatibleModifierMap(modifierList); - if (incompatibleModifierMap == null) return null; - return getIncompatibleModifier(modifier, modifierList, incompatibleModifierMap); + Map> 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> incompatibleModifierMap = getIncompatibleModifierMap(modifierList); + Map> incompatibleModifierMap = getIncompatibleModifierMap(modifierOwner); if (incompatibleModifierMap == null) return null; + @PsiModifier.ModifierConstant String modifier = keyword.getText(); Set 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(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java index 239bbbbe89ae..be6855c8f9cb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/HighlightVisitorImpl.java @@ -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)); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java index fed781907c5c..9e271c08e503 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/JavaModuleGraphUtil.java @@ -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 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 relations = MultiMap.create(); Set transitiveEdges = ContainerUtil.newTroveSet(); @@ -144,21 +159,28 @@ public class JavaModuleGraphUtil { private static void visit(PsiJavaModule module, MultiMap relations, Set 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 myGraph; + private final Graph myGraph; private final Set myTransitiveEdges; - public RequiresGraph(OutboundSemiGraph graph, Set transitiveEdges) { + public RequiresGraph(Graph graph, Set transitiveEdges) { myGraph = graph; myTransitiveEdges = transitiveEdges; } @@ -177,6 +199,52 @@ public class JavaModuleGraphUtil { return false; } + public Trinity findConflict(PsiJavaModule source) { + Map 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 processExports(PsiJavaModule start, BiFunction processor) { + return myGraph.getNodes().contains(start) ? processExports(start.getName(), start, 0, ContainerUtil.newHashSet(), processor) : null; + } + + private T processExports(String name, + PsiJavaModule module, + int layer, + Set visited, + BiFunction processor) { + if (visited.add(module)) { + if (layer == 1) { + for (PsiPackageAccessibilityStatement statement : module.getExports()) { + List 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 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(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java index 15b441741786..fa6e57558a86 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/analysis/ModuleHighlightUtil.java @@ -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 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 cycle = JavaModuleGraphUtil.findCycle((PsiJavaModule)target); @@ -246,7 +265,7 @@ public class ModuleHighlightUtil { Stream 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 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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ArgumentFixerActionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ArgumentFixerActionFactory.java index 026709f05226..750017d2d9cf 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ArgumentFixerActionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ArgumentFixerActionFactory.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CastMethodArgumentFix.java b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CastMethodArgumentFix.java index 241f03789a90..efdf93a2bfcf 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CastMethodArgumentFix.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CastMethodArgumentFix.java @@ -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); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/GuessManager.java b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/GuessManager.java index bda14017499e..eac4c2761fa0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/GuessManager.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/GuessManager.java @@ -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 diff --git a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java index 008216001d0e..3bcb8f37b573 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInsight/guess/impl/GuessManagerImpl.java @@ -79,6 +79,7 @@ public class GuessManagerImpl extends GuessManager { myProject = project; } + @NotNull @Override public PsiType[] guessContainerElementType(PsiExpression containerExpr, TextRange rangeToIgnore) { HashSet 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 types = new LinkedHashSet<>(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/JavaSuppressionUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/JavaSuppressionUtil.java index 90d5823dd64e..2c9662fb0e3b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/JavaSuppressionUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/JavaSuppressionUtil.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java index cf9e5c769732..5bb219eb51a1 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/RedundantSuppressInspectionBase.java @@ -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> 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 suppressedTools = new THashMap<>(); InspectionToolWrapper[] toolWrappers = getInspectionTools(psiElement, manager); @@ -246,13 +249,13 @@ public class RedundantSuppressInspectionBase extends GlobalInspectionTool { for (PsiElement suppressedScope : suppressedScopes.keySet()) { Collection 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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java index 5adea60e6a06..e0d4ffb10284 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/NullableMethodAnalysis.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java index d7353d9e2896..6116628627e6 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/bytecodeAnalysis/ProjectBytecodeAnalysis.java @@ -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[]{ diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java index 8a04b9e96a0c..7930f7a621c3 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/compiler/JavacQuirksInspectionVisitor.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractChecker.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractChecker.java index b9a160440209..e6a3436fa072 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractChecker.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractChecker.java @@ -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 myNonViolations = ContainerUtil.newHashSet(); private final Set 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 checkContractClause(PsiMethod method, - MethodContract contract, - boolean ignoreAssertions, final boolean onTheFly) { + static Map 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(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java index 1bdd1b256bd0..ae986149dc20 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ContractInspection.java @@ -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 errors = ContractChecker.checkContractClause(method, contract, false, isOnTheFly); + Map errors = ContractChecker.checkContractClause(method, contract, false); for (Map.Entry entry : errors.entrySet()) { PsiElement element = entry.getKey(); holder.registerProblem(element, entry.getValue()); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java index 66e300dc0b3f..721ccd319cc8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ControlFlowAnalyzer.java @@ -52,7 +52,6 @@ public class ControlFlowAnalyzer extends JavaElementVisitor { private FList 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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java new file mode 100644 index 000000000000..c5a3295229a7 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/CustomMethodHandlers.java @@ -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 handle(DfaValue qualifier, DfaValue[] args, DfaMemoryState memState, DfaValueFactory factory); + } + + private static final CallMapper CUSTOM_METHOD_HANDLERS = new CallMapper() + .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 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 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 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 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 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 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 singleResult(DfaMemoryState state, DfaValue value) { + state.push(value); + return Collections.singletonList(state); + } + + @NotNull + private static List applyCondition(DfaMemoryState memState, + DfaRelationValue trueRelation, + DfaValue trueResult, + DfaRelationValue falseRelation, + DfaValue falseResult) { + DfaMemoryState falseState = memState.createCopy(); + List 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; + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java index 0ca9d67f98b9..6d3e42b93c6f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowInspectionBase.java @@ -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 calls, List qualifiers) { + if (!REPORT_UNCHECKED_OPTIONALS) return; for (Map.Entry 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 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 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()) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java index 3f8943eb0cbf..ba8897e92b00 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DataFlowRunner.java @@ -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 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 processedStates = MultiMap.createSet(); MultiMap 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 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 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 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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java index 9a06ed0a4f15..fd963326c9d4 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaInstructionState.java @@ -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 nextStates = merger.mergeByFacts(group); + List 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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java index 33997bbfdf07..43ef79454fdc 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryState.java @@ -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); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java index ac6d3f44d4d7..64c93cebb731 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaMemoryStateImpl.java @@ -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 distincts = new ArrayList<>(); - for (UnorderedPair 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 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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java index dafc9fa0cfeb..380673d953f0 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaOptionalSupport.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java index 04aefe4a52c8..91dd8e0f9060 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaPsiUtil.java @@ -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> compute() { final PsiCodeBlock body = constructor.getBody(); final Map 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) || diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java index ddf626ed6d30..e37d308cecef 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaUtil.java @@ -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 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> 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 getPossibleInitializationElements(final PsiElement qualifierExpression) { + public static Collection 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 variableValues = getCachedVariableValues((PsiVariable)targetElement, qualifierExpression); + Collection 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 map = ((ValuableDataFlowRunner.MyDfaMemoryState)memState).getVariableStates(); for (Map.Entry entry : map.entrySet()) { ValuableDataFlowRunner.ValuableDfaVariableState state = (ValuableDataFlowRunner.ValuableDfaVariableState)entry.getValue(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java index e61e10abf342..96d2657c3920 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/DfaVariableState.java @@ -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 myInstanceofValues; @NotNull final Set 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 instanceofValues, - @NotNull Set notInstanceofValues, - @NotNull Nullness nullability, - @NotNull ThreeState optionalPresence) { + @NotNull Set 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 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 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 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 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 instanceofValues, @NotNull Set 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; + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java index 5092bebd2dc2..4d53a925a89d 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/HardcodedContracts.java @@ -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 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(); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java index c962fdd6a156..b61aa1015216 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/MethodContract.java @@ -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); + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java index 00a28620bb0f..7c25483a22cf 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/NullParameterConstraintChecker.java @@ -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 myPossiblyViolatedParameters; private final Set myUsedParameters; - private NullParameterConstraintChecker(Collection parameters, boolean isOnTheFly) { - super(false, true, isOnTheFly); + private NullParameterConstraintChecker(Collection 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)); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java index 6535ee6ce46c..41ecc118b276 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardDataFlowRunner.java @@ -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); } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java index 9b4e2984cdec..6d1d14de2902 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StandardInstructionVisitor.java @@ -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 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 OPTIONAL_METHOD_NAMES = ContainerUtil + .set("of", "ofNullable", "fromNullable", "empty", "absent", "or", "orElseGet", "ifPresent", "map", "flatMap", "filter", "transform"); + private static final CallMapper KNOWN_METHOD_RANGES = new CallMapper() + .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 myReachable = new THashSet<>(); private final Set myCanBeNullInInstanceof = new THashSet<>(); private final MultiMap 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 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 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 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 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 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 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 addContractResults(DfaValue[] argValues, - MethodContract contract, - LinkedHashSet states, - MethodCallInstruction instruction, - DfaValueFactory factory, - Set finalStates) { + private LinkedHashSet addContractResults(DfaValue qualifier, + DfaValue[] argValues, + MethodContract contract, + LinkedHashSet states, + MethodCallInstruction instruction, + DfaValueFactory factory, + Set finalStates) { DfaConstValue.Factory constFactory = factory.getConstFactory(); LinkedHashSet 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 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(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java index 4ad30e86b454..d5e2f1ce1e89 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/StateMerger.java @@ -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> myFacts = ContainerUtil.newIdentityHashMap(); private final Map> myCopyCache = ContainerUtil.newIdentityHashMap(); @@ -92,7 +94,13 @@ class StateMerger { @NotNull private LinkedHashSet getUnrelatedFacts(@NotNull final Fact fact, @NotNull DfaMemoryStateImpl state) { - return new LinkedHashSet<>(ContainerUtil.filter(getFacts(state), another -> !fact.invalidatesFact(another))); + final LinkedHashSet 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 mergedGroup, @NotNull DfaMemoryStateImpl state) { @@ -203,6 +211,122 @@ class StateMerger { return replacements.getMergeResult(); } + @Nullable + List mergeByRanges(List 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> ranges = createRangeMap(states); + boolean changed = false; + for (Map map : ranges.values()) { + for (Map.Entry 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> 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 merged = mergeIndependentRanges(states, ranges); + if(merged != null) return merged; + return dropExcessRangeInfo(states, ranges.keySet()); + } + + @NotNull + private static Map> createRangeMap(List states) { + Map> ranges = new LinkedHashMap<>(); + for (DfaMemoryStateImpl state : states) { + ProgressManager.checkCanceled(); + Map variableStates = state.getVariableStates(); + for (Map.Entry 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 mergeIndependentRanges(List states, Map> 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> 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 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 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 dropExcessRangeInfo(List states, Set 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 complementary) { if (complementary.size() < 2) return false; @@ -220,10 +344,7 @@ class StateMerger { @NotNull private DfaMemoryStateImpl copyWithoutVar(@NotNull DfaMemoryStateImpl state, @NotNull DfaVariableValue var) { - Map map = myCopyCache.get(state); - if (map == null) { - myCopyCache.put(state, map = ContainerUtil.newIdentityHashMap()); - } + Map 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 classInfo = new IdentityHashMap<>(); + for (EqClass eqClass : state.getNonTrivialEqClasses()) { - DfaValue constant = eqClass.findConstant(true); - List vars = eqClass.getVariables(false); - for (DfaVariableValue var : vars) { + EqClassInfo info = classInfo.computeIfAbsent(eqClass, EqClassInfo::new); + DfaValue constant = info.constant; + List 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 classPair : state.getDistinctClassPairs()) { - List vars1 = classPair.first.getVariables(false); - List vars2 = classPair.second.getVariables(false); - - LinkedHashSet 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 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 states = state.getVariableStates(); - for (DfaVariableValue var : states.keySet()) { - DfaVariableState variableState = states.get(var); + for (Map.Entry 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 vars; + final DfaValue constant; + + EqClassInfo(EqClass eqClass) { + vars = eqClass.getVariables(false); + constant = eqClass.findConstant(true); + } + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java index addf17873a3c..73b7f0cb897f 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/ValuableDataFlowRunner.java @@ -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 instanceofValues, Set notInstanceofValues, Nullness nullability, DfaValue value, - @NotNull FList concatenation, ThreeState optionalPresence) { - super(instanceofValues, notInstanceofValues, nullability, optionalPresence); + @NotNull FList 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 instanceofValues, @NotNull Set 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 concatenation) { if (concatenation == myConcatenation) return this; return new ValuableDfaVariableState(myInstanceofValues, myNotInstanceofValues, myNullability, myValue, concatenation, - myOptionalPresence); + myOptionalPresence, myRange); } @Override diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java index 6e64a6b86416..66c13658febb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/instructions/BinopInstruction.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java new file mode 100644 index 000000000000..306c91c01bd2 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSet.java @@ -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. + *

+ * 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(); + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java index 5960f87b6933..c27408783297 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaExpressionFactory.java @@ -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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRangeValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRangeValue.java new file mode 100644 index 000000000000..47825a92cf30 --- /dev/null +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRangeValue.java @@ -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 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)); + } + } +} diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java index cb2d4f1689ce..c77b320603ec 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaRelationValue.java @@ -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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java index dc5ccaf45280..fd29e0c5b37a 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaValueFactory.java @@ -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; + } } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java index f8265fdaabbd..5ac12eaa3dbb 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/dataFlow/value/DfaVariableValue.java @@ -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; } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java index 5c14b72ded54..41a6140e9333 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/deprecation/DeprecationInspection.java @@ -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; diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java index ce22eee0149c..1e1edf8938d8 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/java15api/Java15APIUsageInspectionBase.java @@ -15,16 +15,15 @@ */ package com.intellij.codeInspection.java15api; -import com.intellij.ToolExtensionPoints; import com.intellij.codeHighlighting.HighlightDisplayLevel; import com.intellij.codeInsight.AnnotationUtil; import com.intellij.codeInsight.daemon.GroupNames; import com.intellij.codeInsight.intention.QuickFixFactory; -import com.intellij.codeInspection.*; +import com.intellij.codeInspection.BaseJavaBatchLocalInspectionTool; +import com.intellij.codeInspection.InspectionsBundle; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemsHolder; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.extensions.ExtensionPoint; -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.module.EffectiveLanguageLevelUtil; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; @@ -63,8 +62,6 @@ import java.util.Set; */ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTool { public static final String SHORT_NAME = "Since15"; - public static final ExtensionPointName EP_NAME = - ExtensionPointName.create(ToolExtensionPoints.JAVA15_INSPECTION_TOOL); private static final String EFFECTIVE_LL = "effectiveLL"; @@ -184,7 +181,6 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo private class MyVisitor extends JavaElementVisitor { private final ProblemsHolder myHolder; private final boolean myOnTheFly; - private final ExtensionPoint point = Extensions.getRootArea().getExtensionPoint(EP_NAME); public MyVisitor(final ProblemsHolder holder, boolean onTheFly) { myHolder = holder; @@ -238,7 +234,8 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo final Module module = ModuleUtilCore.findModuleForPsiElement(reference.getElement()); if (module != null) { final LanguageLevel languageLevel = getEffectiveLanguageLevel(module); - if (isForbiddenApiUsage((PsiMember)resolved, languageLevel)) { + LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel((PsiMember)resolved, languageLevel); + if (sinceLanguageLevel != null) { PsiClass psiClass = null; final PsiElement qualifier = reference.getQualifier(); if (qualifier != null) { @@ -255,7 +252,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo if (isIgnored(superClass)) return; } } - registerError(reference, languageLevel); + registerError(reference, sinceLanguageLevel); } else if (resolved instanceof PsiClass && isInProject(reference)&& !languageLevel.isAtLeast(LanguageLevel.JDK_1_7)) { final PsiReferenceParameterList parameterList = reference.getParameterList(); if (parameterList != null && parameterList.getTypeParameterElements().length > 0) { @@ -301,8 +298,9 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo if (module != null) { final LanguageLevel languageLevel = getEffectiveLanguageLevel(module); if (constructor instanceof PsiCompiledElement) { - if (isForbiddenApiUsage(constructor, languageLevel)) { - registerError(expression.getClassReference(), languageLevel); + LanguageLevel sinceLanguageLevel = getLastIncompatibleLanguageLevel(constructor, languageLevel); + if (sinceLanguageLevel != null) { + registerError(expression.getClassReference(), sinceLanguageLevel); } } } @@ -314,12 +312,14 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo PsiAnnotation annotation = !method.isConstructor() ? AnnotationUtil.findAnnotation(method, CommonClassNames.JAVA_LANG_OVERRIDE) : null; if (annotation != null) { final Module module = ModuleUtilCore.findModuleForPsiElement(annotation); + LanguageLevel sinceLanguageLevel = null; if (module != null) { final LanguageLevel languageLevel = getEffectiveLanguageLevel(module); final PsiMethod[] methods = method.findSuperMethods(); for (PsiMethod superMethod : methods) { if (superMethod instanceof PsiCompiledElement) { - if (!isForbiddenApiUsage(superMethod, languageLevel)) { + sinceLanguageLevel = getLastIncompatibleLanguageLevel(superMethod, languageLevel); + if (sinceLanguageLevel == null) { return; } } @@ -328,7 +328,7 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo } } if (methods.length > 0) { - registerError(annotation.getNameReferenceElement(), languageLevel); + registerError(annotation.getNameReferenceElement(), sinceLanguageLevel); } } } @@ -342,19 +342,9 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo private void registerError(PsiJavaCodeReferenceElement reference, LanguageLevel api) { if (reference != null && isInProject(reference)) { //noinspection DialogTitleCapitalization - myHolder.registerProblem(reference, InspectionsBundle.message("inspection.1.5.problem.descriptor", getShortName(api))); - } - } - - @Override - public void visitFile(PsiFile file) { - for (FileCheckingInspection inspection : point.getExtensions()) { - ProblemDescriptor[] descriptors = inspection.checkFile(file, InspectionManager.getInstance(file.getProject()), myOnTheFly); - if (descriptors != null) { - for (ProblemDescriptor descriptor : descriptors) { - myHolder.registerProblem(descriptor); - } - } + myHolder.registerProblem(reference, + InspectionsBundle.message("inspection.1.5.problem.descriptor", getShortName(api)), + myOnTheFly ? new LocalQuickFix[] {(LocalQuickFix)QuickFixFactory.getInstance().createIncreaseLanguageLevelFix(LanguageLevel.values()[api.ordinal() + 1])} : null); } } } @@ -364,33 +354,32 @@ public class Java15APIUsageInspectionBase extends BaseJavaBatchLocalInspectionTo return presentableText.substring(0, presentableText.indexOf(' ')); } - public static boolean isForbiddenApiUsage(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) { - if (member instanceof PsiAnonymousClass) return false; + public static LanguageLevel getLastIncompatibleLanguageLevel(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) { + if (member instanceof PsiAnonymousClass) return null; PsiClass containingClass = member.getContainingClass(); - if (containingClass instanceof PsiAnonymousClass) return false; - if (member instanceof PsiClass && !(member.getParent() instanceof PsiClass || member.getParent() instanceof PsiFile)) return false; + if (containingClass instanceof PsiAnonymousClass) return null; + if (member instanceof PsiClass && !(member.getParent() instanceof PsiClass || member.getParent() instanceof PsiFile)) return null; - return isForbiddenSignature(member, languageLevel) || - containingClass != null && isForbiddenApiUsage(containingClass, languageLevel); - - } - - private static boolean isForbiddenSignature(@NotNull PsiMember member, @NotNull LanguageLevel languageLevel) { Set forbiddenApi = getForbiddenApi(languageLevel); String signature = getSignature(member); - return forbiddenApi != null && signature != null && isForbiddenSignature(signature, languageLevel, forbiddenApi); + if (forbiddenApi != null && signature != null) { + LanguageLevel lastIncompatibleLanguageLevel = getLastIncompatibleLanguageLevelForSignature(signature, languageLevel, forbiddenApi); + if (lastIncompatibleLanguageLevel != null) return lastIncompatibleLanguageLevel; + } + return containingClass != null ? getLastIncompatibleLanguageLevel(containingClass, languageLevel) : null; + } - private static boolean isForbiddenSignature(@NotNull String signature, @NotNull LanguageLevel languageLevel, @NotNull Set forbiddenApi) { + private static LanguageLevel getLastIncompatibleLanguageLevelForSignature(@NotNull String signature, @NotNull LanguageLevel languageLevel, @NotNull Set forbiddenApi) { if (forbiddenApi.contains(signature)) { - return true; + return languageLevel; } if (languageLevel.compareTo(ourHighestKnownLanguage) == 0) { - return false; + return null; } LanguageLevel nextLanguageLevel = LanguageLevel.values()[languageLevel.ordinal() + 1]; Set nextForbiddenApi = getForbiddenApi(nextLanguageLevel); - return nextForbiddenApi != null && isForbiddenSignature(signature, nextLanguageLevel, nextForbiddenApi); + return nextForbiddenApi != null ? getLastIncompatibleLanguageLevelForSignature(signature, nextLanguageLevel, nextForbiddenApi) : null; } /** diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java index 593f2ce0c61c..f7bf4529e25b 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/javaDoc/JavaDocReferenceInspectionBase.java @@ -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. @@ -106,14 +106,18 @@ public class JavaDocReferenceInspectionBase extends BaseJavaBatchLocalInspectio @Nullable @Override public ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull InspectionManager manager, boolean isOnTheFly) { - if (!PsiPackage.PACKAGE_INFO_FILE.equals(file.getName()) || !(file instanceof PsiJavaFile)) { - return null; + final String fileName = file.getName(); + if (PsiPackage.PACKAGE_INFO_FILE.equals(fileName)) { + final PsiDocComment docComment = PsiTreeUtil.getChildOfType(file, PsiDocComment.class); + return checkComment(docComment, file, manager, isOnTheFly); } - final PsiDocComment docComment = PsiTreeUtil.getChildOfType(file, PsiDocComment.class); - final PsiJavaFile javaFile = (PsiJavaFile)file; - final String packageName = javaFile.getPackageName(); - final PsiPackage aPackage = JavaPsiFacade.getInstance(file.getProject()).findPackage(packageName); - return checkComment(docComment, aPackage, manager, isOnTheFly); + else if (PsiJavaModule.MODULE_INFO_FILE.equals(fileName)) { + final PsiJavaModule module = PsiTreeUtil.getChildOfType(file, PsiJavaModule.class); + if (module != null) { + return checkComment(module.getDocComment(), file, manager, isOnTheFly); + } + } + return null; } @Override @@ -139,7 +143,7 @@ public class JavaDocReferenceInspectionBase extends BaseJavaBatchLocalInspectio return checkComment(docCommentOwner.getDocComment(), docCommentOwner, manager, isOnTheFly); } - private ProblemDescriptor[] checkComment(PsiDocComment docComment, PsiElement context, InspectionManager manager, boolean isOnTheFly) { + private ProblemDescriptor[] checkComment(@Nullable PsiDocComment docComment, PsiElement context, InspectionManager manager, boolean isOnTheFly) { if (docComment == null) return null; final List problems = new ArrayList<>(); diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java index 9dced73577b8..f82c948ee070 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/nullable/NullableStuffInspectionBase.java @@ -477,6 +477,7 @@ public class NullableStuffInspectionBase extends BaseJavaBatchLocalInspectionToo private boolean isNonAnnotatedOverridingNotNull(PsiMethod method, PsiMethod superMethod) { return REPORT_NOT_ANNOTATED_METHOD_OVERRIDES_NOTNULL && !(method.getReturnType() instanceof PsiPrimitiveType) && + !method.isConstructor() && !getNullityManager(method).hasNullability(method) && isNotNullNotInferred(superMethod, true, IGNORE_EXTERNAL_SUPER_NOTNULL); } diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java index 37078c49a367..5f6cee2b7d22 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspectionBase.java @@ -61,6 +61,7 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe return uncheckedCb; } + @NotNull private static LocalQuickFix[] getChangeVariableTypeFixes(@NotNull PsiVariable parameter, @Nullable PsiType itemType, LocalQuickFix[] generifyFixes) { if (itemType instanceof PsiMethodReferenceType) return generifyFixes; LOG.assertTrue(parameter.isValid()); @@ -148,8 +149,9 @@ public class UncheckedWarningLocalInspectionBase extends BaseJavaBatchLocalInspe }; } + @NotNull protected LocalQuickFix[] createFixes() { - return null; + return LocalQuickFix.EMPTY_ARRAY; } private static String isMethodCalledOnRawType(PsiElement expression) { diff --git a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java index 9263824d1015..58ca12a62866 100644 --- a/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java +++ b/java/java-analysis-impl/src/com/intellij/codeInspection/visibility/VisibilityInspection.java @@ -223,7 +223,8 @@ public class VisibilityInspection extends GlobalJavaBatchInspectionTool { return null; } - private static CommonProblemDescriptor[] createDescriptions(RefElement refElement, String access, + @NotNull + private static CommonProblemDescriptor[] createDescriptions(RefElement refElement, String access, @NotNull InspectionManager manager, @NotNull GlobalInspectionContext globalContext) { final PsiElement element = refElement.getElement(); @@ -250,7 +251,7 @@ public class VisibilityInspection extends GlobalJavaBatchInspectionTool { new AcceptSuggestedAccess(globalContext.getRefManager(), access, quickFixName), ProblemHighlightType.GENERIC_ERROR_OR_WARNING, false)}; } - return null; + return CommonProblemDescriptor.EMPTY_ARRAY; } int getMinVisibilityLevel(PsiMember member) { diff --git a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java index 26b37eb61212..a9a632fedb00 100644 --- a/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java +++ b/java/java-analysis-impl/src/com/intellij/refactoring/util/duplicates/DuplicatesFinder.java @@ -43,14 +43,14 @@ import java.util.*; public class DuplicatesFinder { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.util.duplicates.DuplicatesFinder"); public static final Key> PARAMETER = Key.create("PARAMETER"); - private final PsiElement[] myPattern; + @NotNull private final PsiElement[] myPattern; private final InputVariables myParameters; private final List myOutputParameters; private final List myPatternAsList; private boolean myMultipleExitPoints; @Nullable private final ReturnValue myReturnValue; - public DuplicatesFinder(PsiElement[] pattern, + public DuplicatesFinder(@NotNull PsiElement[] pattern, InputVariables parameters, @Nullable ReturnValue returnValue, @NotNull List outputParameters @@ -102,6 +102,7 @@ public class DuplicatesFinder { return myParameters; } + @NotNull public PsiElement[] getPattern() { return myPattern; } @@ -674,6 +675,7 @@ public class DuplicatesFinder { return false; } + @NotNull public static PsiElement[] getFilteredChildren(PsiElement element1) { PsiElement[] children1 = element1.getChildren(); ArrayList array = new ArrayList<>(); diff --git a/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java b/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java index dcbf9d18d290..188ad1a334a9 100644 --- a/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java +++ b/java/java-analysis-impl/src/org/jetbrains/java/generate/GenerateToStringUtils.java @@ -24,6 +24,7 @@ import org.jetbrains.java.generate.config.FilterPattern; import org.jetbrains.java.generate.psi.PsiAdapter; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; /** @@ -60,9 +61,27 @@ public class GenerateToStringUtils { FilterPattern pattern) { if (log.isDebugEnabled()) log.debug("Filtering fields using the pattern: " + pattern); List availableFields = new ArrayList<>(); + collectAvailableFields(clazz, includeSuperClass, pattern, availableFields, new HashSet<>()); + return availableFields.toArray(new PsiField[availableFields.size()]); + } + + private static void collectAvailableFields(PsiClass clazz, + boolean includeSuperClass, + FilterPattern pattern, + List availableFields, + HashSet visited) { + + int sortElements = GenerateToStringContext.getConfig().getSortElements(); + + if (includeSuperClass && sortElements == 3) { + PsiClass superClass = clazz.getSuperClass(); + if (superClass != null && visited.add(superClass)) { + collectAvailableFields(superClass, true, pattern, availableFields, visited); + } + } // performs til filtering process - PsiField[] fields = includeSuperClass ? clazz.getAllFields() : clazz.getFields(); + PsiField[] fields = includeSuperClass && sortElements != 3 ? clazz.getAllFields() : clazz.getFields(); for (PsiField field : fields) { if (!JavaResolveUtil.isAccessible(field, field.getContainingClass(), field.getModifierList(), clazz, null, null)) { continue; @@ -72,8 +91,6 @@ public class GenerateToStringUtils { availableFields.add(field); } } - - return availableFields.toArray(new PsiField[availableFields.size()]); } /** @@ -93,7 +110,21 @@ public class GenerateToStringUtils { public static PsiMethod[] filterAvailableMethods(PsiClass clazz, @NotNull FilterPattern pattern) { if (log.isDebugEnabled()) log.debug("Filtering methods using the pattern: " + pattern); List availableMethods = new ArrayList<>(); - PsiMethod[] methods = clazz.getAllMethods(); + collectAvailableMethods(clazz, pattern, availableMethods, new HashSet<>()); + return availableMethods.toArray(new PsiMethod[availableMethods.size()]); + } + + private static void collectAvailableMethods(PsiClass clazz, @NotNull FilterPattern pattern, + List availableMethods, + HashSet visited) { + int sortElements = GenerateToStringContext.getConfig().getSortElements(); + if (sortElements == 3) { + PsiClass superClass = clazz.getSuperClass(); + if (superClass != null && visited.add(superClass)) { + collectAvailableMethods(superClass, pattern, availableMethods, visited); + } + } + PsiMethod[] methods = sortElements != 3 ? clazz.getAllMethods() : clazz.getMethods(); for (PsiMethod method : methods) { // the method should be a getter if (!PsiAdapter.isGetterMethod(method)) { @@ -133,6 +164,5 @@ public class GenerateToStringUtils { log.debug("Adding the method " + methodName + " as there is not a field for this getter"); availableMethods.add(method); } - return availableMethods.toArray(new PsiMethod[availableMethods.size()]); } } diff --git a/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java b/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java index 53381892e3cf..0dcb61bd199b 100644 --- a/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java +++ b/java/java-analysis-impl/src/org/jetbrains/java/generate/config/PolicyOptions.java @@ -15,6 +15,8 @@ */ package org.jetbrains.java.generate.config; +import org.jetbrains.annotations.NotNull; + /** * Options for the various policies. */ @@ -30,6 +32,7 @@ public class PolicyOptions { * Gets the options for the the insert new method policy. * @return the options for the the insert new method policy. */ + @NotNull public static InsertWhere[] getNewMethodOptions() { return newMethodOptions; } @@ -38,6 +41,7 @@ public class PolicyOptions { * Gets the options for the the conflict resolution policy. * @return the options for the the conflict resolution policy. */ + @NotNull public static DuplicationPolicy[] getConflictOptions() { return conflictOptions; } diff --git a/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java b/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java index f4b3ddbfe205..3c923deb54f5 100644 --- a/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java +++ b/java/java-analysis-impl/src/org/jetbrains/java/generate/psi/PsiAdapter.java @@ -18,7 +18,6 @@ package org.jetbrains.java.generate.psi; import com.intellij.openapi.project.Project; import com.intellij.openapi.projectRoots.JavaSdkVersion; import com.intellij.openapi.projectRoots.JavaVersionService; -import com.intellij.openapi.projectRoots.JdkVersionUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; @@ -643,6 +642,7 @@ public class PsiAdapter { * @param clazz the class * @return the names. */ + @NotNull public static String[] getImplementsClassnames(PsiClass clazz) { PsiClass[] interfaces = clazz.getInterfaces(); diff --git a/java/java-impl/java-impl.iml b/java/java-impl/java-impl.iml index 1f9fd8301eef..85c83c42d0d0 100644 --- a/java/java-impl/java-impl.iml +++ b/java/java-impl/java-impl.iml @@ -14,7 +14,6 @@ - diff --git a/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.form b/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.form index d695399ec13e..ad9c2c8a30dd 100644 --- a/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.form +++ b/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.form @@ -3,7 +3,7 @@ - + diff --git a/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.java b/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.java index 007e62e98b21..e4da87b1dfc1 100644 --- a/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.java +++ b/java/java-impl/src/com/intellij/application/options/editor/JavaAutoImportOptions.java @@ -21,6 +21,7 @@ package com.intellij.application.options.editor; import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.CodeInsightWorkspaceSettings; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzerSettings; import com.intellij.openapi.application.ApplicationBundle; @@ -35,7 +36,7 @@ public class JavaAutoImportOptions implements AutoImportOptionsProvider { private static final String INSERT_IMPORTS_ASK = ApplicationBundle.message("combobox.insert.imports.ask"); private static final String INSERT_IMPORTS_NONE = ApplicationBundle.message("combobox.insert.imports.none"); - private JComboBox mySmartPasteCombo; + private JComboBox mySmartPasteCombo; private JCheckBox myCbShowImportPopup; private JPanel myWholePanel; private JCheckBox myCbAddUnambiguousImports; @@ -43,8 +44,11 @@ public class JavaAutoImportOptions implements AutoImportOptionsProvider { private JCheckBox myCbOptimizeImports; private JPanel myExcludeFromImportAndCompletionPanel; private final ExcludeTable myExcludePackagesTable; + private final Project myProject; public JavaAutoImportOptions(Project project) { + myProject = project; + mySmartPasteCombo.addItem(INSERT_IMPORTS_ALWAYS); mySmartPasteCombo.addItem(INSERT_IMPORTS_ASK); mySmartPasteCombo.addItem(INSERT_IMPORTS_NONE); @@ -77,7 +81,7 @@ public class JavaAutoImportOptions implements AutoImportOptionsProvider { myCbShowImportPopup.setSelected(daemonSettings.isImportHintEnabled()); - myCbOptimizeImports.setSelected(codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY); + myCbOptimizeImports.setSelected(CodeInsightWorkspaceSettings.getInstance(myProject).optimizeImportsOnTheFly); myCbAddUnambiguousImports.setSelected(codeInsightSettings.ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY); myCbAddMethodImports.setSelected(codeInsightSettings.ADD_MEMBER_IMPORTS_ON_THE_FLY); @@ -94,7 +98,7 @@ public class JavaAutoImportOptions implements AutoImportOptionsProvider { codeInsightSettings.ADD_IMPORTS_ON_PASTE = getSmartPasteValue(); daemonSettings.setImportHintEnabled(myCbShowImportPopup.isSelected()); - codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY = myCbOptimizeImports.isSelected(); + CodeInsightWorkspaceSettings.getInstance(myProject).optimizeImportsOnTheFly = myCbOptimizeImports.isSelected(); codeInsightSettings.ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = myCbAddUnambiguousImports.isSelected(); codeInsightSettings.ADD_MEMBER_IMPORTS_ON_THE_FLY = myCbAddMethodImports.isSelected(); @@ -114,7 +118,7 @@ public class JavaAutoImportOptions implements AutoImportOptionsProvider { DaemonCodeAnalyzerSettings daemonSettings = DaemonCodeAnalyzerSettings.getInstance(); boolean isModified = isModified(myCbShowImportPopup, daemonSettings.isImportHintEnabled()); - isModified |= isModified(myCbOptimizeImports, codeInsightSettings.OPTIMIZE_IMPORTS_ON_THE_FLY); + isModified |= isModified(myCbOptimizeImports, CodeInsightWorkspaceSettings.getInstance(myProject).optimizeImportsOnTheFly); isModified |= isModified(myCbAddUnambiguousImports, codeInsightSettings.ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY); isModified |= isModified(myCbAddMethodImports, codeInsightSettings.ADD_MEMBER_IMPORTS_ON_THE_FLY); diff --git a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java index d4fb28810d04..59b820e44cbd 100644 --- a/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/CodeInsightUtil.java @@ -202,6 +202,7 @@ public class CodeInsightUtil { }; } + @NotNull public static PsiExpression[] findExpressionOccurrences(PsiElement scope, PsiExpression expr) { List array = new ArrayList<>(); addExpressionOccurrences(RefactoringUtil.unparenthesizeExpression(expr), array, scope); @@ -231,6 +232,7 @@ public class CodeInsightUtil { } } + @NotNull public static PsiExpression[] findReferenceExpressions(PsiElement scope, PsiElement referee) { ArrayList array = new ArrayList<>(); if (scope != null) { @@ -364,6 +366,12 @@ public class CodeInsightUtil { for (PsiTypeParameter inheritorParameter : PsiUtil.typeParametersIterable(inheritor)) { for (PsiTypeParameter baseParameter : PsiUtil.typeParametersIterable(baseClass)) { final PsiType substituted = superSubstitutor.substitute(baseParameter); + PsiClass inheritorCandidateParameter = PsiUtil.resolveClassInType(substituted); + if (inheritorCandidateParameter instanceof PsiTypeParameter && + ((PsiTypeParameter)inheritorCandidateParameter).getOwner() == inheritor && + inheritorCandidateParameter != inheritorParameter) { + continue; + } PsiType arg = baseSubstitutor.substitute(baseParameter); if (arg instanceof PsiWildcardType) { PsiType bound = ((PsiWildcardType)arg).getBound(); @@ -374,7 +382,7 @@ public class CodeInsightUtil { arg, true, PsiUtil.getLanguageLevel(context)); - if (PsiType.NULL.equals(substitution) || substitution != null && substitution.equalsToText(CommonClassNames.JAVA_LANG_OBJECT) || substitution instanceof PsiWildcardType) continue; + if (PsiType.NULL.equals(substitution) || substitution instanceof PsiWildcardType) continue; if (substitution == null) { result.consume(createType(inheritor, facade.getElementFactory().createRawSubstitutor(inheritor), arrayDim)); return true; diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java index f587d9597ebc..ad9d0aaa94f0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypeUtil.java @@ -29,6 +29,7 @@ import java.util.*; public class ExpectedTypeUtil { private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.ExpectedTypeUtil"); + @NotNull public static ExpectedTypeInfo[] intersect(List typeInfos) { if (typeInfos.isEmpty()) return ExpectedTypeInfo.EMPTY_ARRAY; @@ -88,6 +89,7 @@ public class ExpectedTypeUtil { return myInfos.iterator(); } + @NotNull public ExpectedTypeInfo[] toArray() { return myInfos.toArray(new ExpectedTypeInfo[myInfos.size()]); } diff --git a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java index 85fd8b31e184..8f684a61093b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/ExpectedTypesProvider.java @@ -155,6 +155,7 @@ public class ExpectedTypesProvider { return visitor.getResult(); } + @NotNull public static PsiType[] processExpectedTypes(@NotNull ExpectedTypeInfo[] infos, @NotNull PsiTypeVisitor visitor, @NotNull Project project) { LinkedHashSet set = new LinkedHashSet<>(); @@ -985,8 +986,8 @@ public class ExpectedTypesProvider { PsiSubstitutor substitutor; if (candidateInfo instanceof MethodCandidateInfo) { final MethodCandidateInfo info = (MethodCandidateInfo)candidateInfo; - substitutor = MethodCandidateInfo.ourOverloadGuard - .doPreventingRecursion(argumentList, false, () -> info.inferTypeArguments(policy, args, true)); + substitutor = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(argumentList, false, + () -> info.inferSubstitutorFromArgs(policy, args)); if (!info.isStaticsScopeCorrect() && !method.hasModifierProperty(PsiModifier.STATIC)) continue; } else { @@ -999,8 +1000,7 @@ public class ExpectedTypesProvider { if (leftArgs != null && candidateInfo instanceof MethodCandidateInfo) { substitutor = MethodCandidateInfo.ourOverloadGuard.doPreventingRecursion(argumentList, false, - () -> ((MethodCandidateInfo)candidateInfo) - .inferTypeArguments(policy, leftArgs, true)); + () -> ((MethodCandidateInfo)candidateInfo).inferSubstitutorFromArgs(policy, leftArgs)); if (substitutor != null) { inferMethodCallArgumentTypes(argument, forCompletion, leftArgs, index, method, substitutor, array); } diff --git a/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java b/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java index 27c06e42df1c..be15abead8b3 100644 --- a/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java +++ b/java/java-impl/src/com/intellij/codeInsight/FunctionalInterfaceSuggester.java @@ -144,7 +144,7 @@ public class FunctionalInterfaceSuggester { final Project project = element.getProject(); final Set types = new HashSet<>(); final Processor consumer = member -> { - if (member instanceof PsiClass && !Java15APIUsageInspectionBase.isForbiddenApiUsage(member, PsiUtil.getLanguageLevel(element))) { + if (member instanceof PsiClass && Java15APIUsageInspectionBase.getLastIncompatibleLanguageLevel(member, PsiUtil.getLanguageLevel(element)) == null) { if (!JavaResolveUtil.isAccessible(member, null, member.getModifierList(), element, null, null)) { return true; } diff --git a/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java b/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java index 9dcb57ddb859..076c8b39aa6c 100644 --- a/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java +++ b/java/java-impl/src/com/intellij/codeInsight/JavaTargetElementEvaluator.java @@ -280,6 +280,7 @@ public class JavaTargetElementEvaluator extends TargetElementEvaluatorEx2 implem return super.acceptImplementationForReference(reference, element); } + @Nullable private static PsiClass[] getClassesWithMember(final PsiReference reference, final PsiMember member) { return ApplicationManager.getApplication().runReadAction(new Computable() { @Override diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java index 60b2bad07a46..a992b442d55b 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionSorting.java @@ -92,6 +92,7 @@ public class JavaCompletionSorting { return result.withRelevanceSorter(sorter); } + @NotNull private static ExpectedTypeInfo[] getExpectedTypesWithDfa(CompletionParameters parameters, PsiElement position) { if (psiElement().beforeLeaf(psiElement().withText(".")).accepts(position)) { return ExpectedTypeInfo.EMPTY_ARRAY; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java index e1c23442c8d7..a450028e9869 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaCompletionUtil.java @@ -481,7 +481,7 @@ public class JavaCompletionUtil { private static boolean shouldMarkRed(@NotNull Object object, @NotNull PsiElement place) { if (!(object instanceof PsiMember)) return false; - if (Java15APIUsageInspectionBase.isForbiddenApiUsage((PsiMember)object, PsiUtil.getLanguageLevel(place))) return true; + if (Java15APIUsageInspectionBase.getLastIncompatibleLanguageLevel((PsiMember)object, PsiUtil.getLanguageLevel(place)) != null) return true; if (object instanceof PsiEnumConstant) { return findConstantsUsedInSwitch(place).contains(CompletionUtil.getOriginalOrSelf((PsiEnumConstant)object)); @@ -806,7 +806,8 @@ public class JavaCompletionUtil { PsiElement composite = leaf == null ? null : leaf.getParent(); if (composite instanceof PsiMethodReferenceExpression && LambdaHighlightingUtil.insertSemicolon(composite.getParent())) { insertAdditionalSemicolon = false; - } else if (composite instanceof PsiReferenceExpression) { + } + else if (composite instanceof PsiReferenceExpression) { PsiElement parent = composite.getParent(); if (parent instanceof PsiMethodCallExpression) { parent = parent.getParent(); @@ -814,6 +815,9 @@ public class JavaCompletionUtil { if (parent instanceof PsiLambdaExpression && !LambdaHighlightingUtil.insertSemicolonAfter((PsiLambdaExpression)parent)) { insertAdditionalSemicolon = false; } + if (parent instanceof PsiMethodReferenceExpression && LambdaHighlightingUtil.insertSemicolon(parent.getParent())) { + insertAdditionalSemicolon = false; + } } if (insertAdditionalSemicolon) { toInsert = TailType.SEMICOLON; diff --git a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java index 66d7649bc0b7..bbc61536e296 100644 --- a/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java +++ b/java/java-impl/src/com/intellij/codeInsight/completion/JavaInheritorsGetter.java @@ -159,7 +159,7 @@ public class JavaInheritorsGetter extends CompletionProvider()", parameters.getOriginalFile()); + .createStatementFromText(canonicalText + " v = new " + erasedText + "<>()", parameters.getPosition()); final PsiVariable declaredVar = (PsiVariable)((PsiDeclarationStatement)statement).getDeclaredElements()[0]; final PsiNewExpression initializer = (PsiNewExpression)declaredVar.getInitializer(); final boolean hasDefaultConstructorOrNoGenericsOne = PsiDiamondTypeImpl.hasDefaultConstructor(psiClass) || diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java index dfdea4dad087..113d4b01b5bc 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/JavaLineMarkerProvider.java @@ -315,6 +315,7 @@ public class JavaLineMarkerProvider extends LineMarkerProviderDescriptor { return "Java line markers"; } + @NotNull @Override public Option[] getOptions() { return new Option[] {myLambdaOption, myOverriddenOption, myImplementedOption, myOverridingOption, myImplementingOption}; diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java index 64407e5d4306..16a3de346fe0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/actions/AddImportAction.java @@ -18,8 +18,8 @@ package com.intellij.codeInsight.daemon.impl.actions; import com.intellij.application.options.editor.AutoImportOptionsConfigurable; import com.intellij.application.options.editor.JavaAutoImportOptions; -import com.intellij.codeInsight.CodeInsightSettings; import com.intellij.codeInsight.CodeInsightUtil; +import com.intellij.codeInsight.CodeInsightWorkspaceSettings; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInsight.actions.OptimizeImportsProcessor; import com.intellij.codeInsight.daemon.QuickFixBundle; @@ -234,7 +234,7 @@ public class AddImportAction implements QuestionAction { try{ bindReference(ref, targetClass); - if (CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) { + if (CodeInsightWorkspaceSettings.getInstance(myProject).optimizeImportsOnTheFly) { Document document = myEditor.getDocument(); PsiFile psiFile = PsiDocumentManager.getInstance(myProject).getPsiFile(document); new OptimizeImportsProcessor(myProject, psiFile).runWithoutProgress(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java index 760ad0fa4959..bc8ccb263837 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/analysis/IncreaseLanguageLevelFix.java @@ -17,6 +17,8 @@ package com.intellij.codeInsight.daemon.impl.analysis; import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.codeInsight.intention.IntentionAction; +import com.intellij.codeInspection.LocalQuickFix; +import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.module.Module; @@ -29,13 +31,14 @@ import com.intellij.pom.java.LanguageLevel; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.Nls; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** * @author cdr */ -public class IncreaseLanguageLevelFix implements IntentionAction { +public class IncreaseLanguageLevelFix implements IntentionAction, LocalQuickFix { private static final Logger LOG = Logger.getInstance("#" + IncreaseLanguageLevelFix.class.getName()); private final LanguageLevel myLevel; @@ -50,12 +53,25 @@ public class IncreaseLanguageLevelFix implements IntentionAction { return CodeInsightBundle.message("set.language.level.to.0", myLevel.getPresentableText()); } + @Nls + @NotNull + @Override + public String getName() { + return getText(); + } + @Override @NotNull public String getFamilyName() { return CodeInsightBundle.message("set.language.level"); } + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + PsiElement element = descriptor.getPsiElement(); + invoke(project, null, element.getContainingFile()); + } + @Override public boolean isAvailable(@NotNull final Project project, final Editor editor, final PsiFile file) { final VirtualFile virtualFile = file.getVirtualFile(); @@ -67,9 +83,7 @@ public class IncreaseLanguageLevelFix implements IntentionAction { @Override public void invoke(@NotNull final Project project, final Editor editor, final PsiFile file) throws IncorrectOperationException { - final VirtualFile virtualFile = file.getVirtualFile(); - LOG.assertTrue(virtualFile != null); - final Module module = ModuleUtilCore.findModuleForFile(virtualFile, project); + final Module module = ModuleUtilCore.findModuleForPsiElement(file); if (module == null) return; JavaProjectModelModificationService.getInstance(project).changeLanguageLevel(module, myLevel); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java index 36bfb93897a7..906696a91777 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddExceptionToThrowsFix.java @@ -111,6 +111,7 @@ public class AddExceptionToThrowsFix extends BaseIntentionAction { }); } + @NotNull private static PsiMethod[] getSuperMethods(@NotNull PsiMethod targetMethod) { List result = new ArrayList<>(); collectSuperMethods(targetMethod, result); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddReturnFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddReturnFix.java index e145d2a3239e..c0361de76a7e 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddReturnFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/AddReturnFix.java @@ -125,6 +125,7 @@ public class AddReturnFix implements IntentionAction { return null; } + @NotNull private static PsiVariable[] getDeclaredVariables(PsiMethod method) { List variables = new ArrayList<>(); PsiStatement[] statements = method.getBody().getStatements(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java index 74fc1f365474..68f0edf88cc0 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/ChangeMethodSignatureFromUsageFix.java @@ -255,6 +255,7 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction/*, Hig return null; } + @Nullable protected ParameterInfoImpl[] getNewParametersInfo(PsiExpression[] expressions, PsiMethod targetMethod, PsiSubstitutor substitutor) { @@ -263,6 +264,7 @@ public class ChangeMethodSignatureFromUsageFix implements IntentionAction/*, Hig new HashSet<>()); } + @Nullable private ParameterInfoImpl[] getNewParametersInfo(PsiExpression[] expressions, PsiMethod targetMethod, PsiSubstitutor substitutor, diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java index e43c9546ecdb..1285b99ebf95 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/CreateFromUsageUtils.java @@ -509,6 +509,7 @@ public class CreateFromUsageUtils { return result.toArray(new PsiReferenceExpression[result.size()]); } + @NotNull static PsiVariable[] guessMatchingVariables(final PsiExpression expression) { List typesList = new ArrayList<>(); List expectedMethodNames = new ArrayList<>(); @@ -574,7 +575,10 @@ public class CreateFromUsageUtils { PsiElement parent = expr.getParent(); if (!(parent instanceof PsiReferenceExpression)) { - ExpectedTypeInfo[] someExpectedTypes = ExpectedTypesProvider.getExpectedTypes(expr, PsiUtil.skipParenthesizedExprUp(parent) instanceof PsiExpressionList); + boolean isAssignmentToFunctionalExpression = PsiUtil.isOnAssignmentLeftHand(expr) && + ((PsiAssignmentExpression)PsiUtil.skipParenthesizedExprUp(parent)).getRExpression() instanceof PsiFunctionalExpression; + boolean forCompletion = PsiUtil.skipParenthesizedExprUp(isAssignmentToFunctionalExpression ? parent.getParent() : parent) instanceof PsiExpressionList; + ExpectedTypeInfo[] someExpectedTypes = ExpectedTypesProvider.getExpectedTypes(expr, forCompletion); if (someExpectedTypes.length > 0) { Arrays.sort(someExpectedTypes, (o1, o2) -> compareExpectedTypes(o1, o2, expression)); types.add(someExpectedTypes); @@ -616,6 +620,7 @@ public class CreateFromUsageUtils { return compareMembers(c1, c2, expression); } + @NotNull private static ExpectedTypeInfo[] equalsExpectedTypes(PsiMethodCallExpression methodCall) { final PsiType[] argumentTypes = methodCall.getArgumentList().getExpressionTypes(); if (argumentTypes.length != 1) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/EnableOptimizeImportsOnTheFlyFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/EnableOptimizeImportsOnTheFlyFix.java index f3ac84cf05bb..d77d93047e93 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/EnableOptimizeImportsOnTheFlyFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/EnableOptimizeImportsOnTheFlyFix.java @@ -15,7 +15,7 @@ */ package com.intellij.codeInsight.daemon.impl.quickfix; -import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.CodeInsightWorkspaceSettings; import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer; import com.intellij.codeInsight.daemon.QuickFixBundle; import com.intellij.codeInsight.intention.IntentionAction; @@ -43,13 +43,12 @@ public class EnableOptimizeImportsOnTheFlyFix implements IntentionAction, LowPri public boolean isAvailable(@NotNull Project project, Editor editor, PsiFile file) { return file.getManager().isInProject(file) && file instanceof PsiJavaFile - && !CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY - ; + && !CodeInsightWorkspaceSettings.getInstance(project).optimizeImportsOnTheFly; } @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file) { - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = true; + CodeInsightWorkspaceSettings.getInstance(project).optimizeImportsOnTheFly = true; DaemonCodeAnalyzer.getInstance(project).restart(); } diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java index f99520fcac2f..5eb9e571d980 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/InitializeFinalFieldInConstructorFix.java @@ -22,6 +22,7 @@ import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInsight.lookup.LookupElement; import com.intellij.ide.util.MemberChooser; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.*; @@ -31,6 +32,7 @@ import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -40,6 +42,7 @@ import java.util.Collections; import java.util.List; public class InitializeFinalFieldInConstructorFix implements IntentionAction { + private static final Logger LOG = Logger.getInstance(InitializeFinalFieldInConstructorFix.class); private final PsiField myField; public InitializeFinalFieldInConstructorFix(@NotNull PsiField field) { @@ -96,25 +99,27 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction { @Nullable Editor editor) { if (constructors.isEmpty()) return; - final List rExpressions = new ArrayList<>(constructors.size()); final LookupElement[] suggestedInitializers = AddVariableInitializerFix.suggestInitializer(field); + final List rExpressions = new ArrayList<>(constructors.size()); for (PsiMethod constructor : constructors) { - PsiExpression initialization = addFieldInitialization(constructor, suggestedInitializers, field, project); - if (initialization != null) { - rExpressions.add(initialization); - } + rExpressions.add(addFieldInitialization(constructor, suggestedInitializers, field, project)); } AddVariableInitializerFix.runAssignmentTemplate(rExpressions, suggestedInitializers, editor); } - @Nullable + @NotNull private static PsiExpression addFieldInitialization(@NotNull PsiMethod constructor, @NotNull LookupElement[] suggestedInitializers, @NotNull PsiField field, @NotNull Project project) { PsiCodeBlock methodBody = constructor.getBody(); - if (methodBody == null) return null; + if (methodBody == null) { + //incomplete code + CreateFromUsageUtils.setupMethodBody(constructor); + methodBody = constructor.getBody(); + LOG.assertTrue(methodBody != null); + } final String fieldName = field.getName(); String stmtText = fieldName + " = " + suggestedInitializers[0].getPsiElement().getText() + ";"; @@ -128,7 +133,7 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction { final PsiExpressionStatement addedStatement = (PsiExpressionStatement)methodBody.add(codeStyleManager .reformat(factory.createStatementFromText(stmtText, methodBody))); - return ((PsiAssignmentExpression)addedStatement.getExpression()).getRExpression(); + return ObjectUtils.notNull(((PsiAssignmentExpression)addedStatement.getExpression()).getRExpression()); } private static boolean methodContainsParameterWithName(@NotNull PsiMethod constructor, @NotNull String name) { @@ -164,6 +169,7 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction { return Collections.emptyList(); } + @NotNull private static PsiMethodMember[] toPsiMethodMemberArray(@NotNull PsiMethod[] methods) { final PsiMethodMember[] result = new PsiMethodMember[methods.length]; for (int i = 0; i < methods.length; i++) { @@ -172,6 +178,7 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction { return result; } + @NotNull private static PsiMethod[] toPsiMethodArray(@NotNull List methodMembers) { final PsiMethod[] result = new PsiMethod[methodMembers.size()]; int i = 0; @@ -186,6 +193,7 @@ public class InitializeFinalFieldInConstructorFix implements IntentionAction { ApplicationManager.getApplication().runWriteAction(() -> defaultConstructorFix.invoke(project, editor, file)); } + @NotNull private static PsiMethod[] filterIfFieldAlreadyAssigned(@NotNull PsiField field, @NotNull PsiMethod[] ctors) { final List result = new ArrayList<>(Arrays.asList(ctors)); for (PsiReference reference : ReferencesSearch.search(field, new LocalSearchScope(ctors))) { diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodParameterFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodParameterFix.java index 88a86bc89fdb..abf0865bce82 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodParameterFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/MethodParameterFix.java @@ -120,6 +120,7 @@ public class MethodParameterFix extends LocalQuickFixAndIntentionActionOnPsiElem return false; } + @NotNull private ParameterInfoImpl[] getNewParametersInfo(PsiMethod method) throws IncorrectOperationException { List result = new ArrayList<>(); PsiParameter[] parameters = method.getParameterList().getParameters(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RemoveUnusedParameterFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RemoveUnusedParameterFix.java index ae019a7f0642..4d6b9ea32532 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RemoveUnusedParameterFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RemoveUnusedParameterFix.java @@ -88,6 +88,7 @@ public class RemoveUnusedParameterFix extends LocalQuickFixAndIntentionActionOnP processor.run(); } + @NotNull public static ParameterInfoImpl[] getNewParametersInfo(PsiMethod method, PsiParameter parameterToRemove) { List result = new ArrayList<>(); PsiParameter[] parameters = method.getParameterList().getParameters(); diff --git a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameWrongRefFix.java b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameWrongRefFix.java index b521f52d6bdf..514866b6c2f5 100644 --- a/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameWrongRefFix.java +++ b/java/java-impl/src/com/intellij/codeInsight/daemon/impl/quickfix/RenameWrongRefFix.java @@ -88,6 +88,7 @@ public class RenameWrongRefFix implements IntentionAction { return !CreateFromUsageUtils.isValidReference(myRefExpr, myUnresolvedOnly); } + @NotNull private LookupElement[] collectItems() { Set items = new LinkedHashSet<>(); boolean qualified = myRefExpr.getQualifierExpression() != null; diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java index a05a9999cdc1..8e7bdc6b5234 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/CopyPasteReferenceProcessor.java @@ -136,6 +136,7 @@ public abstract class CopyPasteReferenceProcessor exten qClassName, staticMemberName)); } + @NotNull protected abstract TRef[] findReferencesToRestore(PsiFile file, RangeMarker bounds, ReferenceData[] referenceData); @@ -154,7 +155,7 @@ public abstract class CopyPasteReferenceProcessor exten protected abstract void restoreReferences(ReferenceData[] referenceData, TRef[] refs); - private static void askReferencesToRestore(Project project, PsiElement[] refs, + private static void askReferencesToRestore(Project project, @NotNull PsiElement[] refs, ReferenceData[] referenceData) { PsiManager manager = PsiManager.getInstance(project); diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java index 273cbe111c69..df243d294e26 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/JavaCopyPasteReferenceProcessor.java @@ -22,6 +22,7 @@ import com.intellij.openapi.editor.RangeMarker; import com.intellij.openapi.util.TextRange; import com.intellij.psi.*; import com.intellij.util.IncorrectOperationException; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.ArrayList; @@ -59,6 +60,7 @@ public class JavaCopyPasteReferenceProcessor extends CopyPasteReferenceProcessor } + @NotNull @Override protected PsiJavaCodeReferenceElement[] findReferencesToRestore(PsiFile file, RangeMarker bounds, diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/ReferenceTransferableData.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/ReferenceTransferableData.java index 76c37546888b..3ba81322d988 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/ReferenceTransferableData.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/ReferenceTransferableData.java @@ -15,6 +15,8 @@ */ package com.intellij.codeInsight.editorActions; +import org.jetbrains.annotations.NotNull; + import java.awt.datatransfer.DataFlavor; import java.io.Serializable; @@ -24,7 +26,7 @@ import java.io.Serializable; public class ReferenceTransferableData implements TextBlockTransferableData, Cloneable, Serializable { private final ReferenceData[] myReferenceDatas; - public ReferenceTransferableData(final ReferenceData[] referenceDatas) { + public ReferenceTransferableData(@NotNull ReferenceData[] referenceDatas) { myReferenceDatas = referenceDatas; } @@ -65,6 +67,7 @@ public class ReferenceTransferableData implements TextBlockTransferableData, Clo return new ReferenceTransferableData(newReferenceData); } + @NotNull public ReferenceData[] getData() { return myReferenceDatas; } diff --git a/java/java-impl/src/com/intellij/codeInsight/editorActions/RestoreReferencesDialog.java b/java/java-impl/src/com/intellij/codeInsight/editorActions/RestoreReferencesDialog.java index 51427dd93d8b..409469805cde 100644 --- a/java/java-impl/src/com/intellij/codeInsight/editorActions/RestoreReferencesDialog.java +++ b/java/java-impl/src/com/intellij/codeInsight/editorActions/RestoreReferencesDialog.java @@ -27,6 +27,7 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBList; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -97,6 +98,7 @@ class RestoreReferencesDialog extends DialogWrapper { return "#com.intellij.codeInsight.editorActions.RestoreReferencesDialog"; } + @NotNull public Object[] getSelectedElements(){ return mySelectedElements; } diff --git a/java/java-impl/src/com/intellij/codeInsight/highlighting/JavaBraceMatcher.java b/java/java-impl/src/com/intellij/codeInsight/highlighting/JavaBraceMatcher.java index fbf403685224..48c329c54949 100644 --- a/java/java-impl/src/com/intellij/codeInsight/highlighting/JavaBraceMatcher.java +++ b/java/java-impl/src/com/intellij/codeInsight/highlighting/JavaBraceMatcher.java @@ -35,6 +35,7 @@ public class JavaBraceMatcher implements PairedBraceMatcher { new BracePair(JavaTokenType.LT, JavaTokenType.GT, false) }; + @NotNull @Override public BracePair[] getPairs() { return pairs; diff --git a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java index 64313fe643b9..53b988a6316f 100644 --- a/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java +++ b/java/java-impl/src/com/intellij/codeInsight/hint/api/impls/MethodParameterInfoHandler.java @@ -245,7 +245,7 @@ public class MethodParameterInfoHandler implements ParameterInfoHandlerWithTabAc PsiType parmType = parm.getType(); PsiType argType = arg.getType(); if (argType == null) continue; - if (parmType instanceof PsiEllipsisType && parmType.getArrayDimensions() == argType.getArrayDimensions() + 1) { + if (parmType instanceof PsiEllipsisType ) { parmType = ((PsiEllipsisType)parmType).getComponentType(); } parmType = substitutor.substitute(parmType); diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/InvertIfConditionAction.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/InvertIfConditionAction.java index 71046a572197..87ac4653c2aa 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/InvertIfConditionAction.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/InvertIfConditionAction.java @@ -102,7 +102,7 @@ public class InvertIfConditionAction extends PsiElementBaseIntentionAction { final CodeStyleManager codeStyle = CodeStyleManager.getInstance(project); - PsiBlockStatement codeBlock = (PsiBlockStatement)factory.createStatementFromText("{}", null); + PsiBlockStatement codeBlock = (PsiBlockStatement)factory.createStatementFromText("{}", ifStatement); codeBlock = (PsiBlockStatement)codeStyle.reformat(codeBlock); ifStatement.getThenBranch().replace(codeBlock); @@ -182,7 +182,7 @@ public class InvertIfConditionAction extends PsiElementBaseIntentionAction { final CodeStyleManager codeStyle = CodeStyleManager.getInstance(project); if (flow.getSize() == 0) { ifStatement.setElseBranch(thenBranch); - PsiStatement statement = factory.createStatementFromText("{}", null); + PsiStatement statement = factory.createStatementFromText("{}", ifStatement); statement = (PsiStatement) codeStyle.reformat(statement); statement = (PsiStatement) ifStatement.getThenBranch().replace(statement); codeStyle.reformat(statement); @@ -194,7 +194,7 @@ public class InvertIfConditionAction extends PsiElementBaseIntentionAction { LOG.assertTrue(endOffset >= 0); if (endOffset >= flow.getSize()) { - PsiStatement statement = factory.createStatementFromText("return;", null); + PsiStatement statement = factory.createStatementFromText("return;", ifStatement); statement = (PsiStatement) codeStyle.reformat(statement); if (thenBranch instanceof PsiBlockStatement) { PsiStatement[] statements = ((PsiBlockStatement) thenBranch).getCodeBlock().getStatements(); @@ -219,7 +219,7 @@ public class InvertIfConditionAction extends PsiElementBaseIntentionAction { if (element != null && element.getParent() instanceof PsiForStatement && ((PsiForStatement)element.getParent()).getUpdate() == element || element instanceof PsiWhileStatement && flow.getStartOffset(element) == endOffset || element instanceof PsiForeachStatement && flow.getStartOffset(element) + 1 == endOffset) { - PsiStatement statement = factory.createStatementFromText("continue;", null); + PsiStatement statement = factory.createStatementFromText("continue;", ifStatement); statement = (PsiStatement)codeStyle.reformat(statement); ifStatement = addAfterWithinCodeBlock(ifStatement, thenBranch); ifStatement.getThenBranch().replace(statement); @@ -267,7 +267,7 @@ public class InvertIfConditionAction extends PsiElementBaseIntentionAction { last = last.getPrevSibling(); - PsiBlockStatement codeBlock = (PsiBlockStatement) factory.createStatementFromText("{}", null); + PsiBlockStatement codeBlock = (PsiBlockStatement) factory.createStatementFromText("{}", ifStatement); codeBlock.getCodeBlock().addRange(first, last); first.getParent().deleteChildRange(first, last); ifStatement.getThenBranch().replace(codeBlock); @@ -277,7 +277,7 @@ public class InvertIfConditionAction extends PsiElementBaseIntentionAction { } setElseBranch(ifStatement, thenBranch, flow); - PsiStatement statement = factory.createStatementFromText("{}", null); + PsiStatement statement = factory.createStatementFromText("{}", ifStatement); statement = (PsiStatement) codeStyle.reformat(statement); statement = (PsiStatement) ifStatement.getThenBranch().replace(statement); codeStyle.reformat(statement); diff --git a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java index 4da0fcf0ca21..93ec1bfd0293 100644 --- a/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java +++ b/java/java-impl/src/com/intellij/codeInsight/intention/impl/config/QuickFixFactoryImpl.java @@ -15,7 +15,7 @@ */ package com.intellij.codeInsight.intention.impl.config; -import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.CodeInsightWorkspaceSettings; import com.intellij.codeInsight.daemon.HighlightDisplayKey; import com.intellij.codeInsight.daemon.QuickFixActionRegistrar; import com.intellij.codeInsight.daemon.QuickFixBundle; @@ -810,7 +810,7 @@ public class QuickFixFactoryImpl extends QuickFixFactory { } private static boolean timeToOptimizeImports(@NotNull PsiFile file) { - if (!CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY) return false; + if (!CodeInsightWorkspaceSettings.getInstance(file.getProject()).optimizeImportsOnTheFly) return false; DaemonCodeAnalyzerEx codeAnalyzer = DaemonCodeAnalyzerEx.getInstanceEx(file.getProject()); // dont optimize out imports in JSP since it can be included in other JSP diff --git a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionMerger.java b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionMerger.java index 75be9546415b..a61c2b42269e 100644 --- a/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionMerger.java +++ b/java/java-impl/src/com/intellij/codeInspection/deadCode/UnusedDeclarationInspectionMerger.java @@ -19,6 +19,7 @@ import com.intellij.codeInspection.ex.InspectionElementsMergerBase; import com.intellij.codeInspection.unusedSymbol.UnusedSymbolLocalInspection; import com.intellij.openapi.util.WriteExternalException; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; public class UnusedDeclarationInspectionMerger extends InspectionElementsMergerBase { private static final String UNUSED_SYMBOL = "UNUSED_SYMBOL"; @@ -29,6 +30,7 @@ public class UnusedDeclarationInspectionMerger extends InspectionElementsMergerB return UnusedDeclarationInspectionBase.SHORT_NAME; } + @NotNull @Override public String[] getSourceToolNames() { return new String[] {UNUSED_SYMBOL, UNUSED_DECLARATION}; diff --git a/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java b/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java index 5107b6691cae..1a1222220fac 100644 --- a/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java +++ b/java/java-impl/src/com/intellij/codeInspection/inferNullity/InferNullityAnnotationsAction.java @@ -59,6 +59,7 @@ import com.intellij.ui.TitledSeparator; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewUtil; import com.intellij.usages.*; +import com.intellij.util.ObjectUtils; import com.intellij.util.Processor; import com.intellij.util.SequentialModalProgressTask; import com.intellij.util.containers.ContainerUtil; @@ -299,9 +300,10 @@ public class InferNullityAnnotationsAction extends BaseAnalysisAction { @NotNull private Factory rerunFactory(@NotNull final Project project, @NotNull final AnalysisScope scope) { return () -> new UsageInfoSearcherAdapter() { + @NotNull @Override protected UsageInfo[] findUsages() { - return InferNullityAnnotationsAction.this.findUsages(project, scope, scope.getFileCount()); + return ObjectUtils.notNull(InferNullityAnnotationsAction.this.findUsages(project, scope, scope.getFileCount()), UsageInfo.EMPTY_ARRAY); } @Override diff --git a/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspection.java b/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspection.java index 7664533f9cc8..1c6512393eb2 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspection.java @@ -560,7 +560,8 @@ public class Java8MapApiInspection extends BaseJavaBatchLocalInspectionTool { holder.registerProblem(getFullCondition(), QuickFixBundle.message("java.8.map.api.inspection.description", fix.myMethodName), fix); } - public boolean isMapValueType(PsiType type) { + public boolean isMapValueType(@Nullable PsiType type) { + if (type == null) return false; PsiType mapExpressionType = myMapExpression.getType(); PsiType valueTypeParameter = PsiUtil.substituteTypeParameter(mapExpressionType, CommonClassNames.JAVA_UTIL_MAP, 1, false); return valueTypeParameter != null && valueTypeParameter.isAssignableFrom(type); diff --git a/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspectionMerger.java b/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspectionMerger.java index d2631f129cb5..04c8d9f50542 100644 --- a/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspectionMerger.java +++ b/java/java-impl/src/com/intellij/codeInspection/java18api/Java8MapApiInspectionMerger.java @@ -16,6 +16,7 @@ package com.intellij.codeInspection.java18api; import com.intellij.codeInspection.ex.InspectionElementsMerger; +import org.jetbrains.annotations.NotNull; public class Java8MapApiInspectionMerger extends InspectionElementsMerger { private static final String COLLECTION_API_INSPECTION = "Java8CollectionsApi"; @@ -26,6 +27,7 @@ public class Java8MapApiInspectionMerger extends InspectionElementsMerger { return Java8MapApiInspection.SHORT_NAME; } + @NotNull @Override public String[] getSourceToolNames() { return new String[] {COLLECTION_API_INSPECTION, REPLACE_MAP_GET_INSPECTION}; diff --git a/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java index d88b4e59e407..07c9b046bada 100644 --- a/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/miscGenerics/RedundantArrayForVarargsCallInspection.java @@ -102,8 +102,11 @@ public class RedundantArrayForVarargsCallInspection extends GenericsInspectionTo } PsiExpression lastArg = args[args.length - 1]; PsiParameter lastParameter = parameters[args.length - 1]; + if (!lastParameter.isVarArgs()) { + return; + } PsiType lastParamType = lastParameter.getType(); - LOG.assertTrue(lastParamType instanceof PsiEllipsisType); + LOG.assertTrue(lastParamType instanceof PsiEllipsisType, lastParamType); if (!(lastArg instanceof PsiNewExpression)) { return; } diff --git a/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java new file mode 100644 index 000000000000..0702364daf7b --- /dev/null +++ b/java/java-impl/src/com/intellij/codeInspection/reflectiveAccess/JavaLangInvokeHandleSignatureInspection.java @@ -0,0 +1,523 @@ +/* + * 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.reflectiveAccess; + +import com.intellij.codeInsight.daemon.JavaErrorMessages; +import com.intellij.codeInsight.lookup.*; +import com.intellij.codeInspection.*; +import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.WriteAction; +import com.intellij.openapi.editor.Editor; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Key; +import com.intellij.openapi.util.text.StringUtil; +import com.intellij.psi.*; +import com.intellij.psi.codeStyle.JavaCodeStyleManager; +import com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil; +import com.intellij.util.PlatformIcons; +import com.intellij.util.containers.ContainerUtil; +import com.siyeh.ig.psiutils.ParenthesesUtils; +import org.jetbrains.annotations.Nls; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; + +/** + * @author Pavel.Dolgov + */ +public class JavaLangInvokeHandleSignatureInspection extends BaseJavaBatchLocalInspectionTool { + public static final Key> DEFAULT_SIGNATURE = Key.create("DEFAULT_SIGNATURE"); + + private static final String FIND_CONSTRUCTOR = "findConstructor"; + private static final Set KNOWN_METHOD_NAMES = Collections.unmodifiableSet( + ContainerUtil.union(Arrays.asList(HANDLE_FACTORY_METHOD_NAMES), Collections.singletonList(FIND_CONSTRUCTOR))); + + private static final List NO_ARGUMENT_CONSTRUCTOR_SIGNATURE = Collections.singletonList(PsiKeyword.VOID); + + @NotNull + @Override + public PsiElementVisitor buildVisitor(@NotNull ProblemsHolder holder, boolean isOnTheFly) { + return new JavaElementVisitor() { + @Override + public void visitMethodCallExpression(PsiMethodCallExpression callExpression) { + super.visitMethodCallExpression(callExpression); + + final PsiReferenceExpression methodExpression = callExpression.getMethodExpression(); + final String methodName = methodExpression.getReferenceName(); + if (methodName != null && KNOWN_METHOD_NAMES.contains(methodName)) { + final PsiMethod method = callExpression.resolveMethod(); + final PsiClass psiClass = method != null ? method.getContainingClass() : null; + if (psiClass != null && JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP.equals(psiClass.getQualifiedName())) { + final PsiExpression[] arguments = callExpression.getArgumentList().getExpressions(); + checkHandlerFactory(methodName, methodExpression, arguments, holder); + } + } + } + }; + } + + private static void checkHandlerFactory(@NotNull String factoryMethodName, + @NotNull PsiReferenceExpression factoryMethodExpression, + @NotNull PsiExpression[] arguments, + @NotNull ProblemsHolder holder) { + if (arguments.length == 2) { + if (FIND_CONSTRUCTOR.equals(factoryMethodName)) { + final PsiClass ownerClass = getReflectiveClass(arguments[0]); + if (ownerClass != null) { + final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[1]); + checkConstructor(ownerClass, typeExpression, holder); + } + } + } + else if (arguments.length >= 3) { + final PsiClass ownerClass = getReflectiveClass(arguments[0]); + if (ownerClass != null) { + final PsiExpression nameExpression = ParenthesesUtils.stripParentheses(arguments[1]); + final PsiExpression nameDefinition = findDefinition(nameExpression); + final String memberName = computeConstantExpression(nameDefinition, String.class); + if (!StringUtil.isEmpty(memberName)) { + final PsiExpression typeExpression = ParenthesesUtils.stripParentheses(arguments[2]); + + switch (factoryMethodName) { + case FIND_GETTER: + case FIND_SETTER: + case FIND_VAR_HANDLE: + checkField(ownerClass, memberName, nameExpression, typeExpression, false, factoryMethodExpression, holder); + break; + + case FIND_STATIC_GETTER: + case FIND_STATIC_SETTER: + case FIND_STATIC_VAR_HANDLE: + checkField(ownerClass, memberName, nameExpression, typeExpression, true, factoryMethodExpression, holder); + break; + + case FIND_VIRTUAL: + checkMethod(ownerClass, memberName, nameExpression, typeExpression, false, factoryMethodExpression, holder); + break; + + case FIND_STATIC: + checkMethod(ownerClass, memberName, nameExpression, typeExpression, true, factoryMethodExpression, holder); + break; + + case FIND_SPECIAL: + checkMethod(ownerClass, memberName, nameExpression, typeExpression, false, factoryMethodExpression, holder); + break; + } + } + } + } + } + + private static void checkConstructor(@NotNull PsiClass ownerClass, + @NotNull PsiExpression constructorTypeExpression, + @NotNull ProblemsHolder holder) { + final List constructorSignature = composeMethodSignature(constructorTypeExpression); + if (constructorSignature != null) { + final List constructors = ContainerUtil.filter(ownerClass.getMethods(), PsiMethod::isConstructor); + List> validSignatures = null; + if (constructors.isEmpty()) { + if (!constructorSignature.equals(NO_ARGUMENT_CONSTRUCTOR_SIGNATURE)) { + validSignatures = Collections.singletonList(NO_ARGUMENT_CONSTRUCTOR_SIGNATURE); + } + } + else if (!matchMethodSignature(constructors, constructorSignature)) { + validSignatures = constructors.stream() + .map(JavaReflectionReferenceUtil::getMethodSignature) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + } + if (validSignatures != null) { + final String declarationText = getConstructorDeclarationText(ownerClass, constructorSignature); + if (declarationText != null) { + LocalQuickFix fix = null; + final String ownerClassName = ownerClass.getName(); + if (ownerClassName != null) { + fix = ReplaceSignatureQuickFix + .createFix(constructorTypeExpression, ownerClassName, validSignatures, true, holder.isOnTheFly()); + } + holder.registerProblem(constructorTypeExpression, JavaErrorMessages.message("cannot.resolve.constructor", declarationText), fix); + } + } + } + } + + private static void checkField(@NotNull PsiClass ownerClass, + @NotNull String fieldName, + @NotNull PsiExpression fieldNameExpression, + @NotNull PsiExpression fieldTypeExpression, + boolean isStatic, + @NotNull PsiReferenceExpression factoryMethodExpression, + @NotNull ProblemsHolder holder) { + final PsiField field = ownerClass.findFieldByName(fieldName, true); + if (field == null) { + holder.registerProblem(fieldNameExpression, InspectionsBundle.message("inspection.handle.signature.field.cannot.resolve", fieldName)); + return; + } + + if (field.hasModifierProperty(PsiModifier.STATIC) != isStatic) { + final String factoryMethodName = factoryMethodExpression.getReferenceName(); + final PsiElement factoryMethodNameElement = factoryMethodExpression.getReferenceNameElement(); + if (factoryMethodName != null && factoryMethodNameElement != null) { + final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStatic); + final String message = InspectionsBundle.message( + isStatic ? "inspection.handle.signature.field.static" : "inspection.handle.signature.field.not.static", fieldName); + holder.registerProblem(factoryMethodNameElement, message, fix); + return; + } + } + + final ReflectiveType reflectiveType = getReflectiveType(fieldTypeExpression); + if (reflectiveType != null && !reflectiveType.isEqualTo(field.getType())) { + final String expectedTypeText = getTypeText(field.getType(), field); + if (expectedTypeText != null) { + final String message = InspectionsBundle.message("inspection.handle.signature.field.type", fieldName, expectedTypeText); + holder.registerProblem(fieldTypeExpression, message, new FieldTypeQuickFix(expectedTypeText)); + } + } + } + + private static void checkMethod(@NotNull PsiClass ownerClass, + @NotNull String methodName, + @NotNull PsiExpression methodNameExpression, + @NotNull PsiExpression methodTypeExpression, + boolean isStatic, + @NotNull PsiReferenceExpression factoryMethodExpression, + @NotNull ProblemsHolder holder) { + + final PsiMethod[] methods = ownerClass.findMethodsByName(methodName, true); + if (methods.length == 0) { + holder.registerProblem(methodNameExpression, JavaErrorMessages.message("cannot.resolve.method", methodName)); + return; + } + + final List filteredMethods = + ContainerUtil.filter(methods, method -> method.hasModifierProperty(PsiModifier.STATIC) == isStatic); + if (filteredMethods.isEmpty()) { + final String factoryMethodName = factoryMethodExpression.getReferenceName(); + final PsiElement factoryMethodNameElement = factoryMethodExpression.getReferenceNameElement(); + if (factoryMethodName != null && factoryMethodNameElement != null) { + final LocalQuickFix fix = SwitchStaticnessQuickFix.createFix(factoryMethodName, isStatic); + final String message = InspectionsBundle.message( + isStatic ? "inspection.handle.signature.method.static" : "inspection.handle.signature.method.not.static", methodName); + holder.registerProblem(factoryMethodNameElement, message, fix); + return; + } + } + + final List methodSignature = composeMethodSignature(methodTypeExpression); + if (methodSignature != null && !matchMethodSignature(filteredMethods, methodSignature)) { + final String declarationText = getMethodDeclarationText(methodName, methodSignature); + final List> validSignatures = filteredMethods.stream() + .map(JavaReflectionReferenceUtil::getMethodSignature) + .filter(Objects::nonNull) + .collect(Collectors.toList()); + final LocalQuickFix fix = + ReplaceSignatureQuickFix.createFix(methodTypeExpression, methodName, validSignatures, false, holder.isOnTheFly()); + holder.registerProblem(methodTypeExpression, JavaErrorMessages.message("cannot.resolve.method", declarationText), fix); + } + } + + @NotNull + private static String getMethodDeclarationText(@NotNull String name, @NotNull List methodSignature) { + final String argumentTypes = methodSignature.stream().skip(1).collect(Collectors.joining(", ")); + final String returnType = !methodSignature.isEmpty() ? methodSignature.get(0) : ""; + return returnType + " " + name + "(" + argumentTypes + ")"; + } + + @Nullable + private static String getConstructorDeclarationText(@NotNull PsiClass ownerClass, @NotNull List methodSignature) { + final String className = ownerClass.getName(); + if (className != null && !methodSignature.isEmpty()) { + return getConstructorDeclarationText(className, methodSignature); + } + return null; + } + + @NotNull + private static String getConstructorDeclarationText(@NotNull String className, @NotNull List methodSignature) { + // Return type of the constructor should be 'void'. If it isn't so let's make that mistake more noticeable. + final String returnType = methodSignature.get(0); + final String fakeReturnType = !PsiKeyword.VOID.equals(returnType) ? returnType + " " : ""; + final String argumentTypes = methodSignature.stream().skip(1).collect(Collectors.joining(", ")); + return fakeReturnType + className + "(" + argumentTypes + ")"; + } + + private static boolean matchMethodSignature(@NotNull List methods, @NotNull List expectedMethodSignature) { + return methods.stream() + .map(JavaReflectionReferenceUtil::getMethodSignature) + .anyMatch(expectedMethodSignature::equals); + } + + /** + * Take method's return type and parameter types + * from arguments of MethodType.methodType(Class...) and MethodType.genericMethodType(int, boolean?) + */ + @Nullable + private static List composeMethodSignature(@Nullable PsiExpression methodTypeExpression) { + final PsiExpression typeDefinition = findDefinition(methodTypeExpression); + if (typeDefinition instanceof PsiMethodCallExpression) { + final PsiMethodCallExpression methodCallExpression = (PsiMethodCallExpression)typeDefinition; + final String referenceName = methodCallExpression.getMethodExpression().getReferenceName(); + + Function> composer = null; + if (METHOD_TYPE.equals(referenceName)) { + composer = JavaLangInvokeHandleSignatureInspection::composeMethodSignatureFromTypes; + } + else if (GENERIC_METHOD_TYPE.equals(referenceName)) { + composer = JavaLangInvokeHandleSignatureInspection::composeGenericMethodSignature; + } + + if (composer != null) { + final PsiMethod method = methodCallExpression.resolveMethod(); + if (method != null) { + final PsiClass psiClass = method.getContainingClass(); + if (psiClass != null && JAVA_LANG_INVOKE_METHOD_TYPE.equals(psiClass.getQualifiedName())) { + final PsiExpression[] arguments = methodCallExpression.getArgumentList().getExpressions(); + return composer.apply(arguments); + } + } + } + } + return null; + } + + @Nullable + private static List composeMethodSignatureFromTypes(@NotNull PsiExpression[] returnAndParameterTypes) { + final List typeNames = Arrays.stream(returnAndParameterTypes) + .map(JavaReflectionReferenceUtil::getTypeText) + .collect(Collectors.toList()); + return !typeNames.isEmpty() && !typeNames.contains(null) ? typeNames : null; + } + + /** + * All the types in the method signature are either unbounded type parameters or java.lang.Object (with possible vararg) + */ + private static List composeGenericMethodSignature(@NotNull PsiExpression[] genericSignatureShape) { + if (genericSignatureShape.length == 0 || genericSignatureShape.length > 2) { + return null; + } + + final Integer objectArgCount = computeConstantExpression(genericSignatureShape[0], Integer.class); + final Boolean finalArray = // there's an additional parameter which is an ellipsis or an array + genericSignatureShape.length > 1 ? computeConstantExpression(genericSignatureShape[1], Boolean.class) : false; + + if (objectArgCount == null || objectArgCount < 0 || objectArgCount > 255) { + return null; + } + if (finalArray == null || finalArray && objectArgCount > 254) { + return null; + } + + final List typeNames = new ArrayList<>(); + typeNames.add(CommonClassNames.JAVA_LANG_OBJECT); // return type + + for (int i = 0; i < objectArgCount; i++) { + typeNames.add(CommonClassNames.JAVA_LANG_OBJECT); + } + if (finalArray) { + typeNames.add(CommonClassNames.JAVA_LANG_OBJECT + "[]"); + } + return typeNames; + } + + private static class FieldTypeQuickFix implements LocalQuickFix { + private final String myFieldTypeText; + + public FieldTypeQuickFix(String fieldTypeText) {myFieldTypeText = fieldTypeText;} + + @Nls + @NotNull + @Override + public String getFamilyName() { + return InspectionsBundle.message("inspection.handle.signature.change.type.fix.name", myFieldTypeText); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiExpression typeExpression = factory.createExpressionFromText(myFieldTypeText + ".class", element); + final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); + styleManager.shortenClassReferences(element.replace(typeExpression)); + } + } + + private static class SwitchStaticnessQuickFix implements LocalQuickFix { + private static final Map STATIC_TO_NON_STATIC = ContainerUtil.immutableMapBuilder() + .put(FIND_STATIC_GETTER, FIND_GETTER) + .put(FIND_STATIC_SETTER, FIND_SETTER) + .put(FIND_STATIC_VAR_HANDLE, FIND_VAR_HANDLE) + .put(FIND_STATIC, FIND_VIRTUAL) + .build(); + private static final Map NON_STATIC_TO_STATIC = ContainerUtil.immutableMapBuilder() + .put(FIND_GETTER, FIND_STATIC_GETTER) + .put(FIND_SETTER, FIND_STATIC_SETTER) + .put(FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE) + .put(FIND_VIRTUAL, FIND_STATIC) + .build(); + + private final String myReplacementName; + + public SwitchStaticnessQuickFix(@NotNull String replacementName) { + myReplacementName = replacementName; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return InspectionsBundle.message("inspection.handle.signature.replace.with.fix.name", myReplacementName); + } + + @Override + public void applyFix(@NotNull Project project, @NotNull ProblemDescriptor descriptor) { + final PsiElement element = descriptor.getPsiElement(); + final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiIdentifier identifier = factory.createIdentifier(myReplacementName); + final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); + styleManager.shortenClassReferences(element.replace(identifier)); + } + + @Nullable + public static LocalQuickFix createFix(@NotNull String methodName, boolean isStatic) { + final String replacementName = isStatic ? STATIC_TO_NON_STATIC.get(methodName) : NON_STATIC_TO_STATIC.get(methodName); + return replacementName != null ? new SwitchStaticnessQuickFix(replacementName) : null; + } + } + + private static class ReplaceSignatureQuickFix extends LocalQuickFixAndIntentionActionOnPsiElement { + private String myName; + private List> mySignatures; + private boolean myIsConstructor; + + public ReplaceSignatureQuickFix(@Nullable PsiElement element, + @NotNull String name, + @NotNull List> signatures, + boolean isConstructor) { + super(element); + myName = name; + mySignatures = signatures; + myIsConstructor = isConstructor; + } + + @Nls + @NotNull + @Override + public String getFamilyName() { + return getText(); + } + + @NotNull + @Override + public String getText() { + if (mySignatures.size() == 1) { + final String declarationText = getDeclarationText(mySignatures.get(0)); + return InspectionsBundle.message(myIsConstructor + ? "inspection.handle.signature.use.constructor.fix.name" + : "inspection.handle.signature.use.method.fix.name", + declarationText); + } + return InspectionsBundle.message(myIsConstructor + ? "inspection.handle.signature.use.constructor.fix.family.name" + : "inspection.handle.signature.use.method.fix.family.name"); + } + + @Override + public void invoke(@NotNull Project project, + @NotNull PsiFile file, + @Nullable Editor editor, + @NotNull PsiElement startElement, + @NotNull PsiElement endElement) { + if (ApplicationManager.getApplication().isUnitTestMode()) { + final PsiElement element = myStartElement.getElement(); + if (editor != null && element != null) { + final List signature = editor.getUserData(DEFAULT_SIGNATURE); + if (signature != null && mySignatures.contains(signature)) { + applyFix(project, element, signature); + } + } + return; + } + + if (mySignatures.size() == 1) { + applyFix(project, startElement, mySignatures.get(0)); + } + else if (editor != null) { + showLookup(project, editor); + } + } + + private void showLookup(@NotNull Project project, @NotNull Editor editor) { + final List items = mySignatures.stream() + .map(signature -> LookupElementBuilder.create(signature, "") + .withIcon(PlatformIcons.METHOD_ICON) + .withPresentableText(getDeclarationText(signature))) + .collect(Collectors.toList()); + + // Unfortunately, LookupManager.showLookup() doesn't invoke InsertHandler. A workaround with LookupListener is used. + // To let the workaround work we need to make sure that noting is actually replaced by the default behavior of showLookup(). + editor.getSelectionModel().removeSelection(); + + final LookupManager lookupManager = LookupManager.getInstance(project); + final LookupEx lookup = lookupManager.showLookup(editor, items.toArray(LookupElement.EMPTY_ARRAY)); + if (lookup != null) { + lookup.addLookupListener(new LookupAdapter() { + @Override + public void itemSelected(LookupEvent event) { + final LookupElement item = event.getItem(); + if (item != null) { + final PsiElement element = myStartElement.getElement(); + final Object object = item.getObject(); + if (element != null && object instanceof List) { + @SuppressWarnings("unchecked") final List signature = (List)object; + WriteAction.run(() -> applyFix(project, element, signature)); + } + } + } + }); + } + } + + private static void applyFix(@NotNull Project project, @NotNull PsiElement element, @NotNull List signature) { + final String replacementText = getMethodTypeExpressionText(signature); + final PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory(); + final PsiExpression replacement = factory.createExpressionFromText(replacementText, element); + final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); + styleManager.shortenClassReferences(element.replace(replacement)); + } + + @NotNull + private String getDeclarationText(@NotNull List signature) { + return myIsConstructor ? getConstructorDeclarationText(myName, signature) : getMethodDeclarationText(myName, signature); + } + + @Nullable + private static LocalQuickFix createFix(@Nullable PsiElement element, + @NotNull String methodName, + @NotNull List> methodSignatures, + boolean isConstructor, boolean isOnTheFly) { + if (isOnTheFly && !methodSignatures.isEmpty() || methodSignatures.size() == 1) { + return new ReplaceSignatureQuickFix(element, methodName, methodSignatures, isConstructor); + } + return null; + } + } +} diff --git a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java index b0caf994c3b8..75deab9217f4 100644 --- a/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/streamToLoop/StreamToLoopInspection.java @@ -20,7 +20,9 @@ import com.intellij.codeInspection.LocalQuickFix; import com.intellij.codeInspection.ProblemDescriptor; import com.intellij.codeInspection.ProblemsHolder; import com.intellij.codeInspection.ui.SingleCheckboxOptionsPanel; +import com.intellij.diagnostic.LogMessageEx; import com.intellij.lang.java.lexer.JavaLexer; +import com.intellij.openapi.diagnostic.Attachment; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.TextRange; @@ -37,6 +39,7 @@ import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.RedundantCastUtil; import com.intellij.refactoring.util.RefactoringUtil; +import com.intellij.util.ExceptionUtil; import com.intellij.util.containers.ContainerUtil; import com.siyeh.ig.psiutils.*; import one.util.streamex.IntStreamEx; @@ -342,7 +345,8 @@ public class StreamToLoopInspection extends BaseJavaBatchLocalInspectionTool { // Just in case if something went wrong: at least try to restore the original stream code temporaryStreamPlaceholder.replace(factory.createExpressionFromText(text, temporaryStreamPlaceholder)); } - LOG.error("Error converting Stream to loop: "+text, ex); + LOG.error(LogMessageEx.createEvent("Error converting Stream to loop", ExceptionUtil.getThrowableText(ex), + new Attachment("Stream_code.txt", text))); } } diff --git a/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java b/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java index e9b3f498c90c..48f1357b6d16 100644 --- a/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java +++ b/java/java-impl/src/com/intellij/codeInspection/uncheckedWarnings/UncheckedWarningLocalInspection.java @@ -20,6 +20,7 @@ import com.intellij.codeInsight.daemon.impl.quickfix.GenerifyFileFix; import com.intellij.codeInspection.LocalQuickFix; import com.intellij.openapi.util.Pass; import com.intellij.util.ui.JBUI; +import org.jetbrains.annotations.NotNull; import javax.swing.*; import java.awt.*; @@ -30,6 +31,7 @@ import java.awt.*; */ public class UncheckedWarningLocalInspection extends UncheckedWarningLocalInspectionBase { + @NotNull @Override protected LocalQuickFix[] createFixes() { return new LocalQuickFix[]{new GenerifyFileFix()}; diff --git a/java/java-impl/src/com/intellij/codeInspection/util/OptionalUtil.java b/java/java-impl/src/com/intellij/codeInspection/util/OptionalUtil.java index 0aaf900e0f71..ba613719b7a0 100644 --- a/java/java-impl/src/com/intellij/codeInspection/util/OptionalUtil.java +++ b/java/java-impl/src/com/intellij/codeInspection/util/OptionalUtil.java @@ -136,7 +136,7 @@ public class OptionalUtil { } trueExpression = targetType == null ? trueExpression : RefactoringUtil.convertInitializerToNormalExpression(trueExpression, targetType); - String typeArg = getMapTypeArgument(trueExpression, targetType); + String typeArg = getMapTypeArgument(trueExpression, targetType, falseExpression); qualifier += "." + typeArg + "map(" + LambdaUtil.createLambda(var, trueExpression) + ")"; } if (useOrElseGet && !ExpressionUtils.isSimpleExpression(falseExpression)) { @@ -154,6 +154,11 @@ public class OptionalUtil { @NotNull public static String getMapTypeArgument(PsiExpression expression, PsiType type) { + return getMapTypeArgument(expression, type, null); + } + + @NotNull + private static String getMapTypeArgument(PsiExpression expression, PsiType type, PsiExpression falseExpression) { if (!(type instanceof PsiClassType)) return ""; PsiExpression copy = JavaPsiFacade.getElementFactory(expression.getProject()).createExpressionFromText(expression.getText(), expression); @@ -162,7 +167,9 @@ public class OptionalUtil { !exprType.equals(PsiType.NULL) && !LambdaUtil.notInferredType(exprType) && TypeConversionUtil.isAssignable(type, exprType)) { - return ""; + if (falseExpression == null) return ""; + PsiType falseType = falseExpression.getType(); + if (falseType != null && falseType.isAssignableFrom(exprType)) return ""; } return "<" + type.getCanonicalText() + ">"; } diff --git a/java/java-impl/src/com/intellij/ide/util/PackageUtil.java b/java/java-impl/src/com/intellij/ide/util/PackageUtil.java index e48ef65df984..4d0081fe7461 100644 --- a/java/java-impl/src/com/intellij/ide/util/PackageUtil.java +++ b/java/java-impl/src/com/intellij/ide/util/PackageUtil.java @@ -282,7 +282,8 @@ public class PackageUtil { return psiDirectory; } - private static PsiDirectory[] filterSourceDirectories(PsiDirectory baseDir, Project project, PsiDirectory[] moduleDirectories) { + @NotNull + private static PsiDirectory[] filterSourceDirectories(PsiDirectory baseDir, Project project, @NotNull PsiDirectory[] moduleDirectories) { final ProjectFileIndex fileIndex = ProjectRootManager.getInstance(project).getFileIndex(); if (fileIndex.isInTestSourceContent(baseDir.getVirtualFile())) { List result = new ArrayList<>(); @@ -296,6 +297,7 @@ public class PackageUtil { return moduleDirectories; } + @NotNull private static PsiDirectory[] getPackageDirectoriesInModule(PsiPackage rootPackage, Module module) { return rootPackage.getDirectories(GlobalSearchScope.moduleScope(module)); } diff --git a/java/java-impl/src/com/intellij/openapi/module/JavaModuleType.java b/java/java-impl/src/com/intellij/openapi/module/JavaModuleType.java index f467bdd65e5b..b38672b9eed9 100644 --- a/java/java-impl/src/com/intellij/openapi/module/JavaModuleType.java +++ b/java/java-impl/src/com/intellij/openapi/module/JavaModuleType.java @@ -66,11 +66,6 @@ public class JavaModuleType extends ModuleType { return ProjectBundle.message("module.type.java.description"); } - @Override - public Icon getBigIcon() { - return getJavaModuleIcon(); - } - @Override public Icon getNodeIcon(boolean isOpened) { return getJavaModuleNodeIconClosed(); @@ -83,10 +78,6 @@ public class JavaModuleType extends ModuleType { moduleBuilder::isSuitableSdkType); } - private static Icon getJavaModuleIcon() { - return AllIcons.Modules.Types.JavaModule; - } - private static Icon getJavaModuleNodeIconClosed() { return AllIcons.Nodes.Module; } diff --git a/java/java-impl/src/com/intellij/openapi/roots/impl/ModulePackageIndexImpl.java b/java/java-impl/src/com/intellij/openapi/roots/impl/ModulePackageIndexImpl.java index b83ef579c80f..1f94d45a56b9 100644 --- a/java/java-impl/src/com/intellij/openapi/roots/impl/ModulePackageIndexImpl.java +++ b/java/java-impl/src/com/intellij/openapi/roots/impl/ModulePackageIndexImpl.java @@ -40,11 +40,13 @@ public class ModulePackageIndexImpl extends ModulePackageIndex { } }; + @NotNull @Override public Query getDirsByPackageName(@NotNull String packageName, boolean includeLibrarySources) { return new FilteredQuery<>(myDirectoryIndex.getDirectoriesByPackageName(packageName, includeLibrarySources), myDirCondition); } + @NotNull @Override public VirtualFile[] getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources) { return getDirsByPackageName(packageName, includeLibrarySources).toArray(VirtualFile.EMPTY_ARRAY); diff --git a/java/java-impl/src/com/intellij/openapi/vfs/impl/jrt/JrtFileSystemImpl.java b/java/java-impl/src/com/intellij/openapi/vfs/impl/jrt/JrtFileSystemImpl.java index 17d613c5ed12..501c4f1ed838 100644 --- a/java/java-impl/src/com/intellij/openapi/vfs/impl/jrt/JrtFileSystemImpl.java +++ b/java/java-impl/src/com/intellij/openapi/vfs/impl/jrt/JrtFileSystemImpl.java @@ -100,7 +100,7 @@ public class JrtFileSystemImpl extends JrtFileSystem { if (mySubscribed.getAndSet(true)) return; Application app = ApplicationManager.getApplication(); - app.getMessageBus().connect(app).subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { + app.getMessageBus().connect(app).subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { @Override public void after(@NotNull List events) { Set toRefresh = null; diff --git a/java/java-impl/src/com/intellij/psi/AbstractQualifiedReference.java b/java/java-impl/src/com/intellij/psi/AbstractQualifiedReference.java index a113f479bc78..bbb9e5d05005 100644 --- a/java/java-impl/src/com/intellij/psi/AbstractQualifiedReference.java +++ b/java/java-impl/src/com/intellij/psi/AbstractQualifiedReference.java @@ -64,6 +64,7 @@ public abstract class AbstractQualifiedReference result = new THashSet<>(infos.length); for (ExpectedTypeInfo info : infos) { diff --git a/java/java-impl/src/com/intellij/psi/filters/getters/InstanceOfLeftPartTypeGetter.java b/java/java-impl/src/com/intellij/psi/filters/getters/InstanceOfLeftPartTypeGetter.java index a22900e78299..5a145df83274 100644 --- a/java/java-impl/src/com/intellij/psi/filters/getters/InstanceOfLeftPartTypeGetter.java +++ b/java/java-impl/src/com/intellij/psi/filters/getters/InstanceOfLeftPartTypeGetter.java @@ -18,6 +18,7 @@ package com.intellij.psi.filters.getters; import com.intellij.psi.*; import com.intellij.psi.filters.FilterUtil; import com.intellij.psi.util.PsiTreeUtil; +import org.jetbrains.annotations.NotNull; /** * Created by IntelliJ IDEA. @@ -27,6 +28,7 @@ import com.intellij.psi.util.PsiTreeUtil; * To change this template use Options | File Templates. */ public class InstanceOfLeftPartTypeGetter { + @NotNull public static PsiType[] getLeftTypes(PsiElement context) { if((context = FilterUtil.getPreviousElement(context, true)) == null) return PsiType.EMPTY_ARRAY; if(!PsiKeyword.INSTANCEOF.equals(context.getText())) return PsiType.EMPTY_ARRAY; diff --git a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java index 2b74b60aa371..647a74dc6ae5 100644 --- a/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java +++ b/java/java-impl/src/com/intellij/psi/formatter/java/JavaSpacePropertyProcessor.java @@ -253,7 +253,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } else { - myResult = Spacing.createSpacing(0, 0, mySettings.BLANK_LINES_AFTER_CLASS_HEADER + 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); + myResult = Spacing.createSpacing(1, 1, mySettings.BLANK_LINES_AFTER_CLASS_HEADER + 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_DECLARATIONS); } } else if (myRole2 == ChildRole.RBRACE && aClass.isEnum()) { @@ -855,7 +855,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } } } - myResult = Spacing.createSpacing(0, 0, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); + myResult = Spacing.createSpacing(1, 1, blankLines, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); } else { myResult = Spacing.createDependentLFSpacing(0, 1, textRange, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_IN_CODE); @@ -863,7 +863,7 @@ public class JavaSpacePropertyProcessor extends JavaElementVisitor { } else if (myRole2 == ChildRole.RBRACE) { if (!keepInOneLine) { - myResult = Spacing.createSpacing(0, 0, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE); + myResult = Spacing.createSpacing(1, 1, 1, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE); } else { myResult = Spacing.createDependentLFSpacing(0, 1, textRange, mySettings.KEEP_LINE_BREAKS, mySettings.KEEP_BLANK_LINES_BEFORE_RBRACE); diff --git a/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanPropertyElement.java b/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanPropertyElement.java index 00f3fa717a84..602db4863edd 100644 --- a/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanPropertyElement.java +++ b/java/java-impl/src/com/intellij/psi/impl/beanProperties/BeanPropertyElement.java @@ -89,6 +89,7 @@ public class BeanPropertyElement extends FakePsiElement implements PsiMetaOwner, } + @NotNull @Override public Object[] getDependences() { return ArrayUtil.EMPTY_OBJECT_ARRAY; diff --git a/java/java-impl/src/com/intellij/psi/impl/cache/SmartIntToIntArrayMap.java b/java/java-impl/src/com/intellij/psi/impl/cache/SmartIntToIntArrayMap.java index 704442600529..5aee5706816e 100644 --- a/java/java-impl/src/com/intellij/psi/impl/cache/SmartIntToIntArrayMap.java +++ b/java/java-impl/src/com/intellij/psi/impl/cache/SmartIntToIntArrayMap.java @@ -19,6 +19,7 @@ import com.intellij.util.ArrayUtil; import gnu.trove.TIntArrayList; import gnu.trove.TIntIntHashMap; import gnu.trove.TIntObjectHashMap; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; /** @@ -28,6 +29,7 @@ public class SmartIntToIntArrayMap { @Nullable TIntObjectHashMap myMultipleValuesMap; TIntIntHashMap mySingleValueMap = new TIntIntHashMap(10, 0.9f); + @NotNull public int[] keys() { int[] multiKeys = myMultipleValuesMap != null ? myMultipleValuesMap.keys() : ArrayUtil.EMPTY_INT_ARRAY; int[] singleKeys = mySingleValueMap.keys(); @@ -95,6 +97,7 @@ public class SmartIntToIntArrayMap { } } + @NotNull public int[] get(int key) { if (mySingleValueMap.containsKey(key)) { int id = mySingleValueMap.get(key); @@ -104,6 +107,7 @@ public class SmartIntToIntArrayMap { return getFromMultimap(key); } + @NotNull private int[] getFromMultimap(int key) { TIntArrayList res = myMultipleValuesMap != null ? myMultipleValuesMap.get(key) : null; if (res == null) return ArrayUtil.EMPTY_INT_ARRAY; diff --git a/java/java-impl/src/com/intellij/psi/impl/migration/MigrationClassImpl.java b/java/java-impl/src/com/intellij/psi/impl/migration/MigrationClassImpl.java index c1d0e0aea4fd..299710bd8314 100644 --- a/java/java-impl/src/com/intellij/psi/impl/migration/MigrationClassImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/migration/MigrationClassImpl.java @@ -127,6 +127,7 @@ public class MigrationClassImpl extends LightElement implements PsiClass{ .findClass(CommonClassNames.JAVA_LANG_OBJECT, GlobalSearchScope.allScope(myManager.getProject())); } + @NotNull @Override public PsiClass[] getInterfaces() { return PsiClass.EMPTY_ARRAY; diff --git a/java/java-impl/src/com/intellij/psi/impl/migration/MigrationPackageImpl.java b/java/java-impl/src/com/intellij/psi/impl/migration/MigrationPackageImpl.java index 90d5ffe20df8..3b8c2fcb8dc0 100644 --- a/java/java-impl/src/com/intellij/psi/impl/migration/MigrationPackageImpl.java +++ b/java/java-impl/src/com/intellij/psi/impl/migration/MigrationPackageImpl.java @@ -50,6 +50,7 @@ public class MigrationPackageImpl extends PsiPackageImpl implements PsiPackage { throw new UnsupportedOperationException(); } + @NotNull @Override public VirtualFile[] occursInPackagePrefixes() { return VirtualFile.EMPTY_ARRAY; diff --git a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java index 4f9bade44f71..af96f721dd57 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/codeStyle/ImportHelper.java @@ -445,8 +445,8 @@ public class ImportHelper{ !mySettings.PACKAGES_TO_USE_IMPORT_ON_DEMAND.contains(packageName)) { useOnDemand = false; } - // name of class we try to import is the same as of the class defined in this file - if (containsInCurrentFile(file, curRefClass)) { + // name of class we try to import is the same as of the class defined in this package + if (containsInCurrentPackage(file, curRefClass)) { useOnDemand = true; } // check conflicts @@ -464,10 +464,19 @@ public class ImportHelper{ if (useOnDemand && refClass.getContainingClass() != null && mySettings.INSERT_INNER_CLASS_IMPORTS && - containsInCurrentFile(file, curRefClass)) { + containsInCurrentPackage(file, curRefClass)) { return false; } + if (curRefClass != null) { + if (!classesToReimport.isEmpty()) { + return false; + } + else { + useOnDemand = false; + } + } + try { PsiImportList importList = file.getImportList(); assert importList != null : file; @@ -499,7 +508,7 @@ public class ImportHelper{ return true; } - private static boolean containsInCurrentFile(@NotNull PsiJavaFile file, PsiClass curRefClass) { + private static boolean containsInCurrentPackage(@NotNull PsiJavaFile file, PsiClass curRefClass) { if (curRefClass != null) { final String curRefClassQualifiedName = curRefClass.getQualifiedName(); if (curRefClassQualifiedName != null && diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java index 3b0a38ed8040..87d6ce55d1e5 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaLangClassMemberReference.java @@ -38,11 +38,6 @@ import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflection * @author Konstantin Bulenkov */ public class JavaLangClassMemberReference extends PsiReferenceBase implements InsertHandler { - private static final String FIELD = "getField"; - private static final String DECLARED_FIELD = "getDeclaredField"; - private static final String METHOD = "getMethod"; - private static final String DECLARED_METHOD = "getDeclaredMethod"; - private final PsiExpression myContext; public JavaLangClassMemberReference(@NotNull PsiLiteralExpression literal, @NotNull PsiExpression context) { @@ -67,16 +62,16 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method) && isPublic(method)); if (publicMethod != null) { @@ -85,7 +80,7 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method)); } - case DECLARED_METHOD: { + case GET_DECLARED_METHOD: { final PsiMethod[] methods = psiClass.findMethodsByName(name, false); return ContainerUtil.find(methods, method -> isRegularMethod(method) && isPotentiallyAccessible(method, psiClass)); } @@ -110,14 +105,14 @@ public class JavaLangClassMemberReference extends PsiReferenceBase field.getName() != null) .sorted(Comparator.comparing(PsiField::getName)) .map(field -> lookupField(field)) .toArray(); - case FIELD: { + case GET_FIELD: { final Set uniqueNames = new THashSet<>(); return Arrays.stream(psiClass.getAllFields()) .filter(field -> isPotentiallyAccessible(field, psiClass) && field.getName() != null && uniqueNames.add(field.getName())) @@ -126,14 +121,14 @@ public class JavaLangClassMemberReference extends PsiReferenceBase isRegularMethod(method)) .sorted(Comparator.comparing(PsiMethod::getName)) .map(method -> lookupMethod(method)) .toArray(); - case METHOD: { + case GET_METHOD: { return psiClass.getVisibleSignatures() .stream() .map(MethodSignatureBackedByPsiMethod::getMethod) @@ -166,9 +161,10 @@ public class JavaLangClassMemberReference extends PsiReferenceBase implements InsertHandler { - static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup"; - static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType"; - - static final String FIND_VIRTUAL = "findVirtual"; - static final String FIND_STATIC = "findStatic"; - static final String FIND_SPECIAL = "findSpecial"; - - static final String FIND_GETTER = "findGetter"; - static final String FIND_SETTER = "findSetter"; - static final String FIND_STATIC_GETTER = "findStaticGetter"; - static final String FIND_STATIC_SETTER = "findStaticSetter"; - - static final String FIND_VAR_HANDLE = "findVarHandle"; - static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle"; private final PsiExpression myContext; @@ -197,27 +181,19 @@ public class JavaLangInvokeHandleReference extends PsiReferenceBase returnType = Stream.of(method.getReturnType()) - .map(type -> type != null ? type : PsiType.VOID); - final Stream parametersTypes = Arrays.stream(method.getParameterList().getParameters()) - .map(parameter -> parameter.getType()); - - final String types = Stream.concat(returnType, parametersTypes) - .map(type -> TypeConversionUtil.erasure(type)) - .map(type -> (type instanceof PsiEllipsisType) ? new PsiArrayType(((PsiEllipsisType)type).getComponentType()) : type) - .map(type -> type.getPresentableText() + ".class") - .collect(Collectors.joining(", ")); - final String text = ", " + JAVA_LANG_INVOKE_METHOD_TYPE + ".methodType(" + types + ")"; - - replaceText(context, text); + final List signature = getMethodSignature((PsiMethod)object); + if (signature != null) { + final String text = ", " + getMethodTypeExpressionText(signature); + replaceText(context, text); + } } else if (object instanceof PsiField) { final PsiField field = (PsiField)object; - final PsiType type = TypeConversionUtil.erasure(field.getType()); - final String text = ", " + type.getCanonicalText() + ".class"; - - replaceText(context, text); + final String typeText = getTypeText(field.getType(), field); + if (typeText != null) { + final String text = ", " + typeText + ".class"; + replaceText(context, text); + } } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionCompletionContributor.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionCompletionContributor.java index 2d67f7b7d0c5..fd3ec2982562 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionCompletionContributor.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionCompletionContributor.java @@ -160,7 +160,10 @@ public class JavaReflectionCompletionContributor extends CompletionContributor { private static void handleConstructorSignatureInsertion(@NotNull InsertionContext context, @NotNull LookupElement item) { Object object = item.getObject(); if (object instanceof PsiMethod) { - handleParametersInsertion(context, getParameterTypesText((PsiMethod)object)); + String text = getParameterTypesText((PsiMethod)object); + if (text != null) { + handleParametersInsertion(context, text); + } } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java index 803a950e8f67..706f9ec5a4dc 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceContributor.java @@ -26,32 +26,25 @@ import org.jetbrains.annotations.Nullable; import static com.intellij.patterns.PsiJavaPatterns.psiLiteral; import static com.intellij.patterns.PsiJavaPatterns.psiMethod; import static com.intellij.patterns.StandardPatterns.or; -import static com.intellij.patterns.StandardPatterns.string; import static com.intellij.psi.CommonClassNames.JAVA_LANG_CLASS; -import static com.intellij.psi.impl.source.resolve.reference.impl.JavaLangInvokeHandleReference.*; +import static com.intellij.psi.impl.source.resolve.reference.impl.JavaReflectionReferenceUtil.*; /** * @author Konstantin Bulenkov */ public class JavaReflectionReferenceContributor extends PsiReferenceContributor { public static final PsiJavaElementPattern.Capture PATTERN = - psiLiteral().methodCallParameter(psiMethod().withName(string().oneOf("getDeclaredField", - "getField", - "getMethod", - "getDeclaredMethod")) - .definedInClass(JAVA_LANG_CLASS)); + psiLiteral().methodCallParameter(psiMethod().withName(GET_FIELD, GET_DECLARED_FIELD, GET_METHOD, GET_DECLARED_METHOD) + .definedInClass(JAVA_LANG_CLASS)); public static final PsiJavaElementPattern.Capture CLASS_PATTERN = psiLiteral().methodCallParameter(or( - psiMethod().withName(string().equalTo("forName")).definedInClass(JAVA_LANG_CLASS), - psiMethod().withName(string().equalTo("loadClass")).definedInClass("java.lang.ClassLoader"))); + psiMethod().withName(FOR_NAME).definedInClass(JAVA_LANG_CLASS), + psiMethod().withName(LOAD_CLASS).definedInClass(JAVA_LANG_CLASS_LOADER))); private static final ElementPattern METHOD_HANDLE_PATTERN = psiLiteral() .methodCallParameter(1, psiMethod() - .withName(FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL, - FIND_GETTER, FIND_SETTER, - FIND_STATIC_GETTER, FIND_STATIC_SETTER, - FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE) + .withName(HANDLE_FACTORY_METHOD_NAMES) .definedInClass(JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP)); @Override @@ -63,7 +56,7 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor @NotNull PsiReferenceExpression methodReference, @NotNull ProcessingContext context) { - PsiExpression qualifier = methodReference.getQualifierExpression(); + final PsiExpression qualifier = methodReference.getQualifierExpression(); return qualifier != null ? new PsiReference[]{new JavaLangClassMemberReference(literalArgument, qualifier)} : null; } }); @@ -75,14 +68,14 @@ public class JavaReflectionReferenceContributor extends PsiReferenceContributor @NotNull PsiReferenceExpression methodReference, @NotNull ProcessingContext context) { - String referenceName = methodReference.getReferenceName(); - if ("forName".equals(referenceName) || "loadClass".equals(referenceName)) { + final String referenceName = methodReference.getReferenceName(); + if (FOR_NAME.equals(referenceName) || LOAD_CLASS.equals(referenceName)) { return new JavaClassReferenceProvider().getReferencesByElement(literalArgument, context); } return null; } }); - registrar.registerReferenceProvider(METHOD_HANDLE_PATTERN, new JavaLangInvokeHandleReferenceProvider()); + registrar.registerReferenceProvider(METHOD_HANDLE_PATTERN, new JavaLangInvokeHandleReference.JavaLangInvokeHandleReferenceProvider()); } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java index 37e1a57f912e..e179bdce480f 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/JavaReflectionReferenceUtil.java @@ -26,80 +26,107 @@ import com.intellij.psi.*; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.JavaConstantExpressionEvaluator; import com.intellij.psi.search.GlobalSearchScope; -import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiTypesUtil; -import com.intellij.psi.util.PsiUtilCore; -import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.psi.util.*; +import com.intellij.util.ObjectUtils; import com.siyeh.ig.psiutils.DeclarationSearchUtils; import com.siyeh.ig.psiutils.ParenthesesUtils; import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; +import java.util.ArrayList; +import java.util.List; +import java.util.StringJoiner; import java.util.stream.Collectors; /** * @author Pavel.Dolgov */ -class JavaReflectionReferenceUtil { +public class JavaReflectionReferenceUtil { + public static final String JAVA_LANG_INVOKE_METHOD_HANDLES_LOOKUP = "java.lang.invoke.MethodHandles.Lookup"; + public static final String JAVA_LANG_INVOKE_METHOD_TYPE = "java.lang.invoke.MethodType"; + + public static final String METHOD_TYPE = "methodType"; + public static final String GENERIC_METHOD_TYPE = "genericMethodType"; + + public static final String FIND_VIRTUAL = "findVirtual"; + public static final String FIND_STATIC = "findStatic"; + public static final String FIND_SPECIAL = "findSpecial"; + + public static final String FIND_GETTER = "findGetter"; + public static final String FIND_SETTER = "findSetter"; + public static final String FIND_STATIC_GETTER = "findStaticGetter"; + public static final String FIND_STATIC_SETTER = "findStaticSetter"; + + public static final String FIND_VAR_HANDLE = "findVarHandle"; + public static final String FIND_STATIC_VAR_HANDLE = "findStaticVarHandle"; + + public static final String GET_FIELD = "getField"; + public static final String GET_DECLARED_FIELD = "getDeclaredField"; + public static final String GET_METHOD = "getMethod"; + public static final String GET_DECLARED_METHOD = "getDeclaredMethod"; + + public static final String JAVA_LANG_CLASS_LOADER = "java.lang.ClassLoader"; + public static final String FOR_NAME = "forName"; + public static final String LOAD_CLASS = "loadClass"; + public static final String GET_CLASS = "getClass"; + + public static final String[] HANDLE_FACTORY_METHOD_NAMES = { + FIND_VIRTUAL, FIND_STATIC, FIND_SPECIAL, + FIND_GETTER, FIND_SETTER, + FIND_STATIC_GETTER, FIND_STATIC_SETTER, + FIND_VAR_HANDLE, FIND_STATIC_VAR_HANDLE}; + private static final RecursionGuard ourGuard = RecursionManager.createGuard("JavaLangClassMemberReference"); @Nullable - static PsiClass getReflectiveClass(@Nullable PsiExpression context) { + public static ReflectiveType getReflectiveType(@Nullable PsiExpression context) { context = ParenthesesUtils.stripParentheses(context); if (context == null) { return null; } if (context instanceof PsiClassObjectAccessExpression) { // special case for JDK 1.4 - PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand(); - return PsiTypesUtil.getPsiClass(operand.getType()); + final PsiTypeElement operand = ((PsiClassObjectAccessExpression)context).getOperand(); + return ReflectiveType.create(operand.getType(), context); } if (context instanceof PsiMethodCallExpression) { final PsiMethodCallExpression methodCall = (PsiMethodCallExpression)context; final String methodReferenceName = methodCall.getMethodExpression().getReferenceName(); - if ("forName".equals(methodReferenceName)) { + if (FOR_NAME.equals(methodReferenceName)) { final PsiMethod method = methodCall.resolveMethod(); if (method != null && isJavaLangClass(method.getContainingClass())) { final PsiExpression[] expressions = methodCall.getArgumentList().getExpressions(); if (expressions.length == 1) { - PsiExpression argument = ParenthesesUtils.stripParentheses(expressions[0]); - if (argument instanceof PsiReferenceExpression) { - argument = findVariableDefinition(((PsiReferenceExpression)argument)); - } - final Object value = JavaConstantExpressionEvaluator.computeConstantExpression(argument, false); - if (value instanceof String) { - final Project project = context.getProject(); - return JavaPsiFacade.getInstance(project).findClass((String)value, GlobalSearchScope.allScope(project)); + final PsiExpression argument = findDefinition(ParenthesesUtils.stripParentheses(expressions[0])); + final String className = computeConstantExpression(argument, String.class); + if (className != null) { + return ReflectiveType.create(findClass(className, context)); } } } } - else if ("getClass".equals(methodReferenceName) && methodCall.getArgumentList().getExpressions().length == 0) { + else if (GET_CLASS.equals(methodReferenceName) && methodCall.getArgumentList().getExpressions().length == 0) { final PsiMethod method = methodCall.resolveMethod(); if (method != null && isJavaLangObject(method.getContainingClass())) { final PsiExpression qualifier = ParenthesesUtils.stripParentheses(methodCall.getMethodExpression().getQualifierExpression()); if (qualifier instanceof PsiReferenceExpression) { final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)qualifier); if (definition != null) { - final PsiClass actualClass = PsiTypesUtil.getPsiClass(definition.getType()); - if (actualClass != null) { - return actualClass; - } + return ReflectiveType.create(definition.getType(), context); } } //TODO type of the qualifier may be a supertype of the actual value - need to compute the type of the actual value // otherwise getDeclaredField and getDeclaredMethod may work not reliably if (qualifier != null) { - return PsiTypesUtil.getPsiClass(qualifier.getType()); + return ReflectiveType.create(qualifier.getType(), context); } } } } - PsiType type = context.getType(); + final PsiType type = context.getType(); if (type instanceof PsiClassType) { - PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics(); + final PsiClassType.ClassResolveResult resolveResult = ((PsiClassType)type).resolveGenerics(); if (!isJavaLangClass(resolveResult.getElement())) return null; final PsiTypeParameter[] parameters = resolveResult.getElement().getTypeParameters(); if (parameters.length == 1) { @@ -109,7 +136,7 @@ class JavaReflectionReferenceUtil { } final PsiClass argumentClass = PsiTypesUtil.getPsiClass(typeArgument); if (argumentClass != null && !isJavaLangObject(argumentClass)) { - return argumentClass; + return ReflectiveType.create(argumentClass); } } } @@ -118,18 +145,41 @@ class JavaReflectionReferenceUtil { if (resolved instanceof PsiVariable) { final PsiExpression definition = findVariableDefinition((PsiReferenceExpression)context, (PsiVariable)resolved); if (definition != null) { - return ourGuard.doPreventingRecursion(resolved, false, () -> getReflectiveClass(definition)); + return ourGuard.doPreventingRecursion(resolved, false, () -> getReflectiveType(definition)); } } } return null; } + @Nullable + public static T computeConstantExpression(@Nullable PsiExpression expression, @NotNull Class expectedType) { + expression = ParenthesesUtils.stripParentheses(expression); + final Object computed = JavaConstantExpressionEvaluator.computeConstantExpression(expression, false); + return ObjectUtils.tryCast(computed, expectedType); + } + + @Nullable + public static PsiClass getReflectiveClass(PsiExpression context) { + final ReflectiveType reflectiveType = getReflectiveType(context); + return reflectiveType != null ? reflectiveType.myPsiClass : null; + } + + @Nullable + public static PsiExpression findDefinition(@Nullable PsiExpression expression) { + if (expression instanceof PsiReferenceExpression) { + return findVariableDefinition((PsiReferenceExpression)expression); + } + return expression; + } + + @Nullable private static PsiExpression findVariableDefinition(@NotNull PsiReferenceExpression referenceExpression) { final PsiElement resolved = referenceExpression.resolve(); return resolved instanceof PsiVariable ? findVariableDefinition(referenceExpression, (PsiVariable)resolved) : null; } + @Nullable private static PsiExpression findVariableDefinition(@NotNull PsiReferenceExpression referenceExpression, @NotNull PsiVariable variable) { if (variable.hasModifierProperty(PsiModifier.FINAL)) { final PsiExpression initializer = variable.getInitializer(); @@ -140,6 +190,11 @@ class JavaReflectionReferenceUtil { return DeclarationSearchUtils.findDefinition(referenceExpression, variable); } + private static PsiClass findClass(@NotNull String qualifiedName, @NotNull PsiElement context) { + final Project project = context.getProject(); + return JavaPsiFacade.getInstance(project).findClass(qualifiedName, GlobalSearchScope.allScope(project)); + } + static boolean isJavaLangClass(@Nullable PsiClass aClass) { return aClass != null && CommonClassNames.JAVA_LANG_CLASS.equals(aClass.getQualifiedName()); } @@ -157,13 +212,15 @@ class JavaReflectionReferenceUtil { return member.hasModifierProperty(PsiModifier.PUBLIC); } - @NotNull + @Nullable static String getParameterTypesText(@NotNull PsiMethod method) { - return Arrays.stream(method.getParameterList().getParameters()) - .map(parameter -> TypeConversionUtil.erasure(parameter.getType())) - .map(type -> (type instanceof PsiEllipsisType) ? new PsiArrayType(((PsiEllipsisType)type).getComponentType()) : type) - .map(type -> type.getPresentableText() + ".class") - .collect(Collectors.joining(", ")); + final StringJoiner joiner = new StringJoiner(", "); + for (PsiParameter parameter : method.getParameterList().getParameters()) { + final String typeText = getTypeText(parameter.getType(), method); + if (typeText == null) return null; + joiner.add(typeText + ".class"); + } + return joiner.toString(); } static void shortenArgumentsClassReferences(@NotNull InsertionContext context) { @@ -209,4 +266,126 @@ class JavaReflectionReferenceUtil { context.commitDocument(); shortenArgumentsClassReferences(context); } + + @Nullable + public static String getTypeText(@Nullable PsiType type, @NotNull PsiElement context) { + final ReflectiveType reflectiveType = ReflectiveType.create(type, context); + return reflectiveType != null ? reflectiveType.getQualifiedName() : null; + } + + @Nullable + public static String getTypeText(@Nullable PsiExpression argument) { + final ReflectiveType reflectiveType = getReflectiveType(argument); + return reflectiveType != null ? reflectiveType.getQualifiedName() : null; + } + + @Contract("null -> null") + @Nullable + public static List getMethodSignature(@Nullable PsiMethod method) { + if (method != null) { + final List types = new ArrayList<>(); + final PsiType returnType = !method.isConstructor() ? method.getReturnType() : PsiType.VOID; + types.add(getTypeText(returnType, method)); + + for (PsiParameter parameter : method.getParameterList().getParameters()) { + types.add(getTypeText(parameter.getType(), method)); + } + if (!types.contains(null)) { + return types; + } + } + return null; + } + + @NotNull + public static String getMethodTypeExpressionText(@NotNull List signature) { + final String types = signature.stream() + .map(text -> text + ".class") + .collect(Collectors.joining(", ")); + return JAVA_LANG_INVOKE_METHOD_TYPE + "." + METHOD_TYPE + "(" + types + ")"; + } + + + public static class ReflectiveType { + final PsiClass myPsiClass; + final PsiPrimitiveType myPrimitiveType; + final int myArrayDimensions; + + public ReflectiveType(PsiClass psiClass, PsiPrimitiveType primitiveType, int arrayDimensions) { + myPsiClass = psiClass; + myPrimitiveType = primitiveType; + myArrayDimensions = arrayDimensions; + } + + @Nullable + public String getQualifiedName() { + String text = null; + if (myPrimitiveType != null) { + text = myPrimitiveType.getCanonicalText(); + } + else if (myPsiClass != null) { + text = myPsiClass.getQualifiedName(); + } + if (myArrayDimensions == 0 || text == null) { + return text; + } + final StringBuilder sb = new StringBuilder(text); + for (int i = 0; i < myArrayDimensions; i++) { + sb.append("[]"); + } + return sb.toString(); + } + + @Override + public String toString() { + final String name = getQualifiedName(); + return name != null ? name : "null"; + } + + public boolean isEqualTo(@Nullable PsiType otherType) { + if (otherType == null || myArrayDimensions != otherType.getArrayDimensions()) { + return false; + } + final PsiType otherComponentType = otherType.getDeepComponentType(); + if (myPrimitiveType != null) { + return myPrimitiveType.equals(otherComponentType); + } + if (myPsiClass != null) { + final PsiClass otherClass = PsiUtil.resolveClassInType(otherComponentType); + if (otherClass != null) { + final String otherClassName = otherClass instanceof PsiTypeParameter + ? CommonClassNames.JAVA_LANG_OBJECT : otherClass.getQualifiedName(); + if (otherClassName != null) { + return otherClassName.equals(myPsiClass.getQualifiedName()); + } + } + } + return false; + } + + @Nullable + public static ReflectiveType create(@Nullable PsiType originalType, @NotNull PsiElement context) { + if (originalType == null) { + return null; + } + final int arrayDimensions = originalType.getArrayDimensions(); + final PsiType type = originalType.getDeepComponentType(); + if (type instanceof PsiPrimitiveType) { + return new ReflectiveType(null, (PsiPrimitiveType)type, arrayDimensions); + } + PsiClass psiClass = PsiUtil.resolveClassInType(type); + if (psiClass instanceof PsiTypeParameter) { + psiClass = findClass(CommonClassNames.JAVA_LANG_OBJECT, context); + } + if (psiClass != null) { + return new ReflectiveType(psiClass, null, arrayDimensions); + } + return null; + } + + @Nullable + public static ReflectiveType create(@Nullable PsiClass psiClass) { + return psiClass != null ? new ReflectiveType(psiClass, null, 0) : null; + } + } } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReference.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReference.java index d6a45a6e22ea..a44fea45094d 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReference.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReference.java @@ -30,7 +30,6 @@ import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.util.Key; import com.intellij.openapi.util.TextRange; @@ -167,7 +166,7 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer @Override public boolean isReferenceTo(PsiElement element) { - return (element instanceof PsiClass || element instanceof PsiPackage) && super.isReferenceTo(element); + return (element instanceof PsiMember || element instanceof PsiPackage) && super.isReferenceTo(element); } @Override @@ -268,6 +267,7 @@ public class JavaClassReference extends GenericReference implements PsiJavaRefer return context == null ? JavaPsiFacade.getInstance(getElement().getProject()).findPackage("") : context; } + @Nullable public String[] getExtendClassNames() { return JavaClassReferenceProvider.EXTEND_CLASS_NAMES.getValue(getOptions()); } diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReferenceProvider.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReferenceProvider.java index cc0267c567c4..e782f3748043 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReferenceProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/JavaClassReferenceProvider.java @@ -115,6 +115,7 @@ public class JavaClassReferenceProvider extends GenericReferenceProvider impleme return getReferencesByElement(element); } + @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element) { final int offsetInElement = ElementManipulators.getOffsetInElement(element); final String text = ElementManipulators.getValueText(element); diff --git a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java index df171339ff01..c8b409dac004 100644 --- a/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java +++ b/java/java-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/PathListReferenceProvider.java @@ -39,6 +39,7 @@ public class PathListReferenceProvider extends PsiReferenceProvider { return true; } + @NotNull public PsiReference[] getReferencesByElement(@NotNull PsiElement element) { PsiReference[] result = PsiReference.EMPTY_ARRAY; @@ -75,6 +76,7 @@ public class PathListReferenceProvider extends PsiReferenceProvider { return ','; } + @NotNull protected PsiReference[] createReferences(@NotNull PsiElement element, String s, int offset, final boolean soft) { int contentOffset = StringUtil.findFirst(s, CharFilter.NOT_WHITESPACE_FILTER); if (contentOffset >= 0) { diff --git a/java/java-impl/src/com/intellij/psi/refResolve/RefResolveServiceImpl.java b/java/java-impl/src/com/intellij/psi/refResolve/RefResolveServiceImpl.java index dfb72a5ec21e..e1182ee20443 100644 --- a/java/java-impl/src/com/intellij/psi/refResolve/RefResolveServiceImpl.java +++ b/java/java-impl/src/com/intellij/psi/refResolve/RefResolveServiceImpl.java @@ -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. @@ -171,7 +171,7 @@ public class RefResolveServiceImpl extends RefResolveService implements Runnable } private void initListeners(@NotNull MessageBus messageBus, @NotNull PsiManager psiManager) { - messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { + messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { @Override public void after(@NotNull List events) { fileCount.set(0); diff --git a/java/java-impl/src/com/intellij/refactoring/OptimizeImportsRefactoringHelper.java b/java/java-impl/src/com/intellij/refactoring/OptimizeImportsRefactoringHelper.java index f78e66f2433a..af544c7fa9ed 100644 --- a/java/java-impl/src/com/intellij/refactoring/OptimizeImportsRefactoringHelper.java +++ b/java/java-impl/src/com/intellij/refactoring/OptimizeImportsRefactoringHelper.java @@ -31,9 +31,7 @@ import com.intellij.util.SequentialModalProgressTask; import com.intellij.util.SequentialTask; import com.intellij.util.containers.HashSet; -import java.util.Collection; -import java.util.Iterator; -import java.util.Set; +import java.util.*; public class OptimizeImportsRefactoringHelper implements RefactoringHelper> { private static final String REMOVING_REDUNDANT_IMPORTS_TITLE = "Removing redundant imports"; @@ -60,7 +58,7 @@ public class OptimizeImportsRefactoringHelper implements RefactoringHelper> redundants = new HashSet<>(); + final List> redundants = new ArrayList<>(); final Runnable findRedundantImports = () -> ReadAction.run(() -> { final JavaCodeStyleManager styleManager = JavaCodeStyleManager.getInstance(project); final ProgressIndicator progressIndicator = ProgressManager.getInstance().getProgressIndicator(); @@ -106,7 +104,7 @@ class OptimizeImportsTask implements SequentialTask { private final int myTotal; private int myCount; - public OptimizeImportsTask(SequentialModalProgressTask progressTask, Set> pointers) { + public OptimizeImportsTask(SequentialModalProgressTask progressTask, Collection> pointers) { myTask = progressTask; myTotal = pointers.size(); myPointers = pointers.iterator(); diff --git a/java/java-impl/src/com/intellij/refactoring/actions/TypeCookAction.java b/java/java-impl/src/com/intellij/refactoring/actions/TypeCookAction.java index a4cb28f40f90..08c6926ecd39 100644 --- a/java/java-impl/src/com/intellij/refactoring/actions/TypeCookAction.java +++ b/java/java-impl/src/com/intellij/refactoring/actions/TypeCookAction.java @@ -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. @@ -15,56 +15,39 @@ */ package com.intellij.refactoring.actions; -import com.intellij.ide.DataManager; import com.intellij.lang.Language; import com.intellij.lang.java.JavaLanguage; -import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.PlatformDataKeys; -import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.typeCook.TypeCookHandler; import org.jetbrains.annotations.NotNull; -public class TypeCookAction extends BaseRefactoringAction { +import java.util.Arrays; +public class TypeCookAction extends BaseRefactoringAction { + @Override protected boolean isAvailableInEditorOnly() { - return false; + return false; } + @Override public boolean isAvailableForLanguage(Language language) { return language.equals(JavaLanguage.INSTANCE); } + @Override public boolean isEnabledOnElements(@NotNull PsiElement[] elements) { - Project project = CommonDataKeys.PROJECT.getData(DataManager.getInstance().getDataContext()); - - if (project == null) { - return false; - } - - for (int i = 0; i < elements.length; i++) { - PsiElement element = elements[i]; - - if ( - !(element instanceof PsiClass || - element instanceof PsiJavaFile || - element instanceof PsiDirectory || - element instanceof PsiPackage - ) - ) { - return false; - } - } - - return true; + return elements.length > 0 && Arrays.stream(elements).allMatch( + e -> e instanceof PsiClass || e instanceof PsiJavaFile || e instanceof PsiDirectory || e instanceof PsiPackage); } + @Override public RefactoringActionHandler getHandler(@NotNull DataContext dataContext) { return getHandler(); } + public RefactoringActionHandler getHandler() { return new TypeCookHandler(); } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/chainCall/ChainCallExtractor.java b/java/java-impl/src/com/intellij/refactoring/chainCall/ChainCallExtractor.java index 9d1a40874fd7..d3b3a22f8487 100644 --- a/java/java-impl/src/com/intellij/refactoring/chainCall/ChainCallExtractor.java +++ b/java/java-impl/src/com/intellij/refactoring/chainCall/ChainCallExtractor.java @@ -19,6 +19,7 @@ import com.intellij.codeInspection.LambdaCanBeMethodReferenceInspection; import com.intellij.codeInspection.util.OptionalUtil; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.psi.util.PsiTreeUtil; @@ -104,7 +105,8 @@ public interface ChainCallExtractor { PsiMethodCallExpression call = tryCast(args.getParent(), PsiMethodCallExpression.class); if (call == null) return null; for(ChainCallExtractor extractor : KEY.getExtensions()) { - if(extractor.canExtractChainCall(call, expression, targetType)) { + if(extractor.canExtractChainCall(call, expression, targetType) && + StringUtil.isNotEmpty(extractor.getMethodName(parameter, expression, targetType))) { return extractor; } } diff --git a/java/java-impl/src/com/intellij/refactoring/copy/CopyClassesHandler.java b/java/java-impl/src/com/intellij/refactoring/copy/CopyClassesHandler.java index 44ccafd74eef..89724a373009 100644 --- a/java/java-impl/src/com/intellij/refactoring/copy/CopyClassesHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/copy/CopyClassesHandler.java @@ -43,6 +43,7 @@ import com.intellij.util.IncorrectOperationException; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import javax.swing.*; import java.io.IOException; import java.util.*; @@ -208,18 +209,19 @@ public class CopyClassesHandler extends CopyHandlerDelegateBase { } else { defaultTargetDirectory = CopyFilesOrDirectoriesHandler.resolveDirectory(defaultTargetDirectory); if (defaultTargetDirectory == null) return; - PsiElement[] files = PsiUtilCore.toPsiFileArray(classes.keySet()); - if (classes.keySet().size() == 1) { - //do not choose a new name for a file when multiple classes exist in one file - final PsiClass[] psiClasses = classes.values().iterator().next(); - if (psiClasses != null) { - files = psiClasses; + PsiFile[] files = PsiUtilCore.toPsiFileArray(classes.keySet()); + final CopyFilesOrDirectoriesDialog dialog = new CopyFilesOrDirectoriesDialog(files, defaultTargetDirectory, project, false) { + @Override + public JComponent getPreferredFocusedComponent() { + return files.length == 1 ? getTargetDirectoryComponent() : super.getPreferredFocusedComponent(); } - } - final CopyFilesOrDirectoriesDialog dialog = new CopyFilesOrDirectoriesDialog(files, defaultTargetDirectory, project, false); + }; if (dialog.showAndGet()) { targetDirectory = dialog.getTargetDirectory(); - className = dialog.getNewName(); + String newName = dialog.getNewName(); + if (files.length == 1) { //strip file extension when multiple classes exist in one file + className = StringUtil.trimEnd(newName, "." + getFileExtension(files[0])); + } openInEditor = dialog.openInEditor(); } } @@ -337,13 +339,14 @@ public class CopyClassesHandler extends CopyHandlerDelegateBase { } continue; } - for (final PsiClass destination : ((PsiClassOwner)createdFile).getClasses()) { + PsiClass[] classes = ((PsiClassOwner)createdFile).getClasses(); + for (final PsiClass destination : classes) { if (isSynthetic(destination)) { continue; } PsiClass source = findByName(sources, destination.getName()); if (source != null) { - final PsiClass copy = copy(source, copyClassName); + final PsiClass copy = copy(source, classes.length > 1 ? null : copyClassName); newElement = WriteAction.compute(() -> destination.replace(copy)); oldToNewMap.put(source, newElement); } @@ -419,18 +422,23 @@ public class CopyClassesHandler extends CopyHandlerDelegateBase { private static String getNewFileName(PsiFile file, String name) { if (name != null) { - if (file instanceof PsiClassOwner) { - for (final PsiClass psiClass : ((PsiClassOwner)file).getClasses()) { - if (!isSynthetic(psiClass)) { - return name + "." + file.getViewProvider().getVirtualFile().getExtension(); - } - } - } - return name; + String fileExtension = getFileExtension(file); + return fileExtension.isEmpty() ? name : StringUtil.getQualifiedName(name, fileExtension); } return file.getName(); } + private static String getFileExtension(PsiFile file) { + if (file instanceof PsiClassOwner) { + for (final PsiClass psiClass : ((PsiClassOwner)file).getClasses()) { + if (!isSynthetic(psiClass)) { + return file.getViewProvider().getVirtualFile().getExtension(); + } + } + } + return ""; + } + @NotNull private static MoveDirectoryWithClassesProcessor.TargetDirectoryWrapper buildRelativeDir(final @NotNull PsiDirectory directory, final @NotNull String relativePath) { diff --git a/java/java-impl/src/com/intellij/refactoring/extractInterface/ExtractInterfaceHandler.java b/java/java-impl/src/com/intellij/refactoring/extractInterface/ExtractInterfaceHandler.java index da7303462926..8a278b6557f4 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractInterface/ExtractInterfaceHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractInterface/ExtractInterfaceHandler.java @@ -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. @@ -17,6 +17,7 @@ package com.intellij.refactoring.extractInterface; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.findUsages.DescriptiveNameUtil; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; @@ -27,6 +28,7 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.codeStyle.CodeStyleManager; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringBundle; @@ -42,20 +44,23 @@ import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; -public class ExtractInterfaceHandler implements RefactoringActionHandler, ElementsHandler { +public class ExtractInterfaceHandler implements RefactoringActionHandler, ElementsHandler, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractInterface.ExtractInterfaceHandler"); public static final String REFACTORING_NAME = RefactoringBundle.message("extract.interface.title"); - private Project myProject; private PsiClass myClass; - private String myInterfaceName; private MemberInfo[] mySelectedMembers; private PsiDirectory myTargetDir; private DocCommentPolicy myJavaDocPolicy; + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !PsiUtil.isModuleFile(file); + } + public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { int offset = editor.getCaretModel().getOffset(); editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); @@ -80,13 +85,13 @@ public class ExtractInterfaceHandler implements RefactoringActionHandler, Elemen myProject = project; myClass = (PsiClass)elements[0]; - if (!CommonRefactoringUtil.checkReadOnlyStatus(project, myClass)) return; final ExtractInterfaceDialog dialog = new ExtractInterfaceDialog(myProject, myClass); if (!dialog.showAndGet() || !dialog.isExtractSuperclass()) { return; } + final MultiMap conflicts = new MultiMap<>(); ExtractSuperClassUtil.checkSuperAccessible(dialog.getTargetDirectory(), conflicts, myClass); if (!ExtractSuperClassUtil.showConflicts(dialog, conflicts, myProject)) return; @@ -104,7 +109,6 @@ public class ExtractInterfaceHandler implements RefactoringActionHandler, Elemen }), REFACTORING_NAME, null); } - private void doRefactoring() throws IncorrectOperationException { LocalHistoryAction a = LocalHistory.getInstance().startAction(getCommandName()); final PsiClass anInterface; @@ -149,4 +153,4 @@ public class ExtractInterfaceHandler implements RefactoringActionHandler, Elemen public boolean isEnabledOnElements(PsiElement[] elements) { return elements.length == 1 && elements[0] instanceof PsiClass; } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/extractSuperclass/ExtractSuperclassHandler.java b/java/java-impl/src/com/intellij/refactoring/extractSuperclass/ExtractSuperclassHandler.java index 996ade31f8b0..0497e5bac797 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractSuperclass/ExtractSuperclassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractSuperclass/ExtractSuperclassHandler.java @@ -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. @@ -13,11 +13,11 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.refactoring.extractSuperclass; import com.intellij.history.LocalHistory; import com.intellij.history.LocalHistoryAction; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.lang.findUsages.DescriptiveNameUtil; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; @@ -29,8 +29,8 @@ import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.progress.ProgressManager; import com.intellij.openapi.project.Project; import com.intellij.psi.*; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; -import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringBundle; import com.intellij.refactoring.extractInterface.ExtractClassUtil; import com.intellij.refactoring.lang.ElementsHandler; @@ -46,7 +46,7 @@ import org.jetbrains.annotations.NotNull; import java.util.List; -public class ExtractSuperclassHandler implements RefactoringActionHandler, ExtractSuperclassDialog.Callback, ElementsHandler { +public class ExtractSuperclassHandler implements ElementsHandler, ExtractSuperclassDialog.Callback, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.extractSuperclass.ExtractSuperclassHandler"); public static final String REFACTORING_NAME = RefactoringBundle.message("extract.superclass.title"); @@ -54,6 +54,10 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra private PsiClass mySubclass; private Project myProject; + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !PsiUtil.isModuleFile(file); + } @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { @@ -97,7 +101,6 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra return; } - List memberInfos = MemberInfo.extractClassMembers(mySubclass, new MemberInfo.Filter() { @Override public boolean includeMember(PsiMember element) { @@ -110,8 +113,7 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra memberInfo.getOverrides() != null)); } - final ExtractSuperclassDialog dialog = - new ExtractSuperclassDialog(project, mySubclass, memberInfos, this); + final ExtractSuperclassDialog dialog = new ExtractSuperclassDialog(project, mySubclass, memberInfos, this); if (!dialog.showAndGet() || !dialog.isExtractSuperclass()) { return; } @@ -126,26 +128,22 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra public boolean checkConflicts(final ExtractSuperclassDialog dialog) { final MemberInfo[] infos = ArrayUtil.toObjectArray(dialog.getSelectedMemberInfos(), MemberInfo.class); final PsiDirectory targetDirectory = dialog.getTargetDirectory(); - final PsiPackage targetPackage; - if (targetDirectory != null) { - targetPackage = JavaDirectoryService.getInstance().getPackage(targetDirectory); - } - else { - targetPackage = null; - } + final PsiPackage targetPackage = targetDirectory != null ? JavaDirectoryService.getInstance().getPackage(targetDirectory) : null; final MultiMap conflicts = new MultiMap<>(); if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ApplicationManager.getApplication().runReadAction(() -> { final PsiClass superClass = mySubclass.getExtendsListTypes().length > 0 || mySubclass instanceof PsiAnonymousClass ? mySubclass.getSuperClass() : null; - conflicts.putAllValues(PullUpConflictsUtil.checkConflicts(infos, mySubclass, superClass, targetPackage, targetDirectory, - dialog.getContainmentVerifier(), false)); + if (targetPackage != null) { + conflicts.putAllValues(PullUpConflictsUtil.checkConflicts( + infos, mySubclass, superClass, targetPackage, targetDirectory, dialog.getContainmentVerifier(), false)); + } }), RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) return false; ExtractSuperClassUtil.checkSuperAccessible(targetDirectory, conflicts, mySubclass); return ExtractSuperClassUtil.showConflicts(dialog, conflicts, myProject); } // invoked inside Command and Atomic action - private void doRefactoring(final Project project, final PsiClass subclass, final ExtractSuperclassDialog dialog) { + private static void doRefactoring(final Project project, final PsiClass subclass, final ExtractSuperclassDialog dialog) { final String superclassName = dialog.getExtractedSuperName(); final PsiDirectory targetDirectory = dialog.getTargetDirectory(); final MemberInfo[] selectedMemberInfos = ArrayUtil.toObjectArray(dialog.getSelectedMemberInfos(), MemberInfo.class); @@ -153,10 +151,8 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra LocalHistoryAction a = LocalHistory.getInstance().startAction(getCommandName(subclass, superclassName)); try { final PsiClass superclass; - try { - superclass = - ExtractSuperClassUtil.extractSuperClass(project, targetDirectory, superclassName, subclass, selectedMemberInfos, javaDocPolicy); + superclass = ExtractSuperClassUtil.extractSuperClass(project, targetDirectory, superclassName, subclass, selectedMemberInfos, javaDocPolicy); } finally { a.finish(); @@ -168,10 +164,9 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra catch (IncorrectOperationException e) { LOG.error(e); } - } - private String getCommandName(final PsiClass subclass, String newName) { + private static String getCommandName(final PsiClass subclass, String newName) { return RefactoringBundle.message("extract.superclass.command.name", newName, DescriptiveNameUtil.getDescriptiveName(subclass)); } @@ -180,4 +175,4 @@ public class ExtractSuperclassHandler implements RefactoringActionHandler, Extra return elements.length == 1 && elements[0] instanceof PsiClass && !((PsiClass) elements[0]).isInterface() &&!((PsiClass)elements[0]).isEnum(); } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java index 812bd6466404..e66e623f708e 100644 --- a/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/extractclass/ExtractClassHandler.java @@ -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. @@ -15,6 +15,7 @@ */ package com.intellij.refactoring.extractclass; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Editor; @@ -23,18 +24,23 @@ import com.intellij.openapi.editor.ScrollingModel; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.util.PsiTreeUtil; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactorJBundle; import com.intellij.refactoring.lang.ElementsHandler; import com.intellij.refactoring.util.CommonRefactoringUtil; import org.jetbrains.annotations.NotNull; -public class ExtractClassHandler implements ElementsHandler { - +public class ExtractClassHandler implements ElementsHandler, ContextAwareActionHandler { protected static String getHelpID() { return HelpID.ExtractClass; } + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !PsiUtil.isModuleFile(file); + } + @Override public boolean isEnabledOnElements(PsiElement[] elements) { return elements.length == 1 && PsiTreeUtil.getParentOfType(elements[0], PsiClass.class, false) != null; @@ -125,4 +131,4 @@ public class ExtractClassHandler implements ElementsHandler { } return false; } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java index a6b2b289d0f9..2b9f9496336c 100644 --- a/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/inheritanceToDelegation/InheritanceToDelegationHandler.java @@ -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. @@ -24,15 +24,16 @@ */ package com.intellij.refactoring.inheritanceToDelegation; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; -import com.intellij.openapi.actionSystem.PlatformDataKeys; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; import com.intellij.psi.*; import com.intellij.psi.impl.source.jsp.jspJava.JspClass; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringBundle; @@ -46,8 +47,9 @@ import org.jetbrains.annotations.NotNull; import java.util.*; -public class InheritanceToDelegationHandler implements RefactoringActionHandler { +public class InheritanceToDelegationHandler implements RefactoringActionHandler, ContextAwareActionHandler { private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.inheritanceToDelegation.InheritanceToDelegationHandler"); + public static final String REFACTORING_NAME = RefactoringBundle.message("replace.inheritance.with.delegation.title"); private static final MemberInfo.Filter MEMBER_INFO_FILTER = new MemberInfo.Filter() { @@ -64,7 +66,12 @@ public class InheritanceToDelegationHandler implements RefactoringActionHandler } }; + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !PsiUtil.isModuleFile(file); + } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); int offset = editor.getCaretModel().getOffset(); @@ -84,6 +91,7 @@ public class InheritanceToDelegationHandler implements RefactoringActionHandler } } + @Override public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { if (elements.length != 1) return; @@ -131,12 +139,9 @@ public class InheritanceToDelegationHandler implements RefactoringActionHandler final MemberInfoStorage memberInfoStorage = new MemberInfoStorage(baseClass, MEMBER_INFO_FILTER); - ArrayList memberInfoList = new ArrayList<>(memberInfoStorage.getClassMemberInfos(deepestBase)); - List memberInfos = memberInfoStorage.getIntermediateMemberInfosList(deepestBase); - for (final MemberInfo memberInfo : memberInfos) { - memberInfoList.add(memberInfo); - } - + List memberInfoList = new ArrayList<>(); + memberInfoList.addAll(memberInfoStorage.getClassMemberInfos(deepestBase)); + memberInfoList.addAll(memberInfoStorage.getIntermediateMemberInfosList(deepestBase)); return memberInfoList; } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodProcessor.java b/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodProcessor.java index e454cadca134..c3417897e5fa 100644 --- a/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/inline/InlineMethodProcessor.java @@ -55,6 +55,7 @@ import com.intellij.refactoring.rename.RenameJavaVariableProcessor; import com.intellij.refactoring.util.*; import com.intellij.usageView.UsageInfo; import com.intellij.usageView.UsageViewDescriptor; +import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.HashMap; import com.intellij.util.containers.MultiMap; @@ -656,7 +657,24 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor { if (enumConstant != null) { PsiExpression returnExpr = getSimpleReturnedExpression(myMethod); if (returnExpr != null) { - methodCall.replace(returnExpr); + PsiElement copy = returnExpr.copy(); + copy.accept(new JavaRecursiveElementVisitor() { + @Override + public void visitReferenceExpression(PsiReferenceExpression expression) { + super.visitReferenceExpression(expression); + PsiElement resolve = expression.resolve(); + if (resolve instanceof PsiParameter) { + int paramIdx = ArrayUtil.find(myMethod.getParameterList().getParameters(), resolve); + if (paramIdx >= 0) { + PsiExpression initializer = blockData.parmVars[paramIdx].getInitializer(); + if (initializer != null) { + inlineInitializer((PsiVariable)resolve, initializer, expression); + } + } + } + } + }); + methodCall.replace(copy); } } return; @@ -1037,39 +1055,43 @@ public class InlineMethodProcessor extends BaseRefactoringProcessor { declareUsedLocalsFinal(initializer, strictlyFinal); } for (PsiReference ref : refs) { - final PsiJavaCodeReferenceElement javaRef = (PsiJavaCodeReferenceElement)ref; - if (initializer instanceof PsiThisExpression && ((PsiThisExpression)initializer).getQualifier() == null) { - final PsiClass varThisClass = RefactoringChangeUtil.getThisClass(variable); - if (RefactoringChangeUtil.getThisClass(javaRef) != varThisClass) { - initializer = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createExpressionFromText(varThisClass.getName() + ".this", variable); - } - } - - PsiExpression expr = InlineUtil.inlineVariable(variable, initializer, javaRef); - - InlineUtil.tryToInlineArrayCreationForVarargs(expr); - - //Q: move the following code to some util? (addition to inline?) - if (expr instanceof PsiThisExpression) { - if (expr.getParent() instanceof PsiReferenceExpression) { - PsiReferenceExpression refExpr = (PsiReferenceExpression)expr.getParent(); - PsiElement refElement = refExpr.resolve(); - PsiExpression exprCopy = (PsiExpression)refExpr.copy(); - refExpr = (PsiReferenceExpression)refExpr.replace(myFactory.createExpressionFromText(refExpr.getReferenceName(), null)); - if (refElement != null) { - PsiElement newRefElement = refExpr.resolve(); - if (!refElement.equals(newRefElement)) { - // change back - refExpr.replace(exprCopy); - } - } - } - } + initializer = inlineInitializer(variable, initializer, (PsiJavaCodeReferenceElement)ref); } variable.getParent().delete(); } } + private PsiExpression inlineInitializer(PsiVariable variable, PsiExpression initializer, PsiJavaCodeReferenceElement ref) { + if (initializer instanceof PsiThisExpression && ((PsiThisExpression)initializer).getQualifier() == null) { + final PsiClass varThisClass = RefactoringChangeUtil.getThisClass(variable); + if (RefactoringChangeUtil.getThisClass(ref) != varThisClass) { + initializer = JavaPsiFacade.getInstance(myManager.getProject()).getElementFactory().createExpressionFromText(varThisClass.getName() + ".this", variable); + } + } + + PsiExpression expr = InlineUtil.inlineVariable(variable, initializer, ref); + + InlineUtil.tryToInlineArrayCreationForVarargs(expr); + + //Q: move the following code to some util? (addition to inline?) + if (expr instanceof PsiThisExpression) { + if (expr.getParent() instanceof PsiReferenceExpression) { + PsiReferenceExpression refExpr = (PsiReferenceExpression)expr.getParent(); + PsiElement refElement = refExpr.resolve(); + PsiExpression exprCopy = (PsiExpression)refExpr.copy(); + refExpr = (PsiReferenceExpression)refExpr.replace(myFactory.createExpressionFromText(refExpr.getReferenceName(), null)); + if (refElement != null) { + PsiElement newRefElement = refExpr.resolve(); + if (!refElement.equals(newRefElement)) { + // change back + refExpr.replace(exprCopy); + } + } + } + } + return initializer; + } + private boolean canInlineParmOrThisVariable(PsiExpression initializer, boolean shouldBeFinal, boolean strictlyFinal, diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/AbstractInplaceIntroduceFieldPopup.java b/java/java-impl/src/com/intellij/refactoring/introduceField/AbstractInplaceIntroduceFieldPopup.java index 61abe0a11c86..87b285382b1c 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/AbstractInplaceIntroduceFieldPopup.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/AbstractInplaceIntroduceFieldPopup.java @@ -32,7 +32,7 @@ import com.intellij.refactoring.util.occurrences.OccurrenceManager; * User: anna */ public abstract class AbstractInplaceIntroduceFieldPopup extends AbstractJavaInplaceIntroducer { - protected final PsiClass myParentClass; + private final SmartPsiElementPointer myParentClass; protected final OccurrenceManager myOccurrenceManager; private final SmartPsiElementPointer myAnchorElement; @@ -40,7 +40,6 @@ public abstract class AbstractInplaceIntroduceFieldPopup extends AbstractJavaInp private final SmartPsiElementPointer myAnchorElementIfAll; private int myAnchorIdxIfAll = -1; - private final SmartPointerManager mySmartPointerManager; protected RangeMarker myFieldRangeStart; public AbstractInplaceIntroduceFieldPopup(Project project, @@ -55,11 +54,11 @@ public abstract class AbstractInplaceIntroduceFieldPopup extends AbstractJavaInp final OccurrenceManager occurrenceManager, final PsiElement anchorElementIfAll) { super(project, editor, expr, localVariable, occurrences, typeSelectorManager, title); - myParentClass = parentClass; + SmartPointerManager smartPointerManager = SmartPointerManager.getInstance(project); + myParentClass = smartPointerManager.createSmartPsiElementPointer(parentClass); myOccurrenceManager = occurrenceManager; - mySmartPointerManager = SmartPointerManager.getInstance(project); - myAnchorElement = anchorElement != null ? mySmartPointerManager.createSmartPsiElementPointer(anchorElement) : null; - myAnchorElementIfAll = anchorElementIfAll != null ? mySmartPointerManager.createSmartPsiElementPointer(anchorElementIfAll) : null; + myAnchorElement = anchorElement != null ? smartPointerManager.createSmartPsiElementPointer(anchorElement) : null; + myAnchorElementIfAll = anchorElementIfAll != null ? smartPointerManager.createSmartPsiElementPointer(anchorElementIfAll) : null; for (int i = 0, occurrencesLength = occurrences.length; i < occurrencesLength; i++) { PsiExpression occurrence = occurrences[i]; PsiElement parent = occurrence.getParent(); @@ -74,20 +73,20 @@ public abstract class AbstractInplaceIntroduceFieldPopup extends AbstractJavaInp @Override protected PsiElement checkLocalScope() { - return myParentClass; + return getParentClass(); } @Override protected SearchScope getReferencesSearchScope(VirtualFile file) { - return new LocalSearchScope(myParentClass); + return new LocalSearchScope(getParentClass()); } @Override protected boolean startsOnTheSameElement(RefactoringActionHandler handler, PsiElement element) { return super.startsOnTheSameElement(handler, element) || - myParentClass != null && + getParentClass() != null && element instanceof PsiLocalVariable && - myParentClass.findFieldByName(((PsiLocalVariable)element).getName(), false) != null; + getParentClass().findFieldByName(((PsiLocalVariable)element).getName(), false) != null; } protected PsiElement getAnchorElement() { @@ -111,8 +110,9 @@ public abstract class AbstractInplaceIntroduceFieldPopup extends AbstractJavaInp @Override protected PsiVariable getVariable() { if (myFieldRangeStart == null) return null; - if (!myParentClass.isValid()) return null; - PsiElement element = myParentClass.getContainingFile().findElementAt(myFieldRangeStart.getStartOffset()); + PsiClass parentClass = getParentClass(); + if (parentClass == null || !parentClass.isValid()) return null; + PsiElement element = parentClass.getContainingFile().findElementAt(myFieldRangeStart.getStartOffset()); if (element instanceof PsiWhiteSpace) { element = PsiTreeUtil.skipSiblingsForward(element, PsiWhiteSpace.class); } @@ -122,4 +122,8 @@ public abstract class AbstractInplaceIntroduceFieldPopup extends AbstractJavaInp element = PsiTreeUtil.skipSiblingsBackward(element, PsiWhiteSpace.class); return PsiTreeUtil.getParentOfType(element, PsiField.class, false); } + + protected PsiClass getParentClass() { + return myParentClass.getElement(); + } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceConstantPopup.java b/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceConstantPopup.java index 57bfb3dbacc9..1f0f3bb34916 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceConstantPopup.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceConstantPopup.java @@ -122,7 +122,7 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField @NotNull private String getSelectedVisibility() { - if (myParentClass != null && myParentClass.isInterface()) { + if (getParentClass() != null && getParentClass().isInterface()) { return PsiModifier.PUBLIC; } String initialVisibility = JavaRefactoringSettings.getInstance().INTRODUCE_CONSTANT_VISIBILITY; @@ -130,7 +130,7 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField initialVisibility = PsiModifier.PUBLIC; } else { - String effectiveVisibility = IntroduceConstantDialog.getEffectiveVisibility(initialVisibility, myOccurrences, myParentClass, myProject); + String effectiveVisibility = IntroduceConstantDialog.getEffectiveVisibility(initialVisibility, myOccurrences, getParentClass(), myProject); if (effectiveVisibility != null) { return effectiveVisibility; } @@ -146,9 +146,9 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField @Override public PsiField compute() { + PsiClass parentClass = getParentClass(); PsiField field = elementFactory.createFieldFromText( - psiType.getCanonicalText() + " " + (chooseName(names, myParentClass.getLanguage())) + " = " + myInitializerText + ";", - myParentClass); + psiType.getCanonicalText() + " " + (chooseName(names, parentClass.getLanguage())) + " = " + myInitializerText + ";", parentClass); PsiUtil.setModifierProperty(field, PsiModifier.FINAL, true); PsiUtil.setModifierProperty(field, PsiModifier.STATIC, true); myVisibility = getSelectedVisibility(); @@ -156,12 +156,12 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField final PsiElement anchorElementIfAll = getAnchorElementIfAll(); PsiElement finalAnchorElement; for (finalAnchorElement = anchorElementIfAll; - finalAnchorElement != null && finalAnchorElement.getParent() != myParentClass; + finalAnchorElement != null && finalAnchorElement.getParent() != parentClass; finalAnchorElement = finalAnchorElement.getParent()) { } PsiMember anchorMember = finalAnchorElement instanceof PsiMember ? (PsiMember)finalAnchorElement : null; field = BaseExpressionToFieldHandler.ConvertToFieldRunnable - .appendField(myExpr, BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, myParentClass, myParentClass, field, anchorMember); + .appendField(myExpr, BaseExpressionToFieldHandler.InitializationPlace.IN_FIELD_DECLARATION, parentClass, parentClass, field, anchorMember); myFieldRangeStart = myEditor.getDocument().createRangeMarker(field.getTextRange()); return field; } @@ -171,7 +171,7 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField @Override protected String[] suggestNames(PsiType defaultType, String propName) { return IntroduceConstantDialog.createNameSuggestionGenerator(propName, myExpr != null && myExpr.isValid() ? myExpr : null, JavaCodeStyleManager.getInstance(myProject), null, - myParentClass) + getParentClass()) .getSuggestedNameInfo(defaultType).names; } @@ -270,13 +270,13 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField myVisibility, (PsiLocalVariable)getLocalVariable(), getType(), true, - myParentClass, false, false); + getParentClass(), false, false); new WriteCommandAction(myProject, getCommandName(), getCommandName()) { @Override protected void run(@NotNull Result result) throws Throwable { if (getLocalVariable() != null) { final LocalToFieldHandler.IntroduceFieldRunnable fieldRunnable = - new LocalToFieldHandler.IntroduceFieldRunnable(false, (PsiLocalVariable)getLocalVariable(), myParentClass, settings, true, + new LocalToFieldHandler.IntroduceFieldRunnable(false, (PsiLocalVariable)getLocalVariable(), getParentClass(), settings, true, myOccurrences); fieldRunnable.run(); } @@ -284,7 +284,7 @@ public class InplaceIntroduceConstantPopup extends AbstractInplaceIntroduceField final BaseExpressionToFieldHandler.ConvertToFieldRunnable convertToFieldRunnable = new BaseExpressionToFieldHandler.ConvertToFieldRunnable(myExpr, settings, settings.getForcedType(), myOccurrences, myOccurrenceManager, - getAnchorElementIfAll(), getAnchorElement(), myEditor, myParentClass); + getAnchorElementIfAll(), getAnchorElement(), myEditor, getParentClass()); convertToFieldRunnable.run(); } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceFieldPopup.java b/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceFieldPopup.java index d81ede67e71c..f94a7a8e99cb 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceFieldPopup.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceField/InplaceIntroduceFieldPopup.java @@ -87,8 +87,8 @@ public class InplaceIntroduceFieldPopup extends AbstractInplaceIntroduceFieldPop final PsiField field = ApplicationManager.getApplication().runWriteAction(new Computable() { @Override public PsiField compute() { - PsiField field = elementFactory.createField(chooseName(names, myParentClass.getLanguage()), defaultType); - field = (PsiField)myParentClass.add(field); + PsiField field = elementFactory.createField(chooseName(names, getParentClass().getLanguage()), defaultType); + field = (PsiField)getParentClass().add(field); if (myExprText != null) { updateInitializer(elementFactory, field); } @@ -107,7 +107,8 @@ public class InplaceIntroduceFieldPopup extends AbstractInplaceIntroduceFieldPop @Override protected String[] suggestNames(PsiType defaultType, String propName) { - return suggestFieldName(defaultType, (PsiLocalVariable)getLocalVariable(), myExpr != null && myExpr.isValid() ? myExpr : null, myStatic, myParentClass).names; + return suggestFieldName(defaultType, (PsiLocalVariable)getLocalVariable(), myExpr != null && myExpr.isValid() ? myExpr : null, myStatic, + getParentClass()).names; } public static SuggestedNameInfo suggestFieldName(@Nullable PsiType defaultType, @@ -223,13 +224,13 @@ public class InplaceIntroduceFieldPopup extends AbstractInplaceIntroduceFieldPop myIntroduceFieldPanel.getFieldVisibility(), (PsiLocalVariable)getLocalVariable(), forcedType, myIntroduceFieldPanel.isDeleteVariable(), - myParentClass, false, false); + getParentClass(), false, false); new WriteCommandAction(myProject, getCommandName(), getCommandName()){ @Override protected void run(@NotNull Result result) throws Throwable { if (getLocalVariable() != null) { final LocalToFieldHandler.IntroduceFieldRunnable fieldRunnable = - new LocalToFieldHandler.IntroduceFieldRunnable(false, (PsiLocalVariable)getLocalVariable(), myParentClass, settings, myStatic, myOccurrences); + new LocalToFieldHandler.IntroduceFieldRunnable(false, (PsiLocalVariable)getLocalVariable(), getParentClass(), settings, myStatic, myOccurrences); fieldRunnable.run(); } else { @@ -238,7 +239,7 @@ public class InplaceIntroduceFieldPopup extends AbstractInplaceIntroduceFieldPop myOccurrences, myOccurrenceManager, getAnchorElementIfAll(), getAnchorElement(), myEditor, - myParentClass); + getParentClass()); convertToFieldRunnable.run(); } } diff --git a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java index 9cbb79d2cf0d..070cbc98a541 100644 --- a/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/introduceParameter/IntroduceParameterHandler.java @@ -32,6 +32,8 @@ import com.intellij.codeInsight.navigation.NavigationUtil; import com.intellij.ide.util.PsiClassListCellRenderer; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionGuard; import com.intellij.openapi.command.WriteCommandAction; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; @@ -477,9 +479,23 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { Util.analyzeExpression(myExpr, new ArrayList<>(), classMemberRefs, new ArrayList<>()); } + showDialog(method, methodToSearchFor, occurences, replaceAllOccurrences, delegate, initializerType, mustBeFinal, + classMemberRefs, createNameSuggestionGenerator(myExpr, propName, myProject, enteredName)); + } + } + + private void showDialog(PsiMethod method, + PsiMethod methodToSearchFor, + PsiExpression[] occurences, + boolean replaceAllOccurrences, + boolean delegate, + PsiType initializerType, + boolean mustBeFinal, + List classMemberRefs, NameSuggestionsGenerator nameSuggestionGenerator) { + TransactionGuard.getInstance().submitTransactionAndWait(() -> { final IntroduceParameterDialog dialog = new IntroduceParameterDialog(myProject, classMemberRefs, occurences, myLocalVar, myExpr, - createNameSuggestionGenerator(myExpr, propName, myProject, enteredName), + nameSuggestionGenerator, createTypeSelectorManager(occurences, initializerType), methodToSearchFor, method, getParamsToRemove(method, occurences), mustBeFinal); dialog.setReplaceAllOccurrences(replaceAllOccurrences); dialog.setGenerateDelegate(delegate); @@ -489,9 +505,9 @@ public class IntroduceParameterHandler extends IntroduceHandlerBase { myEditor.getSelectionModel().removeSelection(); } }; - SwingUtilities.invokeLater(cleanSelectionRunnable); + ApplicationManager.getApplication().invokeLater(cleanSelectionRunnable, ModalityState.any()); } - } + }); } private TypeSelectorManagerImpl createTypeSelectorManager(PsiExpression[] occurences, PsiType initializerType) { diff --git a/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java b/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java index c894316c377a..9211b9fed73d 100644 --- a/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/memberPullUp/JavaPullUpHandler.java @@ -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. @@ -24,10 +24,10 @@ */ package com.intellij.refactoring.memberPullUp; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; @@ -46,26 +46,42 @@ import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringHierarchyUtil; import com.intellij.refactoring.util.classMembers.MemberInfo; import com.intellij.refactoring.util.classMembers.MemberInfoStorage; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.MultiMap; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; import java.util.List; -public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog.Callback, ElementsHandler { - private static final Logger LOG = Logger.getInstance("#com.intellij.refactoring.memberPullUp.JavaPullUpHandler"); +public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog.Callback, ElementsHandler, ContextAwareActionHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("pull.members.up.title"); + private PsiClass mySubclass; private Project myProject; + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !getElements(editor, file).isEmpty(); + } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); - List elements = new ArrayList<>(); + + List elements = getElements(editor, file); + if (elements.isEmpty()) { + String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP); + } + else { + invoke(project, elements.toArray(PsiElement.EMPTY_ARRAY), dataContext); + } + } + + private static List getElements(Editor editor, PsiFile file) { + List elements = ContainerUtil.newSmartList(); for (Caret caret : editor.getCaretModel().getAllCarets()) { int offset = caret.getOffset(); PsiElement element = file.findElementAt(offset); - while (element != null && !(element instanceof PsiFile)) { if (element instanceof PsiClass || element instanceof PsiField || element instanceof PsiMethod) { elements.add(element); @@ -74,23 +90,13 @@ public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog element = element.getParent(); } } - if (elements.isEmpty()) { - String message = RefactoringBundle - .getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.pull.members.from")); - CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PULL_UP); - } - else { - invoke(project, elements.toArray(PsiElement.EMPTY_ARRAY),dataContext); - } + return elements; } @Override public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) { - myProject = project; - PsiClass aClass = PsiTreeUtil.getParentOfType(PsiTreeUtil.findCommonParent(elements), PsiClass.class, false); - invoke(project, dataContext, aClass, elements); } @@ -103,8 +109,7 @@ public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog return; } - ArrayList bases = RefactoringHierarchyUtil.createBasesList(aClass, false, true); - + List bases = RefactoringHierarchyUtil.createBasesList(aClass, false, true); if (bases.isEmpty()) { final PsiClass containingClass = aClass.getContainingClass(); if (containingClass != null) { @@ -131,31 +136,26 @@ public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog } } - final PullUpDialog dialog = new PullUpDialog(project, aClass, bases, memberInfoStorage, this); - - - dialog.show(); + new PullUpDialog(project, aClass, bases, memberInfoStorage, this).show(); } - - @Override public boolean checkConflicts(final PullUpDialog dialog) { final List infos = dialog.getSelectedMemberInfos(); final MemberInfo[] memberInfos = infos.toArray(new MemberInfo[infos.size()]); final PsiClass superClass = dialog.getSuperClass(); - if (!checkWritable(superClass, memberInfos)) return false; + if (superClass == null || !checkWritable(superClass, memberInfos)) return false; final MultiMap conflicts = new MultiMap<>(); if (!ProgressManager.getInstance().runProcessWithProgressSynchronously(() -> ApplicationManager.getApplication().runReadAction(() -> { final PsiDirectory targetDirectory = superClass.getContainingFile().getContainingDirectory(); final PsiPackage targetPackage = targetDirectory != null ? JavaDirectoryService.getInstance().getPackage(targetDirectory) : null; - conflicts - .putAllValues(PullUpConflictsUtil.checkConflicts(memberInfos, mySubclass, superClass, targetPackage, targetDirectory, dialog.getContainmentVerifier())); + if (targetDirectory != null && targetPackage != null) { + conflicts.putAllValues(PullUpConflictsUtil.checkConflicts(memberInfos, mySubclass, superClass, targetPackage, targetDirectory, dialog.getContainmentVerifier())); + } }), RefactoringBundle.message("detecting.possible.conflicts"), true, myProject)) return false; if (!conflicts.isEmpty()) { ConflictsDialog conflictsDialog = new ConflictsDialog(myProject, conflicts); - conflictsDialog.show(); - final boolean ok = conflictsDialog.isOK(); + boolean ok = conflictsDialog.showAndGet(); if (!ok && conflictsDialog.isShowConflicts()) dialog.close(DialogWrapper.CANCEL_EXIT_CODE); return ok; } @@ -173,20 +173,7 @@ public class JavaPullUpHandler implements RefactoringActionHandler, PullUpDialog @Override public boolean isEnabledOnElements(PsiElement[] elements) { - /* - if (elements.length == 1) { - return elements[0] instanceof PsiClass || elements[0] instanceof PsiField || elements[0] instanceof PsiMethod; - } - else if (elements.length > 1){ - for (int idx = 0; idx < elements.length; idx++) { - PsiElement element = elements[idx]; - if (!(element instanceof PsiField || element instanceof PsiMethod)) return false; - } - return true; - } - return false; - */ // todo: multiple selection etc return elements.length == 1 && elements[0] instanceof PsiClass; } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/memberPushDown/JavaPushDownHandler.java b/java/java-impl/src/com/intellij/refactoring/memberPushDown/JavaPushDownHandler.java index a4de216f7a4b..07002beac599 100644 --- a/java/java-impl/src/com/intellij/refactoring/memberPushDown/JavaPushDownHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/memberPushDown/JavaPushDownHandler.java @@ -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. @@ -15,12 +15,14 @@ */ package com.intellij.refactoring.memberPushDown; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Caret; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ScrollType; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Ref; import com.intellij.psi.*; import com.intellij.psi.impl.source.jsp.jspJava.JspClass; import com.intellij.psi.util.PsiTreeUtil; @@ -40,30 +42,42 @@ import java.util.List; /** * @author dsl */ -public class JavaPushDownHandler implements RefactoringActionHandler, ElementsHandler { +public class JavaPushDownHandler implements RefactoringActionHandler, ElementsHandler, ContextAwareActionHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("push.members.down.title"); + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !getElements(editor, file, Ref.create()).isEmpty(); + } + + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); - ArrayList elements = new ArrayList<>(); - String errorMessage = null; + + Ref errorMessage = Ref.create(); + List elements = getElements(editor, file, errorMessage); + if (elements.isEmpty()) { + String message = + !errorMessage.isNull() ? errorMessage.get() : RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.push.members.from")); + CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PUSH_DOWN); + } + else { + invoke(project, elements.toArray(PsiElement.EMPTY_ARRAY), dataContext); + } + } + + @NotNull + private static List getElements(Editor editor, PsiFile file, Ref errorMessage) { + List elements = new ArrayList<>(); for (Caret caret : editor.getCaretModel().getAllCarets()) { int offset = caret.getOffset(); PsiElement element = file.findElementAt(offset); String errorFromElement = collectElementsUnderCaret(element, elements); if (errorFromElement != null) { - errorMessage = errorFromElement; + errorMessage.set(errorFromElement); } } - - if (elements.isEmpty()) { - String message = errorMessage != null ? errorMessage - : RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("the.caret.should.be.positioned.inside.a.class.to.push.members.from")); - CommonRefactoringUtil.showErrorHint(project, editor, message, REFACTORING_NAME, HelpID.MEMBERS_PUSH_DOWN); - return; - } - - invoke(project, elements.toArray(PsiElement.EMPTY_ARRAY), dataContext); + return elements; } private static String collectElementsUnderCaret(PsiElement element, List elements) { @@ -83,6 +97,7 @@ public class JavaPushDownHandler implements RefactoringActionHandler, ElementsHa } } + @Override public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) { PsiClass aClass = PsiTreeUtil.getParentOfType(PsiTreeUtil.findCommonParent(elements), PsiClass.class, false); if (aClass == null) return; @@ -114,20 +129,8 @@ public class JavaPushDownHandler implements RefactoringActionHandler, ElementsHa dialog.show(); } + @Override public boolean isEnabledOnElements(PsiElement[] elements) { - /* - if (elements.length == 1) { - return elements[0] instanceof PsiClass || elements[0] instanceof PsiField || elements[0] instanceof PsiMethod; - } - else if (elements.length > 1){ - for (int idx = 0; idx < elements.length; idx++) { - PsiElement element = elements[idx]; - if (!(element instanceof PsiField || element instanceof PsiMethod)) return false; - } - return true; - } - return false; - */ // todo: multiple selection etc return elements.length == 1 && elements[0] instanceof PsiClass; } diff --git a/java/java-impl/src/com/intellij/refactoring/rename/RenameJavaModuleProcessor.java b/java/java-impl/src/com/intellij/refactoring/rename/RenameJavaModuleProcessor.java index 92774bebd2fe..b60d68537f05 100644 --- a/java/java-impl/src/com/intellij/refactoring/rename/RenameJavaModuleProcessor.java +++ b/java/java-impl/src/com/intellij/refactoring/rename/RenameJavaModuleProcessor.java @@ -15,13 +15,37 @@ */ package com.intellij.refactoring.rename; +import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiJavaModule; +import com.intellij.psi.impl.java.stubs.index.JavaModuleNameIndex; +import com.intellij.psi.search.ProjectScope; +import com.intellij.refactoring.RefactoringBundle; +import com.intellij.usageView.UsageInfo; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; +import java.util.List; +import java.util.Map; + public class RenameJavaModuleProcessor extends RenamePsiElementProcessor { @Override public boolean canProcessElement(@NotNull PsiElement element) { return element instanceof PsiJavaModule; } + + @Override + public void findCollisions(PsiElement element, String newName, Map allRenames, List result) { + Project project = element.getProject(); + PsiJavaModule existing = ContainerUtil.getFirstItem( + JavaModuleNameIndex.getInstance().get(newName, project, ProjectScope.getProjectScope(project))); + if (existing != null) { + result.add(new UnresolvableCollisionUsageInfo(element, existing) { + @Override + public String getDescription() { + return RefactoringBundle.message("rename.module.already.exists", newName); + } + }); + } + } } \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperDialog.java b/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperDialog.java index b3afdb12ab7f..95712d769d51 100644 --- a/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperDialog.java +++ b/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperDialog.java @@ -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. @@ -39,13 +39,13 @@ import java.util.List; * Date: 06.06.2002 */ public class TurnRefsToSuperDialog extends RefactoringDialog { - @NotNull private final PsiClass mySubClass; - private final List mySuperClasses; + private final PsiClass mySubClass; + private final List mySuperClasses; - private JList mySuperClassesList; + private JList mySuperClassesList; private final JCheckBox myCbReplaceInstanceOf = new JCheckBox(); - TurnRefsToSuperDialog(Project project, @NotNull PsiClass subClass, List superClasses) { + TurnRefsToSuperDialog(Project project, @NotNull PsiClass subClass, List superClasses) { super(project, true); mySubClass = subClass; @@ -57,12 +57,7 @@ public class TurnRefsToSuperDialog extends RefactoringDialog { @Nullable public PsiClass getSuperClass() { - if(mySuperClassesList != null) { - return (PsiClass) mySuperClassesList.getSelectedValue(); - } - else { - return null; - } + return mySuperClassesList != null ? mySuperClassesList.getSelectedValue() : null; } public boolean isUseInInstanceOf() { @@ -77,7 +72,7 @@ public class TurnRefsToSuperDialog extends RefactoringDialog { return mySuperClassesList; } - + @Override protected JComponent createCenterPanel() { JPanel panel = new JPanel(); panel.setLayout(new BorderLayout(UIUtil.DEFAULT_HGAP, UIUtil.DEFAULT_VGAP)); @@ -85,7 +80,7 @@ public class TurnRefsToSuperDialog extends RefactoringDialog { final JLabel classListLabel = new JLabel(); panel.add(classListLabel, BorderLayout.NORTH); - mySuperClassesList = new JBList(mySuperClasses.toArray()); + mySuperClassesList = new JBList<>(mySuperClasses.toArray()); mySuperClassesList.setCellRenderer(new ClassCellRenderer(mySuperClassesList.getCellRenderer())); mySuperClassesList.getSelectionModel().setSelectionMode(ListSelectionModel.SINGLE_SELECTION); classListLabel.setText(RefactoringBundle.message("turnRefsToSuper.change.usages.to", mySubClass.getQualifiedName())); @@ -106,10 +101,12 @@ public class TurnRefsToSuperDialog extends RefactoringDialog { return panel; } + @Override protected String getDimensionServiceKey() { return "#com.intellij.refactoring.turnRefsToSuper.TurnRefsToSuperDialog"; } + @Override protected void doAction() { JavaRefactoringSettings.getInstance().TURN_REFS_TO_SUPER_PREVIEW_USAGES = isPreviewUsages(); final PsiClass superClass = getSuperClass(); @@ -118,7 +115,8 @@ public class TurnRefsToSuperDialog extends RefactoringDialog { } } + @Override protected JComponent createNorthPanel() { return null; } -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperHandler.java b/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperHandler.java index d1678668e745..523f969584c8 100644 --- a/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/turnRefsToSuper/TurnRefsToSuperHandler.java @@ -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. @@ -13,9 +13,9 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.intellij.refactoring.turnRefsToSuper; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; @@ -25,6 +25,7 @@ import com.intellij.psi.PsiAnonymousClass; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.HelpID; import com.intellij.refactoring.RefactoringActionHandler; import com.intellij.refactoring.RefactoringBundle; @@ -32,12 +33,17 @@ import com.intellij.refactoring.util.CommonRefactoringUtil; import com.intellij.refactoring.util.RefactoringHierarchyUtil; import org.jetbrains.annotations.NotNull; -import java.util.ArrayList; +import java.util.List; -public class TurnRefsToSuperHandler implements RefactoringActionHandler { +public class TurnRefsToSuperHandler implements RefactoringActionHandler, ContextAwareActionHandler { public static final String REFACTORING_NAME = RefactoringBundle.message("use.interface.where.possible.title"); + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !PsiUtil.isModuleFile(file); + } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { int offset = editor.getCaretModel().getOffset(); editor.getScrollingModel().scrollToCaret(ScrollType.MAKE_VISIBLE); @@ -56,13 +62,12 @@ public class TurnRefsToSuperHandler implements RefactoringActionHandler { } } + @Override public void invoke(@NotNull final Project project, @NotNull PsiElement[] elements, DataContext dataContext) { if (elements.length != 1) return; - PsiClass subClass = (PsiClass) elements[0]; - - ArrayList basesList = RefactoringHierarchyUtil.createBasesList(subClass, true, true); - + PsiClass subClass = (PsiClass)elements[0]; + List basesList = RefactoringHierarchyUtil.createBasesList(subClass, true, true); if (basesList.isEmpty()) { String message = RefactoringBundle.getCannotRefactorMessage(RefactoringBundle.message("interface.does.not.have.base.interfaces", subClass.getQualifiedName())); Editor editor = CommonDataKeys.EDITOR.getData(dataContext); @@ -72,5 +77,4 @@ public class TurnRefsToSuperHandler implements RefactoringActionHandler { new TurnRefsToSuperDialog(project, subClass, basesList).show(); } - -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/typeCook/TypeCookHandler.java b/java/java-impl/src/com/intellij/refactoring/typeCook/TypeCookHandler.java index 2ebdcb850a55..055d5e399f9c 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeCook/TypeCookHandler.java +++ b/java/java-impl/src/com/intellij/refactoring/typeCook/TypeCookHandler.java @@ -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. @@ -15,25 +15,31 @@ */ package com.intellij.refactoring.typeCook; +import com.intellij.lang.ContextAwareActionHandler; import com.intellij.openapi.actionSystem.DataContext; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; +import com.intellij.psi.util.PsiUtil; import com.intellij.refactoring.RefactoringActionHandler; -import com.intellij.refactoring.util.CommonRefactoringUtil; import org.jetbrains.annotations.NotNull; -public class TypeCookHandler implements RefactoringActionHandler { +public class TypeCookHandler implements RefactoringActionHandler, ContextAwareActionHandler { + @Override + public boolean isAvailableForQuickList(@NotNull Editor editor, @NotNull PsiFile file, @NotNull DataContext dataContext) { + return !PsiUtil.isModuleFile(file); + } + @Override public void invoke(@NotNull Project project, Editor editor, PsiFile file, DataContext dataContext) { invoke(project, new PsiElement[]{file}, dataContext); } + @Override public void invoke(@NotNull Project project, @NotNull PsiElement[] elements, DataContext dataContext) { - if (elements.length == 0) return; - - new TypeCookDialog(project, elements).show(); + if (elements.length != 0) { + new TypeCookDialog(project, elements).show(); + } } - -} +} \ No newline at end of file diff --git a/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java b/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java index 7c4551fe8466..b628abf857ae 100644 --- a/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java +++ b/java/java-impl/src/com/intellij/refactoring/typeMigration/rules/RootTypeConversionRule.java @@ -118,8 +118,10 @@ public class RootTypeConversionRule extends TypeConversionRule { } final PsiType migrationType = methodTypeParamsSubstitutor.substitute(type); - if (!originalType.equals(migrationType) && - !TypeConversionUtil.areTypesAssignmentCompatible(migrationType, actualParams[i])) { + if (!originalType.equals(migrationType) && !areParametersAssignable(migrationType, i, actualParams)) { + if (migrationType instanceof PsiEllipsisType && actualParams.length != migrationParams.length) { + return null; + } labeler.migrateExpressionType(actualParams[i], migrationType, context, false, true); } } @@ -139,6 +141,19 @@ public class RootTypeConversionRule extends TypeConversionRule { return null; } + private static boolean areParametersAssignable(PsiType migrationType, int paramId, PsiExpression[] actualParams) { + if (migrationType instanceof PsiEllipsisType) { + for (int i = paramId; i < actualParams.length; i++) { + if (!TypeConversionUtil.areTypesAssignmentCompatible(migrationType, actualParams[i])) { + return false; + } + } + return true; + } else { + return TypeConversionUtil.areTypesAssignmentCompatible(migrationType, actualParams[paramId]); + } + } + private static class MyStaticMethodConversionDescriptor extends TypeConversionDescriptorBase { private final @NotNull String myTargetClassQName; diff --git a/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java b/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java index 7c1944875894..abd56fb9f8eb 100644 --- a/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java +++ b/java/java-impl/src/com/intellij/refactoring/util/InlineUtil.java @@ -27,7 +27,6 @@ import com.intellij.psi.search.LocalSearchScope; import com.intellij.psi.search.searches.ReferencesSearch; import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.PsiTreeUtil; -import com.intellij.psi.util.PsiTypesUtil; import com.intellij.psi.util.RedundantCastUtil; import com.intellij.refactoring.RefactoringBundle; import com.intellij.util.IncorrectOperationException; @@ -297,6 +296,7 @@ public class InlineUtil { public static TailCallType getTailCallType(@NotNull final PsiReference psiReference) { PsiElement element = psiReference.getElement(); + if (element instanceof PsiMethodReferenceExpression) return TailCallType.None; PsiExpression methodCall = PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression.class); if (methodCall == null) return TailCallType.None; if (methodCall.getParent() instanceof PsiReturnStatement) return TailCallType.Return; diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java b/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java index e6b2507482cd..e0ce1a3ba5fe 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafAnalyzer.java @@ -249,7 +249,7 @@ class SliceLeafAnalyzer { ApplicationManager.getApplication().runReadAction(() -> { final SliceUsage sliceUsage = element.getValue(); - Collection children = element.getChildren(); + Collection children = element.getChildren(); if (children.isEmpty() && sliceUsage instanceof JavaSliceUsage) { PsiElement value = ((JavaSliceUsage)sliceUsage).indexNesting == 0 ? sliceUsage.getElement() : null; if (value != null) { diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java b/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java index 04f64c5bc558..2a4f790b2c71 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafValueClassNode.java @@ -56,7 +56,7 @@ class SliceLeafValueClassNode extends SliceLeafValueRootNode { } @Override - public String toString() { + public String getNodeText() { return myClassName; } } diff --git a/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java b/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java index 52ea3c2db410..2fac95d4a1e7 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java +++ b/java/java-impl/src/com/intellij/slicer/SliceLeafValueRootNode.java @@ -23,7 +23,6 @@ import com.intellij.ui.SimpleTextAttributes; import com.intellij.usageView.UsageViewBundle; import com.intellij.usages.ChunkExtractor; import com.intellij.usages.TextChunk; -import com.intellij.usages.Usage; import com.intellij.usages.UsageInfo2UsageAdapter; import org.jetbrains.annotations.NotNull; @@ -53,22 +52,31 @@ class SliceLeafValueRootNode extends SliceNode implements MyColoredTreeCellRende return myCachedChildren; } + @Override + public List getCachedChildren() { + return myCachedChildren; + } + @Override protected void update(PresentationData presentation) { } @Override public String toString() { - Usage myLeafExpression = getValue(); + return getNodeText(); + } + + public String getNodeText() { + SliceUsage value = getValue(); String text; - if (myLeafExpression instanceof UsageInfo2UsageAdapter) { - PsiElement element = ((UsageInfo2UsageAdapter)myLeafExpression).getUsageInfo().getElement(); + if (value != null) { + PsiElement element = value.getUsageInfo().getElement(); text = element == null ? "" : element.getText(); } else { text = "Other"; } - return "Value: "+ text; + return "Value: " + text; } @Override @@ -80,16 +88,16 @@ class SliceLeafValueRootNode extends SliceNode implements MyColoredTreeCellRende boolean leaf, int row, boolean hasFocus) { - Usage usage = getValue(); + SliceUsage usage = getValue(); renderer.append("Value: ", SimpleTextAttributes.REGULAR_ATTRIBUTES); - if (usage instanceof UsageInfo2UsageAdapter) { - PsiElement element = ((UsageInfo2UsageAdapter)usage).getElement(); + if (usage != null) { + PsiElement element = usage.getElement(); if (element == null) { renderer.append(UsageViewBundle.message("node.invalid") + " ", SliceUsageCellRendererBase.ourInvalidAttributes); } else { - appendElementText((UsageInfo2UsageAdapter)usage, element, renderer); + appendElementText(usage, element, renderer); } } else { diff --git a/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java b/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java index bce9d307d2fd..dcd810b206c7 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java +++ b/java/java-impl/src/com/intellij/slicer/SliceUsageCellRenderer.java @@ -61,14 +61,11 @@ class SliceUsageCellRenderer extends SliceUsageCellRendererBase { } } - if (javaSliceUsage != null) { - for (int i = 0; i < javaSliceUsage.indexNesting; i++) { - append( - " (Tracking container contents" + - (javaSliceUsage.syntheticField.isEmpty() ? "" : " '" + javaSliceUsage.syntheticField + "'") + - ")", - SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); - } + if (javaSliceUsage != null && javaSliceUsage.indexNesting != 0) { + append( + " (Tracking container '" + getContainerName(javaSliceUsage) + + (javaSliceUsage.syntheticField.isEmpty() ? "" : "." + javaSliceUsage.syntheticField) + "' contents)", + SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES); } PsiElement element = sliceUsage.getElement(); @@ -101,5 +98,28 @@ class SliceUsageCellRenderer extends SliceUsageCellRendererBase { } } } + + @NotNull + private static String getContainerName(@NotNull JavaSliceUsage usage) { + String result = ""; + JavaSliceUsage prev = usage; + String name = ""; + while (usage != null) { + if (usage.indexNesting != prev.indexNesting) { + result = name + (result.isEmpty() ? "" : ".") + result; + if (usage.indexNesting == 0) break; + } + PsiElement element = usage.getElement(); + if (element instanceof PsiNamedElement) { + name = ((PsiNamedElement)element).getName(); + } + else if (element instanceof PsiReference) { + name = ((PsiReference)element).getCanonicalText(); + } + prev = usage; + usage = (JavaSliceUsage)usage.getParent(); + } + return result; + } } diff --git a/java/java-impl/src/com/intellij/slicer/SliceUtil.java b/java/java-impl/src/com/intellij/slicer/SliceUtil.java index a1a1785608cd..19dc18baaaf5 100644 --- a/java/java-impl/src/com/intellij/slicer/SliceUtil.java +++ b/java/java-impl/src/com/intellij/slicer/SliceUtil.java @@ -16,6 +16,7 @@ package com.intellij.slicer; import com.intellij.codeInsight.AnnotationUtil; +import com.intellij.codeInspection.dataFlow.DfaPsiUtil; import com.intellij.codeInspection.dataFlow.DfaUtil; import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.progress.ProgressManager; @@ -34,6 +35,7 @@ import com.intellij.psi.tree.IElementType; import com.intellij.psi.util.MethodSignatureUtil; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; +import com.intellij.refactoring.util.RefactoringChangeUtil; import com.intellij.util.ArrayUtilRt; import com.intellij.util.Processor; import gnu.trove.THashMap; @@ -42,10 +44,7 @@ import org.intellij.lang.annotations.Flow; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.Arrays; -import java.util.Collection; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author cdr @@ -117,11 +116,20 @@ class SliceUtil { SliceUsage stopUsage = createTooComplexDFAUsage(expression, parent); return processor.process(stopUsage); } - final Set expressions = new THashSet<>(values); PsiExpression initializer = variable.getInitializer(); - if (initializer != null && expressions.isEmpty()) expressions.add(initializer); + if (values.isEmpty() && initializer != null) { + values = Collections.singletonList(initializer); + } boolean initializerReported = false; - for (PsiExpression exp : expressions) { + // no need to search and report assignments if we are going to report the variable anyway - it would create duplicate nodes if we did + if (values.isEmpty() && !(variable instanceof PsiParameter) && !needToReportDeclaration) { + values = DfaPsiUtil.getVariableAssignmentsInFile(variable, false, variable.getContainingFile().getLastChild()); + initializerReported = !values.isEmpty(); + } + else if (!values.isEmpty() && !(variable instanceof PsiParameter)) { + needToReportDeclaration = false; // already found all values + } + for (PsiExpression exp : values) { if (!handToProcessor(exp, processor, parent, parentSubstitutor, indexNesting, syntheticField)) return false; if (exp == initializer) initializerReported = true; } @@ -237,7 +245,10 @@ class SliceUtil { @NotNull final Processor processor, @NotNull final JavaSliceUsage parent, @NotNull final PsiSubstitutor parentSubstitutor) { - final JavaResolveResult resolved = methodCallExpr.resolveMethodGenerics(); + // if the call looks like 'otherClassObject.methodFromInterface()' + // we can narrow down the overridden methods scan to inheritors of OtherClass only + PsiClass qualifierClass = resolveQualifier(methodCallExpr); + JavaResolveResult resolved = methodCallExpr.resolveMethodGenerics(); PsiElement r = resolved.getElement(); if (r instanceof PsiCompiledElement) { r = r.getNavigationElement(); @@ -250,8 +261,15 @@ class SliceUtil { final PsiType parentType = parentSubstitutor.substitute(methodCallExpr.getType()); final PsiSubstitutor substitutor = resolved.getSubstitutor().putAll(parentSubstitutor); - Collection overrides = - new THashSet<>(OverridingMethodsSearch.search(methodCalled, parent.getScope().toSearchScope(), true).findAll()); + Collection overrides = new THashSet<>(); + OverridingMethodsSearch.search(methodCalled, parent.getScope().toSearchScope(), true).forEach((PsiMethod override) -> { + PsiClass containingClass = override.getContainingClass(); + if (containingClass == null) return true; + if (qualifierClass == null || containingClass.isInheritor(qualifierClass, true)) { + overrides.add(override); + } + return true; + }); overrides.add(methodCalled); final boolean[] result = {true}; @@ -293,6 +311,31 @@ class SliceUtil { return result[0]; } + private static PsiClass resolveQualifier(@NotNull PsiMethodCallExpression expr) { + PsiExpression qualifier = expr.getMethodExpression().getQualifierExpression(); + if (qualifier == null) { + PsiMethodCallExpression copy = (PsiMethodCallExpression)expr.copy(); + PsiReferenceExpression methodExpression = copy.getMethodExpression(); + + PsiThisExpression thisExpression = RefactoringChangeUtil.createThisExpression(expr.getManager(), null); + methodExpression.setQualifierExpression(thisExpression); + qualifier = methodExpression.getQualifierExpression(); + } + if (qualifier != null) { + if (qualifier instanceof PsiReferenceExpression) { + PsiElement resolved = ((PsiReferenceExpression)qualifier).resolve(); + if (resolved instanceof PsiClass) return (PsiClass)resolved; + } + else if (qualifier instanceof PsiThisExpression || qualifier instanceof PsiSuperExpression) { + PsiType type = qualifier.getType(); + if (type instanceof PsiClassType) { + return ((PsiClassType)type).resolve(); + } + } + } + return null; + } + private static boolean processFieldUsages(@NotNull final PsiField field, @NotNull final JavaSliceUsage parent, @NotNull final PsiSubstitutor parentSubstitutor, @@ -376,6 +419,14 @@ class SliceUtil { final int paramSeqNo = ArrayUtilRt.find(actualParameters, parameter); assert paramSeqNo != -1; + // first, check if we are looking for a specific method call. + // it happens when we were processing that very same method() return values somewhere up the tree + PsiCall specificMethodCall = findSpecificMethodCallUpTheTree(parent, method); + if (specificMethodCall != null) { + return processMethodCall(parent, parentSubstitutor, indexNesting, syntheticField, processor, actualParameterType, actualParameters, + paramSeqNo, specificMethodCall); + } + Collection superMethods = new THashSet<>(Arrays.asList(method.findDeepestSuperMethods())); superMethods.add(method); @@ -387,91 +438,9 @@ class SliceUtil { if (!processed.add(reference)) return true; } PsiElement refElement = reference.getElement(); - PsiExpressionList argumentList; - JavaResolveResult result; - if (refElement instanceof PsiCall) { - // the case of enum constant decl - PsiCall call = (PsiCall)refElement; - argumentList = call.getArgumentList(); - result = call.resolveMethodGenerics(); - } - else { - PsiElement element = refElement.getParent(); - if (element instanceof PsiCompiledElement) return true; - if (element instanceof PsiAnonymousClass) { - PsiAnonymousClass anon = (PsiAnonymousClass)element; - argumentList = anon.getArgumentList(); - PsiElement callExp = element.getParent(); - if (!(callExp instanceof PsiCallExpression)) return true; - result = ((PsiCall)callExp).resolveMethodGenerics(); - } - else if (element instanceof PsiCall) { - PsiCall call = (PsiCall)element; - argumentList = call.getArgumentList(); - result = call.resolveMethodGenerics(); - } - else { - return processIfInForeignLanguage(parent, parentSubstitutor, indexNesting, syntheticField, processor, refElement); - } - } - PsiSubstitutor substitutor = result.getSubstitutor(); - - PsiExpression[] expressions = argumentList.getExpressions(); - if (paramSeqNo >= expressions.length) { - return true; - } - PsiElement passExpression; - PsiType actualExpressionType; - if (actualParameterType instanceof PsiEllipsisType) { - passExpression = argumentList; - actualExpressionType = expressions[paramSeqNo].getType(); - } - else { - passExpression = expressions[paramSeqNo]; - actualExpressionType = ((PsiExpression)passExpression).getType(); - } - - Project project = argumentList.getProject(); - PsiElement element = result.getElement(); - if (element instanceof PsiCompiledElement) { - element = element.getNavigationElement(); - } - - // for erased method calls for which we cannot determine target substitutor, - // rely on call argument types. I.e. new Pair(1,2) -> Pair - if (element instanceof PsiTypeParameterListOwner && PsiUtil.isRawSubstitutor((PsiTypeParameterListOwner)element, substitutor)) { - PsiTypeParameter[] typeParameters = substitutor.getSubstitutionMap().keySet().toArray(PsiTypeParameter.EMPTY_ARRAY); - - PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper(); - substitutor = resolveHelper.inferTypeArguments(typeParameters, actualParameters, expressions, parentSubstitutor, argumentList, - DefaultParameterTypeInferencePolicy.INSTANCE); - } - - substitutor = removeRawMappingsLeftFromResolve(substitutor); - - PsiSubstitutor combined = unify(substitutor, parentSubstitutor, project); - if (combined == null) return true; - //PsiType substituted = combined.substitute(passExpression.getType()); - PsiType substituted = combined.substitute(actualExpressionType); - if (substituted instanceof PsiPrimitiveType) { - final PsiClassType boxedType = ((PsiPrimitiveType)substituted).getBoxedType(argumentList); - substituted = boxedType != null ? boxedType : substituted; - } - if (substituted == null) return true; - PsiType typeToCheck; - if (actualParameterType instanceof PsiEllipsisType) { - // there may be the case of passing the vararg argument to the other vararg method: foo(int... ints) { bar(ints); } bar(int... ints) {} - if (TypeConversionUtil.areTypesConvertible(substituted, actualParameterType)) { - return handToProcessor(expressions[paramSeqNo], processor, parent, combined, indexNesting, syntheticField); - } - typeToCheck = ((PsiEllipsisType)actualParameterType).getComponentType(); - } - else { - typeToCheck = actualParameterType; - } - if (!TypeConversionUtil.areTypesConvertible(substituted, typeToCheck)) return true; - - return handToProcessor(passExpression, processor, parent, combined, indexNesting, syntheticField); + return processMethodCall(parent, parentSubstitutor, indexNesting, syntheticField, processor, actualParameterType, actualParameters, + paramSeqNo, + refElement); })) { return false; } @@ -480,6 +449,113 @@ class SliceUtil { return true; } + private static PsiCall findSpecificMethodCallUpTheTree(SliceUsage parent, PsiMethod method) { + while (parent != null) { + PsiElement element = parent.getElement(); + if (element instanceof PsiCall && ((PsiCall)element).resolveMethod() == method) { + return (PsiCall)element; + } + parent = parent.getParent(); + } + return null; + } + + private static boolean processMethodCall(@NotNull SliceUsage parent, + @NotNull PsiSubstitutor parentSubstitutor, + int indexNesting, + @NotNull String syntheticField, + @NotNull Processor processor, + PsiType actualParameterType, + PsiParameter[] actualParameters, + int paramSeqNo, + PsiElement refElement) { + PsiExpressionList argumentList; + JavaResolveResult result; + if (refElement instanceof PsiCall) { + // the case of enum constant decl + PsiCall call = (PsiCall)refElement; + argumentList = call.getArgumentList(); + result = call.resolveMethodGenerics(); + } + else { + PsiElement element = refElement.getParent(); + if (element instanceof PsiCompiledElement) return true; + if (element instanceof PsiAnonymousClass) { + PsiAnonymousClass anon = (PsiAnonymousClass)element; + argumentList = anon.getArgumentList(); + PsiElement callExp = element.getParent(); + if (!(callExp instanceof PsiCallExpression)) return true; + result = ((PsiCall)callExp).resolveMethodGenerics(); + } + else if (element instanceof PsiCall) { + PsiCall call = (PsiCall)element; + argumentList = call.getArgumentList(); + result = call.resolveMethodGenerics(); + } + else { + return processIfInForeignLanguage(parent, parentSubstitutor, indexNesting, syntheticField, processor, refElement); + } + } + PsiSubstitutor substitutor = result.getSubstitutor(); + + PsiExpression[] expressions = argumentList.getExpressions(); + if (paramSeqNo >= expressions.length) { + return true; + } + PsiElement passExpression; + PsiType actualExpressionType; + if (actualParameterType instanceof PsiEllipsisType) { + passExpression = argumentList; + actualExpressionType = expressions[paramSeqNo].getType(); + } + else { + passExpression = expressions[paramSeqNo]; + actualExpressionType = ((PsiExpression)passExpression).getType(); + } + + Project project = argumentList.getProject(); + PsiElement element = result.getElement(); + if (element instanceof PsiCompiledElement) { + element = element.getNavigationElement(); + } + + // for erased method calls for which we cannot determine target substitutor, + // rely on call argument types. I.e. new Pair(1,2) -> Pair + if (element instanceof PsiTypeParameterListOwner && PsiUtil.isRawSubstitutor((PsiTypeParameterListOwner)element, substitutor)) { + PsiTypeParameter[] typeParameters = substitutor.getSubstitutionMap().keySet().toArray(PsiTypeParameter.EMPTY_ARRAY); + + PsiResolveHelper resolveHelper = JavaPsiFacade.getInstance(project).getResolveHelper(); + substitutor = resolveHelper.inferTypeArguments(typeParameters, actualParameters, expressions, parentSubstitutor, argumentList, + DefaultParameterTypeInferencePolicy.INSTANCE); + } + + substitutor = removeRawMappingsLeftFromResolve(substitutor); + + PsiSubstitutor combined = unify(substitutor, parentSubstitutor, project); + if (combined == null) return true; + //PsiType substituted = combined.substitute(passExpression.getType()); + PsiType substituted = combined.substitute(actualExpressionType); + if (substituted instanceof PsiPrimitiveType) { + final PsiClassType boxedType = ((PsiPrimitiveType)substituted).getBoxedType(argumentList); + substituted = boxedType != null ? boxedType : substituted; + } + if (substituted == null) return true; + PsiType typeToCheck; + if (actualParameterType instanceof PsiEllipsisType) { + // there may be the case of passing the vararg argument to the other vararg method: foo(int... ints) { bar(ints); } bar(int... ints) {} + if (TypeConversionUtil.areTypesConvertible(substituted, actualParameterType)) { + return handToProcessor(expressions[paramSeqNo], processor, parent, combined, indexNesting, syntheticField); + } + typeToCheck = ((PsiEllipsisType)actualParameterType).getComponentType(); + } + else { + typeToCheck = actualParameterType; + } + if (!TypeConversionUtil.areTypesConvertible(substituted, typeToCheck)) return true; + + return handToProcessor(passExpression, processor, parent, combined, indexNesting, syntheticField); + } + private static boolean processIfInForeignLanguage(@NotNull SliceUsage parent, @NotNull PsiSubstitutor parentSubstitutor, int indexNesting, diff --git a/java/java-indexing-api/src/com/intellij/psi/search/searches/AnnotatedElementsSearch.java b/java/java-indexing-api/src/com/intellij/psi/search/searches/AnnotatedElementsSearch.java index 9a2d0c3b15cf..23b0349dbf42 100644 --- a/java/java-indexing-api/src/com/intellij/psi/search/searches/AnnotatedElementsSearch.java +++ b/java/java-indexing-api/src/com/intellij/psi/search/searches/AnnotatedElementsSearch.java @@ -54,6 +54,7 @@ public class AnnotatedElementsSearch extends ExtensibleQueryFactory[] getTypes() { return myTypes; } diff --git a/java/java-indexing-impl/src/com/intellij/openapi/roots/impl/ProjectPackageIndexImpl.java b/java/java-indexing-impl/src/com/intellij/openapi/roots/impl/ProjectPackageIndexImpl.java index 601ff499ae60..a42fe07b59c7 100644 --- a/java/java-indexing-impl/src/com/intellij/openapi/roots/impl/ProjectPackageIndexImpl.java +++ b/java/java-indexing-impl/src/com/intellij/openapi/roots/impl/ProjectPackageIndexImpl.java @@ -27,11 +27,13 @@ public class ProjectPackageIndexImpl extends PackageIndex { myDirectoryIndex = directoryIndex; } + @NotNull @Override public VirtualFile[] getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources) { return getDirsByPackageName(packageName, includeLibrarySources).toArray(VirtualFile.EMPTY_ARRAY); } + @NotNull @Override public Query getDirsByPackageName(@NotNull String packageName, boolean includeLibrarySources) { return myDirectoryIndex.getDirectoriesByPackageName(packageName, includeLibrarySources); diff --git a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java index 6af48a42c644..54e226fbdb00 100644 --- a/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java +++ b/java/java-indexing-impl/src/com/intellij/psi/impl/search/JavaFunctionalExpressionSearcher.java @@ -165,7 +165,7 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase toLoad = filterInapplicable(samClasses, vFile, allCandidates.get(vFile)); + List toLoad = filterInapplicable(samClasses, vFile, allCandidates.get(vFile), project); if (!toLoad.isEmpty()) { LOG.trace("To load " + vFile.getPath() + " with values: " + toLoad); if (!processor.process(vFile, ContainerUtil.map(toLoad, it -> it.funExprOffset))) { @@ -186,8 +186,9 @@ public class JavaFunctionalExpressionSearcher extends QueryExecutorBase filterInapplicable(List samClasses, VirtualFile vFile, - Collection occurrences) { - return ReadAction.compute(() -> ContainerUtil.filter(occurrences, it -> it.canHaveType(samClasses, vFile))); + Collection occurrences, Project project) { + return ReadAction.compute(() -> project.isDisposed() ? Collections.emptyList() + : ContainerUtil.filter(occurrences, it -> it.canHaveType(samClasses, vFile))); } private static boolean processFile(@NotNull Processor consumer, diff --git a/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java b/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java index 86ca9fb3d218..ef8845ae0a57 100644 --- a/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java +++ b/java/java-psi-api/src/com/intellij/codeInsight/daemon/impl/analysis/JavaGenericsUtil.java @@ -100,14 +100,21 @@ public class JavaGenericsUtil { } PsiMethod psiMethod = (PsiMethod)resolve; - if (!psiMethod.isVarArgs()) { + PsiParameter[] parameters = psiMethod.getParameterList().getParameters(); + + int parametersCount = parameters.length; + if (parametersCount == 0) { return false; } + PsiParameter varargParameter = parameters[parametersCount - 1]; + if (!varargParameter.isVarArgs()) { + return false; + } + if (AnnotationUtil.isAnnotated(psiMethod, "java.lang.SafeVarargs", false, false)) { return false; } - int parametersCount = psiMethod.getParameterList().getParametersCount(); - PsiParameter varargParameter = psiMethod.getParameterList().getParameters()[parametersCount - 1]; + PsiType componentType = ((PsiEllipsisType)varargParameter.getType()).getComponentType(); if (isReifiableType(resolveResult.getSubstitutor().substitute(componentType))) { return false; diff --git a/java/java-psi-api/src/com/intellij/openapi/roots/PackageIndex.java b/java/java-psi-api/src/com/intellij/openapi/roots/PackageIndex.java index 4fb9f9712e1f..83d166a3eab5 100644 --- a/java/java-psi-api/src/com/intellij/openapi/roots/PackageIndex.java +++ b/java/java-psi-api/src/com/intellij/openapi/roots/PackageIndex.java @@ -37,6 +37,7 @@ public abstract class PackageIndex { * @param includeLibrarySources if true, directories under library sources are included in the returned list. * @return the list of directories. */ + @NotNull public abstract VirtualFile[] getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources); /** @@ -47,5 +48,6 @@ public abstract class PackageIndex { * @param includeLibrarySources if true, directories under library sources are included in the returned list. * @return the query returning the list of directories. */ + @NotNull public abstract Query getDirsByPackageName(@NotNull String packageName, boolean includeLibrarySources); } diff --git a/java/java-psi-api/src/com/intellij/psi/HierarchicalMethodSignature.java b/java/java-psi-api/src/com/intellij/psi/HierarchicalMethodSignature.java index 6430ae0dbff3..ce23392c5a7a 100644 --- a/java/java-psi-api/src/com/intellij/psi/HierarchicalMethodSignature.java +++ b/java/java-psi-api/src/com/intellij/psi/HierarchicalMethodSignature.java @@ -33,6 +33,7 @@ public abstract class HierarchicalMethodSignature extends MethodSignatureBackedB getParameterTypes(signature.getMethod()), signature.getTypeParameters()); } + @NotNull private static PsiType[] getParameterTypes(PsiMethod method) { final PsiParameter[] parameters = method.getParameterList().getParameters(); final PsiType[] paramTypes = PsiType.createArray(parameters.length); diff --git a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java index 95833732fa89..0f9eec926c9b 100644 --- a/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/LambdaUtil.java @@ -554,6 +554,7 @@ public class LambdaUtil { typeByExpression instanceof PsiLambdaParameterType; } + @NotNull public static PsiReturnStatement[] getReturnStatements(PsiLambdaExpression lambdaExpression) { final PsiElement body = lambdaExpression.getBody(); return body instanceof PsiCodeBlock ? PsiUtil.findReturnStatements((PsiCodeBlock)body) : PsiReturnStatement.EMPTY_ARRAY; diff --git a/java/java-psi-api/src/com/intellij/psi/PsiClass.java b/java/java-psi-api/src/com/intellij/psi/PsiClass.java index 017f69a34747..70837c84d15d 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiClass.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiClass.java @@ -116,6 +116,7 @@ public interface PsiClass * * @return the list of interfaces. */ + @NotNull PsiClass[] getInterfaces(); /** @@ -124,7 +125,8 @@ public interface PsiClass * @return the list of classes or interfaces. May return zero elements when jdk is * not configured, so no java.lang.Object is found */ - @NotNull PsiClass[] getSupers(); + @NotNull + PsiClass[] getSupers(); /** * Returns the list of class types for the classes and interfaces extended or diff --git a/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java b/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java index d92fba8c8b3b..e4db84f06360 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiIntersectionType.java @@ -15,12 +15,10 @@ */ package com.intellij.psi; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.search.GlobalSearchScope; import com.intellij.psi.util.PsiUtil; import com.intellij.psi.util.TypeConversionUtil; -import com.intellij.util.Function; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; @@ -50,7 +48,7 @@ public class PsiIntersectionType extends PsiType.Stub { } @NotNull - public static PsiType createIntersection(boolean flatten, PsiType... conjuncts) { + public static PsiType createIntersection(boolean flatten, @NotNull PsiType... conjuncts) { assert conjuncts.length > 0; if (flatten) { conjuncts = flattenAndRemoveDuplicates(conjuncts); @@ -59,7 +57,8 @@ public class PsiIntersectionType extends PsiType.Stub { return new PsiIntersectionType(conjuncts); } - private static PsiType[] flattenAndRemoveDuplicates(final PsiType[] conjuncts) { + @NotNull + private static PsiType[] flattenAndRemoveDuplicates(@NotNull PsiType[] conjuncts) { try { final Set flattenConjuncts = PsiCapturedWildcardType.guard.doPreventingRecursion(conjuncts, true, () -> flatten(conjuncts, ContainerUtil.newLinkedHashSet())); if (flattenConjuncts == null) { diff --git a/java/java-psi-api/src/com/intellij/psi/PsiJavaDocumentedElement.java b/java/java-psi-api/src/com/intellij/psi/PsiJavaDocumentedElement.java index dd7a626b5144..61612a030047 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiJavaDocumentedElement.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiJavaDocumentedElement.java @@ -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. @@ -23,7 +23,7 @@ import org.jetbrains.annotations.Nullable; * * @since 2016.3 */ -public interface PsiJavaDocumentedElement extends PsiElement { +public interface PsiJavaDocumentedElement extends PsiModifierListOwner, NavigatablePsiElement, PsiNameIdentifierOwner { /** * Returns the JavaDoc comment for the element, or {@code null} when the element has no JavaDoc comment. */ diff --git a/java/java-psi-api/src/com/intellij/psi/PsiPackage.java b/java/java-psi-api/src/com/intellij/psi/PsiPackage.java index 22e4a9c12a90..76982b89fbe4 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiPackage.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiPackage.java @@ -90,6 +90,7 @@ public interface PsiPackage extends PsiCheckedRenameElement, NavigationItem, Psi * * @since 14.1 */ + @NotNull PsiFile[] getFiles(@NotNull GlobalSearchScope scope); /** @@ -114,6 +115,7 @@ public interface PsiPackage extends PsiCheckedRenameElement, NavigationItem, Psi * * @return the array of virtual files for the source roots. */ + @NotNull VirtualFile[] occursInPackagePrefixes(); @Override diff --git a/java/java-psi-api/src/com/intellij/psi/PsiTypeParameterList.java b/java/java-psi-api/src/com/intellij/psi/PsiTypeParameterList.java index 6a6b15219ee6..5fb0f6ad6ed2 100644 --- a/java/java-psi-api/src/com/intellij/psi/PsiTypeParameterList.java +++ b/java/java-psi-api/src/com/intellij/psi/PsiTypeParameterList.java @@ -15,6 +15,8 @@ */ package com.intellij.psi; +import org.jetbrains.annotations.NotNull; + /** * Represents a list of generic type parameters for a class or method. * @@ -26,6 +28,7 @@ public interface PsiTypeParameterList extends PsiElement { * * @return the array of type parameters. */ + @NotNull PsiTypeParameter[] getTypeParameters(); /** diff --git a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java index c46d8f8abcb0..aaec31c9d681 100644 --- a/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java +++ b/java/java-psi-api/src/com/intellij/psi/infos/MethodCandidateInfo.java @@ -41,14 +41,14 @@ import java.util.Map; public class MethodCandidateInfo extends CandidateInfo{ public static final RecursionGuard ourOverloadGuard = RecursionManager.createGuard("overload.guard"); public static final ThreadLocal> CURRENT_CANDIDATE = new ThreadLocal<>(); - @ApplicabilityLevelConstant private int myApplicabilityLevel; // benign race - @ApplicabilityLevelConstant private int myPertinentApplicabilityLevel; + @ApplicabilityLevelConstant private volatile int myApplicabilityLevel; + @ApplicabilityLevelConstant private volatile int myPertinentApplicabilityLevel; private final PsiElement myArgumentList; private final PsiType[] myArgumentTypes; private final PsiType[] myTypeArguments; - private PsiSubstitutor myCalcedSubstitutor; // benign race + private PsiSubstitutor myCalcedSubstitutor; - private String myInferenceError; + private volatile String myInferenceError; private final LanguageLevel myLanguageLevel; public MethodCandidateInfo(@NotNull PsiElement candidate, @@ -101,24 +101,27 @@ public class MethodCandidateInfo extends CandidateInfo{ @ApplicabilityLevelConstant public int getApplicabilityLevel() { - if(myApplicabilityLevel == 0){ - myApplicabilityLevel = getApplicabilityLevelInner(); + int result = myApplicabilityLevel; + if (result == 0) { + result = getApplicabilityLevelInner(); + myApplicabilityLevel = result; } - return myApplicabilityLevel; + return result; } @ApplicabilityLevelConstant public int getPertinentApplicabilityLevel() { - if (myPertinentApplicabilityLevel == 0) { - myPertinentApplicabilityLevel = getPertinentApplicabilityLevelInner(); - pullInferenceErrorMessagesFromSubexpressions(); + int result = myPertinentApplicabilityLevel; + if (result == 0) { + myPertinentApplicabilityLevel = result = pullInferenceErrorMessagesFromSubexpressions(getPertinentApplicabilityLevelInner()); } - return myPertinentApplicabilityLevel; + return result; } /** * 15.12.2.2 Identify Matching Arity Methods Applicable by Strict Invocation */ + @ApplicabilityLevelConstant public int getPertinentApplicabilityLevelInner() { if (myArgumentList == null || !PsiUtil.isLanguageLevel8OrHigher(myArgumentList)) { return getApplicabilityLevel(); @@ -433,6 +436,7 @@ public class MethodCandidateInfo extends CandidateInfo{ } } + @Nullable public PsiType[] getArgumentTypes() { return myArgumentTypes; } @@ -474,8 +478,9 @@ public class MethodCandidateInfo extends CandidateInfo{ return errorMessage; } - private void pullInferenceErrorMessagesFromSubexpressions() { - if (myPertinentApplicabilityLevel == ApplicabilityLevel.NOT_APPLICABLE && myArgumentList instanceof PsiExpressionList) { + @ApplicabilityLevelConstant + private int pullInferenceErrorMessagesFromSubexpressions(@ApplicabilityLevelConstant int level) { + if (myArgumentList instanceof PsiExpressionList && level == ApplicabilityLevel.NOT_APPLICABLE) { String errorMessage = null; for (PsiExpression expression : ((PsiExpressionList)myArgumentList).getExpressions()) { final String message = clearErrorMessageInSubexpressions(expression); @@ -487,6 +492,7 @@ public class MethodCandidateInfo extends CandidateInfo{ setInferenceError(errorMessage); } } + return level; } private static String clearErrorMessageInSubexpressions(PsiExpression expression) { diff --git a/java/java-psi-api/src/com/intellij/psi/javadoc/PsiDocTag.java b/java/java-psi-api/src/com/intellij/psi/javadoc/PsiDocTag.java index fc5d2f68363d..3daeb9dbed59 100644 --- a/java/java-psi-api/src/com/intellij/psi/javadoc/PsiDocTag.java +++ b/java/java-psi-api/src/com/intellij/psi/javadoc/PsiDocTag.java @@ -47,6 +47,7 @@ public interface PsiDocTag extends PsiElement, PsiNamedElement { /** * Returns the list of all elements representing the contents of a tag. */ + @NotNull PsiElement[] getDataElements(); /** diff --git a/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java b/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java index cdc464c621e8..c273e6433e35 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/InheritanceUtil.java @@ -15,6 +15,7 @@ */ package com.intellij.psi.util; +import com.intellij.openapi.util.Condition; import com.intellij.psi.*; import com.intellij.util.Processor; import gnu.trove.THashSet; @@ -131,13 +132,20 @@ public class InheritanceUtil { public static boolean hasEnclosingInstanceInScope(PsiClass aClass, PsiElement scope, - final boolean isSuperClassAccepted, + boolean isSuperClassAccepted, + boolean isTypeParamsAccepted) { + return hasEnclosingInstanceInScope(aClass, scope, psiClass -> isSuperClassAccepted, isTypeParamsAccepted); + } + + public static boolean hasEnclosingInstanceInScope(PsiClass aClass, + PsiElement scope, + Condition isSuperClassAccepted, boolean isTypeParamsAccepted) { PsiManager manager = aClass.getManager(); PsiElement place = scope; while (place != null && place != aClass && !(place instanceof PsiFile)) { if (place instanceof PsiClass) { - if (isSuperClassAccepted) { + if (isSuperClassAccepted.value((PsiClass)place)) { if (isInheritorOrSelf((PsiClass)place, aClass, true)) return true; } else { diff --git a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java index aff0a0ed6fbe..f69d38b442a3 100644 --- a/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java +++ b/java/java-psi-api/src/com/intellij/psi/util/PsiTypesUtil.java @@ -334,8 +334,9 @@ public class PsiTypesUtil { return parameterType; } + @NotNull public static PsiTypeParameter[] filterUnusedTypeParameters(final PsiType superReturnTypeInBaseClassType, - final PsiTypeParameter[] typeParameters) { + @NotNull PsiTypeParameter[] typeParameters) { if (typeParameters.length == 0) return typeParameters; final Set usedParameters = new HashSet<>(); diff --git a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java index 35ff6217da95..8e966cb4043f 100644 --- a/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java +++ b/java/java-psi-impl/src/com/intellij/codeInsight/folding/impl/JavaFoldingBuilderBase.java @@ -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. @@ -60,9 +60,17 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem if (element instanceof PsiImportList) { return "..."; } - if (element instanceof PsiMethod || element instanceof PsiClassInitializer || element instanceof PsiClass || - element instanceof PsiLambdaExpression) { - return "{...}"; + if (element instanceof PsiMethod) { + return getCodeBlockPlaceholder(((PsiMethod)element).getBody()); + } + else if (element instanceof PsiClassInitializer) { + return getCodeBlockPlaceholder(((PsiClassInitializer)element).getBody()); + } + else if (element instanceof PsiClass) { + return getCodeBlockPlaceholder(null); + } + else if (element instanceof PsiLambdaExpression) { + return getCodeBlockPlaceholder(((PsiLambdaExpression)element).getBody()); } if (element instanceof PsiDocComment) { return "/**...*/"; @@ -82,6 +90,10 @@ public abstract class JavaFoldingBuilderBase extends CustomFoldingBuilder implem return "..."; } + private static String getCodeBlockPlaceholder(PsiElement codeBlock) { + return codeBlock instanceof PsiCodeBlock && ((PsiCodeBlock)codeBlock).getStatements().length == 0 ? "{}" : "{...}"; + } + private static boolean areOnAdjacentLines(@NotNull PsiElement e1, @NotNull PsiElement e2, @NotNull Document document) { return document.getLineNumber(e1.getTextRange().getEndOffset()) + 1 == document.getLineNumber(e2.getTextRange().getStartOffset()); } diff --git a/java/java-psi-impl/src/com/intellij/core/CorePackageIndex.java b/java/java-psi-impl/src/com/intellij/core/CorePackageIndex.java index ac2e7fd8daea..1215ca58fe03 100644 --- a/java/java-psi-impl/src/com/intellij/core/CorePackageIndex.java +++ b/java/java-psi-impl/src/com/intellij/core/CorePackageIndex.java @@ -49,11 +49,13 @@ public class CorePackageIndex extends PackageIndex { return result; } + @NotNull @Override public VirtualFile[] getDirectoriesByPackageName(@NotNull String packageName, boolean includeLibrarySources) { return getDirsByPackageName(packageName, includeLibrarySources).toArray(VirtualFile.EMPTY_ARRAY); } + @NotNull @Override public Query getDirsByPackageName(@NotNull String packageName, boolean includeLibrarySources) { return new CollectionQuery<>(findDirectoriesByPackageName(packageName)); diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java index b322a971b69a..f007e3c93248 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/FileParser.java @@ -106,9 +106,11 @@ public class FileParser { private static boolean stopImportListParsing(PsiBuilder b) { IElementType type = b.getTokenType(); - String text = b.getTokenText(); if (IMPORT_LIST_STOPPER_SET.contains(type)) return true; - if (type == JavaTokenType.IDENTIFIER && (PsiKeyword.OPEN.equals(text) || PsiKeyword.MODULE.equals(text))) return true; + if (type == JavaTokenType.IDENTIFIER) { + String text = b.getTokenText(); + if (PsiKeyword.OPEN.equals(text) || PsiKeyword.MODULE.equals(text)) return true; + } return false; } diff --git a/java/java-psi-impl/src/com/intellij/lang/java/parser/ModuleParser.java b/java/java-psi-impl/src/com/intellij/lang/java/parser/ModuleParser.java index 873679f9c05c..535db3f68eb6 100644 --- a/java/java-psi-impl/src/com/intellij/lang/java/parser/ModuleParser.java +++ b/java/java-psi-impl/src/com/intellij/lang/java/parser/ModuleParser.java @@ -49,8 +49,8 @@ public class ModuleParser { PsiBuilder.Marker firstAnnotation = myParser.getDeclarationParser().parseAnnotations(builder); IElementType type = builder.getTokenType(); - String text = builder.getTokenText(); - if (type != JavaTokenType.IDENTIFIER || !(PsiKeyword.OPEN.equals(text) || PsiKeyword.MODULE.equals(text))) { + String text = type == JavaTokenType.IDENTIFIER ? builder.getTokenText() : null; + if (!(PsiKeyword.OPEN.equals(text) || PsiKeyword.MODULE.equals(text))) { module.rollbackTo(); return null; } diff --git a/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java b/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java index d6636742ff56..a87ea2230c8d 100644 --- a/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java +++ b/java/java-psi-impl/src/com/intellij/psi/NonClasspathClassFinder.java @@ -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. @@ -58,7 +58,7 @@ public abstract class NonClasspathClassFinder extends PsiElementFinder { myManager = PsiManager.getInstance(myProject); myFileExtensions = ArrayUtil.append(fileExtensions, "class"); final MessageBusConnection connection = project.getMessageBus().connect(project); - connection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener.Adapter() { + connection.subscribe(VirtualFileManager.VFS_CHANGES, new BulkFileListener() { @Override public void after(@NotNull List events) { clearCache(); diff --git a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java index 8eca11108447..761d5f02fe10 100644 --- a/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/PsiDiamondTypeImpl.java @@ -19,7 +19,6 @@ import com.intellij.lang.java.JavaLanguage; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.project.Project; import com.intellij.openapi.util.Comparing; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.codeStyle.JavaCodeStyleManager; import com.intellij.psi.impl.source.resolve.graphInference.InferenceSession; @@ -168,12 +167,7 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { if (staticFactoryCandidateInfo == null) { return DiamondInferenceResult.NULL_RESULT; } - final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, () -> { - PsiSubstitutor substitutor = staticFactoryCandidateInfo.getSubstitutor(); - return staticFactoryCandidateInfo instanceof MethodCandidateInfo && - ((MethodCandidateInfo)staticFactoryCandidateInfo).getInferenceErrorMessage() != null - ? null : substitutor; - }); + final PsiSubstitutor inferredSubstitutor = ourDiamondGuard.doPreventingRecursion(context, false, () -> staticFactoryCandidateInfo.getSubstitutor()); if (inferredSubstitutor == null) { return DiamondInferenceResult.NULL_RESULT; } @@ -182,9 +176,11 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { return DiamondInferenceResult.UNRESOLVED_CONSTRUCTOR; } + final String errorMessage = ((MethodCandidateInfo)staticFactoryCandidateInfo).getInferenceErrorMessage(); + //15.9.3 Choosing the Constructor and its Arguments //The return type and throws clause of cj are the same as the return type and throws clause determined for mj (§15.12.2.6) - if (InferenceSession.wasUncheckedConversionPerformed(context)) { + if (errorMessage == null && InferenceSession.wasUncheckedConversionPerformed(context)) { return DiamondInferenceResult.RAW_RESULT; } @@ -199,9 +195,14 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { final PsiTypeParameter[] classParameters = psiClass.getTypeParameters(); final PsiJavaCodeReferenceElement classOrAnonymousClassReference = newExpression.getClassOrAnonymousClassReference(); LOG.assertTrue(classOrAnonymousClassReference != null); - final DiamondInferenceResult result = new DiamondInferenceResult(classOrAnonymousClassReference.getReferenceName() + "<>"); + final DiamondInferenceResult result = new DiamondInferenceResult(classOrAnonymousClassReference.getReferenceName() + "<>") { + @Override + public String getErrorMessage() { + return errorMessage != null ? DiamondInferenceResult.NULL_RESULT.getErrorMessage() : super.getErrorMessage(); + } + }; - if (PsiUtil.isRawSubstitutor(staticFactory, inferredSubstitutor)) { + if (errorMessage == null && PsiUtil.isRawSubstitutor(staticFactory, inferredSubstitutor)) { //http://www.oracle.com/technetwork/java/javase/8-compatibility-guide-2156366.html#A999198 REF 7144506 if (!PsiUtil.isLanguageLevel8OrHigher(newExpression) && PsiUtil.skipParenthesizedExprUp(newExpression.getParent()) instanceof PsiExpressionList) { for (PsiTypeParameter ignored : parameters) { @@ -408,6 +409,7 @@ public class PsiDiamondTypeImpl extends PsiDiamondType { } } + @NotNull private static PsiTypeParameter[] getAllTypeParams(PsiTypeParameterListOwner listOwner, PsiClass containingClass) { Set params = new LinkedHashSet<>(); Collections.addAll(params, containingClass.getTypeParameters()); diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/CompositeInstructionClientVisitor.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/CompositeInstructionClientVisitor.java index 96d3738d5263..adaeb73a7f42 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/CompositeInstructionClientVisitor.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/CompositeInstructionClientVisitor.java @@ -16,6 +16,8 @@ package com.intellij.psi.controlFlow; +import org.jetbrains.annotations.NotNull; + class CompositeInstructionClientVisitor extends InstructionClientVisitor { private final InstructionClientVisitor[] myVisitors; @@ -24,6 +26,7 @@ class CompositeInstructionClientVisitor extends InstructionClientVisitor writtenVariables = getWrittenVariables(flow, start, end, false); ArrayList array = new ArrayList<>(); @@ -1008,6 +1009,7 @@ public class ControlFlowUtil { return visitor.getResult().booleanValue(); } + @NotNull public static Object[] getAllWorldProblemsAtOnce(final ControlFlow flow) { InstructionClientVisitor[] visitors = { new ReturnPresentClientVisitor(flow), @@ -2044,6 +2046,7 @@ public class ControlFlowUtil { } } + @NotNull int[] getNextOffsets(int offset) { return nextOffsets[offset] != null ? nextOffsets[offset] : ArrayUtil.EMPTY_INT_ARRAY; } diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/DefUseUtil.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/DefUseUtil.java index 31bd527c07e0..15ad46c1fe2f 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/DefUseUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/DefUseUtil.java @@ -454,6 +454,7 @@ public class DefUseUtil { } + @NotNull private static IntArrayList[] getBackwardTraces(final List instructions) { final IntArrayList[] states = new IntArrayList[instructions.size()]; for (int i = 0; i < states.length; i++) { diff --git a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ReturnInstruction.java b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ReturnInstruction.java index 897be1311430..9220c230bded 100644 --- a/java/java-psi-impl/src/com/intellij/psi/controlFlow/ReturnInstruction.java +++ b/java/java-psi-impl/src/com/intellij/psi/controlFlow/ReturnInstruction.java @@ -49,6 +49,7 @@ public class ReturnInstruction extends GoToInstruction { } } + @NotNull public int[] getPossibleReturnOffsets() { return offset == 0 ? new int[]{ diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java index 3178acecb254..af8440a6c12b 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/PsiImplUtil.java @@ -331,6 +331,7 @@ public class PsiImplUtil { } /** @deprecated use {@link AnnotationTargetUtil#getTargetsForLocation(PsiAnnotationOwner)} (to be removed ion IDEA 17) */ + @NotNull public static PsiAnnotation.TargetType[] getTargetsForLocation(@Nullable PsiAnnotationOwner owner) { return AnnotationTargetUtil.getTargetsForLocation(owner); } @@ -560,6 +561,7 @@ public class PsiImplUtil { return null; } + @NotNull public static PsiStatement[] getChildStatements(@NotNull CompositeElement psiCodeBlock) { ApplicationManager.getApplication().assertReadAccessAllowed(); // no lock is needed because all chameleons are expanded already @@ -713,6 +715,7 @@ public class PsiImplUtil { return multiResolveImpl(manager.getProject(), psiFile, element, incompleteCode, resolver); } + @NotNull public static JavaResolveResult[] multiResolveImpl(@NotNull Project project, @NotNull PsiFile psiFile, @NotNull T element, diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java index 2be3ffda22dc..6c0a217520c8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsClassImpl.java @@ -126,6 +126,7 @@ public class ClsClassImpl extends ClsMemberImpl> implements PsiE return PsiClassImplUtil.getSuperClass(this); } + @NotNull @Override public PsiClass[] getInterfaces() { return PsiClassImplUtil.getInterfaces(this); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsDocTagImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsDocTagImpl.java index c49d281c65a4..690ebea39b5e 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsDocTagImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsDocTagImpl.java @@ -100,6 +100,7 @@ class ClsDocTagImpl extends ClsElementImpl implements PsiDocTag { return myNameElement; } + @NotNull @Override public PsiElement[] getDataElements() { return PsiElement.EMPTY_ARRAY; diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterImpl.java index 1a3779eb21ee..b982a6f37edf 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/compiled/ClsTypeParameterImpl.java @@ -247,6 +247,7 @@ public class ClsTypeParameterImpl extends ClsRepositoryPsiElement { Collection result = new ArrayList<>(); Processor processor = Processors.cancelableCollectProcessor(result); - getFacade().processPackageDirectories(PsiPackageImpl.this, allScope(), processor, includeLibrarySources); - return CachedValueProvider.Result - .create(result, PsiPackageImplementationHelper.getInstance().getDirectoryCachedValueDependencies(PsiPackageImpl.this)); + getFacade().processPackageDirectories(this, allScope(), processor, includeLibrarySources); + return CachedValueProvider.Result.create(result, PsiPackageImplementationHelper.getInstance().getDirectoryCachedValueDependencies(this)); }, false); } @@ -101,6 +100,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya PsiPackageImplementationHelper.getInstance().handleQualifiedNameChange(this, newQualifiedName); } + @NotNull @Override public VirtualFile[] occursInPackagePrefixes() { return PsiPackageImplementationHelper.getInstance().occursInPackagePrefixes(this); @@ -111,7 +111,6 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya return (PsiPackageImpl)super.getParentPackage(); } - @Override protected PsiPackageImpl createInstance(PsiManager manager, String qName) { return new PsiPackageImpl(myManager, qName); @@ -125,7 +124,9 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya @Override public boolean isValid() { - return PsiPackageImplementationHelper.getInstance().packagePrefixExists(this) || !getAllDirectories(true).isEmpty(); + return !myManager.getProject().isDisposed() && + (PsiPackageImplementationHelper.getInstance().packagePrefixExists(this) || + !getAllDirectories(true).isEmpty()); } @Override @@ -160,6 +161,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya return getFacade().getClasses(this, scope); } + @NotNull @Override public PsiFile[] getFiles(@NotNull GlobalSearchScope scope) { return getFacade().getPackageFiles(this, scope); @@ -211,6 +213,7 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya return classes; } + @NotNull private PsiClass[] getCachedClassInDumbMode(final String name, GlobalSearchScope scope) { Map> scopeMap = SoftReference.dereference(myDumbModeFullCache); if (scopeMap == null) { @@ -303,11 +306,11 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.CLASS)) { if (providedName != null) { final PsiClass[] classes = findClassByShortName(providedName, scope); - if (!processClasses(processor, state, classes, Conditions.alwaysTrue())) return false; + if (!processClasses(processor, state, classes, Conditions.alwaysTrue())) return false; } else { PsiClass[] classes = getClasses(scope); - if (!processClasses(processor, state, classes, nameCondition != null ? nameCondition : Conditions.alwaysTrue())) return false; + if (!processClasses(processor, state, classes, nameCondition != null ? nameCondition : Conditions.alwaysTrue())) return false; } } if (classHint == null || classHint.shouldProcess(ElementClassHint.DeclarationKind.PACKAGE)) { @@ -400,4 +403,4 @@ public class PsiPackageImpl extends PsiPackageBase implements PsiPackage, Querya public boolean hasModifierProperty(@NonNls @NotNull final String name) { return false; } -} +} \ No newline at end of file diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java index 14946688af06..18f2eb2ce339 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/JavaClassReferenceListElementType.java @@ -85,9 +85,9 @@ public abstract class JavaClassReferenceListElementType extends JavaStubElementT @Override public PsiClassReferenceListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException { int len = dataStream.readVarInt(); - StringRef[] names = StringRef.createArray(len); + String[] names = new String[len]; for (int i = 0; i < names.length; i++) { - names[i] = dataStream.readName(); + names[i] = StringRef.toString(dataStream.readName()); } return new PsiClassReferenceListStubImpl(this, parentStub, names); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java index 7509e086d9e1..3d753ded6aa5 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/java/stubs/impl/PsiClassReferenceListStubImpl.java @@ -23,33 +23,20 @@ import com.intellij.psi.impl.source.PsiClassReferenceType; import com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl; import com.intellij.psi.stubs.StubBase; import com.intellij.psi.stubs.StubElement; -import com.intellij.util.ArrayUtil; import com.intellij.util.IncorrectOperationException; -import com.intellij.util.io.StringRef; +import com.intellij.util.ObjectUtils; import org.jetbrains.annotations.NotNull; /** * @author max */ public class PsiClassReferenceListStubImpl extends StubBase implements PsiClassReferenceListStub { - private final StringRef[] myNames; + private final String[] myNames; private PsiClassType[] myTypes; public PsiClassReferenceListStubImpl(@NotNull JavaClassReferenceListElementType type, StubElement parent, @NotNull String[] names) { - this(type, parent, toStringRefs(names)); - } - - @NotNull - private static StringRef[] toStringRefs(String[] names) { - StringRef[] myNames = StringRef.createArray(names.length); - for (int i = 0; i < names.length; i++) { - myNames[i] = StringRef.fromString(names[i]); - } - return myNames; - } - - public PsiClassReferenceListStubImpl(@NotNull JavaClassReferenceListElementType type, StubElement parent, @NotNull StringRef[] names) { super(parent, type); + ObjectUtils.assertAllElementsNotNull(names); myNames = names; } @@ -68,7 +55,7 @@ public class PsiClassReferenceListStubImpl extends StubBase im final boolean compiled = ((JavaClassReferenceListElementType)getStubType()).isCompiled(this); if (compiled) { for (int i = 0; i < types.length; i++) { - types[i] = new PsiClassReferenceType(new ClsJavaCodeReferenceElementImpl(getPsi(), myNames[i].toString()), null); + types[i] = new PsiClassReferenceType(new ClsJavaCodeReferenceElementImpl(getPsi(), myNames[i]), null); } } else { @@ -78,7 +65,7 @@ public class PsiClassReferenceListStubImpl extends StubBase im final PsiReferenceList psi = getPsi(); for (int i = 0; i < types.length; i++) { try { - final PsiJavaCodeReferenceElement ref = factory.createReferenceFromText(myNames[i].toString(), psi); + final PsiJavaCodeReferenceElement ref = factory.createReferenceFromText(myNames[i], psi); ((PsiJavaCodeReferenceElementImpl)ref).setKindWhenDummy(PsiJavaCodeReferenceElementImpl.CLASS_NAME_KIND); types[i] = factory.createType(ref); } @@ -105,11 +92,7 @@ public class PsiClassReferenceListStubImpl extends StubBase im @NotNull @Override public String[] getReferencedNames() { - String[] names = ArrayUtil.newStringArray(myNames.length); - for (int i = 0; i < names.length; i++) { - names[i] = StringRef.toString(myNames[i]); - } - return names; + return myNames.clone(); } @NotNull diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/AbstractLightClass.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/AbstractLightClass.java index baef0daece76..43decfb9e35c 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/AbstractLightClass.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/AbstractLightClass.java @@ -149,6 +149,7 @@ public abstract class AbstractLightClass extends LightElement implements PsiClas return getDelegate().getSuperClass(); } + @NotNull @Override public PsiClass[] getInterfaces() { return getDelegate().getInterfaces(); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java index a436d8672e11..db05f9a2e037 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightJavaModule.java @@ -19,16 +19,21 @@ import com.intellij.lang.java.JavaLanguage; import com.intellij.navigation.ItemPresentation; import com.intellij.navigation.ItemPresentationProviders; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VfsUtilCore; import com.intellij.openapi.vfs.VirtualFile; +import com.intellij.openapi.vfs.VirtualFileVisitor; import com.intellij.psi.*; import com.intellij.psi.javadoc.PsiDocComment; import com.intellij.psi.util.CachedValueProvider; import com.intellij.psi.util.CachedValuesManager; +import com.intellij.psi.util.PsiUtil; import com.intellij.util.IncorrectOperationException; +import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; +import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -37,6 +42,7 @@ import static com.intellij.util.ObjectUtils.notNull; public class LightJavaModule extends LightElement implements PsiJavaModule { private final LightJavaModuleReferenceElement myRefElement; private final VirtualFile myJarRoot; + private List myExports = null; private LightJavaModule(@NotNull PsiManager manager, @NotNull VirtualFile jarRoot) { super(manager, JavaLanguage.INSTANCE); @@ -64,7 +70,34 @@ public class LightJavaModule extends LightElement implements PsiJavaModule { @NotNull @Override public Iterable getExports() { - return Collections.emptyList(); + if (myExports == null) { + List exports = ContainerUtil.newArrayList(); + + VfsUtilCore.visitChildrenRecursively(myJarRoot, new VirtualFileVisitor() { + private JavaDirectoryService service = JavaDirectoryService.getInstance(); + + @Override + public boolean visitFile(@NotNull VirtualFile file) { + if (file.isDirectory() && !myJarRoot.equals(file)) { + PsiDirectory directory = myManager.findDirectory(file); + if (directory != null) { + PsiPackage pkg = service.getPackage(directory); + if (pkg != null) { + String packageName = pkg.getQualifiedName(); + if (!packageName.isEmpty() && !PsiUtil.isPackageEmpty(new PsiDirectory[]{directory}, packageName)) { + exports.add(new LightPackageAccessibilityStatement(myManager, packageName)); + } + } + } + } + return true; + } + }); + + myExports = exports; + } + + return myExports; } @NotNull @@ -164,6 +197,50 @@ public class LightJavaModule extends LightElement implements PsiJavaModule { } } + private static class LightPackageAccessibilityStatement extends LightElement implements PsiPackageAccessibilityStatement { + private final String myPackageName; + + public LightPackageAccessibilityStatement(@NotNull PsiManager manager, @NotNull String packageName) { + super(manager, JavaLanguage.INSTANCE); + myPackageName = packageName; + } + + @NotNull + @Override + public Role getRole() { + return Role.EXPORTS; + } + + @Nullable + @Override + public PsiJavaCodeReferenceElement getPackageReference() { + return null; + } + + @Nullable + @Override + public String getPackageName() { + return myPackageName; + } + + @NotNull + @Override + public Iterable getModuleReferences() { + return Collections.emptyList(); + } + + @NotNull + @Override + public List getModuleNames() { + return Collections.emptyList(); + } + + @Override + public String toString() { + return "PsiPackageAccessibilityStatement"; + } + } + @NotNull public static LightJavaModule getModule(@NotNull final PsiManager manager, @NotNull final VirtualFile jarRoot) { final PsiDirectory directory = manager.findDirectory(jarRoot); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightModifierList.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightModifierList.java index 4988c6b530ea..b3c2ac00fc91 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightModifierList.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightModifierList.java @@ -135,6 +135,7 @@ public class LightModifierList extends LightElement implements PsiModifierList { return buffer.toString(); } + @NotNull public String[] getModifiers() { return ArrayUtil.toStringArray(myModifiers); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBase.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBase.java index d70471865b90..6e0b5a9bad67 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBase.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightPsiClassBase.java @@ -106,6 +106,7 @@ public abstract class LightPsiClassBase extends LightElement implements PsiClass return PsiClassImplUtil.getSuperClass(this); } + @NotNull @Override public PsiClass[] getInterfaces() { return PsiClassImplUtil.getInterfaces(this); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterListBuilder.java b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterListBuilder.java index aab43cd3c848..f8137d5159cd 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterListBuilder.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/light/LightTypeParameterListBuilder.java @@ -48,6 +48,7 @@ public class LightTypeParameterListBuilder extends LightElement implements PsiTy } } + @NotNull @Override public PsiTypeParameter[] getTypeParameters() { if (cached == null) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java index 0ae6fdb847f4..e002c39843a7 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/ClassInnerStuffCache.java @@ -44,6 +44,7 @@ public class ClassInnerStuffCache { myClass = aClass; } + @NotNull private static T[] copy(T[] value) { return value.length == 0 ? value : value.clone(); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java index 544ee49d8a0e..c631151c96e6 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassImpl.java @@ -218,6 +218,7 @@ public class PsiClassImpl extends JavaStubPsiElement> implements return PsiClassImplUtil.getSuperClass(this); } + @NotNull @Override public PsiClass[] getInterfaces() { return PsiClassImplUtil.getInterfaces(this); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java index 8486761d2396..06dc0c3c6df7 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/PsiClassReferenceType.java @@ -53,6 +53,7 @@ public class PsiClassReferenceType extends PsiClassType.Stub { myReference = reference; } + @NotNull private static PsiAnnotation[] collectAnnotations(PsiJavaCodeReferenceElement reference) { List result = null; for (PsiElement child = reference.getFirstChild(); child != null; child = child.getNextSibling()) { diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java index af92ae85afb9..4622a58f8eb8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocMethodOrFieldRef.java @@ -135,12 +135,14 @@ public class PsiDocMethodOrFieldRef extends CompositePsiElement implements PsiDo return null; } + @NotNull public static PsiVariable[] getAllVariables(PsiElement scope, PsiElement place) { final SmartList result = new SmartList<>(); scope.processDeclarations(new FilterScopeProcessor<>(ElementClassFilter.VARIABLE, result), ResolveState.initial(), null, place); return result.toArray(new PsiVariable[result.size()]); } + @NotNull public static PsiMethod[] getAllMethods(PsiElement scope, PsiElement place) { final SmartList result = new SmartList<>(); scope.processDeclarations(new FilterScopeProcessor<>(ElementClassFilter.METHOD, result), ResolveState.initial(), null, place); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java index 9f2b59ec5538..22e318e29e25 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiDocTagImpl.java @@ -57,6 +57,7 @@ public class PsiDocTagImpl extends CompositePsiElement implements PsiDocTag, Con return (PsiDocTagValue)findPsiChildByType(TAG_VALUE_BIT_SET); } + @NotNull @Override public PsiElement[] getDataElements() { return getChildrenAsPsiElements(VALUE_BIT_SET, PsiElement.ARRAY_FACTORY); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java index 668453c71a70..bc02ff3c5f86 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/javadoc/PsiInlineDocTagImpl.java @@ -59,11 +59,13 @@ public class PsiInlineDocTagImpl extends CompositePsiElement implements PsiInlin return findPsiChildByType(DOC_TAG_NAME); } + @NotNull @Override public PsiElement[] getDataElements() { return getChildrenAsPsiElements(VALUE_BIT_SET, PsiElement.ARRAY_FACTORY); } + @NotNull public PsiElement[] getDataElementsIgnoreWhitespaces() { return getChildrenAsPsiElements(VALUE_NO_WHITESPACE_BIT_SET, PsiElement.ARRAY_FACTORY); } diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/StaticImportResolveProcessor.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/StaticImportResolveProcessor.java index 51eac11d42b8..381d2e2cc3b8 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/StaticImportResolveProcessor.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/resolve/StaticImportResolveProcessor.java @@ -99,6 +99,7 @@ public class StaticImportResolveProcessor extends BaseScopeProcessor implements return super.getHint(hintKey); } + @NotNull public JavaResolveResult[] getResults() { if (myResults.size() + myFieldResults.size() + myClassResult.size() > 1) { filterInvalid(myResults); diff --git a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeParameterImpl.java b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeParameterImpl.java index ca479e39b12e..bdcae30812ba 100644 --- a/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeParameterImpl.java +++ b/java/java-psi-impl/src/com/intellij/psi/impl/source/tree/java/PsiTypeParameterImpl.java @@ -292,6 +292,7 @@ public class PsiTypeParameterImpl extends JavaStubPsiElement { return children; } - static LinkedHashSet getOwnChildren(PsiClass aClass) { + static LinkedHashSet getOwnChildren(@NotNull PsiClass aClass) { LinkedHashSet members = new LinkedHashSet<>(); - addPhysicalElements(aClass.getFields(), members); - addPhysicalElements(aClass.getMethods(), members); - addPhysicalElements(aClass.getInnerClasses(), members); - addPhysicalElements(aClass.getInitializers(), members); + addPhysicalElements(aClass.getFields(), members, aClass); + addPhysicalElements(aClass.getMethods(), members, aClass); + addPhysicalElements(aClass.getInnerClasses(), members, aClass); + addPhysicalElements(aClass.getInitializers(), members, aClass); return members; } - private static void addPhysicalElements(PsiElement[] elements, LinkedHashSet to) { - for (PsiElement element : elements) { + private static void addPhysicalElements(@NotNull PsiMember[] elements, @NotNull Collection to, @NotNull PsiClass aClass) { + for (PsiMember element : elements) { PsiElement mirror = PsiImplUtil.handleMirror(element); - if (!(mirror instanceof LightElement)) { + if (mirror instanceof LightElement) continue; + if (mirror instanceof PsiMember && aClass.equals(((PsiMember)mirror).getContainingClass())) { to.add(mirror); } } diff --git a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaInheritedMembersNodeProvider.java b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaInheritedMembersNodeProvider.java index 262f289a7005..eb75c735c642 100644 --- a/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaInheritedMembersNodeProvider.java +++ b/java/java-structure-view/src/com/intellij/ide/structureView/impl/java/JavaInheritedMembersNodeProvider.java @@ -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. @@ -32,7 +32,9 @@ public class JavaInheritedMembersNodeProvider extends InheritedMembersNodeProvid @Override public Collection provideNodes(@NotNull TreeElement node) { if (node instanceof JavaClassTreeElement) { - final PsiClass aClass = ((JavaClassTreeElement)node).getValue(); + final PsiClass aClass = ((JavaClassTreeElement)node).getElement(); + if (aClass == null) return Collections.emptyList(); + Collection inherited = new LinkedHashSet<>(); Collection ownChildren = JavaClassTreeElement.getOwnChildren(aClass); diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index 973e84156b30..3ce666ee24bd 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -14,7 +14,7 @@ - + diff --git a/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter-out.java b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter-out.java new file mode 100644 index 000000000000..c7a1ec9bed6b --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter-out.java @@ -0,0 +1,8 @@ +import java.util.ArrayList; + +class Test { + + ArrayList foo () { + return new ArrayList<>(); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter.java b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter.java new file mode 100644 index 000000000000..29c836a8d914 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/DiamondCollapsedWithMethodTypeParameter.java @@ -0,0 +1,8 @@ +import java.util.ArrayList; + +class Test { + + ArrayList foo () { + return new Arr + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/InferredObjects.java b/java/java-tests/testData/codeInsight/completion/smartType/InferredObjects.java new file mode 100644 index 000000000000..80b40cd6a154 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/InferredObjects.java @@ -0,0 +1,7 @@ +class M {} +class HM extends M {} +class Foo { + { + M m = new + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs-out.java b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs-out.java new file mode 100644 index 000000000000..9067d7052499 --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs-out.java @@ -0,0 +1,12 @@ + +import java.util.function.Consumer; + +class Test { + private static void _f(Consumer c) {} + + private static void bar(final String s) {} + + public static void main(String[] argArr) { + Test._f(Test::bar); + } +} diff --git a/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs.java b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs.java new file mode 100644 index 000000000000..067b08d7bcba --- /dev/null +++ b/java/java-tests/testData/codeInsight/completion/smartType/StaticMethodReferenceInContextWithTypeArgs.java @@ -0,0 +1,12 @@ + +import java.util.function.Consumer; + +class Test { + private static void _f(Consumer c) {} + + private static void bar(final String s) {} + + public static void main(String[] argArr) { + Test._f(b); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java index 68482c38c8fc..1f54b79676f5 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting/Deprecated.java @@ -16,7 +16,7 @@ class a { * @deprecated */ a(int i,int j,int k) { - new a(k+i+j,dep,notdep); + new a(k+i+j,dep,notdep); } } @@ -33,7 +33,7 @@ class b extends a { * @deprecated */ b(int i) { - super(0,0,i); + super(0,0,i); System.out.print(i); } } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/IDEA111450.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/IDEA111450.java index 9579bf032607..d16f8ffad6fd 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/IDEA111450.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting7/IDEA111450.java @@ -32,7 +32,7 @@ class Test { System.out.println((char) c1 == (char) o); // Although a reference comparison, the cast on the wrapper has a side effect; not redundant. - System.out.println(o == (char) c1); - System.out.println((char) c1 == o); + System.out.println(o == (char) c1); + System.out.println((char) c1 == o); } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/module-info.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/module-info.java new file mode 100644 index 000000000000..05cc64230419 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/advHighlighting9/module-info.java @@ -0,0 +1,5 @@ +/** + * {@linkplain nowhere} + */ +@SuppressWarnings("JavadocReference") +module com.suppress.test.test.test {} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/module-info.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/module-info.java deleted file mode 100644 index 9da527c4bab6..000000000000 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/annotations/module-info.java +++ /dev/null @@ -1 +0,0 @@ -@Deprecated module M.M.M { } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/EnclosingRefInTopLevelClassExtendingInnerWhichExtendsItsOuter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/EnclosingRefInTopLevelClassExtendingInnerWhichExtendsItsOuter.java index 27cfe3cb7f1f..422f47c829d9 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/EnclosingRefInTopLevelClassExtendingInnerWhichExtendsItsOuter.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/genericsHighlighting/EnclosingRefInTopLevelClassExtendingInnerWhichExtendsItsOuter.java @@ -2,4 +2,18 @@ class Outer { class Inner extends Outer {} } -class Impl extends Outer.Inner {} \ No newline at end of file +class Impl extends Outer.Inner {} + +class Impl1 { + class InnerImpl extends Outer.Inner {} +} + +class Impl2 extends Outer { + { + class L extends Outer.Inner {} + } + + class In extends Outer.Inner {} + + static class In1 extends Outer.Inner {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/resolve/pkg/module-info.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/resolve/pkg/module-info.java new file mode 100644 index 000000000000..56c0d3d9f5b2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/javaDoc/resolve/pkg/module-info.java @@ -0,0 +1,7 @@ +/** + * {@linkplain Foo} + * @see Foo + */ +module com.ref.test.test.test { + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/DiamondInsideOverloadedThisReference.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/DiamondInsideOverloadedThisReference.java new file mode 100644 index 000000000000..e4d8d1c3536e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/DiamondInsideOverloadedThisReference.java @@ -0,0 +1,11 @@ + +class A {} +class B extends A {} + +class X { + public X(A b1) { + this(new B<>()); + } + + public X(B b2) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/EraseTypeForNewExpressionWithDiamondsIfUncheckedConversionWasPerformedDuringApplicabilityCheck.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/EraseTypeForNewExpressionWithDiamondsIfUncheckedConversionWasPerformedDuringApplicabilityCheck.java index 169b53cdf0f3..d903ed81b5db 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/EraseTypeForNewExpressionWithDiamondsIfUncheckedConversionWasPerformedDuringApplicabilityCheck.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/diamond/EraseTypeForNewExpressionWithDiamondsIfUncheckedConversionWasPerformedDuringApplicabilityCheck.java @@ -5,10 +5,10 @@ class Test { Result r2 = Result.create(h); Holder dataHolder = null; - Result r3 = new Result<>(new Holder<>(dataHolder)); + Result r3 = new Result<>(new Holder<>(dataHolder)); Result r4 = Result.create(new Holder<>(dataHolder)); - Result r5 = new Result<>(Holder.create(dataHolder)); + Result r5 = new Result<>(Holder.create(dataHolder)); Result r6 = Result.create(Holder.create(dataHolder)); } diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/InferenceFromNotEqualTypeParamsBounds.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/InferenceFromNotEqualTypeParamsBounds.java index b2ff856b4804..1e38cc61e5bd 100644 --- a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/InferenceFromNotEqualTypeParamsBounds.java +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/graphInference/InferenceFromNotEqualTypeParamsBounds.java @@ -29,7 +29,7 @@ public class ConcurrentCollectors { static > C groupingBy(F f, C c, BiConsumer consumer) { - return new CImpl<>(f, consumer, arg(c.getOp())); + return new CImpl<>(f, consumer, arg(c.getOp())); } static > BiOp arg(BiOp op) { diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/StaticMethodInOuterClassConflictWithToString.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/StaticMethodInOuterClassConflictWithToString.java new file mode 100644 index 000000000000..f3268b7b7828 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/lambda/overloadResolution/StaticMethodInOuterClassConflictWithToString.java @@ -0,0 +1,8 @@ +class Foo { + static class Bar { + static final String s = toString(1); + } + static String toString(int x) { + return x + ""; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsLessArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsLessArgs.java new file mode 100644 index 000000000000..4fb1e6f4cde6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsLessArgs.java @@ -0,0 +1,8 @@ +// "Cast parameter to 'long'" "true" +class a { + void f(Long l, String... s) {} + void g() { + f((long) 0); + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsMoreArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsMoreArgs.java new file mode 100644 index 000000000000..bb0e9b4a67a3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsMoreArgs.java @@ -0,0 +1,8 @@ +// "Cast 1st parameter to 'long'" "true" +class a { + void f(Long l, String... s) {} + void g() { + f((long) 0, "", ""); + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsSameArgsLength.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsSameArgsLength.java new file mode 100644 index 000000000000..982e2b413399 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/afterVarargsSameArgsLength.java @@ -0,0 +1,8 @@ +// "Cast 1st parameter to 'long'" "true" +class a { + void f(Long l, String... s) {} + void g() { + f((long) 0, ""); + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsLessArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsLessArgs.java new file mode 100644 index 000000000000..6f73d3692c35 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsLessArgs.java @@ -0,0 +1,8 @@ +// "Cast parameter to 'long'" "true" +class a { + void f(Long l, String... s) {} + void g() { + f(0); + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsMoreArgs.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsMoreArgs.java new file mode 100644 index 000000000000..59e5614c658d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsMoreArgs.java @@ -0,0 +1,8 @@ +// "Cast 1st parameter to 'long'" "true" +class a { + void f(Long l, String... s) {} + void g() { + f(0, "", ""); + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsSameArgsLength.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsSameArgsLength.java new file mode 100644 index 000000000000..94e4d5143aa8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/castMethodParameters15/beforeVarargsSameArgsLength.java @@ -0,0 +1,8 @@ +// "Cast 1st parameter to 'long'" "true" +class a { + void f(Long l, String... s) {} + void g() { + f(0, ""); + } +} + diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/afterAssignmentToFunctionalExpressionInArgumentsList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/afterAssignmentToFunctionalExpressionInArgumentsList.java new file mode 100644 index 000000000000..8145e314de71 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/afterAssignmentToFunctionalExpressionInArgumentsList.java @@ -0,0 +1,10 @@ +// "Create local variable 'lf'" "true" +import java.util.function.Function; + +class Main2 { + void f(Function g) {} + { + Function lf; + f(lf = c -> c); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/beforeAssignmentToFunctionalExpressionInArgumentsList.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/beforeAssignmentToFunctionalExpressionInArgumentsList.java new file mode 100644 index 000000000000..36e822ddf2c8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/createLocalFromUsage/beforeAssignmentToFunctionalExpressionInArgumentsList.java @@ -0,0 +1,9 @@ +// "Create local variable 'lf'" "true" +import java.util.function.Function; + +class Main2 { + void f(Function g) {} + { + f(lf = c -> c); + } +} diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterGetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterGetter.java new file mode 100644 index 000000000000..c833597dc930 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterGetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findGetter(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterGetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterGetter2.java new file mode 100644 index 000000000000..b5a8fe3bcbc4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterGetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findGetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findGetter(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterSetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterSetter.java new file mode 100644 index 000000000000..b97db4b5861b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterSetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findSetter(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterSetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterSetter2.java new file mode 100644 index 000000000000..be6c2aaed23a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterSetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findSetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findSetter(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticGetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticGetter.java new file mode 100644 index 000000000000..750a561b76c4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticGetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticGetter(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticGetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticGetter2.java new file mode 100644 index 000000000000..73af78fbd7fd --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticGetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findStaticGetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticGetter(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticSetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticSetter.java new file mode 100644 index 000000000000..764227123315 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticSetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticSetter(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticSetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticSetter2.java new file mode 100644 index 000000000000..c6444b4738e8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticSetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findStaticSetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticSetter(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticVarHandle.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticVarHandle.java new file mode 100644 index 000000000000..2985d663c3ad --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticVarHandle.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticVarHandle(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticVarHandle2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticVarHandle2.java new file mode 100644 index 000000000000..cb4b4760aa0e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterStaticVarHandle2.java @@ -0,0 +1,13 @@ +// "Replace with 'findStaticVarHandle'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticVarHandle(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterVarHandle.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterVarHandle.java new file mode 100644 index 000000000000..277a2637c003 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterVarHandle.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVarHandle(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterVarHandle2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterVarHandle2.java new file mode 100644 index 000000000000..250df9ac7896 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/afterVarHandle2.java @@ -0,0 +1,13 @@ +// "Replace with 'findVarHandle'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVarHandle(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeGetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeGetter.java new file mode 100644 index 000000000000..2d24a0539a2d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeGetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findGetter(Test.class, "myInt", String.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeGetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeGetter2.java new file mode 100644 index 000000000000..c6cbcccb601b --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeGetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findGetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticGetter(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeSetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeSetter.java new file mode 100644 index 000000000000..965f1f9b9d75 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeSetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findSetter(Test.class, "myInt", String.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeSetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeSetter2.java new file mode 100644 index 000000000000..5238d673f479 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeSetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findSetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticSetter(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticGetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticGetter.java new file mode 100644 index 000000000000..11e354554480 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticGetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticGetter(Test.class, "ourInt", String.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticGetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticGetter2.java new file mode 100644 index 000000000000..de1b5c994248 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticGetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findStaticGetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findGetter(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticSetter.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticSetter.java new file mode 100644 index 000000000000..3d4f99d964e0 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticSetter.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticSetter(Test.class, "ourInt", String.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticSetter2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticSetter2.java new file mode 100644 index 000000000000..e1b3c954f8df --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticSetter2.java @@ -0,0 +1,13 @@ +// "Replace with 'findStaticSetter'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findSetter(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticVarHandle.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticVarHandle.java new file mode 100644 index 000000000000..5f29b5bf2ff2 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticVarHandle.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticVarHandle(Test.class, "ourInt", boolean.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticVarHandle2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticVarHandle2.java new file mode 100644 index 000000000000..88aa9eaaa244 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeStaticVarHandle2.java @@ -0,0 +1,13 @@ +// "Replace with 'findStaticVarHandle'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVarHandle(Test.class, "ourInt", int.class); + } +} + +class Test { + public static int ourInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeVarHandle.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeVarHandle.java new file mode 100644 index 000000000000..c8888c062fa5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeVarHandle.java @@ -0,0 +1,13 @@ +// "Change type to 'int'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVarHandle(Test.class, "myInt", String.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeVarHandle2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeVarHandle2.java new file mode 100644 index 000000000000..aef0c0997077 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle/beforeVarHandle2.java @@ -0,0 +1,13 @@ +// "Replace with 'findVarHandle'" "true" +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStaticVarHandle(Test.class, "myInt", int.class); + } +} + +class Test { + public int myInt; +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/initializeFinalFieldInConstructor/afterIncompleteConstructor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/initializeFinalFieldInConstructor/afterIncompleteConstructor.java new file mode 100644 index 000000000000..c2fd7a905da8 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/initializeFinalFieldInConstructor/afterIncompleteConstructor.java @@ -0,0 +1,8 @@ +// "Initialize in constructor" "true" +class A { + private final int var; + + private Main() { + var = 0; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/initializeFinalFieldInConstructor/beforeIncompleteConstructor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/initializeFinalFieldInConstructor/beforeIncompleteConstructor.java new file mode 100644 index 000000000000..60cc88a0ab56 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/initializeFinalFieldInConstructor/beforeIncompleteConstructor.java @@ -0,0 +1,6 @@ +// "Initialize in constructor" "true" +class A { + private final int var; + + private Main() +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor.java new file mode 100644 index 000000000000..d19b55c74440 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor.java @@ -0,0 +1,13 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class, int.class)); + } +} + +class Test { + public Test() {} + public Test(int a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor2.java new file mode 100644 index 000000000000..3bd11e5f0d22 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor2.java @@ -0,0 +1,12 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class, int.class)); + } +} + +class Test { + public Test(int a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor3.java new file mode 100644 index 000000000000..ac8efa6c9a1d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor3.java @@ -0,0 +1,11 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class)); + } +} + +class Test { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor4.java new file mode 100644 index 000000000000..7b5b98baeeca --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterConstructor4.java @@ -0,0 +1,13 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class)); + } +} + +class Test { + public Test() {} + public Test(int a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod.java new file mode 100644 index 000000000000..598ac944597e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object.class)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod2.java new file mode 100644 index 000000000000..b0b13ffda3ac --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod2.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object.class, Object[].class)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod3.java new file mode 100644 index 000000000000..83cef7e6d46a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod3.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object.class, String.class)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod4.java new file mode 100644 index 000000000000..598ac944597e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterGenericMethod4.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object.class)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod.java new file mode 100644 index 000000000000..343dba266032 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(void.class)); + } +} + +class Test { + public static void method() {} + public static String method(String a) {return a;} + public static String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod2.java new file mode 100644 index 000000000000..cdc39777bfe3 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod2.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(String.class, String.class)); + } +} + +class Test { + public static void method() {} + public static String method(String a) {return a;} + public static String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod3.java new file mode 100644 index 000000000000..b4c746fcf31a --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod3.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(String.class, String.class, String[].class)); + } +} + +class Test { + public static void method() {} + public static String method(String a) {return a;} + public static String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod4.java new file mode 100644 index 000000000000..48cf2c1139bf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterStaticMethod4.java @@ -0,0 +1,12 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(void.class)); + } +} + +class Test { + public static void method() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod.java new file mode 100644 index 000000000000..2f6f048710bf --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVirtual(Test.class, "method", MethodType.methodType(void.class)); + } +} + +class Test { + public void method() {} + public String method(String a) {return a;} + public String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod2.java new file mode 100644 index 000000000000..ce7d30417dc5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod2.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(String.class, String.class)); + } +} + +class Test { + public void method() {} + public String method(String a) {return a;} + public String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod3.java new file mode 100644 index 000000000000..9cfb02ec15f5 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod3.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(String.class, String.class, String[].class)); + } +} + +class Test { + public void method() {} + public String method(String a) {return a;} + public String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod4.java new file mode 100644 index 000000000000..57e44a6388a4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/afterVirtualMethod4.java @@ -0,0 +1,12 @@ +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVirtual(Test.class, "method", MethodType.methodType(void.class)); + } +} + +class Test { + public void method() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor.java new file mode 100644 index 000000000000..fb9dff82c071 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor.java @@ -0,0 +1,13 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class, String.class)); + } +} + +class Test { + public Test() {} + public Test(int a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor2.java new file mode 100644 index 000000000000..b062d83f3cac --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor2.java @@ -0,0 +1,12 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class)); + } +} + +class Test { + public Test(int a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor3.java new file mode 100644 index 000000000000..e5073e8132c1 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor3.java @@ -0,0 +1,11 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class, String.class)); + } +} + +class Test { +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor4.java new file mode 100644 index 000000000000..fb9dff82c071 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeConstructor4.java @@ -0,0 +1,13 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findConstructor(Test.class, MethodType.methodType(void.class, String.class)); + } +} + +class Test { + public Test() {} + public Test(int a) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod.java new file mode 100644 index 000000000000..4a3b990f075d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.genericMethodType(2)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod2.java new file mode 100644 index 000000000000..4a3b990f075d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod2.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.genericMethodType(2)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod3.java new file mode 100644 index 000000000000..4a3b990f075d --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod3.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.genericMethodType(2)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod4.java new file mode 100644 index 000000000000..64d267c5b424 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeGenericMethod4.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.genericMethodType(0, true)); + } +} + +class Test { + public T method(T a) {return null;} + public T method(T a, T... b) {return null;} + public T method(T a, String b) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod.java new file mode 100644 index 000000000000..90779356eaba --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(String.class)); + } +} + +class Test { + public static void method() {} + public static String method(String a) {return a;} + public static String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod2.java new file mode 100644 index 000000000000..d8eb7432ebeb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod2.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(void.class, void.class)); + } +} + +class Test { + public static void method() {} + public static String method(String a) {return a;} + public static String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod3.java new file mode 100644 index 000000000000..90779356eaba --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod3.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findStatic(Test.class, "method", MethodType.methodType(String.class)); + } +} + +class Test { + public static void method() {} + public static String method(String a) {return a;} + public static String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod4.java new file mode 100644 index 000000000000..249470890419 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeStaticMethod4.java @@ -0,0 +1,12 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class)); + } +} + +class Test { + public static void method() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod.java new file mode 100644 index 000000000000..5f5a16a45f31 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findVirtual(Test.class, "method", MethodType.methodType(String.class)); + } +} + +class Test { + public void method() {} + public String method(String a) {return a;} + public String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod2.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod2.java new file mode 100644 index 000000000000..5e8cfc110949 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod2.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, void.class)); + } +} + +class Test { + public void method() {} + public String method(String a) {return a;} + public String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod3.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod3.java new file mode 100644 index 000000000000..609b17d926fe --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod3.java @@ -0,0 +1,14 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + l.findVirtual(Test.class, "method", MethodType.methodType(String.class)); + } +} + +class Test { + public void method() {} + public String method(String a) {return a;} + public String method(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod4.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod4.java new file mode 100644 index 000000000000..2af2740cf9bb --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle/beforeVirtualMethod4.java @@ -0,0 +1,12 @@ +import java.lang.invoke.*; + +public class Main { + void foo() throws Throwable { + MethodHandles.Lookup lookup = MethodHandles.lookup(); + lookup.findStatic(Test.class, "method", MethodType.methodType(void.class)); + } +} + +class Test { + public void method() {} +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/afterDifferentTrueFalseTypes.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/afterDifferentTrueFalseTypes.java new file mode 100644 index 000000000000..2383e93f0d3f --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/afterDifferentTrueFalseTypes.java @@ -0,0 +1,18 @@ +// "Replace Optional.isPresent() condition with functional style expression" "true" + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +public class TestFile { + + public static Collection example() { + final Optional root = Optional.empty(); + return root.>map(TestFile::foo).orElseGet(Collections::emptyList); + } + + private static Set foo(String s) { + return Collections.emptySet(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeDifferentTrueFalseTypes.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeDifferentTrueFalseTypes.java new file mode 100644 index 000000000000..787cb3bf43de --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/optionalIsPresent/beforeDifferentTrueFalseTypes.java @@ -0,0 +1,21 @@ +// "Replace Optional.isPresent() condition with functional style expression" "true" + +import java.util.Collection; +import java.util.Collections; +import java.util.Optional; +import java.util.Set; + +public class TestFile { + + public static Collection example() { + final Optional root = Optional.empty(); + if (root.isPresent()) { + return foo(root.get()); + } + return Collections.emptyList(); + } + + private static Set foo(String s) { + return Collections.emptySet(); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectCast.java new file mode 100644 index 000000000000..44a6f961a6e9 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/afterCollectCast.java @@ -0,0 +1,12 @@ +// "Replace with collect" "true" + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +public class Main { + List getStrings(List list) { + List result = list.stream().filter(o -> o instanceof String).map(o -> (String) o).collect(Collectors.toList()); + return result; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectCast.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectCast.java new file mode 100644 index 000000000000..d68e77473f9e --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/streamApiMigration/beforeCollectCast.java @@ -0,0 +1,18 @@ +// "Replace with collect" "true" + +import java.util.ArrayList; +import java.util.List; + +public class Main { + List getStrings(List list) { + List result = new ArrayList<>(); + for (Object o : list) { + if (o instanceof String) { + String s = (String) o; + result.add(s); + + } + } + return result; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections/module-info.after.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections/module-info.after.java new file mode 100644 index 000000000000..1c0114abf73c --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections/module-info.after.java @@ -0,0 +1,6 @@ +/** + * {@linkplain nothing} + */ +@SuppressWarnings("JavadocReference") module com.suppress.test.test.test { + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections/module-info.java b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections/module-info.java new file mode 100644 index 000000000000..2b0b333ce786 --- /dev/null +++ b/java/java-tests/testData/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections/module-info.java @@ -0,0 +1,6 @@ +/** + * {@linkplain nothing} + */ +module com.suppress.test.test.test { + +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/folding/EmptyMethod.java b/java/java-tests/testData/codeInsight/folding/EmptyMethod.java new file mode 100644 index 000000000000..d62176aa5245 --- /dev/null +++ b/java/java-tests/testData/codeInsight/folding/EmptyMethod.java @@ -0,0 +1,4 @@ +class Test { + void foo() { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/invertIfCondition/afterConflictWithJavaLangClass.java b/java/java-tests/testData/codeInsight/invertIfCondition/afterConflictWithJavaLangClass.java new file mode 100644 index 000000000000..e6b801d124b6 --- /dev/null +++ b/java/java-tests/testData/codeInsight/invertIfCondition/afterConflictWithJavaLangClass.java @@ -0,0 +1,16 @@ +// "Invert 'if' condition" "true" +class Main2 { + private static class Process { } + + public static void main(String[] args) { + List processList = null; + if (processList != null) { + for (Process process : processList) { + System.out.println("process = " + process); + } + } + else { + return; + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/invertIfCondition/beforeConflictWithJavaLangClass.java b/java/java-tests/testData/codeInsight/invertIfCondition/beforeConflictWithJavaLangClass.java new file mode 100644 index 000000000000..b69a64f15fd4 --- /dev/null +++ b/java/java-tests/testData/codeInsight/invertIfCondition/beforeConflictWithJavaLangClass.java @@ -0,0 +1,14 @@ +// "Invert 'if' condition" "true" +class Main2 { + private static class Process { } + + public static void main(String[] args) { + List processList = null; + if (processList == null) { + return; + } + for (Process process : processList) { + System.out.println("process = " + process); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/joinLines/ConvertComment_after.java b/java/java-tests/testData/codeInsight/joinLines/ConvertComment_after.java index 89435b3e02d5..62dcee91d16f 100644 --- a/java/java-tests/testData/codeInsight/joinLines/ConvertComment_after.java +++ b/java/java-tests/testData/codeInsight/joinLines/ConvertComment_after.java @@ -1,6 +1,6 @@ class Foo { void f() { - if (ff) { /*comment*/ return null; + if (ff) { /*comment*/return null; } } } \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/joinLines/JoiningMethodCallWhenItDoesntFit.java b/java/java-tests/testData/codeInsight/joinLines/JoiningMethodCallWhenItDoesntFit.java new file mode 100644 index 000000000000..bedc8848a3cb --- /dev/null +++ b/java/java-tests/testData/codeInsight/joinLines/JoiningMethodCallWhenItDoesntFit.java @@ -0,0 +1,4 @@ +class A { + String s = System + .getProperty("blah"); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/joinLines/JoiningMethodCallWhenItDoesntFit_after.java b/java/java-tests/testData/codeInsight/joinLines/JoiningMethodCallWhenItDoesntFit_after.java new file mode 100644 index 000000000000..84981f268e6b --- /dev/null +++ b/java/java-tests/testData/codeInsight/joinLines/JoiningMethodCallWhenItDoesntFit_after.java @@ -0,0 +1,3 @@ +class A { + String s = System.getProperty("blah"); +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/joinLines/SCR5959_after.java b/java/java-tests/testData/codeInsight/joinLines/SCR5959_after.java index 0b7842ea80b7..6c003c1813a1 100644 --- a/java/java-tests/testData/codeInsight/joinLines/SCR5959_after.java +++ b/java/java-tests/testData/codeInsight/joinLines/SCR5959_after.java @@ -1,3 +1,3 @@ class Test { -} // this line is mandatory +}// this line is mandatory diff --git a/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargs.java b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargs.java new file mode 100644 index 000000000000..90808ffaa7df --- /dev/null +++ b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargs.java @@ -0,0 +1,8 @@ +class Main { + static void of(T t) {} + static void of(T... t) {} + + void foo(String... strs){ + Main.of(strs); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsArray.java b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsArray.java new file mode 100644 index 000000000000..37beb9fd7740 --- /dev/null +++ b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsArray.java @@ -0,0 +1,8 @@ +class Main { + static void of(T t) {} + static void of(T... t) {} + + void foo(String[] strs){ + Main.of(strs); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsMultipleArgs.java b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsMultipleArgs.java new file mode 100644 index 000000000000..f2e5051ffa52 --- /dev/null +++ b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsMultipleArgs.java @@ -0,0 +1,8 @@ +class Main { + static void of(T t) {} + static void of(T... t) {} + + void foo(){ + Main.of("", ""); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsSingleArg.java b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsSingleArg.java new file mode 100644 index 000000000000..a7f228701594 --- /dev/null +++ b/java/java-tests/testData/codeInsight/parameterInfo/OverloadWithVarargsSingleArg.java @@ -0,0 +1,8 @@ +class Main { + static void of(T t) {} + static void of(T... t) {} + + void foo(){ + Main.of(""); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/slice/backward/FinalVarAssignedBeforePassingToAnonymous.java b/java/java-tests/testData/codeInsight/slice/backward/FinalVarAssignedBeforePassingToAnonymous.java new file mode 100644 index 000000000000..e9212dd35437 --- /dev/null +++ b/java/java-tests/testData/codeInsight/slice/backward/FinalVarAssignedBeforePassingToAnonymous.java @@ -0,0 +1,17 @@ +import javax.swing.*; + +class Foo { + void foo(String s) { + } + + void bar() { + final String res; + res = "a"; + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + foo(res); + } + }); + } +} \ No newline at end of file diff --git a/java/java-tests/testData/codeInsight/slice/backward/InterMethod.java b/java/java-tests/testData/codeInsight/slice/backward/InterMethod.java index ff257472a0d0..6144d4d19954 100644 --- a/java/java-tests/testData/codeInsight/slice/backward/InterMethod.java +++ b/java/java-tests/testData/codeInsight/slice/backward/InterMethod.java @@ -1,20 +1,20 @@ class WW { - void f(String ddd) { + void f(String ddd) { if (hashCode() == 0) - ddd = "dd"; - foo(ddd); + ddd = "dd"; + foo(ddd); } { - f("xxx"); + f("xxx"); } { - x("zzz"); + x("zzz"); } - String x(String g) { - String d = foo(g); + String x(String g) { + String d = foo(g); return d; } diff --git a/java/java-tests/testData/codeInsight/slice/backward/LocalVarDeclarationAndAssignment.java b/java/java-tests/testData/codeInsight/slice/backward/LocalVarDeclarationAndAssignment.java new file mode 100644 index 000000000000..caec8e2e7807 --- /dev/null +++ b/java/java-tests/testData/codeInsight/slice/backward/LocalVarDeclarationAndAssignment.java @@ -0,0 +1,11 @@ +class Foo1 { + void foo() { + String s = bar(); + } + + String bar() { + String res; + res = "a"; + return res; + } +} diff --git a/java/java-tests/testData/codeInsight/slice/backward/SearchOverriddenMethodsInThisClassHierarchy.java b/java/java-tests/testData/codeInsight/slice/backward/SearchOverriddenMethodsInThisClassHierarchy.java new file mode 100644 index 000000000000..d8b783560dff --- /dev/null +++ b/java/java-tests/testData/codeInsight/slice/backward/SearchOverriddenMethodsInThisClassHierarchy.java @@ -0,0 +1,21 @@ +interface I { + String getValue(); +} + +abstract class X implements I { + void foo() { + String s = getValue(); + } +} + +class Y implements I { + public String getValue() { + return "Y"; + } +} + +class Impl extends X { + public String getValue() { + return "Impl"; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/build.log b/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/build.log deleted file mode 100644 index 660b752ba345..000000000000 --- a/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/build.log +++ /dev/null @@ -1,6 +0,0 @@ -Cleaning output files: -out/production/ModuleInfoIncluded/Svc.class -End of files -Compiling files: -src/module-info.java -End of files \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/src/Svc.java b/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/src/Svc.java deleted file mode 100644 index f943a73101f2..000000000000 --- a/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/src/Svc.java +++ /dev/null @@ -1 +0,0 @@ -class Svc { } \ No newline at end of file diff --git a/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/src/module-info.java b/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/src/module-info.java deleted file mode 100644 index 911084372cc0..000000000000 --- a/java/java-tests/testData/compileServer/incremental/common/moduleInfoIncluded/src/module-info.java +++ /dev/null @@ -1,2 +0,0 @@ -//todo uncomment after migration to Java 9 -//module M { } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/caseAndNpe/src/CaseAndNpe.java b/java/java-tests/testData/inspection/dataFlow/caseAndNpe/src/CaseAndNpe.java index 91643d5815bb..5da75185ed0c 100644 --- a/java/java-tests/testData/inspection/dataFlow/caseAndNpe/src/CaseAndNpe.java +++ b/java/java-tests/testData/inspection/dataFlow/caseAndNpe/src/CaseAndNpe.java @@ -11,7 +11,7 @@ package test.deadcode; import java.io.IOException; public class aaa { - int a; + Object a; int b; void x(int i, Object o) { diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldsDifferentInstances.java b/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldsDifferentInstances.java index dbe65093702a..29816d582eef 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldsDifferentInstances.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/FinalFieldsDifferentInstances.java @@ -10,10 +10,11 @@ class BrokenAlignment { private static class WatchRequestImpl { private final boolean myToWatchRecursively; - private final String myRootPath = ""; + private final String myRootPath; private WatchRequestImpl(boolean toWatchRecursively) { myToWatchRecursively = toWatchRecursively; + myRootPath = toWatchRecursively ? "one" : "two"; } } diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeAnnotation.java b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeAnnotation.java new file mode 100644 index 000000000000..59d1bc3a54a4 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeAnnotation.java @@ -0,0 +1,17 @@ +import javax.annotation.Nonnegative; +import java.util.Random; + +public class LongRangeAnnotation { + @Nonnegative int method() { + return new Random().nextInt(100); + } + + void testAnnotated() { + int value = method(); + if(value == 0) { + System.out.println("Zero"); + } else if(value > 0) { + System.out.println("Positive"); + } + } +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java new file mode 100644 index 000000000000..b4008421d341 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeBasics.java @@ -0,0 +1,172 @@ +import java.util.List; + +public class LongRangeBasics { + void testSwitch(int i) { + switch (i) { + case 0: + System.out.println("0"); + break; + case 1: + System.out.println("1"); + return; + case 2: + System.out.println("2"); + return; + default: + System.out.println("default"); + break; + } + if(i == 0) { + System.out.println("ouch"); + } + // i > 0 and i < 3 means (i == 1 || i == 2); in both cases we already returned + if(i > 0 && i < 3) { + System.out.println("oops"); + } + } + + void test(int i) { + if(i > 5) { + if(i < 0) { + System.out.println("Hello"); + } + } + } + + void test2(char c) { + int i = c; + if(i > 0x10000) { + System.out.println("Hello"); + } + } + + void test3(String s) { + int i = s.charAt(0); + if(i > 0x10000) { + System.out.println("Hello"); + } + } + + void test4(String s) { + if(s.charAt(0) < 0x10000) { + System.out.println("Hello"); + } + } + + void test1(int i, int j) { + if(i > 0 && j > i) { + // j > i which is > 0 means that j >= 2 + if(j == 1) { + if(i < 0) { + System.out.println("oops"); + } + } + } + } + + void testLength(String s) { + if(s.length() < 2) { + if(s.length() > 4) { + System.out.println("Never"); + } + if(s.length() == 1) { + System.out.println("One"); + } else if(s.length() == 0) { + System.out.println("Empty"); + } + } + if(s.length() < 0) { + System.out.println("Never"); + } + } + + void testArrayLength(int[] arr) { + if (arr.length > 0) { + System.out.println("Ok"); + } else if(arr.length == 0) { + System.out.println("Empty"); + } + if (arr.length < 0) { + System.out.println("Impossible"); + } + } + + static void testTwoLengths(int[] data) { + String s1 = data.length > 0 ? "foo" : null; + String s2 = data.length == 0 ? null : "bar"; + if(s1 == null || s2 == null) { + System.out.println("Test"); + } + } + + + void testWrongMerge(boolean a, boolean b, int c) { + int currentLevel = 0; + if (a) { + currentLevel = c; + } + if(currentLevel > 0 && b || currentLevel < 0) { + + } + } + + void testLength2(String name) { + boolean completeDigits = name.length() > 1; + for (int j = 1; j < name.length(); ++j) { + completeDigits &= Character.isDigit(name.charAt(j)); + if (!completeDigits) break; + } + if (completeDigits) name = "this"; + System.out.println(name); + } + + void testLengthNonFlushed(String s) { + if (s.length() > 10) { + System.out.println(s); + if (s.length() > 5) { + System.out.println(s); + } + } + } + + void getMax(List points, String source) { + int min = Integer.MAX_VALUE; + int nextSelectedIndex = -1; + for (int i = points.size() - 1; i >= 0; i--) { + final int distance = calcDistance(source, points.get(i)); + if (distance < min) { + min = distance; + nextSelectedIndex = i; + } + } + if (min == Integer.MAX_VALUE) { + return; + } + if (nextSelectedIndex == -1) { + System.out.println("Impossible"); + } + System.out.println(nextSelectedIndex); + } + + private int calcDistance(String s1, String s2) { + return s1.length() - s2.length(); + } + + public void testBitwiseAnd() { + int state = getState() & 0xF; + switch (state) { + case 24: + System.out.println("Impossible"); + } + } + + public void testBitwiseAndOk() { + if((getState() & 0x1) == 0x1) { + System.out.println("ok"); + } + } + + private int getState() { + return (int)(Math.random() * 100); + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeKnownMethods.java b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeKnownMethods.java new file mode 100644 index 000000000000..de8b54e5f43a --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeKnownMethods.java @@ -0,0 +1,153 @@ +import java.time.LocalDateTime; +import java.util.List; + +public class LongRangeKnownMethods { + void testIndexOf(String s) { + int idx = s.indexOf("xyz"); + if(idx >= 0) { + System.out.println("Found"); + } else if(idx == -1) { + System.out.println("Not found"); + } + } + + void testLocalDateTime(LocalDateTime ldt) { + if(ldt.getHour() == 24) System.out.println(1); + if(ldt.getMinute() >= 0) System.out.println(2); + if(ldt.getSecond() >= 60) System.out.println(3); + } + + private static int twiceIndexOf(String text, int start, int end) { + int paragraphStart = text.lastIndexOf("\n\n", start); + int paragraphEnd = text.indexOf("\n\n", end); + if (paragraphStart >= paragraphEnd) { + return text.length(); + } + return (paragraphStart >= 0 ? paragraphStart + 2 : 0) + + (paragraphEnd < 0 ? text.length() : paragraphEnd); + } + + void test(String s) { + if (s.isEmpty() && s.length() > 2) { + System.out.println("Never"); + } + } + + void test2(String s) { + if (s.isEmpty() && s.length() == 0) { + System.out.println("Ok"); + } + } + + void test3(String s) { + if (s.startsWith("xyz") && s.length() < 3) { + System.out.println("Impossible"); + } + } + + void testEmpty(String s) { + if (s.isEmpty() && s.startsWith("xyz")) { + System.out.println("Impossible"); + } + } + + void testOk(String s) { + if (s.length() <= 3 && s.endsWith("xyz")) { + System.out.println("Possible"); + } + } + + void testInterfere(String s, String s2) { + if (!s.isEmpty() && s2.startsWith(s)) { + if(s2.isEmpty()) { + System.out.println(); + } + } + } + + void test4() { + String s = "abcd"; + if (s.startsWith("efg")) { + System.out.println("Impossible"); + } + } + + void testEquals(boolean b, boolean c) { + String s1 = b ? "x" : "y"; + String s2 = c ? "x" : "b"; + if(s1.equals(s2) && b) { + System.out.println("B is always true"); + } + } + + void testEqualsIgnoreCase(String s) { + if(s.equalsIgnoreCase("xyz") && s.isEmpty()) { + System.out.println("Never"); + } + } + + void testIndexOfUpperBound(String s) { + int idx = "abcdefgh".indexOf(s); + if(idx > 8) { + System.out.println("Impossible"); + } + } + + void testMax(int x) { + x = Math.max(x, 0); + if (x > -1) { + System.out.println("Always"); + } + if (x > 0) { + System.out.println("Not always"); + } + } + + void testMin(long x, long y) { + if (x < 10 && y > 10) { + y = Long.min(x, y); + if (y > 20) { + System.out.println("Impossible"); + } + } + if (y > 20) { + System.out.println("Possible"); + } + } + + void testMinMax(List rows) { + int start = Integer.MAX_VALUE; + int end = -1; + + for (int i = 0; i < rows.size(); i++) { + String row = rows.get(i); + if (!row.isEmpty()) { + start = Math.min(start, i); + end = Math.max(end, i); + } + } + + if(end >= 0 && start < Integer.MAX_VALUE) { + System.out.println("Ok"); + } + } + + void testAbs(long x, int y) { + x = Math.abs(x); + y = Math.abs(y); + if (x == Long.MIN_VALUE) { + System.out.println("possible"); + } + if (x == Long.MIN_VALUE + 1) { + System.out.println("impossible"); + } + if (x == Integer.MIN_VALUE) { + System.out.println("impossible"); + } + if (y == Integer.MIN_VALUE) { + System.out.println("possible"); + } else if(y < 0) { + System.out.println("impossible"); + } + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeLoop.java b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeLoop.java new file mode 100644 index 000000000000..0cadb272e057 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/LongRangeLoop.java @@ -0,0 +1,36 @@ +import java.util.Collection; + +public class LongRangeLoop { + public static int min(int[] values) { + int min = Integer.MAX_VALUE; + for (int value : values) { + if (value < min) min = value; + } + if (min > 0 && min < -10) { + System.out.println("Invalid result"); + } + return min; + } + + public static void boundedForLoop() { + for(int i=0; i<10; i++) { + if(i == 20) { + System.out.println("Oops"); + } + } + } + + public void testVariablesAreInDeclarationOrder(String file, Collection vars) throws Exception { + int previousIndex = -1; + for (String each : vars) { + int index = file.indexOf(each); + if(index <= previousIndex) { + throw new AssertionError(); + } + previousIndex = index; + if(index == -1) { + System.out.println("Impossible"); + } + } + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ManyDistinctPairsNotComplex.java b/java/java-tests/testData/inspection/dataFlow/fixture/ManyDistinctPairsNotComplex.java new file mode 100644 index 000000000000..857ea1538de1 --- /dev/null +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ManyDistinctPairsNotComplex.java @@ -0,0 +1,31 @@ +public class ManyDistinctPairsNotComplex { + enum SquareType { + BLACKROOK, BLACKKNIGHT, BLACKBISHOP, BLACKKING, BLACKQUEEN, BLACKPAWN + } + + private SquareType[][] squares; + private int WIDTH; + private int HEIGHT; + + public void blackStartPos() { + for (int row = 0; row < HEIGHT; row++) { + for (int col = 0; col < WIDTH; col++) { + if (row == 6) { + squares[row][col] = SquareType.BLACKPAWN; + } else if (row == 7 && (col == 0 || col == 7)) { + squares[row][col] = SquareType.BLACKROOK; + } else if (row == 7 && (col == 1 || col == 6)) { + squares[row][col] = SquareType.BLACKKNIGHT; + } else if (row == 7 && (col == 2 || col == 5)) { + squares[row][col] = SquareType.BLACKBISHOP; + } else if (row == 7 && col == 3) { + squares[row][col] = SquareType.BLACKKING; + } else if (row == 7 && col == 4) { + squares[row][col] = SquareType.BLACKQUEEN; + } else if (row == 7 && col == 2) { + System.out.println("Impossible"); + } + } + } + } +} diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/OptionalGetWithoutIsPresent.java b/java/java-tests/testData/inspection/dataFlow/fixture/OptionalGetWithoutIsPresent.java index b57953ada07b..9642644ebcf3 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/OptionalGetWithoutIsPresent.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/OptionalGetWithoutIsPresent.java @@ -82,7 +82,7 @@ class OptionalWithoutIsPresent { } if (maybe.isPresent()) { maybe = Optional.empty(); - System.out.println(maybe.get()); + System.out.println(maybe.get()); } boolean b = ((maybe.isPresent())) && maybe.get() == 1; boolean c = (!maybe.isPresent()) || maybe.get() == 1; @@ -118,7 +118,7 @@ class OptionalWithoutIsPresent { boolean absent = !present; boolean otherAbsent = !!absent; if(otherAbsent) { - System.out.println(opt.get()); + System.out.println(opt.get()); } else { System.out.println(opt.get()); } @@ -151,7 +151,7 @@ class OptionalWithoutIsPresent { o2 = getOptional(); org.junit.Assert.assertTrue(!o2.isPresent()); - System.out.println(o2.get()); + System.out.println(o2.get()); } private void checkAsserts2() { @@ -175,12 +175,12 @@ class OptionalWithoutIsPresent { test = Optional.empty(); } System.out.println(test.get()); - if(b) { + if(b) { test = Optional.empty(); } else { test = Optional.empty(); } - System.out.println(test.get()); + System.out.println(test.get()); } @@ -228,7 +228,7 @@ class OptionalWithoutIsPresent { void order(Optional order, boolean b) { order.ifPresent(o -> System.out.println(order.get())); - System.out.println(order.orElseGet(() -> order.get().trim())); + System.out.println(order.orElseGet(() -> order.get().trim())); } public static void two(Optional o1,Optional o2) { @@ -318,7 +318,7 @@ class OptionalWithoutIsPresent { void guavaTest(com.google.common.base.Optional opt, String s) { System.out.println(opt.get()); - if(opt.isPresent()) { + if(opt.isPresent()) { System.out.println(opt.get()); } opt = com.google.common.base.Optional.fromNullable(s); @@ -334,4 +334,33 @@ class OptionalWithoutIsPresent { System.out.println(opt.get()); } } + + void testThrow(Optional test) { + test.orElseThrow(RuntimeException::new); + Object o = test.get(); + System.out.println(o); + } + + void testThrow2(Optional test) { + test.orElseThrow(RuntimeException::new); + if (test.isPresent()) { + System.out.println("Yes"); + } + } + + void testThrowCatch(Optional opt) { + try { + opt.orElseThrow(RuntimeException::new); + System.out.println("Ok: " + opt.get()); + } catch (RuntimeException ex) { + System.out.println("Fail: " + opt.get()); + } + } + + public void testThrowFail(Optional arg) { + if(!arg.isPresent()) { + System.out.println(arg.orElseThrow(IllegalAccessError::new)); + } + String res = Optional.empty().orElseThrow(RuntimeException::new); + } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/dataFlow/fixture/ThrowNullable.java b/java/java-tests/testData/inspection/dataFlow/fixture/ThrowNullable.java index 4c9c3986c834..568b193ad217 100644 --- a/java/java-tests/testData/inspection/dataFlow/fixture/ThrowNullable.java +++ b/java/java-tests/testData/inspection/dataFlow/fixture/ThrowNullable.java @@ -15,4 +15,8 @@ class DataFlowBug { } } + void foo(RuntimeException tt) { + throw tt; + } + } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java b/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java new file mode 100644 index 000000000000..404a11c958a9 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/Constructor.java @@ -0,0 +1,43 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + Class c = Test.class; + l.findConstructor(Test.class, MethodType.methodType(void.class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, int.class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, int.class, String.class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, int.class, String[].class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, String[][].class)); + l.findConstructor((c), MethodType.methodType(void.class)); + + l.findConstructor(Test.class, MethodType.methodType(int.class)); + l.findConstructor(Test.class, MethodType.methodType(Test.class)); + l.findConstructor(Test.class, MethodType.methodType(Test.class, int.class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, String.class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, int.class, String[][].class)); + l.findConstructor(Test.class, MethodType.methodType(void.class, String[].class)); + + l.findConstructor(WithDefault.class, MethodType.methodType(void.class)); + l.findConstructor(Class.forName("WithDefault"), MethodType.methodType(void.class)); + l.findConstructor(NoDefault.class, MethodType.methodType(void.class)); + l.findConstructor(Class.forName("NoDefault"), MethodType.methodType(void.class)); + + } +} + +class Test { + public Test() {} + public Test(int a) {} + public Test(int a, String b) {} + public Test(int a, String... b) {} + public Test(String[]... b) {} +} + +class WithDefault { +} + +class NoDefault { + public NoDefault(int n) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/GenericMethod.java b/java/java-tests/testData/inspection/invokeHandleSignature/GenericMethod.java new file mode 100644 index 000000000000..c8492b208a28 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/GenericMethod.java @@ -0,0 +1,25 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findVirtual(Test.class, "method1", MethodType.genericMethodType(2)); + l.findVirtual(Test.class, "method1", MethodType.genericMethodType(2, false)); + l.findVirtual(Test.class, "method1", MethodType.genericMethodType(2, true)); + l.findVirtual(Test.class, "method1", MethodType.genericMethodType(1)); + l.findVirtual(Test.class, "method1", MethodType.genericMethodType(3)); + + l.findVirtual(Test.class, "method2", MethodType.genericMethodType(2, true)); + l.findVirtual(Test.class, "method2", MethodType.genericMethodType(2, false)); + l.findVirtual(Test.class, "method2", MethodType.genericMethodType(1)); + l.findVirtual(Test.class, "method2", MethodType.genericMethodType(1, true)); + l.findVirtual(Test.class, "method2", MethodType.genericMethodType(2)); + l.findVirtual(Test.class, "method2", MethodType.genericMethodType(3)); + } +} + +class Test { + public T method1(T a, T b) {return null;} + public T method2(T a, T b, T... c) {return null;} +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/Getter.java b/java/java-tests/testData/inspection/invokeHandleSignature/Getter.java new file mode 100644 index 000000000000..56e4a8a9dda7 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/Getter.java @@ -0,0 +1,47 @@ +import java.lang.invoke.*; +import java.util.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findGetter(Test.class, "myInt", int.class); + l.findGetter(Test.class, "myInts", int[].class); + l.findGetter(Test.class, "myList", List.class); + l.findGetter(Test.class, "myLists", List[].class); + l.findGetter(Test.class, "myString", String.class); + + l.findStaticGetter(Test.class, "ourInt", int.class); + l.findStaticGetter(Test.class, "ourInts", int[].class); + l.findStaticGetter(Test.class, "ourList", List.class); + l.findStaticGetter(Test.class, "ourLists", List[].class); + l.findStaticGetter(Test.class, "ourString", String.class); + + l.findGetter(Test.class, "doesntExist", String.class); + l.findStaticGetter(Test.class, "doesntExist", String.class); + + l.findGetter(Test.class, "myInt", void.class); + l.findGetter(Test.class, "myInts", int.class); + l.findGetter(Test.class, "myString", List.class); + + l.findStaticGetter(Test.class, "ourInt", void.class); + l.findStaticGetter(Test.class, "ourInts", int.class); + l.findStaticGetter(Test.class, "ourString", List.class); + } +} + +class Test { + public int myInt; + public String myString; + public int[] myInts; + public List myList; + @SuppressWarnings("unchecked") + public List[] myLists; + + public static int ourInt; + public static String ourString; + public static int[] ourInts; + public static List ourList; + @SuppressWarnings("unchecked") + public static List[] ourLists; +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java b/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java new file mode 100644 index 000000000000..71b280985225 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/OverloadedMethod.java @@ -0,0 +1,41 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findVirtual(Test.class, "method", MethodType.methodType(void.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(int.class, int.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(boolean.class, short.class, char.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(int.class, int.class, int[].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(int.class, long.class, int[][].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(int.class, long.class, int[][][].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object[].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(Object.class, Object[][].class)); + l.findVirtual(Test.class, "method", MethodType.genericMethodType(1)); + + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, void.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(void.class, int.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(short.class, char.class)); + l.findVirtual(Test.class, "method", MethodType.methodType(int.class, int[].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(int.class, int[][].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(Object[].class, Object[].class)); + l.findVirtual(Test.class, "method", MethodType.methodType(Object[][].class, Object[][].class)); + + l.findStatic(Test.class, "method", MethodType.methodType(void.class)); + l.findVirtual(Test.class, "doesntExist", MethodType.methodType(void.class)); + } +} + +class Test { + public void method() {} + public int method(int n) {return n;} + public boolean method(short a, char b) {return true;} + public int method(int n, int... a) {return n;} + public int method(long n, int[]... a) {return a.length;} + public int method(long n, int[][][] a) {return a.length;} + public Object method(Object o) {return o;} + public Object method(Object[] o) {return o;} + public Object method(Object[][] o) {return o;} +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/Setter.java b/java/java-tests/testData/inspection/invokeHandleSignature/Setter.java new file mode 100644 index 000000000000..570fc69aaca8 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/Setter.java @@ -0,0 +1,47 @@ +import java.lang.invoke.*; +import java.util.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findSetter(Test.class, "myInt", int.class); + l.findSetter(Test.class, "myInts", int[].class); + l.findSetter(Test.class, "myList", List.class); + l.findSetter(Test.class, "myLists", List[].class); + l.findSetter(Test.class, "myString", String.class); + + l.findStaticSetter(Test.class, "ourInt", int.class); + l.findStaticSetter(Test.class, "ourInts", int[].class); + l.findStaticSetter(Test.class, "ourList", List.class); + l.findStaticSetter(Test.class, "ourLists", List[].class); + l.findStaticSetter(Test.class, "ourString", String.class); + + l.findSetter(Test.class, "doesntExist", String.class); + l.findStaticSetter(Test.class, "doesntExist", String.class); + + l.findSetter(Test.class, "myInt", void.class); + l.findSetter(Test.class, "myInts", int.class); + l.findSetter(Test.class, "myString", List.class); + + l.findStaticSetter(Test.class, "ourInt", void.class); + l.findStaticSetter(Test.class, "ourInts", int.class); + l.findStaticSetter(Test.class, "ourString", List.class); + } +} + +class Test { + public int myInt; + public String myString; + public int[] myInts; + public List myList; + @SuppressWarnings("unchecked") + public List[] myLists; + + public static int ourInt; + public static String ourString; + public static int[] ourInts; + public static List ourList; + @SuppressWarnings("unchecked") + public static List[] ourLists; +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java b/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java new file mode 100644 index 000000000000..0caa77cd9035 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/StaticMethod.java @@ -0,0 +1,24 @@ +import java.lang.invoke.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findStatic(Test.class, "method1", MethodType.methodType(void.class)); + l.findStatic(Test.class, "method2", MethodType.methodType(String.class, String.class)); + l.findStatic(Test.class, "method3", MethodType.methodType(String.class, String.class, String[].class)); + + l.findStatic(Test.class, "method1", MethodType.methodType(Test.class)); + l.findStatic(Test.class, "method2", MethodType.methodType(int.class, String.class)); + l.findStatic(Test.class, "method3", MethodType.methodType(String.class)); + + l.findVirtual(Test.class, "method1", MethodType.methodType(void.class)); + l.findStatic(Test.class, "doesntExist", MethodType.methodType(String.class)); + } +} + +class Test { + public static void method1() {} + public static String method2(String a) {return a;} + public static String method3(String a, String... b) {return a;} +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/invokeHandleSignature/VarHandle.java b/java/java-tests/testData/inspection/invokeHandleSignature/VarHandle.java new file mode 100644 index 000000000000..15cdf0c5db08 --- /dev/null +++ b/java/java-tests/testData/inspection/invokeHandleSignature/VarHandle.java @@ -0,0 +1,47 @@ +import java.lang.invoke.*; +import java.util.*; + +class Main { + void foo() throws Exception { + MethodHandles.Lookup l = MethodHandles.lookup(); + + l.findVarHandle(Test.class, "myInt", int.class); + l.findVarHandle(Test.class, "myInts", int[].class); + l.findVarHandle(Test.class, "myList", List.class); + l.findVarHandle(Test.class, "myLists", List[].class); + l.findVarHandle(Test.class, "myString", String.class); + + l.findStaticVarHandle(Test.class, "ourInt", int.class); + l.findStaticVarHandle(Test.class, "ourInts", int[].class); + l.findStaticVarHandle(Test.class, "ourList", List.class); + l.findStaticVarHandle(Test.class, "ourLists", List[].class); + l.findStaticVarHandle(Test.class, "ourString", String.class); + + l.findVarHandle(Test.class, "doesntExist", String.class); + l.findStaticVarHandle(Test.class, "doesntExist", String.class); + + l.findVarHandle(Test.class, "myInt", void.class); + l.findVarHandle(Test.class, "myInts", int.class); + l.findVarHandle(Test.class, "myString", List.class); + + l.findStaticVarHandle(Test.class, "ourInt", void.class); + l.findStaticVarHandle(Test.class, "ourInts", int.class); + l.findStaticVarHandle(Test.class, "ourString", List.class); + } +} + +class Test { + public int myInt; + public String myString; + public int[] myInts; + public List myList; + @SuppressWarnings("unchecked") + public List[] myLists; + + public static int ourInt; + public static String ourString; + public static int[] ourInts; + public static List ourList; + @SuppressWarnings("unchecked") + public static List[] ourLists; +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListAsList.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListAsList.java index 05242c299274..760dd9493d8d 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListAsList.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListAsList.java @@ -5,5 +5,5 @@ import java.util.Collections; import java.util.List; public class Test { - public static final List EVEN = List.of(2, 4, 6, 8, 10); + public static final List EVEN = List.of(2, 4, 6, 8, 10); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListDoubleBrace.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListDoubleBrace.java index aef70fd2cdea..c9d8eba623d6 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListDoubleBrace.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListDoubleBrace.java @@ -4,5 +4,5 @@ import java.util.Collections; import java.util.List; public class Test { - public static final List EVEN = List.of(0, 2, 4, 6, 8); + public static final List EVEN = List.of(0, 2, 4, 6, 8); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java index d1188ed732b6..29e332c90bcc 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterArrayListExplicit.java @@ -4,7 +4,7 @@ import java.util.*; public class Test { public void testList() { List list; - list = List.of(1, 2); + list = List.of(1, 2); System.out.println(list); } } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterAsList.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterAsList.java index 4758a7891608..29392eb96ab9 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterAsList.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterAsList.java @@ -4,5 +4,5 @@ import java.util.Collections; import java.util.List; public class Test { - public static final List EVEN = List.of(2, 4, 6, 8, 10, 2); + public static final List EVEN = List.of(2, 4, 6, 8, 10, 2); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMap10.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMap10.java index 5762f22b378d..b60ebd145309 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMap10.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMap10.java @@ -5,7 +5,7 @@ public class Test { public void test() { Map myMap; - myMap = Map.of("a", "1", "b", "1", "c", "1", + myMap = Map.of("a", "1", "b", "1", "c", "1", // D follows "d", "1", "e", /* this is also 1*/ "1", "f", "1", "g", "1", // G is important! diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMapSimple.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMapSimple.java index 02f6e2e80d3c..a5dcd1da34d4 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMapSimple.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashMapSimple.java @@ -4,6 +4,6 @@ import java.util.*; public class Test { public void test() { Map myMap; - myMap = Map.of("a", "b", "c", "b"); + myMap = Map.of("a", "b", "c", "b"); } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetAsList.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetAsList.java index f5d47c8a7bf5..7f2c1ffa5879 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetAsList.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetAsList.java @@ -5,5 +5,5 @@ import java.util.HashSet; import java.util.Set; public class Test { - public static final Set MY_SET = Set.of("a", "b", "c", Math.random() > 0.5 ? "d" : Math.random() > 0.5 ? "e" : "d"); + public static final Set MY_SET = Set.of("a", "b", "c", Math.random() > 0.5 ? "d" : Math.random() > 0.5 ? "e" : "d"); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetDoubleBrace.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetDoubleBrace.java index 2fa360827e1d..307b38126b1b 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetDoubleBrace.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetDoubleBrace.java @@ -4,5 +4,5 @@ import java.util.HashSet; import java.util.Set; public class Test { - public static final Set MY_SET = Set.of("a", "b", "c".toUpperCase()); + public static final Set MY_SET = Set.of("a", "b", "c".toUpperCase()); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java index 0144d22d3fb3..81b7dd158366 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicit.java @@ -6,6 +6,6 @@ public class Test { static { Set set; - MY_SET = Set.of("foo", "bar", "xyz"); + MY_SET = Set.of("foo", "bar", "xyz"); } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java index 8d990faece92..31d4eb7e0032 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterHashSetExplicitReusedVar.java @@ -4,7 +4,7 @@ import java.util.*; public class Test { public void test2() { Set set; - set = Set.of("foo", "bar", "xyz"); + set = Set.of("foo", "bar", "xyz"); System.out.println(set); } } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToList.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToList.java index b865779c4db6..704e3a80dfa0 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToList.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToList.java @@ -5,5 +5,5 @@ import java.util.stream.Collectors; import java.util.stream.Stream; public class Test { - public static final List> MY_LIST = List.>of(String.class, int.class, Object.class); + public static final List> MY_LIST = List.of(String.class, int.class, Object.class); } diff --git a/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToSet.java b/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToSet.java index 79c8990dae50..343895844464 100644 --- a/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToSet.java +++ b/java/java-tests/testData/inspection/java9CollectionFactory/afterStreamToSet.java @@ -5,5 +5,5 @@ import java.util.stream.Collectors; import java.util.stream.Stream; public class Test { - public static final Set> MY_SET = Set.>of(String.class, int.class, Object.class); + public static final Set> MY_SET = Set.of(String.class, int.class, Object.class); } diff --git a/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java b/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java index 5e97019c96b4..3e39e73adf34 100644 --- a/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java +++ b/java/java-tests/testData/inspection/nullableProblems/OverriddenViaMethodReference.java @@ -18,6 +18,10 @@ class P2 { }); test(this::getNull); test(this::getPrimitive); + test(this::getNonAnnotated); + + test(WithImplicitConstructor::new); + test(WithExplicitConstructor::new); } @Nullable @@ -29,3 +33,6 @@ class P2 { private void test(final NonnullInterface function) { } } + +class WithImplicitConstructor {} +class WithExplicitConstructor { WithExplicitConstructor() {} } \ No newline at end of file diff --git a/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml b/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml new file mode 100644 index 000000000000..9bc83e9c78ed --- /dev/null +++ b/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/expected.xml @@ -0,0 +1,12 @@ + + + + + module-info.java + 5 + + Redundant suppression + Redundant suppression + + + \ No newline at end of file diff --git a/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/src/module-info.java b/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/src/module-info.java new file mode 100644 index 000000000000..0b5a1f4144d3 --- /dev/null +++ b/java/java-tests/testData/inspection/redundantSuppress/moduleInfo/src/module-info.java @@ -0,0 +1,7 @@ +/** + * no references here + */ +@SuppressWarnings("JavadocReference") +module com.moduleinfo.test { + +} \ No newline at end of file diff --git a/java/java-tests/testData/inspection/usage1.5/overrideAnnotation/expected.xml b/java/java-tests/testData/inspection/usage1.5/overrideAnnotation/expected.xml index bd67b3f7a82c..6502e2c42b21 100644 --- a/java/java-tests/testData/inspection/usage1.5/overrideAnnotation/expected.xml +++ b/java/java-tests/testData/inspection/usage1.5/overrideAnnotation/expected.xml @@ -4,7 +4,7 @@ Test.java 4 Usages of API documented as @since 1.5 (1.6|1.7) - Usage of API documented as @since 1.7+ + Usage of API documented as @since 1.8+ \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineMethod/EnumConstantConstructorWithArgs.java b/java/java-tests/testData/refactoring/inlineMethod/EnumConstantConstructorWithArgs.java new file mode 100644 index 000000000000..eeffe170dbb1 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/EnumConstantConstructorWithArgs.java @@ -0,0 +1,16 @@ +public class TestInlining { + + static String inlineMe(String arg) { + return arg.trim(); + } + + public static final String FP = inlineMe("fa"); + + enum MyEnum { + A(inlineMe("foo")); + + MyEnum(String s) { + } + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineMethod/EnumConstantConstructorWithArgs.java.after b/java/java-tests/testData/refactoring/inlineMethod/EnumConstantConstructorWithArgs.java.after new file mode 100644 index 000000000000..0880ffcbd800 --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/EnumConstantConstructorWithArgs.java.after @@ -0,0 +1,12 @@ +public class TestInlining { + + public static final String FP = "fa".trim(); + + enum MyEnum { + A("foo".trim()); + + MyEnum(String s) { + } + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineMethod/MethodReferenceInsideMethodCall.java b/java/java-tests/testData/refactoring/inlineMethod/MethodReferenceInsideMethodCall.java new file mode 100644 index 000000000000..f7ca89bc32cf --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/MethodReferenceInsideMethodCall.java @@ -0,0 +1,14 @@ + +import java.util.function.Supplier; +class Foo { + + static int m() { + return 1; + } + + void o(){ + mm(Foo::m); + } + + void mm(Supplier r) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/inlineMethod/MethodReferenceInsideMethodCall.java.after b/java/java-tests/testData/refactoring/inlineMethod/MethodReferenceInsideMethodCall.java.after new file mode 100644 index 000000000000..7d486f81f40a --- /dev/null +++ b/java/java-tests/testData/refactoring/inlineMethod/MethodReferenceInsideMethodCall.java.after @@ -0,0 +1,10 @@ + +import java.util.function.Supplier; +class Foo { + + void o(){ + mm(() -> 1); + } + + void mm(Supplier r) {} +} \ No newline at end of file diff --git a/java/java-tests/testData/refactoring/renameClass/collision/after/pack2/Usage2.java b/java/java-tests/testData/refactoring/renameClass/collision/after/pack2/Usage2.java index 8f5774500907..5e8108ad3ee5 100644 --- a/java/java-tests/testData/refactoring/renameClass/collision/after/pack2/Usage2.java +++ b/java/java-tests/testData/refactoring/renameClass/collision/after/pack2/Usage2.java @@ -1,6 +1,5 @@ package pack2; - -import java.util.List; +import java.util.*; public class Usage2 { int method() { diff --git a/java/java-tests/testSrc/com/intellij/TestClassesFilterTest.java b/java/java-tests/testSrc/com/intellij/TestClassesFilterTest.java index 8e39ebd3c9a8..eb69454e73bb 100644 --- a/java/java-tests/testSrc/com/intellij/TestClassesFilterTest.java +++ b/java/java-tests/testSrc/com/intellij/TestClassesFilterTest.java @@ -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,109 +15,149 @@ */ package com.intellij; -import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.vfs.CharsetToolkit; import org.junit.Test; import java.io.ByteArrayInputStream; import java.io.InputStreamReader; -import java.io.UnsupportedEncodingException; import java.util.Arrays; import java.util.Collections; +import static com.intellij.GroupBasedTestClassFilter.createOn; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; public class TestClassesFilterTest { - private static final Logger LOG = Logger.getInstance("#com.intellij.TestClassesFilterTest"); + private static String FILTER_TEXT = "[Group1]\n" + + "com.intellij.package1.*\n" + + "com.intellij.package2.ExcludedTest\n" + + "com.intellij.package3.*package4\n" + + "\n" + + "[Group2]\n" + + "com.intellij.package5.*\n" + + "com.intellij.package6.ExcludedTest\n" + + "com.intellij.package7.*package8\n" + + "[Group3]\n" + + "org.jetbrains.*\n" + + "-org.jetbrains.excluded.*\n" + + "[Group4]\n" + + "org.jetbrains.excluded.TestIncludeInG4"; @Test - public void test() throws Exception { - LOG.info("test"); - - String filterText = "[Group1]\n" + - "com.intellij.package1.*\n" + - "com.intellij.package2.ExcludedTest\n" + - "com.intellij.package3.*package4\n" + - "[Group2]\n" + - "com.intellij.package5.*\n" + - "com.intellij.package6.ExcludedTest\n" + - "com.intellij.package7.*package8"; - - TestClassesFilter classesFilter = GroupBasedTestClassFilter.createOn(getReader(filterText), Collections.singletonList("Group1")); - assertTrue(classesFilter.matches("com.intellij.package1.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package1.Test2", null)); - assertFalse(classesFilter.matches("com.intellij.package2.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package2.ExcludedTest", null)); - assertTrue(classesFilter.matches("com.intellij.package3.package4", null)); - assertTrue(classesFilter.matches("com.intellij.package3.package5.package4", null)); - assertFalse(classesFilter.matches("com.intellij.package3", null)); - assertFalse(classesFilter.matches("com.intellij", null)); - assertFalse(classesFilter.matches("com.intellij.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package5.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package5.Test2", null)); - assertFalse(classesFilter.matches("com.intellij.package6.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package6.ExcludedTest", null)); - assertFalse(classesFilter.matches("com.intellij.package7.package8", null)); - assertFalse(classesFilter.matches("com.intellij.package7.package5.package8", null)); - assertFalse(classesFilter.matches("com.intellij.package7", null)); - - classesFilter = GroupBasedTestClassFilter.createOn(getReader(filterText), Collections.singletonList("Group2")); - assertFalse(classesFilter.matches("com.intellij.package1.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package1.Test2", null)); - assertFalse(classesFilter.matches("com.intellij.package2.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package2.ExcludedTest", null)); - assertFalse(classesFilter.matches("com.intellij.package3.package4", null)); - assertFalse(classesFilter.matches("com.intellij.package3.package5.package4", null)); - assertFalse(classesFilter.matches("com.intellij.package3", null)); - assertFalse(classesFilter.matches("com.intellij", null)); - assertFalse(classesFilter.matches("com.intellij.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package5.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package5.Test2", null)); - assertFalse(classesFilter.matches("com.intellij.package6.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package6.ExcludedTest", null)); - assertTrue(classesFilter.matches("com.intellij.package7.package8", null)); - assertTrue(classesFilter.matches("com.intellij.package7.package5.package8", null)); - assertFalse(classesFilter.matches("com.intellij.package7", null)); - - classesFilter = GroupBasedTestClassFilter.createOn(getReader(filterText), - Collections.singletonList(GroupBasedTestClassFilter.ALL_EXCLUDE_DEFINED)); - checkForAllExcludedDefinedGroup(classesFilter); - - classesFilter = GroupBasedTestClassFilter.createOn(getReader(filterText), Collections.emptyList()); - checkForAllExcludedDefinedGroup(classesFilter); - - classesFilter = GroupBasedTestClassFilter.createOn(getReader(filterText), Arrays.asList("Group1", "Group2")); - assertTrue(classesFilter.matches("com.intellij.package1.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package5.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package4.Test", null)); - - classesFilter = GroupBasedTestClassFilter.createOn(getReader(filterText), Arrays.asList("Group1", GroupBasedTestClassFilter.ALL_EXCLUDE_DEFINED)); - assertTrue(classesFilter.matches("com.intellij.package1.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package5.Test", null)); - assertTrue(classesFilter.matches("com.intellij.package4.Test", null)); + public void excluded() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), Collections.singletonList("Group3")); + assertTrue(classesFilter.matches("org.jetbrains.included")); + assertTrue(classesFilter.matches("org.jetbrains.included.Test1")); + assertFalse(classesFilter.matches("org.jetbrains.excluded.Test1")); + assertFalse(classesFilter.matches("org.jetbrains.excluded.sub.Test1")); + assertTrue(classesFilter.matches("org.jetbrains.excluded")); } - private static InputStreamReader getReader(String filterText) throws UnsupportedEncodingException { + @Test + public void excludedFromGroup3ShouldBeInAllExcluded() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), + Collections.singletonList(GroupBasedTestClassFilter.ALL_EXCLUDE_DEFINED)); + assertTrue(classesFilter.matches("org.jetbrains.excluded.Test1")); + } + + @Test + public void excludedFromGroup3AndIncludeInGroup4ShouldBeOutAllExcluded() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), + Collections.singletonList(GroupBasedTestClassFilter.ALL_EXCLUDE_DEFINED)); + assertFalse(classesFilter.matches("org.jetbrains.excluded.TestIncludeInG4")); + } + + @Test + public void group1AndAllExcludeDefined() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), + Arrays.asList("Group1", GroupBasedTestClassFilter.ALL_EXCLUDE_DEFINED)); + assertTrue(classesFilter.matches("com.intellij.package1.Test")); + assertFalse(classesFilter.matches("com.intellij.package5.Test")); + assertTrue(classesFilter.matches("com.intellij.package4.Test")); + } + + @Test + public void group1AndGroup2() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), + Arrays.asList("Group1", "Group2")); + assertTrue(classesFilter.matches("com.intellij.package1.Test")); + assertTrue(classesFilter.matches("com.intellij.package5.Test")); + assertFalse(classesFilter.matches("com.intellij.package4.Test")); + } + + @Test + public void emptyList() throws Exception { + checkForAllExcludedDefinedGroup(createOn(getReader(FILTER_TEXT), Collections.emptyList())); + } + + @Test + public void allExcluded() throws Exception { + checkForAllExcludedDefinedGroup(createOn(getReader(FILTER_TEXT), + Collections.singletonList(GroupBasedTestClassFilter.ALL_EXCLUDE_DEFINED))); + } + + @Test + public void group2() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), Collections.singletonList("Group2")); + assertFalse(classesFilter.matches("com.intellij.package1.Test")); + assertFalse(classesFilter.matches("com.intellij.package1.Test2")); + assertFalse(classesFilter.matches("com.intellij.package2.Test")); + assertFalse(classesFilter.matches("com.intellij.package2.ExcludedTest")); + assertFalse(classesFilter.matches("com.intellij.package3.package4")); + assertFalse(classesFilter.matches("com.intellij.package3.package5.package4")); + assertFalse(classesFilter.matches("com.intellij.package3")); + assertFalse(classesFilter.matches("com.intellij")); + assertFalse(classesFilter.matches("com.intellij.Test")); + assertTrue(classesFilter.matches("com.intellij.package5.Test")); + assertTrue(classesFilter.matches("com.intellij.package5.Test2")); + assertFalse(classesFilter.matches("com.intellij.package6.Test")); + assertTrue(classesFilter.matches("com.intellij.package6.ExcludedTest")); + assertTrue(classesFilter.matches("com.intellij.package7.package8")); + assertTrue(classesFilter.matches("com.intellij.package7.package5.package8")); + assertFalse(classesFilter.matches("com.intellij.package7")); + } + + @Test + public void group1() throws Exception { + TestClassesFilter classesFilter = createOn(getReader(FILTER_TEXT), Collections.singletonList("Group1")); + assertTrue(classesFilter.matches("com.intellij.package1.Test")); + assertTrue(classesFilter.matches("com.intellij.package1.Test2")); + assertFalse(classesFilter.matches("com.intellij.package2.Test")); + assertTrue(classesFilter.matches("com.intellij.package2.ExcludedTest")); + assertTrue(classesFilter.matches("com.intellij.package3.package4")); + assertTrue(classesFilter.matches("com.intellij.package3.package5.package4")); + assertFalse(classesFilter.matches("com.intellij.package3")); + assertFalse(classesFilter.matches("com.intellij")); + assertFalse(classesFilter.matches("com.intellij.Test")); + assertFalse(classesFilter.matches("com.intellij.package5.Test")); + assertFalse(classesFilter.matches("com.intellij.package5.Test2")); + assertFalse(classesFilter.matches("com.intellij.package6.Test")); + assertFalse(classesFilter.matches("com.intellij.package6.ExcludedTest")); + assertFalse(classesFilter.matches("com.intellij.package7.package8")); + assertFalse(classesFilter.matches("com.intellij.package7.package5.package8")); + assertFalse(classesFilter.matches("com.intellij.package7")); + } + + private static InputStreamReader getReader(String filterText) { return new InputStreamReader(new ByteArrayInputStream(filterText.getBytes(CharsetToolkit.UTF8_CHARSET))); } private static void checkForAllExcludedDefinedGroup(TestClassesFilter classesFilter) { - assertFalse(classesFilter.matches("com.intellij.package1.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package1.Test2", null)); - assertTrue(classesFilter.matches("com.intellij.package2.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package2.ExcludedTest", null)); - assertFalse(classesFilter.matches("com.intellij.package3.package4", null)); - assertFalse(classesFilter.matches("com.intellij.package3.package5.package4", null)); - assertTrue(classesFilter.matches("com.intellij.package3", null)); - assertTrue(classesFilter.matches("com.intellij", null)); - assertTrue(classesFilter.matches("com.intellij.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package5.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package5.Test2", null)); - assertTrue(classesFilter.matches("com.intellij.package6.Test", null)); - assertFalse(classesFilter.matches("com.intellij.package6.ExcludedTest", null)); - assertFalse(classesFilter.matches("com.intellij.package7.package8", null)); - assertFalse(classesFilter.matches("com.intellij.package7.package5.package8", null)); - assertTrue(classesFilter.matches("com.intellij.package7", null)); + assertFalse(classesFilter.matches("com.intellij.package1.Test")); + assertFalse(classesFilter.matches("com.intellij.package1.Test2")); + assertTrue(classesFilter.matches("com.intellij.package2.Test")); + assertFalse(classesFilter.matches("com.intellij.package2.ExcludedTest")); + assertFalse(classesFilter.matches("com.intellij.package3.package4")); + assertFalse(classesFilter.matches("com.intellij.package3.package5.package4")); + assertTrue(classesFilter.matches("com.intellij.package3")); + assertTrue(classesFilter.matches("com.intellij")); + assertTrue(classesFilter.matches("com.intellij.Test")); + assertFalse(classesFilter.matches("com.intellij.package5.Test")); + assertFalse(classesFilter.matches("com.intellij.package5.Test2")); + assertTrue(classesFilter.matches("com.intellij.package6.Test")); + assertFalse(classesFilter.matches("com.intellij.package6.ExcludedTest")); + assertFalse(classesFilter.matches("com.intellij.package7.package8")); + assertFalse(classesFilter.matches("com.intellij.package7.package5.package8")); + assertTrue(classesFilter.matches("com.intellij.package7")); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceActionTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceActionTest.groovy index 1270cb79fad3..d74228437465 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceActionTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/CopyReferenceActionTest.groovy @@ -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. @@ -63,7 +63,7 @@ class CopyReferenceActionTest extends LightCodeInsightFixtureTestCase { performCopy() myFixture.configureByText("a.java", "import p.Foo; class Bar { }") performPaste() - myFixture.checkResult """import p.Foo; class Bar {p1.Foo}""" + myFixture.checkResult """import p.Foo; class Bar { p1.Foo}""" } void testAddImport() { diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/JoinLinesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/JoinLinesTest.java index 717bc581d084..19b8e59ef976 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/JoinLinesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/JoinLinesTest.java @@ -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. @@ -222,6 +222,13 @@ public class JoinLinesTest extends LightCodeInsightTestCase { doTest(); } + public void testJoiningMethodCallWhenItDoesntFit() throws Exception { + CommonCodeStyleSettings settings = CodeStyleSettingsManager.getSettings(getProject()).getCommonSettings(JavaLanguage.INSTANCE); + settings.METHOD_CALL_CHAIN_WRAP = CommonCodeStyleSettings.WRAP_AS_NEEDED; + settings.RIGHT_MARGIN = 20; + doTest(); + } + private void doTest() throws Exception { doTest(".java"); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/ParameterInfoTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/ParameterInfoTest.java index 2e8125b660fd..dc37be04afd5 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/ParameterInfoTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/ParameterInfoTest.java @@ -76,6 +76,26 @@ public class ParameterInfoTest extends LightCodeInsightFixtureTestCase { } public void testSelectionWithGenerics() { + doTest2CandidatesWithPreselection(); + } + + public void testOverloadWithVarargs() { + doTest2CandidatesWithPreselection(); + } + + public void testOverloadWithVarargsMultipleArgs() { + doTest2CandidatesWithPreselection(); + } + + public void testOverloadWithVarargsSingleArg() { + doTest2CandidatesWithPreselection(); + } + + public void testOverloadWithVarargsArray() { + doTest2CandidatesWithPreselection(); + } + + private void doTest2CandidatesWithPreselection() { myFixture.configureByFile(getTestName(false) + ".java"); MethodParameterInfoHandler handler = new MethodParameterInfoHandler(); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java index cee5987f8c5b..2336adced00a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType17CompletionTest.java @@ -42,6 +42,10 @@ public class SmartType17CompletionTest extends LightFixtureCompletionTestCase { doTest(); } + public void testDiamondCollapsedWithMethodTypeParameter() throws Exception { + doTest(); + } + public void testDiamondNotCollapsed() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java index b890597c2e6f..1e294fca1397 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/completion/SmartType18CompletionTest.java @@ -156,6 +156,10 @@ public class SmartType18CompletionTest extends LightFixtureCompletionTestCase { } public void testStaticMethodReference() { doTest(false); } + public void testStaticMethodReferenceInContextWithTypeArgs() { + configureByTestName(); + checkResultByFile("/" + getTestName(false) + "-out.java"); + } public void testOuterMethodReference() { doTest(true); } public void testNoAnonymousOuterMethodReference() { doAntiTest(); } @@ -229,6 +233,11 @@ public void testConvertToObjectStream() { assertOrderedEquals(myFixture.getLookupElementStrings(), "get2"); } + public void testInferredObjects() { + configureByTestName(); + assertOrderedEquals(myFixture.getLookupElementStrings(), "M", "HM"); + } + public void testSuggestMapInheritors() { doTest(); } public void testUnboundTypeArgs() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java index 1af8334b0408..fc2de36c7e64 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/AnnotationsHighlightingTest.java @@ -49,7 +49,6 @@ public class AnnotationsHighlightingTest extends LightDaemonAnalyzerTestCase { public void testEnumValues() { doTest(); } public void testReceiverParameters() { doTest(); } public void testAnnotationOverIncompleteCode() { doTest(); } - public void testModuleAnnotation() { setLanguageLevel(LanguageLevel.JDK_1_9); doTest("module-info.java"); } private void doTest() { doTest(getTestName(true) + ".java"); } private void doTest(String name) { doTest(BASE_PATH + "/" + name, false, false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java index 9147217a0e21..f8b6552e57c5 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ImportHelperTest.java @@ -16,6 +16,7 @@ package com.intellij.codeInsight.daemon; import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.CodeInsightWorkspaceSettings; import com.intellij.codeInsight.daemon.impl.DaemonListeners; import com.intellij.codeInsight.daemon.impl.HighlightInfo; import com.intellij.codeInsight.daemon.impl.quickfix.ImportClassFix; @@ -62,8 +63,11 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { @Override protected void tearDown() throws Exception { - CodeStyleSettingsManager.getInstance(getProject()).dropTemporarySettings(); - super.tearDown(); + try { + CodeStyleSettingsManager.getInstance(getProject()).dropTemporarySettings(); + } finally { + super.tearDown(); + } } @WrapInCommand @@ -373,26 +377,20 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { @NonNls String text = "import xxx.yyy; class S { } "; configureByText(StdFileTypes.JAVA, text); - boolean old = CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY; - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = true; + CodeInsightWorkspaceSettings.getInstance(myProject).setOptimizeImportsOnTheFly(true, getTestRootDisposable()); DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); - try { - List errs = highlightErrors(); + List errs = highlightErrors(); - assertEquals(1, errs.size()); + assertEquals(1, errs.size()); - assertEquals(1, ((PsiJavaFile)getFile()).getImportList().getAllImportStatements().length); + assertEquals(1, ((PsiJavaFile)getFile()).getImportList().getAllImportStatements().length); - type("/* */"); - doHighlighting(); - UIUtil.dispatchAllInvocationEvents(); + type("/* */"); + doHighlighting(); + UIUtil.dispatchAllInvocationEvents(); - assertEmpty(((PsiJavaFile)getFile()).getImportList().getAllImportStatements()); - } - finally { - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = old; - } + assertEmpty(((PsiJavaFile)getFile()).getImportList().getAllImportStatements()); } public void testAutoInsertImportForInnerClass() throws Throwable { @@ -447,9 +445,8 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { configureByText(StdFileTypes.JAVA, text); boolean old = CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; - boolean opt = CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY; CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = true; - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = true; + CodeInsightWorkspaceSettings.getInstance(myProject).setOptimizeImportsOnTheFly(true, getTestRootDisposable()); DaemonCodeAnalyzerSettings.getInstance().setImportHintEnabled(true); try { @@ -458,7 +455,6 @@ public class ImportHelperTest extends DaemonAnalyzerTestCase { } finally { CodeInsightSettings.getInstance().ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY = old; - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = opt; } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/JavadocResolveTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/JavadocResolveTest.java index 2fea76a290e7..1758bcd016fc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/JavadocResolveTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/JavadocResolveTest.java @@ -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. @@ -18,6 +18,8 @@ package com.intellij.codeInsight.daemon; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.javaDoc.JavaDocLocalInspection; import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection; +import com.intellij.openapi.roots.LanguageLevelProjectExtension; +import com.intellij.pom.java.LanguageLevel; public class JavadocResolveTest extends DaemonAnalyzerTestCase { private static final String BASE_PATH = "/codeInsight/daemonCodeAnalyzer/javaDoc/resolve"; @@ -33,6 +35,9 @@ public class JavadocResolveTest extends DaemonAnalyzerTestCase { public void testSee3() throws Exception { doTest(); } public void testPackageInfo() throws Exception { doTest(BASE_PATH + "/pkg/package-info.java", BASE_PATH, false, false); } public void testBrokenPackageInfo() throws Exception { doTest(BASE_PATH + "/pkg1/package-info.java", BASE_PATH, false, false); } + public void testModuleInfo() throws Exception { + LanguageLevelProjectExtension.getInstance(getProject()).setLanguageLevel(LanguageLevel.JDK_1_9); + doTest(BASE_PATH + "/pkg/module-info.java", BASE_PATH, false, false); } private void doTest() throws Exception { doTest(BASE_PATH + "/pkg/" + getTestName(false) + ".java", BASE_PATH, false, false); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java index 873708a11487..c4b9a08f1e0a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/LightAdvHighlightingJdk9Test.java @@ -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,9 +16,11 @@ package com.intellij.codeInsight.daemon; import com.intellij.codeInspection.deadCode.UnusedDeclarationInspection; +import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection; import com.intellij.codeInspection.redundantCast.RedundantCastInspection; import com.intellij.codeInspection.uncheckedWarnings.UncheckedWarningLocalInspection; import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.pom.java.LanguageLevel; import com.intellij.testFramework.IdeaTestUtil; @@ -28,7 +30,7 @@ public class LightAdvHighlightingJdk9Test extends LightDaemonAnalyzerTestCase { @Override protected void setUp() throws Exception { super.setUp(); - enableInspectionTools(new UnusedDeclarationInspection(), new UncheckedWarningLocalInspection(), new RedundantCastInspection()); + enableInspectionTools(new UnusedDeclarationInspection(), new UncheckedWarningLocalInspection(), new RedundantCastInspection(), new JavaDocReferenceInspection()); setLanguageLevel(LanguageLevel.JDK_1_9); IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_9, getModule(), getTestRootDisposable()); } @@ -50,4 +52,13 @@ public class LightAdvHighlightingJdk9Test extends LightDaemonAnalyzerTestCase { public void testDiamondsWithAnonymousDiamond() { doTest(false, false);} public void testValueTypes() { setLanguageLevel(LanguageLevel.JDK_X); doTest(false, false); } + + public void testModuleInfoSuppression() { + doTest(BASE_PATH + "/module-info.java", true, false); + } + + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk9(); + } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt index c5383af285c3..f7f98883def1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/ModuleHighlightingTest.kt @@ -59,19 +59,33 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { highlight("pkg/module-info.java", """module M { }""") } + fun testIncompatibleModifiers() { + highlight(""" + module M { + requires static transitive M2; + requires private volatile M3; + }""".trimIndent()) + } + + fun testAnnotations() { + highlight("""@Deprecated @Override module M { }""") + } + fun testDuplicateStatements() { addFile("pkg/main/C.java", "package pkg.main;\npublic class C { }") addFile("pkg/main/Impl.java", "package pkg.main;\npublic class Impl extends C { }") highlight(""" module M { requires M2; - requires M2; + requires M2; exports pkg.main; - exports pkg. main; + exports pkg. main; + opens pkg.main; + opens pkg. main; uses pkg.main.C; - uses pkg. main . /*...*/ C; + uses pkg. main . /*...*/ C; provides pkg .main .C with pkg.main.Impl; - provides pkg.main.C with pkg. main. Impl; + provides pkg.main.C with pkg. main. Impl; }""".trimIndent()) } @@ -105,7 +119,7 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { module M { exports pkg.missing.unknown; exports pkg.empty; - exports pkg.main to M.missing, M2, M2; + exports pkg.main to M.missing, M2, M2; }""".trimIndent()) } @@ -117,12 +131,12 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { module M { opens pkg.missing.unknown; opens pkg.empty; - opens pkg.main to M.missing, M2, M2; + opens pkg.main to M.missing, M2, M2; }""".trimIndent()) } fun testWeakModule() { - highlight("""open module M { opens pkg.missing; }""") + highlight("""open module M { opens pkg.missing; }""") } fun testUses() { @@ -142,6 +156,7 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { fun testProvides() { addFile("pkg/main/C.java", "package pkg.main;\npublic interface C { }") + addFile("pkg/main/CT.java", "package pkg.main;\npublic interface CT { }") addFile("pkg/main/Impl1.java", "package pkg.main;\nclass Impl1 { }") addFile("pkg/main/Impl2.java", "package pkg.main;\npublic class Impl2 { }") addFile("pkg/main/Impl3.java", "package pkg.main;\npublic abstract class Impl3 implements C { }") @@ -150,8 +165,14 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { addFile("pkg/main/Impl6.java", "package pkg.main;\npublic class Impl6 implements C { }") addFile("pkg/main/Impl7.java", "package pkg.main;\npublic class Impl7 {\n public static void provider();\n}") addFile("pkg/main/Impl8.java", "package pkg.main;\npublic class Impl8 {\n public static C provider();\n}") + addFile("pkg/main/Impl9.java", "package pkg.main;\npublic class Impl9 {\n public class Inner implements C { }\n}") + addFile("pkg/main/Impl10.java", "package pkg.main;\npublic class Impl10 implements CT {\n private Impl10() { }\n public static CT provider();\n}") + addFile("module-info.java", "module M2 {\n exports pkg.m2;\n}", M2) + addFile("pkg/m2/C.java", "package pkg.m2;\npublic class C { }", M2) + addFile("pkg/m2/Impl.java", "package pkg.m2;\npublic class Impl extends C { }", M2) highlight(""" module M { + requires M2; provides pkg.main.C with pkg.main.NoImpl; provides pkg.main.C with pkg.main.Impl1; provides pkg.main.C with pkg.main.Impl2; @@ -161,6 +182,9 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { provides pkg.main.C with pkg.main.Impl6, pkg.main.Impl6; provides pkg.main.C with pkg.main.Impl7; provides pkg.main.C with pkg.main.Impl8; + provides pkg.main.C with pkg.main.Impl9.Inner; + provides pkg.main.CT with pkg.main.Impl10; + provides pkg.m2.C with pkg.m2.Impl; }""".trimIndent()) } @@ -191,7 +215,6 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { addFile("module-info.java", "module M6 { requires transitive M7; }", M6) addFile("module-info.java", "module M7 { exports pkg.m7; }", M7) addFile("pkg/m7/C7.java", "package pkg.m7;\npublic class C7 { }", M7) - highlight("test.java", """ import pkg.m2.C2; import pkg.m2.*; @@ -210,7 +233,10 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { import static pkg.m2.impl.C2Impl.make; - class C { } + class C {{ + C2Impl.make(); + pkg.m2.impl.C2Impl.make(); + }} """.trimIndent()) } @@ -226,6 +252,53 @@ class ModuleHighlightingTest : LightJava9ModulesCodeInsightFixtureTestCase() { highlight("""module M { requires M2; }""") } + fun testPackageConflicts() { + addFile("pkg/collision2/C2.java", "package pkg.collision2;\npublic class C2 { }", M2) + addFile("pkg/collision4/C4.java", "package pkg.collision4;\npublic class C4 { }", M4) + addFile("pkg/collision7/C7.java", "package pkg.collision7;\npublic class C7 { }", M7) + addFile("module-info.java", "module M2 { exports pkg.collision2; }", M2) + addFile("module-info.java", "module M4 { exports pkg.collision4 to M88; }", M4) + addFile("module-info.java", "module M6 { requires transitive M7; }", M6) + addFile("module-info.java", "module M7 { exports pkg.collision7 to M6; }", M7) + addFile("module-info.java", "module M { requires M2; requires M4; requires M6; requires lib.auto; }") + highlight("test1.java", """package pkg.collision2;""") + highlight("test2.java", """package pkg.collision4;""") + highlight("test3.java", """package pkg.collision7;""") + highlight("test4.java", """package java.util;""") + highlight("test5.java", """package pkg.lib2;""") + } + + fun testClashingReads1() { + addFile("pkg/collision/C2.java", "package pkg.collision;\npublic class C2 { }", M2) + addFile("pkg/collision/C7.java", "package pkg.collision;\npublic class C7 { }", M7) + addFile("module-info.java", "module M2 { exports pkg.collision; }", M2) + addFile("module-info.java", "module M6 { requires transitive M7; }", M6) + addFile("module-info.java", "module M7 { exports pkg.collision; }", M7) + highlight(""" + module M { + requires M2; + requires M6; + }""".trimIndent()) + } + + fun testClashingReads2() { + addFile("pkg/collision/C2.java", "package pkg.collision;\npublic class C2 { }", M2) + addFile("pkg/collision/C4.java", "package pkg.collision;\npublic class C4 { }", M4) + addFile("module-info.java", "module M2 { exports pkg.collision; }", M2) + addFile("module-info.java", "module M4 { exports pkg.collision to somewhere; }", M4) + highlight("module M { requires M2; requires M4; }") + } + + fun testClashingReads3() { + addFile("pkg/lib2/C2.java", "package pkg.lib2;\npublic class C2 { }", M2) + addFile("module-info.java", "module M2 { exports pkg.lib2; }", M2) + highlight(""" + module M { + requires M2; + requires lib.auto; + }""".trimIndent()) + } + // private fun highlight(text: String) = highlight("module-info.java", text) diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java index 97a5ca1f258c..a801fc4fa87d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/impl/DaemonRespondToChangesTest.java @@ -2440,7 +2440,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { waitForDaemon(); EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_COLLAPSE_ALL_REGIONS); waitForDaemon(); - checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']"); + checkFoldingState("[FoldRegion +(25:33), placeholder='{}']"); new WriteCommandAction(myProject) { @Override @@ -2449,7 +2449,7 @@ public class DaemonRespondToChangesTest extends DaemonAnalyzerTestCase { } }.execute(); waitForDaemon(); - checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{...}']"); + checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{}']"); EditorTestUtil.executeAction(myEditor, IdeActions.ACTION_EXPAND_ALL_REGIONS); waitForDaemon(); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java index e1e7278ffb77..3827a0bda3ed 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/Diamond8HighlightingTest.java @@ -81,6 +81,10 @@ public class Diamond8HighlightingTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testDiamondInsideOverloadedThisReference() throws Exception { + doTest(); + } + private void doTest() throws Exception { doTest(BASE_PATH + "/" + getTestName(false) + ".java", false, false); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java index c8a0a70f80d0..6b5904e1573c 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/lambda/OverloadResolutionTest.java @@ -244,6 +244,10 @@ public class OverloadResolutionTest extends LightDaemonAnalyzerTestCase { doTest(); } + public void testStaticMethodInOuterClassConflictWithToString() throws Exception { + doTest(); + } + private void doTest() { doTest(true); } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EnableOptimizeImportsOnTheFlyTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EnableOptimizeImportsOnTheFlyTest.java index 83285e780ab0..4a23391e56f5 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EnableOptimizeImportsOnTheFlyTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/EnableOptimizeImportsOnTheFlyTest.java @@ -15,7 +15,7 @@ */ package com.intellij.codeInsight.daemon.quickFix; -import com.intellij.codeInsight.CodeInsightSettings; +import com.intellij.codeInsight.CodeInsightWorkspaceSettings; import com.intellij.codeInsight.intention.IntentionAction; import com.intellij.codeInspection.LocalInspectionTool; import com.intellij.codeInspection.unusedImport.UnusedImportLocalInspection; @@ -34,18 +34,11 @@ public class EnableOptimizeImportsOnTheFlyTest extends LightQuickFixParameterize @Override protected void doAction(@NotNull final ActionHint actionHint, final String testFullPath, final String testName) throws Exception { - boolean old = CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY; - - try { - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = false; - IntentionAction action = findActionAndCheck(actionHint, testFullPath); - if (action != null) { - action.invoke(getProject(), getEditor(), getFile()); - assertTrue(CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY); - } - } - finally { - CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY = old; + CodeInsightWorkspaceSettings.getInstance(ourProject).setOptimizeImportsOnTheFly(false, getTestRootDisposable()); + IntentionAction action = findActionAndCheck(actionHint, testFullPath); + if (action != null) { + action.invoke(getProject(), getEditor(), getFile()); + assertTrue(CodeInsightWorkspaceSettings.getInstance(ourProject).optimizeImportsOnTheFly); } } diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeFieldHandleSignatureFixTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeFieldHandleSignatureFixTest.kt new file mode 100644 index 000000000000..1c69e4ca11ac --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeFieldHandleSignatureFixTest.kt @@ -0,0 +1,37 @@ +/* + * 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.codeInsight.daemon.quickFix + +import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase + +/** + * @author Pavel.Dolgov + */ +class JavaLangInvokeFieldHandleSignatureFixTest : LightQuickFixParameterizedTestCase() { + + override fun getProjectDescriptor(): LightProjectDescriptor = LightCodeInsightFixtureTestCase.JAVA_9 + + override fun setUp() { + super.setUp() + enableInspectionTool(JavaLangInvokeHandleSignatureInspection()) + } + + fun test() = doAllTests() + + override fun getBasePath() = "/codeInsight/daemonCodeAnalyzer/quickFix/fieldHandle" +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt new file mode 100644 index 000000000000..6fd0f1a88839 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/JavaLangInvokeMethodHandleSignatureFixTest.kt @@ -0,0 +1,115 @@ +/* + * 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.codeInsight.daemon.quickFix + +import com.intellij.JavaTestUtil +import com.intellij.codeInsight.intention.IntentionAction +import com.intellij.codeInspection.InspectionsBundle.message +import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection +import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection.DEFAULT_SIGNATURE +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase + +/** + * @author Pavel.Dolgov + */ +class JavaLangInvokeMethodHandleSignatureFixTest : LightCodeInsightFixtureTestCase() { + + override fun getProjectDescriptor(): LightProjectDescriptor = JAVA_9 + + override fun setUp() { + super.setUp() + myFixture.enableInspections(JavaLangInvokeHandleSignatureInspection()) + } + + override fun getTestDataPath() = JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/quickFix/methodHandle" + + fun testConstructor() = doTestConstructor(INT) + fun testConstructor2() = doTestConstructor(INT) + fun testConstructor3() = doTestConstructor() + fun testConstructor4() = doTestConstructor() + + fun testGenericMethod() = doTestMethod(OBJECT, OBJECT) + fun testGenericMethod2() = doTestMethod(OBJECT, OBJECT, OBJECT_ARRAY) + fun testGenericMethod3() = doTestMethod(OBJECT, OBJECT, STRING) + fun testGenericMethod4() = doTestMethod(OBJECT, OBJECT) + + fun testStaticMethod() = doTestMethod(VOID) + fun testStaticMethod2() = doTestMethod(STRING, STRING) + fun testStaticMethod3() = doTestMethod(STRING, STRING, STRING_ARRAY) + fun testStaticMethod4() = doTestReplace("findStatic") + + fun testVirtualMethod() = doTestMethod(VOID) + fun testVirtualMethod2() = doTestMethod(STRING, STRING) + fun testVirtualMethod3() = doTestMethod(STRING, STRING, STRING_ARRAY) + fun testVirtualMethod4() = doTestReplace("findVirtual") + + + fun doTestMethod(vararg withSignature: String) = doTest(USE_METHOD, *withSignature) + fun doTestConstructor(vararg withSignature: String) = doTest(USE_CONSTRUCTOR, VOID, *withSignature) + fun doTestReplace(replacement: String) = doTest(message("inspection.handle.signature.replace.with.fix.name", replacement)) + + fun doTest(actionPrefix: String, vararg withSignature: String) { + val testName = getTestName(false) + myFixture.configureByFile("before$testName.java") + + val action = findAction(actionPrefix) + val signature = listOf(*withSignature) + + launchAction(action, signature) + myFixture.checkResultByFile("after$testName.java") + } + + private fun findAction(actionPrefix: String): IntentionAction { + val actions = myFixture.filterAvailableIntentions(actionPrefix) + if (actions.size == 1) { + return actions[0] + } + assertEquals("Too many actions", 0, actions.size) + + val familyName = when (actionPrefix) { + USE_CONSTRUCTOR -> message("inspection.handle.signature.use.constructor.fix.family.name") + USE_METHOD -> message("inspection.handle.signature.use.method.fix.family.name") + else -> { + fail("Unexpected action " + actionPrefix); "" + } + } + val familyActions = myFixture.filterAvailableIntentions(familyName) + assertEquals("Family action", 1, familyActions.size) + + return familyActions[0] + } + + private fun launchAction(action: IntentionAction, signature: List) { + myFixture.editor.putUserData(DEFAULT_SIGNATURE, signature) + try { + myFixture.launchAction(action) + } + finally { + myFixture.editor.putUserData(DEFAULT_SIGNATURE, null) + } + } + + private val VOID = "void" + private val INT = "int" + private val OBJECT = "java.lang.Object" + private val OBJECT_ARRAY = "java.lang.Object[]" + private val STRING = "java.lang.String" + private val STRING_ARRAY = "java.lang.String[]" + + private val USE_CONSTRUCTOR = "Use constructor" + private val USE_METHOD = "Use method" +} diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java index 3f4f6f80be99..8c7e147c475a 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/RemoveRedundantUncheckedSuppressionTest.java @@ -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. @@ -31,6 +31,7 @@ public class RemoveRedundantUncheckedSuppressionTest extends LightQuickFixParame final PossibleHeapPollutionVarargsInspection varargsInspection = new PossibleHeapPollutionVarargsInspection(); final UncheckedWarningLocalInspection warningLocalInspection = new UncheckedWarningLocalInspection(); final RedundantSuppressInspection inspection = new RedundantSuppressInspection(){ + @NotNull @Override protected InspectionToolWrapper[] getInspectionTools(PsiElement psiElement, @NotNull InspectionManager manager) { return new InspectionToolWrapper[]{ @@ -81,8 +82,7 @@ public class RemoveRedundantUncheckedSuppressionTest extends LightQuickFixParame } private void checkMember(PsiMember member, RedundantSuppressInspection inspection, ProblemsHolder holder) { - final ProblemDescriptor[] problemDescriptors = - (ProblemDescriptor[])inspection.checkElement(member, InspectionManager.getInstance(getProject())); + final ProblemDescriptor[] problemDescriptors = inspection.checkElement(member, InspectionManager.getInstance(getProject())); if (problemDescriptors != null) { for (ProblemDescriptor problemDescriptor : problemDescriptors) { holder.registerProblem(problemDescriptor); diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressModuleInfoInspectionsTest.java b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressModuleInfoInspectionsTest.java new file mode 100644 index 000000000000..85a09dd2fb43 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInsight/daemon/quickFix/SuppressModuleInfoInspectionsTest.java @@ -0,0 +1,52 @@ +/* + * 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.codeInsight.daemon.quickFix; + +import com.intellij.JavaTestUtil; +import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection; +import com.intellij.openapi.projectRoots.JavaSdkVersion; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase; +import org.jetbrains.annotations.NotNull; + + +public class SuppressModuleInfoInspectionsTest extends LightCodeInsightFixtureTestCase { + + public void testModuleInfo() { + // need to set jdk version here because it is not set correctly in the mock jdk which causes problems in + // com.intellij.codeInspection.JavaSuppressionUtil#canHave15Suppressions() + IdeaTestUtil.setTestVersion(JavaSdkVersion.JDK_1_9, myFixture.getModule(), getTestRootDisposable()); + final LocalInspectionTool inspection = new JavaDocReferenceInspection(); + myFixture.enableInspections(inspection); + myFixture.configureByFile("module-info.java"); + myFixture.testHighlighting(); + myFixture.launchAction(myFixture.findSingleIntention("Suppress for module declaration")); + myFixture.checkResultByFile("module-info.after.java"); + } + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/codeInsight/daemonCodeAnalyzer/quickFix/suppressModuleInfoInspections"; + } + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return JAVA_9; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy index 809a0a70c8ff..aea519878cb1 100644 --- a/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy +++ b/java/java-tests/testSrc/com/intellij/codeInsight/folding/JavaFoldingTest.groovy @@ -72,9 +72,7 @@ public class JavaFoldingTest extends LightCodeInsightFixtureTestCase { super.tearDown() } - public void testEndOfLineComments() { - myFixture.testFolding("$PathManagerEx.testDataPath/codeInsight/folding/${getTestName(false)}.java"); - } + public void testEndOfLineComments() { doTest() } public void testEditingImports() { configure """\ @@ -412,8 +410,11 @@ class Test { assertEquals('test1', myFixture.editor.selectionModel.selectedText) } - public void testCustomFolding() { - myFixture.testFolding("$PathManagerEx.testDataPath/codeInsight/folding/${getTestName(false)}.java"); + public void testCustomFolding() { doTest() } + public void testEmptyMethod() { doTest() } + + private doTest() { + myFixture.testFolding("$PathManagerEx.testDataPath/codeInsight/folding/${getTestName(false)}.java") } public void "test custom folding IDEA-122715 and IDEA-87312"() { diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java index b495359d971c..59c7d90faf7f 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTest.java @@ -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. @@ -273,6 +273,7 @@ public class DataFlowInspectionTest extends DataFlowInspectionTestCase { public void testManySequentialIfsNotComplex() { doTest(); } public void testManySequentialInstanceofsNotComplex() { doTest(); } public void testLongDisjunctionsNotComplex() { doTest(); } + public void testManyDistinctPairsNotComplex() { doTest(); } public void testWhileNotComplex() { doTest(); } public void testAssertTrueNotComplex() { doTest(); } public void testAssertThrowsAssertionError() { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java index 63519db6474a..9195edca4d6b 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowInspectionTestSuite.java @@ -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. @@ -33,6 +33,7 @@ public class DataFlowInspectionTestSuite { suite.addTestSuite(DataFlowInspectionAncientTest.class); suite.addTestSuite(ContractCheckTest.class); suite.addTestSuite(HardcodedContractsTest.class); + suite.addTestSuite(DataFlowRangeAnalysisTest.class); suite.addTestSuite(ContractInferenceFromSourceTest.class); suite.addTestSuite(NullityInferenceFromSourceTestCase.DfaInferenceTest.class); diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowRangeAnalysisTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowRangeAnalysisTest.java new file mode 100644 index 000000000000..df35fbdeda35 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/DataFlowRangeAnalysisTest.java @@ -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; + +import com.intellij.JavaTestUtil; +import com.intellij.openapi.projectRoots.Sdk; +import com.intellij.testFramework.IdeaTestUtil; +import com.intellij.testFramework.LightProjectDescriptor; +import com.intellij.testFramework.PsiTestUtil; +import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor; +import org.jetbrains.annotations.NotNull; + +/** + * @author Tagir Valeev + */ +public class DataFlowRangeAnalysisTest extends DataFlowInspectionTestCase { + private static final DefaultLightProjectDescriptor PROJECT_DESCRIPTOR = new DefaultLightProjectDescriptor() { + @Override + public Sdk getSdk() { + return PsiTestUtil.addJdkAnnotations(IdeaTestUtil.getMockJdk18()); + } + }; + + @NotNull + @Override + protected LightProjectDescriptor getProjectDescriptor() { + return PROJECT_DESCRIPTOR; + } + + @Override + protected String getTestDataPath() { + return JavaTestUtil.getJavaTestDataPath() + "/inspection/dataFlow/fixture/"; + } + + public void testLongRangeBasics() { doTest(); } + + public void testLongRangeLoop() { doTest(); } + + public void testLongRangeAnnotation() { + myFixture.addClass("package javax.annotation;\n" + + "\n" + + "public @interface Nonnegative {}"); + doTest(); + } + + public void testLongRangeKnownMethods() { + myFixture.addClass("package java.time;\n" + + "\n" + + "public interface LocalDateTime {\n" + + " int getHour();\n" + + " int getMinute();\n" + + " int getSecond();\n" + + "}"); + doTest(); + } + +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/JavaLangInvokeHandleSignatureTest.kt b/java/java-tests/testSrc/com/intellij/codeInspection/JavaLangInvokeHandleSignatureTest.kt new file mode 100644 index 000000000000..8b571101f658 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/JavaLangInvokeHandleSignatureTest.kt @@ -0,0 +1,56 @@ +/* + * 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 + +import com.intellij.JavaTestUtil +import com.intellij.codeInspection.reflectiveAccess.JavaLangInvokeHandleSignatureInspection +import com.intellij.testFramework.LightProjectDescriptor +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase + +/** + * @author Pavel.Dolgov + */ +class JavaLangInvokeHandleSignatureTest : LightCodeInsightFixtureTestCase() { + + override fun getBasePath() = JavaTestUtil.getRelativeJavaTestDataPath() + "/inspection/invokeHandleSignature" + + override fun getProjectDescriptor(): LightProjectDescriptor { + return LightCodeInsightFixtureTestCase.JAVA_9 + } + + override fun setUp() { + super.setUp() + myFixture.enableInspections(JavaLangInvokeHandleSignatureInspection()) + } + + fun testGenericMethod() = doTest() + + fun testOverloadedMethod() = doTest() + + fun testStaticMethod() = doTest() + + fun testConstructor() = doTest() + + fun testVarHandle() = doTest() + + fun testGetter() = doTest() + + fun testSetter() = doTest() + + private fun doTest() { + myFixture.testHighlighting(getTestName(false) + ".java") + } +} diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/RedundantSuppressTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/RedundantSuppressTest.java index e381d0377b1a..485c9bc010fc 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/RedundantSuppressTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/RedundantSuppressTest.java @@ -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. @@ -21,6 +21,7 @@ import com.intellij.codeInspection.ex.GlobalInspectionToolWrapper; import com.intellij.codeInspection.ex.InspectionToolWrapper; import com.intellij.codeInspection.ex.LocalInspectionToolWrapper; import com.intellij.codeInspection.i18n.I18nInspection; +import com.intellij.codeInspection.javaDoc.JavaDocReferenceInspection; import com.intellij.psi.PsiElement; import com.intellij.psi.impl.source.tree.injected.MyTestInjector; import com.intellij.testFramework.InspectionTestCase; @@ -35,12 +36,14 @@ public class RedundantSuppressTest extends InspectionTestCase { protected void setUp() throws Exception { super.setUp(); myInspectionToolWrappers = new InspectionToolWrapper[]{ + new LocalInspectionToolWrapper(new JavaDocReferenceInspection()), new LocalInspectionToolWrapper(new I18nInspection()), new LocalInspectionToolWrapper(new RawUseOfParameterizedTypeInspection()), new GlobalInspectionToolWrapper(new EmptyMethodInspection()), new GlobalInspectionToolWrapper(new UnusedDeclarationInspection())}; myWrapper = new GlobalInspectionToolWrapper(new RedundantSuppressInspection() { + @NotNull @Override protected InspectionToolWrapper[] getInspectionTools(PsiElement psiElement, @NotNull InspectionManager manager) { return myInspectionToolWrappers; @@ -55,6 +58,10 @@ public class RedundantSuppressTest extends InspectionTestCase { super.tearDown(); } + public void testModuleInfo() throws Exception { + doTest(); + } + public void testDefaultFile() throws Exception { doTest(); } diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java new file mode 100644 index 000000000000..1a31fcd04165 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/codeInspection/dataFlow/rangeSet/LongRangeSetTest.java @@ -0,0 +1,299 @@ +/* + * 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.PsiType; +import com.intellij.util.containers.HashMap; +import org.junit.Test; + +import java.util.Random; +import java.util.function.Function; + +import static com.intellij.codeInspection.dataFlow.rangeSet.LongRangeSet.*; +import static org.junit.Assert.*; + +/** + * @author Tagir Valeev + */ +public class LongRangeSetTest { + @Test + public void testToString() { + assertEquals("{}", empty().toString()); + assertEquals("{10}", point(10).toString()); + assertEquals("{10}", range(10, 10).toString()); + assertEquals("{10, 11}", range(10, 11).toString()); + assertEquals("{10..100}", range(10, 100).toString()); + } + + @Test + public void testFromType() { + assertNull(fromType(PsiType.FLOAT)); + assertNull(fromType(PsiType.NULL)); + assertEquals("{-128..127}", fromType(PsiType.BYTE).toString()); + assertEquals("{0..65535}", fromType(PsiType.CHAR).toString()); + assertEquals("{-32768..32767}", fromType(PsiType.SHORT).toString()); + assertEquals("{-2147483648..2147483647}", fromType(PsiType.INT).toString()); + assertEquals("{0..2147483647}", indexRange().toString()); + assertEquals("{-9223372036854775808..9223372036854775807}", fromType(PsiType.LONG).toString()); + } + + @Test + public void testEquals() { + assertEquals(empty(), empty()); + assertEquals(point(10), point(10)); + assertNotEquals(point(10), point(11)); + assertEquals(point(10), range(10, 10)); + assertNotEquals(point(10), range(10, 11)); + assertEquals(range(10, 11), range(10, 11)); + assertNotEquals(range(10, 11), range(10, 12)); + } + + @Test + public void testDiff() { + assertEquals(empty(), empty().subtract(point(10))); + assertEquals(point(10), point(10).subtract(empty())); + assertEquals(point(10), point(10).subtract(point(11))); + assertEquals(empty(), point(10).subtract(point(10))); + assertEquals(point(10), point(10).subtract(range(15, 20))); + assertEquals(point(10), point(10).subtract(range(-10, -5))); + assertTrue(point(10).subtract(range(10, 20)).isEmpty()); + assertTrue(point(10).subtract(range(-10, 20)).isEmpty()); + assertTrue(point(10).subtract(range(-10, 10)).isEmpty()); + + assertEquals("{0..20}", range(0, 20).subtract(range(30, Long.MAX_VALUE)).toString()); + assertEquals("{0..19}", range(0, 20).subtract(range(20, Long.MAX_VALUE)).toString()); + assertEquals("{0..18}", range(0, 20).subtract(range(19, Long.MAX_VALUE)).toString()); + assertEquals("{0}", range(0, 20).subtract(range(1, Long.MAX_VALUE)).toString()); + assertTrue(range(0, 20).subtract(range(0, Long.MAX_VALUE)).isEmpty()); + + assertEquals("{-9223372036854775808}", all().subtract(range(Long.MIN_VALUE + 1, Long.MAX_VALUE)).toString()); + assertEquals("{9223372036854775807}", all().subtract(range(Long.MIN_VALUE, Long.MAX_VALUE - 1)).toString()); + assertTrue(all().subtract(range(Long.MIN_VALUE, Long.MAX_VALUE)).isEmpty()); + assertEquals(indexRange(), fromType(PsiType.INT).subtract(range(Long.MIN_VALUE, (long)-1))); + assertTrue(all().subtract(all()).isEmpty()); + } + + @Test + public void testSets() { + assertEquals("{0..9, 11..20}", range(0, 20).without(10).toString()); + assertEquals("{0, 20}", range(0, 20).subtract(range(1, 19)).toString()); + assertEquals("{0, 1, 19, 20}", range(0, 20).subtract(range(2, 18)).toString()); + + assertEquals("{0..9, 12..20}", range(0, 20).without(10).without(11).toString()); + assertEquals("{0..9, 12..14, 16..20}", range(0, 20).without(10).without(11).without(15).toString()); + assertEquals("{0, 4..20}", range(0, 20).without(3).without(2).without(1).toString()); + assertEquals("{4..20}", range(0, 20).without(3).without(2).without(1).without(0).toString()); + + assertEquals("{0..2, 5..15, 19, 20}", + range(0, 20).subtract(range(3, 18).subtract(range(5, 15))).toString()); + + LongRangeSet first = fromType(PsiType.CHAR).without(45); + LongRangeSet second = + fromType(PsiType.CHAR).without(32).without(40).without(44).without(45).without(46).without(58).without(59).without(61); + assertEquals("{0..44, 46..65535}", first.toString()); + assertEquals("{0..31, 33..39, 41..43, 47..57, 60, 62..65535}", second.toString()); + assertEquals("{32, 40, 44, 46, 58, 59, 61}", first.subtract(second).toString()); + } + + @Test + public void testHash() { + HashMap map = new HashMap<>(); + map.put(empty(), "empty"); + map.put(point(10), "10"); + map.put(range(10, 10), "10-10"); + map.put(range(10, 11), "10-11"); + map.put(range(10, 12), "10-12"); + LongRangeSet longNotChar = fromType(PsiType.LONG).subtract(fromType(PsiType.CHAR)); + map.put(longNotChar, "longNotChar"); + + assertEquals("empty", map.get(empty())); + assertEquals("10-10", map.get(point(10))); + assertEquals("10-11", map.get(range(10, 11))); + assertEquals("10-12", map.get(range(10, 12))); + assertNull(map.get(range(11, 11))); + assertEquals("longNotChar", map.get(fromType(PsiType.LONG).subtract(fromType(PsiType.CHAR)))); + } + + @Test + public void testIntersects() { + assertFalse(empty().intersects(fromType(PsiType.LONG))); + assertTrue(point(Long.MIN_VALUE).intersects(fromType(PsiType.LONG))); + assertFalse(point(10).intersects(point(11))); + assertTrue(point(10).intersects(point(10))); + + assertTrue(range(10, 100).intersects(point(10))); + assertTrue(range(10, 100).intersects(point(100))); + assertFalse(range(10, 100).intersects(point(101))); + assertFalse(range(10, 100).intersects(point(9))); + + LongRangeSet range1020 = range(10, 20); + assertTrue(range1020.intersects(range1020)); + assertTrue(range1020.intersects(range(10, 30))); + assertTrue(range1020.intersects(range(20, 30))); + assertTrue(range1020.intersects(range(0, 30))); + assertTrue(range1020.intersects(range(0, 10))); + assertTrue(range1020.intersects(range(0, 20))); + + assertFalse(range1020.intersects(range(0, 9))); + assertFalse(range1020.intersects(range(21, 30))); + + LongRangeSet rangeSet = range1020.subtract(range(12, 13)).subtract(range(17, 18)); + assertFalse(rangeSet.intersects(point(12))); + assertFalse(point(12).intersects(rangeSet)); + assertFalse(rangeSet.intersects(empty())); + assertFalse(rangeSet.intersects(range(12, 13))); + assertFalse(range(12, 13).intersects(rangeSet)); + assertFalse(rangeSet.intersects(range(0, 9))); + assertFalse(rangeSet.intersects(range(21, 30))); + assertTrue(rangeSet.intersects(rangeSet)); + assertTrue(rangeSet.intersects(range1020)); + assertTrue(rangeSet.intersects(point(11))); + + LongRangeSet rangeSet2 = range1020.subtract(rangeSet); + assertEquals("{12, 13, 17, 18}", rangeSet2.toString()); + assertFalse(rangeSet.intersects(rangeSet2)); + } + + @Test + public void testIntersect() { + assertEquals("{0..100}", range(0, 100).intersect(range(0, 100)).toString()); + assertEquals("{100}", range(0, 100).intersect(range(100, 200)).toString()); + assertTrue(range(0, 100).intersect(range(101, 200)).isEmpty()); + assertTrue(point(100).intersect(point(200)).isEmpty()); + assertFalse(point(100).intersect(range(99, 101)).isEmpty()); + + LongRangeSet rangeSet = range(-1000, 1000).subtract(range(100, 500)).subtract(range(-500, -100)); + assertEquals("{-1000..-501, -99..99, 501..1000}", rangeSet.toString()); + assertEquals(point(99), rangeSet.intersect(point(99))); + assertTrue(rangeSet.intersect(point(100)).isEmpty()); + assertEquals("{0..99, 501..1000}", rangeSet.intersect(indexRange()).toString()); + } + + @Test + public void testIntersectSubtractRandomized() { + Random r = new Random(1); + LongRangeSet[] data = r.ints(1000, 0, 1000) + .mapToObj(x -> range(x, x + r.nextInt((x % 20) * 100 + 1))).toArray(LongRangeSet[]::new); + for (int i = 0; i < 2000; i++) { + int idx = r.nextInt(data.length); + LongRangeSet left = data[idx]; + LongRangeSet right = data[r.nextInt(data.length)]; + LongRangeSet lDiff = left.subtract(right); + LongRangeSet rDiff = right.subtract(left); + LongRangeSet intersection = left.intersect(right); + String message = left + " & " + right + " = " + intersection; + assertEquals(message, intersection, right.intersect(left)); + if (!intersection.isEmpty()) { + assertTrue(message, intersection.min() >= Math.max(left.min(), right.min())); + assertTrue(message, intersection.max() <= Math.min(left.max(), right.max())); + } + assertEquals(message, intersection, right.subtract(fromType(PsiType.LONG).subtract(left))); + assertEquals(message, intersection, left.subtract(fromType(PsiType.LONG).subtract(right))); + intersection.stream().limit(1000).forEach(e -> { + assertTrue(left.contains(e)); + assertTrue(right.contains(e)); + }); + lDiff.stream().limit(1000).forEach(e -> { + assertTrue(left.contains(e)); + assertFalse(right.contains(e)); + }); + rDiff.stream().limit(1000).forEach(e -> { + assertFalse(left.contains(e)); + assertTrue(right.contains(e)); + }); + switch (r.nextInt(3)) { + case 0: + data[idx] = lDiff; + break; + case 1: + data[idx] = rDiff; + break; + case 2: + data[idx] = intersection; + break; + } + } + } + + @Test + public void testFromConstant() { + assertEquals("{0}", fromConstant(0).toString()); + assertEquals("{0}", fromConstant(0L).toString()); + assertEquals("{1}", fromConstant((byte)1).toString()); + assertEquals("{97}", fromConstant('a').toString()); + assertNull(fromConstant(null)); + assertNull(fromConstant(1.0)); + } + + @Test + public void testFromRelation() { + assertEquals(range(101, Long.MAX_VALUE), range(100, 200).fromRelation(JavaTokenType.GT)); + assertEquals(range(100, Long.MAX_VALUE), range(100, 200).fromRelation(JavaTokenType.GE)); + assertEquals(range(Long.MIN_VALUE, 199), range(100, 200).fromRelation(JavaTokenType.LT)); + assertEquals(range(Long.MIN_VALUE, 200), range(100, 200).fromRelation(JavaTokenType.LE)); + assertEquals(range(100, 200), range(100, 200).fromRelation(JavaTokenType.EQEQ)); + assertNull(range(100, 200).fromRelation(JavaTokenType.EQ)); + assertEquals(fromType(PsiType.LONG), range(100, 200).fromRelation(JavaTokenType.NE)); + assertEquals("{-9223372036854775808..99, 101..9223372036854775807}", point(100).fromRelation(JavaTokenType.NE).toString()); + } + + @Test + public void testAbs() { + assertTrue(empty().abs(true).isEmpty()); + assertEquals(point(Long.MAX_VALUE), point(Long.MIN_VALUE + 1).abs(true)); + assertEquals(point(Long.MIN_VALUE), point(Long.MIN_VALUE).abs(true)); + assertEquals(point(Integer.MIN_VALUE), point(Integer.MIN_VALUE).abs(false)); + assertEquals(point(Integer.MAX_VALUE + 1L), point(Integer.MIN_VALUE).abs(true)); + assertEquals(range(100, 200), range(100, 200).abs(true)); + assertEquals(range(0, 200), range(-1, 200).abs(true)); + assertEquals(range(0, 200), range(-200, 200).abs(false)); + assertEquals(range(0, 201), range(-201, 200).abs(false)); + assertEquals(range(0, Long.MAX_VALUE).union(point(Long.MIN_VALUE)), all().abs(true)); + assertEquals(range(100, Integer.MAX_VALUE).union(point(Integer.MIN_VALUE)), range(Integer.MIN_VALUE, -100).abs(false)); + assertEquals(range(100, Integer.MAX_VALUE + 1L), range(Integer.MIN_VALUE, -100).abs(true)); + LongRangeSet set = range(-900, 1000).subtract(range(-800, -600)).subtract(range(-300, 100)).subtract(range(500, 700)); + assertEquals("{-900..-801, -599..-301, 101..499, 701..1000}", set.toString()); + assertEquals("{101..599, 701..1000}", set.abs(false).toString()); + } + + @Test + public void testBitwiseAnd() { + assertTrue(empty().bitwiseAnd(all()).isEmpty()); + assertTrue(all().bitwiseAnd(empty()).isEmpty()); + assertEquals(all(), all().bitwiseAnd(all())); + assertEquals("{0, 16}", all().bitwiseAnd(point(16)).toString()); + assertEquals("{0, 1}", all().bitwiseAnd(point(1)).toString()); + assertEquals(range(0, 24), all().bitwiseAnd(point(24))); + assertEquals(range(0, 31), all().bitwiseAnd(point(25))); + assertEquals(range(0, 15), all().bitwiseAnd(range(10, 15))); + assertEquals(range(0, 31), all().bitwiseAnd(range(16, 24))); + + checkBitwiseAnd(range(0, 3), range(4, 7), "{0..3}"); + checkBitwiseAnd(range(3, 4), range(3, 4), "{0..7}"); // 0,3,4,7 actually + checkBitwiseAnd(range(-20, 20), point(8), "{0, 8}"); + checkBitwiseAnd(point(3).union(point(5)), point(3).union(point(5)), "{1, 3, 5}"); + checkBitwiseAnd(range(-10, 10), range(-20, 5), "{-32..15}"); + checkBitwiseAnd(range(-30, -20).union(range(20, 33)), point(-10).union(point(10)), "{-32..-26, 0..62}"); + } + + void checkBitwiseAnd(LongRangeSet range1, LongRangeSet range2, String expected) { + LongRangeSet result = range1.bitwiseAnd(range2); + assertEquals(expected, result.toString()); + assertTrue( + range1.stream().mapToObj(l1 -> range2.stream().map(l2 -> l1 & l2)).flatMapToLong(Function.identity()).allMatch(result::contains)); + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspectionTest.java b/java/java-tests/testSrc/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspectionTest.java index 488e25ebcfb2..4bf50a0f8b9d 100644 --- a/java/java-tests/testSrc/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspectionTest.java +++ b/java/java-tests/testSrc/com/intellij/codeInspection/java19api/Java9CollectionFactoryInspectionTest.java @@ -17,7 +17,9 @@ package com.intellij.codeInspection.java19api; import com.intellij.codeInsight.daemon.quickFix.LightQuickFixParameterizedTestCase; import com.intellij.codeInspection.LocalInspectionTool; +import com.intellij.openapi.projectRoots.Sdk; import com.intellij.pom.java.LanguageLevel; +import com.intellij.testFramework.IdeaTestUtil; import org.jetbrains.annotations.NotNull; /** @@ -35,6 +37,11 @@ public class Java9CollectionFactoryInspectionTest extends LightQuickFixParameter return new LocalInspectionTool[]{new Java9CollectionFactoryInspection()}; } + @Override + protected Sdk getProjectJDK() { + return IdeaTestUtil.getMockJdk9(); + } + public void test() throws Exception { doAllTests(); } diff --git a/java/java-tests/testSrc/com/intellij/execution/actions/ContextConfigurationTest.java b/java/java-tests/testSrc/com/intellij/execution/actions/ContextConfigurationTest.java index 7571df4b611b..7875fd1bb362 100644 --- a/java/java-tests/testSrc/com/intellij/execution/actions/ContextConfigurationTest.java +++ b/java/java-tests/testSrc/com/intellij/execution/actions/ContextConfigurationTest.java @@ -1,15 +1,20 @@ package com.intellij.execution.actions; import com.intellij.execution.BaseConfigurationTestCase; +import com.intellij.execution.Location; import com.intellij.execution.RunManagerEx; import com.intellij.execution.RunnerAndConfigurationSettings; import com.intellij.execution.application.ApplicationConfiguration; import com.intellij.execution.junit.*; +import com.intellij.execution.junit2.info.MethodLocation; import com.intellij.execution.testframework.TestSearchScope; +import com.intellij.openapi.actionSystem.CommonDataKeys; import com.intellij.openapi.actionSystem.DataConstants; +import com.intellij.openapi.actionSystem.LangDataKeys; import com.intellij.openapi.actionSystem.ex.DataConstantsEx; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleUtil; +import com.intellij.openapi.module.ModuleUtilCore; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiClass; import com.intellij.psi.PsiMethod; @@ -47,6 +52,30 @@ public class ContextConfigurationTest extends BaseConfigurationTestCase { checkGeneretedName(configuration, shortName + "." + METHOD_NAME); } + public void testMethodInAbstractJUnit3TestCase() throws Exception { + String packageName = "abstractTests"; + String shortName = "AbstractTestImpl1"; + String qualifiedName = StringUtil.getQualifiedName(packageName, shortName); + PsiClass psiClass = findClass(getModule1(), qualifiedName); + PsiMethod testMethod = psiClass.findMethodsByName(METHOD_NAME, true)[0]; + + MapDataContext dataContext = new MapDataContext(); + dataContext.put(CommonDataKeys.PROJECT, myProject); + if (LangDataKeys.MODULE.getData(dataContext) == null) { + dataContext.put(LangDataKeys.MODULE, ModuleUtilCore.findModuleForPsiElement(testMethod)); + } + dataContext.put(Location.DATA_KEY, MethodLocation.elementInClass(testMethod, psiClass)); + + ConfigurationContext context = ConfigurationContext.getFromContext(dataContext); + RunnerAndConfigurationSettings settings = context.getConfiguration(); + JUnitConfiguration configuration = (JUnitConfiguration)settings.getConfiguration(); + + checkClassName(qualifiedName, configuration); + checkMethodName(METHOD_NAME, configuration); + checkPackage(packageName, configuration); + checkGeneretedName(configuration, shortName + "." + METHOD_NAME); + } + public void testJUnitMethodTest() { PsiClass psiClass = findClass(getModule1(), CLASS_NAME); PsiMethod testMethod = psiClass.findMethodsByName(METHOD_NAME, false)[0]; diff --git a/java/java-tests/testSrc/com/intellij/find/impl/JavaReadWriteAccessTest.kt b/java/java-tests/testSrc/com/intellij/find/impl/JavaReadWriteAccessTest.kt new file mode 100644 index 000000000000..d3891136fc96 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/find/impl/JavaReadWriteAccessTest.kt @@ -0,0 +1,67 @@ +/* + * 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.find.impl + +import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector +import com.intellij.psi.PsiFile +import com.intellij.psi.PsiMethodCallExpression +import com.intellij.psi.PsiNameValuePair +import com.intellij.psi.util.PsiTreeUtil +import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase + +class JavaReadWriteAccessTest : LightCodeInsightFixtureTestCase() { + fun testWriteAnnotationsMethodExplicitCall() { + val anno = myFixture.addClass("@interface Anno {String value() default \"\";}") + val detector = ReadWriteAccessDetector.findDetector(anno.methods[0]) + assertNotNull(detector) + + val file: PsiFile = myFixture.configureByText("A.java", "@Anno(value = \"foo\") class A {}") + val element = file.findElementAt(myFixture.editor.caretModel.offset) + assertNotNull(element) + val expressionAccess = detector!!.getExpressionAccess(element!!) + assertEquals(ReadWriteAccessDetector.Access.Write, expressionAccess) + } + + fun testWriteAnnotationsMethodImplicitCall() { + val anno = myFixture.addClass("@interface Anno {String value() default \"\";}") + val detector = ReadWriteAccessDetector.findDetector(anno.methods[0]) + assertNotNull(detector) + + val file: PsiFile = myFixture.configureByText("A.java", "@Anno(\"foo\") class A {}") + val element = file.findElementAt(myFixture.editor.caretModel.offset) + assertNotNull(element) + val expressionAccess = detector!!.getExpressionAccess(PsiTreeUtil.getParentOfType(element, PsiNameValuePair::class.java, true)!!) + assertEquals(ReadWriteAccessDetector.Access.Write, expressionAccess) + } + + fun testExplicitCallToAnnotationMethod() { + val anno = myFixture.addClass("@interface Anno {String value() default \"\";}") + val detector = ReadWriteAccessDetector.findDetector(anno.methods[0]) + assertNotNull(detector) + + val file: PsiFile = myFixture.configureByText("A.java", "import java.lang.reflect.Field;" + + "class A { " + + " void m(Field field) { " + + " Anno a = field.getAnnotation(Anno.class);" + + " System.out.println(a.value());" + + " }" + + "}") + val element = file.findElementAt(myFixture.editor.caretModel.offset) + assertNotNull(element) + val expressionAccess = detector!!.getExpressionAccess(PsiTreeUtil.getParentOfType(element, PsiMethodCallExpression::class.java, true)!!) + assertEquals(ReadWriteAccessDetector.Access.Read, expressionAccess) + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/openapi/editor/impl/FoldingProcessingOnDocumentModificationTest.java b/java/java-tests/testSrc/com/intellij/openapi/editor/impl/FoldingProcessingOnDocumentModificationTest.java index cfe3f4d81a9a..6fda57c481b0 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/editor/impl/FoldingProcessingOnDocumentModificationTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/editor/impl/FoldingProcessingOnDocumentModificationTest.java @@ -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. @@ -80,7 +80,7 @@ public class FoldingProcessingOnDocumentModificationTest extends AbstractEditorT " }\n" + "}"); executeAction(IdeActions.ACTION_COLLAPSE_ALL_REGIONS); - checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']"); + checkFoldingState("[FoldRegion +(25:33), placeholder='{}']"); new WriteCommandAction.Simple(getProject()) { @Override @@ -89,12 +89,12 @@ public class FoldingProcessingOnDocumentModificationTest extends AbstractEditorT } }.execute().throwException(); - checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{...}']"); + checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{}']"); WriteCommandAction.runWriteCommandAction(getProject(), () -> myEditor.getDocument().deleteString(0, 2)); - checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']"); + checkFoldingState("[FoldRegion +(25:33), placeholder='{}']"); } public void testInvalidRegionIsRemovedOnExpanding() throws Exception { @@ -104,7 +104,7 @@ public class FoldingProcessingOnDocumentModificationTest extends AbstractEditorT " }\n" + "}"); executeAction(IdeActions.ACTION_COLLAPSE_ALL_REGIONS); - checkFoldingState("[FoldRegion +(25:33), placeholder='{...}']"); + checkFoldingState("[FoldRegion +(25:33), placeholder='{}']"); new WriteCommandAction.Simple(getProject()) { @Override @@ -113,7 +113,7 @@ public class FoldingProcessingOnDocumentModificationTest extends AbstractEditorT } }.execute().throwException(); - checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{...}']"); + checkFoldingState("[FoldRegion -(0:37), placeholder='/.../', FoldRegion +(27:35), placeholder='{}']"); executeAction(IdeActions.ACTION_EXPAND_ALL_REGIONS); checkFoldingState("[FoldRegion -(0:37), placeholder='/.../']"); diff --git a/java/java-tests/testSrc/com/intellij/openapi/vfs/JrtFileSystemTest.java b/java/java-tests/testSrc/com/intellij/openapi/vfs/JrtFileSystemTest.java index f800828d078f..0b4b460e06bf 100644 --- a/java/java-tests/testSrc/com/intellij/openapi/vfs/JrtFileSystemTest.java +++ b/java/java-tests/testSrc/com/intellij/openapi/vfs/JrtFileSystemTest.java @@ -17,6 +17,8 @@ package com.intellij.openapi.vfs; import com.intellij.JavaTestUtil; import com.intellij.openapi.vfs.jrt.JrtFileSystem; +import com.intellij.openapi.vfs.newvfs.events.VFileEvent; +import com.intellij.testFramework.VfsTestUtil; import com.intellij.testFramework.fixtures.BareTestFixtureTestCase; import com.intellij.testFramework.rules.TempDirectory; import org.junit.Before; @@ -93,9 +95,9 @@ public class JrtFileSystemTest extends BareTestFixtureTestCase { VirtualFile local = LocalFileSystem.getInstance().findFileByIoFile(myTempDir.getRoot()); assertThat(local).isNotNull(); - local.refresh(false, true); + List events = VfsTestUtil.getEvents(() -> local.refresh(false, true)); - assertThat(childNames(myRoot)).containsExactlyInAnyOrder("java.base", "test.a", "test.b"); + assertThat(childNames(myRoot)).describedAs("events=" + events).containsExactlyInAnyOrder("java.base", "test.a", "test.b"); } private static List childNames(VirtualFile dir) { diff --git a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java index c27aa2c38052..9344bd5fd6ed 100644 --- a/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java +++ b/java/java-tests/testSrc/com/intellij/projectView/NavigateFromSourceTest.java @@ -31,6 +31,7 @@ import com.intellij.openapi.wm.ToolWindowId; import com.intellij.openapi.wm.ToolWindowManager; import com.intellij.psi.*; import com.intellij.testFramework.PlatformTestUtil; +import com.intellij.util.ui.UIUtil; import javax.swing.*; import java.io.IOException; @@ -99,6 +100,7 @@ public class NavigateFromSourceTest extends BaseProjectViewTestCase { new ProjectViewToolWindowFactory().createToolWindowContent(getProject(), toolWindow); projectView.changeView(ProjectViewPane.ID); + UIUtil.dispatchAllInvocationEvents(); JComponent component = ((ProjectViewImpl)projectView).getComponent(); DataContext context = DataManager.getInstance().getDataContext(component); diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java index 8009f6a4636f..27c2bceb8e5b 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/smartPointers/SmartPsiElementPointersTest.java @@ -970,4 +970,16 @@ public class SmartPsiElementPointersTest extends CodeInsightTestCase { assertFalse(((PsiFileImpl)psiFile).isContentsLoaded()); } + public void testDoubleRemoveIsAnError() throws Exception { + SmartPointerEx pointer = createPointer(createFile("a.java", "class A {}")); + getPointerManager().removePointer(pointer); + try { + getPointerManager().removePointer(pointer); + fail("Should have failed"); + } + catch (AssertionError e) { + assertTrue(e.getMessage(), e.getMessage().contains("Double smart pointer removal")); + } + } + } diff --git a/java/java-tests/testSrc/com/intellij/psi/impl/source/tree/java/ShortenClassReferencesTest.java b/java/java-tests/testSrc/com/intellij/psi/impl/source/tree/java/ShortenClassReferencesTest.java index df9b57d43c2e..10e0d758208b 100644 --- a/java/java-tests/testSrc/com/intellij/psi/impl/source/tree/java/ShortenClassReferencesTest.java +++ b/java/java-tests/testSrc/com/intellij/psi/impl/source/tree/java/ShortenClassReferencesTest.java @@ -81,6 +81,18 @@ public class ShortenClassReferencesTest extends LightCodeInsightFixtureTestCase "class Outer1 {class Inner {} {boolean b = new Inner() instanceof Inner;}}"); } + public void testConflictingClassInSamePackage() throws Exception { + myFixture.addClass("package p1; public class Outer{}"); + myFixture.addClass("package p2; public class Outer{}"); + myFixture.configureByText("a.java", "package p2; class Outer1 extends p1.Outer {}"); + doShortenRefs(); + myFixture.checkResult("package p2;\n" + + "\n" + + "import p1.Outer;\n" + + "\n" + + "class Outer1 extends Outer {}"); + } + public void testWhiteSpaceForMovedTypeAnnotations() throws Exception { myFixture.configureByFile(getTestName(false) + ".java"); PsiElement elementAtCaret = myFixture.getElementAtCaret(); diff --git a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineMethodTest.java b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineMethodTest.java index a0aa03ef047a..488995d7b5fb 100644 --- a/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineMethodTest.java +++ b/java/java-tests/testSrc/com/intellij/refactoring/inline/InlineMethodTest.java @@ -145,6 +145,10 @@ public class InlineMethodTest extends LightRefactoringTestCase { doTest(); } + public void testEnumConstantConstructorWithArgs() throws Exception { + doTest(); + } + public void testConstantInChainingConstructor() throws Exception { // IDEADEV-28136 doTest(); } @@ -231,6 +235,10 @@ public class InlineMethodTest extends LightRefactoringTestCase { doTestConflict("Inline cannot be applied to multiline method in constructor call"); } + public void testMethodReferenceInsideMethodCall() throws Exception { + doTest(); + } + private void doTestConflict(final String conflict) throws Exception { try { doTest(); diff --git a/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java b/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java index 644afe7d6991..ae7d5b688274 100644 --- a/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java +++ b/java/java-tests/testSrc/com/intellij/roots/RootsChangedTest.java @@ -102,7 +102,7 @@ public class RootsChangedTest extends ModuleTestCase { assertNotNull(vSubdir); move(vDir2, vSubdir); - assertEventsCount(0); + assertEventsCount(1); assertSameElements(ModuleRootManager.getInstance(moduleA).getContentRoots(), vDir2); } diff --git a/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java b/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java index 44cecdbfd029..7321d5e7bd74 100644 --- a/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java +++ b/java/java-tests/testSrc/com/intellij/slicer/SliceBackwardTest.java @@ -81,4 +81,7 @@ public class SliceBackwardTest extends SliceTestCase { public void testListTrackToArray() throws Exception { doTest();} public void testTryCatchFinally() throws Exception { doTest();} + public void testFinalVarAssignedBeforePassingToAnonymous() throws Exception { doTest();} + public void testLocalVarDeclarationAndAssignment() throws Exception { doTest();} + public void testSearchOverriddenMethodsInThisClassHierarchy() throws Exception { doTest();} } diff --git a/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java b/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java index e8bccf44123e..bb0be5dd01f1 100644 --- a/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java +++ b/java/java-tests/testSrc/com/intellij/slicer/SliceTreeTest.java @@ -17,7 +17,6 @@ package com.intellij.slicer; import com.intellij.analysis.AnalysisScope; import com.intellij.codeInsight.daemon.impl.HighlightInfo; -import com.intellij.ide.util.treeView.AbstractTreeNode; import com.intellij.openapi.util.Disposer; import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.wm.impl.ToolWindowHeadlessManagerImpl; @@ -80,9 +79,9 @@ public class SliceTreeTest extends SliceTestCase { node.update(); node.calculateDupNode(); to.add(node); - Collection nodes = node.getChildren(); - for (AbstractTreeNode child : nodes) { - expandNodesTo((SliceNode)child, to); + Collection nodes = node.getChildren(); + for (SliceNode child : nodes) { + expandNodesTo(child, to); } } @@ -172,39 +171,34 @@ public class SliceTreeTest extends SliceTestCase { assertEquals("\"oo\"", leaf.getText()); SliceRootNode newRoot = SliceLeafAnalyzer.createTreeGroupedByValues(leaves, root, map); - Collection children = newRoot.getChildren(); + Collection children = newRoot.getChildren(); assertEquals(1, children.size()); - SliceNode child = (SliceNode)children.iterator().next(); + SliceNode child = children.iterator().next(); assertTrue(child instanceof SliceLeafValueRootNode); children = child.getChildren(); assertEquals(1, children.size()); - child = (SliceNode)children.iterator().next(); + child = children.iterator().next(); assertTrue(child.getValue().getElement() instanceof PsiField); children = child.getChildren(); - assertEquals(1, children.size()); - child = (SliceNode)children.iterator().next(); + child = assertOneElement(children); assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression); children = child.getChildren(); - assertEquals(1, children.size()); - child = (SliceNode)children.iterator().next(); + child = assertOneElement(children); assertTrue(child.getValue().getElement() instanceof PsiParameter); children = child.getChildren(); - assertEquals(1, children.size()); - child = (SliceNode)children.iterator().next(); + child = assertOneElement(children); assertTrue(child.getValue().getElement() instanceof PsiReferenceExpression); children = child.getChildren(); - assertEquals(1, children.size()); - child = (SliceNode)children.iterator().next(); + child = assertOneElement(children); assertTrue(child.getValue().getElement() instanceof PsiParameter); children = child.getChildren(); - assertEquals(1, children.size()); - child = (SliceNode)children.iterator().next(); + child = assertOneElement(children); assertTrue(child.getValue().getElement() instanceof PsiLiteralExpression); assertEquals(child.getValue().getElement(), leaf); } @@ -298,7 +292,7 @@ public class SliceTreeTest extends SliceTestCase { } private static void checkStructure(final SliceNode root, @NonNls String dataExpected) { - List actualNodes = new ArrayList<>((Collection)root.getChildren()); + List actualNodes = new ArrayList<>(root.getChildren()); Collections.sort(actualNodes, SliceTreeBuilder.SLICE_NODE_COMPARATOR); Object[] actualStrings = ContainerUtil.map2Array(actualNodes, SliceNode::toString); diff --git a/java/java-tests/testSrc/com/intellij/testFramework/fixtures/MultiModuleJava9ProjectDescriptor.kt b/java/java-tests/testSrc/com/intellij/testFramework/fixtures/MultiModuleJava9ProjectDescriptor.kt index db6919c98268..d19515e2352c 100644 --- a/java/java-tests/testSrc/com/intellij/testFramework/fixtures/MultiModuleJava9ProjectDescriptor.kt +++ b/java/java-tests/testSrc/com/intellij/testFramework/fixtures/MultiModuleJava9ProjectDescriptor.kt @@ -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. @@ -46,7 +46,7 @@ object MultiModuleJava9ProjectDescriptor : DefaultLightProjectDescriptor() { if (this == MAIN) LightPlatformTestCase.getSourceRoot() else TempFileSystem.getInstance().findFileByPath("/$rootName")!! } - override fun getSdk(): Sdk = IdeaTestUtil.getMockJdk18() + override fun getSdk(): Sdk = IdeaTestUtil.getMockJdk9() override fun setUpProject(project: Project, handler: SetupHandler) { super.setUpProject(project, handler) diff --git a/java/mockJDK-1.8/jre/lib/rt.jar b/java/mockJDK-1.8/jre/lib/rt.jar index 13a2d8bd9b93..49d89d4a2ef8 100644 Binary files a/java/mockJDK-1.8/jre/lib/rt.jar and b/java/mockJDK-1.8/jre/lib/rt.jar differ diff --git a/java/remote-servers/impl/src/com/intellij/remoteServer/impl/module/CloudModuleBuilder.java b/java/remote-servers/impl/src/com/intellij/remoteServer/impl/module/CloudModuleBuilder.java index ccdd58df54d6..17bc78da557c 100644 --- a/java/remote-servers/impl/src/com/intellij/remoteServer/impl/module/CloudModuleBuilder.java +++ b/java/remote-servers/impl/src/com/intellij/remoteServer/impl/module/CloudModuleBuilder.java @@ -73,11 +73,6 @@ public class CloudModuleBuilder extends JavaModuleBuilder { return getClass().getName(); } - @Override - public Icon getBigIcon() { - return AllIcons.General.Balloon; - } - @Override public Icon getNodeIcon() { return AllIcons.General.Balloon; diff --git a/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java b/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java index af857da3cc82..f56fc0970fd5 100644 --- a/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java +++ b/java/testFramework/src/com/intellij/debugger/DebuggerTestCase.java @@ -101,7 +101,8 @@ public abstract class DebuggerTestCase extends ExecutionWithDebuggerToolsTestCas myDebuggerSession = null; } - if (getChecker().contains("JVMTI_ERROR_WRONG_PHASE(112)")) { + // disabled, see JRE-253 + if (false && getChecker().contains("JVMTI_ERROR_WRONG_PHASE(112)")) { myRestart.incrementAndGet(); if (needsRestart()) { return; diff --git a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java index 9a312342d794..5b81ecce040b 100644 --- a/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java +++ b/java/typeMigration/src/com/intellij/refactoring/typeMigration/inspections/GuavaInspection.java @@ -18,7 +18,6 @@ package com.intellij.refactoring.typeMigration.inspections; import com.intellij.codeInsight.FileModificationService; import com.intellij.codeInspection.*; import com.intellij.codeInspection.ui.MultipleCheckboxOptionsPanel; -import com.intellij.openapi.command.undo.UndoUtil; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.Editor; import com.intellij.openapi.project.Project; @@ -34,7 +33,6 @@ import com.intellij.refactoring.typeMigration.TypeMigrationRules; import com.intellij.refactoring.typeMigration.rules.TypeConversionRule; import com.intellij.refactoring.typeMigration.rules.guava.*; import com.intellij.util.Function; -import com.intellij.util.IncorrectOperationException; import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.hash.HashMap; import org.jetbrains.annotations.Nls; @@ -349,34 +347,18 @@ public class GuavaInspection extends BaseJavaLocalInspectionTool { } private void performTypeMigration(List elements, List types) { - PsiFile containingFile = null; - for (PsiElement element : elements) { - final PsiFile currentContainingFile = element.getContainingFile(); - if (containingFile == null) { - containingFile = currentContainingFile; - } - else { - LOG.assertTrue(containingFile.isEquivalentTo(currentContainingFile)); - } - } - LOG.assertTrue(containingFile != null); - if (!FileModificationService.getInstance().prepareFileForWrite(containingFile)) return; - try { - final TypeMigrationRules rules = new TypeMigrationRules(); - rules.setBoundScope(GlobalSearchScopesCore.projectProductionScope(containingFile.getProject()) - .union(GlobalSearchScopesCore.projectTestScope(containingFile.getProject()))); - rules.addConversionRuleSettings(new GuavaConversionSettings(ignoreJavaxNullable)); - TypeMigrationProcessor.runHighlightingTypeMigration(containingFile.getProject(), - null, - rules, - elements.toArray(new PsiElement[elements.size()]), - createMigrationTypeFunction(elements, types), - true); - UndoUtil.markPsiFileForUndo(containingFile); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } + if (!FileModificationService.getInstance().preparePsiElementsForWrite(elements)) return; + final Project project = elements.get(0).getProject(); + final TypeMigrationRules rules = new TypeMigrationRules(); + rules.setBoundScope(GlobalSearchScopesCore.projectProductionScope(project) + .union(GlobalSearchScopesCore.projectTestScope(project))); + rules.addConversionRuleSettings(new GuavaConversionSettings(ignoreJavaxNullable)); + TypeMigrationProcessor.runHighlightingTypeMigration(project, + null, + rules, + elements.toArray(new PsiElement[elements.size()]), + createMigrationTypeFunction(elements, types), + true); } private Function createMigrationTypeFunction(@NotNull final List elements, diff --git a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java index ad7548e1e444..9c8145e51faf 100644 --- a/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java +++ b/java/typeMigration/test/com/intellij/refactoring/TypeMigrationTest.java @@ -862,6 +862,10 @@ public class TypeMigrationTest extends TypeMigrationTestBase { doTestMethodType("migrationMethod", PsiType.VOID); } + public void testGenericEllipsis() { + doTestFieldType("migrationField", myJavaFacade.getElementFactory().createTypeFromText("Test", null)); + } + private void doTestReturnType(final String methodName, final String migrationType) { start(new RulesProvider() { @Override diff --git a/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/Test.items b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/Test.items new file mode 100644 index 000000000000..a00e0a47d582 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/Test.items @@ -0,0 +1,10 @@ +Types: +PsiField:migrationField : Test +PsiMethodCallExpression:migrationField.method() : void +PsiReferenceExpression:migrationField : Test + +Conversions: +migrationField.method() -> $ + +New expression type changes: +Fails: diff --git a/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/test.java b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/test.java new file mode 100644 index 000000000000..f3f5ea781739 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/after/test.java @@ -0,0 +1,11 @@ +class Test { + private static Test migrationField; + + private static void m() { + migrationField.method(); + } + + void method(X... xes) { + + } +} diff --git a/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/before/test.java b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/before/test.java new file mode 100644 index 000000000000..001d53331836 --- /dev/null +++ b/java/typeMigration/testData/refactoring/typeMigration/genericEllipsis/before/test.java @@ -0,0 +1,11 @@ +class Test { + private static Test migrationField; + + private static void m() { + migrationField.method(); + } + + void method(X... xes) { + + } +} diff --git a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java index 017a8fd6e384..8e752f0431f8 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java +++ b/jps/jps-builders/src/org/jetbrains/jps/cmdline/ClasspathBootstrap.java @@ -72,7 +72,7 @@ public class ClasspathBootstrap { cp.addAll(getInstrumentationUtilRoots()); cp.add(getResourcePath(IXMLBuilder.class)); // nano-xml cp.addAll(getJavac8RefScannerClasspath()); - //don't forget to update layoutCommunityJps() in layouts.gant accordingly + //don't forget to update CommunityStandaloneJpsBuilder.layoutJps accordingly try { final Class cmdLineWrapper = Class.forName("com.intellij.rt.execution.CommandLineWrapper"); diff --git a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java index 9896f80bb2eb..360d6519e902 100644 --- a/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java +++ b/jps/jps-builders/src/org/jetbrains/jps/incremental/java/JavaBuilder.java @@ -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. @@ -231,7 +231,8 @@ public class JavaBuilder extends ModuleLevelBuilder { } }); - if (!filesToCompile.isEmpty() || dirtyFilesHolder.hasRemovedFiles()) { + if ((!filesToCompile.isEmpty() || dirtyFilesHolder.hasRemovedFiles()) && + JpsJavaSdkType.parseVersion(getLanguageLevel(ContainerUtil.getFirstItem(chunk.getModules()))) >= 9) { // at the moment, there is no incremental compilation for module-info files, so they should be rebuilt on every change JavaModuleIndex index = getJavaModuleIndex(context); for (JpsModule module : chunk.getModules()) { diff --git a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java index cdfd88131fe0..21561264f493 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java +++ b/jps/jps-builders/testSrc/org/jetbrains/ether/CommonTest.java @@ -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. @@ -177,8 +177,4 @@ public class CommonTest extends IncrementalTestCase { public void testIntegrateOnNonIncrementalMake() throws Exception { doTest(); } - - public void testModuleInfoIncluded() { - doTest(); - } } \ No newline at end of file diff --git a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java index c42a23093fbf..990bfd63825c 100644 --- a/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java +++ b/jps/jps-builders/testSrc/org/jetbrains/jps/builders/JpsBuildTestCase.java @@ -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. @@ -212,7 +212,7 @@ public abstract class JpsBuildTestCase extends UsefulTestCase { } protected void loadProject(String projectPath) { - loadProject(projectPath, Collections.emptyMap()); + loadProject(projectPath, Collections.emptyMap()); } protected void loadProject(String projectPath, diff --git a/jps/jps-launcher/src/org/jetbrains/jps/cmdline/Launcher.java b/jps/jps-launcher/src/org/jetbrains/jps/cmdline/Launcher.java index cb7414f00485..d48ede23beeb 100644 --- a/jps/jps-launcher/src/org/jetbrains/jps/cmdline/Launcher.java +++ b/jps/jps-launcher/src/org/jetbrains/jps/cmdline/Launcher.java @@ -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. @@ -48,7 +48,7 @@ public class Launcher { // IDEA-120811; speeding up DefaultChannelIDd calculation for netty //if (Boolean.parseBoolean(System.getProperty("io.netty.random.id"))) { - System.setProperty("io.netty.machineId", "9e43d860"); + System.setProperty("io.netty.machineId", "28:f0:76:ff:fe:16:65:0e"); System.setProperty("io.netty.processId", Integer.toString(new Random().nextInt(65535))); System.setProperty("io.netty.serviceThreadPrefix", "Netty"); //} diff --git a/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java b/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java index 2770b4d187f6..e1d9131045fa 100644 --- a/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java +++ b/jps/model-impl/src/com/intellij/openapi/fileTypes/impl/FileTypeAssocTable.java @@ -47,19 +47,16 @@ public class FileTypeAssocTable { myExtensionMappings.putAll(extensionMappings); myExactFileNameMappings = new THashMap<>(Math.max(10, exactFileNameMappings.size()), 0.5f, CharSequenceHashingStrategy.CASE_SENSITIVE); myExactFileNameMappings.putAll(exactFileNameMappings); - myExactFileNameAnyCaseMappings = - new THashMap<>(Math.max(10, exactFileNameAnyCaseMappings.size()), 0.5f, CharSequenceHashingStrategy.CASE_INSENSITIVE); + myExactFileNameAnyCaseMappings = new THashMap<>(Math.max(10, exactFileNameAnyCaseMappings.size()), 0.5f, CharSequenceHashingStrategy.CASE_INSENSITIVE); myExactFileNameAnyCaseMappings.putAll(exactFileNameAnyCaseMappings); - myMatchingMappings = new ArrayList<>(matchingMappings); } public FileTypeAssocTable() { - this(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), - Collections.>emptyList()); + this(Collections.emptyMap(), Collections.emptyMap(), Collections.emptyMap(), Collections.emptyList()); } - public boolean isAssociatedWith(@NotNull T type, @NotNull FileNameMatcher matcher) { + boolean isAssociatedWith(@NotNull T type, @NotNull FileNameMatcher matcher) { if (matcher instanceof ExtensionFileNameMatcher || matcher instanceof ExactFileNameMatcher) { return findAssociatedFileType(matcher) == type; } @@ -71,7 +68,7 @@ public class FileTypeAssocTable { return false; } - public void addAssociation(FileNameMatcher matcher, T type) { + public void addAssociation(@NotNull FileNameMatcher matcher, @NotNull T type) { if (matcher instanceof ExtensionFileNameMatcher) { myExtensionMappings.put(((ExtensionFileNameMatcher)matcher).getExtension(), type); } @@ -170,7 +167,7 @@ public class FileTypeAssocTable { } @Nullable - public T findAssociatedFileType(@NotNull FileNameMatcher matcher) { + T findAssociatedFileType(@NotNull FileNameMatcher matcher) { if (matcher instanceof ExtensionFileNameMatcher) { return myExtensionMappings.get(((ExtensionFileNameMatcher)matcher).getExtension()); } @@ -192,10 +189,8 @@ public class FileTypeAssocTable { @Deprecated @NotNull public String[] getAssociatedExtensions(@NotNull T type) { - Map extMap = myExtensionMappings; - List exts = new ArrayList<>(); - for (Map.Entry entry : extMap.entrySet()) { + for (Map.Entry entry : myExtensionMappings.entrySet()) { if (entry.getValue() == type) { exts.add(entry.getKey().toString()); } @@ -238,7 +233,7 @@ public class FileTypeAssocTable { return result; } - public boolean hasAssociationsFor(@NotNull T fileType) { + boolean hasAssociationsFor(@NotNull T fileType) { if (myExtensionMappings.values().contains(fileType) || myExactFileNameMappings.values().contains(fileType) || myExactFileNameAnyCaseMappings.values().contains(fileType)) { diff --git a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java index 3df267b784b9..faed98de016b 100644 --- a/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java +++ b/jps/model-impl/src/org/jetbrains/jps/model/java/impl/JdkVersionDetectorImpl.java @@ -118,7 +118,7 @@ public class JdkVersionDetectorImpl extends JdkVersionDetector { Process process = new ProcessBuilder(javaExe, "-version").redirectErrorStream(true).start(); VersionOutputReader reader = new VersionOutputReader(process.getInputStream(), runner); try { - process.waitFor(); + reader.waitFor(); } catch (InterruptedException e) { LOG.info(e); @@ -161,6 +161,7 @@ public class JdkVersionDetectorImpl extends JdkVersionDetector { @Override protected void onTextAvailable(@NotNull String text) { myLines.add(text); + LOG.debug("text: " + text); } @Nullable @@ -179,6 +180,7 @@ public class JdkVersionDetectorImpl extends JdkVersionDetector { } } + LOG.debug("Returning " + version + " " + arch); return version != null ? new JdkVersionInfo(version, arch) : null; } } diff --git a/json/gen/com/intellij/json/_JsonLexer.java b/json/gen/com/intellij/json/_JsonLexer.java index bc148d9b3e6b..32bbee8bf0a5 100644 --- a/json/gen/com/intellij/json/_JsonLexer.java +++ b/json/gen/com/intellij/json/_JsonLexer.java @@ -119,28 +119,28 @@ public class _JsonLexer implements FlexLexer { /* The ZZ_CMAP_A table has 920 entries */ static final char ZZ_CMAP_A[] = zzUnpackCMap( "\11\20\1\3\1\2\2\1\1\2\6\20\4\0\1\3\1\20\1\7\1\0\1\20\2\0\1\11\2\20\1\6\1"+ - "\17\1\25\1\12\1\15\1\4\1\13\11\14\1\26\5\0\5\20\1\16\5\20\1\23\1\10\1\24\1"+ - "\0\1\20\1\0\1\34\3\20\1\32\1\33\5\20\1\35\1\20\1\37\3\20\1\30\1\36\1\27\1"+ - "\31\5\20\1\21\1\0\1\22\7\20\1\5\2\20\1\3\1\0\4\20\4\0\1\20\2\0\1\20\7\0\1"+ - "\20\4\0\1\20\5\0\7\20\1\0\2\20\4\0\4\20\16\0\5\20\7\0\1\20\1\0\1\20\1\0\5"+ - "\20\1\0\2\20\2\0\4\20\10\0\1\20\1\0\3\20\1\0\1\20\1\0\4\20\1\0\13\20\1\0\1"+ - "\20\2\0\6\20\1\0\7\20\1\0\1\20\15\0\1\20\1\0\2\20\1\0\2\20\1\0\4\20\10\0\1"+ - "\20\4\0\4\20\1\0\4\20\1\0\13\20\2\0\4\20\2\0\11\20\6\0\10\20\2\0\2\20\1\0"+ - "\3\20\1\0\4\20\2\0\6\20\1\0\1\20\3\0\4\20\2\0\5\20\2\0\4\20\5\0\2\20\1\0\4"+ - "\20\4\0\2\20\1\0\2\20\1\0\2\20\1\0\2\20\2\0\1\20\1\0\3\20\2\0\3\20\3\0\4\20"+ - "\1\0\1\20\7\0\3\20\1\0\2\20\1\0\5\20\1\0\3\20\2\0\1\20\11\0\2\20\1\0\6\20"+ - "\3\0\3\20\1\0\4\20\3\0\2\20\1\0\1\20\1\0\2\20\3\0\2\20\3\0\1\20\6\0\3\20\3"+ - "\0\3\20\5\0\2\20\2\0\2\20\5\0\1\20\1\0\5\20\1\0\4\20\1\0\1\20\4\0\1\20\4\0"+ - "\6\20\1\0\1\20\3\0\2\20\5\0\2\20\1\0\1\20\2\0\2\20\1\0\1\20\2\0\1\20\3\0\3"+ - "\20\1\0\1\20\1\0\1\20\5\0\1\20\1\0\1\20\1\0\1\20\4\0\5\20\1\0\4\20\1\3\10"+ - "\20\1\0\2\20\4\0\4\20\3\0\1\20\3\0\3\20\5\0\5\20\1\0\1\20\1\0\1\20\1\0\1\20"+ - "\1\0\1\20\2\0\3\20\1\0\2\20\13\3\5\20\2\1\5\20\1\3\4\0\1\20\12\0\1\3\1\0\1"+ - "\20\3\0\3\20\1\0\5\20\2\0\1\20\1\0\4\20\1\0\1\20\5\0\5\20\4\0\1\20\1\0\1\3"+ - "\4\0\3\20\1\0\2\20\2\0\3\20\2\0\5\20\2\0\6\20\1\0\3\20\1\0\2\20\2\0\2\20\1"+ - "\0\2\20\1\0\2\20\2\0\3\20\3\0\2\20\3\0\2\20\2\0\3\20\4\0\3\20\1\0\2\20\1\0"+ - "\2\20\3\0\1\20\2\0\5\20\1\0\2\20\1\0\3\20\2\0\1\20\4\0\1\20\2\0\2\20\2\0\4"+ - "\20\1\0\4\20\1\0\1\20\1\0\5\20\1\0\4\20\2\0\1\20\1\0\1\20\5\0\1\20\1\0\1\20"+ - "\1\0\3\20"); + "\17\1\25\1\12\1\15\1\4\1\13\11\14\1\26\1\0\3\20\1\0\5\20\1\16\5\20\1\23\1"+ + "\10\1\24\2\20\1\0\1\34\3\20\1\32\1\33\5\20\1\35\1\20\1\37\3\20\1\30\1\36\1"+ + "\27\1\31\5\20\1\21\1\0\1\22\7\20\1\5\2\20\1\3\1\0\4\20\4\0\1\20\2\0\1\20\7"+ + "\0\1\20\4\0\1\20\5\0\7\20\1\0\2\20\4\0\4\20\16\0\5\20\7\0\1\20\1\0\1\20\1"+ + "\0\5\20\1\0\2\20\2\0\4\20\10\0\1\20\1\0\3\20\1\0\1\20\1\0\4\20\1\0\13\20\1"+ + "\0\1\20\2\0\6\20\1\0\7\20\1\0\1\20\15\0\1\20\1\0\2\20\1\0\2\20\1\0\4\20\10"+ + "\0\1\20\4\0\4\20\1\0\4\20\1\0\13\20\2\0\4\20\2\0\11\20\6\0\10\20\2\0\2\20"+ + "\1\0\3\20\1\0\4\20\2\0\6\20\1\0\1\20\3\0\4\20\2\0\5\20\2\0\4\20\5\0\2\20\1"+ + "\0\4\20\4\0\2\20\1\0\2\20\1\0\2\20\1\0\2\20\2\0\1\20\1\0\3\20\2\0\3\20\3\0"+ + "\4\20\1\0\1\20\7\0\3\20\1\0\2\20\1\0\5\20\1\0\3\20\2\0\1\20\11\0\2\20\1\0"+ + "\6\20\3\0\3\20\1\0\4\20\3\0\2\20\1\0\1\20\1\0\2\20\3\0\2\20\3\0\1\20\6\0\3"+ + "\20\3\0\3\20\5\0\2\20\2\0\2\20\5\0\1\20\1\0\5\20\1\0\4\20\1\0\1\20\4\0\1\20"+ + "\4\0\6\20\1\0\1\20\3\0\2\20\5\0\2\20\1\0\1\20\2\0\2\20\1\0\1\20\2\0\1\20\3"+ + "\0\3\20\1\0\1\20\1\0\1\20\5\0\1\20\1\0\1\20\1\0\1\20\4\0\5\20\1\0\4\20\1\3"+ + "\10\20\1\0\2\20\4\0\4\20\3\0\1\20\3\0\3\20\5\0\5\20\1\0\1\20\1\0\1\20\1\0"+ + "\1\20\1\0\1\20\2\0\3\20\1\0\2\20\13\3\5\20\2\1\5\20\1\3\4\0\1\20\12\0\1\3"+ + "\1\0\1\20\3\0\3\20\1\0\5\20\2\0\1\20\1\0\4\20\1\0\1\20\5\0\5\20\4\0\1\20\1"+ + "\0\1\3\4\0\3\20\1\0\2\20\2\0\3\20\2\0\5\20\2\0\6\20\1\0\3\20\1\0\2\20\2\0"+ + "\2\20\1\0\2\20\1\0\2\20\2\0\3\20\3\0\2\20\3\0\2\20\2\0\3\20\4\0\3\20\1\0\2"+ + "\20\1\0\2\20\3\0\1\20\2\0\5\20\1\0\2\20\1\0\3\20\2\0\1\20\4\0\1\20\2\0\2\20"+ + "\2\0\4\20\1\0\4\20\1\0\1\20\1\0\5\20\1\0\4\20\2\0\1\20\1\0\1\20\5\0\1\20\1"+ + "\0\1\20\1\0\3\20"); /** * Translates DFA states to action switch labels. diff --git a/json/json.bnf b/json/json.bnf index 30aa0352d5ee..701015ec6aa5 100644 --- a/json/json.bnf +++ b/json/json.bnf @@ -43,9 +43,10 @@ // auto completion of keywords. Semantically, it represents "bad word" type // of tokens // Could be as loose as [^\s\[\]{}:,\"\']+, but is slightly more restricted - // for the time being to match most forms of npm package names in package.json. + // for the time being to match most forms of npm package names and semver versions + // in package.json. // See https://github.com/npm/validate-npm-package-name - IDENTIFIER="regexp:[[:jletterdigit:]~!()*\-./@]+" + IDENTIFIER="regexp:[[:jletterdigit:]~!()*\-./@\^<>=]+" ] extends("container|literal|reference_expression")=value diff --git a/json/src/com/intellij/json/JsonBraceMatcher.java b/json/src/com/intellij/json/JsonBraceMatcher.java index 2504f3040ba8..90b64353c2cb 100644 --- a/json/src/com/intellij/json/JsonBraceMatcher.java +++ b/json/src/com/intellij/json/JsonBraceMatcher.java @@ -16,6 +16,7 @@ public class JsonBraceMatcher implements PairedBraceMatcher { new BracePair(JsonElementTypes.L_CURLY, JsonElementTypes.R_CURLY, true) }; + @NotNull @Override public BracePair[] getPairs() { return PAIRS; diff --git a/json/src/com/intellij/json/_JsonLexer.flex b/json/src/com/intellij/json/_JsonLexer.flex index 3550a1299a21..fdf5fe1ce515 100644 --- a/json/src/com/intellij/json/_JsonLexer.flex +++ b/json/src/com/intellij/json/_JsonLexer.flex @@ -30,7 +30,7 @@ BLOCK_COMMENT="/"\*([^*]|\*+[^*/])*(\*+"/")? DOUBLE_QUOTED_STRING=\"([^\\\"\r\n]|\\[^\r\n])*\"? SINGLE_QUOTED_STRING='([^\\'\r\n]|\\[^\r\n])*'? NUMBER=-?(0|[1-9][0-9]*)(\.[0-9]+)?([eE][+-]?[0-9]*)? -IDENTIFIER=[[:jletterdigit:]~!()*\-."/"@]+ +IDENTIFIER=[[:jletterdigit:]~!()*\-."/"@\^<>=]+ %% { diff --git a/json/src/com/jetbrains/jsonSchema/JsonSchemaFileTypeManager.java b/json/src/com/jetbrains/jsonSchema/JsonSchemaFileTypeManager.java index f7656c77ee3c..6674ad0887d7 100644 --- a/json/src/com/jetbrains/jsonSchema/JsonSchemaFileTypeManager.java +++ b/json/src/com/jetbrains/jsonSchema/JsonSchemaFileTypeManager.java @@ -30,7 +30,7 @@ import java.util.Collection; * @author Irina.Chernushina on 5/30/2016. */ public class JsonSchemaFileTypeManager implements ProjectManagerListener { - private final Collection myFileSets = ContainerUtil.createConcurrentList(); + private final Collection myFileSets = ContainerUtil.newConcurrentSet(); private volatile boolean mySetsInitialized; private static final Object LOCK = new Object(); diff --git a/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaInsideSchemaResolver.java b/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaInsideSchemaResolver.java index 393e4674979c..ece08d126538 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaInsideSchemaResolver.java +++ b/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaInsideSchemaResolver.java @@ -24,6 +24,7 @@ import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; import com.intellij.psi.PsiManager; import com.jetbrains.jsonSchema.ide.JsonSchemaService; +import com.jetbrains.jsonSchema.impl.JsonSchemaObject; import com.jetbrains.jsonSchema.impl.JsonSchemaWalker; import org.jetbrains.annotations.NotNull; @@ -50,20 +51,45 @@ public class JsonSchemaInsideSchemaResolver { public PsiElement resolveInSchemaRecursively() { final Ref ref = new Ref<>(); - JsonSchemaService.Impl.getEx(myProject).visitSchemaObject(mySchemaFile, - object -> { - JsonSchemaWalker.extractSchemaVariants(myProject, (isName, schema, schemaFile, steps) -> { + final JsonSchemaWalker.CompletionSchemesConsumer consumer = new JsonSchemaWalker.CompletionSchemesConsumer() { + @Override + public void consume(boolean isName, + @NotNull JsonSchemaObject schema, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { if (!ref.isNull()) return; final PsiFile file = PsiManager.getInstance(myProject).findFile(mySchemaFile); if (file == null) return; final JsonObject jsonObject = schema.getPeerPointer().getElement(); if (jsonObject != null && jsonObject.isValid()) { - if (jsonObject.getParent() instanceof JsonProperty) ref.set(((JsonProperty)jsonObject.getParent()).getNameElement()); + if (jsonObject.getParent() instanceof JsonProperty) + ref.set(((JsonProperty)jsonObject.getParent()).getNameElement()); else ref.set(jsonObject); } - }, mySchemaFile, object, true, mySteps, false); - return true; - }); + } + + @Override + public void oneOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + list.stream().findFirst().ifPresent(object -> consume(isName, object, schemaFile, steps)); + } + + @Override + public void anyOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + list.stream().findFirst().ifPresent(object -> consume(isName, object, schemaFile, steps)); + } + }; + JsonSchemaService.Impl.getEx(myProject).visitSchemaObject(mySchemaFile, + object -> { + JsonSchemaWalker.extractSchemaVariants( + myProject, consumer, mySchemaFile, object, true, mySteps, false); + return true; + }); return ref.get(); } } diff --git a/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java b/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java index 2c9c45a17fa9..74d6d8679270 100644 --- a/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java +++ b/json/src/com/jetbrains/jsonSchema/extension/schema/JsonSchemaRefReferenceProvider.java @@ -85,8 +85,9 @@ public class JsonSchemaRefReferenceProvider extends PsiReferenceProvider { } final List steps = JsonSchemaWalker.buildSteps(StringUtil.join(chain, "/")).getFirst(); - return new JsonSchemaInsideSchemaResolver(myElement.getProject(), schemaFile, normalized, steps) + final PsiElement element = new JsonSchemaInsideSchemaResolver(myElement.getProject(), schemaFile, normalized, steps) .resolveInSchemaRecursively(); + return element; } } } diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaDocumentationProvider.java b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaDocumentationProvider.java index 92712e42e767..1b754f4bcbc5 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaDocumentationProvider.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaDocumentationProvider.java @@ -55,7 +55,7 @@ public class JsonBySchemaDocumentationProvider implements DocumentationProvider if (value instanceof JsonObject) { final JsonProperty description = ((JsonObject)value).findProperty("description"); if (description != null && description.getValue() instanceof JsonStringLiteral) { - return StringUtil.unquoteString(description.getValue().getText()); + return StringUtil.escapeXml(StringUtil.unquoteString(description.getValue().getText())); } } return null; @@ -71,6 +71,22 @@ public class JsonBySchemaDocumentationProvider implements DocumentationProvider @NotNull List steps) { result.set(schema.getDescription()); } + + @Override + public void oneOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + //todo? + } + + @Override + public void anyOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + //todo? + } }, myRootSchema, mySchemaFile); return result.get(); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java index 22723c4b4498..a2b0a2c23a3d 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectAnnotator.java @@ -77,6 +77,28 @@ public class JsonBySchemaObjectAnnotator implements Annotator { checker.checkByScheme(firstProp.getValue(), schema, validatedProperties); checkers.add(checker); } + + @Override + public void oneOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + final BySchemaChecker checker = new BySchemaChecker(walker); + final Set validatedProperties = new HashSet<>(); + checker.processOneOf(firstProp.getValue(), list, validatedProperties); + checkers.add(checker); + } + + @Override + public void anyOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + final BySchemaChecker checker = new BySchemaChecker(walker); + final Set validatedProperties = new HashSet<>(); + checker.processAnyOf(firstProp.getValue(), list, validatedProperties); + checkers.add(checker); + } }, myRootSchema, mySchemaFile); if (checkers.isEmpty()) return; @@ -181,15 +203,15 @@ public class JsonBySchemaObjectAnnotator implements Annotator { if (value == null) return; if (schema.getAnyOf() != null && ! schema.getAnyOf().isEmpty()) { - processAnyOf(value, schema, validatedProperties); - } - if (schema.getOneOf() != null && ! schema.getOneOf().isEmpty()) { - processOneOf(value, schema, validatedProperties); - } - if (schema.getAllOf() != null && ! schema.getAllOf().isEmpty()) { - processAllOf(value, schema, validatedProperties); - } + processAnyOf(value, JsonSchemaWalker.mergeList(schema.getAnyOf(), schema, false), validatedProperties); + } else if (schema.getOneOf() != null && ! schema.getOneOf().isEmpty()) { + processOneOf(value, JsonSchemaWalker.mergeList(schema.getOneOf(), schema, true), validatedProperties); + } else if (schema.getAllOf() != null && ! schema.getAllOf().isEmpty()) { + checkByScheme(value, JsonSchemaWalker.mergeAll(schema), validatedProperties); + } else checkBySchemeBare(value, schema, validatedProperties); + } + private void checkBySchemeBare(@NotNull JsonValueAdapter value, @NotNull JsonSchemaObject schema, Set validatedProperties) { final JsonSchemaType type = getType(value); if (type == null) { //typeError(value.getDelegate()); @@ -306,32 +328,56 @@ public class JsonBySchemaObjectAnnotator implements Annotator { final Processor processor = schemaObject -> { final List steps = skipProperties(JsonOriginalPsiWalker.INSTANCE.findPosition(object, false, true)); - JsonSchemaWalker.extractSchemaVariants(object.getProject(), (isName, schema, schemaFile1, steps1) -> { - if (schemaFile.equals(schemaFile1)) { - final Map, String> invalidPatternProperties = schema.getInvalidPatternProperties(); - if (invalidPatternProperties != null) { - for (Map.Entry, String> entry : invalidPatternProperties.entrySet()) { - final JsonObject element = entry.getKey().getElement(); - if (element == null || !element.isValid()) continue; - final PsiElement parent = element.getParent(); - if (parent instanceof JsonProperty) { - error(StringUtil.convertLineSeparators(entry.getValue()), ((JsonProperty)parent).getNameElement()); - } - } - } - final String patternError = schema.getPatternError(); - if (patternError != null && schema.getPattern() != null) { - final SmartPsiElementPointer pointer = schema.getPeerPointer(); - final JsonObject element = pointer.getElement(); - if (element != null && element.isValid()) { - final JsonProperty pattern = element.findProperty("pattern"); - if (pattern != null) { - error(StringUtil.convertLineSeparators(patternError), pattern.getValue()); - } - } - } - } - }, + final JsonSchemaWalker.CompletionSchemesConsumer consumer = + new JsonSchemaWalker.CompletionSchemesConsumer() { + @Override + public void consume(boolean isName, + @NotNull JsonSchemaObject schema, + @NotNull VirtualFile schemaFile1, + @NotNull List steps1) { + if (schemaFile.equals(schemaFile1)) { + final Map, String> invalidPatternProperties = schema.getInvalidPatternProperties(); + if (invalidPatternProperties != null) { + for (Map.Entry, String> entry : invalidPatternProperties.entrySet()) { + final JsonObject element = entry.getKey().getElement(); + if (element == null || !element.isValid()) continue; + final PsiElement parent = element.getParent(); + if (parent instanceof JsonProperty) { + error(StringUtil.convertLineSeparators(entry.getValue()), ((JsonProperty)parent).getNameElement()); + } + } + } + final String patternError = schema.getPatternError(); + if (patternError != null && schema.getPattern() != null) { + final SmartPsiElementPointer pointer = schema.getPeerPointer(); + final JsonObject element = pointer.getElement(); + if (element != null && element.isValid()) { + final JsonProperty pattern = element.findProperty("pattern"); + if (pattern != null) { + error(StringUtil.convertLineSeparators(patternError), pattern.getValue()); + } + } + } + } + } + + @Override + public void oneOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + list.forEach(s -> consume(isName, s, schemaFile, steps)); + } + + @Override + public void anyOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + list.forEach(s -> consume(isName, s, schemaFile, steps)); + } + }; + JsonSchemaWalker.extractSchemaVariants(object.getProject(), consumer, schemaFile, schemaObject, false, steps, true); return true; }; @@ -356,14 +402,26 @@ public class JsonBySchemaObjectAnnotator implements Annotator { private void checkForEnum(PsiElement value, JsonSchemaObject schema) { //enum values + pattern -> don't check enum values if (schema.getEnum() == null || schema.getPattern() != null) return; - final String text = value.getText(); + final String text = StringUtil.notNullize(value.getText()); final List objects = schema.getEnum(); for (Object object : objects) { - if (object.toString().equalsIgnoreCase(text)) return; + if (myWalker.onlyDoubleQuotesForStringLiterals()) { + if (object.toString().equalsIgnoreCase(text)) return; + } else { + if (equalsIgnoreQuotesAndCase(object.toString(), text)) return; + } } error("Value should be one of: [" + StringUtil.join(objects, o -> o.toString(), ", ") + "]", value); } + private static boolean equalsIgnoreQuotesAndCase(@NotNull final String s1, @NotNull final String s2) { + final boolean quoted1 = StringUtil.isQuotedString(s1); + final boolean quoted2 = StringUtil.isQuotedString(s2); + if (quoted1 != quoted2) return false; + if (!quoted1) return s1.equalsIgnoreCase(s2); + return StringUtil.unquoteString(s1).equalsIgnoreCase(StringUtil.unquoteString(s2)); + } + private void checkArray(JsonValueAdapter value, JsonSchemaObject schema) { final JsonArrayValueAdapter asArray = value.getAsArray(); if (asArray == null) return; @@ -546,15 +604,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator { } } - private void processAllOf(JsonValueAdapter value, JsonSchemaObject schema, Set validatedProperties) { - final List allOf = schema.getAllOf(); - for (JsonSchemaObject object : allOf) { - checkByScheme(value, object, validatedProperties); - } - } - - private void processOneOf(JsonValueAdapter value, JsonSchemaObject schema, Set validatedProperties) { - final List oneOf = schema.getOneOf(); + private void processOneOf(JsonValueAdapter value, List oneOf, Set validatedProperties) { final Map errors = new HashMap<>(); int cntCorrect = 0; boolean validatedPropertiesAdded = false; @@ -566,6 +616,7 @@ public class JsonBySchemaObjectAnnotator implements Annotator { final HashSet local = new HashSet<>(); checker.checkByScheme(value, object, local); if (checker.isCorrect()) { + errors.clear(); if (!validatedPropertiesAdded) { validatedPropertiesAdded = true; validatedProperties.addAll(local); @@ -596,13 +647,12 @@ public class JsonBySchemaObjectAnnotator implements Annotator { return !checker.getErrors().containsKey(value); } - private void processAnyOf(JsonValueAdapter value, JsonSchemaObject schema, Set validatedProperties) { - final List anyOf = schema.getAnyOf(); + private void processAnyOf(JsonValueAdapter value, List anyOf, Set validatedProperties) { final Map errors = new HashMap<>(); for (JsonSchemaObject object : anyOf) { final BySchemaChecker checker = new BySchemaChecker(myWalker); final HashSet local = new HashSet<>(); - checker.checkByScheme(value, object, local); + checker.checkBySchemeBare(value, object, local); if (checker.isCorrect()) { validatedProperties.addAll(local); return; diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java index 6c7e5a70efa7..ab959706e9be 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonBySchemaObjectCompletionContributor.java @@ -134,6 +134,22 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor { suggestValues(schema); } } + + @Override + public void oneOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + list.forEach(s -> consume(isName, s, schemaFile, steps)); + } + + @Override + public void anyOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps) { + list.forEach(s -> consume(isName, s, schemaFile, steps)); + } }, myRootSchema, mySchemaFile); for (LookupElement variant : myVariants) { myResultConsumer.consume(variant); @@ -261,8 +277,9 @@ class JsonBySchemaObjectCompletionContributor extends CompletionContributor { final List values = jsonSchemaObject.getEnum(); if (type == null && values != null && !values.isEmpty()) type = detectType(values); final Object defaultValue = jsonSchemaObject.getDefault(); - final String defaultValueAsString = defaultValue == null ? null : defaultValue instanceof String ? "\"" + defaultValue + "\"" : - String.valueOf(defaultValue); + final String defaultValueAsString = defaultValue == null || defaultValue instanceof JsonSchemaObject ? null : + (defaultValue instanceof String ? "\"" + defaultValue + "\"" : + String.valueOf(defaultValue)); JsonSchemaType finalType = type; return new InsertHandler() { @Override diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java index 36e516bc7c3d..7aebd2b1a8df 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonLikePsiWalker.java @@ -32,6 +32,7 @@ public interface JsonLikePsiWalker { PsiElement goUpToCheckable(@NotNull final PsiElement element); List findPosition(@NotNull final PsiElement element, boolean isName, boolean forceLastTransition); boolean isNameQuoted(); + boolean onlyDoubleQuotesForStringLiterals(); boolean hasPropertiesBehindAndNoComma(@NotNull PsiElement element); Set getPropertyNamesOfParentObject(@NotNull PsiElement element); @Nullable JsonPropertyAdapter getParentPropertyAdapter(@NotNull PsiElement element); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java index 66c12fe4d211..6278d281c7ab 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonOriginalPsiWalker.java @@ -121,6 +121,11 @@ public class JsonOriginalPsiWalker implements JsonLikePsiWalker { return true; } + @Override + public boolean onlyDoubleQuotesForStringLiterals() { + return true; + } + @Override public boolean hasPropertiesBehindAndNoComma(@NotNull PsiElement element) { PsiElement current = element instanceof JsonProperty ? element : PsiTreeUtil.getParentOfType(element, JsonProperty.class); diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java index 08cc1925e22c..26a95e761705 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaReader.java @@ -90,13 +90,16 @@ public class JsonSchemaReader { } private void processReferences(JsonSchemaObject root, Set objects) { + final Set queuedDefinitions = new HashSet<>(); final ArrayDeque queue = new ArrayDeque<>(); queue.add(root); queue.addAll(objects); int control = 10000; while (!queue.isEmpty()) { - if (--control == 0) throw new RuntimeException("cyclic definitions search"); + if (--control == 0) { + throw new RuntimeException("cyclic definitions search"); + } final JsonSchemaObject current = queue.removeFirst(); if ("#".equals(current.getRef())) continue; @@ -108,7 +111,8 @@ public class JsonSchemaReader { current.setRef(null); continue; } - if (definition.getRef() != null && !"#".equals(definition.getRef())) { + if (definition.getRef() != null && !"#".equals(definition.getRef()) && !queuedDefinitions.contains(definition.getRef())) { + queuedDefinitions.add(definition.getRef()); queue.addFirst(current); queue.addFirst(definition); continue; diff --git a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java index f4767176ce84..8bf92b0d61b5 100644 --- a/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java +++ b/json/src/com/jetbrains/jsonSchema/impl/JsonSchemaWalker.java @@ -52,6 +52,14 @@ public class JsonSchemaWalker { @NotNull JsonSchemaObject schema, @NotNull VirtualFile schemaFile, @NotNull List steps); + void oneOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps); + void anyOf(boolean isName, + @NotNull List list, + @NotNull VirtualFile schemaFile, + @NotNull List steps); } public static void findSchemasForAnnotation(@NotNull final PsiElement element, @@ -111,7 +119,8 @@ public class JsonSchemaWalker { protected static class DefinitionsResolver { @NotNull private final List myPosition; final List>> myVariants; - private JsonSchemaObject mySchemaObject; + private List mySchemaObjects = new ArrayList<>(); + private boolean myOneOf; public DefinitionsResolver(@NotNull List position) { myPosition = position; @@ -119,7 +128,7 @@ public class JsonSchemaWalker { } public void consumeResult(@NotNull JsonSchemaObject schema) { - mySchemaObject = schema; + mySchemaObjects.add(schema); } public void consumeSmallStep(@NotNull JsonSchemaObject schema, int idx) { @@ -132,16 +141,24 @@ public class JsonSchemaWalker { } public boolean isFound() { - return mySchemaObject != null; + return !mySchemaObjects.isEmpty(); } - public JsonSchemaObject getSchemaObject() { - return mySchemaObject; + public List getSchemaObjects() { + return mySchemaObjects; + } + + public boolean isOneOf() { + return myOneOf; } public List>> getVariants() { return myVariants; } + + public void setOneOf(boolean oneOf) { + myOneOf = oneOf; + } } public static void extractSchemaVariants(@NotNull final Project project, @@ -170,16 +187,26 @@ public class JsonSchemaWalker { extractSchemaVariants(definitionsResolver, object, path, acceptAdditionalPropertiesSchemas); if (definitionsResolver.isFound()) { - final List list = gatherSchemas(definitionsResolver.getSchemaObject()); - for (JsonSchemaObject schemaObject : list) { - if (schemaObject.getDefinitionAddress() != null) { - final List steps = new ArrayList<>(); - // add value step if needed - if (!isName) steps.add(new Step(StateType._value, null)); - visitSchemaByDefinitionAddress(serviceEx, queue, schemaFile, schemaObject.getDefinitionAddress(), steps); + final List matchedSchemas = definitionsResolver.getSchemaObjects(); + matchedSchemas.forEach(matchedSchema -> { + final List list = gatherSchemas(matchedSchema); + for (JsonSchemaObject schemaObject : list) { + if (schemaObject.getDefinitionAddress() != null && !schemaObject.getDefinitionAddress().startsWith("#/")) { + final List steps = new ArrayList<>(); + // add value step if needed + if (!isName) steps.add(new Step(StateType._value, null)); + visitSchemaByDefinitionAddress(serviceEx, queue, schemaFile, schemaObject.getDefinitionAddress(), steps); + } + } + }); + if (matchedSchemas.size() == 1) consumer.consume(isName, matchedSchemas.get(0), schemaFile, path); + else { + if (definitionsResolver.isOneOf()) { + consumer.oneOf(isName, matchedSchemas, schemaFile, path); + } else { + consumer.anyOf(isName, matchedSchemas, schemaFile, path); } } - consumer.consume(isName, definitionsResolver.getSchemaObject(), schemaFile, path); } else { final List>> variants = definitionsResolver.getVariants(); for (Pair> variant : variants) { @@ -228,25 +255,31 @@ public class JsonSchemaWalker { continue; } if (step.getTransition() != null && !StateType._unknown.equals(step.getType()) - && !step.getTransition().possibleFromState(step.getType())) continue; + && !step.getTransition().possibleFromState(step.getType())) { + continue; + } final Condition byTypeFilter = object -> byStateType(step.getType(), object); // not?? + boolean isOneOf = schema.getOneOf() != null; List list = gatherSchemas(schema); list = ContainerUtil.filter(list, byTypeFilter); final Consumer reporter = object -> { - if ((level + 1) >= position.size()) consumer.consumeResult(object); + if ((level + 1) >= position.size()) { + consumer.setOneOf(isOneOf); + consumer.consumeResult(object); + } else { consumer.consumeSmallStep(object, level); queue.add(Pair.create(object, level + 1)); } }; - TransitionResultConsumer transitionResultConsumer = new TransitionResultConsumer(); + TransitionResultConsumer transitionResultConsumer; for (JsonSchemaObject object : list) { - if (schema.getAllOf() == null) transitionResultConsumer = new TransitionResultConsumer(); + transitionResultConsumer = new TransitionResultConsumer(); step.getTransition().step(object, transitionResultConsumer, acceptAdditionalPropertiesSchemas); if (transitionResultConsumer.isNothing()) continue; if (transitionResultConsumer.getSchema() != null) { @@ -257,16 +290,50 @@ public class JsonSchemaWalker { } private static List gatherSchemas(JsonSchemaObject schema) { - List list = new ArrayList<>(); - list.add(schema); if (schema.getAllOf() != null) { - list.addAll(schema.getAllOf()); + return gatherSchemas(mergeAll(schema)); } else { - if (schema.getAnyOf() != null) list.addAll(schema.getAnyOf()); - if (schema.getOneOf() != null) list.addAll(schema.getOneOf()); - + if (schema.getAnyOf() != null) { + return mergeList(schema.getAnyOf(), schema, false); + } + if (schema.getOneOf() != null) { + return mergeList(schema.getOneOf(), schema, true); + } } - return list; + return Collections.singletonList(schema); + } + + @NotNull + public static JsonSchemaObject mergeAll(@NotNull JsonSchemaObject schema) { + JsonSchemaObject currentBase = copySchema(schema); + currentBase.setAllOf(null); + for (JsonSchemaObject object : schema.getAllOf()) { + currentBase = merge(currentBase, object); + } + return currentBase; + } + + @NotNull + private static JsonSchemaObject copySchema(@NotNull JsonSchemaObject schema) { + JsonSchemaObject currentBase = new JsonSchemaObject(schema.getPeerPointer()); + currentBase.mergeValues(schema); + currentBase.setDefinitionsPointer(schema.getDefinitionsPointer()); + return currentBase; + } + + public static List mergeList(@NotNull final Collection coll, + @NotNull JsonSchemaObject schema, boolean oneOf) { + final JsonSchemaObject copy = copySchema(schema); + if (oneOf) copy.setOneOf(null); else copy.setAnyOf(null); + return coll.stream().map(s -> merge(copy, s)).collect(Collectors.toList()); + } + + public static JsonSchemaObject merge(@NotNull JsonSchemaObject base, @NotNull JsonSchemaObject other) { + final JsonSchemaObject object = new JsonSchemaObject(base.getPeerPointer()); + object.mergeValues(other); + object.mergeValues(base); + object.setDefinitionsPointer(base.getDefinitionsPointer()); + return object; } private static boolean byStateType(@NotNull final StateType type, @NotNull final JsonSchemaObject schema) { @@ -435,6 +502,7 @@ public class JsonSchemaWalker { @Nullable private JsonSchemaObject mySchema; private boolean myAny; private boolean myNothing; + private boolean myOneOf; public TransitionResultConsumer() { myNothing = true; @@ -467,5 +535,13 @@ public class JsonSchemaWalker { myNothing = true; myAny = false; } + + public void oneOf() { + myOneOf = true; + } + + public boolean isOneOf() { + return myOneOf; + } } } diff --git a/json/tests/json-tests.iml b/json/tests/json-tests.iml index 4aac20c1b76d..06d046d470ac 100644 --- a/json/tests/json-tests.iml +++ b/json/tests/json-tests.iml @@ -6,7 +6,7 @@ - + diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java index 8f2d8f635c1b..d034b00f5a47 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaCrossReferencesTest.java @@ -553,19 +553,83 @@ public class JsonSchemaCrossReferencesTest extends JsonSchemaHeavyAbstractTest { configureByFiles(null, "withIncorrectReferenceSchema.json"); } + @Override + public void doCheck() { + final String midia = "midia"; + checkNavigationIntoDefinition(midia); + } + }); + } + + private void checkNavigationIntoDefinition(String name) { + int offset = myEditor.getCaretModel().getPrimaryCaret().getOffset(); + final PsiElement element = myFile.findElementAt(offset); + Assert.assertNotNull(element); + + final PsiReference referenceAt = myFile.findReferenceAt(offset); + Assert.assertNotNull(referenceAt); + final PsiElement resolve = referenceAt.resolve(); + Assert.assertNotNull(resolve); + Assert.assertEquals("\"" + name + "\"", resolve.getText()); + final PsiElement parent = resolve.getParent(); + Assert.assertTrue(parent instanceof JsonProperty); + Assert.assertEquals(name, ((JsonProperty) parent).getName()); + Assert.assertTrue(parent.getParent().getParent() instanceof JsonProperty); + Assert.assertEquals("definitions", ((JsonProperty) parent.getParent().getParent()).getName()); + } + + public void testInsideCycledSchemaNavigation() throws Exception { + skeleton(new Callback() { + @Override + public void registerSchemes() { + final String moduleDir = getModuleDir(getProject()); + addSchema(new JsonSchemaMappingsConfigurationBase.SchemaInfo("one", moduleDir + "/insideCycledSchemaNavigationSchema.json", + false, Collections.emptyList())); + } + + @Override + public void configureFiles() throws Exception { + configureByFiles(null, "insideCycledSchemaNavigationSchema.json"); + } + + @Override + public void doCheck() { + checkNavigationIntoDefinition("all"); + } + }); + } + + public void testNavigationIntoCycledSchema() throws Exception { + skeleton(new Callback() { + @Override + public void registerSchemes() { + final String moduleDir = getModuleDir(getProject()); + final List patterns = Collections.singletonList( + new JsonSchemaMappingsConfigurationBase.Item("*.json", true, false)); + addSchema(new JsonSchemaMappingsConfigurationBase.SchemaInfo("one", moduleDir + "/cycledSchema.json", false, patterns)); + } + + @Override + public void configureFiles() throws Exception { + configureByFiles(null, "testNavigationIntoCycled.json", "cycledSchema.json"); + } + @Override public void doCheck() { int offset = myEditor.getCaretModel().getPrimaryCaret().getOffset(); + final PsiElement element = myFile.findElementAt(offset); + Assert.assertNotNull(element); + final PsiReference referenceAt = myFile.findReferenceAt(offset); Assert.assertNotNull(referenceAt); final PsiElement resolve = referenceAt.resolve(); Assert.assertNotNull(resolve); - Assert.assertEquals("\"midia\"", resolve.getText()); + Assert.assertEquals("\"bbb\"", resolve.getText()); final PsiElement parent = resolve.getParent(); Assert.assertTrue(parent instanceof JsonProperty); - Assert.assertEquals("midia", ((JsonProperty) parent).getName()); + Assert.assertEquals("bbb", ((JsonProperty) parent).getName()); Assert.assertTrue(parent.getParent().getParent() instanceof JsonProperty); - Assert.assertEquals("definitions", ((JsonProperty) parent.getParent().getParent()).getName()); + Assert.assertEquals("properties", ((JsonProperty) parent.getParent().getParent()).getName()); } }); } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java index bb46321da1ef..b7f47cb68571 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaDocumentationTest.java @@ -28,4 +28,8 @@ public class JsonSchemaDocumentationTest extends JsonBySchemaDocumentationBaseTe public void testSecondLevel() throws Exception { doTest(true, "json"); } + + public void testCheckEscaping() throws Exception { + doTest(true, "json"); + } } diff --git a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java index 5cabd3d5ca31..8ee3b0b78fb7 100644 --- a/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java +++ b/json/tests/test/com/jetbrains/jsonSchema/JsonSchemaHighlightingTest.java @@ -259,12 +259,12 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { @SuppressWarnings("Duplicates") public void testAllOf() throws Exception { final List subSchemas = new ArrayList<>(); - subSchemas.add("{\"type\": \"string\", \"enum\": [\"a\", \"b\"]}"); - subSchemas.add("{\"type\": \"string\", \"enum\": [\"a\", \"c\"]}"); + subSchemas.add("{\"type\": \"integer\", \"\"multipleOf\": 2}"); + subSchemas.add("{\"enum\": [1,2,3]}"); final String schema = schema("{\"allOf\": [" + StringUtil.join(subSchemas, ", ") + "]}"); - testImpl(schema, "{\"prop\": \"b\"}"); - testImpl(schema, "{\"prop\": \"c\"}"); - testImpl(schema, "{\"prop\": \"a\"}"); + testImpl(schema, "{\"prop\": 1}"); + testImpl(schema, "{\"prop\": 4}"); + testImpl(schema, "{\"prop\": 2}"); } public void testObjectInArray() throws Exception { @@ -297,7 +297,7 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { final String schema = "{\"allOf\": [{\"type\": \"object\", \"properties\": {\"first\": {}}}," + " {\"properties\": {\"second\": {\"enum\": [33,44]}}}], \"additionalProperties\": false}"; testImpl(schema, "{\"first\": {}, \"second\": null}"); - testImpl(schema, "{\"first\": {}, \"second\": 44, \"other\": 15}"); + testImpl(schema, "{\"first\": {}, \"second\": 44, \"other\": 15}"); testImpl(schema, "{\"first\": {}, \"second\": 12}"); } @@ -316,6 +316,24 @@ public class JsonSchemaHighlightingTest extends DaemonAnalyzerTestCase { testImpl(schemaText, inputText); } + public void testOneOf1() throws Exception { + String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOfSchema.json")); + String inputText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOf1.json")); + testImpl(schemaText, inputText); + } + + public void testOneOf2() throws Exception { + String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOfSchema.json")); + String inputText = FileUtil.loadFile(new File(getTestDataPath() + "/oneOf2.json")); + testImpl(schemaText, inputText); + } + + public void testCycledSchema() throws Exception { + String schemaText = FileUtil.loadFile(new File(getTestDataPath() + "/cycledSchema.json")); + String inputText = FileUtil.loadFile(new File(getTestDataPath() + "/testCycledSchema.json")); + testImpl(schemaText, inputText); + } + public void testPatternPropertiesHighlighting() throws Exception { final String schema = "{\n" + " \"patternProperties\": {\n" + diff --git a/json/tests/testData/highlighting/ComplianceProblems.json b/json/tests/testData/highlighting/ComplianceProblems.json index 0768332f0690..1928a83d365e 100644 --- a/json/tests/testData/highlighting/ComplianceProblems.json +++ b/json/tests/testData/highlighting/ComplianceProblems.json @@ -1,5 +1,9 @@ { /* Property key checks */ + "dependencies": { + @foo/bar: ^0.1, + foo.bar: =1.2 + }, "key1": null, 'key2': null, key3: null, @@ -10,7 +14,7 @@ "key5": 'foo', "key6": foo, "key7": [42,], - "key8": false, + "key8": false, } ["another", "top-level", "value"] null \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/crossReferences/cycledSchema.json b/json/tests/testData/jsonSchema/crossReferences/cycledSchema.json new file mode 100644 index 000000000000..9f341fd98c36 --- /dev/null +++ b/json/tests/testData/jsonSchema/crossReferences/cycledSchema.json @@ -0,0 +1,23 @@ +{ + "definitions": { + "koshmar": { + "ref": "#/definitions/koshmar" + }, + "all": { + "$ref": "#/definitions/one" + }, + "one": { + "$ref": "#/definitions/all" + } + }, + "properties": { + "aaa": {"$ref": "#/definitions/koshmar"}, + "bbb": { + "$ref": "#/definitions/all" + }, + "ccc": { + "type": "integer" + } + }, + "additionalProperties": "false" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/crossReferences/insideCycledSchemaNavigationSchema.json b/json/tests/testData/jsonSchema/crossReferences/insideCycledSchemaNavigationSchema.json new file mode 100644 index 000000000000..7873bca07e5e --- /dev/null +++ b/json/tests/testData/jsonSchema/crossReferences/insideCycledSchemaNavigationSchema.json @@ -0,0 +1,23 @@ +{ + "definitions": { + "koshmar": { + "ref": "#/definitions/koshmar" + }, + "all": { + "$ref": "#/definitions/one" + }, + "one": { + "$ref": "#/definitions/all" + } + }, + "properties": { + "aaa": {"$ref": "#/definitions/koshmar"}, + "bbb": { + "$ref": "#/definitions/all" + }, + "ccc": { + "type": "integer" + } + }, + "additionalProperties": "false" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/crossReferences/testNavigationIntoCycled.json b/json/tests/testData/jsonSchema/crossReferences/testNavigationIntoCycled.json new file mode 100644 index 000000000000..49d884f92402 --- /dev/null +++ b/json/tests/testData/jsonSchema/crossReferences/testNavigationIntoCycled.json @@ -0,0 +1,6 @@ +{ + "aaa": 1, + "bbb": true, + "s": 1, + "ccc": "3" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/checkEscaping.html b/json/tests/testData/jsonSchema/documentation/checkEscaping.html new file mode 100644 index 000000000000..c73fc4ac3bd3 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/checkEscaping.html @@ -0,0 +1 @@ +Disallows /// <reference path=> imports (use ES6-style imports instead) \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/checkEscaping.json b/json/tests/testData/jsonSchema/documentation/checkEscaping.json new file mode 100644 index 000000000000..4949d6933b92 --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/checkEscaping.json @@ -0,0 +1,3 @@ +{ + "no-reference": 1 +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/documentation/checkEscapingSchema.json b/json/tests/testData/jsonSchema/documentation/checkEscapingSchema.json new file mode 100644 index 000000000000..84adf7e6bb6c --- /dev/null +++ b/json/tests/testData/jsonSchema/documentation/checkEscapingSchema.json @@ -0,0 +1,10 @@ +{ + "properties": { + "no-reference": { + "description": "Disallows /// imports (use ES6-style imports instead)" + }, + "my_property2": { + "description": "my property description 2" + } + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/highlighting/cycledSchema.json b/json/tests/testData/jsonSchema/highlighting/cycledSchema.json new file mode 100644 index 000000000000..9f341fd98c36 --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/cycledSchema.json @@ -0,0 +1,23 @@ +{ + "definitions": { + "koshmar": { + "ref": "#/definitions/koshmar" + }, + "all": { + "$ref": "#/definitions/one" + }, + "one": { + "$ref": "#/definitions/all" + } + }, + "properties": { + "aaa": {"$ref": "#/definitions/koshmar"}, + "bbb": { + "$ref": "#/definitions/all" + }, + "ccc": { + "type": "integer" + } + }, + "additionalProperties": "false" +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/highlighting/oneOf1.json b/json/tests/testData/jsonSchema/highlighting/oneOf1.json new file mode 100644 index 000000000000..1051623b1571 --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/oneOf1.json @@ -0,0 +1,11 @@ +{ + "client_address": { + "street_address": "d", + "city": "d", + "state": "d", + "type": "home" + }, + "parts": { + "two": 2 + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/highlighting/oneOf2.json b/json/tests/testData/jsonSchema/highlighting/oneOf2.json new file mode 100644 index 000000000000..88e57e9c7c3b --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/oneOf2.json @@ -0,0 +1,6 @@ +{ + "client_address": { + "type": "business", + "building": "1" + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/highlighting/oneOfSchema.json b/json/tests/testData/jsonSchema/highlighting/oneOfSchema.json new file mode 100644 index 000000000000..c1aae4d32d35 --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/oneOfSchema.json @@ -0,0 +1,55 @@ +{ + "$schema": "http://json-schema.org/draft-04/schema#", + + "definitions": { + "address": { + "type": "object", + "properties": { + "street_address": { "type": "string" }, + "city": { "type": "string" }, + "state": { "type": "string" } + }, + "required": ["street_address", "city", "state"] + }, + "office_address": { + "properties": { + "type": { "enum": [ "business" ] }, + "building": { "type": "string" } + }, + "required": ["type", "building"], + "additionalProperties": false + }, + "home_address": { + "properties": { + "type": { "enum": [ "home" ] }, + "street_address": { "type": "string" }, + "city": { "type": "string" }, + "state": { "type": "string" } + }, + "required": ["type", "street_address", "city", "state"], + "additionalProperties": false + }, + "partOne": { + "properties": {"one": {"type": "string"}} + }, + "partTwo": { + "properties": {"two": {"type": "integer"}} + } + }, + "type": "object", + "properties": { + "client_address": { + "oneOf": [ + { "$ref": "#/definitions/office_address" }, + { "$ref": "#/definitions/home_address" } + ] + }, + "parts": { + "oneOf": [ + { "$ref": "#/definitions/partOne" }, + { "$ref": "#/definitions/partTwo" } + ], + "additionalProperties": false + } + } +} \ No newline at end of file diff --git a/json/tests/testData/jsonSchema/highlighting/testCycledSchema.json b/json/tests/testData/jsonSchema/highlighting/testCycledSchema.json new file mode 100644 index 000000000000..eb663c85e451 --- /dev/null +++ b/json/tests/testData/jsonSchema/highlighting/testCycledSchema.json @@ -0,0 +1,6 @@ +{ + "aaa": 1, + "bbb": true, + "s": 1, + "ccc": "3" +} \ No newline at end of file diff --git a/json/tests/testData/psi/ExtendedIdentifierToken.json b/json/tests/testData/psi/ExtendedIdentifierToken.json index 9824b3b6ee31..99d10f78398c 100644 --- a/json/tests/testData/psi/ExtendedIdentifierToken.json +++ b/json/tests/testData/psi/ExtendedIdentifierToken.json @@ -1,5 +1,11 @@ { "devDependencies": { - @types/3d-bin.packing: 42 + @types/3d-bin.packing: 42, + "pkg1": ^4.15.0, + "pkg2": ~4.15.0, + "pkg3": >4.15.0, + "pkg4": >=4.15.0, + "pkg5": <4.15.0, + "pkg6": <=4.15.0 } } \ No newline at end of file diff --git a/json/tests/testData/psi/ExtendedIdentifierToken.txt b/json/tests/testData/psi/ExtendedIdentifierToken.txt index c09bc06a9335..222ce004efd9 100644 --- a/json/tests/testData/psi/ExtendedIdentifierToken.txt +++ b/json/tests/testData/psi/ExtendedIdentifierToken.txt @@ -17,6 +17,60 @@ JsonFile: ExtendedIdentifierToken.json PsiWhiteSpace(' ') JsonNumberLiteral PsiElement(NUMBER)('42') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg1"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('^4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg2"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('~4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg3"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('>4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg4"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('>=4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg5"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('<4.15.0') + PsiElement(,)(',') + PsiWhiteSpace('\n ') + JsonProperty + JsonStringLiteral + PsiElement(DOUBLE_QUOTED_STRING)('"pkg6"') + PsiElement(:)(':') + PsiWhiteSpace(' ') + JsonReferenceExpression + PsiElement(IDENTIFIER)('<=4.15.0') PsiWhiteSpace('\n ') PsiElement(})('}') PsiWhiteSpace('\n') diff --git a/lib/annotations/trove4j/gnu/trove/annotations.xml b/lib/annotations/trove4j/gnu/trove/annotations.xml index a08496cb8cdb..2f6991fddf0e 100644 --- a/lib/annotations/trove4j/gnu/trove/annotations.xml +++ b/lib/annotations/trove4j/gnu/trove/annotations.xml @@ -53,6 +53,9 @@ + + + diff --git a/lib/netty-all-4.1.6.Final.Patched.jar b/lib/netty-all-4.1.9.Final.jar similarity index 56% rename from lib/netty-all-4.1.6.Final.Patched.jar rename to lib/netty-all-4.1.9.Final.jar index b9ecf9be7c9c..67df93166357 100644 Binary files a/lib/netty-all-4.1.6.Final.Patched.jar and b/lib/netty-all-4.1.9.Final.jar differ diff --git a/lib/src/netty-all-4.1.6.Final.Patched-sources.jar b/lib/src/netty-all-4.1.9.Final-sources.jar similarity index 59% rename from lib/src/netty-all-4.1.6.Final.Patched-sources.jar rename to lib/src/netty-all-4.1.9.Final-sources.jar index a6693c64dc92..39a7e76d5f07 100644 Binary files a/lib/src/netty-all-4.1.6.Final.Patched-sources.jar and b/lib/src/netty-all-4.1.9.Final-sources.jar differ diff --git a/native/WinLauncher/WinLauncher/WinLauncher.cpp b/native/WinLauncher/WinLauncher/WinLauncher.cpp index 564cb44c64f0..70b9eb8d78b7 100644 --- a/native/WinLauncher/WinLauncher/WinLauncher.cpp +++ b/native/WinLauncher/WinLauncher/WinLauncher.cpp @@ -344,11 +344,24 @@ bool LoadVMOptionsFile(const TCHAR* path, std::vector& vmOptionLine std::string FindToolsJar() { - std::string toolsJarPath = jvmPath; - size_t lastSlash = toolsJarPath.rfind('\\'); + std::string baseToolsJarPath = jvmPath; + // remove trailing slash if any + size_t lastSlash = baseToolsJarPath.rfind('\\'); + if (lastSlash == baseToolsJarPath.length() - 1) + { + baseToolsJarPath = baseToolsJarPath.substr(0, lastSlash); + } + // 1) look in the base dir + std::string toolsJarPath = baseToolsJarPath + "\\lib\\tools.jar"; + if (FileExists(toolsJarPath)) + { + return toolsJarPath; + } + // 2) look in the up dir + lastSlash = baseToolsJarPath.rfind('\\'); if (lastSlash != std::string::npos) { - toolsJarPath = toolsJarPath.substr(0, lastSlash + 1) + "lib\\tools.jar"; + toolsJarPath = baseToolsJarPath.substr(0, lastSlash + 1) + "lib\\tools.jar"; if (FileExists(toolsJarPath)) { return toolsJarPath; diff --git a/native/runner/runnerw/runnerw.vcxproj b/native/runner/runnerw/runnerw.vcxproj index 373a06b0a707..766404cf8631 100644 --- a/native/runner/runnerw/runnerw.vcxproj +++ b/native/runner/runnerw/runnerw.vcxproj @@ -22,6 +22,7 @@ Application false + v140_xp @@ -81,4 +82,4 @@ - + \ No newline at end of file diff --git a/platform/analysis-api/src/com/intellij/ToolExtensionPoints.java b/platform/analysis-api/src/com/intellij/ToolExtensionPoints.java index 8d61eeac99e0..d2d24dddf46c 100644 --- a/platform/analysis-api/src/com/intellij/ToolExtensionPoints.java +++ b/platform/analysis-api/src/com/intellij/ToolExtensionPoints.java @@ -18,11 +18,6 @@ package com.intellij; import org.jetbrains.annotations.NonNls; public interface ToolExtensionPoints { - @NonNls String INVALID_PROPERTY_KEY_INSPECTION_TOOL = "com.intellij.invalidPropertyKeyInspectionTool"; - @NonNls String I18N_INSPECTION_TOOL = "com.intellij.i18nInspectionTool"; - @NonNls String JAVA15_INSPECTION_TOOL = "com.intellij.java15InspectionTool"; - - @NonNls String INSPECTIONS_GRAPH_ANNOTATOR = "com.intellij.refGraphAnnotator"; @NonNls String DEAD_CODE_TOOL = "com.intellij.deadCode"; diff --git a/platform/analysis-api/src/com/intellij/codeInspection/InspectionToolProvider.java b/platform/analysis-api/src/com/intellij/codeInspection/InspectionToolProvider.java index 628620738a7a..022e83666fed 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/InspectionToolProvider.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/InspectionToolProvider.java @@ -16,6 +16,7 @@ package com.intellij.codeInspection; import com.intellij.openapi.extensions.ExtensionPointName; +import org.jetbrains.annotations.NotNull; /** * Plugin's ApplicationComponent or extension that implements this interface will be automatically queried for inspection tool classes. @@ -27,5 +28,6 @@ public interface InspectionToolProvider { * Query method for inspection tools provided by a plugin. * @return classes that extend {@link InspectionProfileEntry} */ + @NotNull Class[] getInspectionClasses(); } diff --git a/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionElementsMerger.java b/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionElementsMerger.java index 3b451a7bd7c6..42feffa8a485 100644 --- a/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionElementsMerger.java +++ b/platform/analysis-api/src/com/intellij/codeInspection/ex/InspectionElementsMerger.java @@ -18,6 +18,7 @@ package com.intellij.codeInspection.ex; import com.intellij.openapi.extensions.ExtensionPointName; import com.intellij.openapi.extensions.Extensions; import com.intellij.util.ArrayUtilRt; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.HashMap; @@ -49,6 +50,7 @@ public abstract class InspectionElementsMerger { /** * @return the shortNames of the merged inspections */ + @NotNull public abstract String[] getSourceToolNames(); /** @@ -56,6 +58,7 @@ public abstract class InspectionElementsMerger { * If this returns an empty string array, the result of getSourceToolNames() is used instead. * @return the suppressIds of the merged inspections. */ + @NotNull public String[] getSuppressIds() { return ArrayUtilRt.EMPTY_STRING_ARRAY; } diff --git a/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java b/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java index 99915161be33..fc31811426e3 100644 --- a/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java +++ b/platform/analysis-api/src/com/intellij/psi/search/scope/packageSet/NamedScopesHolder.java @@ -81,6 +81,7 @@ public abstract class NamedScopesHolder implements PersistentStateComponent CODE_INSIGHT_CONFLICT_KEYS = new HashSet<>(Arrays.asList( + CodeInsightColors.WRONG_REFERENCES_ATTRIBUTES, + CodeInsightColors.ERRORS_ATTRIBUTES, + CodeInsightColors.WARNINGS_ATTRIBUTES, + CodeInsightColors.GENERIC_SERVER_ERROR_OR_WARNING, + CodeInsightColors.DUPLICATE_FROM_SERVER, + CodeInsightColors.WEAK_WARNING_ATTRIBUTES, + CodeInsightColors.INFORMATION_ATTRIBUTES, + CodeInsightColors.NOT_USED_ELEMENT_ATTRIBUTES, + CodeInsightColors.DEPRECATED_ATTRIBUTES, + CodeInsightColors.MATCHED_BRACE_ATTRIBUTES, + CodeInsightColors.UNMATCHED_BRACE_ATTRIBUTES, + CodeInsightColors.JOIN_POINT, + CodeInsightColors.BLINKING_HIGHLIGHTS_ATTRIBUTES, + CodeInsightColors.HYPERLINK_ATTRIBUTES, + CodeInsightColors.FOLLOWED_HYPERLINK_ATTRIBUTES, + CodeInsightColors.TODO_DEFAULT_ATTRIBUTES, + CodeInsightColors.BOOKMARKS_ATTRIBUTES)); static { for (int i = 0; i < RAINBOW_JB_COLORS_DEFAULT.length; ++i) { @@ -76,7 +92,7 @@ public class RainbowHighlighter { public RainbowHighlighter(@Nullable TextAttributesScheme colorsScheme) { myColorsScheme = colorsScheme != null ? colorsScheme : EditorColorsManager.getInstance().getGlobalScheme(); - myRainbowColors = generateColorSequence(myColorsScheme); + myRainbowColors = generateColorSequence(myColorsScheme, colorsScheme != null); } public static final HighlightInfoType RAINBOW_ELEMENT = new HighlightInfoType.HighlightInfoTypeImpl(HighlightSeverity.INFORMATION, DefaultLanguageHighlighterColors.CONSTANT); @@ -121,18 +137,14 @@ public class RainbowHighlighter { public static void transferRainbowState(@NotNull SchemeMetaInfo dst, @NotNull SchemeMetaInfo src) { final Properties dstProps = dst.getMetaProperties(); - dstProps.keySet().forEach((key) -> { - if (isRainbowKey(key)) { - dstProps.remove(key); - } - }); + dstProps.entrySet().removeIf(entry -> isRainbowKey(entry.getKey())); src.getMetaProperties().forEach((Object key, Object value) -> { if (isRainbowKey(key) && value instanceof String) { dstProps.setProperty((String)key, (String)value); } }); } - + @NotNull public static String generatePaletteExample(@NotNull String indent) { int stopCount = RAINBOW_COLOR_KEYS.length; @@ -150,7 +162,7 @@ public class RainbowHighlighter { final String minor = String.valueOf(i % stopCount); sb.append((i % stopCount == 0) ? "Color#" + anchor : ("SC" + anchor + "." + minor)); sb.append(""); - if (needLineBreak && i == RAINBOW_TEMP_KEYS.length/2) { + if (needLineBreak && i == RAINBOW_TEMP_KEYS.length / 2) { sb.append(indent); needLineBreak = false; //noinspection AssignmentToForLoopParameter @@ -171,7 +183,7 @@ public class RainbowHighlighter { } @NotNull - private static Color[] generateColorSequence(@NotNull TextAttributesScheme colorsScheme) { + private static Color[] generateColorSequence(@NotNull TextAttributesScheme colorsScheme, boolean withCorrectionAndCaching) { String colorDump = ApplicationManager.getApplication().isUnitTestMode() ? UNIT_TEST_COLORS : Registry.get("rainbow.highlighter.colors").asString(); @@ -181,19 +193,170 @@ public class RainbowHighlighter { return registryColors.stream().map(s -> ColorUtil.fromHex(s.trim())).toArray(Color[]::new); } - List stopColors = ContainerUtil.map(RAINBOW_COLOR_KEYS, key -> colorsScheme.getAttributes(key).getForegroundColor()); - List colors = ColorGenerator.generateLinearColorSequence(stopColors, RAINBOW_COLORS_BETWEEN); + if (withCorrectionAndCaching) { + Color[] colors = getColorsFromCache(colorsScheme); + if (colors != null) { + return colors; + } + } + return generateColors(colorsScheme, withCorrectionAndCaching); + } + + @NotNull + @TestOnly + public static Color[] testRainbowGenerateColors(@NotNull TextAttributesScheme colorsScheme) { + return generateColors(colorsScheme, true); + } + + @NotNull + private static Color[] generateColors(@NotNull TextAttributesScheme colorsScheme, boolean withCorrectionAndCaching) { + List stopRainbowColors = + ContainerUtil.map(RAINBOW_COLOR_KEYS, key -> getRainbowColorFromAttribute(colorsScheme.getAttributes(key))); + List rainbowColors = ColorGenerator.generateLinearColorSequence(stopRainbowColors, RAINBOW_COLORS_BETWEEN); + + if (withCorrectionAndCaching && (colorsScheme instanceof EditorColorsScheme)) { + final EditorColorPalette palette = EditorColorPaletteFactory + .getInstance() + .getPalette((EditorColorsScheme)colorsScheme, Language.ANY) + .collectColorsWithFilter(attr -> getRainbowColorFromAttribute(attr), true); + + + final List> colorCircles = new ArrayList<>(); + final Color background = ((EditorColorsScheme)colorsScheme).getDefaultBackground(); + final boolean schemeIsDark = ColorUtil.isDark(background); + final double minDistanceWithOrdinal = schemeIsDark ? 0.06 : 0.10; + final double minDistanceWithDiagnostic = schemeIsDark ? 0.12 : 0.20; + colorCircles.add(Pair.create(background, 0.24)); + + palette.getEntries().forEach(entry -> colorCircles.add(Pair.create(entry.getKey(), + Collections.disjoint(CODE_INSIGHT_CONFLICT_KEYS, entry.getValue()) + ? minDistanceWithOrdinal + : minDistanceWithDiagnostic))); + rainbowColors = ContainerUtil.map(rainbowColors, rainbowColor -> resolveConflict(colorCircles, rainbowColor, 0)); + + + final Map cache = getGeneratedTextAttributesCache(colorsScheme); + if (cache != null) { + cache.entrySet().removeIf(entry -> isRainbowTempKey(entry.getKey())); + int i = 0; + for (Color rainbowColor : rainbowColors) { + TextAttributesKey key = createRainbowKey(i++, rainbowColor); + cache.put(key, key.getDefaultAttributes()); + } + } + } + return rainbowColors.toArray(new Color[rainbowColors.size()]); + } + + private static Color resolveConflict(@NotNull final List> colorCircles, @NotNull final Color sampleColor, int nestLevel) { + if (nestLevel > 4) { + return sampleColor; + } + for (Pair circle: colorCircles) { + final Color paletteColor = circle.first; + final double distance = colorDistance01(sampleColor, paletteColor); + if (distance < circle.second) { + final float[] rgb = rgbDiffColor(sampleColor, paletteColor); + final double factor = 256 * circle.second / getLength(rgb); + final int mod = nestLevel % 4; + final int r = noramalize(sampleColor.getRed() + rgb[0] * factor * (mod == 3 ? 2 : 1)); + final int g = noramalize(sampleColor.getGreen() + rgb[1] * factor * (mod == 1 ? 2 : 1)); + final int b = noramalize(sampleColor.getBlue() + rgb[2] * factor * (mod == 2 ? 2 : 1)); + final float[] hsbNew = Color.RGBtoHSB(r, g, b, null); + final float[] hsbOrig = Color.RGBtoHSB(sampleColor.getRed(), sampleColor.getGreen(), sampleColor.getBlue(), null); + + //System.out.println("#" + nestLevel + ":" + sampleColor + " diff:" + circle.second + " conflict:" + circle.first + " now:" +Color.getHSBColor(hsbNew[0], hsbNew[1], hsbOrig[2])); + return resolveConflict(colorCircles, + Color.getHSBColor(hsbNew[0], hsbNew[1], (hsbOrig[2] + hsbNew[2])/2), + ++nestLevel); + } + } + return sampleColor; + } + + private static int noramalize(double b) { + return Math.min(Math.max(1, (int)b), 254); + } + + public static double colorDistance01(@NotNull Color c1, @NotNull Color c2) { + return getLength(YPbPr01(rgbDiffColor(c1, c2))); + } + + private static double getLength(@NotNull float[] components) { + return Math.sqrt(components[0] * components[0] + components[1] * components[1] + components[2] * components[2]); + } + + @NotNull + private static float[] rgbDiffColor(@NotNull Color c1, @NotNull Color c2) { + return new float[] { + (float)(c1.getRed() - c2.getRed()), + (float)(c1.getGreen() - c2.getGreen()), + (float)(c1.getBlue() - c2.getBlue()) + }; + } + + @NotNull + @Contract(pure = true) + private static float[] YPbPr01(@NotNull float[] rgb) { + // http://www.equasys.de/colorconversion.html + return new float[]{ + (float)(( 0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2])/256), + (float)(( -0.169 * rgb[0] - 0.331 * rgb[1] + 0.500 * rgb[2])/256), + (float)(( +0.500 * rgb[0] - 0.419 * rgb[1] - 0.081 * rgb[2])/256) + }; + } + + @Nullable + private static Color[] getColorsFromCache(@NotNull TextAttributesScheme colorsScheme) { + final Map cache = getGeneratedTextAttributesCache(colorsScheme); + if (cache == null) { + return null; + } + List colors = new ArrayList<>(); + boolean invalidCache = false; + for (TextAttributesKey tempKey : RAINBOW_TEMP_KEYS) { + final TextAttributes attributes = cache.get(tempKey); + if (attributes == null) { + invalidCache = true; + break; + } + colors.add(getRainbowColorFromAttribute(attributes)); + } + if (invalidCache) { + return null; + } return colors.toArray(new Color[colors.size()]); } + @Nullable + private static Map getGeneratedTextAttributesCache(@NotNull TextAttributesScheme colorsScheme) { + return (colorsScheme instanceof EditorGeneratedTextAttributesCache) + ? ((EditorGeneratedTextAttributesCache)colorsScheme).getGeneratedTextAttributesCache() + : null; + } + + @Nullable + public static Color getRainbowColorFromAttribute(@NotNull TextAttributes attributes) { + return attributes.getForegroundColor(); + } + + private static void setRainbowColorToAttribute(@NotNull TextAttributes attributes, @Nullable Color rainbowColor) { + attributes.setForegroundColor(rainbowColor); + } + + @NotNull + private static TextAttributesKey createRainbowKey(int i, Color rainbowColor) { + //noinspection deprecation + TextAttributesKey key = TextAttributesKey.createTextAttributesKey(RAINBOW_TEMP_PREF + i, new TextAttributes()); + key.getDefaultAttributes().setForegroundColor(rainbowColor); + return key; + } + @NotNull public TextAttributesKey[] getRainbowTempKeys() { TextAttributesKey[] keys = new TextAttributesKey[myRainbowColors.length]; for (int i = 0; i < myRainbowColors.length; ++i) { - //noinspection deprecation - TextAttributesKey key = TextAttributesKey.createTextAttributesKey(RAINBOW_TEMP_PREF + i, new TextAttributes()); - key.getDefaultAttributes().setForegroundColor(myRainbowColors[i]); - keys[i] = key; + keys[i] = createRainbowKey(i, myRainbowColors[i]); } return keys; } @@ -227,9 +390,9 @@ public class RainbowHighlighter { private static final TextAttributesKey[] RAINBOW_TEMP_KEYS = new RainbowHighlighter(null).getRainbowTempKeys(); @NotNull - public static TextAttributes createRainbowAttribute(@Nullable Color color) { + public static TextAttributes createRainbowAttribute(@Nullable Color color) { TextAttributes ret = new TextAttributes(); - ret.setForegroundColor(color); + setRainbowColorToAttribute(ret, color); return ret; } diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/ExternalAnnotatorBatchInspection.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/ExternalAnnotatorBatchInspection.java index 306539caf6ff..8f75f9a37446 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/ExternalAnnotatorBatchInspection.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/ExternalAnnotatorBatchInspection.java @@ -42,6 +42,7 @@ public interface ExternalAnnotatorBatchInspection extends PairedUnfairLocalInspe /** * To be invoked during batch run */ + @NotNull default ProblemDescriptor[] checkFile(@NotNull PsiFile file, @NotNull GlobalInspectionContext context, @NotNull InspectionManager manager) { diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionElementsMergerBase.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionElementsMergerBase.java index 64f4c52c4f1a..72d8c227b3f4 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionElementsMergerBase.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionElementsMergerBase.java @@ -19,6 +19,7 @@ import com.intellij.lang.annotation.HighlightSeverity; import com.intellij.openapi.util.JDOMUtil; import com.intellij.openapi.util.WriteExternalException; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; import java.util.*; @@ -164,6 +165,7 @@ public abstract class InspectionElementsMergerBase extends InspectionElementsMer return myMerger.getMergedToolName(); } + @NotNull @Override public String[] getSourceToolNames() { return myMerger.getSourceToolNames(); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java index 15961321ddb7..a2540eaea924 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/ex/InspectionProfileImpl.java @@ -594,6 +594,7 @@ public class InspectionProfileImpl extends NewInspectionProfile { return merger.getMergedToolName(); } + @NotNull @Override public String[] getSourceToolNames() { return merger.getSourceToolNames(); diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefDirectoryImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefDirectoryImpl.java index 2e21b9c33931..60cbd4fae064 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefDirectoryImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefDirectoryImpl.java @@ -21,8 +21,9 @@ package com.intellij.codeInspection.reference; import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.module.Module; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.module.ModuleUtilCore; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiDirectory; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NotNull; @@ -53,6 +54,17 @@ public class RefDirectoryImpl extends RefElementImpl implements RefDirectory{ ApplicationManager.getApplication().runReadAction(() -> visitor.visitDirectory(this)); } + @Override + public boolean isValid() { + if (isDeleted()) return false; + return ReadAction.compute(() -> { + if (getRefManager().getProject().isDisposed()) return false; + + VirtualFile directory = getVirtualFile(); + return directory != null && directory.isValid(); + }); + } + @Nullable @Override public RefModule getModule() { diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java index a4eb67ae6842..9e98ba44659c 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefElementImpl.java @@ -26,8 +26,8 @@ package com.intellij.codeInspection.reference; import com.intellij.codeInspection.SuppressionUtil; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.ReadAction; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.util.Computable; import com.intellij.openapi.util.Iconable; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; @@ -77,24 +77,25 @@ public abstract class RefElementImpl extends RefEntityImpl implements RefElement myFlags = 0; } + protected boolean isDeleted() { + return myIsDeleted; + } + @Override public boolean isValid() { if (myIsDeleted) return false; - return ApplicationManager.getApplication().runReadAction(new Computable() { - @Override - public Boolean compute() { - if (getRefManager().getProject().isDisposed()) return false; + return ReadAction.compute(() -> { + if (getRefManager().getProject().isDisposed()) return false; - final PsiFile file = myID.getContainingFile(); - //no need to check resolve in offline mode - if (ApplicationManager.getApplication().isHeadlessEnvironment()) { - return file != null && file.isPhysical(); - } - - final PsiElement element = getElement(); - return element != null && element.isPhysical(); + final PsiFile file = myID.getContainingFile(); + //no need to check resolve in offline mode + if (ApplicationManager.getApplication().isHeadlessEnvironment()) { + return file != null && file.isPhysical(); } - }).booleanValue(); + + final PsiElement element = getElement(); + return element != null && element.isPhysical(); + }); } @Override diff --git a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java index 0c4a5c3fb255..81dcd162ea0f 100644 --- a/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java +++ b/platform/analysis-impl/src/com/intellij/codeInspection/reference/RefManagerImpl.java @@ -252,7 +252,13 @@ public class RefManagerImpl extends RefManager { Element problem = new Element("problem"); - if (refEntity instanceof RefElement) { + if (refEntity instanceof RefDirectory) { + Element fileElement = new Element("file"); + VirtualFile virtualFile = ((PsiDirectory)((RefDirectory)refEntity).getElement()).getVirtualFile(); + fileElement.addContent(virtualFile.getUrl()); + problem.addContent(fileElement); + } + else if (refEntity instanceof RefElement) { final RefElement refElement = (RefElement)refEntity; final SmartPsiElementPointer pointer = refElement.getPointer(); PsiFile psiFile = pointer.getContainingFile(); diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java index 336ff1cb5e0a..4693b934633f 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReference.java @@ -527,6 +527,7 @@ public class FileReference implements PsiFileReference, FileReferenceOwner, PsiP } } + @NotNull protected static FileReferenceHelper[] getHelpers() { return FileReferenceHelperRegistrar.getHelpers(); } diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCompletion.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCompletion.java index f78f8bd79724..8a25c0b46d00 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCompletion.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceCompletion.java @@ -16,6 +16,7 @@ package com.intellij.psi.impl.source.resolve.reference.impl.providers; import com.intellij.openapi.components.ServiceManager; +import org.jetbrains.annotations.NotNull; /** * @author yole @@ -25,5 +26,6 @@ public abstract class FileReferenceCompletion { return ServiceManager.getService(FileReferenceCompletion.class); } + @NotNull public abstract Object[] getFileReferenceCompletionVariants(FileReference reference); } diff --git a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceHelperRegistrar.java b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceHelperRegistrar.java index 6ce754b2e77b..62b38dd6bffd 100644 --- a/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceHelperRegistrar.java +++ b/platform/analysis-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/providers/FileReferenceHelperRegistrar.java @@ -17,7 +17,6 @@ package com.intellij.psi.impl.source.resolve.reference.impl.providers; import com.intellij.openapi.extensions.Extensions; import com.intellij.openapi.project.Project; -import com.intellij.openapi.util.Condition; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiFileSystemItem; import com.intellij.util.containers.ContainerUtil; @@ -33,6 +32,7 @@ public class FileReferenceHelperRegistrar { private FileReferenceHelperRegistrar() { } + @NotNull public static FileReferenceHelper[] getHelpers() { return Extensions.getExtensions(FileReferenceHelper.EP_NAME); } diff --git a/platform/annotations/java8/annotations-java8.iml b/platform/annotations/java8/annotations-java8.iml index dec1433ef065..5ae79d1d7cf9 100644 --- a/platform/annotations/java8/annotations-java8.iml +++ b/platform/annotations/java8/annotations-java8.iml @@ -5,7 +5,7 @@ - + diff --git a/platform/boot/boot.iml b/platform/boot/boot.iml index 9a0e7ff45829..abdc88bd273a 100644 --- a/platform/boot/boot.iml +++ b/platform/boot/boot.iml @@ -6,7 +6,7 @@ - + diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy index 48504f69241b..4308dc893753 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/CommunityRepositoryModules.groovy @@ -152,9 +152,9 @@ class CommunityRepositoryModules { excludeFromModule("xslt-debugger-engine-impl", "serializer.jar") excludeFromModule("xslt-debugger-engine-impl", "xalan.jar") }, - plugin("student-python") { + plugin("Edu-Python") { withResource("resources/courses", "lib/courses") - excludeFromModule("student-python", "courses/**") + excludeFromModule("Edu-Python", "courses/**") }, plugin("maven") { withModule("maven-jps-plugin") diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy index 5de2d6caa060..6051f8cc7767 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/BuildTasksImpl.groovy @@ -364,7 +364,7 @@ idea.fatal.error.notification=disabled checkModules(layout.pluginModulesWithRestrictedCompatibleBuildRange, "productProperties.productLayout.pluginModulesWithRestrictedCompatibleBuildRange") checkProjectLibraries(layout.projectLibrariesToUnpackIntoMainJar, "productProperties.productLayout.projectLibrariesToUnpackIntoMainJar") nonTrivialPlugins.findAll {layout.enabledPluginModules.contains(it.mainModule)}.each { plugin -> - checkModules(plugin.moduleJars.values(), "'$plugin.mainModule' plugin") + checkModules(plugin.moduleJars.values() - plugin.optionalModules, "'$plugin.mainModule' plugin") checkModules(plugin.moduleExcludes.keySet(), "'$plugin.mainModule' plugin") checkProjectLibraries(plugin.includedProjectLibraries, "'$plugin.mainModule' plugin") } diff --git a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy index 4259b14e6fa3..b3946149b4c9 100644 --- a/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy +++ b/platform/build-scripts/groovy/org/jetbrains/intellij/build/impl/LayoutBuilder.groovy @@ -184,6 +184,15 @@ class LayoutBuilder { } } + /** + * Include files and directories specified in {@code inner} into the current place in this layout + */ + def include(@DelegatesTo(LayoutSpec) Closure inner) { + def body = inner.rehydrate(null, this, inner.thisObject) + body.resolveStrategy = Closure.OWNER_FIRST + body() + } + JpsModule findModule(String name) { def module = project.modules.find { it.name == name } if (module == null) { diff --git a/platform/build-scripts/platform-build-scripts.iml b/platform/build-scripts/platform-build-scripts.iml index 122b8972021a..0308e9d5dc5d 100644 --- a/platform/build-scripts/platform-build-scripts.iml +++ b/platform/build-scripts/platform-build-scripts.iml @@ -19,6 +19,7 @@ + @@ -35,6 +36,6 @@ - + \ No newline at end of file diff --git a/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/PathInfo.kt b/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/PathInfo.kt index f5aad5b6f632..71553d06eea2 100644 --- a/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/PathInfo.kt +++ b/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/PathInfo.kt @@ -3,13 +3,19 @@ package org.jetbrains.builtInWebServer import com.intellij.openapi.fileTypes.FileType import com.intellij.openapi.fileTypes.FileTypeManager import com.intellij.openapi.util.io.FileUtilRt +import com.intellij.openapi.vfs.LocalFileSystem import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import com.intellij.util.io.isDirectory +import com.intellij.util.io.systemIndependentPath import java.io.File import java.nio.file.Files import java.nio.file.Path -class PathInfo(val ioFile: Path?, val file: VirtualFile?, val root: VirtualFile, moduleName: String? = null, val isLibrary: Boolean = false, val isRootNameOptionalInPath: Boolean = false) { +class PathInfo(val ioFile: Path?, file: VirtualFile?, val root: VirtualFile, moduleName: String? = null, val isLibrary: Boolean = false, val isRootNameOptionalInPath: Boolean = false) { + var file = file + private set + var moduleName: String? = moduleName set @@ -39,25 +45,36 @@ class PathInfo(val ioFile: Path?, val file: VirtualFile?, val root: VirtualFile, builder.append(FileUtilRt.getRelativePath(relativeTo.path, ioFile!!.toString().replace(File.separatorChar, '/'), '/')) } else { - builder.append(VfsUtilCore.getRelativePath(file, relativeTo, '/')) + builder.append(VfsUtilCore.getRelativePath(file!!, relativeTo, '/')) } return builder.toString() } + + fun getOrResolveVirtualFile(): VirtualFile? { + return if (file == null) { + val result = LocalFileSystem.getInstance().findFileByPath(ioFile!!.systemIndependentPath) + file = result + result + } + else { + file + } + } /** * System-dependent path to file. */ - val filePath: String by lazy { if (ioFile == null) FileUtilRt.toSystemDependentName(file!!.path) else ioFile.toString() } + val filePath: String by lazy { ioFile?.toString() ?: FileUtilRt.toSystemDependentName(file!!.path) } val isValid: Boolean get() = if (ioFile == null) file!!.isValid else Files.exists(ioFile) val name: String - get() = if (ioFile == null) file!!.name else ioFile.fileName.toString() + get() = ioFile?.fileName?.toString() ?: file!!.name val fileType: FileType get() = if (ioFile == null) file!!.fileType else FileTypeManager.getInstance().getFileTypeByFileName(ioFile.fileName.toString()) - fun isDirectory(): Boolean = if (ioFile == null) file!!.isDirectory else Files.isDirectory(ioFile) + fun isDirectory(): Boolean = ioFile?.isDirectory() ?: file!!.isDirectory } \ No newline at end of file diff --git a/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/WebServerRootsProvider.java b/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/WebServerRootsProvider.java deleted file mode 100644 index 3ca8e985be6f..000000000000 --- a/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/WebServerRootsProvider.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.jetbrains.builtInWebServer; - -import com.intellij.openapi.extensions.ExtensionPointName; -import com.intellij.openapi.project.Project; -import com.intellij.openapi.vfs.VirtualFile; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -public abstract class WebServerRootsProvider { - public static final ExtensionPointName EP_NAME = ExtensionPointName.create("org.jetbrains.webServerRootsProvider"); - - @Nullable - public abstract PathInfo resolve(@NotNull String path, @NotNull Project project); - - @Nullable - public abstract PathInfo getPathInfo(@NotNull VirtualFile file, @NotNull Project project); - - public boolean isClearCacheOnFileContentChanged(@NotNull VirtualFile file) { - return false; - } -} \ No newline at end of file diff --git a/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/WebServerRootsProvider.kt b/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/WebServerRootsProvider.kt new file mode 100644 index 000000000000..10979eff1bb2 --- /dev/null +++ b/platform/built-in-server-api/src/org/jetbrains/builtInWebServer/WebServerRootsProvider.kt @@ -0,0 +1,19 @@ +package org.jetbrains.builtInWebServer + +import com.intellij.openapi.extensions.ExtensionPointName +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile + +data class PathQuery(val searchInLibs: Boolean = true, val searchInArtifacts: Boolean = true, val useHtaccess: Boolean = true, val useVfs: Boolean = false) + +abstract class WebServerRootsProvider { + companion object { + val EP_NAME = ExtensionPointName.create("org.jetbrains.webServerRootsProvider") + } + + abstract fun resolve(path: String, project: Project, pathQuery: PathQuery): PathInfo? + + abstract fun getPathInfo(file: VirtualFile, project: Project): PathInfo? + + open fun isClearCacheOnFileContentChanged(file: VirtualFile) = false +} \ No newline at end of file diff --git a/platform/built-in-server/built-in-server-tests.iml b/platform/built-in-server/built-in-server-tests.iml index be409c1a21d0..61a00fc9e4bf 100644 --- a/platform/built-in-server/built-in-server-tests.iml +++ b/platform/built-in-server/built-in-server-tests.iml @@ -5,7 +5,7 @@ - + diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt index bb66c8c50145..5fb00fc83344 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/BuiltInWebServer.kt @@ -96,10 +96,16 @@ class BuiltInWebServer : HttpRequestHandler() { if (urlDecoder.path().length < 2) { return false } + projectName = null } else { - projectName = host + if (host.endsWith(".localhost")) { + projectName = host.substring(0, host.lastIndexOf('.')) + } + else { + projectName = host + } } return doProcess(urlDecoder, request, context, projectName) } @@ -238,7 +244,7 @@ private fun doProcess(urlDecoder: QueryStringDecoder, request: FullHttpRequest, return false } -internal fun HttpRequest.isSignedRequest(): Boolean { +fun HttpRequest.isSignedRequest(): Boolean { if (BuiltInServerOptions.getInstance().allowUnsignedRequests) { return true } @@ -252,7 +258,7 @@ internal fun HttpRequest.isSignedRequest(): Boolean { return token != null && tokens.getIfPresent(token) != null } -internal fun validateToken(request: HttpRequest, channel: Channel, isSignedRequest: Boolean): HttpHeaders? { +fun validateToken(request: HttpRequest, channel: Channel, isSignedRequest: Boolean): HttpHeaders? { if (BuiltInServerOptions.getInstance().allowUnsignedRequests) { return EmptyHttpHeaders.INSTANCE } diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerPathHandler.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerPathHandler.kt index 5ed656355f21..09415f63a4e6 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerPathHandler.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerPathHandler.kt @@ -55,7 +55,7 @@ private class DefaultWebServerPathHandler : WebServerPathHandler() { val pathToFileManager = WebServerPathToFileManager.getInstance(project) var pathInfo = pathToFileManager.pathToInfoCache.getIfPresent(path) if (pathInfo == null || !pathInfo.isValid) { - pathInfo = pathToFileManager.doFindByRelativePath(path) + pathInfo = pathToFileManager.doFindByRelativePath(path, defaultPathQuery) if (pathInfo == null) { HttpResponseStatus.NOT_FOUND.send(channel, request, extraHeaders = extraHeaders) return true diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.kt index 53eed403e089..4b9de5cd4809 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/DefaultWebServerRootsProvider.kt @@ -35,8 +35,12 @@ import com.intellij.project.rootManager import com.intellij.util.PlatformUtils import com.intellij.util.containers.computeOrNull +internal data class SuitableRoot(val file: VirtualFile, val moduleQualifier: String?) + private class DefaultWebServerRootsProvider : WebServerRootsProvider() { - override fun resolve(path: String, project: Project): PathInfo? { + override fun resolve(path: String, project: Project, pathQuery: PathQuery): PathInfo? { + val pathToFileManager = WebServerPathToFileManager.getInstance(project) + var effectivePath = path if (PlatformUtils.isIntelliJ()) { val index = effectivePath.indexOf('/') @@ -45,9 +49,9 @@ private class DefaultWebServerRootsProvider : WebServerRootsProvider() { val module = runReadAction { ModuleManager.getInstance(project).findModuleByName(moduleName) } if (module != null && !module.isDisposed) { effectivePath = effectivePath.substring(index + 1) - val resolver = WebServerPathToFileManager.getInstance(project).getResolver(effectivePath) - val result = RootProvider.values().computeOrNull { findByRelativePath(effectivePath, it.getRoots(module.rootManager), resolver, moduleName) } - ?: findInModuleLibraries(effectivePath, module, resolver) + val resolver = pathToFileManager.getResolver(effectivePath) + val result = RootProvider.values().computeOrNull { findByRelativePath(effectivePath, it.getRoots(module.rootManager), resolver, moduleName, pathQuery) } + ?: findInModuleLibraries(effectivePath, module, resolver, pathQuery) if (result != null) { return result } @@ -55,42 +59,76 @@ private class DefaultWebServerRootsProvider : WebServerRootsProvider() { } } - val resolver = WebServerPathToFileManager.getInstance(project).getResolver(effectivePath) + val resolver = pathToFileManager.getResolver(effectivePath) val modules = runReadAction { ModuleManager.getInstance(project).modules } - for (rootProvider in RootProvider.values()) { - for (module in modules) { - if (module.isDisposed) { - continue - } - - findByRelativePath(path, rootProvider.getRoots(module.rootManager), resolver, null)?.let { - it.moduleName = getModuleNameQualifier(project, module) - return it - } + if (pathQuery.useVfs) { + var oldestParent = path.indexOf("/").let { if (it > 0) path.substring(0, it) else null } + if (oldestParent == null && !path.isEmpty() && !path.contains('.')) { + // maybe it is top level directory? (in case of dart projects - web) + oldestParent = path } - } - - // https://youtrack.jetbrains.com/issue/WEB-24283 - for (rootProvider in RootProvider.values()) { - for (module in modules) { - if (module.isDisposed) { - continue - } - - for (root in rootProvider.getRoots(module.rootManager)) { - if (resolver.resolve("/config.json", root) != null) { - resolver.resolve("/index.html", root)?.let { - it.moduleName = getModuleNameQualifier(project, module) - return it - } + + if (oldestParent != null) { + for (root in pathToFileManager.parentToSuitableRoot.get(oldestParent)) { + root.file.findFileByRelativePath(path)?.let { + return PathInfo(null, it, root.file, root.moduleQualifier) } } } } + else { + for (rootProvider in RootProvider.values()) { + for (module in modules) { + if (module.isDisposed) { + continue + } - return findInLibraries(project, effectivePath, resolver) + findByRelativePath(path, rootProvider.getRoots(module.rootManager), resolver, null, pathQuery)?.let { + it.moduleName = getModuleNameQualifier(project, module) + return it + } + } + } + } + + if (!pathQuery.searchInLibs) { + // yes, if !searchInLibs, config.json is also not checked + return null + } + + fun findByConfigJson(): PathInfo? { + // https://youtrack.jetbrains.com/issue/WEB-24283 + for (rootProvider in RootProvider.values()) { + for (module in modules) { + if (module.isDisposed) { + continue + } + + for (root in rootProvider.getRoots(module.rootManager)) { + if (resolver.resolve("config.json", root, pathQuery = pathQuery) != null) { + resolver.resolve("index.html", root, pathQuery = pathQuery)?.let { + it.moduleName = getModuleNameQualifier(project, module) + return it + } + } + } + } + } + return null + } + + val exists = pathToFileManager.pathToExistShortTermCache.getIfPresent("config.json") + if (exists == null || exists) { + val result = findByConfigJson() + pathToFileManager.pathToExistShortTermCache.put("config.json", result != null) + if (result != null) { + return result + } + } + + return findInLibraries(project, effectivePath, resolver, pathQuery) } - + override fun getPathInfo(file: VirtualFile, project: Project): PathInfo? { return runReadAction { val directoryIndex = DirectoryIndex.getInstance(project) @@ -138,7 +176,7 @@ private class DefaultWebServerRootsProvider : WebServerRootsProvider() { } } -private enum class RootProvider { +internal enum class RootProvider { SOURCE { override fun getRoots(rootManager: ModuleRootManager): Array = rootManager.sourceRoots }, @@ -169,7 +207,7 @@ private fun getJavadocOrderRootType(): OrderRootType? { } } -private fun findInModuleLibraries(path: String, module: Module, resolver: FileResolver): PathInfo? { +private fun findInModuleLibraries(path: String, module: Module, resolver: FileResolver, pathQuery: PathQuery): PathInfo? { val index = path.indexOf('/') if (index <= 0) { return null @@ -179,12 +217,12 @@ private fun findInModuleLibraries(path: String, module: Module, resolver: FileRe val relativePath = path.substring(index + 1) return ORDER_ROOT_TYPES.computeOrNull { findInModuleLevelLibraries(module, it) { root, module -> - if (StringUtil.equalsIgnoreCase(root.nameSequence, libraryFileName)) resolver.resolve(relativePath, root, isLibrary = true) else null + if (StringUtil.equalsIgnoreCase(root.nameSequence, libraryFileName)) resolver.resolve(relativePath, root, isLibrary = true, pathQuery = pathQuery) else null } } } -private fun findInLibraries(project: Project, path: String, resolver: FileResolver): PathInfo? { +private fun findInLibraries(project: Project, path: String, resolver: FileResolver, pathQuery: PathQuery): PathInfo? { val index = path.indexOf('/') if (index < 0) { return null @@ -193,7 +231,7 @@ private fun findInLibraries(project: Project, path: String, resolver: FileResolv val libraryFileName = path.substring(0, index) val relativePath = path.substring(index + 1) return findInLibrariesAndSdk(project, ORDER_ROOT_TYPES) { root, module -> - if (StringUtil.equalsIgnoreCase(root.nameSequence, libraryFileName)) resolver.resolve(relativePath, root, isLibrary = true) else null + if (StringUtil.equalsIgnoreCase(root.nameSequence, libraryFileName)) resolver.resolve(relativePath, root, isLibrary = true, pathQuery = pathQuery) else null } } @@ -204,14 +242,14 @@ private fun getInfoForDocJar(file: VirtualFile, project: Project): PathInfo? { } } -private fun getModuleNameQualifier(project: Project, module: Module?): String? { +internal fun getModuleNameQualifier(project: Project, module: Module?): String? { if (module != null && PlatformUtils.isIntelliJ() && !(module.name.equals(project.name, ignoreCase = true) || compareNameAndProjectBasePath(module.name, project))) { return module.name } return null } -private fun findByRelativePath(path: String, roots: Array, resolver: FileResolver, moduleName: String?) = roots.computeOrNull { resolver.resolve(path, it, moduleName) } +private fun findByRelativePath(path: String, roots: Array, resolver: FileResolver, moduleName: String?, pathQuery: PathQuery) = roots.computeOrNull { resolver.resolve(path, it, moduleName, pathQuery = pathQuery) } private fun findInLibrariesAndSdk(project: Project, rootTypes: Array, fileProcessor: (root: VirtualFile, module: Module?) -> PathInfo?): PathInfo? { fun findInLibraryTable(table: LibraryTable, rootType: OrderRootType) = table.libraryIterator.computeOrNull { it.getFiles(rootType).computeOrNull { fileProcessor(it, null) } } diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/PrefixlessWebServerRootsProvider.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/PrefixlessWebServerRootsProvider.kt index e337c5d54154..c03d97d0e0c7 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/PrefixlessWebServerRootsProvider.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/PrefixlessWebServerRootsProvider.kt @@ -3,7 +3,7 @@ package org.jetbrains.builtInWebServer import com.intellij.openapi.project.Project abstract class PrefixlessWebServerRootsProvider : WebServerRootsProvider() { - override final fun resolve(path: String, project: Project) = resolve(path, project, WebServerPathToFileManager.getInstance(project).getResolver(path)) + override final fun resolve(path: String, project: Project, pathQuery: PathQuery) = resolve(path, project, WebServerPathToFileManager.getInstance(project).getResolver(path), pathQuery) - abstract fun resolve(path: String, project: Project, resolver: FileResolver): PathInfo? + abstract fun resolve(path: String, project: Project, resolver: FileResolver, pathQuery: PathQuery): PathInfo? } \ No newline at end of file diff --git a/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathToFileManager.kt b/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathToFileManager.kt index 145a0eabaddc..0194969ab7f3 100644 --- a/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathToFileManager.kt +++ b/platform/built-in-server/src/org/jetbrains/builtInWebServer/WebServerPathToFileManager.kt @@ -1,9 +1,13 @@ package org.jetbrains.builtInWebServer +import com.google.common.base.Function import com.google.common.cache.CacheBuilder +import com.google.common.cache.CacheLoader import com.intellij.ProjectTopics import com.intellij.openapi.application.Application +import com.intellij.openapi.application.runReadAction import com.intellij.openapi.components.ServiceManager +import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootEvent import com.intellij.openapi.roots.ModuleRootListener @@ -13,22 +17,57 @@ import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.openapi.vfs.newvfs.BulkFileListener import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent import com.intellij.openapi.vfs.newvfs.events.VFileEvent +import com.intellij.project.rootManager +import com.intellij.util.SmartList import com.intellij.util.containers.computeOrNull import com.intellij.util.io.exists -import com.intellij.util.io.systemIndependentPath import java.nio.file.Paths import java.util.concurrent.TimeUnit +private val cacheSize: Long = 4096 * 4 + /** * Implement [WebServerRootsProvider] to add your provider */ class WebServerPathToFileManager(application: Application, private val project: Project) { - val pathToInfoCache = CacheBuilder.newBuilder().maximumSize(512).expireAfterAccess(10, TimeUnit.MINUTES).build()!! + val pathToInfoCache = CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterAccess(10, TimeUnit.MINUTES).build()!! // time to expire should be greater than pathToFileCache - private val virtualFileToPathInfo = CacheBuilder.newBuilder().maximumSize(512).expireAfterAccess(11, TimeUnit.MINUTES).build() + private val virtualFileToPathInfo = CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterAccess(11, TimeUnit.MINUTES).build() + + internal val pathToExistShortTermCache = CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterAccess(5, TimeUnit.SECONDS).build()!! + /** + * https://youtrack.jetbrains.com/issue/WEB-25900 + * + * Compute suitable roots for oldest parent (web/foo/my/file.dart -> oldest is web and we compute all suitable roots for it in advance) to avoid linear search + * (i.e. to avoid two queries for root if files web/foo and web/bar requested if root doesn't have web dir) + */ + internal val parentToSuitableRoot = CacheBuilder.newBuilder().maximumSize(cacheSize).expireAfterAccess(10, TimeUnit.MINUTES).build>( + CacheLoader.from(Function { path -> + val suitableRoots = SmartList() + var moduleQualifier: String? = null + val modules = runReadAction { ModuleManager.getInstance(project).modules } + for (rootProvider in RootProvider.values()) { + for (module in modules) { + if (module.isDisposed) { + continue + } + + for (root in rootProvider.getRoots(module.rootManager)) { + if (root.findChild(path!!) != null) { + if (moduleQualifier == null) { + moduleQualifier = getModuleNameQualifier(project, module) + } + suitableRoots.add(SuitableRoot(root, moduleQualifier)) + } + } + } + } + suitableRoots + }))!! + init { - application.messageBus.connect(project).subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() { + application.messageBus.connect(project).subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener { override fun after(events: List) { for (event in events) { if (event is VFileContentChangeEvent) { @@ -61,19 +100,29 @@ class WebServerPathToFileManager(application: Application, private val project: private fun clearCache() { pathToInfoCache.invalidateAll() virtualFileToPathInfo.invalidateAll() + pathToExistShortTermCache.invalidateAll() + parentToSuitableRoot.invalidateAll() } - @JvmOverloads fun findVirtualFile(path: String, cacheResult: Boolean = true): VirtualFile? { - val pathInfo = getPathInfo(path, cacheResult) ?: return null - return pathInfo.file ?: LocalFileSystem.getInstance().findFileByPath(pathInfo.ioFile!!.systemIndependentPath) + @JvmOverloads fun findVirtualFile(path: String, cacheResult: Boolean = true, pathQuery: PathQuery = defaultPathQuery): VirtualFile? { + return getPathInfo(path, cacheResult, pathQuery)?.getOrResolveVirtualFile() } - @JvmOverloads fun getPathInfo(path: String, cacheResult: Boolean = true): PathInfo? { + @JvmOverloads fun getPathInfo(path: String, cacheResult: Boolean = true, pathQuery: PathQuery = defaultPathQuery): PathInfo? { var pathInfo = pathToInfoCache.getIfPresent(path) if (pathInfo == null || !pathInfo.isValid) { - pathInfo = doFindByRelativePath(path) - if (cacheResult && pathInfo != null && pathInfo.isValid) { - pathToInfoCache.put(path, pathInfo) + if (pathToExistShortTermCache.getIfPresent(path) == false) { + return null + } + + pathInfo = doFindByRelativePath(path, pathQuery) + if (cacheResult) { + if (pathInfo != null && pathInfo.isValid) { + pathToInfoCache.put(path, pathInfo) + } + else { + pathToExistShortTermCache.put(path, false) + } } } return pathInfo @@ -92,8 +141,8 @@ class WebServerPathToFileManager(application: Application, private val project: return result } - internal fun doFindByRelativePath(path: String): PathInfo? { - val result = WebServerRootsProvider.EP_NAME.extensions.computeOrNull { it.resolve(path, project) } ?: return null + internal fun doFindByRelativePath(path: String, pathQuery: PathQuery): PathInfo? { + val result = WebServerRootsProvider.EP_NAME.extensions.computeOrNull { it.resolve(path, project, pathQuery) } ?: return null result.file?.let { virtualFileToPathInfo.put(it, result) } @@ -104,32 +153,32 @@ class WebServerPathToFileManager(application: Application, private val project: } interface FileResolver { - fun resolve(path: String, root: VirtualFile, moduleName: String? = null, isLibrary: Boolean = false): PathInfo? + fun resolve(path: String, root: VirtualFile, moduleName: String? = null, isLibrary: Boolean = false, pathQuery: PathQuery): PathInfo? } private val RELATIVE_PATH_RESOLVER = object : FileResolver { - override fun resolve(path: String, root: VirtualFile, moduleName: String?, isLibrary: Boolean): PathInfo? { + override fun resolve(path: String, root: VirtualFile, moduleName: String?, isLibrary: Boolean, pathQuery: PathQuery): PathInfo? { // WEB-17691 built-in server doesn't serve files it doesn't have in the project tree // temp:// reports isInLocalFileSystem == true, but it is not true - if (root.isInLocalFileSystem && root.fileSystem == LocalFileSystem.getInstance()) { - val file = Paths.get(root.path, path) - if (file.exists()) { - return PathInfo(file, null, root, moduleName, isLibrary) - } - else { - return null - } + if (pathQuery.useVfs || root.fileSystem != LocalFileSystem.getInstance() || path == ".htaccess" || path == "config.json") { + return root.findFileByRelativePath(path)?.let { PathInfo(null, it, root, moduleName, isLibrary) } + } + + val file = Paths.get(root.path, path) + return if (file.exists()) { + PathInfo(file, null, root, moduleName, isLibrary) } else { - val file = root.findFileByRelativePath(path) ?: return null - return PathInfo(null, file, root, moduleName, isLibrary) + null } } } private val EMPTY_PATH_RESOLVER = object : FileResolver { - override fun resolve(path: String, root: VirtualFile, moduleName: String?, isLibrary: Boolean): PathInfo? { + override fun resolve(path: String, root: VirtualFile, moduleName: String?, isLibrary: Boolean, pathQuery: PathQuery): PathInfo? { val file = findIndexFile(root) ?: return null return PathInfo(null, file, root, moduleName, isLibrary) } -} \ No newline at end of file +} + +internal val defaultPathQuery = PathQuery() \ No newline at end of file diff --git a/platform/configuration-store-impl/src/SchemeManagerImpl.kt b/platform/configuration-store-impl/src/SchemeManagerImpl.kt index d9230f360bab..e1659325d95a 100644 --- a/platform/configuration-store-impl/src/SchemeManagerImpl.kt +++ b/platform/configuration-store-impl/src/SchemeManagerImpl.kt @@ -107,7 +107,7 @@ class SchemeManagerImpl(val fileSpec: String, } } - private inner class SchemeFileTracker() : BulkFileListener.Adapter() { + private inner class SchemeFileTracker : BulkFileListener { private fun isMy(file: VirtualFile) = isMy(file.nameSequence) private fun isMy(name: CharSequence) = name.endsWith(schemeExtension, ignoreCase = true) && (processor !is LazySchemeProcessor || processor.isSchemeFile(name)) diff --git a/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt b/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt index ae096d7869c0..0e303dc2eaf9 100644 --- a/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt +++ b/platform/configuration-store-impl/src/StorageVirtualFileTracker.kt @@ -48,7 +48,7 @@ class StorageVirtualFileTracker(private val messageBus: MessageBus) { } private fun addVfsChangesListener() { - messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener.Adapter() { + messageBus.connect().subscribe(VirtualFileManager.VFS_CHANGES, object : BulkFileListener { override fun after(events: MutableList) { eventLoop@ for (event in events) { var storage: StateStorage? diff --git a/platform/configuration-store-impl/testSrc/configuration-store-tests.iml b/platform/configuration-store-impl/testSrc/configuration-store-tests.iml deleted file mode 100644 index 50158e4c8601..000000000000 --- a/platform/configuration-store-impl/testSrc/configuration-store-tests.iml +++ /dev/null @@ -1,23 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/platform/configuration-store-impl/testSrc/xml/XmlSerializerTest.kt b/platform/configuration-store-impl/testSrc/xml/XmlSerializerTest.kt index 3929ec9996db..01d63ad9c522 100644 --- a/platform/configuration-store-impl/testSrc/xml/XmlSerializerTest.kt +++ b/platform/configuration-store-impl/testSrc/xml/XmlSerializerTest.kt @@ -504,15 +504,15 @@ internal class XmlSerializerTest { doSerializerTest("\n" + " ", bean) } - @Test fun conversionFromTextToAttribute() { - @Tag("condition") - class ConditionBean { - @Attribute("expression") - var newCondition: String? = null - @Text - var oldCondition: String? = null - } + @Tag("condition") + private class ConditionBean { + @Attribute("expression") + var newCondition: String? = null + @Text + var oldCondition: String? = null + } + @Test fun conversionFromTextToAttribute() { @Tag("bean") class Bean { @Property(surroundWithTag = false) @@ -528,6 +528,22 @@ internal class XmlSerializerTest { doSerializerTest("\n \n" + "", bean) } + @Test fun `no_wrap`() { + @Tag("bean") + class Bean { + @Property(flat = true) + var conditionBean = ConditionBean() + } + + var bean = Bean() + bean.conditionBean.oldCondition = "2+2" + doSerializerTest("2+2", bean) + + bean = Bean() + bean.conditionBean.newCondition = "2+2" + doSerializerTest("", bean) + } + @Test fun deserializeInto() { val bean = BeanWithPublicFields() bean.STRING_V = "zzz" diff --git a/platform/core-api/src/com/intellij/codeInsight/PsiEquivalenceUtil.java b/platform/core-api/src/com/intellij/codeInsight/PsiEquivalenceUtil.java index 7e0239c15264..f2e15914b58f 100644 --- a/platform/core-api/src/com/intellij/codeInsight/PsiEquivalenceUtil.java +++ b/platform/core-api/src/com/intellij/codeInsight/PsiEquivalenceUtil.java @@ -116,6 +116,7 @@ public class PsiEquivalenceUtil { return areElementsEquivalent(element1, element2, null, false); } + @NotNull public static PsiElement[] getFilteredChildren(@NotNull final PsiElement element, @Nullable Condition isElementSignificantCondition, boolean areCommentsSignificant) { diff --git a/platform/core-api/src/com/intellij/codeInsight/controlflow/ControlFlow.java b/platform/core-api/src/com/intellij/codeInsight/controlflow/ControlFlow.java index bf72d011c6ea..5151910e3fd9 100644 --- a/platform/core-api/src/com/intellij/codeInsight/controlflow/ControlFlow.java +++ b/platform/core-api/src/com/intellij/codeInsight/controlflow/ControlFlow.java @@ -15,9 +15,12 @@ */ package com.intellij.codeInsight.controlflow; +import org.jetbrains.annotations.NotNull; + /** * @author oleg */ public interface ControlFlow { + @NotNull Instruction[] getInstructions(); } diff --git a/platform/core-api/src/com/intellij/lang/CompositeLanguage.java b/platform/core-api/src/com/intellij/lang/CompositeLanguage.java index d089547eb248..5d362692cc90 100644 --- a/platform/core-api/src/com/intellij/lang/CompositeLanguage.java +++ b/platform/core-api/src/com/intellij/lang/CompositeLanguage.java @@ -47,6 +47,7 @@ public class CompositeLanguage extends Language { return myFilters.remove(filter); } + @NotNull public Language[] getLanguageExtensionsForFile(final PsiFile psi) { final List extensions = new ArrayList<>(1); for (LanguageFilter filter : myFilters) { diff --git a/platform/core-api/src/com/intellij/lang/MetaLanguage.java b/platform/core-api/src/com/intellij/lang/MetaLanguage.java index 100b13a84087..f956c9430786 100644 --- a/platform/core-api/src/com/intellij/lang/MetaLanguage.java +++ b/platform/core-api/src/com/intellij/lang/MetaLanguage.java @@ -32,6 +32,7 @@ public abstract class MetaLanguage extends Language { super(ID); } + @NotNull public static MetaLanguage[] all() { return Extensions.getExtensions(EP_NAME); } diff --git a/platform/core-api/src/com/intellij/openapi/application/ModalityState.java b/platform/core-api/src/com/intellij/openapi/application/ModalityState.java index c24153153117..274c94ee7089 100644 --- a/platform/core-api/src/com/intellij/openapi/application/ModalityState.java +++ b/platform/core-api/src/com/intellij/openapi/application/ModalityState.java @@ -49,13 +49,7 @@ public abstract class ModalityState { final Class ex = (Class)Class.forName("com.intellij.openapi.application.impl.ModalityStateEx"); NON_MODAL = ex.newInstance(); } - catch (ClassNotFoundException e) { - throw new IllegalStateException(e); - } - catch (IllegalAccessException e) { - throw new IllegalStateException(e); - } - catch (InstantiationException e) { + catch (ClassNotFoundException | IllegalAccessException | InstantiationException e) { throw new IllegalStateException(e); } } diff --git a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java index 3aefe326178a..152951b5d358 100644 --- a/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java +++ b/platform/core-api/src/com/intellij/openapi/application/TransactionGuard.java @@ -122,6 +122,13 @@ public abstract class TransactionGuard { guard.submitTransaction(parentDisposable, guard.getContextTransaction(), transaction); } + /** + * Logs an error if the given modality state was created in a write-unsafe context. For modalities created in write-safe contexts, + * {@link Application#invokeLater(Runnable, ModalityState)} and similar calls will be guaranteed to also run in a write-safe context. + * {@link ModalityState#NON_MODAL} is always write-safe, {@link ModalityState#any()} is always write-unsafe. + */ + public abstract void assertWriteSafeContext(@NotNull ModalityState modality); + /** * Schedules a given runnable to be executed inside a transaction later on Swing thread. * Same as {@link #submitTransaction(Disposable, Runnable)}, but the runnable is never executed immediately. diff --git a/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java b/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java index 970ecb8c4a34..9d0a2ba27b2a 100644 --- a/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java +++ b/platform/core-api/src/com/intellij/openapi/fileTypes/FileTypeRegistry.java @@ -40,6 +40,7 @@ public abstract class FileTypeRegistry { * * @return The list of file types. */ + @NotNull public abstract FileType[] getRegisteredFileTypes(); /** diff --git a/platform/core-api/src/com/intellij/openapi/util/BuildNumber.java b/platform/core-api/src/com/intellij/openapi/util/BuildNumber.java index 73a0bec3492a..524d37ba883a 100644 --- a/platform/core-api/src/com/intellij/openapi/util/BuildNumber.java +++ b/platform/core-api/src/com/intellij/openapi/util/BuildNumber.java @@ -42,14 +42,14 @@ public class BuildNumber implements Comparable { private static final BuildNumber CURRENT_VERSION = fromFile(); } - @NotNull private final String myProductCode; - private final int[] myComponents; + @NotNull private final String myProductCode; + @NotNull private final int[] myComponents; public BuildNumber(@NotNull String productCode, int baselineVersion, int buildNumber) { this(productCode, new int[]{baselineVersion, buildNumber}); } - public BuildNumber(@NotNull String productCode, int... components) { + public BuildNumber(@NotNull String productCode, @NotNull int... components) { myProductCode = productCode; myComponents = components; } @@ -221,6 +221,7 @@ public class BuildNumber implements Comparable { return myComponents[1]; } + @NotNull public int[] getComponents() { return myComponents; } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/WritingAccessProvider.java b/platform/core-api/src/com/intellij/openapi/vfs/WritingAccessProvider.java index 19f0141b7d78..f0009761d646 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/WritingAccessProvider.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/WritingAccessProvider.java @@ -38,6 +38,7 @@ public abstract class WritingAccessProvider { public abstract boolean isPotentiallyWritable(@NotNull VirtualFile file); + @NotNull public static WritingAccessProvider[] getProvidersForProject(Project project) { return project == null || project.isDefault() ? new WritingAccessProvider[0] : Extensions.getExtensions(EP_NAME, project); } diff --git a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/BulkFileListener.java b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/BulkFileListener.java index 90c521abab8e..761de2b42355 100644 --- a/platform/core-api/src/com/intellij/openapi/vfs/newvfs/BulkFileListener.java +++ b/platform/core-api/src/com/intellij/openapi/vfs/newvfs/BulkFileListener.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2012 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,11 +24,12 @@ import java.util.List; * @author max */ public interface BulkFileListener { + /** @deprecated obsolete, implement {@link BulkFileListener} directly (to be removed in IDEA 2019) */ class Adapter implements BulkFileListener { @Override public void before(@NotNull List events) { } @Override public void after(@NotNull List events) { } } - void before(@NotNull List events); - void after(@NotNull List events); + default void before(@NotNull List events) { } + default void after(@NotNull List events) { } } \ No newline at end of file diff --git a/platform/core-api/src/com/intellij/psi/PsiReferenceService.java b/platform/core-api/src/com/intellij/psi/PsiReferenceService.java index d6433a59f3cb..a39988f7949a 100644 --- a/platform/core-api/src/com/intellij/psi/PsiReferenceService.java +++ b/platform/core-api/src/com/intellij/psi/PsiReferenceService.java @@ -47,6 +47,7 @@ public abstract class PsiReferenceService { @NotNull public abstract List getReferences(@NotNull final PsiElement element, @NotNull final Hints hints); + @NotNull public PsiReference[] getContributedReferences(@NotNull final PsiElement element) { final List list = getReferences(element, Hints.NO_HINTS); return list.toArray(new PsiReference[list.size()]); diff --git a/platform/core-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java b/platform/core-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java index 1ec14c92b581..481762a0740a 100644 --- a/platform/core-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java +++ b/platform/core-api/src/com/intellij/psi/codeStyle/CodeStyleManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2000-2011 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. @@ -268,6 +268,16 @@ public abstract class CodeStyleManager { public abstract T performActionWithFormatterDisabled(Computable r); + /** + * Calculates minimum spacing, allowed by formatting model (in columns) for a block starting at given offset, + * relative to its previous sibling block. + * Returns -1, if required block cannot be found at provided offset, + * or spacing cannot be calculated due to some other reason. + */ + public int getSpacing(@NotNull PsiFile file, int offset) { + return -1; + } + /** * Retrieves the current formatting mode. * diff --git a/platform/core-api/src/com/intellij/psi/impl/ElementBase.java b/platform/core-api/src/com/intellij/psi/impl/ElementBase.java index 417d426b2dcc..604aaa4d52c7 100644 --- a/platform/core-api/src/com/intellij/psi/impl/ElementBase.java +++ b/platform/core-api/src/com/intellij/psi/impl/ElementBase.java @@ -53,12 +53,11 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable public static final int FLAGS_LOCKED = 0x800; private static final NullableFunction ICON_COMPUTE = request -> { - final PsiElement element = request.getElement(); - if (element == null || !element.isValid() || element.getProject().isDisposed()) return null; + PsiElement element = request.myPointer.getElement(); + if (element == null) return null; - int flags = request.getFlags(); - Icon icon = computeIconNow(element, flags); - LastComputedIcon.put(element, icon, flags); + Icon icon = computeIconNow(element, request.myFlags); + LastComputedIcon.put(element, icon, request.myFlags); return icon; }; @@ -185,19 +184,6 @@ public abstract class ElementBase extends UserDataHolderBase implements Iconable myFlags = flags; } - @Nullable - public PsiElement getElement() { - if (myPointer.getProject().isDisposed()) return null; - PsiElement element = myPointer.getElement(); - SmartPointerManager.getInstance(myPointer.getProject()).removePointer(myPointer); - return element; - } - - @Iconable.IconFlags - public int getFlags() { - return myFlags; - } - @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/platform/core-api/src/com/intellij/psi/meta/PsiMetaData.java b/platform/core-api/src/com/intellij/psi/meta/PsiMetaData.java index 6e6314a24cbe..f1202f34c54f 100644 --- a/platform/core-api/src/com/intellij/psi/meta/PsiMetaData.java +++ b/platform/core-api/src/com/intellij/psi/meta/PsiMetaData.java @@ -17,6 +17,7 @@ package com.intellij.psi.meta; import com.intellij.psi.PsiElement; import org.jetbrains.annotations.NonNls; +import org.jetbrains.annotations.NotNull; /** * @author peter @@ -39,5 +40,6 @@ public interface PsiMetaData { * @return objects this meta data depends on. * @see com.intellij.psi.util.CachedValue */ + @NotNull Object[] getDependences(); } diff --git a/platform/core-api/src/com/intellij/psi/tree/IElementType.java b/platform/core-api/src/com/intellij/psi/tree/IElementType.java index 6208bcc31789..2a8f8a6c27b5 100644 --- a/platform/core-api/src/com/intellij/psi/tree/IElementType.java +++ b/platform/core-api/src/com/intellij/psi/tree/IElementType.java @@ -57,7 +57,7 @@ public class IElementType { private static final short MAX_INDEXED_TYPES = 15000; private static short size; // guarded by lock - private static volatile IElementType[] ourRegistry; // writes are guarded by lock + private static volatile IElementType[] ourRegistry = EMPTY_ARRAY; // writes are guarded by lock private static final Object lock = new String("registry lock"); static { @@ -67,6 +67,7 @@ public class IElementType { push(init); } + @NotNull static IElementType[] push(@NotNull IElementType[] types) { synchronized (lock) { IElementType[] oldRegistry = ourRegistry; diff --git a/platform/core-api/src/com/intellij/testFramework/BinaryLightVirtualFile.java b/platform/core-api/src/com/intellij/testFramework/BinaryLightVirtualFile.java index a2123060eed0..fb7582782706 100644 --- a/platform/core-api/src/com/intellij/testFramework/BinaryLightVirtualFile.java +++ b/platform/core-api/src/com/intellij/testFramework/BinaryLightVirtualFile.java @@ -42,21 +42,21 @@ public class BinaryLightVirtualFile extends LightVirtualFileBase { this(name, ArrayUtil.EMPTY_BYTE_ARRAY); } - public BinaryLightVirtualFile(@NonNls String name, byte[] content) { + public BinaryLightVirtualFile(@NonNls String name, @NotNull byte[] content) { this(name, null, content, LocalTimeCounter.currentTime()); } - public BinaryLightVirtualFile(final String name, final FileType fileType, final byte[] content) { + public BinaryLightVirtualFile(final String name, final FileType fileType, @NotNull byte[] content) { this(name, fileType, content, LocalTimeCounter.currentTime()); } - public BinaryLightVirtualFile(VirtualFile original, final byte[] content, long modificationStamp) { + public BinaryLightVirtualFile(VirtualFile original, @NotNull byte[] content, long modificationStamp) { this(original.getName(), original.getFileType(), content, modificationStamp); } public BinaryLightVirtualFile(final String name, final FileType fileType, - final byte[] content, + @NotNull byte[] content, final long modificationStamp) { super(name, fileType, modificationStamp); setContent(content); @@ -87,16 +87,17 @@ public class BinaryLightVirtualFile extends LightVirtualFileBase { return myContent; } - public void setContent(Object requestor, byte[] content, boolean fireEvent) { + public void setContent(Object requestor, @NotNull byte[] content, boolean fireEvent) { setContent(content); setModificationStamp(LocalTimeCounter.currentTime()); } - private void setContent(byte[] content) { + private void setContent(@NotNull byte[] content) { //StringUtil.assertValidSeparators(content); myContent = content; } + @NotNull public byte[] getContent() { return myContent; } diff --git a/platform/core-api/src/com/intellij/ui/LayeredIcon.java b/platform/core-api/src/com/intellij/ui/LayeredIcon.java index 9dd20d52436c..7f870c1fdacd 100644 --- a/platform/core-api/src/com/intellij/ui/LayeredIcon.java +++ b/platform/core-api/src/com/intellij/ui/LayeredIcon.java @@ -134,6 +134,7 @@ public class LayeredIcon extends JBUI.AuxScalableJBIcon { return myIcons[layer]; } + @NotNull public Icon[] getAllLayers() { return myIcons; } diff --git a/platform/core-api/src/com/intellij/ui/RowIcon.java b/platform/core-api/src/com/intellij/ui/RowIcon.java index af1214786c6e..69cffbf2782a 100644 --- a/platform/core-api/src/com/intellij/ui/RowIcon.java +++ b/platform/core-api/src/com/intellij/ui/RowIcon.java @@ -70,6 +70,7 @@ public class RowIcon extends JBUI.AuxScalableJBIcon { return new RowIcon(this); } + @NotNull private Icon[] myScaledIcons() { if (myScaledIcons != null) { return myScaledIcons; diff --git a/platform/core-api/src/com/intellij/util/IconUtil.java b/platform/core-api/src/com/intellij/util/IconUtil.java index b67dd6f8b101..af928970092a 100644 --- a/platform/core-api/src/com/intellij/util/IconUtil.java +++ b/platform/core-api/src/com/intellij/util/IconUtil.java @@ -202,6 +202,7 @@ public class IconUtil { private static final FileIconProvider[] myProviders = Extensions.getExtensions(FileIconProvider.EP_NAME); } + @NotNull private static FileIconProvider[] getProviders() { return FileIconProviderHolder.myProviders; } @@ -210,6 +211,7 @@ public class IconUtil { private static final FileIconPatcher[] ourPatchers = Extensions.getExtensions(FileIconPatcher.EP_NAME); } + @NotNull private static FileIconPatcher[] getPatchers() { return FileIconPatcherHolder.ourPatchers; } diff --git a/platform/core-api/src/com/intellij/util/PsiIconUtil.java b/platform/core-api/src/com/intellij/util/PsiIconUtil.java index 485242b1a955..bed9d08cb398 100644 --- a/platform/core-api/src/com/intellij/util/PsiIconUtil.java +++ b/platform/core-api/src/com/intellij/util/PsiIconUtil.java @@ -49,6 +49,7 @@ public class PsiIconUtil { private static final IconProvider[] ourIconProviders = Extensions.getExtensions(IconProvider.EXTENSION_POINT_NAME); } + @NotNull private static IconProvider[] getIconProviders() { return IconProviderHolder.ourIconProviders; } diff --git a/platform/core-api/src/com/intellij/util/indexing/FileContent.java b/platform/core-api/src/com/intellij/util/indexing/FileContent.java index d491870151f7..f26abe1df42c 100644 --- a/platform/core-api/src/com/intellij/util/indexing/FileContent.java +++ b/platform/core-api/src/com/intellij/util/indexing/FileContent.java @@ -37,6 +37,7 @@ public interface FileContent extends UserDataHolder { @NotNull String getFileName(); + @NotNull byte[] getContent(); @NotNull diff --git a/platform/core-impl/src/com/intellij/core/CoreFileTypeRegistry.java b/platform/core-impl/src/com/intellij/core/CoreFileTypeRegistry.java index 33b7f059bdfd..d16e6dcae355 100644 --- a/platform/core-impl/src/com/intellij/core/CoreFileTypeRegistry.java +++ b/platform/core-impl/src/com/intellij/core/CoreFileTypeRegistry.java @@ -47,6 +47,7 @@ public class CoreFileTypeRegistry extends FileTypeRegistry { return false; } + @NotNull @Override public FileType[] getRegisteredFileTypes() { return myAllFileTypes.toArray(new FileType[myAllFileTypes.size()]); diff --git a/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java b/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java index aef33bbd9aef..92784387ab48 100644 --- a/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java +++ b/platform/core-impl/src/com/intellij/core/MockDocumentCommitProcessor.java @@ -15,7 +15,7 @@ */ package com.intellij.core; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionId; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; @@ -35,6 +35,6 @@ class MockDocumentCommitProcessor implements DocumentCommitProcessor { public void commitAsynchronously(@NotNull Project project, @NotNull Document document, @NonNls @NotNull Object reason, - @NotNull ModalityState currentModalityState) { + @NotNull TransactionId context) { } } diff --git a/platform/core-impl/src/com/intellij/execution/process/CommandLineArgumentsProvider.java b/platform/core-impl/src/com/intellij/execution/process/CommandLineArgumentsProvider.java index c7ca1e1bcf77..fea61d6ecac6 100644 --- a/platform/core-impl/src/com/intellij/execution/process/CommandLineArgumentsProvider.java +++ b/platform/core-impl/src/com/intellij/execution/process/CommandLineArgumentsProvider.java @@ -18,6 +18,7 @@ package com.intellij.execution.process; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.ArrayUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.util.Collections; @@ -33,6 +34,7 @@ public class CommandLineArgumentsProvider { /** * @return Commands to execute (one command corresponds to one add argument) */ + @NotNull public String[] getArguments() { return ArrayUtil.EMPTY_STRING_ARRAY; } public boolean passParentEnvs() { return false; } diff --git a/platform/core-impl/src/com/intellij/extapi/psi/ASTDelegatePsiElement.java b/platform/core-impl/src/com/intellij/extapi/psi/ASTDelegatePsiElement.java index a07a33f7c79c..0459607ca70a 100644 --- a/platform/core-impl/src/com/intellij/extapi/psi/ASTDelegatePsiElement.java +++ b/platform/core-impl/src/com/intellij/extapi/psi/ASTDelegatePsiElement.java @@ -222,6 +222,7 @@ public abstract class ASTDelegatePsiElement extends PsiElementBase { return nodes.length == 0 ? null : nodes[0].getPsi(); } + @NotNull protected T[] findChildrenByType(IElementType elementType, Class arrayClass) { return ContainerUtil.map2Array(SharedImplUtil.getChildrenOfType(getNode(), elementType), arrayClass, s -> (T)s.getPsi()); } @@ -257,6 +258,7 @@ public abstract class ASTDelegatePsiElement extends PsiElementBase { return result; } + @NotNull protected T[] findChildrenByType(TokenSet elementType, Class arrayClass) { return ContainerUtil.map2Array(getNode().getChildren(elementType), arrayClass, s -> (T)s.getPsi()); } diff --git a/platform/core-impl/src/com/intellij/indentation/OperationParserHelper.java b/platform/core-impl/src/com/intellij/indentation/OperationParserHelper.java index 560f28b73a05..9b0f211b9493 100644 --- a/platform/core-impl/src/com/intellij/indentation/OperationParserHelper.java +++ b/platform/core-impl/src/com/intellij/indentation/OperationParserHelper.java @@ -191,6 +191,7 @@ public class OperationParserHelper { * Provides operation priority and operands * @return array of TokenSets */ + @NotNull TokenSet[] getOperatorsByPriority(); /** @@ -211,6 +212,7 @@ public class OperationParserHelper { * Provides element types to finish binary operation element * @return array of Element Types */ + @NotNull IElementType[] getOperationElementTypes(); } diff --git a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java index 18c537f28050..b1a20d56d1b6 100644 --- a/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java +++ b/platform/core-impl/src/com/intellij/openapi/application/TransactionGuardImpl.java @@ -226,17 +226,28 @@ public class TransactionGuardImpl extends TransactionGuard { public void assertWriteActionAllowed() { ApplicationManager.getApplication().assertIsDispatchThread(); if (areAssertionsEnabled() && !myWritingAllowed && !myErrorReported) { - String message = "Write access is allowed from write-safe contexts only. " + - "Please ensure you're using invokeLater/invokeAndWait with a correct modality state (not \"any\"). " + - "See TransactionGuard documentation for details." + - "\n current modality=" + ModalityState.current() + - "\n known modalities=" + myWriteSafeModalities; // please assign exceptions here to Peter - LOG.error(message); + LOG.error(reportWriteUnsafeContext(ModalityState.current())); myErrorReported = true; } } + private String reportWriteUnsafeContext(@NotNull ModalityState modality) { + return "Write-unsafe context! Model changes are allowed from write-safe contexts only. " + + "Please ensure you're using invokeLater/invokeAndWait with a correct modality state (not \"any\"). " + + "See TransactionGuard documentation for details." + + "\n current modality=" + modality + + "\n known modalities=" + myWriteSafeModalities; + } + + @Override + public void assertWriteSafeContext(@NotNull ModalityState modality) { + if (!isWriteSafeModality(modality) && areAssertionsEnabled()) { + // please assign exceptions here to Peter + LOG.error(reportWriteUnsafeContext(modality)); + } + } + private static boolean areAssertionsEnabled() { Application app = ApplicationManager.getApplication(); if (app.isUnitTestMode() && !ourTestingTransactions) { diff --git a/platform/core-impl/src/com/intellij/openapi/editor/impl/BulkChangesMerger.java b/platform/core-impl/src/com/intellij/openapi/editor/impl/BulkChangesMerger.java index 781d7b61139f..a3c237ac0e37 100644 --- a/platform/core-impl/src/com/intellij/openapi/editor/impl/BulkChangesMerger.java +++ b/platform/core-impl/src/com/intellij/openapi/editor/impl/BulkChangesMerger.java @@ -59,6 +59,7 @@ public class BulkChangesMerger { * are sorted by offsets in ascending order * @return merge result */ + @NotNull public char[] mergeToCharArray(@NotNull char[] text, int textLength, @NotNull List changes) { int newLength = textLength; for (TextChange change : changes) { diff --git a/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java b/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java index d6a2bd877ba3..1779dd3188cd 100644 --- a/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java +++ b/platform/core-impl/src/com/intellij/pom/tree/events/impl/TreeChangeEventImpl.java @@ -215,6 +215,7 @@ public class TreeChangeEventImpl implements TreeChangeEvent{ } } + @NotNull private static int[] getRoute(ASTNode node, TObjectIntHashMap index){ final List parents = new ArrayList<>(20); while(node != null){ diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java index ca04c2cfaeb4..1fd9254724bb 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitProcessor.java @@ -15,17 +15,18 @@ */ package com.intellij.psi.impl; -import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.application.TransactionId; import com.intellij.openapi.editor.Document; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiFile; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public interface DocumentCommitProcessor { void commitSynchronously(@NotNull Document document, @NotNull Project project, @NotNull PsiFile psiFile); void commitAsynchronously(@NotNull final Project project, - @NotNull final Document document, - @NonNls @NotNull Object reason, - @NotNull ModalityState currentModalityState); + @NotNull final Document document, + @NonNls @NotNull Object reason, + @Nullable TransactionId context); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java index 7c87ef4ccdd0..309c06cbbb84 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java +++ b/platform/core-impl/src/com/intellij/psi/impl/DocumentCommitThread.java @@ -152,25 +152,25 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi public void commitAsynchronously(@NotNull final Project project, @NotNull final Document document, @NonNls @NotNull Object reason, - @NotNull ModalityState currentModalityState) { + @Nullable TransactionId context) { assert !isDisposed : "already disposed"; if (!project.isInitialized()) return; PsiFile psiFile = PsiDocumentManager.getInstance(project).getCachedPsiFile(document); if (psiFile == null) return; - doQueue(project, document, getAllFileNodes(psiFile), reason, currentModalityState, + doQueue(project, document, getAllFileNodes(psiFile), reason, context, PsiDocumentManager.getInstance(project).getLastCommittedText(document)); } private void doQueue(@NotNull Project project, - @NotNull Document document, - @NotNull List> oldFileNodes, - @NotNull Object reason, - @NotNull ModalityState currentModalityState, - @NotNull CharSequence lastCommittedText) { + @NotNull Document document, + @NotNull List> oldFileNodes, + @NotNull Object reason, + @Nullable TransactionId context, + @NotNull CharSequence lastCommittedText) { synchronized (lock) { if (!project.isInitialized()) return; // check the project is disposed under lock. - CommitTask newTask = createNewTaskAndCancelSimilar(project, document, oldFileNodes, reason, currentModalityState, + CommitTask newTask = createNewTaskAndCancelSimilar(project, document, oldFileNodes, reason, context, lastCommittedText); documentsToCommit.offer(newTask); @@ -185,13 +185,13 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi @NotNull Document document, @NotNull List> oldFileNodes, @NotNull Object reason, - @NotNull ModalityState currentModalityState, + @Nullable TransactionId context, @NotNull CharSequence lastCommittedText) { synchronized (lock) { for (Pair pair : oldFileNodes) { assert pair.first.getProject() == project; } - CommitTask newTask = new CommitTask(project, document, oldFileNodes, createProgressIndicator(), reason, currentModalityState, + CommitTask newTask = new CommitTask(project, document, oldFileNodes, createProgressIndicator(), reason, context, lastCommittedText); cancelAndRemoveFromDocsToCommit(newTask, reason); cancelAndRemoveCurrentTask(newTask, reason); @@ -380,7 +380,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi if (success) { assert !myApplication.isDispatchThread(); TransactionGuardImpl guard = (TransactionGuardImpl)TransactionGuard.getInstance(); - guard.submitTransaction(project, guard.getModalityTransaction(task.myCreationModalityState), finishRunnable); + guard.submitTransaction(project, task.myCreationContext, finishRunnable); } } } @@ -408,7 +408,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi List> oldFileNodes = file == null ? null : getAllFileNodes(file); if (oldFileNodes != null) { doQueue(finalProject, finalDocument, oldFileNodes, "re-added on failure: " + finalFailureReason, - finalTask.myCreationModalityState, + finalTask.myCreationContext, lastCommittedText); } }); @@ -442,7 +442,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi CommitTask task; synchronized (lock) { // synchronized to ensure no new similar tasks can start before we hold the document's lock - task = createNewTaskAndCancelSimilar(project, document, allFileNodes, SYNC_COMMIT_REASON, ModalityState.current(), + task = createNewTaskAndCancelSimilar(project, document, allFileNodes, SYNC_COMMIT_REASON, TransactionGuard.getInstance().getContextTransaction(), PsiDocumentManager.getInstance(project).getLastCommittedText(document)); documentLock.lock(); } @@ -541,7 +541,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi throw new PsiInvalidElementAccessException(file, "File " + file + " invalidated during sync commit"); } commitAsynchronously(project, document, "File " + file + " invalidated during background commit; task: "+task, - task.myCreationModalityState); + task.myCreationContext); } } } @@ -605,7 +605,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi } else { // add document back to the queue - commitAsynchronously(project, document, "Re-added back", task.myCreationModalityState); + commitAsynchronously(project, document, "Re-added back", task.myCreationContext); } }; } @@ -655,7 +655,7 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi // when failed it's canceled @NotNull final ProgressIndicator indicator; // progress to commit this doc under. @NotNull final Object reason; - @NotNull final ModalityState myCreationModalityState; + @Nullable final TransactionId myCreationContext; private final CharSequence myLastCommittedText; @NotNull final List> myOldFileNodes; @@ -664,13 +664,13 @@ public class DocumentCommitThread implements Runnable, Disposable, DocumentCommi @NotNull final List> oldFileNodes, @NotNull ProgressIndicator indicator, @NotNull Object reason, - @NotNull ModalityState currentModalityState, + @Nullable TransactionId context, @NotNull CharSequence lastCommittedText) { this.document = document; this.project = project; this.indicator = indicator; this.reason = reason; - myCreationModalityState = currentModalityState; + myCreationContext = context; myLastCommittedText = lastCommittedText; myOldFileNodes = oldFileNodes; modificationSequence = ((DocumentEx)document).getModificationSequence(); diff --git a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java index e0378f1cd8e0..0a13546a7dcc 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java +++ b/platform/core-impl/src/com/intellij/psi/impl/PsiDocumentManagerBase.java @@ -104,11 +104,19 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen @Override @Nullable public PsiFile getPsiFile(@NotNull Document document) { + if (document instanceof DocumentWindow && !((DocumentWindow)document).isValid()) { + return null; + } + final PsiFile userData = document.getUserData(HARD_REF_TO_PSI); - if (userData != null) return userData; + if (userData != null) { + return ensureValidFile(userData, "From hard ref"); + } PsiFile psiFile = getCachedPsiFile(document); - if (psiFile != null) return psiFile; + if (psiFile != null) { + return ensureValidFile(psiFile, "Cached PSI"); + } final VirtualFile virtualFile = FileDocumentManager.getInstance().getFile(document); if (virtualFile == null || !virtualFile.isValid()) return null; @@ -121,6 +129,12 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen return psiFile; } + @NotNull + private static PsiFile ensureValidFile(@NotNull PsiFile psiFile, @NotNull String debugInfo) { + if (!psiFile.isValid()) throw new PsiInvalidElementAccessException(psiFile, debugInfo); + return psiFile; + } + @Deprecated // todo remove when Database Navigator plugin doesn't need that anymore // todo to be removed in idea 17 @@ -530,7 +544,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen } actions.add(action); - ModalityState current = ModalityState.current(); + TransactionId current = TransactionGuard.getInstance().getContextTransaction(); if (current != ModalityState.NON_MODAL) { // re-add all uncommitted documents into the queue with this new modality // because this client obviously expects them to commit even inside modal dialog @@ -743,7 +757,8 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen public boolean isCommitted(@NotNull Document document) { if (document instanceof DocumentWindow) document = ((DocumentWindow)document).getDelegate(); if (getSynchronizer().isInSynchronization(document)) return true; - return !((DocumentEx)document).isInEventsHandling() && !isInUncommittedSet(document); + return (!(document instanceof DocumentEx) || !((DocumentEx)document).isInEventsHandling()) + && !isInUncommittedSet(document); } @Override @@ -831,7 +846,7 @@ public abstract class PsiDocumentManagerBase extends PsiDocumentManager implemen commitDocument(document); } else if (!((DocumentEx)document).isInBulkUpdate() && myPerformBackgroundCommit) { - myDocumentCommitProcessor.commitAsynchronously(myProject, document, event, ApplicationManager.getApplication().getCurrentModalityState()); + myDocumentCommitProcessor.commitAsynchronously(myProject, document, event, TransactionGuard.getInstance().getContextTransaction()); } } else { diff --git a/platform/core-impl/src/com/intellij/psi/impl/RenameableFakePsiElement.java b/platform/core-impl/src/com/intellij/psi/impl/RenameableFakePsiElement.java index 45a2f3b02ff7..19b33007c9ab 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/RenameableFakePsiElement.java +++ b/platform/core-impl/src/com/intellij/psi/impl/RenameableFakePsiElement.java @@ -92,6 +92,7 @@ public abstract class RenameableFakePsiElement extends FakePsiElement implements public void init(final PsiElement element) { } + @NotNull @Override public Object[] getDependences() { return ArrayUtil.EMPTY_OBJECT_ARRAY; diff --git a/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java b/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java index d686c741fd1e..76181184e70b 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/file/PsiDirectoryImpl.java @@ -78,7 +78,7 @@ public class PsiDirectoryImpl extends PsiElementBase implements PsiDirectory, Qu @Override public boolean isValid() { - return myFile.isValid(); + return myFile.isValid() && !getProject().isDisposed(); } @Override diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java index 90fbec40fdd0..509fefb3da60 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SelfElementInfo.java @@ -56,7 +56,7 @@ public class SelfElementInfo extends SmartPointerElementInfo { setRange(range); } - protected void switchToAnchor(@NotNull PsiElement element) { + void switchToAnchor(@NotNull PsiElement element) { Pair pair = Identikit.withAnchor(element, myIdentikit.getFileLanguage()); if (pair != null) { assert pair.first.hashCode() == myIdentikit.hashCode(); @@ -195,9 +195,10 @@ public class SelfElementInfo extends SmartPointerElementInfo { return areRestoredElementsEqual(other); } - protected boolean areRestoredElementsEqual(@NotNull final SmartPointerElementInfo other) { + boolean areRestoredElementsEqual(@NotNull final SmartPointerElementInfo other) { return ApplicationManager.getApplication().runReadAction( - (Computable)() -> Comparing.equal(restoreElement(), other.restoreElement())); + (Computable)() -> Comparing.equal(getVirtualFile(), other.getVirtualFile()) + && Comparing.equal(restoreElement(), other.restoreElement())); } @Override diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java index a8e09ee9e57e..ce4112046731 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerManagerImpl.java @@ -145,6 +145,11 @@ public class SmartPointerManagerImpl extends SmartPointerManager { } PsiFile containingFile = pointer.getContainingFile(); int refCount = ((SmartPsiElementPointerImpl)pointer).incrementAndGetReferenceCount(-1); + if (refCount == -1) { + LOG.error("Double smart pointer removal: " + pointer); + return; + } + if (refCount == 0) { PsiElement element = ((SmartPointerEx)pointer).getCachedElement(); if (element != null) { @@ -155,11 +160,14 @@ public class SmartPointerManagerImpl extends SmartPointerManager { info.cleanup(); if (containingFile == null) return; + + assert containingFile.getProject() == myProject : "Project mismatch: expected " + myProject + ", got " + containingFile.getProject(); + VirtualFile vFile = containingFile.getViewProvider().getVirtualFile(); SmartPointerTracker pointers = getTracker(vFile); SmartPointerTracker.PointerReference reference = ((SmartPsiElementPointerImpl)pointer).pointerReference; if (pointers != null && reference != null) { - pointers.removeReference(reference); + pointers.removeReference(reference, POINTERS_KEY); } } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerTracker.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerTracker.java index 3d02360392e3..027a5b7d76ad 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerTracker.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPointerTracker.java @@ -68,7 +68,7 @@ class SmartPointerTracker { return true; } - private boolean isActual(VirtualFile file, Key key) { + boolean isActual(VirtualFile file, Key key) { return file.getUserData(key) == this; } @@ -94,11 +94,11 @@ class SmartPointerTracker { nextAvailableIndex = index; } - synchronized void removeReference(@NotNull PointerReference reference) { + synchronized void removeReference(@NotNull PointerReference reference, @NotNull Key expectedKey) { int index = reference.index; if (index < 0) return; - assert isActual(reference.file, reference.key); + assertActual(expectedKey, reference.file, reference.key); assert references[index] == reference : "At " + index + " expected " + reference + ", found " + references[index]; references[index].index = -1; references[index] = null; @@ -107,6 +107,13 @@ class SmartPointerTracker { } } + private void assertActual(Key expectedKey, VirtualFile file, Key refKey) { + assert isActual(file, refKey) : "Smart pointer list mismatch mismatch:" + + " ref.key=" + expectedKey + + ", manager.key=" + refKey + + (file.getUserData(refKey) != null ? "; has another pointer list" : ""); + } + private void processAlivePointers(@NotNull Processor processor) { for (int i = 0; i < nextAvailableIndex; i++) { PointerReference ref = references[i]; @@ -115,7 +122,7 @@ class SmartPointerTracker { assert isActual(ref.file, ref.key); SmartPsiElementPointerImpl pointer = ref.get(); if (pointer == null) { - removeReference(ref); + removeReference(ref, ref.key); continue; } @@ -235,7 +242,7 @@ class SmartPointerTracker { SmartPointerTracker pointers = reference.file.getUserData(reference.key); if (pointers != null) { - pointers.removeReference(reference); + pointers.removeReference(reference, reference.key); } } } diff --git a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java index eae3e2f66e7c..a46a47cdc50c 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/smartPointers/SmartPsiElementPointerImpl.java @@ -144,9 +144,12 @@ class SmartPsiElementPointerImpl implements SmartPointerEx @NotNull E element, PsiFile containingFile, boolean forInjected) { SmartPointerElementInfo elementInfo = doCreateElementInfo(project, element, containingFile, forInjected); - if (ApplicationManager.getApplication().isUnitTestMode() && !element.equals(elementInfo.restoreElement())) { - // likely cause: PSI having isPhysical==true, but which can't be restored by containing file and range. To fix, make isPhysical return false - LOG.error("Cannot restore " + element + " of " + element.getClass() + " from " + elementInfo); + if (ApplicationManager.getApplication().isUnitTestMode()) { + PsiElement restored = elementInfo.restoreElement(); + if (!element.equals(restored)) { + // likely cause: PSI having isPhysical==true, but which can't be restored by containing file and range. To fix, make isPhysical return false + LOG.error("Cannot restore " + element + " of " + element.getClass() + " from " + elementInfo + "; restored=" + restored + " in " + element.getProject()); + } } return elementInfo; } @@ -251,7 +254,7 @@ class SmartPsiElementPointerImpl implements SmartPointerEx synchronized int incrementAndGetReferenceCount(int delta) { if (myReferenceCount == Byte.MAX_VALUE) return Byte.MAX_VALUE; // saturated - if (myReferenceCount == 0) return 0; // disposed, not to be reused again + if (myReferenceCount == 0) return -1; // disposed, not to be reused again return myReferenceCount += delta; } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java b/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java index af23d6fbad40..412562ee9d3f 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/FileTrees.java @@ -24,6 +24,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.lang.ref.Reference; +import java.util.Set; /** * @author peter @@ -67,8 +68,10 @@ final class FileTrees { return new FileTrees(myStub, ast, true, useStrongRefs); } - FileTrees withExclusiveStub(@NotNull StubTree stub) { - assert derefTreeElement() == null && !useStrongRefs : this; + FileTrees withExclusiveStub(@NotNull StubTree stub, Set allRoots) { + if (derefTreeElement() != null || useStrongRefs) { + throw new RuntimeException(toString() + "; roots=" + allRoots); + } return new FileTrees(new SoftReference<>(stub), null, false, false); } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java index 3ea808fc3559..3e89314b1b66 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/PsiFileImpl.java @@ -716,7 +716,7 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF // now stubs can be safely published for (PsiFileImpl eachPsiRoot : bindings.keySet()) { - eachPsiRoot.updateTrees(eachPsiRoot.myTrees.withExclusiveStub(bindings.get(eachPsiRoot))); + eachPsiRoot.updateTrees(eachPsiRoot.myTrees.withExclusiveStub(bindings.get(eachPsiRoot), bindings.keySet())); } return result; } @@ -753,9 +753,13 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF } private boolean hasUnbindableCachedPsi() { - for (StubBasedPsiElementBase psi : myRefToPsi.getAllCachedPsi()) { - if (psi.getStubIndex() < 0) { - return true; + for (PsiFile file : myViewProvider.getAllFiles()) { + if (file instanceof PsiFileImpl) { + for (StubBasedPsiElementBase psi : ((PsiFileImpl)file).myRefToPsi.getAllCachedPsi()) { + if (psi.getStubIndex() < 0) { + return true; + } + } } } return false; @@ -1137,18 +1141,23 @@ public abstract class PsiFileImpl extends ElementBase implements PsiFileEx, PsiF public final void beforeAstChange() { if (!useStrongRefs()) { - updateTrees(myTrees.switchToStrongRefs()); - myRefToPsi.switchToStrongRefs(); - - FileElement element = getTreeElement(); - if (element != null) { - AstPath.invalidatePaths(element); - } else { - LOG.error("No AST; " + derefStub() + "; " + this + " of " + getClass() + "; " + getViewProvider() + " of " + getViewProvider().getClass()); + synchronized (PsiLock.LOCK) { + for (PsiFile root : myViewProvider.getAllFiles()) { + if ((root instanceof PsiFileImpl)) { + ((PsiFileImpl)root).switchToStrongRefs(); + } + } } } } + private void switchToStrongRefs() { + FileElement node = calcTreeElement(); + updateTrees(myTrees.switchToStrongRefs()); + myRefToPsi.switchToStrongRefs(); + AstPath.invalidatePaths(node); + } + @Nullable public StubBasedPsiElementBase obtainPsi(@NotNull AstPath path, @NotNull Factory> creator) { if (useStrongRefs()) { diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java index 497aab1ae550..fb8d5cec695c 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/resolve/reference/impl/PsiMultiReference.java @@ -69,6 +69,7 @@ public class PsiMultiReference implements PsiPolyVariantReference { myElement = element; } + @NotNull public PsiReference[] getReferences() { return myReferences; } diff --git a/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstBufferUtil.java b/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstBufferUtil.java index d3b09c11b0e4..ea088ff43c05 100644 --- a/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstBufferUtil.java +++ b/platform/core-impl/src/com/intellij/psi/impl/source/tree/AstBufferUtil.java @@ -75,6 +75,7 @@ public class AstBufferUtil { return end; } + @NotNull public char[] getBuffer() { assert buffer != null; return buffer; diff --git a/platform/core-impl/src/com/intellij/util/CachedValueLeakChecker.java b/platform/core-impl/src/com/intellij/util/CachedValueLeakChecker.java index e963816c4d07..71f7447ae1e3 100644 --- a/platform/core-impl/src/com/intellij/util/CachedValueLeakChecker.java +++ b/platform/core-impl/src/com/intellij/util/CachedValueLeakChecker.java @@ -17,6 +17,7 @@ package com.intellij.util; import com.intellij.openapi.application.Application; import com.intellij.openapi.application.ApplicationManager; +import com.intellij.openapi.application.impl.ApplicationInfoImpl; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -45,7 +46,7 @@ class CachedValueLeakChecker { static void checkProvider(@NotNull final CachedValueProvider provider, @NotNull final Key key, @NotNull final UserDataHolder userDataHolder) { - if (!DO_CHECKS) return; + if (!DO_CHECKS || ApplicationInfoImpl.isInStressTest()) return; if (!ourCheckedKeys.add(key.toString())) return; // store strings because keys are created afresh in each (test) project findReferencedPsi(provider, userDataHolder, 5); diff --git a/platform/core-impl/src/com/intellij/util/indexing/FileContentImpl.java b/platform/core-impl/src/com/intellij/util/indexing/FileContentImpl.java index d1af65160e4e..7772ce30a039 100644 --- a/platform/core-impl/src/com/intellij/util/indexing/FileContentImpl.java +++ b/platform/core-impl/src/com/intellij/util/indexing/FileContentImpl.java @@ -201,6 +201,7 @@ public class FileContentImpl extends UserDataHolderBase implements FileContent { return myStamp; } + @NotNull @Override public byte[] getContent() { if (myContent == null) { diff --git a/platform/diff-impl/diff-tests.iml b/platform/diff-impl/diff-tests.iml index de73aadff5bc..a378b2dfcfd9 100644 --- a/platform/diff-impl/diff-tests.iml +++ b/platform/diff-impl/diff-tests.iml @@ -5,7 +5,7 @@ - + diff --git a/platform/diff-impl/src/com/intellij/diff/actions/impl/SetEditorSettingsAction.java b/platform/diff-impl/src/com/intellij/diff/actions/impl/SetEditorSettingsAction.java index a815aa8200ca..5f5af446cab8 100644 --- a/platform/diff-impl/src/com/intellij/diff/actions/impl/SetEditorSettingsAction.java +++ b/platform/diff-impl/src/com/intellij/diff/actions/impl/SetEditorSettingsAction.java @@ -33,6 +33,8 @@ import org.jetbrains.annotations.Nullable; import java.util.ArrayList; import java.util.List; +import static com.intellij.util.ArrayUtil.toObjectArray; + public class SetEditorSettingsAction extends ActionGroup implements DumbAware { @NotNull private final TextDiffSettings myTextSettings; @NotNull private final List myEditors; @@ -167,7 +169,7 @@ public class SetEditorSettingsAction extends ActionGroup implements DumbAware { ContainerUtil.addAll(result, myActions); result.add(Separator.getInstance()); result.add(ActionManager.getInstance().getAction(IdeActions.GROUP_DIFF_EDITOR_GUTTER_POPUP)); - return ContainerUtil.toArray(result, new AnAction[result.size()]); + return toObjectArray(result, AnAction.class); } private abstract class EditorSettingToggleAction extends ToggleAction implements DumbAware, EditorSettingAction { diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java index 4ba8bae56b2e..677384ef9dad 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/ComparisonManagerImpl.java @@ -16,6 +16,7 @@ package com.intellij.diff.comparison; import com.intellij.diff.comparison.iterables.DiffIterable; +import com.intellij.diff.comparison.iterables.DiffIterableUtil; import com.intellij.diff.comparison.iterables.FairDiffIterable; import com.intellij.diff.fragments.*; import com.intellij.diff.util.DiffUtil; @@ -38,6 +39,7 @@ import java.util.BitSet; import java.util.Collections; import java.util.List; +import static com.intellij.diff.comparison.iterables.DiffIterableUtil.fair; import static java.util.Collections.singletonList; public class ComparisonManagerImpl extends ComparisonManager { @@ -461,9 +463,11 @@ public class ComparisonManagerImpl extends ComparisonManager { List lineTexts1 = ContainerUtil.map(lines1, line -> line.getNotIgnoredContent(ignored1)); List lineTexts2 = ContainerUtil.map(lines2, line -> line.getNotIgnoredContent(ignored2)); - FairDiffIterable iterable = ByLine.compare(lineTexts1, lineTexts2, ComparisonPolicy.DEFAULT, indicator); - List lineFragments = convertIntoLineFragments(lines1, lines2, iterable); + + FairDiffIterable correctedIterable = correctIgnoredRangesSecondStep(iterable, lines1, lines2, ignored1, ignored2); + + List lineFragments = convertIntoLineFragments(lines1, lines2, correctedIterable); if (innerFragments) { lineFragments = createInnerFragments(lineFragments, text1, text2, ComparisonPolicy.DEFAULT, indicator); @@ -483,6 +487,26 @@ public class ComparisonManagerImpl extends ComparisonManager { return set; } + @NotNull + private static FairDiffIterable correctIgnoredRangesSecondStep(@NotNull FairDiffIterable iterable, + @NotNull List lines1, + @NotNull List lines2, + @NotNull BitSet ignored1, + @NotNull BitSet ignored2) { + DiffIterableUtil.ChangeBuilder builder = new DiffIterableUtil.ChangeBuilder(lines1.size(), lines2.size()); + for (Range range : iterable.iterateUnchanged()) { + int count = range.end1 - range.start1; + for (int i = 0; i < count; i++) { + int index1 = range.start1 + i; + int index2 = range.start2 + i; + if (areIgnoredEqualLines(lines1.get(index1), lines2.get(index2), ignored1, ignored2)) { + builder.markEqual(index1, index2); + } + } + } + return fair(builder.finish()); + } + @Nullable private static LineFragment trimIgnoredChanges(@NotNull LineFragment fragment, @NotNull List lines1, @@ -542,7 +566,7 @@ public class ComparisonManagerImpl extends ComparisonManager { } private static boolean isIgnoredLine(@NotNull Line line, @NotNull BitSet ignored) { - return trimIgnoredRange(line.getOffset1(), line.getOffset2(), ignored, 0).isEmpty(); + return isIgnoredRange(ignored, line.getOffset1(), line.getOffset2()); } private static boolean areIgnoredEqualLines(@NotNull Line line1, @NotNull Line line2, @@ -551,13 +575,36 @@ public class ComparisonManagerImpl extends ComparisonManager { int end1 = line1.getOffset2(); int start2 = line2.getOffset1(); int end2 = line2.getOffset2(); - Range range = TrimUtil.trimExpandText(line1.getOriginalText(), line2.getOriginalText(), start1, start2, end1, end2, ignored1, ignored2); - return range.isEmpty(); + if (!range.isEmpty()) return false; + + List words1 = getNonIgnoredWords(line1, ignored1); + List words2 = getNonIgnoredWords(line2, ignored2); + if (words1.size() != words2.size()) return false; + + for (int i = 0; i < words1.size(); i++) { + CharSequence word1 = getWordContent(line1, words1.get(i)); + CharSequence word2 = getWordContent(line2, words2.get(i)); + if (!StringUtil.equals(word1, word2)) return false; + } + + return true; } + @NotNull + private static List getNonIgnoredWords(@NotNull Line line, @NotNull BitSet ignored) { + int offset = line.getOffset1(); + List innerChunks = ByWord.getInlineChunks(line.getContent()); + return ContainerUtil.filter(innerChunks, it -> it instanceof ByWord.WordChunk && + !isIgnoredRange(ignored, offset + it.getOffset1(), offset + it.getOffset2())); + } + + @NotNull + private static CharSequence getWordContent(@NotNull Line line, @NotNull ByWord.InlineChunk word) { + return line.getContent().subSequence(word.getOffset1(), word.getOffset2()); + } @NotNull private static TextRange trimIgnoredRange(int start, int end, @NotNull BitSet ignored, int offset) { @@ -565,6 +612,9 @@ public class ComparisonManagerImpl extends ComparisonManager { return new TextRange(intPair.val1 - offset, intPair.val2 - offset); } + private static boolean isIgnoredRange(@NotNull BitSet ignored, int start, int end) { + return ignored.nextClearBit(start) >= end; + } private static class Line { @NotNull private final CharSequence myChars; diff --git a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java index 4858e8dc3a93..afb479f52be7 100644 --- a/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java +++ b/platform/diff-impl/src/com/intellij/diff/comparison/iterables/DiffIterableUtil.java @@ -79,12 +79,8 @@ public class DiffIterableUtil { @NotNull public static FairDiffIterable diff(@NotNull List objects1, @NotNull List objects2, @NotNull ProgressIndicator indicator) throws DiffTooBigException { - indicator.checkCanceled(); - // TODO: compare lists instead of arrays in Diff - Object[] data1 = ContainerUtil.toArray((List)objects1, new Object[objects1.size()]); - Object[] data2 = ContainerUtil.toArray((List)objects2, new Object[objects2.size()]); - return diff(data1, data2, indicator); + return diff(objects1.toArray(), objects2.toArray(), indicator); } // diff --git a/platform/diff-impl/src/com/intellij/diff/merge/MergeWindow.java b/platform/diff-impl/src/com/intellij/diff/merge/MergeWindow.java index 3e9779b8beb4..a493503ceb5e 100644 --- a/platform/diff-impl/src/com/intellij/diff/merge/MergeWindow.java +++ b/platform/diff-impl/src/com/intellij/diff/merge/MergeWindow.java @@ -34,6 +34,8 @@ import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.List; +import static com.intellij.util.ArrayUtil.toObjectArray; + public class MergeWindow { private static final Logger LOG = Logger.getInstance(MergeWindow.class); @@ -134,7 +136,7 @@ public class MergeWindow { if (bottomActions.resolveAction != null) { bottomActions.resolveAction.putValue(DialogWrapper.DEFAULT_ACTION, true); } - return actions.toArray(new Action[actions.size()]); + return toObjectArray(actions, Action.class); } @NotNull @@ -142,7 +144,7 @@ public class MergeWindow { protected Action[] createLeftSideActions() { MergeRequestProcessor.BottomActions bottomActions = myProcessor.getBottomActions(); List actions = ContainerUtil.skipNulls(ContainerUtil.list(bottomActions.applyLeft, bottomActions.applyRight)); - return actions.toArray(new Action[actions.size()]); + return toObjectArray(actions, Action.class); } @NotNull diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java index defe89e10024..683fcd8a6f98 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/SimpleDiffViewer.java @@ -53,6 +53,7 @@ import java.util.BitSet; import java.util.Iterator; import java.util.List; +import static com.intellij.util.ArrayUtil.toObjectArray; import static com.intellij.util.ObjectUtils.assertNotNull; public class SimpleDiffViewer extends TwosideTextDiffViewer { @@ -819,7 +820,7 @@ public class SimpleDiffViewer extends TwosideTextDiffViewer { private final MyPaintable myPaintable = new MyPaintable(0, 1); public MyFoldingModel(@NotNull List editors, @NotNull Disposable disposable) { - super(editors.toArray(new EditorEx[2]), disposable); + super(toObjectArray(editors, EditorEx.class), disposable); } public void install(@Nullable final List fragments, diff --git a/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java b/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java index 8902dc080de2..5cff44075928 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/simple/ThreesideTextDiffViewerEx.java @@ -45,6 +45,8 @@ import java.awt.*; import java.util.Iterator; import java.util.List; +import static com.intellij.util.ArrayUtil.toObjectArray; + public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer { @NotNull private final SyncScrollSupport.SyncScrollable mySyncScrollable1; @NotNull private final SyncScrollSupport.SyncScrollable mySyncScrollable2; @@ -67,7 +69,7 @@ public abstract class ThreesideTextDiffViewerEx extends ThreesideTextDiffViewer myPrevNextDifferenceIterable = new MyPrevNextDifferenceIterable(); myPrevNextConflictIterable = new MyPrevNextConflictIterable(); myStatusPanel = new MyStatusPanel(); - myFoldingModel = new MyFoldingModel(getEditors().toArray(new EditorEx[3]), this); + myFoldingModel = new MyFoldingModel(toObjectArray(getEditors(), EditorEx.class), this); DiffUtil.registerAction(new PrevConflictAction(), myPanel); DiffUtil.registerAction(new NextConflictAction(), myPanel); diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java index f14e3a430435..a39b7e9c9f38 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/FoldingModelSupport.java @@ -50,6 +50,8 @@ import java.util.Iterator; import java.util.List; import java.util.Set; +import static com.intellij.util.ArrayUtil.toObjectArray; + /** * This class allows to add custom foldings to hide unchanged regions in diff. * EditorSettings#isAutoCodeFoldingEnabled() should be true, to avoid collisions with language-specific foldings @@ -181,7 +183,7 @@ public class FoldingModelSupport { } if (result.size() > 0) { - FoldedBlock[] block = ContainerUtil.toArray(result, new FoldedBlock[result.size()]); + FoldedBlock[] block = toObjectArray(result, FoldedBlock.class); for (FoldedBlock folding : block) { folding.installHighlighter(block); } diff --git a/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java b/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java index 53ba493bb4cd..d298d4199ac3 100644 --- a/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java +++ b/platform/diff-impl/src/com/intellij/diff/tools/util/SyncScrollSupport.java @@ -34,6 +34,8 @@ import javax.swing.*; import java.awt.*; import java.util.List; +import static com.intellij.util.ArrayUtil.toObjectArray; + public class SyncScrollSupport { public interface SyncScrollable { @CalledInAwt @@ -249,7 +251,7 @@ public class SyncScrollSupport { assert startLines.length == count; assert endLines.length == count; - final int[] offsets = getTargetOffsets(editors.toArray(new Editor[count]), startLines, endLines, -1); + final int[] offsets = getTargetOffsets(toObjectArray(editors, Editor.class), startLines, endLines, -1); final int[] startOffsets = new int[count]; for (int i = 0; i < count; i++) { diff --git a/platform/diff-impl/src/com/intellij/diff/util/DiffGutterRenderer.java b/platform/diff-impl/src/com/intellij/diff/util/DiffGutterRenderer.java index b367ec784914..4541a3fc4fd1 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/DiffGutterRenderer.java +++ b/platform/diff-impl/src/com/intellij/diff/util/DiffGutterRenderer.java @@ -15,6 +15,7 @@ */ package com.intellij.diff.util; +import com.intellij.codeInsight.daemon.NonHideableIconGutterMark; import com.intellij.openapi.actionSystem.AnAction; import com.intellij.openapi.actionSystem.AnActionEvent; import com.intellij.openapi.editor.markup.GutterIconRenderer; @@ -24,7 +25,7 @@ import org.jetbrains.annotations.Nullable; import javax.swing.*; -public abstract class DiffGutterRenderer extends GutterIconRenderer { +public abstract class DiffGutterRenderer extends GutterIconRenderer implements NonHideableIconGutterMark { @NotNull private final Icon myIcon; @Nullable private final String myTooltip; diff --git a/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java b/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java index 30084b5bda0a..99b44631bb5e 100644 --- a/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java +++ b/platform/diff-impl/src/com/intellij/diff/util/TextDiffTypeFactory.java @@ -32,6 +32,8 @@ import java.awt.*; import java.util.ArrayList; import java.util.List; +import static com.intellij.util.ArrayUtil.toObjectArray; + public class TextDiffTypeFactory { @NotNull public static final TextDiffTypeImpl INSERTED = new TextDiffTypeImpl(DiffColors.DIFF_INSERTED, DiffBundle.message("diff.type.inserted.name")); @@ -58,7 +60,7 @@ public class TextDiffTypeFactory { } public synchronized TextDiffTypeImpl[] getAllDiffTypes() { - return myTypes.toArray(new TextDiffTypeImpl[myTypes.size()]); + return toObjectArray(myTypes, TextDiffTypeImpl.class); } public static TextDiffTypeFactory getInstance() { diff --git a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java index b3442a08f637..ec8ac42e3a8d 100644 --- a/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java +++ b/platform/diff-impl/src/com/intellij/openapi/diff/impl/dir/DirDiffPanel.java @@ -53,7 +53,6 @@ import com.intellij.ui.components.JBLabel; import com.intellij.ui.components.JBLoadingPanel; import com.intellij.ui.components.JBLoadingPanelListener; import com.intellij.ui.table.JBTable; -import com.intellij.util.containers.ContainerUtil; import com.intellij.util.ui.JBUI; import com.intellij.util.ui.UIUtil; import org.jetbrains.annotations.NonNls; @@ -73,6 +72,8 @@ import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.atomic.AtomicBoolean; +import static com.intellij.util.ArrayUtil.toObjectArray; + /** * @author Konstantin Bulenkov */ @@ -528,7 +529,7 @@ public class DirDiffPanel implements Disposable, DataProvider { if (navigatable1 != null) navigatables.add(navigatable1); if (navigatable2 != null) navigatables.add(navigatable2); } - return ContainerUtil.toArray(navigatables, new Navigatable[navigatables.size()]); + return toObjectArray(navigatables, Navigatable.class); } private class MyPrevNextDifferenceIterable implements PrevNextDifferenceIterable { diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt index f58ddee5a4db..fce98b3c2574 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/IgnoreComparisonUtilTest.kt @@ -122,6 +122,43 @@ class IgnoreComparisonUtilTest : DiffTestCase() { .run() } + fun testTrim() { + Test("A B_", "", + " ", "", + "----", "") + .run() + + Test("A B_", "", + "++++", "", + " ", "") + .run() + + Test("A B_", "", + " +", "", + "----", "") + .run() + + Test("A B_", "", + "+++ ", "", + "----", "") + .run() + + Test("A B_", "", + "+ +", "", + "----", "") + .run() + + Test("A B_", "", + "+ ++", "", + "----", "") + .run() + + Test("A B_", "", + " +++", "", + "----", "") + .run() + } + fun testLines() { Test("X_", "X_Y_", " ", " ", @@ -309,8 +346,8 @@ class IgnoreComparisonUtilTest : DiffTestCase() { Test("X Y", "XY", " + ", " ", - " ", " ") - .changedLinesNumber(0, 0) + "---", "--") + .changedLinesNumber(1, 1) .run() } @@ -337,17 +374,17 @@ class IgnoreComparisonUtilTest : DiffTestCase() { " - ", " - ") .changedLinesNumber(2, 1) .run() + + Test("int X = 0;", "intX = 0;", + " + + + ", " + + ", + "----- ", "---- ") + .changedLinesNumber(1, 1) + .run() } fun `test Java bad samples`() { //TODO - Test("int X = 0;", "intX = 0;", - " + + + ", " + + ", - " ", " ") - .changedLinesNumber(0, 0) - .run() - Test("System.out.println (\"Hello world\");", "System.out.println(\"Hello world\");", " . + . ", " . . ", " - . - . ", " . . ") diff --git a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt index c6c1e115a1ed..799c448c3931 100644 --- a/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt +++ b/platform/diff-impl/tests/com/intellij/diff/comparison/WordComparisonUtilTest.kt @@ -338,6 +338,23 @@ class WordComparisonUtilTest : ComparisonUtilTestBase() { } } + fun `test trailing punctuation`() { + lines_inner { + ("X = { };" - "X = { _ };") + (" " - " -- ").default() + (" " - " ").trim() + testAll() + } + + // TODO + lines_inner { + ("X = { };_" - "X = { _ };_") + (" -- " - " ----").default() + (" -- " - " -- ").trim() + testAll() + } + } + fun `test legacy cases from ByWordTest`() { lines_inner { ("abc def, 123" - "ab def, 12") diff --git a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java index f94bea487728..e72124462eae 100644 --- a/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java +++ b/platform/dvcs-impl/src/com/intellij/dvcs/push/ui/PushLog.java @@ -19,7 +19,6 @@ import com.intellij.openapi.actionSystem.*; import com.intellij.openapi.keymap.KeymapUtil; import com.intellij.openapi.project.DumbAwareAction; import com.intellij.openapi.project.Project; -import com.intellij.openapi.ui.Splitter; import com.intellij.openapi.vcs.VcsDataKeys; import com.intellij.openapi.vcs.changes.Change; import com.intellij.openapi.vcs.changes.TextRevisionNumber; @@ -64,6 +63,7 @@ public class PushLog extends JPanel implements DataProvider { private static final String CONTEXT_MENU = "Vcs.Push.ContextMenu"; private static final String START_EDITING = "startEditing"; + private static final String SPLITTER_PROPORTION = "Vcs.Push.Splitter.Proportion"; private final ChangesBrowser myChangesBrowser; private final CheckboxTree myTree; private final MyTreeCellRenderer myTreeCellRenderer; @@ -232,9 +232,10 @@ public class PushLog extends JPanel implements DataProvider { final EditSourceForDialogAction editSourceAction = new EditSourceForDialogAction(myChangesBrowser); editSourceAction.registerCustomShortcutSet(CommonShortcuts.getEditSource(), myChangesBrowser); myChangesBrowser.addToolbarAction(editSourceAction); + myChangesBrowser.setMinimumSize(new Dimension(200, myChangesBrowser.getPreferredSize().height)); setDefaultEmptyText(); - Splitter splitter = new Splitter(false, 0.7f); + JBSplitter splitter = new JBSplitter(SPLITTER_PROPORTION, 0.7f); final JComponent syncStrategyPanel = myAllowSyncStrategy ? createStrategyPanel() : null; myScrollPane = new JBScrollPane(myTree) { @@ -263,8 +264,9 @@ public class PushLog extends JPanel implements DataProvider { setLayout(new BorderLayout()); add(splitter); - myTree.setMinimumSize(new Dimension(200, myTree.getPreferredSize().height)); + myTree.setMinimumSize(new Dimension(400, myTree.getPreferredSize().height)); myTree.setRowHeight(0); + myScrollPane.setMinimumSize(new Dimension(myTree.getMinimumSize().width, myScrollPane.getPreferredSize().height)); } private class MyShowCommitInfoAction extends DumbAwareAction { diff --git a/platform/editor-ui-api/src/com/intellij/codeInsight/daemon/NonHideableIconGutterMark.java b/platform/editor-ui-api/src/com/intellij/codeInsight/daemon/NonHideableIconGutterMark.java new file mode 100644 index 000000000000..3bbcec3136b9 --- /dev/null +++ b/platform/editor-ui-api/src/com/intellij/codeInsight/daemon/NonHideableIconGutterMark.java @@ -0,0 +1,25 @@ +/* + * 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.codeInsight.daemon; + +/** + * A marker interface, that allows to show gutter icon + * even with "do not show gutter icons" option enabled. + * + * @see com.intellij.openapi.editor.EditorSettings#areGutterIconsShown() + */ +public interface NonHideableIconGutterMark extends GutterMark { +} diff --git a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt index 494b0366d992..66152624f082 100644 --- a/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt +++ b/platform/editor-ui-api/src/com/intellij/ide/ui/UISettings.kt @@ -57,7 +57,7 @@ class UISettings : BaseState(), PersistentStateComponent { @get:Property(filter = FontFilter::class) @get:OptionTag("FONT_SCALE") - private var fontScale by storedProperty(0f) + var fontScale by storedProperty(0f) @get:OptionTag("RECENT_FILES_LIMIT") var recentFilesLimit by storedProperty(50) @get:OptionTag("CONSOLE_COMMAND_HISTORY_LIMIT") var consoleCommandHistoryLimit by storedProperty(300) @@ -197,10 +197,11 @@ class UISettings : BaseState(), PersistentStateComponent { override fun accepts(accessor: Accessor, bean: Any): Boolean { val settings = bean as UISettings val fontData = systemFontFaceAndSize - if ("FONT_FACE" == accessor.name) { + if ("fontFace" == accessor.name) { return fontData.first != settings.fontFace } - // store only in pair + // fontSize/fontScale should either be stored in pair or not stored at all + // otherwise the fontSize restore logic gets broken (see loadState) return !(fontData.second == settings.fontSize && 1f == settings.fontScale) } } @@ -236,7 +237,7 @@ class UISettings : BaseState(), PersistentStateComponent { else { fontSize = JBUI.scale(fontSize / fontScale).toInt() } - fontScale = JBUI.scale(1f) + fontScale = JBUI.scale(1f) // at this moment JBUI.scale only depends on the HiDPI mode and the system scale initDefFont() // 1. Sometimes system font cannot display standard ASCII symbols. If so we have @@ -303,7 +304,7 @@ class UISettings : BaseState(), PersistentStateComponent { val shadowInstance: UISettings get() { val app = ApplicationManager.getApplication() - return (if (app == null) null else instance) ?: UISettings().withDefFont() + return (if (app == null) null else instanceOrNull) ?: UISettings().withDefFont() } private val systemFontFaceAndSize: Pair diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorPalette.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorPalette.java index 9fc67bf2a1a4..be33d3455bec 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorPalette.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorPalette.java @@ -18,6 +18,8 @@ package com.intellij.openapi.editor.colors; import com.intellij.openapi.editor.markup.TextAttributes; import com.intellij.ui.ColorUtil; import com.intellij.util.Function; +import com.intellij.util.containers.MultiMap; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -29,8 +31,8 @@ import java.util.List; * Contains colors used in editor color scheme. */ public abstract class EditorColorPalette { - private EditorColorsScheme myColorsScheme; - private final Set myColors = new HashSet<>(); + protected final EditorColorsScheme myColorsScheme; + private final MultiMap myColors = MultiMap.createSmart(); public final static Comparator ORDER_NONE = Comparator.comparingInt(EditorColorPalette::getDefaultOrder); public final static Comparator ORDER_BY_INTENSITY = Comparator.comparingInt(EditorColorPalette::getIntensity); @@ -38,52 +40,79 @@ public abstract class EditorColorPalette { public EditorColorPalette(EditorColorsScheme colorsScheme) { myColorsScheme = colorsScheme; } - + public EditorColorPalette withBackgroundColors() { return collectColors(attr -> attr.getBackgroundColor()); } - + public EditorColorPalette withForegroundColors() { return collectColors(attr -> attr.getForegroundColor()); } - + private Collection orderBy(@Nullable Comparator comparator) { List sorted = new ArrayList<>(); - sorted.addAll(myColors); + sorted.addAll(myColors.keySet()); Collections.sort(sorted, comparator); return sorted; } - + private static int getIntensity(@NotNull Color color) { return (color.getRed() + color.getGreen() + color.getBlue()) / 3; } - + @SuppressWarnings("unused") + @Contract(pure = true) private static int getDefaultOrder(@NotNull Color color) { return 0; } public Collection getColors(@NotNull Comparator comparator) { - return comparator == ORDER_NONE ? myColors : orderBy(comparator); + return comparator == ORDER_NONE ? myColors.keySet() : orderBy(comparator); } - private EditorColorPalette collectColors(@NotNull Function attrColorReader) { - Set colors = new HashSet<>(); - for (TextAttributesKey key : getTextAttributeKeys()) { + @NotNull + public Set>> getEntries() { + return myColors.entrySet(); + } + + /** + * Collects colors from known color setup pages. + * + * @param attrColorReader the function to extract the color from attribute (ex. foreground or background) + * @return the pallete with collected colors + */ + public EditorColorPalette collectColors(@NotNull Function attrColorReader) { + return collectColorsWithFilter(attrColorReader, false); + } + + /** + * Collects colors from known color setup pages. + * + * @param attrColorReader the function to extract the color from attribute (ex. foreground or background) + * @param filterOutRainbowAttrKeys the flag to filter out the attributes that can be overwritten by semantic highlighting + * or not conflicting with semantic highlighting + * @return the pallete with collected colors + */ + public EditorColorPalette collectColorsWithFilter(@NotNull Function attrColorReader, + boolean filterOutRainbowAttrKeys) { + final MultiMap colors = MultiMap.createSmart(); + for (TextAttributesKey key : getTextAttributeKeys(filterOutRainbowAttrKeys)) { TextAttributes attributes = myColorsScheme.getAttributes(key); if (attributes != null) { Color usedColor = attrColorReader.fun(attributes); - if (usedColor != null) colors.add(usedColor); + if (usedColor != null) { + colors.putValue(usedColor, key); + } } } - myColors.addAll(colors); + myColors.putAllValues(colors); return this; } /** - * Search for a color which does not exist in the color palette by adjusting brightness of a given {@code sampleColor}. - * + * Search for a color which does not exist in the color palette by adjusting brightness of a given {@code sampleColor}. + * * @param sampleColor The sample color to start with. * @return An adjusted color or the sample color if it doesn't conflict with the palette or {@code null} if non-conflicting - * color can't be found with used algorithms of brightness adjustment. + * color can't be found with used algorithms of brightness adjustment. */ @Nullable public Color getClosestNonConflictingColor(@NotNull Color sampleColor) { @@ -94,27 +123,28 @@ public abstract class EditorColorPalette { } return foundColor; } - - private static Function getAdjuster(boolean searchBrighter) { + + @Contract(pure = true) + private static Function getAdjuster(boolean searchBrighter) { return searchBrighter ? Color::brighter : Color::darker; } /** * Search for a color which does not exist in the color palette using a given algorithm of color adjustment. - * + * * @param sampleColor The sample color to start with. * @param colorAdjuster The color adjustment function, for example {@code Color:brighter} * @return An adjusted color or the sample color if it doesn't conflict with the palette or {@code null} if non-conflicting - * color can't be found with used algorithms of brightness adjustment. + * color can't be found with used algorithms of brightness adjustment. */ @Nullable - public Color getClosestNonConflictingColor(@NotNull Color sampleColor, @NotNull Function colorAdjuster) { - if (myColors.contains(sampleColor)) { + public Color getClosestNonConflictingColor(@NotNull Color sampleColor, @NotNull Function colorAdjuster) { + if (myColors.containsKey(sampleColor)) { Color newColor = colorAdjuster.fun(sampleColor); return !sampleColor.equals(newColor) ? getClosestNonConflictingColor(newColor, colorAdjuster) : null; } return sampleColor; } - - protected abstract Collection getTextAttributeKeys(); + + protected abstract Collection getTextAttributeKeys(boolean filterRainbowAttrKeys); } diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java index 1c03ce24419b..5d015730449f 100644 --- a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorColorsScheme.java @@ -26,7 +26,7 @@ import org.jetbrains.annotations.Nullable; import java.awt.*; -public interface EditorColorsScheme extends Cloneable, TextAttributesScheme, Scheme, SchemeMetaInfo { +public interface EditorColorsScheme extends Cloneable, TextAttributesScheme, Scheme, SchemeMetaInfo, EditorGeneratedTextAttributesCache { @NonNls String DEFAULT_SCHEME_NAME = "Default"; void setName(String name); diff --git a/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorGeneratedTextAttributesCache.java b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorGeneratedTextAttributesCache.java new file mode 100644 index 000000000000..6de929560e07 --- /dev/null +++ b/platform/editor-ui-api/src/com/intellij/openapi/editor/colors/EditorGeneratedTextAttributesCache.java @@ -0,0 +1,29 @@ +/* + * 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.openapi.editor.colors; + +import com.intellij.openapi.editor.markup.TextAttributes; +import org.jetbrains.annotations.Nullable; + +import java.util.Map; + +public interface EditorGeneratedTextAttributesCache { + @Nullable + default Map getGeneratedTextAttributesCache() { + return null; + } + default void dropGeneratedTextAttributesCache() {} +} diff --git a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java index 122cc01fe328..1654891b2d37 100644 --- a/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java +++ b/platform/editor-ui-ex/src/com/intellij/openapi/editor/colors/impl/AbstractColorsScheme.java @@ -32,6 +32,7 @@ import com.intellij.openapi.options.SchemeState; import com.intellij.openapi.util.*; import com.intellij.util.JdomKt; import com.intellij.util.PlatformUtils; +import com.intellij.util.containers.ContainerUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.containers.HashMap; import com.intellij.util.ui.JBUI; @@ -1009,7 +1010,23 @@ public abstract class AbstractColorsScheme implements EditorColorsScheme, Serial assert newParent instanceof ReadOnlyColorsScheme : "New parent scheme must be read-only"; myParentScheme = newParent; } - + + private volatile Map myAttributesCacheMap; + + @NotNull + @Override + public Map getGeneratedTextAttributesCache() { + if (myAttributesCacheMap == null) { + myAttributesCacheMap = ContainerUtil.newConcurrentMap(); + } + return myAttributesCacheMap; + } + + @Override + public void dropGeneratedTextAttributesCache() { + myAttributesCacheMap = null; + } + void resolveParent(@NotNull Function nameResolver) { if (myParentScheme instanceof TemporaryParent) { String parentName = ((TemporaryParent)myParentScheme).getParentName(); diff --git a/platform/extensions/src/com/intellij/openapi/extensions/SortingException.java b/platform/extensions/src/com/intellij/openapi/extensions/SortingException.java index fe5721f96d3c..a0f8d96e1baa 100644 --- a/platform/extensions/src/com/intellij/openapi/extensions/SortingException.java +++ b/platform/extensions/src/com/intellij/openapi/extensions/SortingException.java @@ -16,8 +16,8 @@ package com.intellij.openapi.extensions; import com.intellij.openapi.util.text.StringUtil; -import com.intellij.util.Function; import org.jdom.Element; +import org.jetbrains.annotations.NotNull; /** * @author Alexander Kireyev @@ -25,12 +25,13 @@ import org.jdom.Element; public class SortingException extends RuntimeException { private final Element[] myConflictingElements; - public SortingException(String message, Element... conflictingElements) { + SortingException(String message, @NotNull Element... conflictingElements) { super(message + ": " + StringUtil.join(conflictingElements, element -> element.getAttributeValue("id") + "(" + element.getAttributeValue("order") + ")", "; ")); myConflictingElements = conflictingElements; } + @NotNull public Element[] getConflictingElements() { return myConflictingElements; } diff --git a/platform/extensions/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java b/platform/extensions/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java index 5373f257916c..d8614c89772c 100644 --- a/platform/extensions/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java +++ b/platform/extensions/src/com/intellij/util/pico/CachingConstructorInjectionComponentAdapter.java @@ -119,6 +119,7 @@ public class CachingConstructorInjectionComponentAdapter extends InstantiatingCo } } + @NotNull private Object[] getConstructorArguments(PicoContainer container, Constructor ctor) { Class[] parameterTypes = ctor.getParameterTypes(); Object[] result = new Object[parameterTypes.length]; @@ -204,6 +205,7 @@ public class CachingConstructorInjectionComponentAdapter extends InstantiatingCo return matchingConstructors; } + @NotNull private Constructor[] getConstructors() { return (Constructor[]) AccessController.doPrivileged(new PrivilegedAction() { public Object run() { diff --git a/platform/external-system-api/resources/i18n/ExternalSystemBundle.properties b/platform/external-system-api/resources/i18n/ExternalSystemBundle.properties index 2307ab11a9c7..53e713f9feb5 100644 --- a/platform/external-system-api/resources/i18n/ExternalSystemBundle.properties +++ b/platform/external-system-api/resources/i18n/ExternalSystemBundle.properties @@ -90,12 +90,13 @@ run.configuration.title.choose.registered.project=Choose {0} project run.configuration.settings.label.project={0} project: run.configuration.settings.label.tasks=Tasks: run.configuration.settings.label.vmoptions=VM options: -run.configuration.settings.label.script.parameters=Script parameters: +run.configuration.settings.label.arguments=Arguments: run.text.starting.multiple.task={0}: Executing external tasks ''{1}''...\n run.text.starting.single.task={0}: Executing external task ''{1}''...\n run.text.ended.multiple.task={0}: External tasks execution finished ''{1}''.\n run.text.ended.single.task={0}: External task execution finished ''{1}''.\n run.error.undefined.task=No task to execute is specified +run.invalid.jvm.agent.configuration=Invalid JVM agent configuration used ''{0}'' external.system.java.internal.jre=Use Internal JRE external.system.java.home.env=Use JAVA_HOME diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/execution/ExternalSystemTaskExecutionSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/execution/ExternalSystemTaskExecutionSettings.java index fb7d8e1c0b0e..c12d4b7ba545 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/execution/ExternalSystemTaskExecutionSettings.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/execution/ExternalSystemTaskExecutionSettings.java @@ -15,7 +15,9 @@ */ package com.intellij.openapi.externalSystem.model.execution; +import com.intellij.execution.configurations.ParametersList; import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.util.Key; import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtilRt; import com.intellij.util.xmlb.annotations.Tag; @@ -36,6 +38,7 @@ import java.util.List; public class ExternalSystemTaskExecutionSettings implements Cloneable { @NotNull @NonNls public static final String TAG_NAME = "ExternalSystemSettings"; + @NotNull @NonNls public static final Key JVM_AGENT_SETUP_KEY = Key.create("jvmAgentSetup"); private List myTaskNames = ContainerUtilRt.newArrayList(); private List myTaskDescriptions = ContainerUtilRt.newArrayList(); diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/settings/ExternalSystemExecutionSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/settings/ExternalSystemExecutionSettings.java index eb5bc7c6ea4b..2ade3fa60dd9 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/settings/ExternalSystemExecutionSettings.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/settings/ExternalSystemExecutionSettings.java @@ -8,6 +8,7 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.Serializable; +import java.util.*; /** * Holds execution settings of particular invocation of an external system. @@ -22,12 +23,16 @@ public class ExternalSystemExecutionSettings implements Serializable, UserDataHo private long myRemoteProcessIdleTtlInMs; private boolean myVerboseProcessing; + @NotNull private final Set myVmOptions; + @NotNull private final List myArguments; @NotNull private transient UserDataHolderBase myUserData = new UserDataHolderBase(); public ExternalSystemExecutionSettings() { int ttl = SystemProperties.getIntProperty(REMOTE_PROCESS_IDLE_TTL_IN_MS_KEY, DEFAULT_REMOTE_PROCESS_TTL_MS); setRemoteProcessIdleTtlInMs(ttl); + myVmOptions = new LinkedHashSet<>(); + myArguments = new ArrayList<>(); } /** @@ -49,6 +54,46 @@ public class ExternalSystemExecutionSettings implements Serializable, UserDataHo myVerboseProcessing = verboseProcessing; } + @NotNull + public Set getVmOptions() { + return Collections.unmodifiableSet(myVmOptions); + } + + @NotNull + public List getArguments() { + return Collections.unmodifiableList(myArguments); + } + + public ExternalSystemExecutionSettings withVmOptions(Collection vmOptions) { + myVmOptions.addAll(vmOptions); + return this; + } + + public ExternalSystemExecutionSettings withVmOptions(String... vmOptions) { + Collections.addAll(myVmOptions, vmOptions); + return this; + } + + public ExternalSystemExecutionSettings withVmOption(String vmOption) { + myVmOptions.add(vmOption); + return this; + } + + public ExternalSystemExecutionSettings withArguments(Collection arguments) { + myArguments.addAll(arguments); + return this; + } + + public ExternalSystemExecutionSettings withArguments(String... arguments) { + Collections.addAll(myArguments, arguments); + return this; + } + + public ExternalSystemExecutionSettings withArgument(String argument) { + myArguments.add(argument); + return this; + } + @Nullable @Override public U getUserData(@NotNull Key key) { @@ -64,6 +109,8 @@ public class ExternalSystemExecutionSettings implements Serializable, UserDataHo public int hashCode() { int result = (int)(myRemoteProcessIdleTtlInMs ^ (myRemoteProcessIdleTtlInMs >>> 32)); result = 31 * result + (myVerboseProcessing ? 1 : 0); + result = 31 * result + myVmOptions.hashCode(); + result = 31 * result + myArguments.hashCode(); return result; } @@ -76,6 +123,8 @@ public class ExternalSystemExecutionSettings implements Serializable, UserDataHo if (myRemoteProcessIdleTtlInMs != that.myRemoteProcessIdleTtlInMs) return false; if (myVerboseProcessing != that.myVerboseProcessing) return false; + if (!myVmOptions.equals(that.myVmOptions)) return false; + if (!myArguments.equals(that.myArguments)) return false; return true; } } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListener.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListener.java index 2d6c426f37d6..fd8cded4a1eb 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListener.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListener.java @@ -15,20 +15,23 @@ public interface ExternalSystemTaskNotificationListener { = ExtensionPointName.create("com.intellij.externalSystemTaskNotificationListener"); /** - * Notifies that task with the given id is queued for the execution. - *

- * 'Queued' here means that intellij process-local codebase receives request to execute the target task and even has not been - * sent it to the slave gradle api process. - * - * @param id target task's id - * @param workingDir + * @deprecated use {@link #onStart(ExternalSystemTaskId, String)} */ void onQueued(@NotNull ExternalSystemTaskId id, String workingDir); - + /** * Notifies that task with the given id is about to be started. - * + * * @param id target task's id + * @param workingDir working directory + */ + default void onStart(@NotNull ExternalSystemTaskId id, String workingDir) { + onQueued(id, workingDir); + onStart(id); + } + + /** + * @deprecated use {@link #onStart(ExternalSystemTaskId, String)} */ void onStart(@NotNull ExternalSystemTaskId id); diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListenerAdapter.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListenerAdapter.java index 87a36684af3b..ec9e20c8289a 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListenerAdapter.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/model/task/ExternalSystemTaskNotificationListenerAdapter.java @@ -10,11 +10,21 @@ public abstract class ExternalSystemTaskNotificationListenerAdapter implements E @NotNull public static final ExternalSystemTaskNotificationListener NULL_OBJECT = new ExternalSystemTaskNotificationListenerAdapter() { }; - @Override + /** + * @deprecated use {@link #onStart(ExternalSystemTaskId, String)} + */ public void onQueued(@NotNull ExternalSystemTaskId id, String workingDir) { } @Override + public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) { + onQueued(id, workingDir); + onStart(id); + } + + /** + * @deprecated use {@link #onStart(ExternalSystemTaskId, String)} + */ public void onStart(@NotNull ExternalSystemTaskId id) { } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java index 218e2490c6c6..03c918416c70 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/service/project/IdeModifiableModelsProvider.java @@ -27,6 +27,7 @@ import com.intellij.packaging.artifacts.ModifiableArtifactModel; import com.intellij.packaging.elements.PackagingElementResolvingContext; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; /** * @author Vladislav.Soroka @@ -71,4 +72,7 @@ public interface IdeModifiableModelsProvider extends IdeModelsProvider { void dispose(); void setTestModuleProperties(Module testModule, String productionModuleName); + + @Nullable + String getProductionModuleName(Module module); } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java index 7453a51674d4..b975789e5064 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/settings/AbstractExternalSystemLocalSettings.java @@ -22,7 +22,6 @@ import com.intellij.openapi.externalSystem.model.execution.ExternalTaskPojo; import com.intellij.openapi.externalSystem.model.project.ExternalProjectBuildClasspathPojo; import com.intellij.openapi.externalSystem.model.project.ExternalProjectPojo; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; -import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; import com.intellij.openapi.externalSystem.view.ExternalProjectsViewState; import com.intellij.openapi.module.Module; import com.intellij.openapi.module.ModuleManager; @@ -252,11 +251,8 @@ public abstract class AbstractExternalSystemLocalSettings { pathsToForget.remove(projectSettings.getExternalProjectPath()); } for (Module module : ModuleManager.getInstance(myProject).getModules()) { - String id = module.getOptionValue(ExternalSystemConstants.EXTERNAL_SYSTEM_ID_KEY); - if (!myExternalSystemId.toString().equals(id)) { - continue; - } - pathsToForget.remove(module.getOptionValue(ExternalSystemConstants.LINKED_PROJECT_PATH_KEY)); + if (!ExternalSystemApiUtil.isExternalSystemAwareModule(myExternalSystemId, module)) continue; + pathsToForget.remove(ExternalSystemApiUtil.getExternalProjectPath(module)); } if (!pathsToForget.isEmpty()) { diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/AbstractExternalSystemTaskManager.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/AbstractExternalSystemTaskManager.java index 0bb5a87464ba..b98bbd6bae6a 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/AbstractExternalSystemTaskManager.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/AbstractExternalSystemTaskManager.java @@ -28,6 +28,7 @@ import java.util.List; * @author Vladislav.Soroka * @since 12/19/13 */ +@Deprecated public abstract class AbstractExternalSystemTaskManager implements ExternalSystemTaskManager { public abstract void executeTasks(@NotNull ExternalSystemTaskId id, @@ -36,6 +37,6 @@ public abstract class AbstractExternalSystemTaskManager vmOptions, @NotNull List scriptParameters, - @Nullable String debuggerSetup, + @Nullable String jvmAgentSetup, @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException; } diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/ExternalSystemTaskManager.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/ExternalSystemTaskManager.java index 1c51b5a58041..dd7d802dc60e 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/ExternalSystemTaskManager.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/task/ExternalSystemTaskManager.java @@ -22,6 +22,7 @@ import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskNotifica import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import java.util.Collections; import java.util.List; /** @@ -32,14 +33,27 @@ import java.util.List; */ public interface ExternalSystemTaskManager { - void executeTasks(@NotNull ExternalSystemTaskId id, - @NotNull List taskNames, - @NotNull String projectPath, - @Nullable S settings, - @NotNull final List vmOptions, - @NotNull List scriptParameters, - @Nullable String debuggerSetup, - @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException; + /** + * @deprecated use {@link ExternalSystemTaskManager#executeTasks(ExternalSystemTaskId, List, String, ExternalSystemExecutionSettings, String, ExternalSystemTaskNotificationListener)} + */ + default void executeTasks(@NotNull ExternalSystemTaskId id, + @NotNull List taskNames, + @NotNull String projectPath, + @Nullable S settings, + @NotNull List vmOptions, + @NotNull List scriptParameters, + @Nullable String jvmAgentSetup, + @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException { + } + + default void executeTasks(@NotNull ExternalSystemTaskId id, + @NotNull List taskNames, + @NotNull String projectPath, + @Nullable S settings, + @Nullable String jvmAgentSetup, + @NotNull ExternalSystemTaskNotificationListener listener) throws ExternalSystemException { + executeTasks(id, taskNames, projectPath, settings, Collections.emptyList(), Collections.emptyList(), jvmAgentSetup, listener); + } boolean cancelTask(@NotNull ExternalSystemTaskId id, @NotNull ExternalSystemTaskNotificationListener listener) diff --git a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java index 5bd165bd5bb1..726087c069c0 100644 --- a/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java +++ b/platform/external-system-api/src/com/intellij/openapi/externalSystem/util/ExternalSystemApiUtil.java @@ -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. @@ -17,9 +17,7 @@ package com.intellij.openapi.externalSystem.util; import com.intellij.execution.rmi.RemoteUtil; import com.intellij.ide.util.PropertiesComponent; -import com.intellij.openapi.application.Application; -import com.intellij.openapi.application.ApplicationManager; -import com.intellij.openapi.application.PathManager; +import com.intellij.openapi.application.*; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.externalSystem.ExternalSystemAutoImportAware; import com.intellij.openapi.externalSystem.ExternalSystemManager; @@ -455,6 +453,7 @@ public class ExternalSystemApiUtil { } public static void executeProjectChangeAction(boolean synchronous, @NotNull final DisposeAwareProjectChange task) { + TransactionGuard.getInstance().assertWriteSafeContext(ModalityState.defaultModalityState()); executeOnEdt(synchronous, () -> ApplicationManager.getApplication().runWriteAction(() -> task.run())); } @@ -651,6 +650,9 @@ public class ExternalSystemApiUtil { @NotNull public static String buildErrorMessage(@NotNull Throwable e) { Throwable unwrapped = RemoteUtil.unwrap(e); + if (ApplicationManager.getApplication().isUnitTestMode()) { + return stacktraceAsString(unwrapped); + } String reason = unwrapped.getLocalizedMessage(); if (!StringUtil.isEmpty(reason)) { return reason; @@ -659,12 +661,16 @@ public class ExternalSystemApiUtil { return String.format("exception during working with external system: %s", ((ExternalSystemException)unwrapped).getOriginalReason()); } else { - StringWriter writer = new StringWriter(); - unwrapped.printStackTrace(new PrintWriter(writer)); - return writer.toString(); + return stacktraceAsString(unwrapped); } } + private static String stacktraceAsString(Throwable unwrapped) { + StringWriter writer = new StringWriter(); + unwrapped.printStackTrace(new PrintWriter(writer)); + return writer.toString(); + } + @SuppressWarnings("unchecked") @NotNull public static AbstractExternalSystemSettings getSettings(@NotNull Project project, @NotNull ProjectSystemId externalSystemId) diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/DetachExternalProjectAction.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/DetachExternalProjectAction.java index 5c4c9d7d75d7..e84d1b09ca54 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/DetachExternalProjectAction.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/action/DetachExternalProjectAction.java @@ -17,6 +17,7 @@ package com.intellij.openapi.externalSystem.action; import com.intellij.icons.AllIcons; import com.intellij.openapi.actionSystem.AnActionEvent; +import com.intellij.openapi.actionSystem.Presentation; import com.intellij.openapi.externalSystem.model.DataNode; import com.intellij.openapi.externalSystem.model.ExternalSystemDataKeys; import com.intellij.openapi.externalSystem.model.ProjectKeys; @@ -52,6 +53,17 @@ public class DetachExternalProjectAction extends ExternalSystemNodeAction externalProject) { + if (externalProject == null) { + return; + } + final boolean synchronous = mySpec.getProgressExecutionMode() == ProgressExecutionMode.MODAL_SYNC; + ServiceManager.getService(ProjectDataManager.class).importData(externalProject, mySpec.getProject(), synchronous); + } + + @Override + public void onFailure(@NotNull String errorMessage, @Nullable String errorDetails) { + } + }); + } + else { + mySpec.setCallback(myCallback); + } + mySpec.setPreviewMode(isPreviewMode); + mySpec.setReportRefreshError(isReportRefreshError); + mySpec.setArguments(myArguments); + mySpec.setVmOptions(myVmOptions); return mySpec; } + + private void apply(ImportSpec spec) { + myWhenAutoImportEnabled = spec.whenAutoImportEnabled(); + myProgressExecutionMode = spec.getProgressExecutionMode(); + myForceWhenUptodate = spec.isForceWhenUptodate(); + myCreateDirectoriesForEmptyContentRoots = spec.shouldCreateDirectoriesForEmptyContentRoots(); + myCallback = spec.getCallback(); + isPreviewMode = spec.isPreviewMode(); + isReportRefreshError = spec.isReportRefreshError(); + myArguments = spec.getArguments(); + myVmOptions = spec.getVmOptions(); + } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java new file mode 100644 index 000000000000..0e48df5bab93 --- /dev/null +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/importing/ImportSpecImpl.java @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2014 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.openapi.externalSystem.importing; + +import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.service.execution.ProgressExecutionMode; +import com.intellij.openapi.externalSystem.service.project.ExternalProjectRefreshCallback; +import com.intellij.openapi.project.Project; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +/** + * @author Vladislav.Soroka + * @since 5/29/2014 + */ +public class ImportSpecImpl implements ImportSpec { + @NotNull private final Project myProject; + @NotNull private final ProjectSystemId myExternalSystemId; + @NotNull private ProgressExecutionMode myProgressExecutionMode; + private boolean forceWhenUptodate; + private boolean whenAutoImportEnabled; + @Nullable private ExternalProjectRefreshCallback myCallback; + private boolean isPreviewMode; + private boolean createDirectoriesForEmptyContentRoots; + private boolean isReportRefreshError; + @Nullable private String myVmOptions; + @Nullable private String myArguments; + + public ImportSpecImpl(@NotNull Project project, @NotNull ProjectSystemId id) { + myProject = project; + myExternalSystemId = id; + myProgressExecutionMode = ProgressExecutionMode.MODAL_SYNC; + } + + @NotNull + @Override + public Project getProject() { + return myProject; + } + + @NotNull + @Override + public ProjectSystemId getExternalSystemId() { + return myExternalSystemId; + } + + @NotNull + @Override + public ProgressExecutionMode getProgressExecutionMode() { + return myProgressExecutionMode; + } + + public void setProgressExecutionMode(@NotNull ProgressExecutionMode progressExecutionMode) { + myProgressExecutionMode = progressExecutionMode; + } + + @Override + public boolean isForceWhenUptodate() { + return forceWhenUptodate; + } + + public void setForceWhenUptodate(boolean forceWhenUptodate) { + this.forceWhenUptodate = forceWhenUptodate; + } + + @Override + public boolean whenAutoImportEnabled() { + return whenAutoImportEnabled; + } + + public void setWhenAutoImportEnabled(boolean whenAutoImportEnabled) { + this.whenAutoImportEnabled = whenAutoImportEnabled; + } + + public void setCallback(@Nullable ExternalProjectRefreshCallback callback) { + myCallback = callback; + } + + @Nullable + @Override + public ExternalProjectRefreshCallback getCallback() { + return myCallback; + } + + @Override + public boolean isPreviewMode() { + return isPreviewMode; + } + + public void setPreviewMode(boolean isPreviewMode) { + this.isPreviewMode = isPreviewMode; + } + + @Override + public boolean shouldCreateDirectoriesForEmptyContentRoots() { + return createDirectoriesForEmptyContentRoots; + } + + public void setCreateDirectoriesForEmptyContentRoots(boolean createDirectoriesForEmptyContentRoots) { + this.createDirectoriesForEmptyContentRoots = createDirectoriesForEmptyContentRoots; + } + + @Override + public boolean isReportRefreshError() { + return isReportRefreshError; + } + + public void setReportRefreshError(boolean isReportRefreshError) { + this.isReportRefreshError = isReportRefreshError; + } + + @Nullable + @Override + public String getVmOptions() { + return myVmOptions; + } + + public void setVmOptions(@Nullable String vmOptions) { + myVmOptions = vmOptions; + } + + @Nullable + @Override + public String getArguments() { + return myArguments; + } + + public void setArguments(@Nullable String arguments) { + myArguments = arguments; + } +} diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/AbstractExternalSystemFacadeImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/AbstractExternalSystemFacadeImpl.java index bc6efab979ea..9a8e9f3782e3 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/AbstractExternalSystemFacadeImpl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/AbstractExternalSystemFacadeImpl.java @@ -210,14 +210,12 @@ public abstract class AbstractExternalSystemFacadeImpl 0) { - debuggerSetup = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + myDebugPort; + jvmAgentSetup = "-agentlib:jdwp=transport=dt_socket,server=n,suspend=y,address=" + myDebugPort; + } else { + ParametersList parametersList = myEnv.getUserData(ExternalSystemTaskExecutionSettings.JVM_AGENT_SETUP_KEY); + if (parametersList != null) { + for (String parameter : parametersList.getList()) { + if (parameter.startsWith("-agentlib:")) continue; + if (parameter.startsWith("-agentpath:")) continue; + if (parameter.startsWith("-javaagent:")) continue; + throw new ExecutionException(ExternalSystemBundle.message("run.invalid.jvm.agent.configuration", parameter)); + } + jvmAgentSetup = parametersList.getParametersString(); + } } ApplicationManager.getApplication().assertIsDispatchThread(); @@ -179,7 +193,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase { tasks, mySettings.getVmOptions(), mySettings.getScriptParameters(), - debuggerSetup); + jvmAgentSetup); copyUserDataTo(task); final MyProcessHandler processHandler = new MyProcessHandler(task); @@ -203,7 +217,7 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase { } processHandler.notifyTextAvailable(greeting, ProcessOutputTypes.SYSTEM); foldGreetingOrFarewell(consoleView, greeting, true); - task.execute(new ExternalSystemTaskNotificationListenerAdapter() { + ExternalSystemTaskNotificationListenerAdapter taskListener = new ExternalSystemTaskNotificationListenerAdapter() { private boolean myResetGreeting = true; @@ -241,7 +255,8 @@ public class ExternalSystemRunConfiguration extends LocatableConfigurationBase { foldGreetingOrFarewell(consoleView, farewell, false); processHandler.notifyProcessTerminated(0); } - }); + }; + task.execute(ArrayUtil.prepend(taskListener, ExternalSystemTaskNotificationListener.EP_NAME.getExtensions())); }); DefaultExecutionResult result = new DefaultExecutionResult(consoleView, processHandler); result.setRestartActions(consoleManager.getRestartActions(consoleView)); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskSettingsControl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskSettingsControl.java index 8bb868f30aa1..c0afa26798cd 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskSettingsControl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/execution/ExternalSystemTaskSettingsControl.java @@ -31,7 +31,6 @@ import com.intellij.openapi.util.text.StringUtil; import com.intellij.ui.EditorTextField; import com.intellij.ui.RawCommandLineEditor; import com.intellij.ui.components.JBLabel; -import com.intellij.ui.components.JBTextField; import com.intellij.util.ui.GridBag; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -59,8 +58,8 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings private JBLabel myVmOptionsLabel; private RawCommandLineEditor myVmOptionsEditor; @SuppressWarnings("FieldCanBeLocal") // Used via reflection at showUi() and disposeResources() - private JBLabel myScriptParametersLabel; - private RawCommandLineEditor myScriptParametersEditor; + private JBLabel myArgumentsLabel; + private RawCommandLineEditor myArgumentsEditor; @Nullable private ExternalSystemTaskExecutionSettings myOriginalSettings; @@ -110,11 +109,11 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings myVmOptionsEditor.setDialogCaption(ExternalSystemBundle.message("run.configuration.settings.label.vmoptions")); canvas.add(myVmOptionsLabel, ExternalSystemUiUtil.getLabelConstraints(0)); canvas.add(myVmOptionsEditor, ExternalSystemUiUtil.getFillLineConstraints(0)); - myScriptParametersLabel = new JBLabel(ExternalSystemBundle.message("run.configuration.settings.label.script.parameters")); - myScriptParametersEditor = new RawCommandLineEditor(); - myScriptParametersEditor.setDialogCaption(ExternalSystemBundle.message("run.configuration.settings.label.script.parameters")); - canvas.add(myScriptParametersLabel, ExternalSystemUiUtil.getLabelConstraints(0)); - canvas.add(myScriptParametersEditor, ExternalSystemUiUtil.getFillLineConstraints(0)); + myArgumentsLabel = new JBLabel(ExternalSystemBundle.message("run.configuration.settings.label.arguments")); + myArgumentsEditor = new RawCommandLineEditor(); + myArgumentsEditor.setDialogCaption(ExternalSystemBundle.message("run.configuration.settings.label.arguments")); + canvas.add(myArgumentsLabel, ExternalSystemUiUtil.getLabelConstraints(0)); + canvas.add(myArgumentsEditor, ExternalSystemUiUtil.getFillLineConstraints(0)); } @Override @@ -122,7 +121,7 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings myProjectPathField.setText(""); myTasksTextField.setText(""); myVmOptionsEditor.setText(""); - myScriptParametersEditor.setText(""); + myArgumentsEditor.setText(""); showUi(true); if (myOriginalSettings == null) { @@ -136,7 +135,7 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings myProjectPathField.setText(path); myTasksTextField.setText(StringUtil.join(myOriginalSettings.getTaskNames(), " ")); myVmOptionsEditor.setText(myOriginalSettings.getVmOptions()); - myScriptParametersEditor.setText(myOriginalSettings.getScriptParameters()); + myArgumentsEditor.setText(myOriginalSettings.getScriptParameters()); } @Override @@ -151,7 +150,7 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings normalizePath(StringUtil.join(myOriginalSettings.getTaskNames(), " "))) || !Comparing.equal(normalizePath(myVmOptionsEditor.getText()), normalizePath(myOriginalSettings.getVmOptions())) - || !Comparing.equal(normalizePath(myScriptParametersEditor.getText()), + || !Comparing.equal(normalizePath(myArgumentsEditor.getText()), normalizePath(myOriginalSettings.getScriptParameters())); } @@ -161,7 +160,7 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings settings.setExternalProjectPath(projectPath); settings.setTaskNames(StringUtil.split(myTasksTextField.getText(), " ")); settings.setVmOptions(myVmOptionsEditor.getText()); - settings.setScriptParameters(myScriptParametersEditor.getText()); + settings.setScriptParameters(myArgumentsEditor.getText()); } @Override @@ -170,8 +169,8 @@ public class ExternalSystemTaskSettingsControl implements ExternalSystemSettings if (myOriginalSettings == null) { throw new ConfigurationException(String.format( "Can't store external task settings into run configuration. Reason: target run configuration is undefined. Tasks: '%s', " + - "external project: '%s', vm options: '%s', script parameters: '%s'", - myTasksTextField.getText(), projectPath, myVmOptionsEditor.getText(), myScriptParametersEditor.getText() + "external project: '%s', vm options: '%s', arguments: '%s'", + myTasksTextField.getText(), projectPath, myVmOptionsEditor.getText(), myArgumentsEditor.getText() )); } return true; diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemExecuteTaskTask.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemExecuteTaskTask.java index 83d9ff945fbd..9f555e7c5ac5 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemExecuteTaskTask.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemExecuteTaskTask.java @@ -44,20 +44,20 @@ public class ExternalSystemExecuteTaskTask extends AbstractExternalSystemTask { @NotNull private final List myTasksToExecute; @Nullable private final String myVmOptions; - @Nullable private String myScriptParameters; - @Nullable private final String myDebuggerSetup; + @Nullable private String myArguments; + @Nullable private final String myJvmAgentSetup; public ExternalSystemExecuteTaskTask(@NotNull ProjectSystemId externalSystemId, @NotNull Project project, @NotNull List tasksToExecute, @Nullable String vmOptions, - @Nullable String scriptParameters, - @Nullable String debuggerSetup) throws IllegalArgumentException { + @Nullable String arguments, + @Nullable String jvmAgentSetup) throws IllegalArgumentException { super(externalSystemId, ExternalSystemTaskType.EXECUTE_TASK, project, getLinkedExternalProjectPath(tasksToExecute)); myTasksToExecute = tasksToExecute; myVmOptions = vmOptions; - myScriptParameters = scriptParameters; - myDebuggerSetup = debuggerSetup; + myArguments = arguments; + myJvmAgentSetup = jvmAgentSetup; } @NotNull @@ -71,12 +71,12 @@ public class ExternalSystemExecuteTaskTask extends AbstractExternalSystemTask { } @Nullable - public String getScriptParameters() { - return myScriptParameters; + public String getArguments() { + return myArguments; } - public void appendScriptParameters(@NotNull String scriptParameters) { - myScriptParameters = myScriptParameters == null ? scriptParameters : myScriptParameters + ' ' + scriptParameters; + public void appendArguments(@NotNull String arguments) { + myArguments = myArguments == null ? arguments : myArguments + ' ' + arguments; } @NotNull @@ -121,9 +121,10 @@ public class ExternalSystemExecuteTaskTask extends AbstractExternalSystemTask { List taskNames = ContainerUtilRt.map2List(myTasksToExecute, ExternalTaskPojo::getName); final List vmOptions = parseCmdParameters(myVmOptions); - final List scriptParametersList = parseCmdParameters(myScriptParameters); + final List arguments = parseCmdParameters(myArguments); + settings.withVmOptions(vmOptions).withArguments(arguments); - taskManager.executeTasks(getId(), taskNames, getExternalProjectPath(), settings, vmOptions, scriptParametersList, myDebuggerSetup); + taskManager.executeTasks(getId(), taskNames, getExternalProjectPath(), settings, myJvmAgentSetup); } @Override diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemProcessingManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemProcessingManager.java index 8d5da5e7276d..d7cfdf9335fa 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemProcessingManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemProcessingManager.java @@ -131,6 +131,11 @@ public class ExternalSystemProcessingManager implements ExternalSystemTaskNotifi @Override public void onQueued(@NotNull ExternalSystemTaskId id, String workingDir) { + onStart(id, workingDir); + } + + @Override + public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) { myTasksInProgress.put(id, System.currentTimeMillis() + TOO_LONG_EXECUTION_MS); if (myAlarm.getActiveRequestCount() <= 0) { myAlarm.addRequest(() -> update(), TOO_LONG_EXECUTION_MS); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemResolveProjectTask.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemResolveProjectTask.java index 53e435a693e2..817d01c69b3c 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemResolveProjectTask.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/internal/ExternalSystemResolveProjectTask.java @@ -1,5 +1,6 @@ package com.intellij.openapi.externalSystem.service.internal; +import com.intellij.execution.configurations.ParametersList; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.externalSystem.ExternalSystemManager; import com.intellij.openapi.externalSystem.model.DataNode; @@ -21,7 +22,9 @@ import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemBundle; import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.text.StringUtil; import com.intellij.util.containers.ContainerUtil; +import com.intellij.util.execution.ParametersListUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -41,14 +44,27 @@ public class ExternalSystemResolveProjectTask extends AbstractExternalSystemTask @NotNull private final String myProjectPath; private final boolean myIsPreviewMode; + @Nullable private final String myVmOptions; + @Nullable private final String myArguments; public ExternalSystemResolveProjectTask(@NotNull ProjectSystemId externalSystemId, @NotNull Project project, @NotNull String projectPath, boolean isPreviewMode) { + this(externalSystemId, project, projectPath, null, null, isPreviewMode); + } + + public ExternalSystemResolveProjectTask(@NotNull ProjectSystemId externalSystemId, + @NotNull Project project, + @NotNull String projectPath, + @Nullable String vmOptions, + @Nullable String arguments, + boolean isPreviewMode) { super(externalSystemId, ExternalSystemTaskType.RESOLVE_PROJECT, project, projectPath); myProjectPath = projectPath; myIsPreviewMode = isPreviewMode; + myVmOptions = vmOptions; + myArguments = arguments; } @SuppressWarnings("unchecked") @@ -57,11 +73,17 @@ public class ExternalSystemResolveProjectTask extends AbstractExternalSystemTask Project ideProject = getIdeProject(); RemoteExternalSystemProjectResolver resolver = manager.getFacade(ideProject, myProjectPath, getExternalSystemId()).getResolver(); ExternalSystemExecutionSettings settings = ExternalSystemApiUtil.getExecutionSettings(ideProject, myProjectPath, getExternalSystemId()); + if(StringUtil.isNotEmpty(myVmOptions)) { + settings.withVmOptions(ParametersListUtil.parse(myVmOptions)); + } + if(StringUtil.isNotEmpty(myArguments)) { + settings.withArguments(ParametersListUtil.parse(myArguments)); + } ExternalSystemProgressNotificationManagerImpl progressNotificationManager = (ExternalSystemProgressNotificationManagerImpl)ServiceManager.getService(ExternalSystemProgressNotificationManager.class); ExternalSystemTaskId id = getId(); - progressNotificationManager.onQueued(id, myProjectPath); + progressNotificationManager.onStart(id, myProjectPath); try { DataNode project = resolver.resolveProjectInfo(id, myProjectPath, myIsPreviewMode, settings); if (project != null) { diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java index 6a98ae509460..11e0b0c999c8 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/AbstractIdeModifiableModelsProvider.java @@ -433,4 +433,10 @@ public abstract class AbstractIdeModifiableModelsProvider extends IdeModelsProvi public void setTestModuleProperties(Module testModule, String productionModuleName) { myProductionModulesForTestModules.put(testModule, productionModuleName); } + + @Nullable + @Override + public String getProductionModuleName(Module module) { + return myProductionModulesForTestModules.get(module); + } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java index 1ac5390e3ee1..8d2135415410 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/IdeModelsProviderImpl.java @@ -94,7 +94,7 @@ public class IdeModelsProviderImpl implements IdeModelsProvider { if (modulePath.getParentFile() != null) { prefix = modulePath.getParentFile().getName(); } - char delimiter = ModuleGrouperKt.isQualifiedModuleNamesEnabled() ? '.' : '_'; + char delimiter = ModuleGrouperKt.isQualifiedModuleNamesEnabled() ? '.' : '-'; return new String[]{ module.getInternalName(), prefix + delimiter + module.getInternalName(), diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/autoimport/ExternalSystemProjectsWatcher.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/autoimport/ExternalSystemProjectsWatcher.java index a05c054586de..bb8b2db75726 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/autoimport/ExternalSystemProjectsWatcher.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/autoimport/ExternalSystemProjectsWatcher.java @@ -48,7 +48,6 @@ import com.intellij.openapi.util.Key; import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.vfs.*; -import com.intellij.openapi.vfs.newvfs.NewVirtualFile; import com.intellij.openapi.vfs.newvfs.events.VFileContentChangeEvent; import com.intellij.openapi.vfs.newvfs.events.VFileEvent; import com.intellij.openapi.vfs.pointers.VirtualFilePointer; @@ -176,7 +175,7 @@ public class ExternalSystemProjectsWatcher extends ExternalSystemTaskNotificatio } @Override - public void onQueued(@NotNull ExternalSystemTaskId id, String workingDir) { + public void onStart(@NotNull ExternalSystemTaskId id, String workingDir) { if (id.getType() == ExternalSystemTaskType.RESOLVE_PROJECT) { final ProjectSystemId systemId = id.getProjectSystemId(); for (String filePath : ContainerUtil.newArrayList(myKnownAffectedFiles.get(workingDir))) { @@ -404,7 +403,8 @@ public class ExternalSystemProjectsWatcher extends ExternalSystemTaskNotificatio String projectPath) { super(systemId.getReadableName() + " Import", ExternalSystemBundle.message("import.needed", systemId.getReadableName()), - "" + ExternalSystemBundle.message("import.importChanged") + " " + + "" + ExternalSystemBundle.message("import.importChanged") + "" + + "   " + "" + ExternalSystemBundle.message("import.enableAutoImport") + "", NotificationType.INFORMATION, null); @@ -420,6 +420,7 @@ public class ExternalSystemProjectsWatcher extends ExternalSystemTaskNotificatio projectPaths.stream() .map(path -> ExternalSystemApiUtil.getSettings(project, systemId).getLinkedProjectSettings(path)) .distinct() + .filter(Objects::nonNull) .forEach(settings -> { if (isReimport) { scheduleRefresh(project, settings.getExternalProjectPath(), systemId, true); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java index bade1175200a..02335c24f99f 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/project/manage/ContentRootDataService.java @@ -15,11 +15,11 @@ */ package com.intellij.openapi.externalSystem.service.project.manage; +import com.intellij.ide.projectView.ProjectView; +import com.intellij.ide.projectView.impl.ProjectViewPane; +import com.intellij.openapi.application.ModalityState; import com.intellij.openapi.diagnostic.Logger; -import com.intellij.openapi.externalSystem.model.DataNode; -import com.intellij.openapi.externalSystem.model.Key; -import com.intellij.openapi.externalSystem.model.ProjectKeys; -import com.intellij.openapi.externalSystem.model.ProjectSystemId; +import com.intellij.openapi.externalSystem.model.*; import com.intellij.openapi.externalSystem.model.project.ContentRootData; import com.intellij.openapi.externalSystem.model.project.ContentRootData.SourceRoot; import com.intellij.openapi.externalSystem.model.project.ExternalSystemSourceType; @@ -30,6 +30,7 @@ import com.intellij.openapi.externalSystem.settings.AbstractExternalSystemSettin import com.intellij.openapi.externalSystem.settings.ExternalProjectSettings; import com.intellij.openapi.externalSystem.util.ExternalSystemApiUtil; import com.intellij.openapi.externalSystem.util.ExternalSystemConstants; +import com.intellij.openapi.externalSystem.util.ExternalSystemUtil; import com.intellij.openapi.externalSystem.util.Order; import com.intellij.openapi.module.Module; import com.intellij.openapi.project.Project; @@ -64,6 +65,8 @@ import java.util.Set; */ @Order(ExternalSystemConstants.BUILTIN_SERVICE_ORDER) public class ContentRootDataService extends AbstractProjectDataService { + public static final com.intellij.openapi.util.Key CREATE_EMPTY_DIRECTORIES = + com.intellij.openapi.util.Key.create("createEmptyDirectories"); private static final Logger LOG = Logger.getInstance("#" + ContentRootDataService.class.getName()); @@ -82,6 +85,14 @@ public class ContentRootDataService extends AbstractProjectDataService projectDataNode = ExternalSystemApiUtil.findParent(toImport.iterator().next(), ProjectKeys.PROJECT); + if (projectDataNode != null) { + forceDirectoriesCreation = projectDataNode.getUserData(CREATE_EMPTY_DIRECTORIES) == Boolean.TRUE; + } + + Set modulesToExpand = ContainerUtil.newTroveSet(); MultiMap, DataNode> byModule = ExternalSystemApiUtil.groupBy(toImport, ModuleData.class); for (Map.Entry, Collection>> entry : byModule.entrySet()) { Module module = entry.getKey().getUserData(AbstractModuleDataService.MODULE_KEY); @@ -93,13 +104,34 @@ public class ContentRootDataService extends AbstractProjectDataService 0) { + VirtualFile virtualFile = roots[0]; + ExternalSystemUtil.invokeLater(project, ModalityState.NON_MODAL, () -> { + final ProjectView projectView = ProjectView.getInstance(project); + projectView.changeViewCB(ProjectViewPane.ID, null).doWhenProcessed(() -> projectView.selectCB(null, virtualFile, false)); + }); + } + } + } } } private static void importData(@NotNull IdeModifiableModelsProvider modelsProvider, @NotNull final Collection> data, - @NotNull final Module module) { + @NotNull final Module module, boolean forceDirectoriesCreation) { final ModifiableRootModel modifiableRootModel = modelsProvider.getModifiableRootModel(module); final ContentEntry[] contentEntries = modifiableRootModel.getContentEntries(); final Map contentEntriesMap = ContainerUtilRt.newHashMap(); @@ -107,8 +139,8 @@ public class ContentRootDataService extends AbstractProjectDataService map = ContainerUtil.newConcurrentMap(); + public final Map map = ContainerUtil.newConcurrentMap(); } static class ProjectState { @Property(surroundWithTag = false) @MapAnnotation(surroundWithTag = false, surroundValueWithTag = false, surroundKeyWithTag = false, keyAttributeName = "path", entryTagName = "dataType") - public Map map = ContainerUtil.newConcurrentMap(); + public final Map map = ContainerUtil.newConcurrentMap(); public boolean isInclusion; } static class ModuleState { @Property(surroundWithTag = false) @AbstractCollection(surroundWithTag = false, elementTag = "id") - public Set set = ContainerUtil.newConcurrentSet(); + public final Set set = ContainerUtil.newConcurrentSet(); public ModuleState() { } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/ExternalSystemProgressNotificationManagerImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/ExternalSystemProgressNotificationManagerImpl.java index b7eb7e8a6d7e..c21868f668e8 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/ExternalSystemProgressNotificationManagerImpl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/ExternalSystemProgressNotificationManagerImpl.java @@ -67,25 +67,15 @@ public class ExternalSystemProgressNotificationManagerImpl extends RemoteObject } @Override - public void onQueued(@NotNull ExternalSystemTaskId id, @NotNull String workingDir) { + public void onStart(@NotNull ExternalSystemTaskId id, @NotNull String workingDir) { for (Map.Entry> entry : myListeners.entrySet()) { final Set ids = entry.getValue(); if (Collections.EMPTY_SET == ids || ids.contains(id)) { - entry.getKey().onQueued(id, workingDir); + entry.getKey().onStart(id, workingDir); } } } - @Override - public void onStart(@NotNull ExternalSystemTaskId id) { - for (Map.Entry> entry : myListeners.entrySet()) { - final Set ids = entry.getValue(); - if (Collections.EMPTY_SET == ids || ids.contains(id)) { - entry.getKey().onStart(id); - } - } - } - @Override public void onStatusChange(@NotNull ExternalSystemTaskNotificationEvent event) { for (Map.Entry> entry : myListeners.entrySet()) { diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemProgressNotificationManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemProgressNotificationManager.java index f151d68cc30a..ca6fb84032de 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemProgressNotificationManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemProgressNotificationManager.java @@ -19,11 +19,7 @@ public interface RemoteExternalSystemProgressNotificationManager extends Remote RemoteExternalSystemProgressNotificationManager NULL_OBJECT = new RemoteExternalSystemProgressNotificationManager() { @Override - public void onQueued(@NotNull ExternalSystemTaskId id, @NotNull String projectPath) throws RemoteException { - } - - @Override - public void onStart(@NotNull ExternalSystemTaskId id) { + public void onStart(@NotNull ExternalSystemTaskId id, @NotNull String projectPath) throws RemoteException { } @Override @@ -55,9 +51,7 @@ public interface RemoteExternalSystemProgressNotificationManager extends Remote } }; - void onQueued(@NotNull ExternalSystemTaskId id, @NotNull String projectPath) throws RemoteException; - - void onStart(@NotNull ExternalSystemTaskId id) throws RemoteException; + void onStart(@NotNull ExternalSystemTaskId id, @NotNull String projectPath) throws RemoteException; void onStatusChange(@NotNull ExternalSystemTaskNotificationEvent event) throws RemoteException; diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManager.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManager.java index ab3f6c55a3af..6cd8a2aedf38 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManager.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManager.java @@ -47,10 +47,18 @@ public interface RemoteExternalSystemTaskManager vmOptions, @NotNull List scriptParameters, - @Nullable String debuggerSetup) throws RemoteException, ExternalSystemException + @Nullable String jvmAgentSetup) throws RemoteException, ExternalSystemException { } + @Override + public void executeTasks(@NotNull ExternalSystemTaskId id, + @NotNull List taskNames, + @NotNull String projectPath, + @Nullable ExternalSystemExecutionSettings settings, + @Nullable String jvmAgentSetup) throws RemoteException, ExternalSystemException { + } + @Override public boolean cancelTask(@NotNull ExternalSystemTaskId id) throws RemoteException, ExternalSystemException { @@ -77,13 +85,25 @@ public interface RemoteExternalSystemTaskManager taskNames, - @NotNull String projectPath, - @Nullable S settings, - @NotNull List vmOptions, - @NotNull List scriptParameters, - @Nullable String debuggerSetup) throws RemoteException, ExternalSystemException; + /** + * @deprecated use {@link RemoteExternalSystemTaskManager#executeTasks(ExternalSystemTaskId, List, String, ExternalSystemExecutionSettings, String)} + */ + default void executeTasks(@NotNull ExternalSystemTaskId id, + @NotNull List taskNames, + @NotNull String projectPath, + @Nullable S settings, + @NotNull List vmOptions, + @NotNull List scriptParameters, + @Nullable String jvmAgentSetup) throws RemoteException, ExternalSystemException { + } + + default void executeTasks(@NotNull ExternalSystemTaskId id, + @NotNull List taskNames, + @NotNull String projectPath, + @Nullable S settings, + @Nullable String jvmAgentSetup) throws RemoteException, ExternalSystemException { + executeTasks(id, taskNames, projectPath, settings, Collections.emptyList(), Collections.emptyList(), jvmAgentSetup); + } boolean cancelTask(@NotNull ExternalSystemTaskId id) throws RemoteException, ExternalSystemException; } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManagerImpl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManagerImpl.java index 639b284b42c4..24c95249c654 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManagerImpl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/RemoteExternalSystemTaskManagerImpl.java @@ -19,7 +19,6 @@ import com.intellij.openapi.externalSystem.model.ExternalSystemException; import com.intellij.openapi.externalSystem.model.settings.ExternalSystemExecutionSettings; import com.intellij.openapi.externalSystem.model.task.ExternalSystemTaskId; import com.intellij.openapi.externalSystem.task.ExternalSystemTaskManager; -import com.intellij.util.Producer; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -31,8 +30,7 @@ import java.util.List; * @since 4/9/13 7:49 PM */ public class RemoteExternalSystemTaskManagerImpl - extends AbstractRemoteExternalSystemService implements RemoteExternalSystemTaskManager -{ + extends AbstractRemoteExternalSystemService implements RemoteExternalSystemTaskManager { @NotNull private final ExternalSystemTaskManager myDelegate; @@ -45,20 +43,16 @@ public class RemoteExternalSystemTaskManagerImpl taskNames, @NotNull final String projectPath, @Nullable final S settings, - @NotNull final List vmOptions, - @NotNull final List scriptParameters, - @Nullable final String debuggerSetup) throws RemoteException, ExternalSystemException - { + @Nullable final String jvmAgentSetup) throws RemoteException, ExternalSystemException { execute(id, () -> { myDelegate.executeTasks( - id, taskNames, projectPath, settings, vmOptions, scriptParameters, debuggerSetup, getNotificationListener()); + id, taskNames, projectPath, settings, jvmAgentSetup, getNotificationListener()); return null; }); } @Override - public boolean cancelTask(@NotNull final ExternalSystemTaskId id) throws RemoteException, ExternalSystemException - { + public boolean cancelTask(@NotNull final ExternalSystemTaskId id) throws RemoteException, ExternalSystemException { return myDelegate.cancelTask(id, getNotificationListener()); } } diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemProjectResolverWrapper.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemProjectResolverWrapper.java index cc5ee1c89293..2b13fb84e19f 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemProjectResolverWrapper.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemProjectResolverWrapper.java @@ -15,7 +15,7 @@ import java.rmi.RemoteException; /** * Intercepts calls to the target {@link RemoteExternalSystemProjectResolver} and - * {@link ExternalSystemTaskNotificationListener#onQueued(ExternalSystemTaskId, String) updates 'queued' task status}. + * {@link ExternalSystemTaskNotificationListener#onStart(ExternalSystemTaskId, String) updates 'queued' task status}. *

* Thread-safe. * diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemTaskManagerWrapper.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemTaskManagerWrapper.java index 12cfc03b8389..b4b50c17385c 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemTaskManagerWrapper.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/remote/wrapper/ExternalSystemTaskManagerWrapper.java @@ -47,12 +47,11 @@ public class ExternalSystemTaskManagerWrapper taskNames, @NotNull String projectPath, @Nullable S settings, - @NotNull List vmOptions, - @NotNull List scriptParameters, - @Nullable String debuggerSetup) throws RemoteException, ExternalSystemException { - myProgressManager.onQueued(id, projectPath); + @Nullable String jvmAgentSetup) throws RemoteException, ExternalSystemException { + myProgressManager.onStart(id, projectPath); try { - getDelegate().executeTasks(id, taskNames, projectPath, settings, vmOptions, scriptParameters, debuggerSetup); + getDelegate().executeTasks(id, taskNames, projectPath, settings, jvmAgentSetup); + myProgressManager.onSuccess(id); } catch (ExternalSystemException e) { myProgressManager.onFailure(id, e); diff --git a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java index e71dddc52143..b87b035e3615 100644 --- a/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java +++ b/platform/external-system-impl/src/com/intellij/openapi/externalSystem/service/settings/AbstractExternalProjectSettingsControl.java @@ -101,7 +101,13 @@ public abstract class AbstractExternalProjectSettingsControl manager = ExternalSystemApiUtil.getManager(spec.getExternalSystemId()); if (manager == null) { return; @@ -256,7 +264,7 @@ public class ExternalSystemUtil { Set toRefresh = ContainerUtilRt.newHashSet(); for (ExternalProjectSettings setting : projectsSettings) { // don't refresh project when auto-import is disabled if such behavior needed (e.g. on project opening when auto-import is disabled) - if (!setting.isUseAutoImport() && spec.isWhenAutoImportEnabled()) continue; + if (!setting.isUseAutoImport() && spec.whenAutoImportEnabled()) continue; toRefresh.add(setting.getExternalProjectPath()); } @@ -265,8 +273,7 @@ public class ExternalSystemUtil { .clearNotifications(null, NotificationSource.PROJECT_SYNC, spec.getExternalSystemId()); for (String path : toRefresh) { - refreshProject( - spec.getProject(), spec.getExternalSystemId(), path, callback, false, spec.getProgressExecutionMode()); + refreshProject(path, new ImportSpecBuilder(spec).callback(callback).build()); } } } @@ -323,14 +330,13 @@ public class ExternalSystemUtil { } /** - * TODO[Vlad]: refactor the method to use {@link ImportSpecBuilder} *

- * Queries slave gradle process to refresh target gradle project. + * Refresh target gradle project. * * @param project target intellij project to use - * @param externalProjectPath path of the target gradle project's file + * @param externalProjectPath path of the target external project * @param callback callback to be notified on refresh result - * @param isPreviewMode flag that identifies whether gradle libraries should be resolved during the refresh + * @param isPreviewMode flag that identifies whether libraries should be resolved during the refresh * @param reportRefreshError prevent to show annoying error notification, e.g. if auto-import mode used */ public static void refreshProject(@NotNull final Project project, @@ -340,6 +346,22 @@ public class ExternalSystemUtil { final boolean isPreviewMode, @NotNull final ProgressExecutionMode progressExecutionMode, final boolean reportRefreshError) { + ImportSpecBuilder builder = new ImportSpecBuilder(project, externalSystemId).callback(callback).use(progressExecutionMode); + if (isPreviewMode) builder.usePreviewMode(); + if (!reportRefreshError) builder.dontReportRefreshErrors(); + refreshProject(externalProjectPath, builder.build()); + } + + public static void refreshProject(@NotNull final String externalProjectPath, @NotNull final ImportSpec importSpec) { + Project project = importSpec.getProject(); + ProjectSystemId externalSystemId = importSpec.getExternalSystemId(); + ExternalProjectRefreshCallback callback = importSpec.getCallback(); + boolean isPreviewMode = importSpec.isPreviewMode(); + ProgressExecutionMode progressExecutionMode = importSpec.getProgressExecutionMode(); + boolean reportRefreshError = importSpec.isReportRefreshError(); + String arguments = importSpec.getArguments(); + String vmOptions = importSpec.getVmOptions(); + File projectFile = new File(externalProjectPath); final String projectName; if (projectFile.isFile()) { @@ -350,7 +372,7 @@ public class ExternalSystemUtil { } final TaskUnderProgress refreshProjectStructureTask = new TaskUnderProgress() { private final ExternalSystemResolveProjectTask myTask - = new ExternalSystemResolveProjectTask(externalSystemId, project, externalProjectPath, isPreviewMode); + = new ExternalSystemResolveProjectTask(externalSystemId, project, externalProjectPath, vmOptions, arguments, isPreviewMode); @SuppressWarnings({"ThrowableResultOfMethodCallIgnored", "IOResourceOpenedButNotSafelyClosed"}) @Override @@ -371,7 +393,9 @@ public class ExternalSystemUtil { ExternalSystemProcessingManager processingManager = ServiceManager.getService(ExternalSystemProcessingManager.class); if (processingManager.findTask(ExternalSystemTaskType.RESOLVE_PROJECT, externalSystemId, externalProjectPath) != null) { - callback.onFailure(ExternalSystemBundle.message("error.resolve.already.running", externalProjectPath), null); + if (callback != null) { + callback.onFailure(ExternalSystemBundle.message("error.resolve.already.running", externalProjectPath), null); + } return; } @@ -390,7 +414,13 @@ public class ExternalSystemUtil { final Throwable error = myTask.getError(); if (error == null) { - callback.onSuccess(myTask.getExternalProject()); + if (callback != null) { + DataNode externalProject = myTask.getExternalProject(); + if (externalProject != null && importSpec.shouldCreateDirectoriesForEmptyContentRoots()) { + externalProject.putUserData(ContentRootDataService.CREATE_EMPTY_DIRECTORIES, Boolean.TRUE); + } + callback.onSuccess(externalProject); + } if (!isPreviewMode) { externalSystemTaskActivator.runTasks(externalProjectPath, ExternalSystemTaskActivator.Phase.AFTER_SYNC); } @@ -407,7 +437,9 @@ public class ExternalSystemUtil { ); } - callback.onFailure(message, extractDetails(error)); + if (callback != null) { + callback.onFailure(message, extractDetails(error)); + } ExternalSystemManager manager = ExternalSystemApiUtil.getManager(externalSystemId); if (manager == null) { diff --git a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java index 94fbb9c3b953..68f0a00e0f91 100644 --- a/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java +++ b/platform/external-system-impl/testSrc/com/intellij/openapi/externalSystem/test/ExternalSystemTestCase.java @@ -38,7 +38,10 @@ import com.intellij.openapi.util.SystemInfo; import com.intellij.openapi.util.io.ByteSequence; import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtilRt; -import com.intellij.openapi.vfs.*; +import com.intellij.openapi.vfs.CharsetToolkit; +import com.intellij.openapi.vfs.JarFileSystem; +import com.intellij.openapi.vfs.LocalFileSystem; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.openapi.vfs.newvfs.impl.VfsRootAccess; import com.intellij.packaging.artifacts.Artifact; import com.intellij.packaging.impl.compiler.ArtifactCompileScope; @@ -75,16 +78,12 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { private File ourTempDir; protected IdeaProjectTestFixture myTestFixture; - protected Project myProject; - protected File myTestDir; protected VirtualFile myProjectRoot; protected VirtualFile myProjectConfig; protected List myAllConfigs = new ArrayList<>(); - private List myAllowedRoots = new ArrayList<>(); - @Before @Override public void setUp() throws Exception { @@ -115,7 +114,7 @@ public abstract class ExternalSystemTestCase extends UsefulTestCase { List allowedRoots = new ArrayList<>(); collectAllowedRoots(allowedRoots); if (!allowedRoots.isEmpty()) { - VfsRootAccess.allowRootAccess(getTestRootDisposable(), ArrayUtil.toStringArray(allowedRoots)); + VfsRootAccess.allowRootAccess(myTestFixture.getTestRootDisposable(), ArrayUtil.toStringArray(allowedRoots)); } CompilerTestUtil.enableExternalCompiler(); diff --git a/platform/icons/src/actions/closeNew.png b/platform/icons/src/actions/closeNew.png index 30161d03e7dd..cd9daf8c4b91 100644 Binary files a/platform/icons/src/actions/closeNew.png and b/platform/icons/src/actions/closeNew.png differ diff --git a/platform/icons/src/actions/closeNew@2x.png b/platform/icons/src/actions/closeNew@2x.png index 78c01faecf33..8aad18367e15 100644 Binary files a/platform/icons/src/actions/closeNew@2x.png and b/platform/icons/src/actions/closeNew@2x.png differ diff --git a/platform/icons/src/actions/closeNew@2x_dark.png b/platform/icons/src/actions/closeNew@2x_dark.png index 837e05ea156a..8aad18367e15 100644 Binary files a/platform/icons/src/actions/closeNew@2x_dark.png and b/platform/icons/src/actions/closeNew@2x_dark.png differ diff --git a/platform/icons/src/actions/closeNewHovered.png b/platform/icons/src/actions/closeNewHovered.png index 7fbf4147a23b..6fe6ad248caf 100644 Binary files a/platform/icons/src/actions/closeNewHovered.png and b/platform/icons/src/actions/closeNewHovered.png differ diff --git a/platform/icons/src/actions/closeNewHovered@2x.png b/platform/icons/src/actions/closeNewHovered@2x.png index 34a72c5e0434..ace69181f1cf 100644 Binary files a/platform/icons/src/actions/closeNewHovered@2x.png and b/platform/icons/src/actions/closeNewHovered@2x.png differ diff --git a/platform/icons/src/actions/closeNewHovered@2x_dark.png b/platform/icons/src/actions/closeNewHovered@2x_dark.png index f42ef0c4a316..ace69181f1cf 100644 Binary files a/platform/icons/src/actions/closeNewHovered@2x_dark.png and b/platform/icons/src/actions/closeNewHovered@2x_dark.png differ diff --git a/platform/icons/src/actions/closeNewHovered_dark.png b/platform/icons/src/actions/closeNewHovered_dark.png index dd4506b5492b..6fe6ad248caf 100644 Binary files a/platform/icons/src/actions/closeNewHovered_dark.png and b/platform/icons/src/actions/closeNewHovered_dark.png differ diff --git a/platform/icons/src/actions/closeNew_dark.png b/platform/icons/src/actions/closeNew_dark.png index 7b1a5cb62db8..cd9daf8c4b91 100644 Binary files a/platform/icons/src/actions/closeNew_dark.png and b/platform/icons/src/actions/closeNew_dark.png differ diff --git a/platform/icons/src/general/recursive.png b/platform/icons/src/general/recursive.png index c7e85c1ec0f8..b9aaf44964a3 100644 Binary files a/platform/icons/src/general/recursive.png and b/platform/icons/src/general/recursive.png differ diff --git a/platform/icons/src/general/recursive@2x.png b/platform/icons/src/general/recursive@2x.png index ca7e8404b580..699dd9b5a2af 100644 Binary files a/platform/icons/src/general/recursive@2x.png and b/platform/icons/src/general/recursive@2x.png differ diff --git a/platform/icons/src/general/recursive@2x_dark.png b/platform/icons/src/general/recursive@2x_dark.png index 0ce3ae3f7bf0..362b1e141920 100644 Binary files a/platform/icons/src/general/recursive@2x_dark.png and b/platform/icons/src/general/recursive@2x_dark.png differ diff --git a/platform/icons/src/general/recursive_dark.png b/platform/icons/src/general/recursive_dark.png index af69fb641a86..4bc8774f20ea 100644 Binary files a/platform/icons/src/general/recursive_dark.png and b/platform/icons/src/general/recursive_dark.png differ diff --git a/platform/icons/src/modules/types/JavaeeAppModule.png b/platform/icons/src/modules/types/JavaeeAppModule.png deleted file mode 100644 index 9d27a8e4d28e..000000000000 Binary files a/platform/icons/src/modules/types/JavaeeAppModule.png and /dev/null differ diff --git a/platform/icons/src/modules/types/JavaeeAppModule@2x.png b/platform/icons/src/modules/types/JavaeeAppModule@2x.png deleted file mode 100644 index 5d7ac6b866f2..000000000000 Binary files a/platform/icons/src/modules/types/JavaeeAppModule@2x.png and /dev/null differ diff --git a/platform/icons/src/modules/types/JavaeeAppModule@2x_dark.png b/platform/icons/src/modules/types/JavaeeAppModule@2x_dark.png deleted file mode 100644 index 09f394c18070..000000000000 Binary files a/platform/icons/src/modules/types/JavaeeAppModule@2x_dark.png and /dev/null differ diff --git a/platform/icons/src/modules/types/JavaeeAppModule_dark.png b/platform/icons/src/modules/types/JavaeeAppModule_dark.png deleted file mode 100644 index 1a442266005b..000000000000 Binary files a/platform/icons/src/modules/types/JavaeeAppModule_dark.png and /dev/null differ diff --git a/platform/icons/src/modules/types/ejbModule.png b/platform/icons/src/modules/types/ejbModule.png deleted file mode 100644 index 356156c24ae4..000000000000 Binary files a/platform/icons/src/modules/types/ejbModule.png and /dev/null differ diff --git a/platform/icons/src/modules/types/ejbModule@2x.png b/platform/icons/src/modules/types/ejbModule@2x.png deleted file mode 100644 index 007808d6611d..000000000000 Binary files a/platform/icons/src/modules/types/ejbModule@2x.png and /dev/null differ diff --git a/platform/icons/src/modules/types/emptyProjectType.png b/platform/icons/src/modules/types/emptyProjectType.png deleted file mode 100644 index 639ad5701228..000000000000 Binary files a/platform/icons/src/modules/types/emptyProjectType.png and /dev/null differ diff --git a/platform/icons/src/modules/types/emptyProjectType@2x.png b/platform/icons/src/modules/types/emptyProjectType@2x.png deleted file mode 100644 index c3ef948ce63a..000000000000 Binary files a/platform/icons/src/modules/types/emptyProjectType@2x.png and /dev/null differ diff --git a/platform/icons/src/modules/types/javaModule.png b/platform/icons/src/modules/types/javaModule.png deleted file mode 100644 index 1c294793adb8..000000000000 Binary files a/platform/icons/src/modules/types/javaModule.png and /dev/null differ diff --git a/platform/icons/src/modules/types/javaModule@2x.png b/platform/icons/src/modules/types/javaModule@2x.png deleted file mode 100644 index 4ebbd7467594..000000000000 Binary files a/platform/icons/src/modules/types/javaModule@2x.png and /dev/null differ diff --git a/platform/icons/src/modules/types/pluginModule.png b/platform/icons/src/modules/types/pluginModule.png deleted file mode 100644 index 244bd2c09b50..000000000000 Binary files a/platform/icons/src/modules/types/pluginModule.png and /dev/null differ diff --git a/platform/icons/src/modules/types/pluginModule@2x.png b/platform/icons/src/modules/types/pluginModule@2x.png deleted file mode 100644 index c166d910d288..000000000000 Binary files a/platform/icons/src/modules/types/pluginModule@2x.png and /dev/null differ diff --git a/platform/icons/src/modules/types/pluginModule@2x_dark.png b/platform/icons/src/modules/types/pluginModule@2x_dark.png deleted file mode 100644 index d4048c7dbf0f..000000000000 Binary files a/platform/icons/src/modules/types/pluginModule@2x_dark.png and /dev/null differ diff --git a/platform/icons/src/modules/types/pluginModule_dark.png b/platform/icons/src/modules/types/pluginModule_dark.png deleted file mode 100644 index e2525561f623..000000000000 Binary files a/platform/icons/src/modules/types/pluginModule_dark.png and /dev/null differ diff --git a/platform/icons/src/modules/types/webModule.png b/platform/icons/src/modules/types/webModule.png deleted file mode 100644 index 528314e5ed49..000000000000 Binary files a/platform/icons/src/modules/types/webModule.png and /dev/null differ diff --git a/platform/icons/src/modules/types/webModule@2x.png b/platform/icons/src/modules/types/webModule@2x.png deleted file mode 100644 index 1687d667ff36..000000000000 Binary files a/platform/icons/src/modules/types/webModule@2x.png and /dev/null differ diff --git a/platform/indexing-api/src/com/intellij/psi/search/PsiReferenceProcessor.java b/platform/indexing-api/src/com/intellij/psi/search/PsiReferenceProcessor.java index 2c0239e70887..20efa52f909e 100644 --- a/platform/indexing-api/src/com/intellij/psi/search/PsiReferenceProcessor.java +++ b/platform/indexing-api/src/com/intellij/psi/search/PsiReferenceProcessor.java @@ -16,6 +16,7 @@ package com.intellij.psi.search; import com.intellij.psi.PsiReference; +import org.jetbrains.annotations.NotNull; import java.util.ArrayList; import java.util.Collection; @@ -35,10 +36,12 @@ public interface PsiReferenceProcessor{ this(new ArrayList<>()); } + @NotNull public PsiReference[] toArray(){ return myCollection.toArray(new PsiReference[myCollection.size()]); } + @NotNull public PsiReference[] toArray(PsiReference[] array){ return myCollection.toArray(array); } diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/IndexPatternUtil.java b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/IndexPatternUtil.java index 383687a3960d..e4f4f31a80af 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/IndexPatternUtil.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/cache/impl/IndexPatternUtil.java @@ -18,10 +18,12 @@ package com.intellij.psi.impl.cache.impl; import com.intellij.openapi.extensions.Extensions; import com.intellij.psi.search.IndexPattern; import com.intellij.psi.search.IndexPatternProvider; +import org.jetbrains.annotations.NotNull; import java.util.Arrays; public class IndexPatternUtil { + @NotNull public static IndexPatternProvider[] getIndexPatternProviders() { return Extensions.getExtensions(IndexPatternProvider.EP_NAME); } @@ -30,6 +32,7 @@ public class IndexPatternUtil { return Arrays.stream(getIndexPatternProviders()).mapToInt(provider -> provider.getIndexPatterns().length).sum(); } + @NotNull public static IndexPattern[] getIndexPatterns() { IndexPattern[] result = new IndexPattern[getIndexPatternCount()]; int destIndex = 0; diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java b/platform/indexing-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java index 1d9c23798efa..9ada05a55d26 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/file/impl/ResolveScopeManagerImpl.java @@ -153,7 +153,7 @@ public class ResolveScopeManagerImpl extends ResolveScopeManager { @Override public GlobalSearchScope getDefaultResolveScope(final VirtualFile vFile) { final PsiFile psiFile = myManager.findFile(vFile); - assert psiFile != null; + assert psiFile != null : "directory=" + vFile.isDirectory() + "; " + myProject; return getResolveScopeFromProviders(vFile); } diff --git a/platform/indexing-impl/src/com/intellij/psi/impl/search/LowLevelSearchUtil.java b/platform/indexing-impl/src/com/intellij/psi/impl/search/LowLevelSearchUtil.java index 31b26d7ea01a..6f5f5f403b97 100644 --- a/platform/indexing-impl/src/com/intellij/psi/impl/search/LowLevelSearchUtil.java +++ b/platform/indexing-impl/src/com/intellij/psi/impl/search/LowLevelSearchUtil.java @@ -183,6 +183,7 @@ public class LowLevelSearchUtil { return processElementsAtOffsets(scope, searcher, processInjectedPsi, progress, occurrences, processor); } + @NotNull static int[] getTextOccurrencesInScope(@NotNull PsiElement scope, @NotNull StringSearcher searcher, ProgressIndicator progress) { if (progress != null) progress.checkCanceled(); @@ -265,6 +266,7 @@ public class LowLevelSearchUtil { return true; } + @NotNull private static int[] getTextOccurrences(@NotNull CharSequence text, int startOffset, int endOffset, diff --git a/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java b/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java index 58067b0a2f61..d4d2b80efd1e 100644 --- a/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java +++ b/platform/indexing-impl/src/com/intellij/psi/search/FilenameIndex.java @@ -43,6 +43,7 @@ public class FilenameIndex { @Deprecated public @NonNls static final ID NAME = ID.create("FilenameIndex"); + @NotNull public static String[] getAllFilenames(Project project) { Set names = new THashSet<>(); getService().processAllFileNames((String s) -> { @@ -68,6 +69,7 @@ public class FilenameIndex { return getVirtualFilesByNameIgnoringCase(name, scope, project, null); } + @NotNull public static PsiFile[] getFilesByName(final Project project, final String name, final GlobalSearchScope scope) { return (PsiFile[])getFilesByName(project, name, scope, false); } @@ -142,6 +144,7 @@ public class FilenameIndex { return files; } + @NotNull public static PsiFileSystemItem[] getFilesByName(final Project project, final String name, @NotNull final GlobalSearchScope scope, diff --git a/platform/lang-api/src/com/intellij/codeInsight/daemon/GutterIconDescriptor.java b/platform/lang-api/src/com/intellij/codeInsight/daemon/GutterIconDescriptor.java index db8e10de0890..dde382c816c5 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/daemon/GutterIconDescriptor.java +++ b/platform/lang-api/src/com/intellij/codeInsight/daemon/GutterIconDescriptor.java @@ -51,6 +51,7 @@ public abstract class GutterIconDescriptor { return getClass().getName(); } + @NotNull public Option[] getOptions() { return NO_OPTIONS; } diff --git a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.java b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.java index 9f7874a71f91..56a54e1d14ce 100644 --- a/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.java +++ b/platform/lang-api/src/com/intellij/codeInsight/hints/InlayParameterHintsProvider.java @@ -15,6 +15,7 @@ */ package com.intellij.codeInsight.hints; +import com.intellij.codeInsight.CodeInsightBundle; import com.intellij.lang.Language; import com.intellij.psi.PsiElement; import com.intellij.util.containers.ContainerUtil; @@ -75,6 +76,13 @@ public interface InlayParameterHintsProvider { return true; } + /** + * Text explaining black list patterns + */ + default String getBlacklistExplanationHTML() { + return CodeInsightBundle.message("inlay.hints.blacklist.pattern.explanation"); + } + /** * Customise hints presentation */ diff --git a/platform/lang-api/src/com/intellij/ide/PsiActionSupportFactory.java b/platform/lang-api/src/com/intellij/ide/PsiActionSupportFactory.java index a98c904aa1a6..65bb3b88b0ec 100644 --- a/platform/lang-api/src/com/intellij/ide/PsiActionSupportFactory.java +++ b/platform/lang-api/src/com/intellij/ide/PsiActionSupportFactory.java @@ -19,6 +19,7 @@ package com.intellij.ide; import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.project.Project; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.NotNull; import javax.swing.*; @@ -31,6 +32,7 @@ public abstract class PsiActionSupportFactory { } public static interface PsiElementSelector { + @NotNull PsiElement[] getSelectedElements(); } diff --git a/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java b/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java index 4f9c930af817..113720bbebd8 100644 --- a/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java +++ b/platform/lang-api/src/com/intellij/ide/util/projectWizard/ModuleBuilder.java @@ -344,10 +344,6 @@ public abstract class ModuleBuilder extends AbstractModuleBuilder { return null; } - public Icon getBigIcon() { - return getModuleType().getBigIcon(); - } - public Icon getNodeIcon() { return getModuleType().getNodeIcon(false); } diff --git a/platform/lang-api/src/com/intellij/lang/PairedBraceMatcher.java b/platform/lang-api/src/com/intellij/lang/PairedBraceMatcher.java index 1a40637f0fed..5d969ddff08a 100644 --- a/platform/lang-api/src/com/intellij/lang/PairedBraceMatcher.java +++ b/platform/lang-api/src/com/intellij/lang/PairedBraceMatcher.java @@ -37,6 +37,7 @@ public interface PairedBraceMatcher { * * @return the array of brace pair definitions. */ + @NotNull BracePair[] getPairs(); /** diff --git a/platform/lang-api/src/com/intellij/lang/parameterInfo/CreateParameterInfoContext.java b/platform/lang-api/src/com/intellij/lang/parameterInfo/CreateParameterInfoContext.java index e15586e34137..26bb72087d26 100644 --- a/platform/lang-api/src/com/intellij/lang/parameterInfo/CreateParameterInfoContext.java +++ b/platform/lang-api/src/com/intellij/lang/parameterInfo/CreateParameterInfoContext.java @@ -17,8 +17,10 @@ package com.intellij.lang.parameterInfo; import com.intellij.psi.PsiElement; +import org.jetbrains.annotations.Nullable; public interface CreateParameterInfoContext extends ParameterInfoContext { + @Nullable Object[] getItemsToShow(); void setItemsToShow(Object[] items); diff --git a/platform/lang-api/src/com/intellij/openapi/module/ModuleType.java b/platform/lang-api/src/com/intellij/openapi/module/ModuleType.java index 643cab16cc1d..dd1079b8b5ef 100644 --- a/platform/lang-api/src/com/intellij/openapi/module/ModuleType.java +++ b/platform/lang-api/src/com/intellij/openapi/module/ModuleType.java @@ -48,7 +48,6 @@ public abstract class ModuleType { public abstract String getName(); @NotNull public abstract String getDescription(); - public abstract Icon getBigIcon(); public Icon getIcon() { return getNodeIcon(false); diff --git a/platform/lang-api/src/com/intellij/openapi/projectRoots/SdkType.java b/platform/lang-api/src/com/intellij/openapi/projectRoots/SdkType.java index 9ef75f4a6fb7..affe26e8374b 100644 --- a/platform/lang-api/src/com/intellij/openapi/projectRoots/SdkType.java +++ b/platform/lang-api/src/com/intellij/openapi/projectRoots/SdkType.java @@ -245,13 +245,24 @@ public abstract class SdkType implements SdkTypeId { } /** - * Shows the custom SDK create UI. The returned SDK needs to have the correct name and home path; the framework will call - * setupSdkPaths() on the returned SDK. + * Shows the custom SDK create UI based on selected SDK in parent component. The returned SDK needs to have the correct name and home path; + * the framework will call setupSdkPaths() on the returned SDK. * * @param sdkModel the list of SDKs currently displayed in the configuration dialog. * @param parentComponent the parent component for showing the dialog. + * @param selectedSdk current selected sdk in parentComponent * @param sdkCreatedCallback the callback to which the created SDK is passed. - * @since 12.0 + * @since 2017.1 + */ + public void showCustomCreateUI(@NotNull SdkModel sdkModel, + @NotNull JComponent parentComponent, + @Nullable Sdk selectedSdk, + @NotNull Consumer sdkCreatedCallback) { + showCustomCreateUI(sdkModel, parentComponent, sdkCreatedCallback); + } + + /** + * @deprecated Use {@link #showCustomCreateUI(SdkModel, JComponent, Sdk, Consumer)} method instead */ public void showCustomCreateUI(@NotNull SdkModel sdkModel, @NotNull JComponent parentComponent, @NotNull Consumer sdkCreatedCallback) { } diff --git a/platform/lang-api/src/com/intellij/psi/util/ReferenceSetBase.java b/platform/lang-api/src/com/intellij/psi/util/ReferenceSetBase.java index b36e17dad4df..26b47c95cefa 100644 --- a/platform/lang-api/src/com/intellij/psi/util/ReferenceSetBase.java +++ b/platform/lang-api/src/com/intellij/psi/util/ReferenceSetBase.java @@ -97,6 +97,7 @@ public abstract class ReferenceSetBase { return myReferences; } + @NotNull public PsiReference[] getPsiReferences() { return myReferences.toArray(new PsiReference[myReferences.size()]); } diff --git a/platform/lang-api/src/com/intellij/semantic/SemKey.java b/platform/lang-api/src/com/intellij/semantic/SemKey.java index 7a0bcf82b20b..844c38def4fa 100644 --- a/platform/lang-api/src/com/intellij/semantic/SemKey.java +++ b/platform/lang-api/src/com/intellij/semantic/SemKey.java @@ -34,7 +34,7 @@ import java.util.concurrent.atomic.AtomicInteger; public class SemKey { private static final AtomicInteger counter = new AtomicInteger(0); private final String myDebugName; - private final SemKey[] mySupers; + @NotNull private final SemKey[] mySupers; private final List myInheritors = ContainerUtil.createEmptyCOWList(); private final int myUniqueId; @@ -54,6 +54,7 @@ public class SemKey { } } + @NotNull public SemKey[] getSupers() { return mySupers; } diff --git a/platform/lang-impl/lang-tests.iml b/platform/lang-impl/lang-tests.iml index 75858013625d..34556a95af19 100644 --- a/platform/lang-impl/lang-tests.iml +++ b/platform/lang-impl/lang-tests.iml @@ -6,7 +6,7 @@ - + diff --git a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java index 4ee8016852f1..b4b9e0ca947a 100644 --- a/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java +++ b/platform/lang-impl/src/com/intellij/application/options/colors/SimpleEditorPreview.java @@ -332,7 +332,7 @@ public class SimpleEditorPreview implements PreviewPanel { public void setupRainbow(@NotNull EditorColorsScheme colorsScheme, @NotNull RainbowColorSettingsPage page) { final List initialMarkup = new ArrayList<>(); myHighlightsExtractor.extractHighlights(page.getDemoText(), initialMarkup); - + colorsScheme.dropGeneratedTextAttributesCache(); final List rainbowMarkup = setupRainbowHighlighting( page, initialMarkup, diff --git a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java index f34eff76f77a..d75d1bbb2583 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightSettings.java @@ -21,6 +21,7 @@ import com.intellij.openapi.components.ServiceManager; import com.intellij.openapi.components.State; import com.intellij.openapi.components.Storage; import com.intellij.openapi.diagnostic.Logger; +import com.intellij.openapi.project.Project; import com.intellij.util.ArrayUtil; import com.intellij.util.xmlb.SkipDefaultValuesSerializationFilters; import com.intellij.util.xmlb.XmlSerializationException; @@ -129,7 +130,11 @@ public class CodeInsightSettings implements PersistentStateComponent, C public boolean HIGHLIGHT_IDENTIFIER_UNDER_CARET = true; + /** + * @deprecated use {@link CodeInsightWorkspaceSettings#optimizeImportsOnTheFly} + */ public boolean OPTIMIZE_IMPORTS_ON_THE_FLY; + public boolean ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; public boolean ADD_MEMBER_IMPORTS_ON_THE_FLY = true; public boolean JSP_ADD_UNAMBIGIOUS_IMPORTS_ON_THE_FLY; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightWorkspaceSettings.java b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightWorkspaceSettings.java new file mode 100644 index 000000000000..20e6c0e1cf35 --- /dev/null +++ b/platform/lang-impl/src/com/intellij/codeInsight/CodeInsightWorkspaceSettings.java @@ -0,0 +1,58 @@ +/* + * 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.codeInsight; + +import com.intellij.openapi.Disposable; +import com.intellij.openapi.components.*; +import com.intellij.openapi.project.Project; +import com.intellij.openapi.util.Disposer; +import com.intellij.util.xmlb.XmlSerializerUtil; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; + +/** + * @author peter + */ +@State(name = "CodeInsightWorkspaceSettings", storages = @Storage(StoragePathMacros.WORKSPACE_FILE)) +public class CodeInsightWorkspaceSettings implements PersistentStateComponent { + public boolean optimizeImportsOnTheFly = CodeInsightSettings.getInstance().OPTIMIZE_IMPORTS_ON_THE_FLY; + + @TestOnly + public void setOptimizeImportsOnTheFly(boolean optimizeImportsOnTheFly, Disposable parentDisposable) { + boolean prev = this.optimizeImportsOnTheFly; + this.optimizeImportsOnTheFly = optimizeImportsOnTheFly; + Disposer.register(parentDisposable, () -> { + this.optimizeImportsOnTheFly = prev; + }); + } + + public static CodeInsightWorkspaceSettings getInstance(@NotNull Project project) { + return ServiceManager.getService(project, CodeInsightWorkspaceSettings.class); + } + + + @Override + public void loadState(CodeInsightWorkspaceSettings state) { + XmlSerializerUtil.copyBean(state, this); + } + + @Nullable + @Override + public CodeInsightWorkspaceSettings getState() { + return this; + } +} diff --git a/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java b/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java index cac49ae29f8e..f2e8685c8f91 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/actions/AbstractLayoutCodeProcessor.java @@ -278,7 +278,7 @@ public abstract class AbstractLayoutCodeProcessor { return new FileTreeIterator(myProject); } - return new FileTreeIterator(Collections.emptyList()); + return new FileTreeIterator(Collections.emptyList()); } @NotNull @@ -355,7 +355,7 @@ public abstract class AbstractLayoutCodeProcessor { LOG.error(e); } }; - runLayoutCodeProcess(readAction, writeAction, false ); + runLayoutCodeProcess(readAction, writeAction); } private boolean checkFileWritable(final PsiFile file){ @@ -396,7 +396,7 @@ public abstract class AbstractLayoutCodeProcessor { return !GeneratedSourcesFilter.isGeneratedSourceByAnyFilter(virtualFile, file.getProject()); } - private void runLayoutCodeProcess(final Runnable readAction, final Runnable writeAction, final boolean globalAction) { + private void runLayoutCodeProcess(final Runnable readAction, final Runnable writeAction) { final ProgressWindow progressWindow = new ProgressWindow(true, myProject); progressWindow.setTitle(myCommandName); progressWindow.setText(myProgressText); @@ -418,7 +418,6 @@ public abstract class AbstractLayoutCodeProcessor { } final Runnable writeRunnable = () -> CommandProcessor.getInstance().executeCommand(myProject, () -> { - if (globalAction) CommandProcessor.getInstance().markCurrentCommandAsGlobal(myProject); try { writeAction.run(); @@ -544,18 +543,15 @@ public abstract class AbstractLayoutCodeProcessor { } private void performFileProcessing(@NotNull PsiFile file) { - myProcessors.stream().forEach((processor) -> { - Ref> writeTaskRef = Ref.create(); - - ApplicationManager.getApplication().runReadAction(() -> writeTaskRef.set(processor.prepareTask(file, myProcessChangedTextOnly))); + for (AbstractLayoutCodeProcessor processor : myProcessors) { + FutureTask writeTask = ReadAction.compute(() -> processor.prepareTask(file, myProcessChangedTextOnly)); ProgressIndicatorProvider.checkCanceled(); - FutureTask writeTask = writeTaskRef.get(); - + ApplicationManager.getApplication().invokeAndWait(() -> WriteCommandAction.runWriteCommandAction(myProject, myCommandName, null, writeTask)); checkStop(writeTask, file); - }); + } } private void checkStop(FutureTask task, PsiFile file) { @@ -565,6 +561,11 @@ public abstract class AbstractLayoutCodeProcessor { } } catch (InterruptedException | ExecutionException e) { + Throwable cause = e.getCause(); + if (cause instanceof IndexNotReadyException) { + LOG.warn(cause); + return; + } LOG.error("Got unexpected exception during formatting " + file, e); } } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java index 15d6833ab4e7..880aa5179371 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CodeCompletionHandlerBase.java @@ -303,10 +303,8 @@ public class CodeCompletionHandlerBase { CompletionServiceImpl.assertPhase(CompletionPhase.NoCompletion.getClass()); } - final Semaphore freezeSemaphore = new Semaphore(); - freezeSemaphore.down(); final CompletionProgressIndicator indicator = new CompletionProgressIndicator(editor, initContext.getCaret(), - parameters, this, freezeSemaphore, + parameters, this, initContext.getOffsetMap(), hostOffsets, hasModifiers, lookup); Disposer.register(indicator, hostCopyOffsets.getOffsets()); Disposer.register(indicator, context.getOffsetMap()); @@ -320,20 +318,18 @@ public class CodeCompletionHandlerBase { return; } - if (freezeSemaphore.waitFor(ourAutoInsertItemTimeout)) { - if (!indicator.isRunning() && !indicator.isCanceled()) { // the completion is really finished, now we may auto-insert or show lookup - try { - indicator.getLookup().refreshUi(true, false); - } - catch (Exception e) { - CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); - LOG.error(e); - return; - } - - completionFinished(indicator, hasModifiers); + if (indicator.blockingWaitForFinish(ourAutoInsertItemTimeout)) { + try { + indicator.getLookup().refreshUi(true, false); + } + catch (Exception e) { + CompletionServiceImpl.setCompletionPhase(CompletionPhase.NoCompletion); + LOG.error(e); return; } + + completionFinished(indicator, hasModifiers); + return; } CompletionServiceImpl.setCompletionPhase(new CompletionPhase.BgCalculation(indicator)); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java index 63903e022e72..393305d6c186 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/completion/CompletionProgressIndicator.java @@ -107,7 +107,8 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement myQueue.setMergingTimeSpan(ourShowPopupGroupingTime); } }; - private final Semaphore myFreezeSemaphore; + private final Semaphore myFreezeSemaphore = new Semaphore(1); + private final Semaphore myFinishSemaphore = new Semaphore(1); private final OffsetMap myOffsetMap; private final Set>> myRestartingPrefixConditions = ContainerUtil.newConcurrentSet(); private final LookupAdapter myLookupListener = new LookupAdapter() { @@ -139,7 +140,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement @NotNull Caret caret, CompletionParameters parameters, CodeCompletionHandlerBase handler, - Semaphore freezeSemaphore, final OffsetMap offsetMap, OffsetsInFile hostOffsets, boolean hasModifiers, @@ -148,7 +148,6 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement myCaret = caret; myParameters = parameters; myHandler = handler; - myFreezeSemaphore = freezeSemaphore; myOffsetMap = offsetMap; myHostOffsets = hostOffsets; myLookup = lookup; @@ -514,12 +513,27 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement StatisticsUpdate.cancelLastCompletionStatisticsUpdate(); } + boolean blockingWaitForFinish(int timeoutMs) { + if (ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup) { + if (!myFinishSemaphore.waitFor(100 * 1000)) { + throw new AssertionError("Too long completion"); + } + return true; + } + if (myFreezeSemaphore.waitFor(timeoutMs)) { + // the completion is really finished, now we may auto-insert or show lookup + return !isRunning() && !isCanceled(); + } + return false; + } + @Override public void stop() { super.stop(); myQueue.cancelAllUpdates(); myFreezeSemaphore.up(); + myFinishSemaphore.up(); GuiUtils.invokeLaterIfNeeded(() -> { final CompletionPhase phase = CompletionServiceImpl.getCompletionPhase(); @@ -773,7 +787,7 @@ public class CompletionProgressIndicator extends ProgressIndicatorBase implement void startCompletion(final CompletionInitializationContext initContext) { - boolean sync = ApplicationManager.getApplication().isUnitTestMode() && !CompletionAutoPopupHandler.ourTestingAutopopup; + boolean sync = ApplicationManager.getApplication().isWriteAccessAllowed(); myStrategy = sync ? new SyncCompletion() : new AsyncCompletion(); myStrategy.startThread(ProgressWrapper.wrap(this), this::scheduleAdvertising); final WeighingDelegate weigher = myStrategy.delegateWeighing(this); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowBuilder.java b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowBuilder.java index ac8e8702ae56..4fca34e3c40f 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowBuilder.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/ControlFlowBuilder.java @@ -204,6 +204,7 @@ public class ControlFlowBuilder { } + @FunctionalInterface public interface PendingProcessor { void process(PsiElement pendingScope, Instruction instruction); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/impl/ControlFlowImpl.java b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/impl/ControlFlowImpl.java index eccf9469e275..636e6548a2a2 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/controlflow/impl/ControlFlowImpl.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/controlflow/impl/ControlFlowImpl.java @@ -17,6 +17,7 @@ package com.intellij.codeInsight.controlflow.impl; import com.intellij.codeInsight.controlflow.ControlFlow; import com.intellij.codeInsight.controlflow.Instruction; +import org.jetbrains.annotations.NotNull; /** * @author oleg @@ -24,10 +25,11 @@ import com.intellij.codeInsight.controlflow.Instruction; public class ControlFlowImpl implements ControlFlow { private final Instruction[] myInstructions; - public ControlFlowImpl(Instruction[] instructions) { + public ControlFlowImpl(@NotNull Instruction[] instructions) { myInstructions = instructions; } + @NotNull @Override public Instruction[] getInstructions() { return myInstructions; diff --git a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java index 5a5a9ec1244d..9c8d70507b95 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/daemon/impl/WholeFileLocalInspectionsPassFactory.java @@ -21,6 +21,7 @@ import com.intellij.codeHighlighting.TextEditorHighlightingPass; import com.intellij.codeHighlighting.TextEditorHighlightingPassFactory; import com.intellij.codeHighlighting.TextEditorHighlightingPassRegistrar; import com.intellij.codeInsight.daemon.DaemonBundle; +import com.intellij.codeInsight.daemon.ProblemHighlightFilter; import com.intellij.codeInspection.InspectionManager; import com.intellij.codeInspection.InspectionProfile; import com.intellij.codeInspection.ex.InspectionProfileWrapper; @@ -93,6 +94,10 @@ public class WholeFileLocalInspectionsPassFactory extends AbstractProjectCompone return null; //optimization } + if (!ProblemHighlightFilter.shouldHighlightFile(file)) { + return null; + } + if (myFileToolsCache.containsKey(file) && !myFileToolsCache.get(file)) { return null; } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/JoinLinesHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/JoinLinesHandler.java index 9c92e968866c..28712aaf5af7 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/JoinLinesHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/JoinLinesHandler.java @@ -210,8 +210,8 @@ public class JoinLinesHandler extends EditorActionHandler { return; } - - if (caretRestoreOffset.get() == CANNOT_JOIN) caretRestoreOffset.set(start == offsets.lineEndOffset ? start : start + 1); + int replaceStart = start == offsets.lineEndOffset ? start : start + 1; + if (caretRestoreOffset.get() == CANNOT_JOIN) caretRestoreOffset.set(replaceStart); if (offsets.isStartLineEndsWithComment && offsets.isNextLineStartsWithComment) { @@ -224,12 +224,18 @@ public class JoinLinesHandler extends EditorActionHandler { while (end < doc.getTextLength() && (text.charAt(end) == ' ' || text.charAt(end) == '\t')) end++; } - doc.replaceString(start == offsets.lineEndOffset ? start : start + 1, end, " "); + doc.replaceString(replaceStart, end, " "); return; } while (end < doc.getTextLength() && (text.charAt(end) == ' ' || text.charAt(end) == '\t')) end++; - doc.replaceString(start == offsets.lineEndOffset ? start : start + 1, end, " "); + + int spacesToCreate = CodeStyleManager.getInstance(project).getSpacing(psiFile, end); + if (spacesToCreate < 0) spacesToCreate = 1; + String spacing = StringUtil.repeatSymbol(' ', spacesToCreate); + + doc.replaceString(replaceStart, end, spacing); + docManager.commitDocument(doc); if (start <= doc.getLineStartOffset(startLine)) { try { @@ -241,29 +247,6 @@ public class JoinLinesHandler extends EditorActionHandler { } } - int prevLineCount = doc.getLineCount(); - - docManager.commitDocument(doc); - try { - CodeStyleManager.getInstance(project).reformatRange(psiFile, start + 1, end, true); - } - catch (IncorrectOperationException e) { - LOG.error(e); - } - - if (prevLineCount < doc.getLineCount()) { - docManager.doPostponedOperationsAndUnblockDocument(doc); - end = doc.getLineEndOffset(startLine) + doc.getLineSeparatorLength(startLine); - start = end - doc.getLineSeparatorLength(startLine); - int addedLinesCount = doc.getLineCount() - prevLineCount - 1; - while (end < doc.getTextLength() && - (text.charAt(end) == ' ' || text.charAt(end) == '\t' || text.charAt(end) == '\n' && addedLinesCount > 0)) { - if (text.charAt(end) == '\n') addedLinesCount--; - end++; - } - doc.replaceString(start, end, " "); - } - docManager.commitDocument(doc); } diff --git a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java index fa67438cfbf8..3d425bd0e30c 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/editorActions/SmartIndentingBackspaceHandler.java @@ -16,10 +16,6 @@ package com.intellij.codeInsight.editorActions; import com.intellij.codeStyle.CodeStyleFacade; -import com.intellij.formatting.FormatterEx; -import com.intellij.formatting.FormattingModel; -import com.intellij.formatting.FormattingModelBuilder; -import com.intellij.lang.LanguageFormatting; import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.CaretModel; import com.intellij.openapi.editor.Document; @@ -29,8 +25,7 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.util.text.StringUtil; import com.intellij.psi.PsiDocumentManager; import com.intellij.psi.PsiFile; -import com.intellij.psi.codeStyle.CodeStyleSettings; -import com.intellij.psi.codeStyle.CodeStyleSettingsManager; +import com.intellij.psi.codeStyle.CodeStyleManager; import com.intellij.util.text.CharArrayUtil; import org.jetbrains.annotations.NotNull; @@ -80,7 +75,8 @@ public class SmartIndentingBackspaceHandler extends AbstractIndentingBackspaceHa int prevLineEndOffset = document.getLineEndOffset(logicalPosition.line - 1); myStartOffset = CharArrayUtil.shiftBackward(charSequence, prevLineEndOffset - 1, " \t") + 1; if (myStartOffset != document.getLineStartOffset(logicalPosition.line - 1)) { - myReplacement = getSpacing(file, endOffset); + int spacing = CodeStyleManager.getInstance(project).getSpacing(file, endOffset); + myReplacement = StringUtil.repeatSymbol(' ', Math.max(0, spacing)); } } } @@ -101,17 +97,6 @@ public class SmartIndentingBackspaceHandler extends AbstractIndentingBackspaceHa return true; } - private static String getSpacing(PsiFile file, int offset) { - FormattingModelBuilder builder = LanguageFormatting.INSTANCE.forContext(file); - if (builder == null) { - return ""; - } - CodeStyleSettings settings = CodeStyleSettingsManager.getSettings(file.getProject()); - FormattingModel model = builder.createModel(file, settings); - int spacing = FormatterEx.getInstance().getSpacingForBlockAtOffset(model, offset); - return StringUtil.repeatSymbol(' ', spacing); - } - private static int getWidth(@NotNull String indent, int tabSize) { int width = 0; for (int i = 0; i < indent.length(); i++) { diff --git a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/PairedBraceMatcherAdapter.java b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/PairedBraceMatcherAdapter.java index d1c385557ce7..3b138085cb07 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/highlighting/PairedBraceMatcherAdapter.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/highlighting/PairedBraceMatcherAdapter.java @@ -39,6 +39,7 @@ public class PairedBraceMatcherAdapter implements NontrivialBraceMatcher, Paired myLanguage = language; } + @NotNull @Override public BracePair[] getPairs() { return myMatcher.getPairs(); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java index e1ff494df347..628fc953959a 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hint/ParameterInfoController.java @@ -28,10 +28,12 @@ import com.intellij.lang.parameterInfo.UpdateParameterInfoContext; import com.intellij.openapi.Disposable; import com.intellij.openapi.application.ApplicationManager; import com.intellij.openapi.application.ModalityState; +import com.intellij.openapi.diagnostic.Logger; import com.intellij.openapi.editor.*; import com.intellij.openapi.editor.event.*; import com.intellij.openapi.editor.impl.EditorImpl; import com.intellij.openapi.project.DumbService; +import com.intellij.openapi.project.IndexNotReadyException; import com.intellij.openapi.project.Project; import com.intellij.openapi.ui.popup.Balloon.Position; import com.intellij.openapi.util.Disposer; @@ -64,6 +66,7 @@ import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.LockSupport; public class ParameterInfoController implements Disposable { + private static final Logger LOG = Logger.getInstance("#com.intellij.codeInsight.hint.ParameterInfoController"); private final Project myProject; @NotNull private final Editor myEditor; @@ -247,9 +250,15 @@ public class ParameterInfoController implements Disposable { private void addAlarmRequest(){ Runnable request = () -> { if (!myDisposed && !myProject.isDisposed()) { - PsiDocumentManager.getInstance(myProject).performLaterWhenAllCommitted(() -> - DumbService.getInstance(myProject).withAlternativeResolveEnabled(this::updateComponent) - ); + PsiDocumentManager.getInstance(myProject).performLaterWhenAllCommitted(() -> { + try { + DumbService.getInstance(myProject).withAlternativeResolveEnabled(this::updateComponent); + } + catch (IndexNotReadyException e) { + LOG.info(e); + Disposer.dispose(this); + } + }); } }; myAlarm.addRequest(request, DELAY, ModalityState.stateForComponent(myEditor.getComponent())); diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt index f7c1e02d3231..a67be8799891 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/PopupActions.kt @@ -19,11 +19,16 @@ import com.intellij.codeInsight.CodeInsightBundle import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer import com.intellij.codeInsight.daemon.impl.ParameterHintsPresentationManager import com.intellij.codeInsight.hints.HintInfo.MethodInfo +import com.intellij.codeInsight.hints.settings.Diff import com.intellij.codeInsight.hints.settings.ParameterNameHintsConfigurable import com.intellij.codeInsight.hints.settings.ParameterNameHintsSettings import com.intellij.codeInsight.intention.HighPriorityAction import com.intellij.codeInsight.intention.IntentionAction import com.intellij.injected.editor.EditorWindow +import com.intellij.lang.Language +import com.intellij.notification.Notification +import com.intellij.notification.NotificationListener +import com.intellij.notification.NotificationType import com.intellij.openapi.actionSystem.ActionPlaces import com.intellij.openapi.actionSystem.AnAction import com.intellij.openapi.actionSystem.AnActionEvent @@ -50,7 +55,7 @@ class ShowSettingsWithAddedPattern : AnAction() { val editor = CommonDataKeys.EDITOR.getData(e.dataContext) ?: return val provider = InlayParameterHintsExtension.forLanguage(file.language) ?: return - if (!provider.isBlackListSupported()) { + if (!provider.isBlackListSupported) { e.presentation.isEnabledAndVisible = false return } @@ -89,7 +94,7 @@ class BlacklistCurrentMethodIntention : IntentionAction, HighPriorityAction { override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { val language = file.language val hintsProvider = InlayParameterHintsExtension.forLanguage(language) ?: return false - return hintsProvider.isBlackListSupported() + return hintsProvider.isBlackListSupported && hasEditorParameterHintAtOffset(editor, file) && isMethodHintAtOffset(editor, file) } @@ -101,7 +106,47 @@ class BlacklistCurrentMethodIntention : IntentionAction, HighPriorityAction { override fun invoke(project: Project, editor: Editor, file: PsiFile) { val offset = editor.caretModel.offset - addMethodAtCaretToBlackList(offset, file) + + val info = getHintInfoFromProvider(offset, file) as? MethodInfo ?: return + ParameterNameHintsSettings.getInstance().addIgnorePattern(file.language, info.toPattern()) + refreshAllOpenEditors() + + showHint(project, file, info) + } + + private fun showHint(project: Project, file: PsiFile, info: MethodInfo) { + val methodName = info.getMethodName() + + val language = file.language + val listener = NotificationListener { notification, event -> + when (event.description) { + "settings" -> showSettings(language) + "undo" -> undo(language, info) + } + } + + val notification = Notification("Parameter Name Hints", "Method \"$methodName\" added to blacklist", + "Show Parameter Hints Settings or Undo", + NotificationType.INFORMATION, listener) + + notification.notify(project) + } + + private fun showSettings(language: Language) { + val dialog = ParameterNameHintsConfigurable(language, null) + dialog.show() + } + + private fun undo(language: Language, info: MethodInfo) { + val settings = ParameterNameHintsSettings.getInstance() + + val diff = settings.getBlackListDiff(language) + val updated = diff.added.toMutableSet().apply { + remove(info.toPattern()) + } + + settings.setBlackListDiff(language, Diff(updated, diff.removed)) + refreshAllOpenEditors() } override fun startInWriteAction() = false @@ -142,41 +187,6 @@ class DisableCustomHintsOption: IntentionAction, HighPriorityAction { } -class EnableCustomHintsOption: IntentionAction, HighPriorityAction { - companion object { - private val presentableFamilyName = CodeInsightBundle.message("inlay.hints.intention.family.name") - } - - private var lastOptionName = "" - - override fun getText(): String = CodeInsightBundle.message("inlay.hints.enable.custom.option", lastOptionName) - override fun getFamilyName(): String = presentableFamilyName - - override fun isAvailable(project: Project, editor: Editor, file: PsiFile): Boolean { - InlayParameterHintsExtension.forLanguage(file.language) ?: return false - if (hasEditorParameterHintAtOffset(editor, file)) return false - - val option = getOptionHintAtOffset(editor, file) ?: return false - lastOptionName = option.optionName - - return true - } - - private fun getOptionHintAtOffset(editor: Editor, file: PsiFile): HintInfo.OptionInfo? { - val offset = editor.caretModel.offset - return getHintInfoFromProvider(offset, file) as? HintInfo.OptionInfo - } - - override fun invoke(project: Project, editor: Editor, file: PsiFile) { - val option = getOptionHintAtOffset(editor, file) ?: return - option.enable() - } - - override fun startInWriteAction() = false -} - - - class ToggleInlineHintsAction : AnAction() { companion object { @@ -257,14 +267,6 @@ private fun getHintInfoFromProvider(offset: Int, file: PsiFile): HintInfo? { return hintsProvider.getHintInfo(method) } - -private fun addMethodAtCaretToBlackList(offset: Int, file: PsiFile) { - val info = getHintInfoFromProvider(offset, file) as? MethodInfo ?: return - ParameterNameHintsSettings.getInstance().addIgnorePattern(file.language, info.toPattern()) - refreshAllOpenEditors() -} - - fun isPossibleHintNearOffset(file: PsiFile, offset: Int): Boolean { val hintProvider = InlayParameterHintsExtension.forLanguage(file.language) ?: return false diff --git a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java index 1b8ca6fa7f54..e0e8657a216b 100644 --- a/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java +++ b/platform/lang-impl/src/com/intellij/codeInsight/hints/settings/ParameterNameHintsConfigurable.java @@ -222,12 +222,21 @@ public class ParameterNameHintsConfigurable extends DialogWrapper { blacklistPanel.setLayout(layout); blacklistPanel.setBorder(IdeBorderFactory.createTitledBorder("Blacklist")); - blacklistPanel.add(new JBLabel(CodeInsightBundle.message("inlay.hints.blacklist.pattern.explanation"))); + blacklistPanel.add(new JBLabel(getBlacklistExplanationHTML(language))); blacklistPanel.add(editorTextField); return blacklistPanel; } + @NotNull + private String getBlacklistExplanationHTML(Language language) { + InlayParameterHintsProvider hintsProvider = InlayParameterHintsExtension.INSTANCE.forLanguage(language); + if (hintsProvider == null) { + return CodeInsightBundle.message("inlay.hints.blacklist.pattern.explanation"); + } + return hintsProvider.getBlacklistExplanationHTML(); + } + @Nullable private JPanel createOptionsPanel(@NotNull Language language) { List