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

Camelcade / Perl5-IDEA / #525521519

21 Apr 2025 01:57PM UTC coverage: 82.17% (+0.01%) from 82.156%
#525521519

push

github

hurricup
CAMELCADE-22634 Cleanup

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

102 existing lines in 15 files now uncovered.

30868 of 37566 relevant lines covered (82.17%)

0.82 hits per line

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

75.64
/plugin/core/src/main/java/com/perl5/lang/perl/psi/references/PerlImplicitDeclarationsProvider.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.references;
18

19
import com.intellij.openapi.diagnostic.Logger;
20
import com.intellij.openapi.extensions.ExtensionPointName;
21
import com.intellij.openapi.util.JDOMUtil;
22
import com.intellij.openapi.util.text.StringUtil;
23
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlScalarValue;
24
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue;
25
import com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValues;
26
import com.perl5.lang.perl.psi.PerlAnnotationWithValue;
27
import com.perl5.lang.perl.psi.impl.PerlBuiltInSubDefinition;
28
import com.perl5.lang.perl.psi.impl.PerlImplicitSubDefinition;
29
import com.perl5.lang.perl.psi.impl.PerlImplicitVariableDeclaration;
30
import com.perl5.lang.perl.psi.utils.PerlAnnotations;
31
import com.perl5.lang.perl.psi.utils.PerlSubArgument;
32
import com.perl5.lang.perl.psi.utils.PerlVariableType;
33
import com.perl5.lang.perl.util.PerlPackageUtil;
34
import org.jdom.Element;
35
import org.jetbrains.annotations.NonNls;
36
import org.jetbrains.annotations.NotNull;
37
import org.jetbrains.annotations.Nullable;
38

39
import java.util.ArrayList;
40
import java.util.Collections;
41
import java.util.List;
42
import java.util.Objects;
43

44
import static com.perl5.lang.perl.util.PerlPackageUtil.NAMESPACE_ANY_VALUE;
45

46

47
/**
48
 * Provides information about implicitly defined subs, e.g {@code Types::Standard}
49
 */
50
public abstract class PerlImplicitDeclarationsProvider {
1✔
51
  static final ExtensionPointName<PerlImplicitDeclarationsProvider> EP_NAME = ExtensionPointName.create("com.perl5.implicitSubsProvider");
1✔
52
  private static final Logger LOG = Logger.getInstance(PerlImplicitDeclarationsProvider.class);
1✔
53
  private static final @NonNls String PACKAGE = "package";
54
  private static final @NonNls String SUB_ELEMENT = "sub";
55
  private static final @NonNls String VARIABLE = "var";
56
  private static final @NonNls String NAME = "name";
57
  private static final @NonNls String ARGUMENTS_ELEMENT = "arguments";
58
  private static final @NonNls String OPTIONAL_ELEMENT = "optional";
59
  private static final @NonNls String ARGUMENT_ELEMENT = "argument";
60
  private static final @NonNls String RETURNS_ELEMENT = "returns";
61

62
  /**
63
   * @return path to XML resource with entities description or null if there is no one
64
   * @see PerlImplicitDeclarationsService#readSubs(java.lang.ClassLoader, java.lang.String)
65
   */
66
  protected abstract @NonNls @Nullable String getDataFileName();
67

68
  /**
69
   * Registers implicit entities with project-level {@link PerlImplicitDeclarationsService}
70
   *
71
   * @apiNote default implementation reads definitions from the xml file, provided by {@link #getDataFileName()}
72
   */
73
  protected void registerDeclarations(@NotNull PerlImplicitDeclarationsService declarationsService) {
74
    String fileName = getDataFileName();
1✔
75
    if (StringUtil.isEmpty(fileName)) {
1✔
UNCOV
76
      return;
×
77
    }
78

79
    ClassLoader classLoader = getClass().getClassLoader();
1✔
80
    final Element xmlElement;
81
    try {
82
      xmlElement = JDOMUtil.load(classLoader.getResourceAsStream(fileName));
1✔
83
    }
84
    catch (Exception e) {
×
85
      LOG.warn("Error loading resources from " + classLoader + " " + fileName, e);
×
UNCOV
86
      return;
×
87
    }
1✔
88
    if (xmlElement == null) {
1✔
89
      LOG.warn("Error loading resources from " + classLoader + " " + fileName);
×
UNCOV
90
      return;
×
91
    }
92
    for (Element namespaceElement : xmlElement.getChildren(PACKAGE)) {
1✔
93
      String namespaceName = namespaceElement.getAttributeValue("name");
1✔
94
      if (StringUtil.isEmpty(namespaceName)) {
1✔
95
        LOG.warn("Missing or empty package name");
×
UNCOV
96
        continue;
×
97
      }
98
      for (Element element : namespaceElement.getChildren()) {
1✔
99
        switch (element.getName()) {
1✔
100
          case SUB_ELEMENT -> readSub(declarationsService, namespaceName, element);
1✔
101
          case VARIABLE -> readVariable(declarationsService, namespaceName, element);
1✔
UNCOV
102
          default -> LOG.warn("Don't know what to do with: " + element.getName());
×
103
        }
104
      }
1✔
105
    }
1✔
106
  }
1✔
107

108
  private void readVariable(@NotNull PerlImplicitDeclarationsService declarationsService,
109
                            @NotNull String namespaceName,
110
                            @NotNull Element element) {
111
    String varName = element.getAttributeValue(NAME);
1✔
112
    if (StringUtil.isEmpty(varName)) {
1✔
113
      LOG.warn("Missing or empty variable name");
×
UNCOV
114
      return;
×
115
    }
116
    declarationsService.registerVariable(
1✔
117
      PerlImplicitVariableDeclaration.createGlobal(declarationsService.getPsiManager(), varName, namespaceName));
1✔
118
  }
1✔
119

120
  private void readSub(@NotNull PerlImplicitDeclarationsService declarationsService,
121
                       @NotNull String namespaceName,
122
                       @NotNull Element element) {
123
    String subName = element.getAttributeValue(NAME);
1✔
124
    if (StringUtil.isEmpty(subName)) {
1✔
125
      LOG.warn("Missing or empty name attribute for sub");
×
UNCOV
126
      return;
×
127
    }
128
    PerlImplicitSubDefinition subDefinition;
129
    if (PerlPackageUtil.CORE_NAMESPACE.equals(namespaceName)) {
1✔
130
      subDefinition = new PerlBuiltInSubDefinition(
1✔
131
        declarationsService.getPsiManager(),
1✔
132
        subName,
133
        PerlPackageUtil.CORE_NAMESPACE,
134
        readArguments(element.getChild(ARGUMENTS_ELEMENT), subName),
1✔
135
        readReturnValue(element)
1✔
136
      );
137
    }
138
    else {
139
      subDefinition = new PerlImplicitSubDefinition(
1✔
140
        declarationsService.getPsiManager(),
1✔
141
        subName,
142
        namespaceName,
143
        readArguments(element.getChild(ARGUMENTS_ELEMENT), subName),
1✔
144
        readReturnValue(element)
1✔
145
      );
146
    }
147
    declarationsService.registerSub(subDefinition);
1✔
148
  }
1✔
149

150
  /**
151
   * It is unclear how to generify reading the value, but this should be some generic logic for this. Probably we should attempt to lex
152
   * the value as an {@code @returns} annotation argument and process it via some method. But this is good enough for now.
153
   *
154
   * @see PerlAnnotations#getReturnClass(PerlAnnotationWithValue)
155
   */
156
  private @NotNull PerlValue readReturnValue(@NotNull Element subElement) {
157
    var returnsAttribute = subElement.getAttributeValue(RETURNS_ELEMENT);
1✔
158
    if (returnsAttribute == null) {
1✔
159
      return PerlValues.UNKNOWN_VALUE;
1✔
160
    }
161
    if (returnsAttribute.equals("*")) {
1✔
UNCOV
162
      return NAMESPACE_ANY_VALUE.get();
×
163
    }
164
    return PerlScalarValue.create(PerlPackageUtil.getCanonicalName(returnsAttribute));
1✔
165
  }
166

167
  private @NotNull List<PerlSubArgument> readArguments(@Nullable Element argumentsElement, @NotNull String subName) {
168
    if (argumentsElement == null) {
1✔
169
      return Collections.emptyList();
1✔
170
    }
171
    List<Element> mandatoryElements = argumentsElement.getChildren(ARGUMENT_ELEMENT);
1✔
172

173
    List<Element> optionalElements;
174
    Element optionalElement = argumentsElement.getChild(OPTIONAL_ELEMENT);
1✔
175
    if (optionalElement == null) {
1✔
176
      optionalElements = Collections.emptyList();
1✔
177
    }
178
    else {
179
      optionalElements = optionalElement.getChildren(ARGUMENT_ELEMENT);
1✔
180
    }
181

182
    if (mandatoryElements.isEmpty() && optionalElements.isEmpty()) {
1✔
UNCOV
183
      return Collections.emptyList();
×
184
    }
185

186
    List<PerlSubArgument> result = new ArrayList<>();
1✔
187
    mandatoryElements.stream().map(element -> readArgument(element, false, subName)).filter(Objects::nonNull).forEach(result::add);
1✔
188
    optionalElements.stream().map(element -> readArgument(element, true, subName)).filter(Objects::nonNull).forEach(result::add);
1✔
189

190
    return result;
1✔
191
  }
192

193
  private @Nullable PerlSubArgument readArgument(@NotNull Element element, boolean isOptional, String subName) {
194
    String variableName = element.getAttributeValue("name");
1✔
195
    if (StringUtil.isEmpty(variableName)) {
1✔
196
      LOG.warn("Missing argument name for " + subName);
×
UNCOV
197
      return null;
×
198
    }
199

200
    String type = element.getAttributeValue("type");
1✔
201
    if (type == null || type.length() != 1) {
1✔
202
      LOG.warn("Unknown type modifier for argument: " + variableName + " in " + subName);
×
UNCOV
203
      return null;
×
204
    }
205
    return PerlSubArgument.create(PerlVariableType.bySigil(type.charAt(0)), variableName, isOptional);
1✔
206
  }
207
}
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