Mobile Development 13 min read

Enhancing Android Lint: HashMap Detection with JDK 7 Generics and Fixing Retrolambda Toast False Positives

This article explains how Meituan’s Android Lint was extended to reliably detect HashMap usage under JDK 7 generics—even when declarations and assignments are separated—by leveraging JavaContext.getType, and how a false‑positive Toast‑in‑lambda bug caused by Retrolambda was eliminated through explicit LambdaExpression checks and reflective issue‑registry replacement.

Meituan Technology Team
Meituan Technology Team
Meituan Technology Team
Enhancing Android Lint: HashMap Detection with JDK 7 Generics and Fixing Retrolambda Toast False Positives

In the previous blog " Android Custom Lint Practice ", we introduced how Meituan App uses custom Lint checks for code inspection. During the use of Lint we discovered several shortcomings of the native Lint implementation, and this article presents the solutions we devised.

Improving HashMap detection under JDK 7 generics

The earlier improvement could only handle cases where the variable declaration and assignment occurred together. It failed when the declaration and the assignment were separated. The following example demonstrates the problematic pattern:

public static void testHashMap() {
    // This case can be handled by the previous blog's check
    Map<Integer, String> map1 = new HashMap<>();
    map1.put(1, "name"); // map2's declaration cannot be found, so the previous check cannot determine it
    map2 = new HashMap<>();
    map2.put(2, "name2");
}

We have now solved this issue. The solution covers three typical situations:

Variables in the same class (member and static fields): public Map<Integer, String> map; public static Map<Integer, String> map2; public void test() { // 1: member variable map = new HashMap<>(); map.put(1, "name"); // 2: static variable map2 = new HashMap<>(); map2.put(1, "name"); }

Method parameters: public void test1(Map<Integer, String> map) { map = new HashMap<>(); map.put(1, "name"); }

Variable declarations in another class (including inner classes and static fields): public class HashMapCase4_2 { public void test() { // 1: static field in another class HashMapCase4_1.map2 = new HashMap<>(); HashMapCase4_1.map2.put(1, "name"); // 2: member field of another object HashMapCase4_1 case4_1 = new HashMapCase4_1(); case4_1.map = new HashMap<>(); case4_1.map.put(1, "name"); // 3: static field in inner class Sub.map2 = new HashMap<>(); // 4: member field of inner class instance Sub sub = new Sub(); sub.map = new HashMap<>(); } private static class Sub { public Map<Integer, String> map; public static Map<Integer, String> map2; } }

After experimenting with the Lint API we found that JavaContext.getType(Node) works for all the above cases, while JavaContext.resolve(Node) only returns a binding that may miss the exact type for complex expressions. The relevant API definitions are:

@Nullable
public ResolvedNode resolve(@NonNull Node node) {
    return mParser.resolve(this, node);
}
@Nullable
public TypeDescriptor getType(@NonNull Node node) {
    return mParser.getType(this, node);
}

Using getType yields a TypeDescriptor (a TypeBinding) that reliably represents the variable’s type, whereas resolve returns a generic binding that can be ambiguous. The implementation relies on ECJ’s AST (via EcjParser) and its powerful binding mechanism (e.g., VariableBinding, TypeBinding). However, we recommend staying within the Lombok‑AST abstraction instead of directly accessing ECJ classes.

Fixing Retrolambda Toast detection false positives

Meituan App also uses Retrolambda. To keep Lint functional under Retrolambda we replace the official AST with the Retrolambda‑provided one. In lambda expressions, Lint incorrectly reports “Toast created but not shown” even though show() is called:

public void test() {
    findViewById(R.id.button).setOnClickListener(view ->
        Toast.makeText(MainActivity.this, "xxx", Toast.LENGTH_SHORT).show());
}

The detector searches for the surrounding method using JavaContext.findSurroundingMethod, which walks up the AST until it finds a MethodDeclaration or ConstructorDeclaration. For lambda expressions this algorithm stops at the outer method ( test) and never sees the lambda body, causing the false‑positive.

Solution: add an explicit check for lombok.ast.LambdaExpression and return early. The helper method is:

private static boolean isLambdaExpression(Class type) {
    return "lombok.ast.LambdaExpression".equals(type.getName());
}

Because the LambdaExpression class exists only in the Retrolambda AST, we compare class names as strings to avoid a hard dependency. After adding this check, the Toast detector correctly recognises the show() call.

Finally, the Toast detector replaces the built‑in issue registry ( BuiltinIssueRegistry) via reflection, because unlike the HashMap enhancement it cannot coexist with the system implementation.

The full source code for the examples is available at MeituanLintDemo .

Author

Chen Tong, Senior Android Engineer at Meituan. Joined Meituan in 2015, responsible for static code analysis, network layer optimisation, and other core infrastructure. The client technology team focuses on mobile infrastructure, open‑source contributions, and improving development efficiency and quality.

Recruitment notice: the client technology team is hiring Android Technical Experts. Interested candidates can send their resumes to fangjintao#meituan.com.

References

Writing a Lint Check

eclipse.jdt.core

Abstract Syntax Tree

rzwitserloot/lombok.ast

tnorbye/lombok.ast

evant/android-retrolambda-lombok

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaASTRetroLambdaAndroid LintHashMap detection
Meituan Technology Team
Written by

Meituan Technology Team

Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.