Add lint rule to remember getBackstackEntry()
You should always remember the value returned from
navController.getBackStackEntry() when using compose.
Test: added test
Bug: 197675882
Change-Id: I92249572af057ce5969f734cf3db2ca1f6606323
diff --git a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/libraries/Libraries.kt b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/libraries/Libraries.kt
index f160221..89e8c65 100644
--- a/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/libraries/Libraries.kt
+++ b/compose/integration-tests/docs-snippets/src/main/java/androidx/compose/integration/docs/libraries/Libraries.kt
@@ -35,6 +35,7 @@
import androidx.compose.runtime.getValue
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
@@ -138,9 +139,8 @@
navigation(startDestination = innerStartRoute, route = "Parent") {
// ...
composable("exampleWithRoute") { backStackEntry ->
- val parentViewModel = hiltViewModel<ParentViewModel>(
- navController.getBackStackEntry("Parent")
- )
+ val parentEntry = remember { navController.getBackStackEntry("Parent") }
+ val parentViewModel = hiltViewModel<ParentViewModel>(parentEntry)
ExampleWithRouteScreen(parentViewModel)
}
}
diff --git a/hilt/hilt-navigation-compose/samples/src/main/java/androidx/hilt/navigation/compose/samples/HiltViewModelSamples.kt b/hilt/hilt-navigation-compose/samples/src/main/java/androidx/hilt/navigation/compose/samples/HiltViewModelSamples.kt
index e52fb79..30b2111 100644
--- a/hilt/hilt-navigation-compose/samples/src/main/java/androidx/hilt/navigation/compose/samples/HiltViewModelSamples.kt
+++ b/hilt/hilt-navigation-compose/samples/src/main/java/androidx/hilt/navigation/compose/samples/HiltViewModelSamples.kt
@@ -20,6 +20,7 @@
import androidx.annotation.Sampled
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.remember
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.ViewModel
import androidx.navigation.compose.NavHost
@@ -47,14 +48,12 @@
NavHost(navController, startDestination = "Parent") {
navigation(startDestination = "InnerRouteA", route = "Parent") {
composable("InnerRouteA") {
- val viewModel = hiltViewModel<ParentViewModel>(
- navController.getBackStackEntry("Parent")
- )
+ val parentEntry = remember { navController.getBackStackEntry("Parent") }
+ val viewModel = hiltViewModel<ParentViewModel>(parentEntry)
}
composable("InnerRouteB") {
- val viewModel = hiltViewModel<ParentViewModel>(
- navController.getBackStackEntry("Parent")
- )
+ val parentEntry = remember { navController.getBackStackEntry("Parent") }
+ val viewModel = hiltViewModel<ParentViewModel>(parentEntry)
}
}
}
diff --git a/navigation/navigation-compose-lint/build.gradle b/navigation/navigation-compose-lint/build.gradle
new file mode 100644
index 0000000..aacd3fdd
--- /dev/null
+++ b/navigation/navigation-compose-lint/build.gradle
@@ -0,0 +1,49 @@
+/*
+ * 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.
+ */
+
+import androidx.build.BundleInsideHelper
+import androidx.build.LibraryGroups
+import androidx.build.LibraryType
+
+plugins {
+ id("AndroidXPlugin")
+ id("kotlin")
+}
+
+BundleInsideHelper.forInsideLintJar(project)
+
+dependencies {
+ compileOnly(libs.androidLintMinComposeApi)
+ compileOnly(libs.kotlinStdlib)
+ bundleInside(projectOrArtifact(":compose:lint:common"))
+
+ testImplementation(projectOrArtifact(":compose:lint:common-test"))
+ testImplementation(libs.kotlinStdlib)
+ testImplementation(libs.kotlinReflect)
+ testImplementation(libs.kotlinStdlibJdk8)
+ testImplementation(libs.androidLint)
+ testImplementation(libs.androidLintTests)
+ testImplementation(libs.junit)
+ testImplementation(libs.truth)
+}
+
+androidx {
+ name = "Navigation Compose Lint"
+ type = LibraryType.LINT
+ mavenGroup = LibraryGroups.NAVIGATION
+ inceptionYear = "2021"
+ description = "Lint checks for Navigation Compose"
+}
diff --git a/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/NavigationComposeIssueRegistry.kt b/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/NavigationComposeIssueRegistry.kt
new file mode 100644
index 0000000..92e1225
--- /dev/null
+++ b/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/NavigationComposeIssueRegistry.kt
@@ -0,0 +1,39 @@
+/*
+ * 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.navigation.compose.lint
+
+import com.android.tools.lint.client.api.IssueRegistry
+import com.android.tools.lint.client.api.Vendor
+import com.android.tools.lint.detector.api.CURRENT_API
+
+/**
+ * [IssueRegistry] containing runtime specific lint issues.
+ */
+class NavigationComposeIssueRegistry : IssueRegistry() {
+ // Tests are run with this version. We ensure that with ApiLintVersionsTest
+ override val api = 11
+ override val minApi = CURRENT_API
+ override val issues get() = listOf(
+ UnrememberedGetBackStackEntryDetector.UnrememberedGetBackStackEntry
+ )
+ override val vendor = Vendor(
+ vendorName = "Jetpack Navigation Compose",
+ identifier = "androidx.navigation.compose"
+ )
+}
\ No newline at end of file
diff --git a/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/UnrememberedGetBackStackEntryDetector.kt b/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/UnrememberedGetBackStackEntryDetector.kt
new file mode 100644
index 0000000..00a0b44
--- /dev/null
+++ b/navigation/navigation-compose-lint/src/main/java/androidx/navigation/compose/lint/UnrememberedGetBackStackEntryDetector.kt
@@ -0,0 +1,77 @@
+/*
+ * 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.navigation.compose.lint
+
+import androidx.compose.lint.Name
+import androidx.compose.lint.Package
+import androidx.compose.lint.invokedInComposableBodyAndNotRemembered
+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 java.util.EnumSet
+
+/**
+ * [Detector] that checks `getBackStackEntry` calls to make sure that if they are called inside a
+ * Composable body, they are `remember`ed.
+ */
+class UnrememberedGetBackStackEntryDetector : Detector(), SourceCodeScanner {
+ override fun getApplicableMethodNames(): List<String> = listOf(
+ GetBackStackEntry.shortName
+ )
+
+ override fun visitMethodCall(context: JavaContext, node: UCallExpression, method: PsiMethod) {
+ if (!method.isInPackageName(PackageName)) return
+
+ if (node.invokedInComposableBodyAndNotRemembered()) {
+ context.report(
+ UnrememberedGetBackStackEntry,
+ node,
+ context.getNameLocation(node),
+ "Calling getBackStackEntry during composition without using `remember`"
+ )
+ }
+ }
+
+ companion object {
+ val UnrememberedGetBackStackEntry = Issue.create(
+ "UnrememberedGetBackStackEntry",
+ "Calling getBackStackEntry during composition with using `remember`",
+ "Backstack entries retrieved during composition need to be `remember`ed, otherwise " +
+ "they will be retrieved from the navController again, and be changed. Either " +
+ "hoist the state to an object that is not created during composition, or wrap " +
+ "the state in a call to `remember`.",
+ Category.CORRECTNESS, 3, Severity.ERROR,
+ Implementation(
+ UnrememberedGetBackStackEntryDetector::class.java,
+ EnumSet.of(Scope.JAVA_FILE, Scope.TEST_SOURCES)
+ )
+ )
+ }
+}
+
+private val PackageName = Package("androidx.navigation")
+private val GetBackStackEntry = Name(PackageName, "getBackStackEntry")
diff --git a/navigation/navigation-compose-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry b/navigation/navigation-compose-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
new file mode 100644
index 0000000..796abf7
--- /dev/null
+++ b/navigation/navigation-compose-lint/src/main/resources/META-INF/services/com.android.tools.lint.client.api.IssueRegistry
@@ -0,0 +1 @@
+androidx.navigation.compose.lint.NavigationComposeIssueRegistry
diff --git a/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/Stubs.kt b/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/Stubs.kt
new file mode 100644
index 0000000..984a6000
--- /dev/null
+++ b/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/Stubs.kt
@@ -0,0 +1,82 @@
+/*
+ * 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.
+ */
+
+package androidx.navigation.compose.lint
+
+import androidx.compose.lint.test.compiledStub
+
+internal val NAV_BACK_STACK_ENTRY = compiledStub(
+ filename = "NavBackStackEntry.kt",
+ filepath = "androidx/navigation",
+ checksum = 0x6920c3ac,
+ source = """
+ package androidx.navigation
+
+ public class NavBackStackEntry
+""",
+ """
+ META-INF/main.kotlin_module:
+ H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3AJcTFnZyfq5dakZhbkJMqxBzvXaLE
+ oMUAAMRK5d0sAAAA
+ """,
+ """
+ androidx/navigation/NavBackStackEntry.class:
+ H4sIAAAAAAAAAI2Ru04CQRSG/zPAoisKKiqosTNeCleMncZEjCYkiIkYGqqB
+ 3eBwmU12hw12PItvYGViYYilD2U8u9rZOMWX8/9nMucyn19v7wBOsU3YldoN
+ fOVOHC0j1ZNG+dppyKgqu4OmYVxrEzxlQYRCX0bSGUrdc+46fa9rskgRrHOl
+ lbkgpPYPWjlkYNlII0tIm0cVEvbq/6pwRliuD3wzVNq59Yx0pZHsiVGU4lYp
+ RoZAA7YmKlbHHLkVws5satuiJGxR4Gg2Lc2mJ+KYqpmPZ0sURHzrhPgFFP/U
+ PBoYbvPKdz1Cvq601xiPOl7wIDtDdlbqflcOWzJQsf417aY/DrrejYpF+X6s
+ jRp5LRUqzl5q7ZtkvBAVCN5CfLjpeCnMDVZOonmWw1fMvXAgUGJaiZlGmZn7
+ uYB52El+M+E6tpIvIyxwLtdGqobFGpaYyMco1LCMlTYoxCqKnA9hh1gLYX0D
+ +QLjIO8BAAA=
+ """
+)
+
+internal val NAV_CONTROLLER = compiledStub(
+ filename = "NavController.kt",
+ filepath = "androidx/navigation",
+ checksum = 0xa6eda16e,
+ source = """
+ package androidx.navigation
+
+ public class NavController {
+ public fun getBackStackEntry(route: String) = NavBackStackEntry()
+ }
+""",
+ """
+ META-INF/main.kotlin_module:
+ H4sIAAAAAAAAAGNgYGBmYGBgBGJWKM3AJcTFnZyfq5dakZhbkJMqxBzvXaLE
+ oMUAAMRK5d0sAAAA
+ """,
+ """
+ androidx/navigation/NavController.class:
+ H4sIAAAAAAAAAI1SW08TQRT+Ztru4oJlQbkrioDclAXikzVGIZqU1GrEkBie
+ pttJmXY7m+xOG3zjt/gL9AmjiSE++qOMZ8pGrGhkkz1nzjnf+WbO5fuPz18B
+ PMA6w5zQ9SRW9aNAi65qCKNiHVRFdyfWJomjSCYuGIPfFF0RREI3gpe1pgyN
+ ixyD80hpZR4z5JZX9odQgOMhD5chbw5VyjBf+S97iWGkIc22CFt7hsQzCrxj
+ KC1Xzm/cM4nSjdLKv9j6k0v23jhpBE1paolQOg2E1rHpwdOgGptqJ4oIVUji
+ jpEDKDLMtmITKR00u+1AaSMTLaKgrO29qQpTFz7DWHgow1aW/kokoi0JyLD0
+ +1PPmlP6y+OpP6O45mEE1xkWL1WJi3EPE7afoxcJqW+V7NUvpBF1YQT5eLub
+ o9EyKwoMrEWuI2WtDTrVNxkenh6Pe3ySe9w/Pfb4AO8Z9ugXJ0+Pt/gG2y58
+ e+9wn+8W/dw038hvOX6BtGMZthixY+nSo/D7pr3eMrQcO3FdMgxXlJbVTrsm
+ kzeiFklbZRyKaF8kytqZc+Z1RxvVlmXdVaki19PzYTIs/Bn9NZg+mLcXd5JQ
+ PleWcSrL2b/Ah01wWmD7caqS9tnWSlZAmtmWrp5g4GMvvEzS6TnzWCE5dAbA
+ FXikRzBInlwveZvQnHRxbXT4E8a+YOLtCSY/9LE4lGlZxs+QGYs9FTFF8dUM
+ d5X0Gv0uywyOez15F/dJPyHvNFHNHCBXxo0ybpLErBW3yriNuQOwFHcwfwA3
+ hZdiIYWTYjDFYooimT8B9qxp7xsEAAA=
+ """
+)
diff --git a/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/UnrememberedGetBackStackEntryDetectorTest.kt b/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/UnrememberedGetBackStackEntryDetectorTest.kt
new file mode 100644
index 0000000..defabf5
--- /dev/null
+++ b/navigation/navigation-compose-lint/src/test/java/androidx/navigation/compose/lint/UnrememberedGetBackStackEntryDetectorTest.kt
@@ -0,0 +1,281 @@
+/*
+ * 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.navigation.compose.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 [UnrememberedGetBackStackEntryDetector].
+ */
+class UnrememberedGetBackStackEntryDetectorTest : LintDetectorTest() {
+ override fun getDetector(): Detector = UnrememberedGetBackStackEntryDetector()
+
+ override fun getIssues(): MutableList<Issue> =
+ mutableListOf(UnrememberedGetBackStackEntryDetector.UnrememberedGetBackStackEntry)
+
+ @Test
+ fun notRemembered() {
+ lint().files(
+ kotlin(
+ """
+ package com.example
+
+ import androidx.compose.runtime.*
+ import androidx.navigation.NavController
+
+ @Composable
+ fun Test() {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ val lambda = @Composable {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ val lambda2: @Composable () -> Unit = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ @Composable
+ fun LambdaParameter(content: @Composable () -> Unit) {}
+
+ @Composable
+ fun Test2() {
+ LambdaParameter(content = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ })
+ LambdaParameter {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+ }
+
+ fun test3() {
+ val localLambda1 = @Composable {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ val localLambda2: @Composable () -> Unit = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+ }
+
+ @Composable
+ fun Test4() {
+ val localObject = object {
+ val navController = NavController()
+ val entry = navController.getBackStackEntry("test")
+ }
+ }
+ """
+ ),
+ Stubs.Composable,
+ NAV_CONTROLLER
+ )
+ .skipTestModes(TestMode.TYPE_ALIAS)
+ .run()
+ .expect(
+ """
+src/com/example/{.kt:10: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:15: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:20: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:30: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:34: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:41: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:46: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+src/com/example/{.kt:54: Error: Calling getBackStackEntry during composition without using remember [UnrememberedGetBackStackEntry]
+ val entry = navController.getBackStackEntry("test")
+ ~~~~~~~~~~~~~~~~~
+8 errors, 0 warnings
+ """
+ )
+ }
+
+ @Test
+ fun rememberedInsideComposableBody() {
+ lint().files(
+ kotlin(
+ """
+ package com.example
+
+ import androidx.compose.runtime.*
+ import androidx.navigation.NavController
+
+ @Composable
+ fun Test() {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ }
+
+ val lambda = @Composable {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ }
+
+ val lambda2: @Composable () -> Unit = {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ }
+
+ @Composable
+ fun LambdaParameter(content: @Composable () -> Unit) {}
+
+ @Composable
+ fun Test2() {
+ LambdaParameter(content = {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ })
+ LambdaParameter {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ }
+ }
+
+ fun test3() {
+ val localLambda1 = @Composable {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ }
+
+ val localLambda2: @Composable () -> Unit = {
+ val navController = NavController()
+ val entry = remember { navController.getBackStackEntry("test") }
+ }
+ }
+ """
+ ),
+ Stubs.Composable,
+ Stubs.Remember,
+ NAV_CONTROLLER
+ )
+ .run()
+ .expectClean()
+ }
+
+ @Test
+ fun noErrors() {
+ lint().files(
+ kotlin(
+ """
+ package com.example
+
+ import androidx.compose.runtime.*
+ import androidx.navigation.NavController
+
+ fun test() {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ val lambda = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ val lambda2: () -> Unit = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ fun LambdaParameter(content: () -> Unit) {}
+
+ fun test2() {
+ LambdaParameter(content = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ })
+ LambdaParameter {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+ }
+
+ fun test3() {
+ val localLambda1 = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+
+ val localLambda2: () -> Unit = {
+ val navController = NavController()
+ navController.getBackStackEntry("test")
+ }
+ }
+
+ fun test3() {
+ class Foo {
+ val navController = NavController()
+ val entry = navController.getBackStackEntry("test")
+ }
+
+ val localObject = object {
+ val navController = NavController()
+ val entry = navController.getBackStackEntry("test")
+ }
+ }
+
+ @Composable
+ fun Test4() {
+ class Foo {
+ val navController = NavController()
+ val entry = navController.getBackStackEntry("test")
+ }
+ }
+ """
+ ),
+ Stubs.Composable,
+ Stubs.Remember,
+ NAV_CONTROLLER
+ )
+ .run()
+ .expectClean()
+ }
+}
+/* ktlint-enable max-line-length */
diff --git a/navigation/navigation-compose/build.gradle b/navigation/navigation-compose/build.gradle
index 6448a7e..12e1b2f 100644
--- a/navigation/navigation-compose/build.gradle
+++ b/navigation/navigation-compose/build.gradle
@@ -48,6 +48,9 @@
androidTestImplementation(libs.testRunner)
androidTestImplementation(libs.junit)
androidTestImplementation(libs.truth)
+
+ lintChecks(projectOrArtifact(":navigation:navigation-compose-lint"))
+ lintPublish(projectOrArtifact(":navigation:navigation-compose-lint"))
}
androidx {
diff --git a/settings.gradle b/settings.gradle
index 12807502..538a36a 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -522,6 +522,7 @@
includeProject(":navigation:navigation-compose", "navigation/navigation-compose", [BuildType.COMPOSE])
includeProject(":navigation:navigation-compose:navigation-compose-samples", "navigation/navigation-compose/samples", [BuildType.COMPOSE])
includeProject(":navigation:navigation-compose:integration-tests:navigation-demos", "navigation/navigation-compose/integration-tests/navigation-demos", [BuildType.COMPOSE])
+includeProject(":navigation:navigation-compose-lint", "navigation/navigation-compose-lint", [BuildType.COMPOSE])
includeProject(":navigation:navigation-dynamic-features-fragment", "navigation/navigation-dynamic-features-fragment", [BuildType.MAIN, BuildType.FLAN])
includeProject(":navigation:navigation-dynamic-features-runtime", "navigation/navigation-dynamic-features-runtime", [BuildType.MAIN, BuildType.FLAN])
includeProject(":navigation:navigation-fragment", "navigation/navigation-fragment", [BuildType.MAIN, BuildType.FLAN])