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

Camelcade / Perl5-IDEA / #525521563

01 Jun 2025 02:15PM UTC coverage: 82.332% (+0.04%) from 82.289%
#525521563

push

github

hurricup
Bounded wildcards

2 of 2 new or added lines in 2 files covered. (100.0%)

69 existing lines in 17 files now uncovered.

30882 of 37509 relevant lines covered (82.33%)

0.82 hits per line

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

96.72
/plugin/core/src/main/java/com/perl5/lang/perl/psi/utils/PerlAnnotations.java
1
/*
2
 * Copyright 2015-2025 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.PsiComment;
20
import com.intellij.psi.PsiElement;
21
import com.intellij.psi.PsiFile;
22
import com.intellij.psi.PsiWhiteSpace;
23
import com.intellij.psi.tree.IElementType;
24
import com.intellij.psi.util.PsiTreeUtil;
25
import com.intellij.psi.util.PsiUtilCore;
26
import com.intellij.util.Processor;
27
import com.intellij.util.containers.ContainerUtil;
28
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlScalarValue;
29
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue;
30
import com.perl5.lang.perl.lexer.PerlElementTypes;
31
import com.perl5.lang.perl.psi.*;
32
import com.perl5.lang.perl.psi.references.PerlImplicitDeclarationsProvider;
33
import org.jdom.Element;
34
import org.jetbrains.annotations.NotNull;
35
import org.jetbrains.annotations.Nullable;
36

37
import java.util.*;
38

39
import static com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValues.UNKNOWN_VALUE;
40
import static com.perl5.lang.perl.util.PerlPackageUtil.NAMESPACE_ANY;
41
import static com.perl5.lang.perl.util.PerlPackageUtil.NAMESPACE_ANY_VALUE;
42

43

44
public final class PerlAnnotations implements PerlElementTypes {
45
  private PerlAnnotations() {
46
  }
47

48
  public static final Map<String, IElementType> TOKENS_MAP = Map.of(
1✔
49
    "deprecated", ANNOTATION_DEPRECATED_KEY,
50
    "returns", ANNOTATION_RETURNS_KEY,
51
    "type", ANNOTATION_TYPE_KEY,
52
    "method", ANNOTATION_METHOD_KEY,
53
    "inject", ANNOTATION_INJECT_KEY,
54
    "noinject", ANNOTATION_NO_INJECT_KEY,
55

56
    // these are parsed but not used
57
    "override", ANNOTATION_OVERRIDE_KEY,
58
    "abstract", ANNOTATION_ABSTRACT_KEY,
59
    "noinspection", ANNOTATION_NOINSPECTION_KEY
60
  );
61

62
  /**
63
   * @return true iff injection for {@code host} is suppressed with {@code #@noinject} annotations
64
   */
65
  public static boolean isInjectionSuppressed(@NotNull PsiElement host) {
66
    return getAnyAnnotationByClass(host, PsiPerlAnnotationNoInject.class) != null;
1✔
67
  }
68

69
  /**
70
   * Requires some generic approach with method in `see` section
71
   *
72
   * @see PerlImplicitDeclarationsProvider#readReturnValue(Element)
73
   */
74
  private static @Nullable String getReturnClass(@NotNull PerlAnnotationWithValue annotation) {
75
    PerlNamespaceElement namespaceElement = PsiTreeUtil.getChildOfType(annotation, PerlNamespaceElement.class);
1✔
76
    if (namespaceElement != null) {
1✔
77
      return namespaceElement.getCanonicalName();
1✔
78
    }
79

80
    PsiElement run = annotation.getFirstChild();
1✔
81
    while (run != null) {
1✔
82
      if (PsiUtilCore.getElementType(run) == OPERATOR_MUL) {
1✔
83
        return NAMESPACE_ANY;
1✔
84
      }
85
      run = run.getNextSibling();
1✔
86
    }
87

88
    return null;
1✔
89
  }
90

91
  /**
92
   * @return value desscribing annotation parameter: classname, wildcard, etc.
93
   */
94
  public static PerlValue getParameterValue(@NotNull PerlAnnotationWithValue annotation) {
95
    String returnClass = getReturnClass(annotation);
1✔
96
    return returnClass == null ? UNKNOWN_VALUE :
1✔
97
      returnClass.equals(NAMESPACE_ANY) ? NAMESPACE_ANY_VALUE.get() : PerlScalarValue.create(returnClass);
1✔
98
  }
99

100
  /**
101
   * Gets annotation before element, statement
102
   *
103
   * @param element         element to search for
104
   * @param annotationClass annotation class
105
   * @return annotation or null
106
   */
107
  public static @Nullable <T extends PerlAnnotation> T getAnyAnnotationByClass(@NotNull PsiElement element,
108
                                                                               final Class<T> annotationClass) {
109
    // before element
110
    T annotation = getAnnotationByClass(element, annotationClass);
1✔
111
    if (annotation != null) {
1✔
112
      return annotation;
1✔
113
    }
114

115
    // before statement
116
    PsiPerlStatement statement = PsiTreeUtil.getParentOfType(element, PsiPerlStatement.class);
1✔
117
    if (statement != null) {
1✔
118
      return getAnnotationByClass(statement, annotationClass);
1✔
119
    }
120
    return null;
1✔
121
  }
122

123
  public static @Nullable <T extends PerlAnnotation> T getAnnotationByClass(@NotNull PsiElement element, final Class<T> annotationClass) {
124
    final PerlAnnotation[] result = new PerlAnnotation[]{null};
1✔
125
    processElementAnnotations(element, perlAnnotation -> {
1✔
126
      if (annotationClass.isInstance(perlAnnotation)) {
1✔
127
        result[0] = perlAnnotation;
1✔
128
        return false;
1✔
129
      }
130
      return true;
1✔
131
    });
132
    //noinspection unchecked
133
    return (T)result[0];
1✔
134
  }
135

136
  /**
137
   * Checking the tree before the {@code element} and looking for annotations in code. Each annotation is processed by {@code annotationProcessor}
138
   */
139
  public static void processElementAnnotations(@NotNull PsiElement element,
140
                                               @NotNull Processor<? super PerlAnnotation> annotationProcessor) {
141
    if (element instanceof PsiFile) {
1✔
UNCOV
142
      return;
×
143
    }
144
    PsiElement run = element.getPrevSibling();
1✔
145

146
    PsiElement parentElement = element.getParent();
1✔
147
    IElementType parentElementType = PsiUtilCore.getElementType(parentElement);
1✔
148
    boolean isListItem = parentElementType == STRING_LIST;
1✔
149
    if (run == null || isListItem) {
1✔
150
      run = isListItem ? parentElement : element;
1✔
151
      int elementOffset = element.getNode().getStartOffset();
1✔
152
      while (true) {
153
        PsiElement parent = run.getParent();
1✔
154
        if (parent != null && !(parent instanceof PsiFile) && parent.getNode().getStartOffset() == elementOffset) {
1✔
155
          run = parent;
1✔
156
        }
157
        else {
158
          break;
159
        }
160
      }
1✔
161

162
      run = run.getPrevSibling();
1✔
163
    }
164

165
    while (run != null) {
1✔
166
      if (run instanceof PerlAnnotationContainer annotationContainer) {
1✔
167
        PerlAnnotation annotation = annotationContainer.getAnnotation();
1✔
168
        if (annotation != null) {
1✔
169
          if (!annotationProcessor.process(annotation)) {
1✔
170
            return;
1✔
171
          }
172
        }
173
      }
1✔
174
      else if (!(run instanceof PsiComment || run instanceof PsiWhiteSpace)) {
1✔
175
        return;
1✔
176
      }
177
      run = run.getPrevSibling();
1✔
178
    }
179
  }
1✔
180

181
  public static @NotNull List<PerlAnnotation> collectAnnotations(@Nullable PsiElement element) {
182
    if (element == null) {
1✔
UNCOV
183
      return Collections.emptyList();
×
184
    }
185

186
    final List<PerlAnnotation> result = new ArrayList<>();
1✔
187
    processElementAnnotations(element, perlAnnotation -> {
1✔
188
      result.add(perlAnnotation);
1✔
189
      return true;
1✔
190
    });
191
    ContainerUtil.sort(result, Comparator.comparingInt(PsiElement::getTextOffset));
1✔
192
    return result;
1✔
193
  }
194
}
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