Skip to content

Conversation

@joshfriend
Copy link
Contributor

Proposed Changes

Add new issue ID for overly broad capture of configuration cache inputs based on the Gradle docs

I had to rework most of the bad method detection because the existing implementation assumes that any occurrence of a given method should be blocked regardless of arity. System.getenv() is a new case where the no-arg version is bad but System.getenv(String) is fine. Those methods are also static and the existing code was not resolving them.

Testing

Test: Added new integration tests for the new lint issue. All existing tests pass with no additional changes.

Issues Fixed

Fixes: https://issuetracker.google.com/issues/462234633

@joshfriend joshfriend requested a review from dlam as a code owner November 21, 2025 16:35
// A map from eager method name to the containing class of the method and the name of the
// replacement method, if there is a direct equivalent.
private val REPLACEMENTS =
private val REPLACEMENTS: Map<String, Replacement> =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fact that we now combine the fully qualified class name, the method name, and optionally argument into a single thing makes the RELEVANT_METHOD_NAMES parameter construction fragile.

I think we should move to using a real class here to reduce changes of mess ups / typos, etc

Copy link
Contributor Author

@joshfriend joshfriend Nov 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You mean something like this for the key?

data class SuspectMethod(
  val klass: Class,
  val method: ???,
  val arguments: List<Type>? = null,
)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been spinning on a proper solution for this for an hour now :|

Ideally, when we do checking, we can go from relevant method to required parameter types to isInstanceOf for the relevant class. That would be best perf wise.

So maybe we want to go with Map< Map< Tuple<method name, requiredParams>, classname, Replacement>

WDYT?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about something like Map<String (method name), List<MethodDetails>> with

data class MethodDetails(
    val containingClass: String,
    val requiredParams: List<String>? = null,
    val replacement: Replacement
)

The implementation could look mostly like the previous one, with an additional line in the potentialReplacements.forEach which checked the parameters.

moseyj19-design

This comment was marked as spam.

@androidx androidx deleted a comment from moseyj19-design Nov 25, 2025

val expected =
"""
src/test.kt:4: Error: Avoid using method getenv [GradleConfigurationCacheInputs]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this message make sense, given that a different method with the same name is allowed? It seems like it would make sense to include the "()"

"""
.trimIndent()
)
check(input).expect("No warnings.")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could use expectClean().

val CONFIGURATION_CACHE_BROAD_INPUTS =
Issue.create(
"GradleConfigurationCacheInputs",
"Avoid using APIs that capture too much of the environment in the" +
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There should be a space either before the closing " on this line or after the opening " on the next.

// A map from eager method name to the containing class of the method and the name of the
// replacement method, if there is a direct equivalent.
private val REPLACEMENTS =
private val REPLACEMENTS: Map<String, Replacement> =
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about something like Map<String (method name), List<MethodDetails>> with

data class MethodDetails(
    val containingClass: String,
    val requiredParams: List<String>? = null,
    val replacement: Replacement
)

The implementation could look mostly like the previous one, with an additional line in the potentialReplacements.forEach which checked the parameters.

?: return
val methodName = method.name
val paramSig =
method.parameterList.parameters.joinToString(",") { it.type.canonicalText }
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I'm concerned about for the parameter type strings is how they would work with generics, e.g. if we had

interface Interface<T> {
    fun someMethod(t: T)
}
class StringClass: Interface<String> {
    override fun someMethod(t: String) = Unit
}

and wanted to ban Interface.someMethod(T). When this is used as StringClass.someMethod(String), then I don't think matching against the parameter strings would work, and might require looking at the super methods to fully support that case.

@liutikas
Copy link
Member

liutikas commented Dec 4, 2025

Now that i've thought about this for a while, do we even want people to use System.getenv even for the parameter version? It seems to be strictly better to push folks to use provider.environmentVariable all the time.

WDYT?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants