Allow to suppress kotlin inspections with Suppress() annotation

This commit is contained in:
Nikolay Krasko
2015-12-11 20:13:19 +03:00
committed by Nikolay Krasko
parent ef265e23f8
commit 36210f0a27
14 changed files with 161 additions and 11 deletions
@@ -20,36 +20,44 @@ import com.intellij.openapi.components.ServiceManager
import com.intellij.openapi.diagnostic.Logger import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.roots.ProjectRootModificationTracker import com.intellij.openapi.roots.ProjectRootModificationTracker
import com.intellij.psi.util.CachedValue
import com.intellij.psi.util.CachedValueProvider import com.intellij.psi.util.CachedValueProvider
import com.intellij.psi.util.CachedValuesManager import com.intellij.psi.util.CachedValuesManager
import com.intellij.psi.util.PsiModificationTracker import com.intellij.psi.util.PsiModificationTracker
import com.intellij.util.containers.SLRUCache import com.intellij.util.containers.SLRUCache
import org.jetbrains.kotlin.analyzer.EmptyResolverForProject import org.jetbrains.kotlin.analyzer.EmptyResolverForProject
import org.jetbrains.kotlin.container.getService 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.AnalyzerFacadeProvider
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector import org.jetbrains.kotlin.idea.project.TargetPlatformDetector
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
import org.jetbrains.kotlin.idea.util.ProjectRootsUtil import org.jetbrains.kotlin.idea.util.ProjectRootsUtil
import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.psi.KtAnnotated
import org.jetbrains.kotlin.psi.KtCodeFragment import org.jetbrains.kotlin.psi.KtCodeFragment
import org.jetbrains.kotlin.psi.KtElement import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtFile
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.TargetPlatform 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.jvm.platform.JvmPlatform
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.keysToMap import org.jetbrains.kotlin.utils.keysToMap
internal val LOG = Logger.getInstance(javaClass<KotlinCacheService>()) internal val LOG = Logger.getInstance(KotlinCacheService::class.java)
public class KotlinCacheService(val project: Project) { public class KotlinCacheService(val project: Project) {
companion object { companion object {
@JvmStatic @JvmStatic
public fun getInstance(project: Project): KotlinCacheService = ServiceManager.getService(project, javaClass<KotlinCacheService>())!! public fun getInstance(project: Project): KotlinCacheService = ServiceManager.getService(project, KotlinCacheService::class.java)!!
} }
public fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade { public fun getResolutionFacade(elements: List<KtElement>): ResolutionFacade {
return getFacadeToAnalyzeFiles(elements.map { it.getContainingKtFile() }) return getFacadeToAnalyzeFiles(elements.map { it.getContainingKtFile() })
} }
public fun getSuppressionCache(): KotlinSuppressCache = kotlinSuppressCache.value
private val globalFacadesPerPlatform = listOf(JvmPlatform, JsPlatform).keysToMap { platform -> GlobalFacade(platform) } private val globalFacadesPerPlatform = listOf(JvmPlatform, JsPlatform).keysToMap { platform -> GlobalFacade(platform) }
private inner class GlobalFacade(platform: TargetPlatform) { private inner class GlobalFacade(platform: TargetPlatform) {
@@ -147,6 +155,22 @@ public class KotlinCacheService(val project: Project) {
} }
} }
private val kotlinSuppressCache: CachedValue<KotlinSuppressCache> = CachedValuesManager.getManager(project).createCachedValue({
CachedValueProvider.Result<KotlinSuppressCache>(object : KotlinSuppressCache() {
override fun getSuppressionAnnotations(annotated: KtAnnotated): List<AnnotationDescriptor> {
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 syntheticFileCachesLock = Any()
private val slruCacheProvider = CachedValueProvider { private val slruCacheProvider = CachedValueProvider {
@@ -16,12 +16,15 @@
package org.jetbrains.kotlin.idea.inspections package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.LocalInspectionTool import com.intellij.codeHighlighting.HighlightDisplayLevel
import com.intellij.codeInspection.CustomSuppressableInspectionTool
import com.intellij.psi.PsiElement
import com.intellij.codeInsight.daemon.HighlightDisplayKey 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.SuppressIntentionAction
import com.intellij.codeInspection.SuppressManager 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 abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSuppressableInspectionTool {
public override fun getSuppressActions(element: PsiElement?): Array<SuppressIntentionAction>? { public override fun getSuppressActions(element: PsiElement?): Array<SuppressIntentionAction>? {
@@ -29,6 +32,30 @@ public abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSup
} }
public override fun isSuppressedFor(element: PsiElement): Boolean { 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
}
} }
} }
@@ -91,7 +91,9 @@ public abstract class SelfTargetingIntention<TElement : KtElement>(
val inspection = findInspection(javaClass) ?: return false val inspection = findInspection(javaClass) ?: return false
val key = HighlightDisplayKey.find(inspection.shortName) 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) return inspection.intentions.single { it.intention.javaClass == javaClass }.additionalChecker(target)
} }
@@ -1,8 +1,21 @@
class Klass { class Klass {
fun unused() { fun unusedFun() {
}
@Suppress("UnusedSymbol")
fun unusedNoWarn() {
}
}
@Suppress("UnusedSymbol")
class OtherKlass {
fun unusedNoWarn() {
} }
} }
fun main(args: Array<String>) { fun main(args: Array<String>) {
Klass() Klass()
OtherKlass()
} }
@@ -10,7 +10,7 @@
<problem> <problem>
<file>topLevelOnlyInImport.kt</file> <file>topLevelOnlyInImport.kt</file>
<line>5</line> <line>6</line>
<module>light_idea_test_case</module> <module>light_idea_test_case</module>
<entry_point TYPE="file" FQNAME="temp:///src/topLevelOnlyInImport.kt" /> <entry_point TYPE="file" FQNAME="temp:///src/topLevelOnlyInImport.kt" />
<problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class> <problem_class severity="WARNING" attribute_key="NOT_USED_ELEMENT_ATTRIBUTES">Unused Symbol</problem_class>
@@ -2,4 +2,16 @@ fun outer() {
fun local() { fun local() {
} }
@Suppress("UnusedSymbol")
fun localNoWarn() {
}
}
@Suppress("UnusedSymbol")
fun otherFun() {
fun localNoWarn() {
}
} }
@@ -3,6 +3,9 @@ class Klass {
fun f() { fun f() {
} }
@Suppress("UnusedSymbol")
fun fNoWarn() {}
val p = 5 val p = 5
} }
@@ -16,9 +19,21 @@ class Klass {
fun f() { fun f() {
} }
@Suppress("UnusedSymbol")
fun fNoWarn() {}
val p = 5 val p = 5
} }
fun localObject3() = @Suppress("UnusedSymbol") object {
fun fNoWarn() {}
}
@Suppress("UnusedSymbol")
private val localObject4 = object {
fun fNoWarn() {}
}
init { init {
localObject2().f() localObject2().f()
localObject2().p localObject2().p
@@ -39,5 +54,5 @@ class Klass {
} }
fun main(args: Array<String>) { fun main(args: Array<String>) {
Klass() Klass().localObject3()
} }
@@ -14,9 +14,17 @@ fun main(args: Array<String>) {
fun f() { fun f() {
} }
@Suppress("UnusedSymbol")
fun fNoWarn() {}
val p = 5 val p = 5
} }
@Suppress("UnusedSymbol")
fun localObject3() = object {
fun fNoWarn() {}
}
localObject2().f() localObject2().f()
localObject2().p localObject2().p
@@ -3,10 +3,25 @@ fun main(args: Array<String>) {
fun f() { fun f() {
} }
@Suppress("UnusedSymbol")
fun fNoWarn() {}
val p = 5 val p = 5
} }
@Suppress("UnusedSymbol")
class OtherClass {
fun fNoWarn() {}
}
LocalClass().f() LocalClass().f()
LocalClass().p LocalClass().p
} }
@Suppress("UnusedSymbol")
fun other() {
class OtherClass {
fun fNoWarn() {}
}
}
@@ -1,7 +1,11 @@
package foo package foo
import foo.onlyInImport import foo.onlyInImport
import foo.onlyInImportNoWarn
fun onlyInImport() { fun onlyInImport() {
} }
@Suppress("UnusedSymbol")
fun onlyInImportNoWarn() {}
@@ -1,3 +1,6 @@
fun unused() { fun unusedFun() {
} }
@Suppress("UnusedSymbol")
fun unusedNoWarn() {}
@@ -1,3 +1,8 @@
fun usedOnlyRecursively() { fun usedOnlyRecursively() {
usedOnlyRecursively() usedOnlyRecursively()
} }
@Suppress("UnusedSymbol")
fun usedOnlyRecursivelyNoWarn() {
usedOnlyRecursivelyNoWarn()
}
@@ -7,3 +7,14 @@ fun test() {
i 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 }
}
@@ -7,3 +7,14 @@ fun test() {
i 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 }
}