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

pmd / pmd / 4509

21 Mar 2025 11:23AM UTC coverage: 77.757% (-0.004%) from 77.761%
4509

push

github

adangel
[doc] Use full class name for deprecation in release notes

17505 of 23464 branches covered (74.6%)

Branch coverage included in aggregate %.

38318 of 48328 relevant lines covered (79.29%)

0.8 hits per line

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

88.5
/pmd-java/src/main/java/net/sourceforge/pmd/lang/java/symbols/internal/asm/TypeAnnotationHelper.java
1
/*
2
 * BSD-style license; for more info see http://pmd.sourceforge.net/license.html
3
 */
4

5
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
6

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

10
import org.apache.commons.lang3.tuple.Pair;
11
import org.apache.commons.lang3.tuple.Triple;
12
import org.checkerframework.checker.nullness.qual.NonNull;
13
import org.checkerframework.checker.nullness.qual.Nullable;
14
import org.objectweb.asm.TypePath;
15
import org.objectweb.asm.TypeReference;
16
import org.pcollections.ConsPStack;
17

18
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
19
import net.sourceforge.pmd.lang.java.types.JArrayType;
20
import net.sourceforge.pmd.lang.java.types.JClassType;
21
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
22
import net.sourceforge.pmd.lang.java.types.JWildcardType;
23

24
/**
25
 * @author Clément Fournier
26
 */
27
final class TypeAnnotationHelper {
1✔
28

29
    private TypeAnnotationHelper() {
30
        // utility class
31
    }
32

33

34
    /** Accumulate type annotations to be applied on a single type. */
35
    static final class TypeAnnotationSet {
1✔
36

37
        private final List<Pair<@Nullable TypePath, SymAnnot>> pathAndAnnot = new ArrayList<>();
1✔
38

39
        void add(@Nullable TypePath path, SymAnnot annot) {
40
            pathAndAnnot.add(Pair.of(path, annot));
1✔
41
        }
1✔
42

43
        /**
44
         * Transform the given type to apply type annotations. Returns
45
         * the type decorated with type annotations in the right places.
46
         */
47
        JTypeMirror decorate(@NonNull JTypeMirror base) {
48
            for (Pair<@Nullable TypePath, SymAnnot> pair : pathAndAnnot) {
1✔
49
                base = applySinglePath(base, pair.getLeft(), pair.getRight());
1✔
50
            }
1✔
51
            return base;
1✔
52
        }
53

54
    }
55

56
    /**
57
     * Accumulate type annotations to be applied on a more complex signature than just a field.
58
     * This includes method signatures and class signatures.
59
     */
60
    static final class TypeAnnotationSetWithReferences {
1✔
61

62
        private final List<Triple<TypeReference, @Nullable TypePath, SymAnnot>> pathAndAnnot = new ArrayList<>();
1✔
63

64
        void add(TypeReference reference, @Nullable TypePath path, SymAnnot annot) {
65
            pathAndAnnot.add(Triple.of(reference, path, annot));
1✔
66
        }
1✔
67

68
        /** Execute a callback on each annotations in this set. */
69
        void forEach(TypeAnnotationConsumer consumer) {
70
            for (Triple<TypeReference, TypePath, SymAnnot> triple : pathAndAnnot) {
1✔
71
                consumer.acceptAnnotation(triple.getLeft(), triple.getMiddle(), triple.getRight());
1✔
72
            }
1✔
73
        }
1✔
74

75
        /** Accumulate a value while applying type annotations. */
76
        <T> T reduce(final T init, TypeAnnotationReducer<T> consumer) {
77
            T acc = init;
1✔
78
            for (Triple<TypeReference, TypePath, SymAnnot> triple : pathAndAnnot) {
1✔
79
                acc = consumer.acceptAnnotation(triple.getLeft(), triple.getMiddle(), triple.getRight(), acc);
1✔
80
            }
1✔
81
            return acc;
1✔
82
        }
83

84
        @Override
85
        public String toString() {
86
            return pathAndAnnot.toString();
×
87
        }
88

89
        @FunctionalInterface
90
        interface TypeAnnotationConsumer {
91

92
            /** Add an annotation at the given path and type ref. */
93
            boolean acceptAnnotation(TypeReference tyRef, @Nullable TypePath path, SymAnnot annot);
94
        }
95

96
        @FunctionalInterface
97
        interface TypeAnnotationReducer<T> {
98

99
            /** Add an annotation at the given path and type ref. */
100
            T acceptAnnotation(TypeReference tyRef, @Nullable TypePath path, SymAnnot annot, T acc);
101
        }
102
    }
103

104
    /**
105
     * Add one type annotation into the given type at the location given
106
     * by the given path.
107
     */
108
    static JTypeMirror applySinglePath(@NonNull JTypeMirror base, @Nullable TypePath path, SymAnnot annot) {
109
        return resolvePathStep(base, path, 0, annot);
1✔
110
    }
111

112
    private static JTypeMirror resolvePathStep(JTypeMirror t, @Nullable TypePath path, int i, SymAnnot annot) {
113
        if (t instanceof JClassType && ((JClassType) t).getEnclosingType() != null) {
1✔
114
            return handleEnclosingType((JClassType) t, path, i, annot);
1✔
115
        }
116
        return resolvePathStepNoInner(t, path, i, annot);
1✔
117
    }
118

119
    private static JTypeMirror resolvePathStepNoInner(JTypeMirror t, @Nullable TypePath path, int i, SymAnnot annot) {
120
        assert path == null || path.getLength() == i
1✔
121
            || path.getStep(i) != TypePath.INNER_TYPE;
1!
122

123
        if (path == null || i == path.getLength()) {
1✔
124
            return t.addAnnotation(annot);
1✔
125
        }
126

127
        switch (path.getStep(i)) {
1!
128
        case TypePath.TYPE_ARGUMENT:
129
            if (t instanceof JClassType) {
1!
130
                int typeArgIndex = path.getStepArgument(i);
1✔
131
                JTypeMirror arg = ((JClassType) t).getTypeArgs().get(typeArgIndex);
1✔
132
                JTypeMirror newArg = resolvePathStep(arg, path, i + 1, annot);
1✔
133
                List<JTypeMirror> newArgs = replaceAtIndex(((JClassType) t).getTypeArgs(), typeArgIndex, newArg);
1✔
134
                return ((JClassType) t).withTypeArguments(newArgs);
1✔
135
            }
136
            throw new IllegalArgumentException("Expected class type: " + t);
×
137
        case TypePath.ARRAY_ELEMENT:
138
            if (t instanceof JArrayType) {
1!
139
                JTypeMirror component = ((JArrayType) t).getComponentType();
1✔
140
                JTypeMirror newComponent = resolvePathStep(component, path, i + 1, annot);
1✔
141
                return t.getTypeSystem().arrayType(newComponent).withAnnotations(t.getTypeAnnotations());
1✔
142
            }
143
            throw new IllegalArgumentException("Expected array type: " + t);
×
144
        case TypePath.INNER_TYPE:
145
            throw new IllegalStateException("Should be handled elsewhere"); // there's an assert above too
×
146
        case TypePath.WILDCARD_BOUND:
147
            if (t instanceof JWildcardType) {
1!
148
                JWildcardType wild = (JWildcardType) t;
1✔
149
                JTypeMirror newBound = resolvePathStep(wild.getBound(), path, i + 1, annot);
1✔
150
                return wild.getTypeSystem().wildcard(wild.isUpperBound(), newBound).withAnnotations(wild.getTypeAnnotations());
1✔
151
            }
152
            throw new IllegalArgumentException("Expected wilcard type: " + t);
×
153
        default:
154
            throw new IllegalArgumentException("Illegal path step for annotation TypePath" + i);
×
155
        }
156
    }
157

158
    private static JClassType handleEnclosingType(JClassType t, @Nullable TypePath path, int i, SymAnnot annot) {
159
        // We need to resolve the inner types left to right as given in the path.
160
        // Because JClassType is left-recursive its structure does not match the
161
        // structure of the path.
162
        final JClassType selectedT;
163
        // this list is in inner to outer order
164
        // eg for A.B.C, the list is [A.B.C, A.B, A]
165
        List<JClassType> enclosingTypes = getEnclosingTypes(t);
1✔
166
        int selectionDepth = 0;
1✔
167
        while (path != null && path.getStep(i + selectionDepth) == TypePath.INNER_TYPE) {
1✔
168
            selectionDepth++;
1✔
169
        }
170
        final int selectedTypeIndex = enclosingTypes.size() - 1 - selectionDepth;
1✔
171
        selectedT = enclosingTypes.get(selectedTypeIndex);
1✔
172

173
        // interpret the rest of the path as with this type as context
174
        JClassType rebuiltType = (JClassType) resolvePathStepNoInner(selectedT, path, i + selectionDepth, annot);
1✔
175
        // Then, we may need to rebuild the type by adding the remaining segments.
176
        for (int j = selectedTypeIndex - 1; j >= 0; j--) {
1✔
177
            JClassType nextInner = enclosingTypes.get(j);
1✔
178
            rebuiltType = rebuiltType.selectInner(nextInner.getSymbol(), nextInner.getTypeArgs(), nextInner.getTypeAnnotations());
1✔
179
        }
180
        return rebuiltType;
1✔
181
    }
182

183
    /** Returns a list containing the given type and all its enclosing types, in reverse order. */
184
    private static List<JClassType> getEnclosingTypes(JClassType t) {
185
        List<JClassType> enclosing = new ArrayList<>(1);
1✔
186
        do {
187
            enclosing.add(t);
1✔
188
            t = t.getEnclosingType();
1✔
189
        } while (t != null);
1✔
190
        return enclosing;
1✔
191
    }
192

193
    static @NonNull List<JTypeMirror> replaceAtIndex(List<JTypeMirror> typeArgs, int typeArgIndex, JTypeMirror newArg) {
194
        if (typeArgs.size() == 1 && typeArgIndex == 0) {
1!
195
            return ConsPStack.singleton(newArg);
1✔
196
        }
197
        return ConsPStack.from(typeArgs).with(typeArgIndex, newArg);
1✔
198
    }
199
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc