Skip to content

Commit

Permalink
Расчет типа возвращаемого значения локального метода и описания перем…
Browse files Browse the repository at this point in the history
…енной
  • Loading branch information
nixel2007 committed Apr 24, 2024
1 parent d789d6f commit 872b84e
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -74,20 +79,34 @@ private List<Type> 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();

Expand All @@ -107,38 +126,47 @@ private List<Type> calculateTypes(URI uri, Reference reference) {
if (reference.getOccurrenceType() == OccurrenceType.DEFINITION) {
var document = serverContext.getDocument(uri);
var ast = document.getAst();

Check warning on line 128 in src/main/java/com/github/_1c_syntax/bsl/languageserver/types/TypeResolver.java

View workflow job for this annotation

GitHub Actions / Qodana Community for JVM

Nullability and data flow problems

Method invocation `getAst` may produce `NullPointerException`

Check warning

Code scanning / QDJVMC

Nullability and data flow problems Warning

Method invocation getAst may produce NullPointerException
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<Type> calculateTypes(BSLParser.FileContext ast, Position position) {
private List<Type> 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<Type> calculateTypes(BSLParser.ExpressionContext expression) {
private List<Type> calculateTypes(URI uri, BSLParser.ExpressionContext expression) {

// only simple cases for now. Use ExpressionTree in the future.
if (!expression.operation().isEmpty()) {
return Collections.emptyList();
}

// 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) {
Expand Down Expand Up @@ -168,14 +196,48 @@ private List<Type> 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<Type> 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();

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand Down Expand Up @@ -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();
Expand All @@ -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("Строка");
}

}
18 changes: 18 additions & 0 deletions src/test/resources/types/TypeResolver.os
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
// Строка - какая-то переменная
Перем ПеременнаяСОписанием;

ИмяКорневойАннотации = "Завязь";

Сообщить(ИмяКорневойАннотации);
Expand All @@ -14,3 +17,18 @@

ДругоеИмяМассива = Новый Массив;
ДругоеИмяМассива.Добавить(1);

РезультатФункции = КакаяТоФункция();

Сообщить(РезультатФункции);

Сообщить(ПеременнаяСОписанием);

// Важная функция
//
// Возвращаемое значение:
// Строка - что-то там.
//
Функция КакаяТоФункция()
Возврат "Какая-то функция";
КонецФункции

0 comments on commit 872b84e

Please sign in to comment.