diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheService.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheService.kt index 8f32f0d21cc..83e62a7d73d 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheService.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/caches/resolve/KotlinCacheService.kt @@ -20,36 +20,44 @@ import com.intellij.openapi.components.ServiceManager import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ProjectRootModificationTracker +import com.intellij.psi.util.CachedValue import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.PsiModificationTracker import com.intellij.util.containers.SLRUCache import org.jetbrains.kotlin.analyzer.EmptyResolverForProject import org.jetbrains.kotlin.container.getService +import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor import org.jetbrains.kotlin.idea.project.AnalyzerFacadeProvider import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.js.resolve.JsPlatform +import org.jetbrains.kotlin.psi.KtAnnotated import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.resolve.diagnostics.KotlinSuppressCache import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.utils.keysToMap -internal val LOG = Logger.getInstance(javaClass()) +internal val LOG = Logger.getInstance(KotlinCacheService::class.java) public class KotlinCacheService(val project: Project) { companion object { @JvmStatic - public fun getInstance(project: Project): KotlinCacheService = ServiceManager.getService(project, javaClass())!! + public fun getInstance(project: Project): KotlinCacheService = ServiceManager.getService(project, KotlinCacheService::class.java)!! } public fun getResolutionFacade(elements: List): ResolutionFacade { return getFacadeToAnalyzeFiles(elements.map { it.getContainingKtFile() }) } + public fun getSuppressionCache(): KotlinSuppressCache = kotlinSuppressCache.value + private val globalFacadesPerPlatform = listOf(JvmPlatform, JsPlatform).keysToMap { platform -> GlobalFacade(platform) } private inner class GlobalFacade(platform: TargetPlatform) { @@ -147,6 +155,22 @@ public class KotlinCacheService(val project: Project) { } } + private val kotlinSuppressCache: CachedValue = CachedValuesManager.getManager(project).createCachedValue({ + CachedValueProvider.Result(object : KotlinSuppressCache() { + override fun getSuppressionAnnotations(annotated: KtAnnotated): List { + val context = annotated.analyze(BodyResolveMode.PARTIAL) + val annotatedDescriptor = context.get(BindingContext.DECLARATION_TO_DESCRIPTOR, annotated) + + if (annotatedDescriptor != null) { + return annotatedDescriptor.annotations.toList() + } + else { + return annotated.annotationEntries.map { context.get(BindingContext.ANNOTATION, it) }.filterNotNull() + } + } + }, LibraryModificationTracker.getInstance(project), PsiModificationTracker.MODIFICATION_COUNT) + }, false) + private val syntheticFileCachesLock = Any() private val slruCacheProvider = CachedValueProvider { diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractKotlinInspection.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractKotlinInspection.kt index 74e8b4166c2..9427ebf61e0 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractKotlinInspection.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/inspections/AbstractKotlinInspection.kt @@ -16,12 +16,15 @@ package org.jetbrains.kotlin.idea.inspections -import com.intellij.codeInspection.LocalInspectionTool -import com.intellij.codeInspection.CustomSuppressableInspectionTool -import com.intellij.psi.PsiElement +import com.intellij.codeHighlighting.HighlightDisplayLevel import com.intellij.codeInsight.daemon.HighlightDisplayKey +import com.intellij.codeInspection.CustomSuppressableInspectionTool +import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeInspection.SuppressIntentionAction import com.intellij.codeInspection.SuppressManager +import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.diagnostics.Severity +import org.jetbrains.kotlin.idea.caches.resolve.KotlinCacheService public abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSuppressableInspectionTool { public override fun getSuppressActions(element: PsiElement?): Array? { @@ -29,6 +32,30 @@ public abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSup } public override fun isSuppressedFor(element: PsiElement): Boolean { - return SuppressManager.getInstance()!!.isSuppressedFor(element, getID()) + if (SuppressManager.getInstance()!!.isSuppressedFor(element, getID())) { + return true + } + + val project = element.project + if (KotlinCacheService.getInstance(project).getSuppressionCache().isSuppressed(element, this.shortName, toSeverity(defaultLevel))) { + return true + } + + return false + } + + public fun toSeverity(highlightDisplayLevel: HighlightDisplayLevel): Severity { + return when (highlightDisplayLevel) { + HighlightDisplayLevel.DO_NOT_SHOW -> Severity.INFO + + HighlightDisplayLevel.WARNING, + HighlightDisplayLevel.WEAK_WARNING -> Severity.WARNING + + HighlightDisplayLevel.ERROR, + HighlightDisplayLevel.GENERIC_SERVER_ERROR_OR_WARNING, + HighlightDisplayLevel.NON_SWITCHABLE_ERROR -> Severity.ERROR + + else -> Severity.ERROR + } } } diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/SelfTargetingIntention.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/SelfTargetingIntention.kt index 779016c26d0..ba11a1e7d60 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/SelfTargetingIntention.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/intentions/SelfTargetingIntention.kt @@ -91,7 +91,9 @@ public abstract class SelfTargetingIntention( val inspection = findInspection(javaClass) ?: return false val key = HighlightDisplayKey.find(inspection.shortName) - if (!InspectionProjectProfileManager.getInstance(project).getInspectionProfile(target).isToolEnabled(key)) return false + if (!InspectionProjectProfileManager.getInstance(project).getInspectionProfile(target).isToolEnabled(key)) { + return false + } return inspection.intentions.single { it.intention.javaClass == javaClass }.additionalChecker(target) } diff --git a/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt b/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt index e711c7d8f5a..a3066368820 100644 --- a/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt +++ b/idea/testData/inspections/unusedSymbol/function/classMemberUnused.kt @@ -1,8 +1,21 @@ class Klass { - fun unused() { + fun unusedFun() { + } + + @Suppress("UnusedSymbol") + fun unusedNoWarn() { + + } +} + +@Suppress("UnusedSymbol") +class OtherKlass { + fun unusedNoWarn() { + } } fun main(args: Array) { Klass() + OtherKlass() } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml b/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml index 6c57dc3abd3..d9b81a1b9a7 100644 --- a/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml +++ b/idea/testData/inspections/unusedSymbol/function/inspectionData/expected.xml @@ -10,7 +10,7 @@ topLevelOnlyInImport.kt - 5 + 6 light_idea_test_case Unused Symbol diff --git a/idea/testData/inspections/unusedSymbol/function/local.kt b/idea/testData/inspections/unusedSymbol/function/local.kt index 007aa90f7ec..45b7ac4b41b 100644 --- a/idea/testData/inspections/unusedSymbol/function/local.kt +++ b/idea/testData/inspections/unusedSymbol/function/local.kt @@ -2,4 +2,16 @@ fun outer() { fun local() { } + + @Suppress("UnusedSymbol") + fun localNoWarn() { + + } } + +@Suppress("UnusedSymbol") +fun otherFun() { + fun localNoWarn() { + + } +} \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInClass.kt b/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInClass.kt index 7f03e71a59b..ceadfc3832b 100644 --- a/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInClass.kt +++ b/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInClass.kt @@ -3,6 +3,9 @@ class Klass { fun f() { } + @Suppress("UnusedSymbol") + fun fNoWarn() {} + val p = 5 } @@ -16,9 +19,21 @@ class Klass { fun f() { } + @Suppress("UnusedSymbol") + fun fNoWarn() {} + val p = 5 } + fun localObject3() = @Suppress("UnusedSymbol") object { + fun fNoWarn() {} + } + + @Suppress("UnusedSymbol") + private val localObject4 = object { + fun fNoWarn() {} + } + init { localObject2().f() localObject2().p @@ -39,5 +54,5 @@ class Klass { } fun main(args: Array) { - Klass() + Klass().localObject3() } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInFunction.kt b/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInFunction.kt index b80a4278ae2..77836b735bc 100644 --- a/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInFunction.kt +++ b/idea/testData/inspections/unusedSymbol/function/membersOfAnonymousInFunction.kt @@ -14,9 +14,17 @@ fun main(args: Array) { fun f() { } + @Suppress("UnusedSymbol") + fun fNoWarn() {} + val p = 5 } + @Suppress("UnusedSymbol") + fun localObject3() = object { + fun fNoWarn() {} + } + localObject2().f() localObject2().p diff --git a/idea/testData/inspections/unusedSymbol/function/membersOfLocalClass.kt b/idea/testData/inspections/unusedSymbol/function/membersOfLocalClass.kt index e7bf73b1be2..3920917319c 100644 --- a/idea/testData/inspections/unusedSymbol/function/membersOfLocalClass.kt +++ b/idea/testData/inspections/unusedSymbol/function/membersOfLocalClass.kt @@ -3,10 +3,25 @@ fun main(args: Array) { fun f() { } + @Suppress("UnusedSymbol") + fun fNoWarn() {} + val p = 5 } + @Suppress("UnusedSymbol") + class OtherClass { + fun fNoWarn() {} + } + LocalClass().f() LocalClass().p +} + +@Suppress("UnusedSymbol") +fun other() { + class OtherClass { + fun fNoWarn() {} + } } \ No newline at end of file diff --git a/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt b/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt index 4171bd3641f..471dfab3c44 100644 --- a/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt +++ b/idea/testData/inspections/unusedSymbol/function/topLevelOnlyInImport.kt @@ -1,7 +1,11 @@ package foo import foo.onlyInImport +import foo.onlyInImportNoWarn fun onlyInImport() { } + +@Suppress("UnusedSymbol") +fun onlyInImportNoWarn() {} diff --git a/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt b/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt index d7c611df34c..5b27441dd41 100644 --- a/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt +++ b/idea/testData/inspections/unusedSymbol/function/topLevelUnused.kt @@ -1,3 +1,6 @@ -fun unused() { +fun unusedFun() { } + +@Suppress("UnusedSymbol") +fun unusedNoWarn() {} diff --git a/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt b/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt index 87243a9dc18..e0f2565edd1 100644 --- a/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt +++ b/idea/testData/inspections/unusedSymbol/function/usedOnlyRecursively.kt @@ -1,3 +1,8 @@ fun usedOnlyRecursively() { usedOnlyRecursively() +} + +@Suppress("UnusedSymbol") +fun usedOnlyRecursivelyNoWarn() { + usedOnlyRecursivelyNoWarn() } \ No newline at end of file diff --git a/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt b/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt index 934eda5c428..892d89964a8 100644 --- a/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt +++ b/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt @@ -7,3 +7,14 @@ fun test() { i } } + +fun withSuppression() { + class Test{ + operator fun get(a: Int, b: Int, fn: (i: Int) -> Int) : Int = 0 + } + + val test = Test() + + @Suppress("ReplaceGetOrSet") + test.get(1, 2) { i -> i } +} diff --git a/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt.after b/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt.after index 37257daadf1..f696421ed28 100644 --- a/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt.after +++ b/idea/testData/intentions/conventionNameCalls/replaceGetOrSet/argumentAndFunction.kt.after @@ -7,3 +7,14 @@ fun test() { i }] } + +fun withSuppression() { + class Test{ + operator fun get(a: Int, b: Int, fn: (i: Int) -> Int) : Int = 0 + } + + val test = Test() + + @Suppress("ReplaceGetOrSet") + test.get(1, 2) { i -> i } +}