From b423d58a146f6afe94838030a2be9dd5b6163980 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Tue, 1 Aug 2017 19:38:04 +0200 Subject: [PATCH] Load JSR-305 annotations for libraries if it's enabled for any module #KT-19303 Fixed --- .../arguments/K2JVMCompilerArguments.kt | 2 +- .../jetbrains/kotlin/config/AnalysisFlag.kt | 2 + .../compiler/IDELanguageSettingsProvider.kt | 28 +++++++-- .../jetbrains/kotlin/idea/project/Platform.kt | 8 ++- .../highlighterJsr305/library/foo/A.java | 19 ++++++ .../library/foo/FieldsAreNullable.java | 17 ++++++ idea/testData/highlighterJsr305/project/A.kt | 5 ++ .../findUsages/FindUsagesMultiModuleTest.kt | 1 + .../AbstractMultiModuleHighlightingTest.kt | 1 + .../highlighter/Jsr305HighlightingTest.kt | 60 +++++++++++++++++++ .../idea/quickfix/QuickFixMultiModuleTest.kt | 1 + .../idea/stubs/AbstractMultiModuleTest.kt | 32 +++++----- 12 files changed, 153 insertions(+), 23 deletions(-) create mode 100644 idea/testData/highlighterJsr305/library/foo/A.java create mode 100644 idea/testData/highlighterJsr305/library/foo/FieldsAreNullable.java create mode 100644 idea/testData/highlighterJsr305/project/A.kt create mode 100644 idea/tests/org/jetbrains/kotlin/idea/highlighter/Jsr305HighlightingTest.kt diff --git a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt index c1db8b3d5c7..d474475295a 100644 --- a/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt +++ b/compiler/cli/cli-common/src/org/jetbrains/kotlin/cli/common/arguments/K2JVMCompilerArguments.kt @@ -166,7 +166,7 @@ class K2JVMCompilerArguments : CommonCompilerArguments() { override fun configureAnalysisFlags(): MutableMap, Any> { val result = super.configureAnalysisFlags() - Jsr305State.values().firstOrNull { it.description == jsr305GlobalReportLevel }?.let { + Jsr305State.findByDescription(jsr305GlobalReportLevel)?.let { result.put(AnalysisFlag.loadJsr305Annotations, it) } return result diff --git a/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt b/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt index 288bfc67bfc..e1e7b428779 100644 --- a/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt +++ b/compiler/util/src/org/jetbrains/kotlin/config/AnalysisFlag.kt @@ -68,5 +68,7 @@ enum class Jsr305State( companion object { @JvmField val DEFAULT: Jsr305State = IGNORE + + fun findByDescription(description: String?) = values().firstOrNull { it.description == description } } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt index 58ddd5ad940..f2ae4d1abf1 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/compiler/IDELanguageSettingsProvider.kt @@ -16,19 +16,39 @@ package org.jetbrains.kotlin.idea.compiler +import com.intellij.openapi.module.ModuleManager import com.intellij.openapi.project.Project import org.jetbrains.kotlin.analyzer.LanguageSettingsProvider import org.jetbrains.kotlin.analyzer.ModuleInfo -import org.jetbrains.kotlin.config.LanguageVersionSettings -import org.jetbrains.kotlin.config.TargetPlatformVersion +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.config.* +import org.jetbrains.kotlin.idea.caches.resolve.LibraryInfo import org.jetbrains.kotlin.idea.caches.resolve.ModuleSourceInfo import org.jetbrains.kotlin.idea.project.getLanguageVersionSettings import org.jetbrains.kotlin.idea.project.languageVersionSettings import org.jetbrains.kotlin.idea.project.targetPlatform object IDELanguageSettingsProvider : LanguageSettingsProvider { - override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings { - return (moduleInfo as? ModuleSourceInfo)?.module?.languageVersionSettings ?: project.getLanguageVersionSettings() + override fun getLanguageVersionSettings(moduleInfo: ModuleInfo, project: Project): LanguageVersionSettings = + when (moduleInfo) { + is ModuleSourceInfo -> moduleInfo.module.languageVersionSettings + is LibraryInfo -> project.getLanguageVersionSettings(extraAnalysisFlags = getExtraAnalysisFlags(project)) + else -> project.getLanguageVersionSettings() + } + + private fun getExtraAnalysisFlags(project: Project): Map, Any?> { + val map = mutableMapOf, Any>() + for (module in ModuleManager.getInstance(project).modules) { + val settings = KotlinFacetSettingsProvider.getInstance(project).getSettings(module) ?: continue + val compilerArguments = settings.compilerArguments as? K2JVMCompilerArguments ?: continue + + val jsr305state = Jsr305State.findByDescription(compilerArguments.jsr305GlobalReportLevel) + if (jsr305state != null && jsr305state != Jsr305State.IGNORE) { + map.put(AnalysisFlag.loadJsr305Annotations, jsr305state) + break + } + } + return map } override fun getTargetPlatform(moduleInfo: ModuleInfo): TargetPlatformVersion { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt index 1d039c4752d..feac82957f3 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/project/Platform.kt @@ -74,7 +74,9 @@ fun Module.getAndCacheLanguageLevelByDependencies(): LanguageVersion { return languageLevel } -fun Project.getLanguageVersionSettings(contextModule: Module? = null): LanguageVersionSettings { +@JvmOverloads +fun Project.getLanguageVersionSettings(contextModule: Module? = null, + extraAnalysisFlags: Map, Any?> = emptyMap()): LanguageVersionSettings { val arguments = KotlinCommonCompilerArgumentsHolder.getInstance(this).settings val languageVersion = LanguageVersion.fromVersionString(arguments.languageVersion) @@ -88,7 +90,9 @@ fun Project.getLanguageVersionSettings(contextModule: Module? = null): LanguageV compilerSettings, null ) - return LanguageVersionSettingsImpl(languageVersion, apiVersion, arguments.configureAnalysisFlags(), extraLanguageFeatures) + return LanguageVersionSettingsImpl(languageVersion, apiVersion, + arguments.configureAnalysisFlags() + extraAnalysisFlags, + extraLanguageFeatures) } val Module.languageVersionSettings: LanguageVersionSettings diff --git a/idea/testData/highlighterJsr305/library/foo/A.java b/idea/testData/highlighterJsr305/library/foo/A.java new file mode 100644 index 00000000000..16d1934658c --- /dev/null +++ b/idea/testData/highlighterJsr305/library/foo/A.java @@ -0,0 +1,19 @@ +package foo; + +import javax.annotation.*; + +@FieldsAreNullable +public class A { + public String field = null; + @Nonnull + public String nonNullField = ""; + + public String foo(String q, @Nonnull String x, @CheckForNull CharSequence y) { + return ""; + } + + @Nonnull + public String bar() { + return ""; + } +} diff --git a/idea/testData/highlighterJsr305/library/foo/FieldsAreNullable.java b/idea/testData/highlighterJsr305/library/foo/FieldsAreNullable.java new file mode 100644 index 00000000000..8a59c7309b2 --- /dev/null +++ b/idea/testData/highlighterJsr305/library/foo/FieldsAreNullable.java @@ -0,0 +1,17 @@ +package foo; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; +import javax.annotation.Nonnull; +import javax.annotation.CheckForNull; +import javax.annotation.meta.TypeQualifierDefault; + +@Retention(RetentionPolicy.RUNTIME) +@Documented +@CheckForNull +@TypeQualifierDefault({ElementType.FIELD}) +public @interface FieldsAreNullable { +} diff --git a/idea/testData/highlighterJsr305/project/A.kt b/idea/testData/highlighterJsr305/project/A.kt new file mode 100644 index 00000000000..9cf4a4c8478 --- /dev/null +++ b/idea/testData/highlighterJsr305/project/A.kt @@ -0,0 +1,5 @@ +import foo.A + +fun main(a: A) { + a.field.length +} diff --git a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesMultiModuleTest.kt b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesMultiModuleTest.kt index ebea1771b83..1c6a6e60d82 100644 --- a/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesMultiModuleTest.kt +++ b/idea/tests/org/jetbrains/kotlin/findUsages/FindUsagesMultiModuleTest.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.findUsages import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.idea.stubs.createFacet import org.junit.Test class FindUsagesMultiModuleTest : AbstractFindUsagesMultiModuleTest() { diff --git a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt index 440f6300157..0f727fe9bc1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/caches/resolve/AbstractMultiModuleHighlightingTest.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime import org.jetbrains.kotlin.config.TargetPlatformKind import org.jetbrains.kotlin.idea.framework.CommonLibraryKind import org.jetbrains.kotlin.idea.stubs.AbstractMultiHighlightingTest +import org.jetbrains.kotlin.idea.stubs.createFacet import org.jetbrains.kotlin.test.TestJdkKind abstract class AbstractMultiModuleHighlightingTest : AbstractMultiHighlightingTest() { diff --git a/idea/tests/org/jetbrains/kotlin/idea/highlighter/Jsr305HighlightingTest.kt b/idea/tests/org/jetbrains/kotlin/idea/highlighter/Jsr305HighlightingTest.kt new file mode 100644 index 00000000000..e81839abe6c --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/highlighter/Jsr305HighlightingTest.kt @@ -0,0 +1,60 @@ +/* + * Copyright 2010-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.kotlin.idea.highlighter + +import com.intellij.openapi.module.Module +import com.intellij.openapi.roots.ModifiableRootModel +import com.intellij.testFramework.LightProjectDescriptor +import org.jetbrains.kotlin.cli.common.arguments.K2JVMCompilerArguments +import org.jetbrains.kotlin.codegen.forTestCompile.ForTestCompileRuntime +import org.jetbrains.kotlin.config.Jsr305State +import org.jetbrains.kotlin.config.JvmTarget +import org.jetbrains.kotlin.config.KotlinFacetSettingsProvider +import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.idea.stubs.createFacet +import org.jetbrains.kotlin.idea.test.KotlinJdkAndLibraryProjectDescriptor +import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase +import org.jetbrains.kotlin.test.MockLibraryUtil + +class Jsr305HighlightingTest : KotlinLightCodeInsightFixtureTestCase() { + override fun getProjectDescriptor(): LightProjectDescriptor { + val foreignAnnotationsJar = MockLibraryUtil.compileJvmLibraryToJar("compiler/testData/foreignAnnotations/annotations", "foreign-annotations") + val libraryJar = MockLibraryUtil.compileJvmLibraryToJar("idea/testData/highlighterJsr305/library", "jsr305-library", + extraClasspath = listOf(foreignAnnotationsJar.absolutePath)) + return object : KotlinJdkAndLibraryProjectDescriptor( + listOf( + ForTestCompileRuntime.runtimeJarForTests(), + foreignAnnotationsJar, + libraryJar + ) + ) { + override fun configureModule(module: Module, model: ModifiableRootModel) { + super.configureModule(module, model) + module.createFacet(TargetPlatformKind.Jvm(JvmTarget.JVM_1_8)) + val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getInitializedSettings(module) + (facetSettings.compilerArguments as K2JVMCompilerArguments).jsr305GlobalReportLevel = Jsr305State.ENABLE.description + } + } + } + + fun testSimple() { + myFixture.configureByFile("A.kt") + myFixture.checkHighlighting() + } + + override fun getTestDataPath() = "idea/testData/highlighterJsr305/project" +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt index 3815bf138a9..182194df024 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/quickfix/QuickFixMultiModuleTest.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.quickfix import org.jetbrains.kotlin.config.JvmTarget import org.jetbrains.kotlin.config.TargetPlatformKind +import org.jetbrains.kotlin.idea.stubs.createFacet import org.junit.Test class QuickFixMultiModuleTest : AbstractQuickFixMultiModuleTest() { diff --git a/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt b/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt index 322792b79d3..cdd2d5cd625 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/stubs/AbstractMultiModuleTest.kt @@ -96,22 +96,6 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() { }, this, kind) } - protected fun Module.createFacet(platformKind: TargetPlatformKind<*>? = null) { - val accessToken = WriteAction.start() - try { - val modelsProvider = IdeModifiableModelsProviderImpl(project) - getOrCreateFacet(modelsProvider, true).configuration.settings.initializeIfNeeded( - this, - modelsProvider.getModifiableRootModel(this), - platformKind - ) - modelsProvider.commit() - } - finally { - accessToken.finish() - } - } - protected fun Module.enableMultiPlatform() { createFacet() val facetSettings = KotlinFacetSettingsProvider.getInstance(project).getInitializedSettings(this) @@ -132,3 +116,19 @@ abstract class AbstractMultiModuleTest : DaemonAnalyzerTestCase() { Assert.assertTrue(atLeastOneFile) } } + +fun Module.createFacet(platformKind: TargetPlatformKind<*>? = null) { + val accessToken = WriteAction.start() + try { + val modelsProvider = IdeModifiableModelsProviderImpl(project) + getOrCreateFacet(modelsProvider, true).configuration.settings.initializeIfNeeded( + this, + modelsProvider.getModifiableRootModel(this), + platformKind + ) + modelsProvider.commit() + } + finally { + accessToken.finish() + } +}