diff --git a/docs/diagnostics/AttachIdleHandler.md b/docs/diagnostics/AttachIdleHandler.md new file mode 100644 index 00000000000..5952a060746 --- /dev/null +++ b/docs/diagnostics/AttachIdleHandler.md @@ -0,0 +1,59 @@ +# Использование метода ПодключитьОбработчикОжидания (AttachIdleHandler) + +| Тип | Поддерживаются
языки | Важность | Включена
по умолчанию | Время на
исправление (мин) | Тэги | +| :-: | :-: | :-: | :-: | :-: | :-: | +| `Ошибка` | `BSL`
`OS` | `Информационный` | `Да` | `1` | `error`
`unpredictable` | + + +## Описание диагностики +Подключение или отключение обработчика ожидания с указанием несуществующего метода + +## Примеры +Неправильно + +```Bsl + +&НаКлиенте +Процедура НайтиСтрокуСОшибкой(Команда) + + ПодключитьОбработчикОжидания("НеизвестныйМетод", 0.1, Истина); + +КонецПроцедуры + +``` + +Правильно + +```Bsl +&НаКлиенте +Процедура НайтиСтроку(Команда) + + ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1, Истина); + +КонецПроцедуры + +&НаКлиенте +Процедура ВыполнитьПоиск() + +КонецПроцедуры + +``` +## Источники + +* Полезная информация: [Отложенная обработка события элемента управления в форме](https://its.1c.ru/db/metod8dev/content/1820/hdoc) + +## Сниппеты + + +### Экранирование кода + +```bsl +// BSLLS:AttachIdleHandler-off +// BSLLS:AttachIdleHandler-on +``` + +### Параметр конфигурационного файла + +```json +"AttachIdleHandler": false +``` diff --git a/docs/diagnostics/index.md b/docs/diagnostics/index.md index c260de678e7..1a957f7adb0 100644 --- a/docs/diagnostics/index.md +++ b/docs/diagnostics/index.md @@ -8,15 +8,16 @@ ## Список реализованных диагностик -Общее количество: **113** +Общее количество: **114** * Дефект кода: **71** * Уязвимость: **3** -* Ошибка: **35** +* Ошибка: **36** * Потенциальная уязвимость: **4** | Ключ | Название | Включена по умолчанию | Важность | Тип | Тэги | | --- | --- | :-: | --- | --- | --- | +| [AttachIdleHandler](AttachIdleHandler.md) | Использование метода ПодключитьОбработчикОжидания | Да | Информационный | Ошибка | `error`
`unpredictable` | | [BeginTransactionBeforeTryCatch](BeginTransactionBeforeTryCatch.md) | Нарушение правил работы с транзакциями для метода 'НачатьТранзакцию' | Да | Важный | Ошибка | `standard` | | [CachedPublic](CachedPublic.md) | Кеширование программного интерфейса | Да | Важный | Дефект кода | `standard`
`design` | | [CanonicalSpellingKeywords](CanonicalSpellingKeywords.md) | Каноническое написание ключевых слов | Да | Информационный | Дефект кода | `standard` | diff --git a/docs/en/diagnostics/AttachIdleHandler.md b/docs/en/diagnostics/AttachIdleHandler.md new file mode 100644 index 00000000000..1279e739120 --- /dev/null +++ b/docs/en/diagnostics/AttachIdleHandler.md @@ -0,0 +1,60 @@ +# Usage AttachIdleHandler (AttachIdleHandler) + +| Type | Scope | Severity | Activated
by default | Minutes
to fix | Tags | +| :-: | :-: | :-: | :-: | :-: | :-: | +| `Error` | `BSL`
`OS` | `Info` | `Yes` | `1` | `error`
`unpredictable` | + + +## Description +Attach or detach idle handler with not existed method + +## Examples + +BAD + +```Bsl + +&НаКлиенте +Процедура НайтиСтрокуСОшибкой(Команда) + + ПодключитьОбработчикОжидания("НеизвестныйМетод", 0.1, Истина); + +КонецПроцедуры + +``` + +GOOD + +```Bsl +&НаКлиенте +Процедура НайтиСтроку(Команда) + + ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1, Истина); + +КонецПроцедуры + +&НаКлиенте +Процедура ВыполнитьПоиск() + +КонецПроцедуры + +``` +## Sources + +* Usage info: [Отложенная обработка события элемента управления в форме](https://its.1c.ru/db/metod8dev/content/1820/hdoc) + +## Snippets + + +### Diagnostic ignorance in code + +```bsl +// BSLLS:AttachIdleHandler-off +// BSLLS:AttachIdleHandler-on +``` + +### Parameter for config + +```json +"AttachIdleHandler": false +``` diff --git a/docs/en/diagnostics/index.md b/docs/en/diagnostics/index.md index 6de16916d85..ec693e97fa5 100644 --- a/docs/en/diagnostics/index.md +++ b/docs/en/diagnostics/index.md @@ -8,15 +8,16 @@ To escape individual sections of code or files from triggering diagnostics, you ## Implemented diagnostics -Total: **113** +Total: **114** -* Error: **35** +* Error: **36** * Code smell: **71** * Vulnerability: **3** * Security Hotspot: **4** | Key | Name| Enabled by default | Severity | Type | Tags | | --- | --- | :-: | --- | --- | --- | +| [AttachIdleHandler](AttachIdleHandler.md) | Usage AttachIdleHandler | Yes | Info | Error | `error`
`unpredictable` | | [BeginTransactionBeforeTryCatch](BeginTransactionBeforeTryCatch.md) | Violating transaction rules for the 'BeginTransaction' method | Yes | Major | Error | `standard` | | [CachedPublic](CachedPublic.md) | Cached public methods | Yes | Major | Code smell | `standard`
`design` | | [CanonicalSpellingKeywords](CanonicalSpellingKeywords.md) | Canonical keyword writing | Yes | Info | Code smell | `standard` | diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic.java new file mode 100644 index 00000000000..20ba550def3 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic.java @@ -0,0 +1,120 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.diagnostics; + +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticInfo; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticMetadata; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticSeverity; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticTag; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticType; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8TypeHelper; +import com.github._1c_syntax.bsl.parser.BSLParser; +import com.github._1c_syntax.mdclasses.metadata.additional.ModuleType; +import com.github._1c_syntax.utils.CaseInsensitivePattern; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.regex.Pattern; + +@DiagnosticMetadata( + type = DiagnosticType.ERROR, + severity = DiagnosticSeverity.INFO, + minutesToFix = 1, + modules = { + ModuleType.FormModule + }, + tags = { + DiagnosticTag.ERROR, + DiagnosticTag.UNPREDICTABLE + } + +) +public class AttachIdleHandlerDiagnostic extends AbstractVisitorDiagnostic { + + private static final Pattern MESSAGE_PATTERN_ATTACH = CaseInsensitivePattern.compile( + "ПодключитьОбработчикОжидания|AttachIdleHandler" + ); + private static final Pattern MESSAGE_PATTERN_DETACH = CaseInsensitivePattern.compile( + "ОтключитьОбработчикОжидания|DetachIdleHandler" + ); + + public AttachIdleHandlerDiagnostic(DiagnosticInfo info) { + super(info); + } + + @Override + public ParseTree visitGlobalMethodCall(BSLParser.GlobalMethodCallContext ctx) { + + if (checkGlobalMethodCall(ctx)) { + diagnosticStorage.addDiagnostic(ctx.methodName(), getMessage(ctx)); + } + + return super.visitGlobalMethodCall(ctx); + } + + /** + * Получает сообщение диагностики для пользователя + * + * @param ctx контекст узла + * @return В случае если передан контекст метода, параметризованное сообщение, + * первым параметром которого всегда будет имя метода. + * В противном случае возвращается обычное сообщение без параметров. + */ + protected String getMessage(BSLParser.GlobalMethodCallContext ctx) { + return ctx.methodName().getText(); + } + + protected boolean checkGlobalMethodCall(BSLParser.GlobalMethodCallContext ctx) { + + boolean isAttachHandler = MESSAGE_PATTERN_ATTACH.matcher(ctx.methodName().getText()).matches(); + if (!(isAttachHandler + || MESSAGE_PATTERN_DETACH.matcher(ctx.methodName().getText()).matches())) { + return false; + } + + var callContext = ctx.doCall(); + // Проверка на существование метода в текущем контексте без параметров + boolean hasError = V8TypeHelper.getStringConstantFromFirstParam(callContext) + .get().map(methodName -> documentContext.getSymbolTree().getMethods() + .stream() + .noneMatch(method -> method.getName().equalsIgnoreCase(methodName) && method.getParameters().size() == 0) + ).orElse(false); + + if (!isAttachHandler) { + return hasError; + } + + // Проверка что при таймауте меньше 1 секунды, третий параметр не равен Ложь + hasError = hasError || V8TypeHelper.getFloatNumberConstantFromParam(callContext, 1) + .get() + .filter(timeout -> timeout < 1.0f) + // TODO change while got context + .map(e -> V8TypeHelper.getBooleanConstantFromParam(callContext, 2, Boolean.FALSE) + // .map(e -> V8TypeHelper.get(Boolean.FALSE, (constValue) -> constValue.TRUE() != null).apply(callContext,2) + .get() + .map(be -> be.equals(Boolean.FALSE)) + .orElse(false) + ) + .orElse(false); + return hasError; + } +} + diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnostic.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnostic.java index 8de4d96733e..5136445f139 100644 --- a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnostic.java +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnostic.java @@ -26,24 +26,23 @@ import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticSeverity; import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticTag; import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticType; +import com.github._1c_syntax.bsl.languageserver.utils.variable.scope.CodeFlowType; +import com.github._1c_syntax.bsl.languageserver.utils.variable.scope.ProgramScope; +import com.github._1c_syntax.bsl.languageserver.utils.variable.scope.VariableDefinition; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8Type; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8TypeFromPresentationSupplier; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8TypeHelper; import com.github._1c_syntax.bsl.parser.BSLParser; import com.github._1c_syntax.bsl.parser.BSLParser.AssignmentContext; import com.github._1c_syntax.bsl.parser.BSLParserRuleContext; import com.github._1c_syntax.utils.CaseInsensitivePattern; -import lombok.ToString; -import org.antlr.v4.runtime.ParserRuleContext; -import org.antlr.v4.runtime.RuleContext; import org.antlr.v4.runtime.tree.ParseTree; -import java.util.ArrayDeque; -import java.util.Collection; -import java.util.Deque; -import java.util.HashMap; -import java.util.HashSet; +import java.util.Collections; import java.util.Optional; import java.util.Set; +import java.util.function.Predicate; import java.util.regex.Pattern; -import java.util.stream.Collectors; @DiagnosticMetadata( type = DiagnosticType.ERROR, @@ -61,178 +60,130 @@ public class CreateQueryInCycleDiagnostic extends AbstractVisitorDiagnostic { CaseInsensitivePattern.compile("ПостроительОтчета|ReportBuilder"); private static final Pattern QUERY_PATTERN = CaseInsensitivePattern.compile("Запрос|Query"); - private static final String BOOLEAN_TYPE = "Boolean"; - private static final String DATE_TYPE = "Datetime"; - private static final String NULL_TYPE = "Null"; - private static final String NUMBER_TYPE = "Number"; - private static final String REPORT_BUILDER_TYPE = "ReportBuilder"; - private static final String STRING_TYPE = "String"; - private static final String QUERY_BUILDER_TYPE = "QueryBuilder"; - private static final String QUERY_TYPE = "Query"; - private static final String UNDEFINED_TYPE = "Undefined"; private static final String GLOBAL_SCOPE = "GLOBAL_SCOPE"; private static final String MODULE_SCOPE = "MODULE_SCOPE"; - private VariableScope currentScope; + private ProgramScope programScope = new ProgramScope(); public CreateQueryInCycleDiagnostic(DiagnosticInfo info) { super(info); + programScope.getTypeSuppliers().add(getTypeSupplier()); } - private static String getTypeFromConstValue(BSLParser.ConstValueContext constValue) { - String result; - if (constValue.string() != null) { - result = STRING_TYPE; - } else if (constValue.DATETIME() != null) { - result = DATE_TYPE; - } else if (constValue.numeric() != null) { - result = NUMBER_TYPE; - } else if (constValue.TRUE() != null) { - result = BOOLEAN_TYPE; - } else if (constValue.FALSE() != null) { - result = BOOLEAN_TYPE; - } else if (constValue.NULL() != null) { - result = NULL_TYPE; - } else { - result = UNDEFINED_TYPE; - } - - return result; - } - - private static String getTypeFromNewExpressionContext(BSLParser.NewExpressionContext newExpression) { - - String typeName = Optional.ofNullable(newExpression.typeName()) - .map(RuleContext::getText) - .or(() -> Optional.ofNullable(newExpression.doCall()) - .map(BSLParser.DoCallContext::callParamList) - .flatMap(callParamListContext -> callParamListContext.callParam().stream().findFirst()) - .map(BSLParser.CallParamContext::expression) - .map(BSLParser.ExpressionContext::member) - .flatMap(memberListContext -> memberListContext.stream().findFirst()) - .map(BSLParser.MemberContext::constValue) - .filter(constValue -> getTypeFromConstValue(constValue).equals(STRING_TYPE)) - .map(RuleContext::getText) - .map(constValueText -> constValueText.substring(1, constValueText.length() - 1)) - ) - .orElse(UNDEFINED_TYPE); - - if (QUERY_BUILDER_PATTERN.matcher(typeName).matches()) { - return QUERY_BUILDER_TYPE; - } else if (REPORT_BUILDER_PATTERN.matcher(typeName).matches()) { - return REPORT_BUILDER_TYPE; - } else if (QUERY_PATTERN.matcher(typeName).matches()) { - return QUERY_TYPE; - } else { - return typeName; - } - } - - private static String getVariableNameFromCallStatementContext(BSLParser.CallStatementContext callStatement) { - return callStatement.IDENTIFIER().getText(); + public static V8TypeFromPresentationSupplier getTypeSupplier() { + return (typeName) -> { + if (QUERY_PATTERN.matcher(typeName).matches()) { + return Optional.of(NecessaryTypes.QUERY_TYPE); + } else if (REPORT_BUILDER_PATTERN.matcher(typeName).matches()) { + return Optional.of(NecessaryTypes.REPORT_BUILDER_TYPE); + } else if (QUERY_BUILDER_PATTERN.matcher(typeName).matches()) { + return Optional.of(NecessaryTypes.QUERY_BUILDER_TYPE); + } + return Optional.empty(); + }; } - private static String getVariableNameFromModifierContext(BSLParser.ModifierContext modifier) { - ParserRuleContext parent = modifier.getParent(); - if (parent instanceof BSLParser.ComplexIdentifierContext) { - return getComplexPathName(((BSLParser.ComplexIdentifierContext) parent), modifier); - } else if (parent instanceof BSLParser.CallStatementContext) { - BSLParser.CallStatementContext parentCall = (BSLParser.CallStatementContext) parent; - - return parentCall.modifier().stream() - .takeWhile(e -> !e.equals(modifier)) - .map(RuleContext::getText) - .collect(Collectors.joining("", parentCall.IDENTIFIER().getText(), "")); + private static BSLParserRuleContext getProperErrorContext(BSLParser.AccessCallContext ctx) { + BSLParserRuleContext errorContext = null; + BSLParserRuleContext parent = (BSLParserRuleContext) ctx.getParent(); + if (parent instanceof BSLParser.CallStatementContext) { + errorContext = parent; + } else if (parent instanceof BSLParser.ModifierContext) { + BSLParser.ModifierContext callModifier = (BSLParser.ModifierContext) parent; + errorContext = (BSLParserRuleContext) callModifier.getParent(); } - return null; - } - - private static String getComplexPathName(BSLParser.ComplexIdentifierContext ci, BSLParser.ModifierContext to) { - - return ci.modifier().stream() - .takeWhile(e -> !e.equals(to)) - .map(RuleContext::getText) - .collect(Collectors.joining("", ci.getChild(0).getText(), "")); - + return errorContext; } @Override public ParseTree visitFile(BSLParser.FileContext ctx) { - currentScope = new VariableScope(); - currentScope.enterScope(GLOBAL_SCOPE); + programScope.enterScope(GLOBAL_SCOPE); ParseTree result = super.visitFile(ctx); - currentScope = null; + programScope = null; return result; } @Override public ParseTree visitFileCodeBlock(BSLParser.FileCodeBlockContext ctx) { - currentScope.enterScope(MODULE_SCOPE); + programScope.enterScope(MODULE_SCOPE); ParseTree result = super.visitFileCodeBlock(ctx); - currentScope.leaveScope(); + programScope.leaveScope(); return result; } @Override public ParseTree visitProcedure(BSLParser.ProcedureContext ctx) { - currentScope.enterScope(ctx.procDeclaration().subName().getText()); + programScope.enterScope(ctx.procDeclaration().subName().getText()); ParseTree result = super.visitProcedure(ctx); - currentScope.leaveScope(); + programScope.leaveScope(); return result; } @Override public ParseTree visitFunction(BSLParser.FunctionContext ctx) { - currentScope.enterScope(ctx.funcDeclaration().subName().getText()); + programScope.enterScope(ctx.funcDeclaration().subName().getText()); ParseTree result = super.visitFunction(ctx); - currentScope.leaveScope(); + programScope.leaveScope(); return result; } + @Override + public ParseTree visitIfStatement(BSLParser.IfStatementContext ctx) { + if (ctx.ifBranch() != null) { + visitExpression(ctx.ifBranch().expression()); + programScope.enterFlowScope(CodeFlowType.CONDITIONAL); + visitCodeBlock(ctx.ifBranch().codeBlock()); + programScope.leaveFlowScope(); + } + for (var branch : ctx.elsifBranch()) { + visitExpression(branch.expression()); + programScope.enterFlowScope(CodeFlowType.CONDITIONAL); + visitCodeBlock(branch.codeBlock()); + programScope.leaveFlowScope(); + } + if (ctx.elseBranch() != null) { + programScope.enterFlowScope(CodeFlowType.CONDITIONAL); + visitCodeBlock(ctx.elseBranch().codeBlock()); + programScope.leaveFlowScope(); + } + + return null; + } + @Override public ParseTree visitAssignment(AssignmentContext ctx) { if (ctx.expression() == null) { return super.visitAssignment(ctx); } - BSLParser.MemberContext firstMember = ctx.expression().member(0); - if (firstMember == null) { + Set types = V8TypeHelper.getTypesFromExpressionContext(ctx.expression(), programScope); + if (types == null) { return super.visitAssignment(ctx); } + String variableName = ctx.lValue().getText(); - VariableDefinition currentVariable = new VariableDefinition(variableName); - currentVariable.addDeclaration(ctx.lValue()); - if (firstMember.complexIdentifier() != null) { - currentVariable.types.addAll(getTypesFromComplexIdentifier(firstMember.complexIdentifier())); - } else if (firstMember.constValue() != null) { - currentVariable.types.add(getTypeFromConstValue(firstMember.constValue())); + VariableDefinition currentVariable = programScope.getVariableByName(variableName) + .orElseGet(() -> programScope.addVariable(VariableDefinition.fromLValue(ctx.lValue()))); + + if (programScope.codeFlowInConditionalBlock()) { + currentVariable.addAll(types); } else { - currentVariable.addType(UNDEFINED_TYPE); + currentVariable.replaceAll(types); } - - currentScope.addVariable(currentVariable); return super.visitAssignment(ctx); } - private Set getTypesFromComplexIdentifier(BSLParser.ComplexIdentifierContext complexId) { - if (complexId.newExpression() != null) { - return Set.of(getTypeFromNewExpressionContext(complexId.newExpression())); - } else if (complexId.IDENTIFIER() != null) { - return currentScope.getVariableByName(getComplexPathName(complexId, null)) - .map(variableDefinition -> variableDefinition.types) - .orElse(Set.of(UNDEFINED_TYPE)); - } else { - return Set.of(); - } + @Override + public ParseTree visitCodeBlock(BSLParser.CodeBlockContext ctx) { + return Optional.ofNullable(ctx) + .map(super::visitCodeBlock).orElse(null); } - private void visitDescendantCodeBlock(BSLParser.CodeBlockContext ctx){ - Optional.ofNullable(ctx) - .map(e -> e.children) - .stream() - .flatMap(Collection::stream) - .forEach( t -> t.accept(this)); + @Override + public ParseTree visitExpression(BSLParser.ExpressionContext ctx) { + return Optional.ofNullable(ctx) + .map(super::visitExpression).orElse(null); } @Override @@ -240,166 +191,61 @@ public ParseTree visitAccessCall(BSLParser.AccessCallContext ctx) { if (!EXECUTE_CALL_PATTERN.matcher(ctx.methodCall().methodName().getText()).matches()) { return super.visitAccessCall(ctx); } - if (!currentScope.codeFlowInCycle()) { + if (!programScope.codeFlowInCycle()) { return super.visitAccessCall(ctx); } - String variableName = null; - BSLParserRuleContext errorContext = null; - BSLParserRuleContext parent = (BSLParserRuleContext) ctx.getParent(); - if (parent instanceof BSLParser.CallStatementContext) { - errorContext = parent; - variableName = getVariableNameFromCallStatementContext((BSLParser.CallStatementContext) parent); - } else if (parent instanceof BSLParser.ModifierContext) { - BSLParser.ModifierContext callModifier = (BSLParser.ModifierContext) parent; - errorContext = (BSLParserRuleContext) callModifier.getParent(); - variableName = getVariableNameFromModifierContext(callModifier); + BSLParserRuleContext errorContext = getProperErrorContext(ctx); + if (errorContext != null) { + String variableName = V8TypeHelper.getVariableNameFromAccessCallContext(ctx); + programScope.getVariableByName(variableName) + .filter(Predicate.not((VariableDefinition definition) -> Collections.disjoint(definition.getTypes(), NecessaryTypes.FULL_COLLECTION))) + .ifPresent(e -> diagnosticStorage.addDiagnostic(errorContext)); } - Optional variableDefinition = currentScope.getVariableByName(variableName); - BSLParserRuleContext finalErrorContext = errorContext; - if (finalErrorContext != null) { - variableDefinition.ifPresent((VariableDefinition definition) -> { - - if (definition.types.contains(QUERY_BUILDER_TYPE) - || definition.types.contains(REPORT_BUILDER_TYPE) - || definition.types.contains(QUERY_TYPE)) { - diagnosticStorage.addDiagnostic(finalErrorContext); - } - - }); - } - return super.visitAccessCall(ctx); } @Override public ParseTree visitForEachStatement(BSLParser.ForEachStatementContext ctx) { - boolean alreadyInCycle = currentScope.codeFlowInCycle(); - currentScope.flowMode.push(CodeFlowType.CYCLE); - if(alreadyInCycle) { - Optional.ofNullable(ctx.expression()) - .ifPresent( e -> e.accept(this)); - } - visitDescendantCodeBlock(ctx.codeBlock()); - currentScope.flowMode.pop(); + visitExpression(ctx.expression()); + programScope.enterFlowScope(CodeFlowType.CYCLE); + visitCodeBlock(ctx.codeBlock()); + programScope.leaveFlowScope(); return ctx; } @Override public ParseTree visitWhileStatement(BSLParser.WhileStatementContext ctx) { - currentScope.flowMode.push(CodeFlowType.CYCLE); + programScope.enterFlowScope(CodeFlowType.CYCLE); ParseTree result = super.visitWhileStatement(ctx); - currentScope.flowMode.pop(); + programScope.leaveFlowScope(); return result; } @Override public ParseTree visitForStatement(BSLParser.ForStatementContext ctx) { - boolean alreadyInCycle = currentScope.codeFlowInCycle(); - currentScope.flowMode.push(CodeFlowType.CYCLE); - if(alreadyInCycle) { - ctx.expression() - .forEach( e-> e.accept(this)); - } - visitDescendantCodeBlock(ctx.codeBlock()); - currentScope.flowMode.pop(); - return ctx; - } - - public enum CodeFlowType { - LINEAR, CYCLE - } - - @ToString - public static class VariableDefinition { - private final String variableName; - private final Set types = new HashSet<>(); - private ParseTree firstDeclaration; - - VariableDefinition(String variableName) { - this.variableName = variableName; - } - public void addType(String type) { - this.types.add(type); - } - - public void addDeclaration(ParseTree firstDeclaration) { - if (this.firstDeclaration == null) { - this.firstDeclaration = firstDeclaration; - } - } + ctx.expression() + .forEach(this::visitExpression); + programScope.enterFlowScope(CodeFlowType.CYCLE); + visitCodeBlock(ctx.codeBlock()); + programScope.leaveFlowScope(); + return ctx; } - private static class Scope { - private final String name; + enum NecessaryTypes implements V8Type { + REPORT_BUILDER_TYPE("ReportBuilder"), QUERY_BUILDER_TYPE("QueryBuilder"), QUERY_TYPE("Query"); + public static Set FULL_COLLECTION = Set.of(QUERY_TYPE, REPORT_BUILDER_TYPE, QUERY_BUILDER_TYPE); + String name; - private final HashMap variables = new HashMap<>(); - - public Scope(String name) { + NecessaryTypes(String name) { this.name = name; } - public void addVariable(VariableDefinition variableDefinition, boolean typesMerge) { - this.variables.merge( - variableDefinition.variableName, - variableDefinition, - (VariableDefinition key, VariableDefinition value) -> { - if (!typesMerge) { - key.types.clear(); - } - key.types.addAll(value.types); - - return key; - }); - } - + @Override public String getName() { - return name; + return this.name; } } - private static class VariableScope extends ArrayDeque { - private final Deque flowMode = new ArrayDeque<>(); - - public boolean codeFlowInCycle() { - final CodeFlowType flowType = flowMode.peek(); - if (flowType == null) { - return false; - } - return flowType == CodeFlowType.CYCLE; - } - - public Optional getVariableByName(String variableName) { - return Optional.ofNullable(current().variables.get(variableName)); - } - - public void addVariable(VariableDefinition variableDefinition) { - final CodeFlowType flowType = flowMode.peek(); - if (flowType == null) { - return; - } - this.current().addVariable(variableDefinition, flowType == CodeFlowType.CYCLE); - } - - public void enterScope(String name) { - Scope newScope = new Scope(name); - if (!this.isEmpty()) { - Scope prevScope = this.peek(); - newScope.variables.putAll(prevScope.variables); - } - this.push(newScope); - flowMode.push(CodeFlowType.LINEAR); - } - - public void leaveScope() { - this.pop(); - flowMode.pop(); - } - - public Scope current() { - return this.peek(); - } - - } } diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/CodeFlowType.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/CodeFlowType.java new file mode 100644 index 00000000000..2effcecfee5 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/CodeFlowType.java @@ -0,0 +1,26 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +public enum CodeFlowType { + LINEAR, CYCLE, CONDITIONAL +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/HierarchicalNamedScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/HierarchicalNamedScope.java new file mode 100644 index 00000000000..a2a73d6714d --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/HierarchicalNamedScope.java @@ -0,0 +1,82 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.values.V8NamedObject; + +import java.util.ArrayDeque; +import java.util.Arrays; +import java.util.Deque; + +@SuppressWarnings({"rawtypes", "unchecked"}) +public class HierarchicalNamedScope extends NamedScope { + private final Deque scope = new ArrayDeque<>(); + + public HierarchicalNamedScope(String name) { + super(name); + scope.push(this); + } + + public HierarchicalNamedScope() { + super(); + scope.push(this); + } + + public NamedScope current() { + return scope.peek(); + } + + public void enterSubScope(V8NamedObject object) { + NamedScope newScope = new NamedScope(); + newScope.fillFromScope(current()); + newScope.addToScope(object); + scope.push(newScope); + } + + public void enterNamedSubScope(String nameOfScope) { + NamedScope newScope = new NamedScope(nameOfScope); + newScope.fillFromScope(current()); + scope.push(newScope); + } + + public void leaveSubScope() { + scope.pop(); + } + + @Override + public boolean contains(T object) { + if (current().equals(this)) { + return super.contains(object); + } else { + return current().contains(object); + } + } + + @Override + public boolean containsAnyOf(T... objects) { + if (current().equals(this)) { + return super.containsAnyOf(Arrays.asList(objects)); + } else { + return current().containsAnyOf(objects); + } + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/HierarchicalScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/HierarchicalScope.java new file mode 100644 index 00000000000..40024084ae8 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/HierarchicalScope.java @@ -0,0 +1,80 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +import java.util.ArrayDeque; +import java.util.Deque; + +public class HierarchicalScope extends Scope { + private final Deque> scope = new ArrayDeque<>(); + + public HierarchicalScope(String name) { + super(name); + scope.push(this); + } + + public HierarchicalScope() { + super(); + scope.push(this); + } + + public Scope current() { + return scope.peek(); + } + + public void enterSubScope(T object) { + Scope newScope = new Scope<>(); + newScope.fillFromScope(current()); + newScope.addToScope(object); + scope.push(newScope); + } + + public void enterNamedScope(String nameOfScope) { + Scope newScope = new Scope<>(nameOfScope); + newScope.fillFromScope(current()); + scope.push(newScope); + } + + public void leaveSubScope() { + scope.pop(); + } + + @Override + public boolean contains(T object) { + if (current().equals(this)) { + return super.contains(object); + } else { + return current().contains(object); + } + + } + + @SafeVarargs + @Override + public final boolean containsAnyOf(T... objects) { + if (current().equals(this)) { + return super.containsAnyOf(objects); + } else { + return current().containsAnyOf(objects); + } + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/NamedScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/NamedScope.java new file mode 100644 index 00000000000..2a3f212e0fe --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/NamedScope.java @@ -0,0 +1,63 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.values.V8NamedObject; + +import java.util.HashMap; + +@SuppressWarnings("uncheked") +public class NamedScope extends Scope { + + private final HashMap namedObject = new HashMap<>(); + + public NamedScope(String name) { + super(name); + } + + public NamedScope() { + super(); + } + + @Override + public void addToScope(T variableDefinition) { + super.addToScope(variableDefinition); + this.namedObject.put(variableDefinition.getName(), variableDefinition); + } + + @Override + @SuppressWarnings("uncheked") + public void fillFromScope(Scope anotherScope) { + super.fillFromScope(anotherScope); + if (anotherScope instanceof NamedScope) { + this.namedObject.putAll(((NamedScope) anotherScope).namedObject); + } else { + anotherScope.objects.forEach(e -> + this.namedObject.putIfAbsent(e.getName(), e)); + } + } + + public T findByName(String variableName) { + return namedObject.get(variableName); + } + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/ProgramScope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/ProgramScope.java new file mode 100644 index 00000000000..c2d367d4b7c --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/ProgramScope.java @@ -0,0 +1,94 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8Type; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8TypeFromPresentationSupplier; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8TypeFromVariableSupplier; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class ProgramScope implements V8TypeFromPresentationSupplier, V8TypeFromVariableSupplier { + private final HierarchicalScope flowMode = new HierarchicalScope<>(); + private final HierarchicalNamedScope variableScope = new HierarchicalNamedScope<>(); + private List typeSuppliers = new ArrayList<>(); + + public boolean codeFlowInConditionalBlock() { + return flowMode.containsAnyOf(CodeFlowType.CYCLE, CodeFlowType.CONDITIONAL); + } + + public boolean codeFlowInCycle() { + return flowMode.contains(CodeFlowType.CYCLE); + } + + public Optional getVariableByName(String variableName) { + return Optional.ofNullable(this.variableScope.findByName(variableName)); + } + + public VariableDefinition addVariable(VariableDefinition variableDefinition) { + this.variableScope.addToScope(variableDefinition); + return variableDefinition; + } + + public void enterScope(String name) { + variableScope.enterNamedSubScope(name); + flowMode.enterSubScope(CodeFlowType.LINEAR); + } + + public void leaveScope() { + variableScope.leaveSubScope(); + flowMode.leaveSubScope(); + } + + public void enterFlowScope(CodeFlowType additionalMode) { + flowMode.enterSubScope(additionalMode); + } + + public void leaveFlowScope() { + flowMode.leaveSubScope(); + } + + @Override + public Optional getTypeFromPresentation(String presentation) { + Optional started = Optional.empty(); + for (var typeSupplier : typeSuppliers) { + started = typeSupplier.getTypeFromPresentation(presentation); + if (started.isPresent()) { + break; + } + } + return started; + } + + @Override + public Optional> getTypesFromVariable(String variableName) { + return this.getVariableByName(variableName) + .map(VariableDefinition::getTypes); + } + + public List getTypeSuppliers() { + return typeSuppliers; + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/Scope.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/Scope.java new file mode 100644 index 00000000000..67b315bb265 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/Scope.java @@ -0,0 +1,65 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +import lombok.ToString; + +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +@ToString +public class Scope { + private final String name; + protected Set objects = new HashSet<>(); + + public Scope(String name) { + this.name = name; + } + public Scope() { + this.name = "UnnamedScope"; + } + public void addToScope(T scopedObject) { + objects.add(scopedObject); + } + + public void fillFromScope(Scope anotherScope) { + this.objects.addAll(anotherScope.objects); + } + + public String getName() { + return name; + } + + public boolean contains(T object){ + return objects.contains(object); + } + + public boolean containsAnyOf(T ...objects){ + return !Collections.disjoint(this.objects, Arrays.asList(objects)); + } + public boolean containsAnyOf(Collection objects){ + return !Collections.disjoint(this.objects, objects); + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/VariableDefinition.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/VariableDefinition.java new file mode 100644 index 00000000000..aaac7c00103 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/scope/VariableDefinition.java @@ -0,0 +1,77 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.scope; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8Type; +import com.github._1c_syntax.bsl.languageserver.utils.variable.values.V8NamedObject; +import com.github._1c_syntax.bsl.parser.BSLParser; +import lombok.ToString; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.Collection; +import java.util.HashSet; +import java.util.Set; + +@ToString(exclude = {"firstDeclaration"}) +public class VariableDefinition implements V8NamedObject { + + private final String variablePath; + private final Set types = new HashSet<>(); + private ParseTree firstDeclaration; + + public VariableDefinition(String variableName) { + this.variablePath = variableName; + } + + public static VariableDefinition fromLValue(BSLParser.LValueContext lValue) { + var variable = new VariableDefinition(lValue.getText()); + variable.addDeclaration(lValue); + return variable; + } + + public Set getTypes() { + return types; + } + + public void addDeclaration(ParseTree firstDeclaration) { + if (this.firstDeclaration == null) { + this.firstDeclaration = firstDeclaration; + } + } + + public void clearTypes() { + this.types.clear(); + } + + public void addAll(Collection newTypes) { + this.types.addAll(newTypes); + } + public void replaceAll(Collection newTypes) { + this.types.clear(); + this.types.addAll(newTypes); + } + public String getName() { + return variablePath; + } + + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8BasicType.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8BasicType.java new file mode 100644 index 00000000000..5633fd790c5 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8BasicType.java @@ -0,0 +1,48 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +public enum V8BasicType implements V8Type, V8TypedObject { + STRING_TYPE("Строка"), DATE_TYPE("Дата"), NUMBER_TYPE("Число"), + BOOLEAN_TYPE("Булево"), NULL_TYPE("Null"), UNDEFINED_TYPE("Неопределено"), + TYPE_TYPE("Тип"); + + private String name; + + V8BasicType(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + @Override + public String toString() { + return getName(); + } + + @Override + public V8Type getType() { + return TYPE_TYPE; + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8Type.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8Type.java new file mode 100644 index 00000000000..25c56d03026 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8Type.java @@ -0,0 +1,27 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.values.V8NamedObject; + +public interface V8Type extends V8NamedObject { +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeFromPresentationSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeFromPresentationSupplier.java new file mode 100644 index 00000000000..e4808da93ef --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeFromPresentationSupplier.java @@ -0,0 +1,28 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +import java.util.Optional; + +public interface V8TypeFromPresentationSupplier extends V8TypeSupplier { + Optional getTypeFromPresentation(String presentation); +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeFromVariableSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeFromVariableSupplier.java new file mode 100644 index 00000000000..5369a43c614 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeFromVariableSupplier.java @@ -0,0 +1,29 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +import java.util.Optional; +import java.util.Set; + +public interface V8TypeFromVariableSupplier extends V8TypeSupplier { + Optional> getTypesFromVariable(String variableName); +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeHelper.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeHelper.java new file mode 100644 index 00000000000..42a00437d0d --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeHelper.java @@ -0,0 +1,266 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.scope.ProgramScope; +import com.github._1c_syntax.bsl.languageserver.utils.variable.values.V8BasicValue; +import com.github._1c_syntax.bsl.languageserver.utils.variable.values.V8Value; +import com.github._1c_syntax.bsl.parser.BSLParser; +import com.github._1c_syntax.bsl.parser.BSLParserRuleContext; +import lombok.ToString; +import org.antlr.v4.runtime.ParserRuleContext; +import org.antlr.v4.runtime.RuleContext; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.Collectors; + + +public class V8TypeHelper { + private static V8TypeFromPresentationSupplier anyTypeSupplier = (typeNameValue) -> Optional.of(new AnyType(typeNameValue)); + + private static Function> GET_CONST_VALUE_FROM_EXPRESSION = (paramListContext) -> { + if (paramListContext.member().size() == 1) { + return getConstValue(paramListContext.member(0).constValue()); + } else { + return Optional.empty(); + } + }; + + + private static Predicate CONST_VALUE_TYPE_IS_STRING = constValue -> V8BasicType.STRING_TYPE.equals(constValue.getType()); + private static Predicate CONST_VALUE_TYPE_IS_NUMBER = constValue -> V8BasicType.NUMBER_TYPE.equals(constValue.getType()); + private static Predicate CONST_VALUE_TYPE_IS_BOOLEAN = constValue -> V8BasicType.BOOLEAN_TYPE.equals(constValue.getType()); + + /** + * @param constValue Константное значение, тип которого необходимо получить + * @return тип переданного выражения или null + */ + public static V8Type getConstValueType(BSLParser.ConstValueContext constValue) { + + return getConstValue(constValue) + .map(V8Value::getType) + .orElse(null); + + } + + /** + * @param constValue Константное значение, значение которого необходимо получить + * @return значение переданного выражения или null + */ + public static Optional getConstValue(BSLParser.ConstValueContext constValue) { + if (constValue == null) { + return Optional.empty(); + } + + if (constValue.string() != null) { + return V8BasicValue.fromStringLiteral(constValue.string().getText()); + } else if (constValue.DATETIME() != null) { + // TODO need reimplement V8BasicType.DATE_TYPE; + } else if (constValue.numeric() != null) { + return V8BasicValue.fromNumberLiteral(constValue.numeric().getText()); + } else if (constValue.TRUE() != null) { + return Optional.of(V8BasicValue.TRUE); + } else if (constValue.FALSE() != null) { + return Optional.of(V8BasicValue.FALSE); + } else if (constValue.NULL() != null) { + return Optional.of(V8BasicValue.NULL); + } else if (constValue.UNDEFINED() != null) { + return Optional.of(V8BasicValue.UNDEFINED); + } + return Optional.empty(); + } + + + public static Optional getParamByIndexInDoCallContext(BSLParser.DoCallContext doCallContext, int index) { + return Optional.ofNullable(doCallContext) + .map(BSLParser.DoCallContext::callParamList) + .stream() + .flatMap(e -> e.callParam().stream()) + .skip(index) + .findFirst() + .map(BSLParser.CallParamContext::expression); + } + + public static Supplier> getStringConstantFromFirstParam(BSLParser.DoCallContext doCallContext) { + return () -> getParamByIndexInDoCallContext(doCallContext, 0) + .flatMap(GET_CONST_VALUE_FROM_EXPRESSION) + .filter(CONST_VALUE_TYPE_IS_STRING) + .map(V8Value::getValue) + .filter(String.class::isInstance) + .map(String.class::cast); + } + + public static Supplier> getFloatNumberConstantFromParam(BSLParser.DoCallContext doCallContext, int index) { + return () -> getParamByIndexInDoCallContext(doCallContext, index) + .flatMap(GET_CONST_VALUE_FROM_EXPRESSION) + .filter(CONST_VALUE_TYPE_IS_NUMBER) + .map(V8Value::getValue) + .filter(Float.class::isInstance) + .map(Float.class::cast); + + } + + public static Supplier> getBooleanConstantFromParam(BSLParser.DoCallContext doCallContext, int index, Boolean defaultValue) { + return () -> { + var param = getParamByIndexInDoCallContext(doCallContext, index); + if (param.isPresent()) { + return param + .flatMap(GET_CONST_VALUE_FROM_EXPRESSION) + .filter(CONST_VALUE_TYPE_IS_BOOLEAN) + .map(V8Value::getValue) + .filter(Boolean.class::isInstance) + .map(Boolean.class::cast); + } else { + return Optional.ofNullable(defaultValue); + } + }; + } + + public static V8Type getTypeFromNewExpressionContext(BSLParser.NewExpressionContext newExpression, V8TypeSupplier supplier) { + + Optional typeName = Optional.ofNullable(newExpression.typeName()) + .map(RuleContext::getText) + .or(getStringConstantFromFirstParam(newExpression.doCall())); + + if (typeName.isPresent()) { + String typeNameValue = typeName.get(); + Optional calculatedType = Optional.empty(); + if (supplier instanceof V8TypeFromPresentationSupplier) { + calculatedType = ((V8TypeFromPresentationSupplier) supplier).getTypeFromPresentation(typeNameValue); + } + return calculatedType.orElseGet(anyTypeSupplier.getTypeFromPresentation(typeNameValue)::get); + } else { + return V8BasicType.UNDEFINED_TYPE; + } + } + + public static String getVariableNameFromComplexIdentifierContext(BSLParser.ComplexIdentifierContext complexIdentifier) { + return getLimitedText(complexIdentifier, null); + } + + public static String getLimitedText(ParserRuleContext context, BSLParserRuleContext limitedChild) { + if (limitedChild == null) { + return context.getText(); + } + + return context.children.stream() + .takeWhile(Predicate.not(Predicate.isEqual(limitedChild))) + .map(ParseTree::getText) + .collect(Collectors.joining("")); + } + + public static String getVariableNameFromCallStatementContext(BSLParser.CallStatementContext callStatement, BSLParserRuleContext limitedChild) { + return getLimitedText(callStatement, limitedChild); + } + + public static String getVariableNameFromModifierContext(BSLParser.ModifierContext modifier) { + ParserRuleContext parent = modifier.getParent(); + if (parent instanceof BSLParser.ComplexIdentifierContext) { + return V8TypeHelper.getLimitedText(parent, modifier); + } else if (parent instanceof BSLParser.CallStatementContext) { + return V8TypeHelper.getLimitedText(parent, modifier); + } + return null; + } + + public static String getVariableNameFromAccessCallContext(BSLParser.AccessCallContext accessCall) { + String variableName = null; + BSLParserRuleContext parent = (BSLParserRuleContext) accessCall.getParent(); + if (parent instanceof BSLParser.CallStatementContext) { + variableName = V8TypeHelper.getVariableNameFromCallStatementContext((BSLParser.CallStatementContext) parent, accessCall); + } else if (parent instanceof BSLParser.ModifierContext) { + variableName = V8TypeHelper.getVariableNameFromModifierContext((BSLParser.ModifierContext) parent); + } + return variableName; + } + + public static Set getTypesFromComplexIdentifier(BSLParser.ComplexIdentifierContext complexId, V8TypeSupplier supplier) { + if (complexId.newExpression() != null) { + return Set.of(getTypeFromNewExpressionContext(complexId.newExpression(), supplier)); + } else if (complexId.IDENTIFIER() != null) { + Optional> calculatedTypes = Optional.empty(); + if (supplier instanceof V8TypeFromVariableSupplier) { + calculatedTypes = ((V8TypeFromVariableSupplier) supplier).getTypesFromVariable(getVariableNameFromComplexIdentifierContext(complexId)); + } + return calculatedTypes.orElse(Set.of(V8BasicType.UNDEFINED_TYPE)); + } + return Set.of(); + } + + public static Set getTypesFromExpressionContext(BSLParser.ExpressionContext ctx, ProgramScope programScope) { + List members = ctx.member(); + if (members.size() != 1) { + if (ctx.operation().size() == 0) { + return null; + } + BSLParser.OperationContext firstOperation = ctx.operation(0); + if (firstOperation.boolOperation() != null + || firstOperation.compareOperation() != null) { + return Collections.singleton(V8BasicType.BOOLEAN_TYPE); + } else if(firstOperation.MODULO() != null + || firstOperation.QUOTIENT() != null + || firstOperation.MUL() != null) { + return Collections.singleton(V8BasicType.NUMBER_TYPE); + } + } + + BSLParser.MemberContext firstMember = members.get(0); + if (firstMember == null) { + return null; + } + Set types; + + if (firstMember.complexIdentifier() != null) { + types = V8TypeHelper.getTypesFromComplexIdentifier(firstMember.complexIdentifier(), programScope); + } else if (firstMember.constValue() != null) { + types = Collections.singleton(V8TypeHelper.getConstValueType(firstMember.constValue())); + } else if (firstMember.expression() != null) { + types = getTypesFromExpressionContext(firstMember.expression(), programScope); + } else { + types = Collections.singleton(V8BasicType.UNDEFINED_TYPE); + } + return types; + } + + @ToString + static class AnyType implements V8Type { + + String name; + + AnyType(String name) { + this.name = name; + } + + @Override + public String getName() { + return this.name; + } + } +} + diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeSupplier.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeSupplier.java new file mode 100644 index 00000000000..53e4d53bff0 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypeSupplier.java @@ -0,0 +1,26 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +public interface V8TypeSupplier { + +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypedObject.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypedObject.java new file mode 100644 index 00000000000..e0da003cba8 --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/types/V8TypedObject.java @@ -0,0 +1,26 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.types; + +public interface V8TypedObject { + V8Type getType(); +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8BasicValue.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8BasicValue.java new file mode 100644 index 00000000000..f34872f12bb --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8BasicValue.java @@ -0,0 +1,134 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.values; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8BasicType; +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8Type; + +import java.util.Optional; + +public enum V8BasicValue implements V8Value { + TRUE(V8BasicType.BOOLEAN_TYPE), FALSE(V8BasicType.BOOLEAN_TYPE), NULL(V8BasicType.NULL_TYPE), UNDEFINED(V8BasicType.UNDEFINED_TYPE); + + private V8Type type; + + V8BasicValue(V8Type type) { + this.type = type; + } + + public static Optional fromStringLiteral(String literal) { + return Optional.of(new StringValue(literal.substring(1, literal.length() - 1).replace("\"\"","\""))); + } + + public static Optional fromNumberLiteral(String literal) { + return Optional.of(new FloatValue(Float.parseFloat(literal))); + } + + @Override + public V8Type getType() { + return type; + } + + @Override + public Object getValue() { + if(this.equals(TRUE)){ + return Boolean.TRUE; + }else if(this.equals(FALSE)){ + return Boolean.FALSE; + } + return null; + } + + @Override + public void setValue(Object o) { + + } + + static class FloatValue implements V8Value { + private Float value; + + public FloatValue(Float value) { + this.value = value; + } + + @Override + public V8Type getType() { + return V8BasicType.NUMBER_TYPE; + } + + @Override + public void setValue(Float s) { + this.value = s; + } + + @Override + public Float getValue() { + return value; + } + } + + static class V8TypeValue implements V8Value { + private V8Type value; + + public V8TypeValue(V8Type value) { + this.value = value; + } + + @Override + public V8Type getType() { + return V8BasicType.TYPE_TYPE; + } + + @Override + public void setValue(V8Type s) { + this.value = s; + } + + @Override + public V8Type getValue() { + return value; + } + } + + public static class StringValue implements V8Value { + String value; + + StringValue(String literal) { + value = literal; + } + + @Override + public V8Type getType() { + return V8BasicType.STRING_TYPE; + } + + @Override + public String getValue() { + return value; + } + + @Override + public void setValue(String s) { + this.value = s; + } + } +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8NamedObject.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8NamedObject.java new file mode 100644 index 00000000000..4d486096fcf --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8NamedObject.java @@ -0,0 +1,26 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.values; + +public interface V8NamedObject { + String getName(); +} diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8Value.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8Value.java new file mode 100644 index 00000000000..da7c7f901ef --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/utils/variable/values/V8Value.java @@ -0,0 +1,29 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.utils.variable.values; + +import com.github._1c_syntax.bsl.languageserver.utils.variable.types.V8TypedObject; + +public interface V8Value extends V8TypedObject { + void setValue(T t); + T getValue(); +} diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/parameters-schema.json b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/parameters-schema.json index e38e48aed8d..eea56a53445 100644 --- a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/parameters-schema.json +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/parameters-schema.json @@ -4,6 +4,16 @@ "type": "object", "title": "BSL Language Server Configuration File. Parameters definition part", "definitions": { + "AttachIdleHandler": { + "description": "Usage AttachIdleHandler", + "default": true, + "type": [ + "boolean", + "object" + ], + "title": "Usage AttachIdleHandler", + "$id": "#/definitions/AttachIdleHandler" + }, "BeginTransactionBeforeTryCatch": { "description": "Violating transaction rules for the 'BeginTransaction' method", "default": true, diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json index fab08d69adf..7b32925fbba 100644 --- a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json @@ -23,6 +23,9 @@ "$ref": "#/definitions/parameter" }, "properties": { + "AttachIdleHandler": { + "$ref": "parameters-schema.json#/definitions/AttachIdleHandler" + }, "BeginTransactionBeforeTryCatch": { "$ref": "parameters-schema.json#/definitions/BeginTransactionBeforeTryCatch" }, diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic_en.properties b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic_en.properties new file mode 100644 index 00000000000..772cd422a5d --- /dev/null +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic_en.properties @@ -0,0 +1,2 @@ +diagnosticMessage=Select existed method in params +diagnosticName=Usage AttachIdleHandler diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic_ru.properties b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic_ru.properties new file mode 100644 index 00000000000..33b3d483c19 --- /dev/null +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnostic_ru.properties @@ -0,0 +1,2 @@ +diagnosticMessage=Выбирите существующий метод в параметрах +diagnosticName=Использование метода ПодключитьОбработчикОжидания diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnosticTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnosticTest.java new file mode 100644 index 00000000000..4f5a10bec0b --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AttachIdleHandlerDiagnosticTest.java @@ -0,0 +1,53 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright © 2018-2020 + * Alexey Sosnoviy , Nikita Gryzlov and contributors + * + * SPDX-License-Identifier: LGPL-3.0-or-later + * + * BSL Language Server is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3.0 of the License, or (at your option) any later version. + * + * BSL Language Server is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with BSL Language Server. + */ +package com.github._1c_syntax.bsl.languageserver.diagnostics; + +import org.eclipse.lsp4j.Diagnostic; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static com.github._1c_syntax.bsl.languageserver.util.Assertions.assertThat; + +class AttachIdleHandlerDiagnosticTest extends AbstractDiagnosticTest { + AttachIdleHandlerDiagnosticTest() { + super(AttachIdleHandlerDiagnostic.class); + } + + + @Test + void test() { + + List diagnostics = getDiagnostics(); + + assertThat(diagnostics).hasSize(5); + assertThat(diagnostics, true) + .hasRange(9, 4, 9, 32) + .hasRange(38, 0, 38, 27) + .hasRange(42, 0, 42, 28) + .hasRange(44, 0, 44, 28) + .hasRange(52, 0, 52, 28); + + + } + +} diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnosticTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnosticTest.java index 81e82a4be88..7955c15f8c0 100644 --- a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnosticTest.java +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/CreateQueryInCycleDiagnosticTest.java @@ -39,7 +39,7 @@ class CreateQueryInCycleDiagnosticTest extends AbstractDiagnosticTest diagnostics = getDiagnostics(); - assertThat(diagnostics).hasSize(10); + assertThat(diagnostics).hasSize(11); assertThat(diagnostics, true) .hasRange(4, 8, 4, 36) .hasRange(27, 23, 27, 47) @@ -50,6 +50,7 @@ void test() { .hasRange(66, 4, 66, 22) .hasRange(73, 2, 73, 30) .hasRange(79, 4, 79, 34) - .hasRange(90, 41, 90, 71); + .hasRange(90, 41, 90, 71) + .hasRange(103, 4, 103, 25); } } diff --git a/src/test/resources/diagnostics/AttachIdleHandlerDiagnostic.bsl b/src/test/resources/diagnostics/AttachIdleHandlerDiagnostic.bsl new file mode 100644 index 00000000000..7d637174d5a --- /dev/null +++ b/src/test/resources/diagnostics/AttachIdleHandlerDiagnostic.bsl @@ -0,0 +1,53 @@ + +&НаКлиенте +Процедура НайтиСтрокуСОшибкой(Команда) + + Если ПустаяСтрока(СтрокаПоиска) Тогда + ТекущийЭлемент = Элементы.СтрокаПоиска; + Возврат; + КонецЕсли; + + ПодключитьОбработчикОжидания("НеизвестныйПоиск", 0.1, Истина); //bad + +КонецПроцедуры + +&НаКлиенте +Процедура НайтиСтроку(Команда) + + Если ПустаяСтрока(СтрокаПоиска) Тогда + ТекущийЭлемент = Элементы.СтрокаПоиска; + Возврат; + КонецЕсли; + + ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1, Истина); //good + +КонецПроцедуры + +&НаКлиенте +Процедура ВыполнитьПоиск() + + НичегоНеНашли = Ложь; + ИскатьСтрокуНаСервере(НичегоНеНашли); + + Если НичегоНеНашли Тогда + Предупреждение(НСтр("ru = 'Строка не найдена'")); + КонецЕсли; + +КонецПроцедуры + + +ОтключитьОбработчикОжидания("НеизвестныйПоиск"); //bad + +ОтключитьОбработчикОжидания("ВыполнитьПоиск"); //good + +ПодключитьОбработчикОжидания("НайтиСтроку", 0.1, Истина); //bad + +ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1, Ложь); //bad + +ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1, Истина); //good + +ПодключитьОбработчикОжидания("ВыполнитьПоиск", 1, Ложь); //good + +ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1, Переменная); //good + +ПодключитьОбработчикОжидания("ВыполнитьПоиск", 0.1); //bad diff --git a/src/test/resources/diagnostics/CreateQueryInCycleDiagnostic.bsl b/src/test/resources/diagnostics/CreateQueryInCycleDiagnostic.bsl index fd524b44328..c5f36c3ab9c 100644 --- a/src/test/resources/diagnostics/CreateQueryInCycleDiagnostic.bsl +++ b/src/test/resources/diagnostics/CreateQueryInCycleDiagnostic.bsl @@ -93,4 +93,13 @@ КонецЦикла; +КонецЦикла; + +Запрос222 = Новый("Запрос"); +Если Ложь Тогда + Запрос222 = 1; +КонецЦикла + +Для ит = 1 По 10 Цикл + Запрос222.Выполнить();// Тут КонецЦикла; \ No newline at end of file