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

Camelcade / Perl5-IDEA / #525521824

24 Apr 2026 06:38PM UTC coverage: 76.187% (+0.2%) from 75.952%
#525521824

push

github

hurricup
Pass a disposable to Registry.get to revert it in the end

14757 of 22542 branches covered (65.46%)

Branch coverage included in aggregate %.

31096 of 37643 relevant lines covered (82.61%)

0.83 hits per line

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

90.57
/plugin/common/src/main/java/com/perl5/lang/perl/psi/utils/PerlVariableAnnotations.java
1
/*
2
 * Copyright 2015-2026 Alexandr Evstigneev
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 * http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16

17
package com.perl5.lang.perl.psi.utils;
18

19
import com.intellij.psi.PsiElement;
20
import com.intellij.psi.stubs.StubBuildCachedValuesManager.StubBuildCachedValueProvider;
21
import com.intellij.psi.util.CachedValueProvider;
22
import com.intellij.psi.util.PsiModificationTracker;
23
import com.intellij.psi.util.PsiTreeUtil;
24
import com.intellij.psi.util.PsiUtilCore;
25
import com.intellij.util.Processor;
26
import com.intellij.util.containers.ContainerUtil;
27
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue;
28
import com.perl5.lang.perl.lexer.PerlTokenSetsEx;
29
import com.perl5.lang.perl.parser.PerlElementTypesGenerated;
30
import com.perl5.lang.perl.psi.*;
31
import org.jetbrains.annotations.NotNull;
32

33
import java.util.List;
34

35
import static com.intellij.psi.stubs.StubBuildCachedValuesManager.getCachedValueStubBuildOptimized;
36
import static com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValues.UNKNOWN_VALUE;
37

38
public class PerlVariableAnnotations {
39
  private static final PerlVariableAnnotations EMPTY = new PerlVariableAnnotations() {
1✔
40
    @Override
41
    public void setIsDeprecated() {
42
      throw new UnsupportedOperationException();
×
43
    }
44

45
    @Override
46
    public void setValue(@NotNull PerlValue value) {
47
      throw new UnsupportedOperationException();
×
48
    }
49

50
    @Override
51
    public boolean isEmpty() {
52
      return true;
1✔
53
    }
54
  };
55

56
  private static final byte IS_DEPRECATED = 0x01;
57

58
  private byte myFlags = 0;
1✔
59

60
  private @NotNull PerlValue myValue = UNKNOWN_VALUE;
1✔
61

62
  public byte getFlags() {
63
    return myFlags;
1✔
64
  }
65

66
  private PerlVariableAnnotations() {
1✔
67
  }
1✔
68

69
  public PerlVariableAnnotations(byte flags, @NotNull PerlValue value) {
1✔
70
    myFlags = flags;
1✔
71
    myValue = value;
1✔
72
  }
1✔
73

74
  public boolean isDeprecated() {
75
    return (myFlags & IS_DEPRECATED) == IS_DEPRECATED;
1!
76
  }
77

78
  public void setIsDeprecated() {
79
    myFlags |= IS_DEPRECATED;
1✔
80
  }
1✔
81

82
  public @NotNull PerlValue getAnnotatedValue() {
83
    return myValue;
1!
84
  }
85

86
  public void setValue(@NotNull PerlValue value) {
87
    myValue = value;
1✔
88
  }
1✔
89

90
  public boolean isEmpty() {
91
    return myFlags == 0 && myValue.isUnknown();
1✔
92
  }
93

94
  public static PerlVariableAnnotations empty() {
95
    return EMPTY;
1✔
96
  }
97

98
  public static @NotNull PerlVariableAnnotations from(@NotNull PerlVariableDeclarationElement variableDeclarationElement) {
99
    PerlVariableAnnotations annotations = new PerlVariableAnnotations();
1✔
100
    processAnnotations(variableDeclarationElement, new VariableAnnotationProcessor() {
1✔
101
      @Override
102
      public boolean processDeprecated() {
103
        annotations.setIsDeprecated();
1✔
104
        return true;
1✔
105
      }
106

107
      @Override
108
      public boolean processType(@NotNull PerlAnnotationType annotationType) {
109
        annotations.setValue(annotationType.getValue());
1✔
110
        return true;
1✔
111
      }
112
    });
113
    return annotations.isEmpty() ? EMPTY : annotations;
1!
114
  }
115

116
  private static final StubBuildCachedValueProvider<List<PerlAnnotation>, PerlVariableDeclarationElement> ANNOTATIONS_PROVIDER =
1✔
117
    new StubBuildCachedValueProvider<>(
118
      "perl.variable.annotations",
119
      variableDeclarationElement -> {
120
        List<PerlAnnotation> perlAnnotations = PerlAnnotations.collectAnnotations(variableDeclarationElement);
1✔
121
        var declarationExpression = variableDeclarationElement.getDeclarationExpression();
1✔
122
        if (perlAnnotations.isEmpty() && declarationExpression != null) {
1✔
123
          perlAnnotations = PerlAnnotations.collectAnnotations(declarationExpression);
1✔
124
        }
125
        if (perlAnnotations.isEmpty() && declarationExpression != null &&
1✔
126
            PsiUtilCore.getElementType(declarationExpression.getParent()) == PerlElementTypesGenerated.FOREACH_ITERATOR) {
1✔
127
          perlAnnotations =
1✔
128
            PerlAnnotations.collectAnnotations(PsiTreeUtil.getParentOfType(variableDeclarationElement, PerlForeachCompound.class));
1✔
129
        }
130
        if (perlAnnotations.isEmpty() &&
1✔
131
            PsiUtilCore.getElementType(variableDeclarationElement.getParent()) == PerlElementTypesGenerated.SIGNATURE_ELEMENT) {
1✔
132
          perlAnnotations = ContainerUtil.filter(
1✔
133
            PerlAnnotations.collectAnnotations(PsiTreeUtil.getParentOfType(variableDeclarationElement, PerlSubElement.class)),
1✔
134
            it -> !(it instanceof PsiPerlAnnotationDeprecated));
1✔
135
        }
136
        return CachedValueProvider.Result.create(perlAnnotations, PsiModificationTracker.MODIFICATION_COUNT);
1✔
137
      }
138
    );
139

140
  private static @NotNull List<PerlAnnotation> collectAnnotationsInScope(@NotNull PerlVariableDeclarationElement variableDeclarationElement) {
141
    return getCachedValueStubBuildOptimized(variableDeclarationElement, ANNOTATIONS_PROVIDER);
1!
142
  }
143

144
  public static boolean processAnnotations(@NotNull PerlVariableDeclarationElement variableDeclarationElement,
145
                                           @NotNull PerlVariableAnnotations.VariableAnnotationProcessor annotationsProcessor) {
146

147
    var annotationList = collectAnnotationsInScope(variableDeclarationElement);
1✔
148
    if (annotationList.isEmpty()) {
1✔
149
      return true;
1✔
150
    }
151
    boolean valueSet = false;
1✔
152
    boolean valueSetExplicitly = false;
1✔
153
    for (PerlAnnotation annotation : annotationList) {
1✔
154
      if (annotation instanceof PsiPerlAnnotationDeprecated) {
1✔
155
        if (!annotationsProcessor.processDeprecated()) {
1!
156
          return false;
×
157
        }
158
      }
159
      else if (!valueSetExplicitly && annotation instanceof PerlAnnotationType typeAnnotation &&
1!
160
               typeAnnotation.accept(variableDeclarationElement.getVariable())) {
1✔
161
        var isWildCard = typeAnnotation.isWildCard();
1✔
162
        if (!valueSet || !isWildCard) {
1✔
163
          valueSet = true;
1✔
164
          valueSetExplicitly = !isWildCard;
1✔
165
          if (!annotationsProcessor.processType(typeAnnotation)) {
1✔
166
            return false;
1✔
167
          }
168
        }
169
      }
170
    }
1✔
171
    return true;
1✔
172
  }
173

174
  @Override
175
  public String toString() {
176
    return isEmpty() ? "none" : "PerlVariableAnnotations{" +
1✔
177
                                "myFlags=" + myFlags +
178
                                ", myValue=" + myValue +
179
                                '}';
1✔
180
  }
181

182
  /**
183
   * Processes variable declarations to which {@code typeAnnotation} may belong
184
   */
185
  public static boolean processPotentialTargets(@NotNull PerlAnnotationType typeAnnotation,
186
                                                @NotNull Processor<? super PerlVariableDeclarationElement> declarationElementProcessor) {
187
    PsiElement run = typeAnnotation.getAnnotationContainer();
1✔
188
    while (run != null && PerlTokenSetsEx.getWHITE_SPACE_AND_REAL_COMMENTS().contains(PsiUtilCore.getElementType(run))) {
1✔
189
      run = run.getNextSibling();
1✔
190
    }
191

192
    if (run instanceof PerlForeachCompound foreachCompound) {
1✔
193
      run = foreachCompound.getForeachIterator();
1✔
194
    }
195
    else if (run instanceof PerlSubElement subElement) {
1✔
196
      run = subElement.getSignatureContent();
1✔
197
    }
198
    else if (!(run instanceof PsiPerlExpr || run instanceof PsiPerlStatement || run instanceof PsiPerlVariableDeclarationElement)) {
1!
199
      return true;
1✔
200
    }
201

202
    if (run == null) {
1✔
203
      return true;
1✔
204
    }
205

206
    var visitor = new PerlRecursiveVisitor() {
1✔
207
      @Override
208
      public void visitVariableDeclarationElement(@NotNull PsiPerlVariableDeclarationElement o) {
209
        if (!declarationElementProcessor.process(o)) {
1!
210
          stop();
×
211
        }
212
      }
1✔
213

214
      @Override
215
      public void visitBlock(@NotNull PsiPerlBlock o) {
216
        // do not enter blocks
217
      }
1✔
218
    };
219
    run.accept(visitor);
1✔
220

221
    return !visitor.isStopped();
1!
222
  }
223

224
  public interface VariableAnnotationProcessor {
225
    @SuppressWarnings("SameReturnValue")
226
    default boolean processDeprecated() {
227
      return true;
×
228
    }
229

230
    default boolean processType(@NotNull PerlAnnotationType annotationType) {
231
      return true;
×
232
    }
233
  }
234
}
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