Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Sign up for free
Menu
Search
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Features
All features
Private URLs
Password Protection
Custom URLS
Scheduled publishing
Remove Branding
Restrict embedding
Deck Collections
Notes
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Explore
Featured decks
Featured speakers
Programming
Technology
Storyboards
Pricing
Search
Sign in
Sign up for free
my_custom_lint
Search
takako-fire
March 27, 2019
Technology
410
2
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
my_custom_lint
takako-fire
March 27, 2019
More Decks by takako-fire
See All by takako-fire
mobile app development
takakofire
0
910
jetpack_benchmark.pdf
takakofire
2
150
livedata
takakofire
0
260
Other Decks in Technology
See All in Technology
Rustuna: Rustによる高速なOptuna実装
pfn
PRO
0
190
AI × アーキテクト 第1・2章|生成AIの課題とアーキテクトの素養
daidaidai
0
170
JAWS-UGグッズ職人仕事の流儀
kentapapa
0
100
その Valkey 使いこなしていますか?
onk
PRO
5
1.5k
オントロジーって 結局なにもの? AI エージェントに「意味」を渡す知識設計の話
greenmidori
0
200
⾮構造化データを価値に変える AI エージェント基盤とその組織
ivry_presentationmaterials
0
190
実践! SRE × AI - ABEMA における AIOps -
tetsuya28
8
3.1k
M5Stackで作る BLEマウス GPSログの世界
tomokusaba
0
420
JAWSUG TOHOKU 青森支部 「LoGoチャット」のAWS移行で 乗り越えたこと
taketakekaho
0
330
Running Kotlin + Quarkus with Lambda
nealle
1
3.7k
AI駆動で「他アプリの音を加工できる」アプリを作った
satomasahiro2005
0
150
Refining Active Record
shugo
0
160
Featured
See All Featured
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
3k
svc-hook: hooking system calls on ARM64 by binary rewriting
retrage
2
630
The Organizational Zoo: Understanding Human Behavior Agility Through Metaphoric Constructive Conversations (based on the works of Arthur Shelley, Ph.D)
kimpetersen
PRO
0
500
Un-Boring Meetings
codingconduct
0
460
YesSQL, Process and Tooling at Scale
rocio
174
15k
Ten Tips & Tricks for a 🌱 transition
stuffmc
1
270
Accessibility Awareness
sabderemane
1
250
The Power of CSS Pseudo Elements
geoffreycrofte
82
6.6k
The Web Performance Landscape in 2024 [PerfNow 2024]
tammyeverts
12
1.3k
Visualizing Your Data: Incorporating Mongo into Loggly Infrastructure
mongodb
50
10k
Building a A Zero-Code AI SEO Workflow
portentint
PRO
0
780
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
310
Transcript
զ͕ՈͷCustom Lint @Takako
ࣗݾհ
ࣗݾհ ɾTakako ɹɾTwitter: Takako(@kotokoto_fire) ɾAndroidΤϯδχΞ ɾmenusͱ͍͏ݙཱΞϓϦΛ࡞ͬͯ·͢
ͱ͋Δͷίϛοτ
ͱ͋Δͷίϛοτ
ϨϏϡʔͰݟ͚ͭΔͷ ͘͠ͳ͍ʁ
ͦ͏ͩʂ CustomLintͩʂ
Androidʹ͓͚ΔLint
Androidʹ͓͚ΔLint LintΛར༻͢ΔͱΞϓϦΛ࣮ߦͨ͠Γɺςε τέʔεΛ࡞ͨ͠Γ͠ͳͯ͘ɺίʔυߏ ͷ࣭ʹؔ͢ΔΛಛఆͯ͠मਖ਼͢Δ͜ ͱ͕Ͱ͖·͢ IUUQTEFWFMPQFSBOESPJEDPNTUVEJPXSJUFMJOU IMKB
Androidʹ͓͚ΔLint
Androidʹ͓͚ΔLint
LintʹRuleΛՃʁ
Lintͷछྨ
Lintͷछྨ ɾktlint ɾandroid-lint ɾ(detekt)
Lintͷछྨ ɾktlint ɾandroid-lint ɾ(detekt)
LintΛCustom͢Δ
LintΛCustom͢Δ ~ಋೖ~ <ktlint> https://github.com/pinterest/ktlint <android-lint> https://github.com/googlesamples/android- custom-lint-rules
LintΛCustom͢Δ ~AST~ fun sampel() { val a = b +
c }
զ͕ՈͷRule
Check onError in subscribeBy
Check onError in subscribeBy <త> ɾsubscribe͢Δͱ͖ʹonErrorΛΩϟον͍ͯ͠ͳ ͚Εܯࠂ <લఏ> ɾݱঢ়subscribeΛ࣮ߦ͢Δࡍ΄ͱΜͱ subscribeByͰ࣮ߦ͢Δ
Check onError in subscribeBy <త> ɾsubscribe͢Δͱ͖ʹonErrorΛΩϟον͍ͯ͠ͳ ͚Εܯࠂ <લఏ> ɾݱঢ়subscribeΛ࣮ߦ͢Δࡍ΄ͱΜͱ subscribeByͰ࣮ߦ͢Δ
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just("").subscribeBy {} } IUUQTHJUIVCDPNHPPHMFTBNQMFTBOESPJETVOqPXFS
Check onError in subscribeBy
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“”).subscribeBy {} } 1"$,"(&@%*3&$5*0/
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“").subscribeBy {} } ˡ1TJ8IJUF4QBDF
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“”).subscribeBy {} } ˢ*.1035@-*45
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“").subscribeBy {} } ˡ1TJ8IJUF4QBDF
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just("").subscribeBy {} } ˡ$-"44
Check onError in subscribeBy package com.google.samples.apps.sunflower import io.reactivex.Single class Sample
{ val sample = Single.just(“").subscribeBy {} } ˡ1TJ8IJUF4QBDF
Check onError in subscribeBy ˢ461&3@5:1&@-*45ʹFYUFOETJNQMFNFOUT͕ฒͿ
Check onError in subscribeBy TVCTDSJCF#ZҎ߱ͷUFYU͕ཏྻ͞Ε͍ͯΔ
Check onError in subscribeBy class Sample { val sample =
Single.just("").subscribeBy( onError = { } ) }
Check onError in subscribeBy ͜ͷลΓո͍͠
Check onError in subscribeBy
Check onError in subscribeBy Detector ɾDetector ɾResourceXmlDetector ɾLayoutDetector …. Scanner
ɾUastScanner ɾClassScanner ɾXmlScanner ….
Check onError in subscribeBy Detector ɾDetector ɾResourceXmlDetector ɾLayoutDetector …. Scanner
ɾUastScanner ɾClassScanner ɾXmlScanner ….
Check onError in subscribeBy class NotHandledOnError : Detector(), Detector.UastScanner {
override fun getApplicableUastTypes(): List<Class<out UElement>>? { return Collections.singletonList( UCallExpression::class.java ) } }
Check onError in subscribeBy class NotHandledOnError : Detector(), Detector.UastScanner {
override fun createUastHandler(context: JavaContext): UElementHandler? { return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { } } } }
Check onError in subscribeBy override fun createUastHandler(context: JavaContext): UElementHandler? {
return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if ( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } } } }
Check onError in subscribeBy override fun createUastHandler(context: JavaContext): UElementHandler? {
return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if ( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } } } }
Check onError in subscribeBy override fun createUastHandler(context: JavaContext): UElementHandler? {
return object : UElementHandler() { override fun visitCallExpression(node: UCallExpression) { if ( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } } } }
Check onError in subscribeBy override fun visitCallExpression(node: UCallExpression) { if
( node.sourcePsi?.context.toString() == "DOT_QUALIFIED_EXPRESSION" && node.methodName?.startsWith("subscribeBy") == true && node.valueArgumentCount > 0 && node.sourcePsi?.text?.contains("onError") == false ) { context.report( NotHandledOnError.ISSUE, node, context.getLocation(node), "Should handle onError" ) } }
Check onError in subscribeBy class NotHandledOnError : Detector(), Detector.UastScanner {
companion object { val ISSUE: Issue = Issue.create( "NotHandledOnError", "Not Handled onError", "Add onError to subscribeBy.", Category.CORRECTNESS, 6, Severity.ERROR, Implementation( NotHandledOnError::class.java, Scope.JAVA_FILE_SCOPE ) ) } }
Check onError in subscribeBy class CustomIssueRegistry : IssueRegistry() { override
val issues: List<Issue> get() { return Collections.singletonList( NotHandledOnError.ISSUE ) } }
Check onError in subscribeBy jar { manifest { attributes("Lint-Registry-v2": "com.example.custom_lint.CustomIssueRegistry")
} }
Is Drawable attr in TextView
Is Drawable attr in TextView <త> ɾTextViewʹdrawableXX͕͋Εwarning
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun getApplicableElements(): Collection<String>? { return Collections.singletonList(SdkConstants.TEXT_VIEW) } override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { // ͜͜ʹ֘࣌ͷॲཧ } } private fun getDrawableAttribute(element: Element): Attr? { return element.getAttributeNodeNS(SdkConstants.ANDROID_URI, "drawableEnd") ?: ུ } }
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
override fun visitElement( context: XmlContext, element: Element ) { val attrNode = getDrawableAttribute(element) if (attrNode != null) { context.report( ISSUE, attrNode, context.getLocation(attrNode), "If you use vector resource, ུ" ) } } }
Is Drawable attr in TextView
Is Drawable attr in TextView class NotUseDrawableInTextView : LayoutDetector() {
companion object { val ISSUE: Issue = Issue.create( "NotUseDrawableInTextView", "Not use drawable in TextView", "Not use drawable in TextView", Category.LINT, 6, Severity.WARNING, Implementation( NotUseDrawableInTextView::class.java, Scope.RESOURCE_FILE_SCOPE ) ) } }
PRͷվળ
PR্Ͱ…
PR্Ͱ…
·ͱΊ
·ͱΊ ɾLintΧελϚΠζͰ͖Δ ɾScanner, DetectorྟػԠมʹ͍͚Α͏ ɾDangerͳͲͱ࿈ܞ͢ΔͱɺPRΛϚʔδ͢Δ લʹbot͔ΒࢦఠͰ͖Δ
Let’s improve your code with CustomLint
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ