diff options
author | Treehugger Robot <treehugger-gerrit@google.com> | 2021-10-06 23:04:01 +0000 |
---|---|---|
committer | Gerrit Code Review <noreply-gerritcodereview@google.com> | 2021-10-06 23:04:01 +0000 |
commit | 62cc602c8f5e32e783bca74b2d29ef327162d08d (patch) | |
tree | 5a360b3f046d968d46ee4bc4ec8185ac0c2c8834 | |
parent | 6eef86645382785334e3c4a6146f95903e2d535c (diff) | |
parent | cfd2dede568a797f6300659227032614d10ec03b (diff) | |
download | support-62cc602c8f5e32e783bca74b2d29ef327162d08d.tar.gz |
Merge "Lint check to ensure that produceState calls assign `value`" into androidx-main
7 files changed, 452 insertions, 60 deletions
diff --git a/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt b/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt index 518d4a595a4..74299717738 100644 --- a/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt +++ b/compose/lint/common-test/src/main/java/androidx/compose/lint/test/Stubs.kt @@ -423,86 +423,184 @@ object Stubs { """ ) - val MutableState: TestFile = compiledStub( - filename = "MutableState.kt", + val SnapshotState: TestFile = compiledStub( + filename = "SnapshotState.kt", filepath = "androidx/compose/runtime", - checksum = 0xbd322309, + checksum = 0x57e9e684, source = """ package androidx.compose.runtime - fun <T> mutableStateOf(value: T) = MutableState<T>() + interface State<out T> { + val value: T + } + + interface MutableState<T> : State<T> { + override var value: T + } - class MutableState<T> + private class MutableStateImpl<T>(override var value: T) : MutableState<T> + + fun <T> mutableStateOf(value: T): MutableState<T> = MutableStateImpl(value) fun <T> mutableStateListOf() = SnapshotStateList<T>() class SnapshotStateList<T> fun <K, V> mutableStateMapOf() = SnapshotStateMap<K, V>() class SnapshotStateMap<K, V> + + @Composable + fun <T> produceState( + initialValue: T, + key1: Any?, + producer: suspend ProduceStateScope<T>.() -> Unit + ): State<T> { + return object : State<T> { + override val value = initialValue + } + } + + interface ProduceStateScope<T> : MutableState<T> { + suspend fun awaitDispose(onDispose: () -> Unit): Nothing + } """, """ META-INF/main.kotlin_module: - H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3ApcUlkZiXUpSfmVKhl5yfW5BfnKpX - VJpXkpmbKsTnW1qSmJSTGlySWJLqXcJlzqWIS61ecWJZKkitkFBQam5qblJq - UTBUBKiRl4ulJLW4RIjVLT/fu0SJQYsBAFB+NmSMAAAA + H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3Apc0lkZiXUpSfmVKhl5yfW5BfnKpX + VJpXkpmbKsQfnJdYUJyRXxJckliS6l3CpcYlg0uxXlp+vhBbSGpxiXeJEoMW + AwBiOobHbQAAAA== """, """ androidx/compose/runtime/MutableState.class: - H4sIAAAAAAAAAI1QTUsbQRh+ZjbZ6Jrq+tXG2tpT8ePgqghCK0JbKATWFpqQ - S06T7KBjkhnZmRWP+1v8B54ED7L06I8qfSd6KO2lc3je93nm4f16/HX/AOAQ - 7xjeC53lRmXXydBMLo2VSV5opyYyOS2cGIxlxwknG2AMW8fdD+mFuBLJWOiz - 5PvgQg7dx5N/JYb4b62BGkN4rLRyJwzB1naviRCNCHXMMNTcubIMm+l/DUP1 - F9ORcWOlk1PpRCacII1PrgLainmoM7ARSdfKsz3Ksn1aoCqbEW/xqCojHhNU - Zasqd2ozVRmzA77HP9d/3oQ8Drz/gEp0GdXDwp/Nd0eO5v1iMkkfqdLyWzEZ - yLzrDQxLqRmKcU/kyvNncbajzrRwRU551DFFPpRflf9Y+/G0XU9ZRc5PWhtq - oYy22Aen0/hHY/hLEa4RS6acFty5w+wtJRyvCcOpGGKdsPlkQIQ5igHeTF0B - 3k5jCxsUj8jTJM+LPoI25ttYIETsYbGNJSz3wSxWsNpHzWLO4qXFK4vGb2l1 - Fhc6AgAA + H4sIAAAAAAAAAIVRXWsTQRQ9dz+ymzTGbWw1jVqLICY+uG3xQUwpiCgGEoQm + hEKepskap9nslsxs6OP+lj70R/RBlj76o8S7qRQxVF/uvWfm3HNnzv3x8+o7 + gDfYIbwQ0Xgey/G5P4pnZ7EK/HkSaTkL/G6ixUkY9LTQgQMidA/67zqnYiH8 + UEQT/8vJaTDSrcPVo86dmkuxg36/ddgieH83OrAI2/9udlAguJNAD0SYBISN + RnP1AQS70eQpzFS3zM3GKrE5IBQazMyL9c401qGM/G6gxVhowf3GbGGyVZQH + m0BTPjqXOdrlarxH+JillZJRM0pZukyGa7tfa1n6ynKz1KN9t2pVjc+0axzV + PLNuvM3S4+vLyvVFoVy3XMuzn1tuwXNysX3Cy7ut+3Md/DDqE3b+Y3Tuw+Lm + 814vEmfqW6yXF6+nmlDsyUkkdDLn61IvTuaj4JMMGWwd3YgMpJI88X0Uxdwk + 40ix9QZsEBw2wOBluSgyepojlBivoXyL78H8XZnYXuYneMb5AzMqrHJ/CLMN + r411jqjm4UEbG9gcghQe4tGQt4eawpZCXeGxymFRYU2h/AtreVcywgIAAA== + """, + """ + androidx/compose/runtime/MutableStateImpl.class: + H4sIAAAAAAAAAI1RXU8TQRQ9M7vdLmspS/lG/EKRtoiLxAcDTY2aGJsUTWjT + GHka2g0MtLukMyU89lf4A/wFmmhMfDANj/4o4522IWpN7MPej7Pn3nPv3B8/ + v30H8BibDDkRNdqxbFwE9bh1FqswaHciLVthsNfR4rAZVrTQYal11kyCMVQL + 1Z3yiTgXQVNER8Gbw5OwrneLo1B5rL6FanW3uMvg/12fhM2wNlaPJBwGpyAj + qYsMc9nRWXI1ImRJygRWNldLwcU1DwmkGBLnotkJGTKjdSmkMTUBDp/B1sdS + MWyMt5d5L1rLPQp1bdB+NpsbFSD1bI7mIqa6YiYLFG8+pV2s/q/p8mmsmzIK + 9kItGkILwnjr3KILMmMSDOyUoAtpsi2KGo8Y3vW6aY8vcq/X7TvuOi5f7HXz + ttvr+mzbzdgZ/opt8eeTGce3lvmTXvfyg8N9e39lmL69fJ8myPe47y7bbsJ3 + Vm036dtGYZtEqwzr4z2HuXAlEmfqONZ94OGpZpioyKNI6E6blrZfxA1yU2UZ + ha87rcOwXTXV5ixxXTRroi1NPgS9Stxp18OX0iRL+wPFmlSS/j6LopgkZBwp + bNHtEqAnpY+bY5Jfp0fiWIJFsQtz3SwhRfKcvJf/isn8xhdMf+rzcmQdYgIp + 5MnOD1jIYAboR7939SiaxdywZ0CZqUzkP2P64z/bpQaEYbtBk3nCFq4G2xkO + 5vx3KOdqKAeLfwxlDSMLG31/Hw/IvyTGMmlfP4BVwkoJN8jipjG3SriNOwdg + Cqu4e4AJhRmFewprCmll0lmFOYUFhalf9MYJP00EAAA= + """, + """ + androidx/compose/runtime/ProduceStateScope.class: + H4sIAAAAAAAAAI1TW28SQRQ+M9wWpLrFG9Bq1WpUYty18UkI0WhIMVQbQV94 + GpYFB5YZsjOLfST+FB/8DcYHQ/DNH2U8C902Fmv7sOfMOec735nLt79+f/8B + AE9hm0CJia4veffAcuRoLJVr+YHQfORa+77sBo7b1Ey7TUeO3RQQAq1K61lj + wCbM8pjoW287A9fR5epqqnEq8V6gWcdbEldarXK1TMA82Z+COIF75+JIQZJA + ln1iXL/iKoThNh80hlJ7XFiDycjqBcLRXApl1Q5XdjmqO9KXgebCVdZLieQi + YCGg/HD1SAR6Z9FWovp7wfFazppSKVX/PehuQ/p9a+Dqjs84DmBCSM2Ww94E + nhceHmHb/4NJHSIRtR7tYs/VrMs0wxwdTWKoARKaBAEyxNQBDyMbV90neNbZ + dDND8zQzmx45k0QRNcOM0cvPpqW4MZuaZMfIxXN0l9j09ZYZK1I7vpM1E8VF + 1k7Zyd351+c/v5HZdP4lSU1j/pnGM9QohNN2CDw6XS8rQsTtkxaB++eTGKKB + QFqKI3Hkovs4VgMqsCnYWH2UetH0eKixp8n7gunAx56Nd0vquphwxZH7xfFV + 43OdrO4zn41c7fp/wTJNGfiOW+MeMhYOez6s8KGcKSRw06nwhfA/MCANMbiJ + EYUMbKFPYvUC+lv4rVEMsiF0YSNgDG4v/A24g76G1TUkvdiGWB0u1cFEC+uh + ydXhMlxpA1FwFa61Ia3guoK8goICQ0FRwYaCzcUi/QcmTVyUOwQAAA== """, """ - androidx/compose/runtime/MutableStateKt.class: - H4sIAAAAAAAAAKVUXU8TQRQ9s223ZUG6VBFaFBBEWr624MeDbUiMidq0hcQ2 - TQwPZtquuNDukp1pwyO/xV/gm4kmhvjojzLeaResVE2Jm+y9d+6cc+7e2b37 - /cfnrwAe4QnDKnebvuc0T62G1z7xhG35HVc6bdsqdySvt+yK5NIuyigYg3nE - u9xqcffQ2q8f2Q3Khhgm2wPI/XcMuXTpKjCXKY1UKMfwMl99OszfTVero4rk - CbpLSsslzz+0jmxZ97njCou7rkf7jkfxnif3Oq0WoVZG0oxijEHPO64jdxlC - 6UxtAuOYMGDgBkOky1sdmyEx/OCUHDyfkiOkOqOt9D+aqbj8RLz35CWDVAp/ - OZbryFwczNrolChuGZhWPU4NtlHmJ6qLzZHLE4EKv80X/9BE7b8aI+V8tZir - 1nqtZUYmRZEyMNfrrHTsyZbjWmVb8iaXnHS0djdEM8KUiTCwYxVolD91VJSl - qLnNcHp+ljLOzwxtVjO0mBb40MU6lTAJEEuEE9orLcuWwrHzM1NLzZuh1GQv - GelZPRvub0XIMVNPLZjRS1YsYO3o5liKEt8+6JppqPo7DBvX+4hYlWH9Wm+M - Femu0X9i5PmND663jiVD+LnXpOGIlxzX3uu067ZfVQA1Ll6Dt2rcd9Q6SI5V - nEOXy45P8dzrfo2C23WEQ9vPfo0wg1HxOn7DfuEoWjKA1oaA2IaGMNQVQhIR - 6OQtWu1QnpGPfYHx5hMmP6pXjCxZvZcPExOY6GMQh9njKEwUDwNUjHwSU0gE - mou0i77mNGneVppsQDMWKNy8ojCD2WGFOVK4c1VhPFBI/qag0e9c2S08Jl+i - 7F3qdP4AoQIWClgki3sFLGG5gPtYOQATeIDVA4wLTAmkBTICMwJrAusCGwIR - gU2BuID+E+SdSmEvBgAA + androidx/compose/runtime/SnapshotStateKt$produceState$1.class: + H4sIAAAAAAAAAI1S204TURRdZzq9MA5QKpeCgqhVp0UZqCRqCiSGSNJYMaGk + MeFp6AzlQHuGzJw2PPYr/AC/QBOJiQ+m4dGPMu4zbRQFwYfZe+2dfVl7zvr+ + 4+s3ACtYYXjmCDfwuXti1/3WsR96dtAWkrc8uyqc4/DAl1XpSO+1zB0Hvtuu + e1GYW06CUXPl0Ok4dtMRDfvt3qFXl6XKv+epxtWdndJ6iSH9d2MSOsPc1c1J + JBgSq1xwuc4wYV3cnq9RgUU7FBjOqUruNGtOs+0xZC7Wm7gBcwgahhliVr5m + IoVRA3GkGeId1WYi06+4yaDLAx4yvLjixiv/Gd2danhyQGfcyl8kRGutPPEn + OpEdqxz5ssmF/caTjutIh3JaqxOj52PKxBnYEaVOuIqWCLnLDGu97rDR6xpa + VjO0lF5gvW7KyPa6xVRGz2jPe90ltj2Z1mYUfHf2Xj/7kDAMLR2f0VOxtK6G + FBnmr3lKYmL9749I4iGDef5vMOxf8n6XZAb3H3Za9n5b1CX3RWhvDlCxlL+O + pQkLeRLcH4wWjyTDUJU3hCPbAZHRN3yX3GiFC2+r3drzgh1nrxmJxq8rBQVc + xYOkWRbCCzaaThh6JIjRV6Le9EMuGvRKB77LYFT9dlD3Nrmqnt7uE6rxkFP7 + SyF84qDuwDIJKw6GJPmMUhrhx/SQGmbSaaXN3zF9IHUqKT4htEVeZTKFhVOM + FL5grFD4jJFTjH+K6hfJjiBG029TzyzGyNuUm+x3YYIQInR+i0FoCtnBDpsi + pkRGk8c//hqbiJKz0TizXzAY1x8yTfESITVMi5ZM06GAjkcoDGpiKEZ+AU/J + r1HlLUV1F7EyZsuYI4s7ysyXcRf3dsFC3EduF4lQwQchMiGmQmRDmD8BgQM/ + sc8EAAA= + """, + """ + androidx/compose/runtime/SnapshotStateKt.class: + H4sIAAAAAAAAAKVVbU8bRxB+1u8cDhwmBDAtEALEvJ5xaNLGLm1KQ3AxL40d + VxGqqsM+yIF9Z92eEfmG+lP6C/otUSNVVvqtP6U/ours+UyMwcaoJ+3t7Owz + z+zszcz9/e8ffwJYxU8MMdUoWqZePFMKZrlick2xqoatlzUla6gV/sa0s7Zq + a1t2EIxBPlZPVaWkGkfK7sGxViCtl6GvXLXVg5LmIHcPGZKxTCswOZdp62m7 + yTzJ8CKVe3rVfi2Wy3VLkiLoGjE9yJjWkXKs2QeWqhtcUQ3DpH3dJHnHtHeq + pRKh5rriTJcrpSB6GAIp3dDtNYah66LMh9GLsAQJdxhmumIOop/Bf6qWqhpD + 5ConKZsvOKNzW1zycqzDbVz6eMKCWNJt7vU2NI2bne/eJIh7DN5Y/WZGJAxj + lGGgOaJttSICWur6JGRAZ/gltXVNPPn/FSMxp3JbyVzeibJ9ZrQaBTEuYUJE + Fq5YZrFaqEfGcHhNklyjOTHtkm4ox6dl5bBqFOopuuFKiU5536iaf9pXza39 + pZba+9trCi9bMCtusS01KAumZVZt3dC4sm6SiVF1Ci51AXhF1UMGC9cc9sY4 + G+k30x637qxFbhFuulMDoOp3YSH3m1khzDKMN12ObtiaZaglJW3YFjHoBR5E + jGq/8EYrnLg9ZE+11LJGQIaHnT93VpAcOV1iHgsS5rDI8KTbFjzdnFrTK0Es + S1BEnxnvfGtBrFBeiq6lq6V8vc/4TrS3pJ28KfOoVBuQbc1Wi6qtks5TPvXS + H4SJl5+BnQjBQ/ozXUhxkoorjPXWztek2rnkGfFInpDHnb0tMw2ZQA1cY8j1 + uXYejdB2NBTxRTybnjib8oVq57InOi57o32O0u+8A3FffctPE5MD0Qk5eGEV + cq0SAbknSoqPvwU8shT9We69gIQ/Qe4ISCIk90V9Iyzen1iS5ehciEWkSAMc + acjxu/HBSCDi4OIDgjY0tBn86z2rnTs+7n381eOjIEfFlSQYFm/Xs1mOYeFW + XZFt0chTJnb9v528ublA/JjcNGjOjQ7RXOkTyaZsa9MkCDLegDw/szWqN9No + +Mu9dTjkSzEvn9iUy+tmkVK6P0OEO9XygWblRHzizGZBZLyli7Wr7MnqR4Zq + Vy2Sx17WT5s2TnWu0/azTw2Cukfr7kWhX4KF04ahWesllXONllLWrFoFbUMX + zkZdivwVeqzAAx/E48Mo/AjAix1aPaaZbhzhD5Bez79DXw3y76K8sEvvgLPX + hz2BqOMwgAjNPzqYIF66qBDNoxjEXZd3knbFE/qA4dfvEBWcrIlTdhmGWhjG + 8NlVhglimGxlGHQZPm9huI8pilEw7BGTaBCRxcjD91j6AEVEGK8hcTnCAPnc + czw6aDxyIhTSKg3mSLP4gizqHh84HiWSpsX5aGRp9Ds9CjPOWyi9yDkwRm0T + ztFeOebbyNNcIf1j+hBP9uFN48s0vqI3nqaRRCqNr7G2D8bxDb7dxzDHIMcz + ju84xjjWOb7neM7h59jgGOAIcNzneMHxiGOVY5NjliPN8QPHlrPMcEz9B3Pl + RGuICwAA """, """ androidx/compose/runtime/SnapshotStateList.class: - H4sIAAAAAAAAAI1QTUsbQRh+ZjYfZo111bbGautVg7gqhUIrQhUKgbUFE3LJ - aZIddEwyIzuz4nF/i/+gp0IPZemxP6r0ndWTvTiH532fZx7erz9/f/4C8B7b - DF2h08yo9C6emPmNsTLOcu3UXMZ9LW7slXF9J5xMlHVNMIad48HH5Frcingm - 9GX8bXwtJ+7Tyf8SQ/RUa6LG0DhWWrkThmBnd9hGA80QdSww1NyVsgx7yfMn - oiYrydS4mdLxuXQiFU6Qxue3Ae3HPNQZ2JSkO+XZAWXpIW1RFu2Qd3hYFiGP - CMqiUxbd2kJZROyIH/DT+u/7Bo8C7z+iEgNG9bB8njsxnslqgP2po6HPTCrp - I1Fafs3nY5kNvIFhNTETMRuKTHn+KLb66lILl2eUh32TZxP5RfmPjYuHFYfK - KnJ+1tpQC2W0xSE43cc/GsOfi3CDWFxxWrD7A63vlHC8IWxUYgubhO0HA0Is - UgywVbkCvK1iB+8ofiBPmzxLIwQ9vOhhmRCRh5UeVrE2ArN4iVcj1CwWLV5b - rFs0/wH+GL11RAIAAA== + H4sIAAAAAAAAAI1QTWsbMRB90vojXrvNOm1SO/26uqZ0YxMoJCGQFgKGTQq1 + 8cUn2SscxbYUVnLwcX9L/0FOgRzC0mN/VMis00vbS3V4M/P0mHkzvx7u7gHs + 4z1DW+g4MSpehROzuDJWhslSO7WQYV+LK3thXN8JJyNlXRmMoXU0OIguxbUI + 50JPw2/jSzlxh8f/UgzB31wZBYbSkdLKHTN4rQ/DGkoo+yhig6HgLpRl+Bj9 + vyMaUo9mxs2VDs+kE7Fwgji+uPZoP5ZDkYHNiFqpvNqjLO7QFlla83mD+1nq + 84AgSxtZ2i5sZGnAunyPfyn+/FHigZfru9RiwKgfgj8cfJo5cv3VxJJhM1Ja + ni8XY5kMxHhOzFZkJmI+FInK699kpa+mWrhlQrnfN8tkIk9V/tH8/rTjUFlF + yhOtDY1QRlt0wOlA+SMf+b0Im1SF65o2bN+ickMJxy5haU3W8Zqw9iSAjypF + D2/WKg9v17GBdxQ/k6ZGmmcjeD0872GTEEEO9R628GIEZvES2yMULKoWOxav + LMqPlkzrHEUCAAA= """, """ androidx/compose/runtime/SnapshotStateMap.class: - H4sIAAAAAAAAAI1QTW8TMRB99uar29BuWz5SvsqRFoltK04lqgRISFE3IBG0 - l5ycrNW6Sexo7a163N/Sf8AJqYdqxZEfVXW8cAIOWJo3M2+exzP+eXt9A+AN - XjDsCp3lRmWX8dQslsbKOC+0UwsZj7RY2jPjRk44ORTLNhhDv39ylJyLCxHP - hT6NP0/O5dS9Tf/BHf9NMUR/cm00GFp9pZU7Zghe7qZdtNAO0USHoeHOlGV4 - lfz3kPTGRjIzbq50PJROZMIJ4vjiIqCNmYcmA5sRdal8tk9RdsAQV+VayHs8 - 5B2yqCrDquxV5V6jU5URI8cifsj3g/fNH1ctHjX8tUPqdEKWMmqN9WHhxGQu - 61FezxyN/8FkkgqJ0vJTsZjI/KsXMGwmZirmqciVz3+TKyN1qoUrcorDkSny - qfyofGH7y69lU2UVKd9pbegJZbTFATj9lD80hv84wseUxXVOu+59x8o3Cjie - ELZqsounNdYChFglH+BZrQrwvPbb2CF/RJouae6NEQywNsA6ISIPGwNsYmsM - ZnEfD8ZoWqxaPLR4ZNGzaN8Bn285EWQCAAA= + H4sIAAAAAAAAAI1QyU4bQRB91eONwYEBsphs5BiIlAGUE1hISaRIFkMixdFc + fGp7WtDY7ram24jjfAt/kFOkHKIRx3wUosbkkuWQlupV1evXtfTPm+8/ALzB + C8K2NFludXYZj+x0Zp2K87nxeqrivpEzd2Z930uvTuSsCSJ0u8cHybm8kPFE + mtP40/Bcjfxh+g/u6G+KEP3JNVEjNLraaH9ECF5up2000AxRR4tQ82faEV4l + /z0k91hLxtZPtIlPlJeZ9JI5Mb0IeGOqoE6gMVOXusp2Ocr2CHFZrISiI0LR + YovKIiyLTlns1FplERE7isS+2A3e1a+vGiKqVc/2udIxW0pcGtFvs7wee57/ + vc0UYTXRRn2cT4cq/yKHE2bWEzuSk1Tmusp/kUt9fWqkn+cch307z0fqg64u + Nj/fbZtqp1n51hjLLbQ1DnsQ/FXV4Tmqn2N8zFm8yHnZnW9Y+sqBwBPGxoK8 + j6eM7TsBQiyzD/BsoQrwfOE3scX+gDVt1twbIOhhpYdVRkQVrPWwjo0ByHG9 + BwPUHZYdHjo8cug4NG8BNQLEHmUCAAA= + """, + """ + androidx/compose/runtime/State.class: + H4sIAAAAAAAAAH1QwU7bQBB9Y8eOcQt1gJYQJMQxcKhDxAEBrdRLpUhBSEmE + kHJakiUscdZRdhNx9Ldw6Ef0gCyOfBTqOPRUKvbwZt6bndm38/zy+xHAEXYI + u0IPZ6ka3seDdDJNjYxnc23VRMZdK6wsgwj1s95J+04sRJwIPYovru/kwJ5+ + fysRon+1MkqEYCTtpUjmkrBZ3/9fn1ff7/U4Vtrj1CZKx+fSiqGwgjVnsnDZ + LhXgEWjM0r0qWIOz4SHhLM/WQqfqhHkWOlEBgRvcVPPswA/yLKI9ajoNp7Me + uTXnOM+unn6Vnh58v1YKSpFXzGgS9trvb4KNUI/YBrzF61eirhZTc5vaZf3r + 2BJWumqkhZ3PuBx20/lsIH+qhMl253XWpTLqOpE/tE65SaXa+Pw+PBSHeFU+ + eOWoMnMQwP2budhexi3UOH7jGyvcE/bhtvChhY+MWC1grYVPiPoggwrW+/AN + Ngw2DT4bfDEFLf8BwBF7S/4BAAA= """ ) } diff --git a/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt b/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt index 3facab48ea3..016e11264d4 100644 --- a/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt +++ b/compose/lint/common/src/main/java/androidx/compose/lint/Names.kt @@ -37,6 +37,7 @@ object Names { val MutableStateOf = Name(PackageName, "mutableStateOf") val MutableStateListOf = Name(PackageName, "mutableStateListOf") val MutableStateMapOf = Name(PackageName, "mutableStateMapOf") + val ProduceState = Name(PackageName, "produceState") val Remember = Name(PackageName, "remember") } object Ui { diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ProduceStateDetector.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ProduceStateDetector.kt new file mode 100644 index 00000000000..a18642d4397 --- /dev/null +++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/ProduceStateDetector.kt @@ -0,0 +1,144 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UnstableApiUsage") + +package androidx.compose.runtime.lint + +import androidx.compose.lint.Name +import androidx.compose.lint.Names +import androidx.compose.lint.inheritsFrom +import androidx.compose.lint.isInPackageName +import com.android.tools.lint.detector.api.Category +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Implementation +import com.android.tools.lint.detector.api.Issue +import com.android.tools.lint.detector.api.JavaContext +import com.android.tools.lint.detector.api.Scope +import com.android.tools.lint.detector.api.Severity +import com.android.tools.lint.detector.api.SourceCodeScanner +import com.intellij.psi.PsiMethod +import org.jetbrains.uast.UCallExpression +import org.jetbrains.uast.USimpleNameReferenceExpression +import org.jetbrains.uast.getParameterForArgument +import org.jetbrains.uast.tryResolve +import org.jetbrains.uast.visitor.AbstractUastVisitor +import java.util.EnumSet + +/** + * [Detector] that checks calls to produceState, to make sure that the producer lambda writes to + * MutableState#value. + * + * We also check to see if the lambda calls an external function that accepts a parameter of type + * ProduceStateScope / MutableState to avoid false positives in case there is a utility function + * that writes to MutableState#value. + */ +class ProduceStateDetector : Detector(), SourceCodeScanner { + override fun getApplicableMethodNames(): List<String> = + listOf(Names.Runtime.ProduceState.shortName) + + override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) { + if (method.isInPackageName(Names.Runtime.PackageName)) { + // The ProduceStateScope lambda + val producer = node.valueArguments.find { + node.getParameterForArgument(it)?.name == "producer" + } ?: return + + var referencesReceiver = false + var callsSetValue = false + + producer.accept(object : AbstractUastVisitor() { + val mutableStatePsiClass = + context.evaluator.findClass(Names.Runtime.MutableState.javaFqn) + + /** + * Visit function calls to see if the functions have a parameter of MutableState + * / ProduceStateScope. If they do, we cannot know for sure whether those + * functions internally call setValue, so we avoid reporting an error to avoid + * false positives. + */ + override fun visitCallExpression( + node: UCallExpression + ): Boolean { + val resolvedMethod = node.resolve() ?: return false + return resolvedMethod.parameterList.parameters.any { parameter -> + val type = parameter.type + + // Is the parameter type ProduceStateScope or a subclass + if (type.inheritsFrom(ProduceStateScopeName)) { + referencesReceiver = true + } + + // Is the parameter type MutableState + if (mutableStatePsiClass != null && + context.evaluator.getTypeClass(type) == mutableStatePsiClass) { + referencesReceiver = true + } + + referencesReceiver + } + } + + /** + * Visit any simple name reference expressions to see if there is a reference to + * `value` that resolves to a call to MutableState#setValue. + */ + override fun visitSimpleNameReferenceExpression( + node: USimpleNameReferenceExpression + ): Boolean { + if (node.identifier != "value") return false + val resolvedMethod = node.tryResolve() as? PsiMethod ?: return false + if (resolvedMethod.name == "setValue" && + resolvedMethod.containingClass?.inheritsFrom( + Names.Runtime.MutableState + ) == true + ) { + callsSetValue = true + } + return callsSetValue + } + }) + + if (!callsSetValue && !referencesReceiver) { + context.report( + ProduceStateDoesNotAssignValue, + node, + context.getNameLocation(node), + "produceState calls should assign `value` inside the producer lambda" + ) + } + } + } + + companion object { + val ProduceStateDoesNotAssignValue = Issue.create( + "ProduceStateDoesNotAssignValue", + "produceState calls should assign `value` inside the producer lambda", + "produceState returns an observable State using values assigned inside the producer " + + "lambda. If the lambda never assigns (i.e `value = foo`), then the State will " + + "never change. Make sure to assign a value when the source you are producing " + + "values from changes / emits a new value. For sample usage see the produceState " + + "documentation.", + Category.CORRECTNESS, 3, Severity.ERROR, + Implementation( + ProduceStateDetector::class.java, + EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES) + ) + ) + } +} + +private val ProduceStateScopeName = Name(Names.Runtime.PackageName, "ProduceStateScope") diff --git a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt index 76a858fac6f..5473733466c 100644 --- a/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt +++ b/compose/runtime/runtime-lint/src/main/java/androidx/compose/runtime/lint/RuntimeIssueRegistry.kt @@ -37,6 +37,7 @@ class RuntimeIssueRegistry : IssueRegistry() { ComposableNamingDetector.ComposableNaming, ComposableStateFlowValueDetector.StateFlowValueCalledInComposition, CompositionLocalNamingDetector.CompositionLocalNaming, + ProduceStateDetector.ProduceStateDoesNotAssignValue, RememberDetector.RememberReturnType, UnrememberedMutableStateDetector.UnrememberedMutableState ) diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ProduceStateDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ProduceStateDetectorTest.kt new file mode 100644 index 00000000000..e83b2b9653b --- /dev/null +++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/ProduceStateDetectorTest.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UnstableApiUsage") + +package androidx.compose.runtime.lint + +import androidx.compose.lint.test.Stubs +import com.android.tools.lint.checks.infrastructure.LintDetectorTest +import com.android.tools.lint.checks.infrastructure.TestMode +import com.android.tools.lint.detector.api.Detector +import com.android.tools.lint.detector.api.Issue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +/* ktlint-disable max-line-length */ +@RunWith(JUnit4::class) + +/** + * Test for [ProduceStateDetector]. + */ +class ProduceStateDetectorTest : LintDetectorTest() { + override fun getDetector(): Detector = ProduceStateDetector() + + override fun getIssues(): MutableList<Issue> = + mutableListOf(ProduceStateDetector.ProduceStateDoesNotAssignValue) + + @Test + fun errors() { + lint().files( + kotlin( + """ + package androidx.compose.runtime.foo + + import androidx.compose.runtime.* + + @Composable + fun Test() { + produceState(true, true) { + // Reading, not assigning the value, so this should be an error + val foo = value + } + produceState(true, true) { + // This method is a member of ProduceStateScope, so we know that it isn't + // going to assign value for us + awaitDispose { } + } + produceState(true, true) { + // Receiver type of State, so assigning value is not possible + doSomethingWithState() + } + produceState(true, true) { + // Parameter type of State, so assigning value is not possible + doSomethingElseWithState(this) + } + } + + fun <T> State<T>.doSomethingWithState() {} + + fun <T> doSomethingElseWithState(state: State<T>) {} + """ + ), + Stubs.Composable, + Stubs.SnapshotState + ) + .skipTestModes(TestMode.TYPE_ALIAS) + .run() + .expect( + """ +src/androidx/compose/runtime/foo/test.kt:8: Error: produceState calls should assign value inside the producer lambda [ProduceStateDoesNotAssignValue] + produceState(true, true) { + ~~~~~~~~~~~~ +src/androidx/compose/runtime/foo/test.kt:12: Error: produceState calls should assign value inside the producer lambda [ProduceStateDoesNotAssignValue] + produceState(true, true) { + ~~~~~~~~~~~~ +src/androidx/compose/runtime/foo/test.kt:17: Error: produceState calls should assign value inside the producer lambda [ProduceStateDoesNotAssignValue] + produceState(true, true) { + ~~~~~~~~~~~~ +src/androidx/compose/runtime/foo/test.kt:21: Error: produceState calls should assign value inside the producer lambda [ProduceStateDoesNotAssignValue] + produceState(true, true) { + ~~~~~~~~~~~~ +4 errors, 0 warnings + """ + ) + } + + @Test + fun noErrors() { + lint().files( + kotlin( + """ + package androidx.compose.runtime.foo + + import androidx.compose.runtime.* + + @Composable + fun Test() { + produceState(true, true) { + value = true + } + produceState(true, true) { + this.value = true + } + produceState(true, true) { + doSomethingWithScope() + } + produceState(true, true) { + doSomethingElseWithScope(this) + } + produceState(true, true) { + doSomethingWithState() + } + produceState(true, true) { + doSomethingElseWithState(this) + } + } + + fun <T> MutableState<T>.doSomethingWithState() {} + + fun <T> doSomethingElseWithState(state: MutableState<T>) {} + + fun <T> ProduceStateScope<T>.doSomethingWithScope() {} + + fun <T> doSomethingElseWithScope(scope: ProduceStateScope<T>) {} + """ + ), + Stubs.Composable, + Stubs.SnapshotState + ) + .run() + .expectClean() + } +} +/* ktlint-enable max-line-length */ diff --git a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedMutableStateDetectorTest.kt b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedMutableStateDetectorTest.kt index 422709f9758..02b45aa9b03 100644 --- a/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedMutableStateDetectorTest.kt +++ b/compose/runtime/runtime-lint/src/test/java/androidx/compose/runtime/lint/UnrememberedMutableStateDetectorTest.kt @@ -109,7 +109,7 @@ class UnrememberedMutableStateDetectorTest : LintDetectorTest() { """ ), Stubs.Composable, - Stubs.MutableState, + Stubs.SnapshotState, Stubs.Remember ) .skipTestModes(TestMode.TYPE_ALIAS) @@ -275,7 +275,7 @@ src/androidx/compose/runtime/foo/{.kt:61: Error: Creating a state object during """ ), Stubs.Composable, - Stubs.MutableState, + Stubs.SnapshotState, Stubs.Remember ) .run() @@ -363,7 +363,7 @@ src/androidx/compose/runtime/foo/{.kt:61: Error: Creating a state object during """ ), Stubs.Composable, - Stubs.MutableState, + Stubs.SnapshotState, Stubs.Remember ) .run() diff --git a/compose/runtime/runtime-saveable-lint/src/test/java/androidx/compose/runtime/saveable/lint/RememberSaveableDetectorTest.kt b/compose/runtime/runtime-saveable-lint/src/test/java/androidx/compose/runtime/saveable/lint/RememberSaveableDetectorTest.kt index b5ea2e67e79..ffb6fa2b387 100644 --- a/compose/runtime/runtime-saveable-lint/src/test/java/androidx/compose/runtime/saveable/lint/RememberSaveableDetectorTest.kt +++ b/compose/runtime/runtime-saveable-lint/src/test/java/androidx/compose/runtime/saveable/lint/RememberSaveableDetectorTest.kt @@ -162,7 +162,7 @@ class RememberSaveableDetectorTest : LintDetectorTest() { ), rememberSaveableStub, Stubs.Composable, - Stubs.MutableState + Stubs.SnapshotState ) .run() .expect( @@ -276,7 +276,7 @@ Fix for src/test/Foo.kt line 22: Change to `stateSaver = fooSaver4`: ), rememberSaveableStub, Stubs.Composable, - Stubs.MutableState + Stubs.SnapshotState ) .run() .expectClean() |