use of com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue in project Perl5-IDEA by Camelcade.
the class PerlPackageUtil method getExpectedSelfValue.
/**
* @return the expected value of the {@code $self} passed to the method. This is either context value or value from the self hinter
*/
@NotNull
public static PerlValue getExpectedSelfValue(@NotNull PsiElement psiElement) {
PsiElement run = psiElement;
while (true) {
PerlSelfHinterElement selfHinter = PsiTreeUtil.getParentOfType(run, PerlSelfHinterElement.class);
if (selfHinter == null) {
break;
}
PerlValue hintedType = selfHinter.getSelfType();
if (!hintedType.isUnknown()) {
return hintedType;
}
run = selfHinter;
}
return PerlScalarValue.create(getContextNamespaceName(psiElement));
}
use of com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue in project Perl5-IDEA by Camelcade.
the class PerlResolveUtil method getValueFromControlFlow.
/**
* Building a control flow for the {@code element} and attempts to find a value of variable
*
* @param element to build control flow for
* @param namespaceName namespace name of the variable if any
* @param variableName name of the variable
* @param actualType actual type of the variable
* @param lexicalDeclaration variable declaration element
* @param stopElement stop element, lexical declaration or it's context for the light elements
* @return a value of found variable or {@link PerlValues#UNKNOWN_VALUE}
* @see PerlControlFlowBuilder#getControlFlowScope(com.intellij.psi.PsiElement)
*/
@NotNull
private static PerlValue getValueFromControlFlow(@NotNull PsiElement element, @Nullable String namespaceName, @NotNull String variableName, @NotNull PerlVariableType actualType, @Nullable PerlVariableDeclarationElement lexicalDeclaration, @Nullable PsiElement stopElement) {
PsiElement controlFlowScope = PerlControlFlowBuilder.getControlFlowScope(element);
if (controlFlowScope == null) {
VirtualFile virtualFile = PsiUtilCore.getVirtualFile(element);
if (!(element instanceof PsiFile) || !(virtualFile instanceof VirtualFileWindow)) {
LOG.error("Unable to find control flow scope for:" + element.getClass() + " at " + element.getTextOffset() + " in " + virtualFile + "; " + PerlUtil.getParentsChain(element));
}
return UNKNOWN_VALUE;
}
Instruction[] instructions = PerlControlFlowBuilder.getFor(controlFlowScope);
PsiElement elementToFind = element instanceof PerlFile ? element.getContext() : element;
int elementInstructionIndex = findElementInstruction(elementToFind, instructions, element);
if (elementInstructionIndex < 0) {
String message = "Unable to find an instruction for " + element.getClass() + "; " + element.getText() + "; " + element.getTextRange() + "; " + PsiUtilCore.getVirtualFile(element) + "; " + controlFlowScope.getClass() + "; " + PerlUtil.getParentsChain(element);
Application application = ApplicationManager.getApplication();
if (!SUPPRESS_ERRORS && (application.isUnitTestMode() || application.isInternal())) {
LOG.error(message);
} else {
LOG.warn(message);
}
return UNKNOWN_VALUE;
}
PerlOneOfValue.Builder valueBuilder = PerlOneOfValue.builder();
ControlFlowUtil.iteratePrev(elementInstructionIndex, instructions, currentInstruction -> {
if (!(currentInstruction instanceof PerlMutationInstruction)) {
PsiElement instructionElement = currentInstruction.getElement();
if ((instructionElement instanceof PerlSubDefinitionElement || instructionElement instanceof PerlSubExpr) && lexicalDeclaration instanceof PerlBuiltInVariable && "_".equals(variableName) && actualType == PerlVariableType.ARRAY) {
valueBuilder.addVariant(PerlValues.ARGUMENTS_VALUE);
return CONTINUE;
}
if (Objects.equals(stopElement, instructionElement)) {
return CONTINUE;
}
if (currentInstruction.num() == 1 && instructionElement != null && instructionElement.getContext() != null) {
valueBuilder.addVariant(getValueFromControlFlow(instructionElement, namespaceName, variableName, actualType, lexicalDeclaration, stopElement));
}
return NEXT;
}
if (currentInstruction.num() > elementInstructionIndex) {
return NEXT;
}
// fixme pop instruction should be decomposed
if (currentInstruction.num() == elementInstructionIndex && !(currentInstruction instanceof PerlAssignInstruction)) {
return NEXT;
}
PsiElement assignee = ((PerlMutationInstruction) currentInstruction).getLeftSide();
if (!(assignee instanceof PerlVariable) || ((PerlVariable) assignee).getActualType() != actualType) {
return NEXT;
}
if (!Objects.equals(variableName, ((PerlVariable) assignee).getName())) {
return NEXT;
}
String explicitNamespaceName = ((PerlVariable) assignee).getExplicitNamespaceName();
if ((explicitNamespaceName != null || namespaceName != null) && !Objects.equals(namespaceName, explicitNamespaceName)) {
return NEXT;
}
PerlVariableDeclarationElement assigneeDeclaration = getLexicalDeclaration((PerlVariable) assignee);
if (element == assignee || lexicalDeclaration == null && assigneeDeclaration == null && !(assignee.getParent() instanceof PerlVariableDeclarationElement) || lexicalDeclaration != null && (Objects.equals(lexicalDeclaration, assigneeDeclaration) || Objects.equals(lexicalDeclaration, assignee.getParent()))) {
valueBuilder.addVariant(((PerlMutationInstruction) currentInstruction).createValue());
}
return CONTINUE;
});
if (lexicalDeclaration != null) {
PerlValue declaredValue = lexicalDeclaration.getDeclaredValue();
if (!declaredValue.isUnknown()) {
valueBuilder.addVariant(declaredValue);
}
}
return valueBuilder.build();
}
use of com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue in project Perl5-IDEA by Camelcade.
the class PerlResolveUtil method computeReturnValueFromControlFlow.
/**
* @return a perl value for the psi element, computed from the control flow graph
* @apiNote should be used for subs and subs expressions
*/
@NotNull
public static PerlValue computeReturnValueFromControlFlow(PsiElement subElement) {
PerlOneOfValue.Builder valueBuilder = PerlOneOfValue.builder();
Instruction[] instructions = PerlControlFlowBuilder.getFor(subElement);
Instruction exitInstruction = instructions[instructions.length - 1];
PerlControlFlowBuilder.iteratePrev(instructions, it -> {
if (it == exitInstruction || it.num() == 0) {
return NEXT;
}
PsiElement element = it.getElement();
if (element == null) {
return NEXT;
}
valueBuilder.addVariant(PerlValuesManager.from(element));
return CONTINUE;
});
return valueBuilder.build();
}
use of com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue in project Perl5-IDEA by Camelcade.
the class PerlMooseAttributeHandler method createMooseAttributes.
@NotNull
private List<PerlDelegatingLightNamedElement<?>> createMooseAttributes(@NotNull PerlSubCallElement subCallElement, @NotNull List<PsiElement> identifiers, @NotNull List<PsiElement> listElements) {
List<PerlDelegatingLightNamedElement<?>> result = new ArrayList<>();
String packageName = PerlPackageUtil.getContextNamespaceName(subCallElement);
Map<String, PerlHashEntry> parameters = PerlHashUtil.packToHash(listElements.subList(1, listElements.size()));
// handling is
PerlHashEntry isParameter = parameters.get(IS_KEY);
boolean isWritableProtected = isParameter != null && StringUtil.equals(RWP_KEY, isParameter.getValueString());
boolean isWritable = isParameter != null && (StringUtil.equals(RW_KEY, isParameter.getValueString()) || isWritableProtected);
PsiElement forcedIdentifier = null;
// handling isa and does
PerlHashEntry isaEntry = parameters.get(ISA_KEY);
String valueClass = null;
if (isaEntry == null) {
isaEntry = parameters.get(DOES_KEY);
}
if (isaEntry != null && subCallElement.isAcceptableIdentifierElement(isaEntry.valueElement)) {
valueClass = isaEntry.getValueString();
if (StringUtil.isEmpty(valueClass)) {
valueClass = null;
}
}
// handling accessor, reader, etc.
boolean createMooClearer = false;
boolean createMooPredicate = false;
List<PerlLightMethodDefinitionElement<?>> secondaryResult = new ArrayList<>();
for (String key : MOOSE_SUB_NAMES_KEYS) {
PerlHashEntry entry = parameters.get(key);
if (entry == null) {
continue;
}
String methodName = entry.getValueString();
if (StringUtil.isEmpty(methodName)) {
if (entry.valueElement instanceof PsiPerlNumberConstant && "1".equals(entry.valueElement.getText())) {
if (key.equals(CLEARER_KEY)) {
createMooClearer = true;
} else if (key.equals(PREDICATE_KEY)) {
createMooPredicate = true;
}
}
continue;
}
if (!isWritable && key.equals(WRITER_KEY)) {
continue;
}
if (!isWritable && key.equals(READER_KEY) || key.equals(ACCESSOR_KEY)) {
forcedIdentifier = entry.valueElement;
continue;
}
if (!subCallElement.isAcceptableIdentifierElement(entry.valueElement)) {
continue;
}
// fixme we could optimize new_value with subclassing and hardcoding of signature
PsiElement identifier = entry.getNonNullValueElement();
PerlLightMethodDefinitionElement<PerlSubCallElement> secondaryElement = new PerlLightMethodDefinitionElement<>(subCallElement, ElementManipulators.getValueText(identifier), LIGHT_METHOD_DEFINITION, identifier, packageName, key.equals(WRITER_KEY) ? Arrays.asList(PerlSubArgument.self(), PerlSubArgument.optionalScalar(PerlSubArgument.NEW_VALUE_VALUE, valueClass)) : Collections.emptyList(), PerlSubAnnotations.tryToFindAnnotations(identifier, entry.keyElement, subCallElement.getParent()));
if (key.equals(READER_KEY) && valueClass != null) {
secondaryElement.setReturnValueFromCode(PerlScalarValue.create(valueClass));
}
secondaryResult.add(secondaryElement);
}
// handle handles
PerlHashEntry handlesEntry = parameters.get(HANDLES_KEY);
if (handlesEntry != null) {
// to show proper signatures, we need an access to delegates, what requires indexes; we should do this in runtime, not indexing, but store delegation target
if (handlesEntry.valueElement instanceof PsiPerlAnonHash) {
// handle handles HASH
Map<String, PerlHashEntry> delegatesMap = PerlHashUtil.collectHashMap(handlesEntry.valueElement);
for (PerlHashEntry delegateEntry : delegatesMap.values()) {
if (!subCallElement.isAcceptableIdentifierElement(delegateEntry.keyElement)) {
continue;
}
secondaryResult.add(new PerlLightMethodDefinitionElement<>(subCallElement, ElementManipulators.getValueText(delegateEntry.keyElement), LIGHT_METHOD_DEFINITION, delegateEntry.keyElement, packageName, Collections.emptyList(), PerlSubAnnotations.tryToFindAnnotations(delegateEntry.keyElement, handlesEntry.keyElement, subCallElement.getParent())));
}
} else if (handlesEntry.valueElement instanceof PsiPerlAnonArray) {
// handle handles ARRAY
List<PsiElement> delegatesIdentifiers = PerlArrayUtil.collectListElements(((PsiPerlAnonArray) handlesEntry.valueElement).getExpr());
for (PsiElement identifier : delegatesIdentifiers) {
if (!subCallElement.isAcceptableIdentifierElement(identifier)) {
continue;
}
secondaryResult.add(new PerlLightMethodDefinitionElement<>(subCallElement, ElementManipulators.getValueText(identifier), LIGHT_METHOD_DEFINITION, identifier, packageName, Collections.emptyList(), PerlSubAnnotations.tryToFindAnnotations(identifier, handlesEntry.keyElement, subCallElement.getParent())));
}
}
}
for (PsiElement identifier : identifiers) {
if (forcedIdentifier != null) {
identifier = forcedIdentifier;
}
if (!subCallElement.isAcceptableIdentifierElement(identifier)) {
continue;
}
List<PerlSubArgument> subArguments = isWritable && !isWritableProtected ? Arrays.asList(PerlSubArgument.self(), PerlSubArgument.optionalScalar(PerlSubArgument.NEW_VALUE_VALUE, valueClass)) : Collections.emptyList();
var identifierText = ElementManipulators.getValueText(identifier);
var identifierAnnotations = PerlSubAnnotations.tryToFindAnnotations(identifier, subCallElement.getParent());
PerlAttributeDefinition newElement = new PerlAttributeDefinition(subCallElement, PerlAttributeDefinition.DEFAULT_NAME_COMPUTATION.fun(identifierText), LIGHT_ATTRIBUTE_DEFINITION, identifier, packageName, subArguments, identifierAnnotations);
if (valueClass != null) {
PerlValue returnValue = PerlScalarValue.create(valueClass);
newElement.setReturnValueFromCode(returnValue);
}
result.add(newElement);
if (isWritableProtected) {
result.add(new PerlLightMethodDefinitionElement<>(subCallElement, PROTECTED_MUTATOR_PREFIX + identifierText, LIGHT_METHOD_DEFINITION, identifier, packageName, Arrays.asList(PerlSubArgument.self(), PerlSubArgument.mandatoryScalar(PerlSubArgument.NEW_VALUE_VALUE, StringUtil.notNullize(valueClass))), identifierAnnotations));
}
if (createMooClearer) {
var clearerName = identifierText.startsWith("_") ? _MOO_CLEARER_PREFIX + identifierText.substring(1) : MOO_CLEARER_PREFIX + identifierText;
result.add(new PerlLightMethodDefinitionElement<>(subCallElement, clearerName, LIGHT_METHOD_DEFINITION, identifier, packageName, Collections.emptyList(), identifierAnnotations));
}
if (createMooPredicate) {
var predicateName = identifierText.startsWith("_") ? _MOO_PREDICATE_PREFIX + identifierText.substring(1) : MOO_PREDICATE_PREFIX + identifierText;
result.add(new PerlLightMethodDefinitionElement<>(subCallElement, predicateName, LIGHT_METHOD_DEFINITION, identifier, packageName, Collections.emptyList(), identifierAnnotations));
}
result.addAll(secondaryResult);
secondaryResult.clear();
}
return result;
}
use of com.perl5.lang.perl.idea.codeInsight.typeInference.value.PerlValue in project Perl5-IDEA by Camelcade.
the class PerlSub method getReturnValueFromAnnotations.
@NotNull
default PerlValue getReturnValueFromAnnotations() {
PerlSubAnnotations subAnnotations = getAnnotations();
if (subAnnotations == null) {
return UNKNOWN_VALUE;
}
PerlValue returnValue = subAnnotations.getReturnValue();
if (PerlPackageUtil.NAMESPACE_ANY_VALUE.equals(returnValue)) {
return PerlValues.FIRST_ARGUMENT_VALUE;
} else {
return returnValue;
}
}
Aggregations