diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java index b835642ef54..18d99905174 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java @@ -24,11 +24,14 @@ import com.github._1c_syntax.bsl.languageserver.context.ServerContext; import com.github._1c_syntax.bsl.languageserver.context.symbol.Describable; import com.github._1c_syntax.bsl.languageserver.context.symbol.SourceDefinedSymbol; +import com.github._1c_syntax.bsl.languageserver.context.symbol.description.MethodDescription; +import com.github._1c_syntax.bsl.languageserver.context.symbol.description.TypeDescription; import com.github._1c_syntax.bsl.languageserver.context.symbol.variable.VariableDescription; import com.github._1c_syntax.bsl.languageserver.references.ReferenceIndex; import com.github._1c_syntax.bsl.languageserver.references.ReferenceResolver; import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType; import com.github._1c_syntax.bsl.languageserver.references.model.Reference; +import com.github._1c_syntax.bsl.languageserver.utils.Ranges; import com.github._1c_syntax.bsl.languageserver.utils.Trees; import com.github._1c_syntax.bsl.languageserver.utils.bsl.Constructors; import com.github._1c_syntax.bsl.parser.BSLParser; @@ -41,6 +44,8 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import java.util.regex.MatchResult; +import java.util.regex.Pattern; import java.util.stream.Stream; @Component @@ -74,20 +79,34 @@ private List calculateTypes(SourceDefinedSymbol symbol) { if (maybeDescription.isPresent()) { var description = maybeDescription.get(); if (description instanceof VariableDescription variableDescription) { - // TODO: extract types from type description and return. + // TODO: use new type information from new bsp-parser + var purposeDescription = variableDescription.getPurposeDescription(); + var typeName = Pattern.compile("^(\\S+)").matcher(purposeDescription).results() + .findFirst() + .map(MatchResult::group) + .orElse(""); + + if (!typeName.isEmpty()) { + return List.of(new Type(typeName)); + } } } } // reference-based type resolver + var uri = symbol.getOwner().getUri(); var ast = symbol.getOwner().getAst(); + if (ast == null) { + return Collections.emptyList(); + } + var position = symbol.getSelectionRange().getStart(); - var typesOfCurrentReference = calculateTypes(ast, position); + var typesOfCurrentReference = calculateTypes(uri, ast, position); var typesOfOtherReferences = referenceIndex.getReferencesTo(symbol).stream() .filter(referenceTo -> referenceTo.getOccurrenceType() == OccurrenceType.DEFINITION) - .map(referenceTo -> calculateTypes(ast, referenceTo.getSelectionRange().getStart())) + .map(referenceTo -> calculateTypes(uri, ast, referenceTo.getSelectionRange().getStart())) .flatMap(Collection::stream) .toList(); @@ -107,25 +126,28 @@ private List calculateTypes(URI uri, Reference reference) { if (reference.getOccurrenceType() == OccurrenceType.DEFINITION) { var document = serverContext.getDocument(uri); var ast = document.getAst(); + if (ast == null) { + return Collections.emptyList(); + } var position = reference.getSelectionRange().getStart(); - return calculateTypes(ast, position); + return calculateTypes(uri, ast, position); } // no-op return Collections.emptyList(); } - private List calculateTypes(BSLParser.FileContext ast, Position position) { + private List calculateTypes(URI uri, BSLParser.FileContext ast, Position position) { return Trees.findTerminalNodeContainsPosition(ast, position) .map(TerminalNode::getParent) .map(ruleNode -> Trees.getRootParent(ruleNode, BSLParser.RULE_assignment)) .map(BSLParser.AssignmentContext.class::cast) .map(BSLParser.AssignmentContext::expression) - .map(this::calculateTypes) + .map(expression -> calculateTypes(uri, expression)) .orElseGet(Collections::emptyList); } - private List calculateTypes(BSLParser.ExpressionContext expression) { + private List calculateTypes(URI uri, BSLParser.ExpressionContext expression) { // only simple cases for now. Use ExpressionTree in the future. if (!expression.operation().isEmpty()) { @@ -133,12 +155,18 @@ private List calculateTypes(BSLParser.ExpressionContext expression) { } // new-resolver - var typeName = typeName(expression); + var typeName = newTypeName(expression); if (!typeName.isEmpty()) { Type type = new Type(typeName); return List.of(type); } + // globalMethodCall resolver + var typeNames = returnedValue(uri, expression); + if (!typeNames.isEmpty()) { + return typeNames; + } + // const-value resolver var constValueContext = expression.member(0).constValue(); if (constValueContext == null) { @@ -168,14 +196,48 @@ private List calculateTypes(BSLParser.ExpressionContext expression) { } - private String typeName(BSLParser.ExpressionContext ctx) { + private String newTypeName(BSLParser.ExpressionContext expression) { var typeName = ""; - var newCtx = Trees.getNextNode(ctx, ctx, BSLParser.RULE_newExpression); + var newCtx = Trees.getNextNode(expression, expression, BSLParser.RULE_newExpression); if (newCtx instanceof BSLParser.NewExpressionContext newExpression) { typeName = Constructors.typeName(newExpression).orElse(""); } return typeName; } + private List returnedValue(URI uri, BSLParser.ExpressionContext expression) { + var complexIdentifier = expression.member(0).complexIdentifier(); + + if (complexIdentifier == null) { + return Collections.emptyList(); + } + + if (!complexIdentifier.modifier().isEmpty()) { + return Collections.emptyList(); + } + + var globalMethodCall = complexIdentifier.globalMethodCall(); + + if (globalMethodCall == null) { + return Collections.emptyList(); + } + + var calledMethod = referenceResolver.findReference(uri, Ranges.create(globalMethodCall.methodName()).getStart()); + + return calledMethod.filter(Reference::isSourceDefinedSymbolReference) + .flatMap(Reference::getSourceDefinedSymbol) + .filter(Describable.class::isInstance) + .map(Describable.class::cast) + .flatMap(Describable::getDescription) + .filter(MethodDescription.class::isInstance) + .map(MethodDescription.class::cast) + .map(MethodDescription::getReturnedValue) + .stream() + .flatMap(List::stream) + .map(TypeDescription::getName) + .map(Type::new) + .toList(); + + } } diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolverTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolverTest.java index b08f2930c2e..8d0c0f0519f 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolverTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolverTest.java @@ -41,7 +41,7 @@ void simpleType() { var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/types/TypeResolver.os"); // when - var types = typeResolver.findTypes(documentContext.getUri(), new Position(2, 10)); + var types = typeResolver.findTypes(documentContext.getUri(), new Position(5, 10)); // then assertThat(types).hasSize(1); @@ -53,7 +53,7 @@ void twoTypesOnReassignment() { var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/types/TypeResolver.os"); // when - var types = typeResolver.findTypes(documentContext.getUri(), new Position(5, 4)); + var types = typeResolver.findTypes(documentContext.getUri(), new Position(8, 4)); // then assertThat(types).hasSize(2); @@ -65,7 +65,7 @@ void twoTypesOnPlaceOfUsage() { var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/types/TypeResolver.os"); // when - var types = typeResolver.findTypes(documentContext.getUri(), new Position(7, 10)); + var types = typeResolver.findTypes(documentContext.getUri(), new Position(10, 10)); // then assertThat(types).hasSize(2); @@ -98,7 +98,7 @@ void twoAssignments() { } @Test - void array() { + void newArray() { // given var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/types/TypeResolver.os"); var variableSymbol = documentContext.getSymbolTree().getVariableSymbol("ДругоеИмяМассива", documentContext.getSymbolTree().getModule()).orElseThrow(); @@ -111,4 +111,32 @@ void array() { assertThat(types.get(0).getName()).isEqualTo("Массив"); } + @Test + void globalMethodCall() { + // given + var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/types/TypeResolver.os"); + var variableSymbol = documentContext.getSymbolTree().getVariableSymbol("РезультатФункции", documentContext.getSymbolTree().getModule()).orElseThrow(); + + // when + var types = typeResolver.findTypes(variableSymbol); + + // then + assertThat(types).hasSize(1); + assertThat(types.get(0).getName()).isEqualTo("Строка"); + } + + @Test + void varWithDescription() { + // given + var documentContext = TestUtils.getDocumentContextFromFile("./src/test/resources/types/TypeResolver.os"); + var variableSymbol = documentContext.getSymbolTree().getVariableSymbol("ПеременнаяСОписанием", documentContext.getSymbolTree().getModule()).orElseThrow(); + + // when + var types = typeResolver.findTypes(variableSymbol); + + // then + assertThat(types).hasSize(1); + assertThat(types.get(0).getName()).isEqualTo("Строка"); + } + } \ No newline at end of file diff --git a/src/test/resources/types/TypeResolver.os b/src/test/resources/types/TypeResolver.os index 1ed212f057d..f3f37b4ee24 100644 --- a/src/test/resources/types/TypeResolver.os +++ b/src/test/resources/types/TypeResolver.os @@ -1,3 +1,6 @@ +// Строка - какая-то переменная +Перем ПеременнаяСОписанием; + ИмяКорневойАннотации = "Завязь"; Сообщить(ИмяКорневойАннотации); @@ -14,3 +17,18 @@ ДругоеИмяМассива = Новый Массив; ДругоеИмяМассива.Добавить(1); + +РезультатФункции = КакаяТоФункция(); + +Сообщить(РезультатФункции); + +Сообщить(ПеременнаяСОписанием); + +// Важная функция +// +// Возвращаемое значение: +// Строка - что-то там. +// +Функция КакаяТоФункция() + Возврат "Какая-то функция"; +КонецФункции \ No newline at end of file