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
900
jetpack_benchmark.pdf
takakofire
2
150
livedata
takakofire
0
260
Other Decks in Technology
See All in Technology
synctest時代のhttptest Go 1.27で変わるHTTPサーバテストの裏側 / go conference2026 synctest and httptest
budougumi0617
1
3k
あるけみー式LTスライド作成術
alchemy1115
2
220
HHKBエバンジェリストになる方法
941
0
110
登壇の自信を奪う3匹のオバケ / 3 Ghosts That Rob You of Your Confidence in Public Speaking
pauli
9
960
研究開発部の紹介 / Sansan R&D Profile
sansan33
PRO
5
25k
【技術的負債conf】事業成長に伴う技術的負債の説明責任とAIによるモニタリング、認知的負債について
i35_267
3
1.6k
白金鉱業Meetup Vol.25 アウトカムが二値のデータに対するCausal Impact
brainpadpr
0
210
What the customer really needed
kawaguti
PRO
3
190
山手線を徒歩で一周してわかった、 位置情報アプリは「足」が最強のデバッガー
hinakko
0
160
AIエージェントの自己改善をどう設計するか / How to Design Self-Improvement for AI Agents
22mi
25
16k
現場で役立つ技術負債の効果的な返済方法
masuda220
PRO
8
4.2k
アプリログインとWeb認証基盤をつなぐ ASWebAuthenticationSession 作法
shimastripe
1
320
Featured
See All Featured
Agile Leadership in an Agile Organization
kimpetersen
PRO
0
230
Future Trends and Review - Lecture 12 - Web Technologies (1019888BNR)
signer
PRO
0
3.7k
Imperfection Machines: The Place of Print at Facebook
scottboms
270
14k
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Build your cross-platform service in a week with App Engine
jlugia
234
19k
Tips & Tricks on How to Get Your First Job In Tech
honzajavorek
1
740
Leading Effective Engineering Teams in the AI Era
addyosmani
9
2.6k
The Mindset for Success: Future Career Progression
greggifford
PRO
0
490
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
10
1.3k
Into the Great Unknown - MozCon
thekraken
41
2.7k
How to Grow Your eCommerce with AI & Automation
katarinadahlin
PRO
2
280
<Decoding/> the Language of Devs - We Love SEO 2024
nikkihalliwell
1
330
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
͝੩ௌ ͋Γ͕ͱ͏͍͟͝·ͨ͠ʂ