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 40d14fc5cce..59d43d993ae 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
@@ -72,7 +72,7 @@ abstract class AbstractKotlinInspection: LocalInspectionTool(), CustomSuppressab
}
}
-private fun toSeverity(highlightDisplayLevel: HighlightDisplayLevel): Severity {
+fun toSeverity(highlightDisplayLevel: HighlightDisplayLevel): Severity {
return when (highlightDisplayLevel) {
HighlightDisplayLevel.DO_NOT_SHOW -> Severity.INFO
diff --git a/idea/idea-jvm/resources/inspectionDescriptions/IncompatibleAPI.html b/idea/idea-jvm/resources/inspectionDescriptions/IncompatibleAPI.html
new file mode 100644
index 00000000000..99aab83016d
--- /dev/null
+++ b/idea/idea-jvm/resources/inspectionDescriptions/IncompatibleAPI.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports usages of incompatible API.
+
+
diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddIncompatibleApiAction.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddIncompatibleApiAction.kt
new file mode 100644
index 00000000000..c8ee27ee89a
--- /dev/null
+++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddIncompatibleApiAction.kt
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.api
+
+import com.intellij.codeInsight.TargetElementUtil
+import com.intellij.openapi.actionSystem.DataContext
+import com.intellij.openapi.editor.Caret
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.editor.actionSystem.EditorAction
+import com.intellij.openapi.editor.actionSystem.EditorActionHandler
+import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
+
+class AddIncompatibleApiAction : EditorAction(AddIncompatibleApiInspectionHandler())
+
+private class AddIncompatibleApiInspectionHandler : EditorActionHandler() {
+ override fun isEnabledForCaret(editor: Editor, caret: Caret, dataContext: DataContext?): Boolean {
+ if (!super.isEnabledForCaret(editor, caret, dataContext)) {
+ return false
+ }
+
+ val project = editor.project ?: return false
+ val incompatibleAPIToolState = ProjectInspectionProfileManager.getInstance(project).currentProfile.getToolDefaultState(
+ IncompatibleAPIInspection.SHORT_NAME, project
+ )
+
+ return incompatibleAPIToolState.isEnabled
+ }
+
+ override fun doExecute(editor: Editor, caret: Caret?, dataContext: DataContext?) {
+ val project = editor.project ?: return
+ val element = TargetElementUtil.findTargetElement(
+ editor, TargetElementUtil.ELEMENT_NAME_ACCEPTED or TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED
+ ) ?: return
+ val qualified = getQualifiedNameFromProviders(element) ?: return
+ AddToIncompatibleApiDialog(project, qualified).show()
+ }
+}
\ No newline at end of file
diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddToIncompatibleApiDialog.form b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddToIncompatibleApiDialog.form
new file mode 100644
index 00000000000..b0cc73c13bb
--- /dev/null
+++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddToIncompatibleApiDialog.form
@@ -0,0 +1,60 @@
+
+
diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddToIncompatibleApiDialog.java b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddToIncompatibleApiDialog.java
new file mode 100644
index 00000000000..3e56596f011
--- /dev/null
+++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/AddToIncompatibleApiDialog.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.api;
+
+import com.intellij.codeInspection.ex.InspectionProfileModifiableModelKt;
+import com.intellij.codeInspection.ex.ToolsImpl;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.ui.DialogWrapper;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.awt.*;
+
+public class AddToIncompatibleApiDialog extends DialogWrapper {
+ private JPanel contentPane;
+ private JTextField referenceTextField;
+ private JTextField reasonTextField;
+
+ private final Project project;
+
+ public AddToIncompatibleApiDialog(@NotNull Project project, @NotNull String qualifiedReference) {
+ super(project, true);
+ this.project = project;
+
+ setModal(true);
+ setTitle("Report as Incompatible API");
+ referenceTextField.setText(qualifiedReference);
+
+ init();
+ }
+
+ @Nullable
+ @Override
+ public JComponent getPreferredFocusedComponent() {
+ return reasonTextField;
+ }
+
+ @Override
+ protected JComponent createCenterPanel() {
+ return contentPane;
+ }
+
+ @Override
+ public Container getContentPane() {
+ return contentPane;
+ }
+
+ @Override
+ protected void doOKAction() {
+ String reference = referenceTextField.getText();
+ String reason = reasonTextField.getText();
+
+ reference = reference != null ? reference.trim() : null;
+ reason = reason != null ? reason.trim() : null;
+
+ if (reference != null && !reference.isEmpty()) {
+ String finalReference = reference;
+ String finalReason = reason == null || reason.isEmpty() ? null : reason;
+
+ InspectionProfileModifiableModelKt.modifyAndCommitProjectProfile(project, inspectionProfileModifiableModel -> {
+ ToolsImpl toolImpl = inspectionProfileModifiableModel.getToolsOrNull(IncompatibleAPIInspection.SHORT_NAME, project);
+ if (toolImpl != null) {
+ IncompatibleAPIInspection incompatibleAPIInspection =
+ (IncompatibleAPIInspection) toolImpl.getDefaultState().getTool().getTool();
+ incompatibleAPIInspection.addProblem(finalReference, finalReason);
+ }
+ });
+ }
+
+ super.doOKAction();
+ }
+
+ @Override
+ public void dispose() {
+ super.dispose();
+ }
+}
diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIInspection.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIInspection.kt
new file mode 100644
index 00000000000..5ed74d6e1c0
--- /dev/null
+++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIInspection.kt
@@ -0,0 +1,201 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.api
+
+import com.intellij.codeInsight.daemon.HighlightDisplayKey
+import com.intellij.codeInspection.*
+import com.intellij.ide.actions.QualifiedNameProvider
+import com.intellij.lang.java.JavaLanguage
+import com.intellij.openapi.extensions.Extensions
+import com.intellij.openapi.project.DumbService
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiElementVisitor
+import com.intellij.psi.PsiReference
+import com.intellij.util.xmlb.annotations.Attribute
+import org.jdom.Element
+import org.jetbrains.kotlin.caches.resolve.KotlinCacheService
+import org.jetbrains.kotlin.idea.KotlinLanguage
+import org.jetbrains.kotlin.idea.highlighter.createSuppressWarningActions
+import org.jetbrains.kotlin.idea.inspections.api.IncompatibleAPIInspection.Companion.DEFAULT_REASON
+import org.jetbrains.kotlin.idea.inspections.toSeverity
+
+class IncompatibleAPIInspection : LocalInspectionTool(), CustomSuppressableInspectionTool {
+ class Problem(
+ @Attribute var reference: String? = "",
+ @Attribute var reason: String? = ""
+ )
+
+ // Stored in inspection setting
+ var problems: List = arrayListOf()
+
+ fun addProblem(reference: String, reason: String?) {
+ problems += Problem(reference, reason)
+ problemsCache.update(problems)
+ }
+
+ override fun readSettings(node: Element) {
+ super.readSettings(node)
+ problemsCache.update(problems)
+ }
+
+ override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
+ if (problems.isEmpty()) {
+ return super.buildVisitor(holder, isOnTheFly)
+ }
+
+ return when (holder.file.language) {
+ KotlinLanguage.INSTANCE -> {
+ IncompatibleAPIKotlinVisitor(holder, problemsCache)
+ }
+
+ JavaLanguage.INSTANCE -> {
+ IncompatibleAPIJavaVisitor(holder, problemsCache)
+ }
+
+ else -> {
+ super.buildVisitor(holder, isOnTheFly)
+ }
+ }
+ }
+
+ override fun getSuppressActions(element: PsiElement?): Array? {
+ return when {
+ element == null -> emptyArray()
+
+ element.language == JavaLanguage.INSTANCE -> {
+ val key = HighlightDisplayKey.find(shortName)
+ ?: throw AssertionError("HighlightDisplayKey.find($shortName) is null. Inspection: $javaClass")
+ return SuppressManager.getInstance().createSuppressActions(key)
+ }
+
+ element.language == KotlinLanguage.INSTANCE -> {
+ createSuppressWarningActions(element, toSeverity(defaultLevel), suppressionKey).toTypedArray()
+ }
+
+ else -> emptyArray()
+ }
+ }
+
+ override fun isSuppressedFor(element: PsiElement): Boolean {
+ if (SuppressManager.getInstance()!!.isSuppressedFor(element, id)) {
+ return true
+ }
+
+ val project = element.project
+ if (KotlinCacheService.getInstance(project).getSuppressionCache().isSuppressed(element, suppressionKey, toSeverity(defaultLevel))) {
+ return true
+ }
+
+ return false
+ }
+
+ private val suppressionKey: String get() = this.shortName
+ private val problemsCache = ProblemsCache()
+
+ companion object {
+ const val SHORT_NAME = "IncompatibleAPI"
+ const val DEFAULT_REASON = "Incompatible API"
+ }
+}
+
+internal class ProblemsCache {
+ var forbiddenApiReferences: Map = emptyMap()
+ private set
+
+ private var words: Set = emptySet()
+
+ fun update(problems: List) {
+ val validProblems = problems.filter { !it.reference.isNullOrBlank() }
+
+ forbiddenApiReferences = validProblems.map { it.reference!! to it }.toMap()
+
+ words = validProblems.mapTo(HashSet()) {
+ val reference = it.reference!!
+ if (reference.contains('#')) {
+ reference.substringAfterLast('#').substringBefore('(')
+ } else {
+ reference.substringAfterLast('.')
+ }
+ }
+ }
+
+ fun containsWord(word: String) = word in words
+}
+
+internal fun registerProblemForReference(
+ reference: PsiReference?,
+ holder: ProblemsHolder,
+ problem: IncompatibleAPIInspection.Problem
+) {
+ if (reference == null) return
+ holder.registerProblem(
+ reference,
+ problem.reason ?: DEFAULT_REASON,
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING
+ )
+}
+
+internal fun registerProblemForElement(
+ element: PsiElement?,
+ holder: ProblemsHolder,
+ problem: IncompatibleAPIInspection.Problem
+) {
+ if (element == null) return
+ holder.registerProblem(
+ element,
+ problem.reason ?: DEFAULT_REASON,
+ ProblemHighlightType.GENERIC_ERROR_OR_WARNING
+ )
+}
+
+internal fun findProblem(
+ resolvedTo: PsiElement?,
+ problemsCache: ProblemsCache
+): IncompatibleAPIInspection.Problem? {
+ if (resolvedTo == null) return null
+ val referenceStr = getQualifiedNameFromProviders(resolvedTo) ?: return null
+
+ val forbiddenApiReferences = problemsCache.forbiddenApiReferences
+
+ return forbiddenApiReferences[referenceStr] ?: run {
+ // check constructor
+ val lastPart = referenceStr.substringAfterLast('#', "")
+ if (lastPart.isNotEmpty()) {
+ val classFqName = referenceStr.substringBeforeLast('#')
+ val className = classFqName.substringAfterLast('.')
+ if (lastPart.startsWith("$className(")) {
+ return@run forbiddenApiReferences[classFqName]
+ }
+ }
+
+ null
+ } ?: run {
+ // Check reference without params
+ val beforeParams = referenceStr.substringBeforeLast('(', "")
+ if (beforeParams.isNotEmpty()) {
+ val overloadSignatureKey = "$beforeParams("
+ val particularOverloadPresent = forbiddenApiReferences.keys.any { it.startsWith(overloadSignatureKey) }
+ if (!particularOverloadPresent) {
+ return@run forbiddenApiReferences[beforeParams]
+ }
+ }
+
+ null
+ }
+}
+
+internal fun getQualifiedNameFromProviders(element: PsiElement): String? {
+ DumbService.getInstance(element.project).isAlternativeResolveEnabled = true
+ try {
+ for (provider in Extensions.getExtensions(QualifiedNameProvider.EP_NAME)) {
+ val result = provider.getQualifiedName(element)
+ if (result != null) return result
+ }
+ } finally {
+ DumbService.getInstance(element.project).isAlternativeResolveEnabled = false
+ }
+ return null
+}
\ No newline at end of file
diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIJavaVisitor.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIJavaVisitor.kt
new file mode 100644
index 00000000000..c4787349e84
--- /dev/null
+++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIJavaVisitor.kt
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.api
+
+import com.intellij.codeInsight.AnnotationUtil
+import com.intellij.codeInspection.ProblemsHolder
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.module.ModuleUtilCore
+import com.intellij.psi.*
+import com.intellij.psi.javadoc.PsiDocComment
+import org.jetbrains.kotlin.psi.psiUtil.parents
+
+class IncompatibleAPIJavaVisitor internal constructor(
+ private val myHolder: ProblemsHolder,
+ private val problemsCache: ProblemsCache
+) : JavaElementVisitor() {
+ override fun visitDocComment(comment: PsiDocComment) {
+ // No references inside doc comment are of interest.
+ }
+
+ override fun visitImportList(list: PsiImportList?) {
+ // Do not report anything in imports
+ }
+
+ override fun visitClass(aClass: PsiClass) {}
+
+ override fun visitReferenceExpression(expression: PsiReferenceExpression) {
+ visitReferenceElement(expression)
+ }
+
+ override fun visitReferenceElement(reference: PsiJavaCodeReferenceElement) {
+ val isInsideImport = reference.element.parents
+ .takeWhile { it is PsiJavaCodeReferenceElement || it is PsiImportStatement }
+ .any { it is PsiImportStatement }
+ if (isInsideImport) {
+ return
+ }
+
+ ModuleUtilCore.findModuleForPsiElement(reference.element) ?: return
+ super.visitReferenceElement(reference)
+
+ val name = reference.referenceName
+ if (name == null || !problemsCache.containsWord(name)) {
+ return
+ }
+
+ val psiMember = reference.resolve() as? PsiMember ?: return
+ val problem = findProblem(psiMember, problemsCache) ?: return
+ registerProblemForReference(reference, myHolder, problem)
+ }
+
+ override fun visitNewExpression(expression: PsiNewExpression) {
+ ModuleUtilCore.findModuleForPsiElement(expression) ?: return
+ super.visitNewExpression(expression)
+
+ val name = expression.classReference?.referenceName
+ if (name == null || !problemsCache.containsWord(name)) {
+ return
+ }
+
+ val constructor = expression.resolveConstructor()
+ if (exitOnNonCompiled(constructor)) return
+
+ val problem = findProblem(constructor, problemsCache)
+ if (problem != null) {
+ registerProblemForReference(expression.classReference, myHolder, problem)
+ }
+ }
+
+ override fun visitMethod(method: PsiMethod) {
+ ModuleUtilCore.findModuleForPsiElement(method) ?: return
+ super.visitMethod(method)
+
+ if (!problemsCache.containsWord(method.name)) {
+ return
+ }
+
+ (if (!method.isConstructor) AnnotationUtil.findAnnotation(method, CommonClassNames.JAVA_LANG_OVERRIDE) else null) ?: return
+
+ val methods = method.findSuperMethods()
+ for (superMethod in methods) {
+ if (exitOnNonCompiled(superMethod)) {
+ return
+ }
+
+ val problem = findProblem(superMethod, problemsCache)
+ if (problem != null) {
+ registerProblemForElement(method.nameIdentifier, myHolder, problem)
+ return
+ }
+ }
+ }
+
+ private fun exitOnNonCompiled(psiElement: PsiElement?): Boolean {
+ if (psiElement != null && psiElement !is PsiCompiledElement) {
+ if (!ApplicationManager.getApplication().isInternal && !ApplicationManager.getApplication().isUnitTestMode) {
+ return true
+ }
+ }
+
+ return false
+ }
+}
\ No newline at end of file
diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIKotlinVisitor.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIKotlinVisitor.kt
new file mode 100644
index 00000000000..78af8d9620c
--- /dev/null
+++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/inspections/api/IncompatibleAPIKotlinVisitor.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
+ * that can be found in the license/LICENSE.txt file.
+ */
+
+package org.jetbrains.kotlin.idea.inspections.api
+
+import com.intellij.codeInspection.ProblemsHolder
+import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
+import org.jetbrains.kotlin.lexer.KtTokens
+import org.jetbrains.kotlin.name.Name
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.psi.psiUtil.isInImportDirective
+import org.jetbrains.kotlin.resolve.source.getPsi
+import org.jetbrains.kotlin.synthetic.JavaSyntheticPropertiesScope
+
+internal class IncompatibleAPIKotlinVisitor(
+ private val holder: ProblemsHolder,
+ private val problemsCache: ProblemsCache
+) : KtVisitorVoid() {
+ override fun visitImportList(importList: KtImportList) {
+ // Do not report anything in imports
+ }
+
+ override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
+ val nameStr = expression.text
+ if (!Name.isValidIdentifier(nameStr)) {
+ return
+ }
+
+ val names = HashSet()
+ names.add(nameStr)
+
+ val gettersNames = JavaSyntheticPropertiesScope.possibleGetMethodNames(
+ Name.identifier(nameStr)
+ )
+ if (!gettersNames.isEmpty()) {
+ names.addAll(gettersNames.map { it.identifier })
+ names.add(JavaSyntheticPropertiesScope.setMethodName(gettersNames.first()).identifier)
+ }
+
+ if (names.none { name -> problemsCache.containsWord(name) }) {
+ return
+ }
+
+ checkReference(expression)
+ }
+
+ override fun visitNamedFunction(function: KtNamedFunction) {
+ super.visitNamedFunction(function)
+
+ if (function.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD) == null) {
+ return
+ }
+
+ val funName = function.name
+ if (funName == null || !problemsCache.containsWord(funName)) {
+ return
+ }
+
+ val functionDescriptor = function.resolveToDescriptorIfAny() ?: return
+ for (overriddenDescriptor in functionDescriptor.original.overriddenDescriptors) {
+ val psi = overriddenDescriptor.source.getPsi() ?: continue
+ val problem = findProblem(psi, problemsCache)
+ if (problem != null) {
+ registerProblemForElement(function.nameIdentifier, holder, problem)
+ return
+ }
+ }
+ }
+
+ private fun checkReference(expression: KtSimpleNameExpression) {
+ if (expression.isInImportDirective()) {
+ // Ignore imports
+ return
+ }
+
+ for (reference in expression.references) {
+ val resolveTo = reference.resolve()
+ val problem = findProblem(resolveTo, problemsCache) ?: continue
+
+ registerProblemForReference(reference, holder, problem)
+ break
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/src/META-INF/jvm.xml b/idea/src/META-INF/jvm.xml
index dbdaf80482f..750c29549aa 100644
--- a/idea/src/META-INF/jvm.xml
+++ b/idea/src/META-INF/jvm.xml
@@ -57,6 +57,10 @@
text="Kotlin Project Post-Open Activity" internal="true">
+
+
+
@@ -118,6 +122,14 @@
+
+
diff --git a/idea/testData/inspections/incompatibleAPI/JavaTest.java b/idea/testData/inspections/incompatibleAPI/JavaTest.java
new file mode 100644
index 00000000000..da64b4af870
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/JavaTest.java
@@ -0,0 +1,45 @@
+package problem.api;
+
+import lib.LibClass;
+import lib.LibMethods;
+import lib.LibSuper;
+import lib.LibConstructor;
+
+public class JavaTest {
+ void test() {
+ new LibClass();
+ LibMethods.staticMethod();
+ }
+
+ static void overloads(LibMethods lib) {
+ lib.overload1(12);
+ lib.overload1("Some");
+
+ lib.overload2(12);
+ lib.overload2("Some");
+
+ //noinspection IncompatibleAPI
+ lib.overload2(13);
+ }
+
+ public static class Extends extends LibClass {
+ }
+
+ public class Subclass extends LibSuper {
+ @Override
+ public void test(String str) {
+ }
+ }
+
+ public class SubclassSuppress extends LibSuper {
+ @SuppressWarnings("IncompatibleAPI")
+ @Override
+ public void test(String str) {
+ }
+ }
+
+ public static constructor() {
+ new LibConstructor(null, "some");
+ new LibConstructor(null);
+ }
+}
diff --git a/idea/testData/inspections/incompatibleAPI/classes.kt b/idea/testData/inspections/incompatibleAPI/classes.kt
new file mode 100644
index 00000000000..a3894b08e27
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/classes.kt
@@ -0,0 +1,17 @@
+package problem.api.kotlin.classes
+
+import lib.LibClass
+
+fun ktTest() {
+ LibClass()
+}
+
+class KtUsage : LibClass()
+
+fun ktTestSuppress() {
+ @Suppress("IncompatibleAPI")
+ LibClass()
+}
+
+@Suppress("IncompatibleAPI")
+class KtUsageSuppress : LibClass()
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/constructors.kt b/idea/testData/inspections/incompatibleAPI/constructors.kt
new file mode 100644
index 00000000000..a65f9714ddb
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/constructors.kt
@@ -0,0 +1,8 @@
+package problem.api.kotlin.constructors
+
+import lib.LibConstructor
+
+fun ktOverloads(lib: LibMethods) {
+ LibConstructor(null, "some")
+ LibConstructor(null)
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/inspectionData/expected.xml b/idea/testData/inspections/incompatibleAPI/inspectionData/expected.xml
new file mode 100644
index 00000000000..34bb4f2eff4
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/inspectionData/expected.xml
@@ -0,0 +1,87 @@
+
+
+ classes.kt
+ 6
+ class example
+
+
+ classes.kt
+ 9
+ class example
+
+
+
+ overloads.kt
+ 6
+ concrete overload example
+
+
+ overloads.kt
+ 9
+ overload with no params example
+
+
+ overloads.kt
+ 10
+ overload with no params example
+
+
+
+ static.kt
+ 6
+ static method example
+
+
+
+ JavaTest.java
+ 10
+ class example
+
+
+ JavaTest.java
+ 11
+ static method example
+
+
+ JavaTest.java
+ 15
+ concrete overload example
+
+
+ JavaTest.java
+ 18
+ overload with no params example
+
+
+ JavaTest.java
+ 19
+ overload with no params example
+
+
+ JavaTest.java
+ 25
+ class example
+
+
+ JavaTest.java
+ 30
+ signature changed example
+
+
+ JavaTest.java
+ 43
+ constructor example
+
+
+
+ super.kt
+ 6
+ signature changed example
+
+
+
+ constructors.kt
+ 7
+ constructor example
+
+
diff --git a/idea/testData/inspections/incompatibleAPI/inspectionData/inspections.test b/idea/testData/inspections/incompatibleAPI/inspectionData/inspections.test
new file mode 100644
index 00000000000..5a796ea58b1
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.inspections.api.IncompatibleAPIInspection
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/inspectionData/settings.xml b/idea/testData/inspections/incompatibleAPI/inspectionData/settings.xml
new file mode 100644
index 00000000000..b6a82b110b9
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/inspectionData/settings.xml
@@ -0,0 +1,12 @@
+
+
+
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/lib/LibClass.java b/idea/testData/inspections/incompatibleAPI/lib/LibClass.java
new file mode 100644
index 00000000000..6d5c492b18d
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/lib/LibClass.java
@@ -0,0 +1,4 @@
+package lib;
+
+public class LibClass {
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/lib/LibConstructor.java b/idea/testData/inspections/incompatibleAPI/lib/LibConstructor.java
new file mode 100644
index 00000000000..3e7911891dc
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/lib/LibConstructor.java
@@ -0,0 +1,9 @@
+package lib;
+
+public class LibConstructor {
+ public LibConstructor(Object object) {
+ }
+
+ public LibConstructor(Object object, String other) {
+ }
+}
diff --git a/idea/testData/inspections/incompatibleAPI/lib/LibMethods.java b/idea/testData/inspections/incompatibleAPI/lib/LibMethods.java
new file mode 100644
index 00000000000..48b9af33999
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/lib/LibMethods.java
@@ -0,0 +1,12 @@
+package lib;
+
+public class LibMethods {
+ public static void staticMethod() {
+ }
+
+ public void overload1(int i) {}
+ public void overload1(String s) {}
+
+ public void overload2(int i) {}
+ public void overload2(String s) {}
+}
diff --git a/idea/testData/inspections/incompatibleAPI/lib/LibSuper.java b/idea/testData/inspections/incompatibleAPI/lib/LibSuper.java
new file mode 100644
index 00000000000..747845497cd
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/lib/LibSuper.java
@@ -0,0 +1,7 @@
+package lib;
+
+import org.jetbrains.annotations.Nullable;
+
+public class LibSuper {
+ public void test(@Nullable String str) {}
+}
diff --git a/idea/testData/inspections/incompatibleAPI/overloads.kt b/idea/testData/inspections/incompatibleAPI/overloads.kt
new file mode 100644
index 00000000000..22795600b21
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/overloads.kt
@@ -0,0 +1,11 @@
+package problem.api.kotlin.overloads
+
+import lib.LibMethods
+
+fun ktOverloads(lib: LibMethods) {
+ lib.overload1(12)
+ lib.overload1("Some")
+
+ lib.overload2(12)
+ lib.overload2("Some")
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/static.kt b/idea/testData/inspections/incompatibleAPI/static.kt
new file mode 100644
index 00000000000..ecd88e8c10b
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/static.kt
@@ -0,0 +1,7 @@
+package problem.api.kotlin.statics
+
+import lib.LibMethods
+
+fun ktTest() {
+ LibMethods.staticMethod()
+}
\ No newline at end of file
diff --git a/idea/testData/inspections/incompatibleAPI/super.kt b/idea/testData/inspections/incompatibleAPI/super.kt
new file mode 100644
index 00000000000..40e619d9366
--- /dev/null
+++ b/idea/testData/inspections/incompatibleAPI/super.kt
@@ -0,0 +1,14 @@
+package problem.api.kotlin.s
+
+import lib.LibSuper
+
+class Subclass : LibSuper() {
+ override fun test(str: String?) {
+ }
+}
+
+class SubclassSuppress : LibSuper() {
+ @Suppress("IncompatibleAPI")
+ override fun test(str: String?) {
+ }
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractInspectionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractInspectionTest.kt
index acd662294d2..db06949b519 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractInspectionTest.kt
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/AbstractInspectionTest.kt
@@ -21,6 +21,8 @@ import com.intellij.openapi.command.CommandProcessor
import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiFile
import com.intellij.testFramework.TestLoggerFactory
+import org.jdom.Document
+import org.jdom.input.SAXBuilder
import org.jetbrains.kotlin.idea.inspections.runInspection
import org.jetbrains.kotlin.idea.test.*
import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -66,6 +68,13 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
val inspectionsTestDir = optionsFile.parentFile!!
val srcDir = inspectionsTestDir.parentFile!!
+ val settingsFile = File(inspectionsTestDir, "settings.xml")
+ val settingsElement = if (settingsFile.exists()) {
+ (SAXBuilder().build(settingsFile) as Document).rootElement
+ } else {
+ null
+ }
+
with(myFixture) {
testDataPath = "${KotlinTestUtils.getHomeDirectory()}/$srcDir"
@@ -110,7 +119,9 @@ abstract class AbstractInspectionTest : KotlinLightCodeInsightFixtureTestCase()
configExtra(psiFiles, options)
val presentation = runInspection(
- inspectionClass, project, files = psiFiles.map { it.virtualFile!! }, withTestDir = inspectionsTestDir.path
+ inspectionClass, project,
+ settings = settingsElement,
+ files = psiFiles.map { it.virtualFile!! }, withTestDir = inspectionsTestDir.path
)
if (afterFiles.isNotEmpty()) {
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
index 654087e1bca..c38f2fb3905 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
@@ -174,6 +174,11 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
runTest("idea/testData/inspections/hasPlatformType/inspectionData/inspections.test");
}
+ @TestMetadata("incompatibleAPI/inspectionData/inspections.test")
+ public void testIncompatibleAPI_inspectionData_Inspections_test() throws Exception {
+ runTest("idea/testData/inspections/incompatibleAPI/inspectionData/inspections.test");
+ }
+
@TestMetadata("javaCollectionsStaticMethodOnImmutableList/inspectionData/inspections.test")
public void testJavaCollectionsStaticMethodOnImmutableList_inspectionData_Inspections_test() throws Exception {
runTest("idea/testData/inspections/javaCollectionsStaticMethodOnImmutableList/inspectionData/inspections.test");
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/InspectionUtils.kt b/idea/tests/org/jetbrains/kotlin/idea/inspections/InspectionUtils.kt
index 08b44405e02..bd468b3f97c 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/InspectionUtils.kt
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/InspectionUtils.kt
@@ -24,6 +24,7 @@ import com.intellij.openapi.project.Project
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.testFramework.InspectionTestUtil
import com.intellij.testFramework.createGlobalContextForTool
+import org.jdom.Element
fun runInspection(
inspection: LocalInspectionTool, project: Project, files: List? = null, withTestDir: String? = null
@@ -46,8 +47,12 @@ fun runInspection(
}
fun runInspection(
- inspectionClass: Class<*>, project: Project, files: List? = null, withTestDir: String? = null
+ inspectionClass: Class<*>, project: Project,
+ settings: Element? = null, files: List? = null, withTestDir: String? = null
): InspectionToolPresentation {
val inspection = inspectionClass.newInstance() as LocalInspectionTool
+ if (settings != null) {
+ inspection.readSettings(settings)
+ }
return runInspection(inspection, project, files, withTestDir)
}