• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pmd / pmd / 674

18 Jul 2026 04:15PM UTC coverage: 79.164% (-0.04%) from 79.203%
674

push

github

web-flow
[kotlin] Add kotlin-type-mapper infrastructure (#6795)

* feat(kotlin): add KotlinNodeTypeData with package-private setters and InternalApiBridge

KotlinNodeTypeData stores type-mapper results on AST nodes via DataMap.
Setters are package-private; InternalApiBridge exposes them to callers
outside the types package (e.g. KotlinLanguageProcessor).

* feat(kotlin): add KotlinTypeAnalysisContext and holder with kotlin-type-mapper deps

Add KotlinTypeAnalysisContext (indexes call sites, declarations, type
hierarchy from TypedAst) and KotlinTypeAnalysisContextHolder (global +
thread-local slots). Add kotlin-type-mapper-model and -analyzer deps.

* feat(kotlin): integrate kotlin-type-mapper AST annotation phase

Add KotlinTypeAnnotationVisitor, AnnotationAttributeAnnotator, and
DelegationSpecifierAnnotator to walk parsed ASTs and set @TypeName /
@ReturnTypeName attributes on declaration nodes. KotlinLanguageProcessor
now runs kotlin-type-mapper before parsing and annotates each file via
InternalApiBridge; falls back gracefully when no auxClasspath is set.

* test(kotlin): expand KotlinTypeAnnotationVisitorTest to full visitor coverage

9 tests covering all node types with specific type assertions:
PropertyDeclaration, FunctionDeclaration, CatchBlock, ForStatement (loop var
+ collection param), ClassParameter, ClassDeclaration (simple name without
package; FQN with package declaration), annotation FQN. Uses
KotlinParsingHelper for production-like parsing.

* fix(kotlin): upgrade ktm to 0.5.1, fix NPE, add missing tests

- Upgrade kotlin-type-mapper deps from 0.5.1-SNAPSHOT to 0.5.1
- Add delegation specifier tests (superclass + interface) to KotlinTypeAnnotationVisitorTest
- Add KotlinAuxClasspathIntegrationTest: end-to-end auxClasspath flow
- Remove KotlinNodeTypeDataTest.typeInfoAvailableFalseWhenNotSet (always failed)
- Add KotlinTypeAnalysisContextHolder.clear() to test teardown
- Add kotlin-type-mapper attribution to kotlin.md docs
- Fix NPE in KotlinAux... (continued)

19328 of 25381 branches covered (76.15%)

Branch coverage included in aggregate %.

296 of 369 new or added lines in 11 files covered. (80.22%)

41976 of 52058 relevant lines covered (80.63%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.5
/pmd-kotlin/src/main/java/net/sourceforge/pmd/lang/kotlin/types/KotlinNodeTypeData.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.kotlin.types;
6

7
import java.util.Collections;
8
import java.util.List;
9

10
import org.checkerframework.checker.nullness.qual.Nullable;
11

12
import net.sourceforge.pmd.annotation.Experimental;
13
import net.sourceforge.pmd.lang.kotlin.ast.KotlinNode;
14
import net.sourceforge.pmd.lang.kotlin.ast.KotlinParser.KtKotlinFile;
15
import net.sourceforge.pmd.lang.kotlin.rule.internal.KotlinTypeAnalysisContext;
16
import net.sourceforge.pmd.util.DataMap;
17
import net.sourceforge.pmd.util.DataMap.SimpleDataKey;
18

19
/**
20
 * Stores and retrieves type-mapper data on Kotlin AST nodes via {@link DataMap} keys.
21
 *
22
 * <p>DataKeys are private to this class. The kotlin-type-mapper library
23
 * uses the {@code set*} methods (via {@link InternalApiBridge}) to populate
24
 * values during its pre-analysis pass; rule code uses the {@code get*} methods
25
 * to read them.
26
 *
27
 * @since 7.27.0
28
 * @experimental Provides the initial API to access type information on Kotlin AST nodes.
29
 */
30
@Experimental
31
public final class KotlinNodeTypeData {
32

33
    private static final SimpleDataKey<String> TYPE_NAME_KEY =
1✔
34
            DataMap.simpleDataKey("kotlin.typeName");
1✔
35

36
    private static final SimpleDataKey<String> RETURN_TYPE_KEY =
1✔
37
            DataMap.simpleDataKey("kotlin.returnTypeName");
1✔
38

39
    private static final SimpleDataKey<List<String>> ANNOTATION_NAMES_KEY =
1✔
40
            DataMap.simpleDataKey("kotlin.annotationNames");
1✔
41

42
    private static final SimpleDataKey<Boolean> TYPE_INFO_AVAILABLE_KEY =
1✔
43
            DataMap.simpleDataKey("kotlin.typeInfoAvailable");
1✔
44

45
    private static final SimpleDataKey<KotlinTypeAnalysisContext> ANALYSIS_CONTEXT_KEY =
1✔
46
            DataMap.simpleDataKey("kotlin.analysisContext");
1✔
47

48
    private KotlinNodeTypeData() {}
49

50
    /**
51
     * Returns the resolved type name stored on this node,
52
     * or {@code null} when type analysis has not been run or the node has no type.
53
     * Used on variable declarations, function parameters, annotation nodes,
54
     * catch blocks, delegation specifiers, and for-loop variables.
55
     */
56
    public static @Nullable String getTypeName(KotlinNode node) {
57
        return node.getUserMap().get(TYPE_NAME_KEY);
1✔
58
    }
59

60
    /**
61
     * Stores the resolved type name on a node.
62
     * Called by the kotlin-type-mapper pre-analysis pass via {@link InternalApiBridge}.
63
     */
64
    static void setTypeName(KotlinNode node, String typeName) {
65
        node.getUserMap().set(TYPE_NAME_KEY, typeName);
1✔
66
    }
1✔
67

68
    /**
69
     * Returns the resolved return type name for a function declaration node,
70
     * or {@code null} when type analysis has not been run.
71
     */
72
    public static @Nullable String getReturnTypeName(KotlinNode node) {
73
        return node.getUserMap().get(RETURN_TYPE_KEY);
1✔
74
    }
75

76
    /**
77
     * Stores the resolved return type name on a function declaration node.
78
     * Called by the kotlin-type-mapper pre-analysis pass via {@link InternalApiBridge}.
79
     */
80
    static void setReturnTypeName(KotlinNode node, String returnTypeName) {
81
        node.getUserMap().set(RETURN_TYPE_KEY, returnTypeName);
1✔
82
    }
1✔
83

84
    /**
85
     * Returns an unmodifiable list of fully-qualified annotation class names
86
     * for a declaration node, or an empty list if none are present or type
87
     * analysis has not been run.
88
     */
89
    public static List<String> getAnnotationFqNames(KotlinNode node) {
90
        List<String> stored = node.getUserMap().get(ANNOTATION_NAMES_KEY);
1✔
91
        return stored != null ? stored : Collections.emptyList();
1✔
92
    }
93

94
    /**
95
     * Stores the fully-qualified annotation class names on a declaration node.
96
     * Called by the kotlin-type-mapper pre-analysis pass via {@link InternalApiBridge}.
97
     */
98
    static void setAnnotationFqNames(KotlinNode node, List<String> annotationFqNames) {
99
        node.getUserMap().set(ANNOTATION_NAMES_KEY, annotationFqNames);
1✔
100
    }
1✔
101

102
    /**
103
     * Returns {@code true} when the kotlin-type-mapper pre-analysis ran successfully
104
     * for the file represented by this root node, {@code false} otherwise.
105
     */
106
    public static boolean isTypeInfoAvailable(KtKotlinFile rootNode) {
107
        Boolean value = rootNode.getUserMap().get(TYPE_INFO_AVAILABLE_KEY);
1✔
108
        return Boolean.TRUE.equals(value);
1✔
109
    }
110

111
    /**
112
     * Marks a root node as having completed type analysis.
113
     * Called via {@link InternalApiBridge}.
114
     */
115
    static void setTypeInfoAvailable(KtKotlinFile rootNode) {
116
        rootNode.getUserMap().set(TYPE_INFO_AVAILABLE_KEY, Boolean.TRUE);
1✔
117
    }
1✔
118

119
    /**
120
     * Returns the {@link KotlinTypeAnalysisContext} stored on this root node,
121
     * or {@link KotlinTypeAnalysisContext#empty()} when not set.
122
     * XPath functions use this to retrieve per-run type data from the context node's root.
123
     */
124
    public static KotlinTypeAnalysisContext getAnalysisContext(KtKotlinFile rootNode) {
NEW
125
        KotlinTypeAnalysisContext ctx = rootNode.getUserMap().get(ANALYSIS_CONTEXT_KEY);
×
NEW
126
        return ctx != null ? ctx : KotlinTypeAnalysisContext.empty();
×
127
    }
128

129
    /**
130
     * Stores the analysis context on a root node.
131
     * Called via {@link InternalApiBridge} after type analysis completes for a file.
132
     */
133
    static void setAnalysisContext(KtKotlinFile rootNode, KotlinTypeAnalysisContext ctx) {
134
        rootNode.getUserMap().set(ANALYSIS_CONTEXT_KEY, ctx);
1✔
135
    }
1✔
136
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc