From 85d0907f45181570406f73deac4e556d18841d3d Mon Sep 17 00:00:00 2001 From: Artur Ayukhanov Date: Wed, 21 Dec 2022 09:35:06 +0300 Subject: [PATCH] =?UTF-8?q?feat(diagnostic):=20=D0=9D=D0=BE=D0=B2=D0=BE?= =?UTF-8?q?=D0=B5=20=D0=BF=D1=80=D0=B0=D0=B2=D0=B8=D0=BB=D0=BE=20"=D0=9E?= =?UTF-8?q?=D0=B1=D1=80=D0=B0=D1=89=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BA=20?= =?UTF-8?q?=D0=BE=D1=82=D1=81=D1=83=D1=82=D1=81=D1=82=D0=B2=D1=83=D1=8E?= =?UTF-8?q?=D1=89=D0=B5=D0=BC=D1=83=20=D0=BC=D0=B5=D1=82=D0=BE=D0=B4=D1=83?= =?UTF-8?q?=20=D0=BE=D0=B1=D1=89=D0=B5=D0=B3=D0=BE=20=D0=BC=D0=BE=D0=B4?= =?UTF-8?q?=D1=83=D0=BB=D1=8F=20MissingCommonModuleMethod=20"-=20=20=D0=93?= =?UTF-8?q?=D0=9E=D0=A2=D0=9E=D0=92=D0=9E=20(#2827)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Реализация правила * Обращение к приватным методам * Исключил ФП параметры с именами общих модулей * переименовал правило * документация + настройка правила * добавил тег правила * уточнил сообщения правила * использован символьный репозиторий вместо работы с аст-деревом * @CleanupContextBeforeClassAndAfterEachTestMethod * реализованы недостающие кейсы * убрал комментарий * комментарий про приватные методы исключил срабатывание на внутренних вызовах внутри общих модулей * Поправил текст сообщения исправил замечания из ПР * исправил замечания из ПР * кейс для покрытия * уточнил проверку приватных методов * замечание СонарЛинт --- docs/diagnostics/MissingCommonModuleMethod.md | 23 ++++ .../diagnostics/MissingCommonModuleMethod.md | 16 +++ .../MissingCommonModuleMethodDiagnostic.java | 128 ++++++++++++++++++ .../configuration/parameters-schema.json | 10 ++ ...CommonModuleMethodDiagnostic_en.properties | 4 + ...CommonModuleMethodDiagnostic_ru.properties | 4 + ...ssingCommonModuleMethodDiagnosticTest.java | 70 ++++++++++ .../MissingCommonModuleMethodDiagnostic.bsl | 37 +++++ 8 files changed, 292 insertions(+) create mode 100644 docs/diagnostics/MissingCommonModuleMethod.md create mode 100644 docs/en/diagnostics/MissingCommonModuleMethod.md create mode 100644 src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java create mode 100644 src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_en.properties create mode 100644 src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_ru.properties create mode 100644 src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnosticTest.java create mode 100644 src/test/resources/diagnostics/MissingCommonModuleMethodDiagnostic.bsl diff --git a/docs/diagnostics/MissingCommonModuleMethod.md b/docs/diagnostics/MissingCommonModuleMethod.md new file mode 100644 index 00000000000..7745c3825eb --- /dev/null +++ b/docs/diagnostics/MissingCommonModuleMethod.md @@ -0,0 +1,23 @@ +# Обращение к отсутствующему методу общего модуля (MissingCommonModuleMethod) + + +## Описание диагностики + +Правило регистрирует ошибочные обращения к методам общих модулей. +Находятся проблемные варианты +- когда метода нет в указанном общем модуле +- когда метод есть в общем модуле, но метод не является экспортным +- когда у общего модуля отсутствуют исходники, все обращения к любым его методам помечаются как ошибочные + +Исключаются варианты +- когда имя переменной совпадает с именем общего модуля +## Примеры + + +## Источники + + diff --git a/docs/en/diagnostics/MissingCommonModuleMethod.md b/docs/en/diagnostics/MissingCommonModuleMethod.md new file mode 100644 index 00000000000..306771c1e60 --- /dev/null +++ b/docs/en/diagnostics/MissingCommonModuleMethod.md @@ -0,0 +1,16 @@ +# Referencing a missing common module method (MissingCommonModuleMethod) + + +## Description + + +## Examples + + +## Sources + + diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java new file mode 100644 index 00000000000..60d6465db7f --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic.java @@ -0,0 +1,128 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2022 + * Alexey Sosnoviy , Nikita Fedkin 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.DiagnosticMetadata; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticScope; +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.references.model.LocationRepository; +import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType; +import com.github._1c_syntax.bsl.languageserver.references.model.SymbolOccurrence; +import com.github._1c_syntax.bsl.languageserver.utils.Trees; +import com.github._1c_syntax.bsl.parser.BSLParserRuleContext; +import com.github._1c_syntax.bsl.types.ConfigurationSource; +import com.github._1c_syntax.bsl.types.ModuleType; +import lombok.AllArgsConstructor; +import lombok.RequiredArgsConstructor; +import lombok.Value; +import org.antlr.v4.runtime.tree.ParseTree; +import org.eclipse.lsp4j.Range; +import org.eclipse.lsp4j.SymbolKind; + +import java.util.Optional; + +@DiagnosticMetadata( + type = DiagnosticType.ERROR, + severity = DiagnosticSeverity.BLOCKER, + scope = DiagnosticScope.BSL, + minutesToFix = 5, + tags = { + DiagnosticTag.ERROR + } +) + +@RequiredArgsConstructor +public class MissingCommonModuleMethodDiagnostic extends AbstractDiagnostic { + public static final String PRIVATE_METHOD_MESSAGE = "privateMethod"; + private final LocationRepository locationRepository; + + private static String getMethodNameByLocation(BSLParserRuleContext node, Range range) { + return Trees.findTerminalNodeContainsPosition(node, range.getEnd()) + .map(ParseTree::getText) + .orElseThrow(); + } + + @Override + protected void check() { + if (documentContext.getServerContext().getConfiguration().getConfigurationSource() == ConfigurationSource.EMPTY){ + return; + } + locationRepository.getSymbolOccurrencesByLocationUri(documentContext.getUri()) + .filter(symbolOccurrence -> symbolOccurrence.getOccurrenceType() == OccurrenceType.REFERENCE) + .filter(symbolOccurrence -> symbolOccurrence.getSymbol().getSymbolKind() == SymbolKind.Method) + .filter(symbolOccurrence -> symbolOccurrence.getSymbol().getModuleType() == ModuleType.CommonModule) + .map(this::getReferenceToMethodCall) + .flatMap(Optional::stream) + .forEach(this::fireIssue); + } + + private Optional getReferenceToMethodCall(SymbolOccurrence symbolOccurrence) { + final var symbol = symbolOccurrence.getSymbol(); + final var document = documentContext.getServerContext() + .getDocument(symbol.getMdoRef(), symbol.getModuleType()) + .orElseThrow(); + final var mdObject = document.getMdObject().orElseThrow(); + + // т.к. через refIndex.getReferences нельзя получить приватные методы, приходится обходить символы модуля + final var methodSymbol = document + .getSymbolTree().getMethodSymbol(symbol.getSymbolName()); + if (methodSymbol.isEmpty()){ + final var location = symbolOccurrence.getLocation(); + // Нельзя использовать symbol.getSymbolName(), т.к. имя в нижнем регистре + return Optional.of( + new CallData(mdObject.getName(), + getMethodNameByLocation(documentContext.getAst(), location.getRange()), + location.getRange(), false, false)); + } + // вызовы приватных методов внутри самого модуля пропускаем + if (document.getUri().equals(documentContext.getUri())){ + return Optional.empty(); + } + return methodSymbol + .filter(methodSymbol2 -> !methodSymbol2.isExport()) + .map(methodSymbol1 -> new CallData(mdObject.getName(), + methodSymbol1.getName(), + symbolOccurrence.getLocation().getRange(), true, true)); + } + + private void fireIssue(CallData callData) { + final String message; + if (!callData.exists){ + message = info.getMessage(callData.methodName, callData.moduleName); + } else { + message = info.getResourceString(PRIVATE_METHOD_MESSAGE, callData.methodName, callData.moduleName); + } + diagnosticStorage.addDiagnostic(callData.moduleMethodRange, message); + } + + @Value + @AllArgsConstructor + private static class CallData { + String moduleName; + String methodName; + Range moduleMethodRange; + boolean nonExport; + boolean exists; + } +} 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 25601dee8b8..e08a71965de 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 @@ -1082,6 +1082,16 @@ }, "$id": "#/definitions/MissingCodeTryCatchEx" }, + "MissingCommonModuleMethod": { + "description": "Referencing a missing common module method", + "default": true, + "type": [ + "boolean", + "object" + ], + "title": "Referencing a missing common module method", + "$id": "#/definitions/MissingCommonModuleMethod" + }, "MissingEventSubscriptionHandler": { "description": "Event subscription handler missing", "default": true, diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_en.properties b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_en.properties new file mode 100644 index 00000000000..4710f919fa2 --- /dev/null +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_en.properties @@ -0,0 +1,4 @@ +diagnosticMessage=The method %s of %s common module does not exist +diagnosticName=Referencing a missing common module method + +privateMethod=Correct the reference to the non export %s method of the common module %s \ No newline at end of file diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_ru.properties b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_ru.properties new file mode 100644 index 00000000000..1ae0931663e --- /dev/null +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnostic_ru.properties @@ -0,0 +1,4 @@ +diagnosticMessage=Метод %s общего модуля %s не существует +diagnosticName=Обращение к отсутствующему методу общего модуля + +privateMethod=Исправьте обращение к закрытому, неэкспортному методу %s общего модуля %s \ No newline at end of file diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnosticTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnosticTest.java new file mode 100644 index 00000000000..92464a98b9f --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MissingCommonModuleMethodDiagnosticTest.java @@ -0,0 +1,70 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2022 + * Alexey Sosnoviy , Nikita Fedkin 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.util.CleanupContextBeforeClassAndAfterEachTestMethod; +import com.github._1c_syntax.utils.Absolute; +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; + +@CleanupContextBeforeClassAndAfterEachTestMethod +class MissingCommonModuleMethodDiagnosticTest extends AbstractDiagnosticTest { + + private static final String PATH_TO_METADATA = "src/test/resources/metadata/designer"; + + MissingCommonModuleMethodDiagnosticTest() { + super(MissingCommonModuleMethodDiagnostic.class); + } + + @Test + void test() { + initServerContext(Absolute.path(PATH_TO_METADATA)); + + List diagnostics = getDiagnostics(); + + assertThat(diagnostics, true) + .hasMessageOnRange("Метод МетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 1, 22, 41) + .hasMessageOnRange("Метод ДругойМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 2, 26, 51) + .hasMessageOnRange("Метод ЕщеМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 3, 22, 44) + .hasMessageOnRange("Метод ЕщеОдинМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 4, 22, 48) + .hasMessageOnRange("Метод ЕщеДругойМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 5, 26, 54) + + .hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу РегистрацияИзмененийПередУдалением общего модуля ПервыйОбщийМодуль", 11, 22, 56) + .hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 12, 26, 30) + .hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 13, 22, 26) + .hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 14, 22, 26) + .hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 15, 26, 30) + .hasSize(10); + } + + @Test + void testWithoutMetadata() { + + List diagnostics = getDiagnostics(); + + assertThat(diagnostics).isEmpty(); + } +} diff --git a/src/test/resources/diagnostics/MissingCommonModuleMethodDiagnostic.bsl b/src/test/resources/diagnostics/MissingCommonModuleMethodDiagnostic.bsl new file mode 100644 index 00000000000..aa665cee4fb --- /dev/null +++ b/src/test/resources/diagnostics/MissingCommonModuleMethodDiagnostic.bsl @@ -0,0 +1,37 @@ +Процедура Тест1() + ПервыйОбщийМодуль.МетодНесуществующий(1, 2); // ошибка + А = ПервыйОбщийМодуль.ДругойМетодНесуществующий(); // ошибка + ПервыйОбщийМодуль.ЕщеМетодНесуществующий().Добавить(); // ошибка + ПервыйОбщийМодуль.ЕщеОдинМетодНесуществующий().Реквизит = 10; // ошибка + Б = ПервыйОбщийМодуль.ЕщеДругойМетодНесуществующий().Добавить(); // ошибка + + НесуществующийОбщийМодульИлиПростоПеременная.МетодНесуществующий(1, 2); // не ошибка +КонецПроцедуры + +Процедура Тест2_ОбращениеКПриватномуМетоду() + ПервыйОбщийМодуль.РегистрацияИзмененийПередУдалением(Источник, Отказ); // ошибка + А = ПервыйОбщийМодуль.Тест(); // ошибка + ПервыйОбщийМодуль.Тест().Добавить(); // ошибка + ПервыйОбщийМодуль.Тест().Реквизит = 10; // ошибка + Б = ПервыйОбщийМодуль.Тест().Добавить(); // ошибка +КонецПроцедуры + +Процедура Тест3() + ПервыйОбщийМодуль.НеУстаревшаяПроцедура(); // не ошибка + А = ПервыйОбщийМодуль.НеУстаревшаяФункция(); // не ошибка + ПервыйОбщийМодуль.НеУстаревшаяФункция().Добавить(); // не ошибка + ПервыйОбщийМодуль.НеУстаревшаяФункция().Реквизит = 10; // не ошибка + Б = ПервыйОбщийМодуль.НеУстаревшаяФункция().Добавить(); // не ошибка +КонецПроцедуры + +Процедура Тест4_ИмяПараметр(ПервыйОбщийМодуль) + ПервыйОбщийМодуль.МетодНесуществующий(1, 2); // не ошибка + А = ПервыйОбщийМодуль.ДругойМетодНесуществующий(); // не ошибка + ПервыйОбщийМодуль.ЕщеМетодНесуществующий().Добавить(); // не ошибка + ПервыйОбщийМодуль.ЕщеОдинМетодНесуществующий().Реквизит = 10; // не ошибка + Б = ПервыйОбщийМодуль.ЕщеДругойМетодНесуществующий().Добавить(); // не ошибка +КонецПроцедуры + +Процедура Тест5_МодулиМенеджеров() + Справочники.Справочник1.НесуществующийМетод(); // пока не ошибка +КонецПроцедуры \ No newline at end of file