forked from Hukumister/Gemini
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Hukumister#52: Add lint rule for the wrong usage
- Loading branch information
Showing
10 changed files
with
280 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,4 +30,6 @@ object Versions { | |
const val bintray = "1.8.5" | ||
|
||
const val ktlint = "0.39.0" | ||
|
||
const val androidTools = "27.1.2" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
plugins { | ||
id("java-library") | ||
id("kotlin") | ||
} | ||
|
||
java { | ||
sourceCompatibility = JavaVersion.VERSION_1_8 | ||
targetCompatibility = JavaVersion.VERSION_1_8 | ||
} | ||
|
||
dependencies { | ||
compileOnly(Deps.lint.api) | ||
compileOnly(Deps.lint.checks) | ||
|
||
testImplementation(Deps.kotlinTestJunit) | ||
testImplementation(Deps.lint.core) | ||
testImplementation(Deps.lint.tests) | ||
testImplementation(Deps.testutils) | ||
} | ||
|
||
tasks.jar { | ||
manifest { | ||
attributes( | ||
"Lint-Registry-v3" to "com.haroncode.gemini.lint.GenimiIssueRegistry" | ||
) | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
gemini-lint/src/main/java/com/haroncode/gemini/lint/GenimiIssueRegistry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package com.haroncode.gemini.lint | ||
|
||
import com.android.tools.lint.client.api.IssueRegistry | ||
import com.android.tools.lint.detector.api.CURRENT_API | ||
import com.android.tools.lint.detector.api.Issue | ||
|
||
class GenimiIssueRegistry : IssueRegistry() { | ||
override val issues = WrongGenimiUsageDetector.issues | ||
|
||
override val api = CURRENT_API | ||
} |
49 changes: 49 additions & 0 deletions
49
gemini-lint/src/main/java/com/haroncode/gemini/lint/WrongGenimiUsageDetector.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
@file:Suppress("UnstableApiUsage") | ||
|
||
package com.haroncode.gemini.lint | ||
|
||
import com.android.tools.lint.detector.api.* | ||
import com.intellij.psi.PsiMethod | ||
import org.jetbrains.uast.UCallExpression | ||
|
||
class WrongGenimiUsageDetector : Detector(), SourceCodeScanner { | ||
override fun getApplicableMethodNames() = listOf("bind") | ||
|
||
override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { | ||
val evaluator = context.evaluator | ||
|
||
if (evaluator.isMemberInSubClassOf(method, "com.haroncode.gemini.binder.Binder")) { | ||
checkCallMethod(method, node, context) | ||
} | ||
} | ||
|
||
|
||
|
||
private fun checkCallMethod(method: PsiMethod, call: UCallExpression, context: JavaContext) { | ||
var parent = call.uastParent | ||
while (parent != null && parent !is PsiMethod) { | ||
parent = parent.uastParent | ||
} | ||
if (parent !is PsiMethod) return | ||
if (parent.name != "onCreate") { | ||
context.report(ISSUE_ON_CREATE, method, context.getLocation(call), "Calling bind from ${parent.name} instead of onCreate") | ||
} | ||
} | ||
|
||
companion object { | ||
val ISSUE_ON_CREATE = Issue.create( | ||
"ShouldBeCalledInOnCreate", | ||
"Bind method for ViewStoreBinding should be called in onCreate method", | ||
"Based on documentation you should call bind in the onCreate method of your's view", | ||
Category.CORRECTNESS, | ||
5, | ||
Severity.ERROR, | ||
Implementation(WrongGenimiUsageDetector::class.java, Scope.JAVA_FILE_SCOPE) | ||
) | ||
|
||
val issues = listOf( | ||
ISSUE_ON_CREATE | ||
) | ||
} | ||
|
||
} |
1 change: 1 addition & 0 deletions
1
...lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
com.haroncode.gemini.lint.GenimiIssueRegistry |
99 changes: 99 additions & 0 deletions
99
gemini-lint/src/test/java/com/haroncode/gemini/lint/WrongGenimiUsageDetectorTest.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
package com.haroncode.gemini.lint | ||
|
||
import com.android.tools.lint.checks.infrastructure.LintDetectorTest | ||
|
||
@Suppress("UnstableApiUsage") | ||
class WrongGenimiUsageDetectorTest : LintDetectorTest() { | ||
private val GEMINI_STUB1 = kotlin(BINDER_STUB) | ||
private val GEMINI_STUB2 = kotlin(BINDER_RULES) | ||
|
||
fun testRightUsageWithActivity() { | ||
lint() | ||
.files( | ||
GEMINI_STUB1, | ||
GEMINI_STUB2, | ||
kotlin(""" | ||
package ru.test.app | ||
import android.app.Activity | ||
import com.haroncode.gemini.binder.* | ||
import com.haroncode.gemini.binder.rule.* | ||
class Activity1 : Activity() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
StoreViewBinding.with(generateSampleRulesFactory<Any>()) | ||
.bind(this) | ||
} | ||
} | ||
""".trimIndent()) | ||
) | ||
.requireCompileSdk() | ||
.run() | ||
.expectClean() | ||
} | ||
|
||
fun testRightUsageWithFragment() { | ||
lint() | ||
.files( | ||
GEMINI_STUB1, | ||
GEMINI_STUB2, | ||
kotlin(""" | ||
package ru.test.app | ||
import android.app.Fragment | ||
import com.haroncode.gemini.binder.* | ||
import com.haroncode.gemini.binder.rule.* | ||
class Fragment1 : Fragment() { | ||
override fun onCreate(savedInstanceState: Bundle?) { | ||
super.onCreate(savedInstanceState) | ||
StoreViewBinding.with(generateSampleRulesFactory<Any>()) | ||
.bind(this) | ||
} | ||
} | ||
""".trimIndent()) | ||
) | ||
.requireCompileSdk() | ||
.run() | ||
.expectClean() | ||
} | ||
|
||
fun testWrongUsageWithFragment_onViewCreated() { | ||
lint() | ||
.files( | ||
GEMINI_STUB1, | ||
GEMINI_STUB2, | ||
kotlin(""" | ||
package ru.test.app | ||
import android.app.Fragment | ||
import com.haroncode.gemini.binder.* | ||
import com.haroncode.gemini.binder.rule.* | ||
class Fragment1 : Fragment() { | ||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { | ||
super.onViewCreated(view, savedInstanceState) | ||
StoreViewBinding.with(generateSampleRulesFactory<Any>()) | ||
.bind(this) | ||
} | ||
} | ||
""".trimIndent()) | ||
) | ||
.requireCompileSdk() | ||
.run() | ||
.expect(""" | ||
src/ru/test/app/Fragment1.kt:11: Error: Calling bind from onViewCreated instead of onCreate [ShouldBeCalledInOnCreate] | ||
StoreViewBinding.with(generateSampleRulesFactory<Any>()) | ||
^ | ||
1 errors, 0 warnings | ||
""".trimIndent()) | ||
} | ||
|
||
override fun getDetector() = WrongGenimiUsageDetector() | ||
|
||
override fun getIssues() = WrongGenimiUsageDetector.issues | ||
} |
80 changes: 80 additions & 0 deletions
80
gemini-lint/src/test/java/com/haroncode/gemini/lint/stubs.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
package com.haroncode.gemini.lint | ||
|
||
val BINDER_STUB = """ | ||
package com.haroncode.gemini.binder | ||
import com.haroncode.gemini.binder.rule.* | ||
object StoreViewBinding { | ||
fun <T : SavedStateRegistryOwner> with( | ||
lifecycleStrategy: LifecycleStrategy = StartStopStrategy, | ||
factoryProvider: () -> BindingRulesFactory<T>, | ||
): Binder<T> = SimpleBinder( | ||
factoryProvider = factoryProvider, | ||
lifecycleStrategy = lifecycleStrategy, | ||
) | ||
fun <T : SavedStateRegistryOwner> withRestore( | ||
lifecycleStrategy: LifecycleStrategy = StartStopStrategy, | ||
factoryProvider: () -> BindingRulesFactory<T>, | ||
): Binder<T> = RestoreBinder( | ||
factoryProvider = factoryProvider, | ||
lifecycleStrategy = lifecycleStrategy, | ||
) | ||
fun <T : LifecycleOwner> with( | ||
factory: BindingRulesFactory<T>, | ||
lifecycleStrategy: LifecycleStrategy = StartStopStrategy | ||
): Binder<T> = BinderImpl( | ||
factory = factory, | ||
lifecycleStrategy = lifecycleStrategy | ||
) | ||
} | ||
interface Binder<View> { | ||
fun bind(view: View) | ||
} | ||
internal class BinderImpl<View : LifecycleOwner>( | ||
private val factory: BindingRulesFactory<View>, | ||
private val lifecycleStrategy: LifecycleStrategy | ||
) : Binder<View> { | ||
override fun bind(view: View) { } | ||
} | ||
""".trimIndent() | ||
|
||
val BINDER_RULES = """ | ||
package com.haroncode.gemini.binder.rule | ||
interface BindingRulesFactory<P : Any> { | ||
fun create(param: P): Collection<BindingRule> | ||
} | ||
interface BindingRule { | ||
suspend fun bind() | ||
} | ||
fun <T : Any> generateSampleRulesFactory() = object : BindingRulesFactory<T> { | ||
override fun create(param: T): Collection<BindingRule> = emptyList() | ||
} | ||
""".trimIndent() | ||
|
||
val OTHER_BINDER_STUB = """ | ||
package ru.test.app | ||
object StoreViewBinding { | ||
fun <T : LifecycleOwner> with( | ||
factory: BindingRulesFactory<T>, | ||
lifecycleStrategy: LifecycleStrategy = StartStopStrategy | ||
): Binder<T> = BinderImpl( | ||
factory = factory, | ||
lifecycleStrategy = lifecycleStrategy | ||
) | ||
} | ||
interface Binder<View> { | ||
fun bind(view: View) | ||
} | ||
class BinderImpl<View> : Binder<View> { | ||
override fun bind(view: View) {} | ||
} | ||
""".trimIndent() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
include(":gemini-lint") | ||
include( | ||
":gemini-core", | ||
":gemini-core-test", | ||
|