Skip to content

Commit 85d0907

Browse files
authored
feat(diagnostic): Новое правило "Обращение к отсутствующему методу общего модуля MissingCommonModuleMethod "- ГОТОВО (#2827)
* Реализация правила * Обращение к приватным методам * Исключил ФП параметры с именами общих модулей * переименовал правило * документация + настройка правила * добавил тег правила * уточнил сообщения правила * использован символьный репозиторий вместо работы с аст-деревом * @CleanupContextBeforeClassAndAfterEachTestMethod * реализованы недостающие кейсы * убрал комментарий * комментарий про приватные методы исключил срабатывание на внутренних вызовах внутри общих модулей * Поправил текст сообщения исправил замечания из ПР * исправил замечания из ПР * кейс для покрытия * уточнил проверку приватных методов * замечание СонарЛинт
1 parent 829ef80 commit 85d0907

File tree

8 files changed

+292
-0
lines changed

8 files changed

+292
-0
lines changed
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Обращение к отсутствующему методу общего модуля (MissingCommonModuleMethod)
2+
3+
<!-- Блоки выше заполняются автоматически, не трогать -->
4+
## Описание диагностики
5+
<!-- Описание диагностики заполняется вручную. Необходимо понятным языком описать смысл и схему работу -->
6+
Правило регистрирует ошибочные обращения к методам общих модулей.
7+
Находятся проблемные варианты
8+
- когда метода нет в указанном общем модуле
9+
- когда метод есть в общем модуле, но метод не является экспортным
10+
- когда у общего модуля отсутствуют исходники, все обращения к любым его методам помечаются как ошибочные
11+
12+
Исключаются варианты
13+
- когда имя переменной совпадает с именем общего модуля
14+
## Примеры
15+
<!-- В данном разделе приводятся примеры, на которые диагностика срабатывает, а также можно привести пример, как можно исправить ситуацию -->
16+
17+
## Источники
18+
<!-- Необходимо указывать ссылки на все источники, из которых почерпнута информация для создания диагностики -->
19+
<!-- Примеры источников
20+
21+
* Источник: [Стандарт: Тексты модулей](https://its.1c.ru/db/v8std#content:456:hdoc)
22+
* Полезная информация: [Отказ от использования модальных окон](https://its.1c.ru/db/metod8dev#content:5272:hdoc)
23+
* Источник: [Cognitive complexity, ver. 1.4](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) -->
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# Referencing a missing common module method (MissingCommonModuleMethod)
2+
3+
<!-- Блоки выше заполняются автоматически, не трогать -->
4+
## Description
5+
<!-- Описание диагностики заполняется вручную. Необходимо понятным языком описать смысл и схему работу -->
6+
7+
## Examples
8+
<!-- В данном разделе приводятся примеры, на которые диагностика срабатывает, а также можно привести пример, как можно исправить ситуацию -->
9+
10+
## Sources
11+
<!-- Необходимо указывать ссылки на все источники, из которых почерпнута информация для создания диагностики -->
12+
<!-- Примеры источников
13+
14+
* Источник: [Стандарт: Тексты модулей](https://its.1c.ru/db/v8std#content:456:hdoc)
15+
* Полезная информация: [Отказ от использования модальных окон](https://its.1c.ru/db/metod8dev#content:5272:hdoc)
16+
* Источник: [Cognitive complexity, ver. 1.4](https://www.sonarsource.com/docs/CognitiveComplexity.pdf) -->
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2022
5+
* Alexey Sosnoviy <[email protected]>, Nikita Fedkin <[email protected]> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.diagnostics;
23+
24+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticMetadata;
25+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticScope;
26+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticSeverity;
27+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticTag;
28+
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticType;
29+
import com.github._1c_syntax.bsl.languageserver.references.model.LocationRepository;
30+
import com.github._1c_syntax.bsl.languageserver.references.model.OccurrenceType;
31+
import com.github._1c_syntax.bsl.languageserver.references.model.SymbolOccurrence;
32+
import com.github._1c_syntax.bsl.languageserver.utils.Trees;
33+
import com.github._1c_syntax.bsl.parser.BSLParserRuleContext;
34+
import com.github._1c_syntax.bsl.types.ConfigurationSource;
35+
import com.github._1c_syntax.bsl.types.ModuleType;
36+
import lombok.AllArgsConstructor;
37+
import lombok.RequiredArgsConstructor;
38+
import lombok.Value;
39+
import org.antlr.v4.runtime.tree.ParseTree;
40+
import org.eclipse.lsp4j.Range;
41+
import org.eclipse.lsp4j.SymbolKind;
42+
43+
import java.util.Optional;
44+
45+
@DiagnosticMetadata(
46+
type = DiagnosticType.ERROR,
47+
severity = DiagnosticSeverity.BLOCKER,
48+
scope = DiagnosticScope.BSL,
49+
minutesToFix = 5,
50+
tags = {
51+
DiagnosticTag.ERROR
52+
}
53+
)
54+
55+
@RequiredArgsConstructor
56+
public class MissingCommonModuleMethodDiagnostic extends AbstractDiagnostic {
57+
public static final String PRIVATE_METHOD_MESSAGE = "privateMethod";
58+
private final LocationRepository locationRepository;
59+
60+
private static String getMethodNameByLocation(BSLParserRuleContext node, Range range) {
61+
return Trees.findTerminalNodeContainsPosition(node, range.getEnd())
62+
.map(ParseTree::getText)
63+
.orElseThrow();
64+
}
65+
66+
@Override
67+
protected void check() {
68+
if (documentContext.getServerContext().getConfiguration().getConfigurationSource() == ConfigurationSource.EMPTY){
69+
return;
70+
}
71+
locationRepository.getSymbolOccurrencesByLocationUri(documentContext.getUri())
72+
.filter(symbolOccurrence -> symbolOccurrence.getOccurrenceType() == OccurrenceType.REFERENCE)
73+
.filter(symbolOccurrence -> symbolOccurrence.getSymbol().getSymbolKind() == SymbolKind.Method)
74+
.filter(symbolOccurrence -> symbolOccurrence.getSymbol().getModuleType() == ModuleType.CommonModule)
75+
.map(this::getReferenceToMethodCall)
76+
.flatMap(Optional::stream)
77+
.forEach(this::fireIssue);
78+
}
79+
80+
private Optional<CallData> getReferenceToMethodCall(SymbolOccurrence symbolOccurrence) {
81+
final var symbol = symbolOccurrence.getSymbol();
82+
final var document = documentContext.getServerContext()
83+
.getDocument(symbol.getMdoRef(), symbol.getModuleType())
84+
.orElseThrow();
85+
final var mdObject = document.getMdObject().orElseThrow();
86+
87+
// т.к. через refIndex.getReferences нельзя получить приватные методы, приходится обходить символы модуля
88+
final var methodSymbol = document
89+
.getSymbolTree().getMethodSymbol(symbol.getSymbolName());
90+
if (methodSymbol.isEmpty()){
91+
final var location = symbolOccurrence.getLocation();
92+
// Нельзя использовать symbol.getSymbolName(), т.к. имя в нижнем регистре
93+
return Optional.of(
94+
new CallData(mdObject.getName(),
95+
getMethodNameByLocation(documentContext.getAst(), location.getRange()),
96+
location.getRange(), false, false));
97+
}
98+
// вызовы приватных методов внутри самого модуля пропускаем
99+
if (document.getUri().equals(documentContext.getUri())){
100+
return Optional.empty();
101+
}
102+
return methodSymbol
103+
.filter(methodSymbol2 -> !methodSymbol2.isExport())
104+
.map(methodSymbol1 -> new CallData(mdObject.getName(),
105+
methodSymbol1.getName(),
106+
symbolOccurrence.getLocation().getRange(), true, true));
107+
}
108+
109+
private void fireIssue(CallData callData) {
110+
final String message;
111+
if (!callData.exists){
112+
message = info.getMessage(callData.methodName, callData.moduleName);
113+
} else {
114+
message = info.getResourceString(PRIVATE_METHOD_MESSAGE, callData.methodName, callData.moduleName);
115+
}
116+
diagnosticStorage.addDiagnostic(callData.moduleMethodRange, message);
117+
}
118+
119+
@Value
120+
@AllArgsConstructor
121+
private static class CallData {
122+
String moduleName;
123+
String methodName;
124+
Range moduleMethodRange;
125+
boolean nonExport;
126+
boolean exists;
127+
}
128+
}

src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/parameters-schema.json

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1082,6 +1082,16 @@
10821082
},
10831083
"$id": "#/definitions/MissingCodeTryCatchEx"
10841084
},
1085+
"MissingCommonModuleMethod": {
1086+
"description": "Referencing a missing common module method",
1087+
"default": true,
1088+
"type": [
1089+
"boolean",
1090+
"object"
1091+
],
1092+
"title": "Referencing a missing common module method",
1093+
"$id": "#/definitions/MissingCommonModuleMethod"
1094+
},
10851095
"MissingEventSubscriptionHandler": {
10861096
"description": "Event subscription handler missing",
10871097
"default": true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
diagnosticMessage=The method %s of %s common module does not exist
2+
diagnosticName=Referencing a missing common module method
3+
4+
privateMethod=Correct the reference to the non export %s method of the common module %s
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
diagnosticMessage=Метод %s общего модуля %s не существует
2+
diagnosticName=Обращение к отсутствующему методу общего модуля
3+
4+
privateMethod=Исправьте обращение к закрытому, неэкспортному методу %s общего модуля %s
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* This file is a part of BSL Language Server.
3+
*
4+
* Copyright (c) 2018-2022
5+
* Alexey Sosnoviy <[email protected]>, Nikita Fedkin <[email protected]> and contributors
6+
*
7+
* SPDX-License-Identifier: LGPL-3.0-or-later
8+
*
9+
* BSL Language Server is free software; you can redistribute it and/or
10+
* modify it under the terms of the GNU Lesser General Public
11+
* License as published by the Free Software Foundation; either
12+
* version 3.0 of the License, or (at your option) any later version.
13+
*
14+
* BSL Language Server is distributed in the hope that it will be useful,
15+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
16+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17+
* Lesser General Public License for more details.
18+
*
19+
* You should have received a copy of the GNU Lesser General Public
20+
* License along with BSL Language Server.
21+
*/
22+
package com.github._1c_syntax.bsl.languageserver.diagnostics;
23+
24+
import com.github._1c_syntax.bsl.languageserver.util.CleanupContextBeforeClassAndAfterEachTestMethod;
25+
import com.github._1c_syntax.utils.Absolute;
26+
import org.eclipse.lsp4j.Diagnostic;
27+
import org.junit.jupiter.api.Test;
28+
29+
import java.util.List;
30+
31+
import static com.github._1c_syntax.bsl.languageserver.util.Assertions.assertThat;
32+
33+
@CleanupContextBeforeClassAndAfterEachTestMethod
34+
class MissingCommonModuleMethodDiagnosticTest extends AbstractDiagnosticTest<MissingCommonModuleMethodDiagnostic> {
35+
36+
private static final String PATH_TO_METADATA = "src/test/resources/metadata/designer";
37+
38+
MissingCommonModuleMethodDiagnosticTest() {
39+
super(MissingCommonModuleMethodDiagnostic.class);
40+
}
41+
42+
@Test
43+
void test() {
44+
initServerContext(Absolute.path(PATH_TO_METADATA));
45+
46+
List<Diagnostic> diagnostics = getDiagnostics();
47+
48+
assertThat(diagnostics, true)
49+
.hasMessageOnRange("Метод МетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 1, 22, 41)
50+
.hasMessageOnRange("Метод ДругойМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 2, 26, 51)
51+
.hasMessageOnRange("Метод ЕщеМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 3, 22, 44)
52+
.hasMessageOnRange("Метод ЕщеОдинМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 4, 22, 48)
53+
.hasMessageOnRange("Метод ЕщеДругойМетодНесуществующий общего модуля ПервыйОбщийМодуль не существует", 5, 26, 54)
54+
55+
.hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу РегистрацияИзмененийПередУдалением общего модуля ПервыйОбщийМодуль", 11, 22, 56)
56+
.hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 12, 26, 30)
57+
.hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 13, 22, 26)
58+
.hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 14, 22, 26)
59+
.hasMessageOnRange("Исправьте обращение к закрытому, неэкспортному методу Тест общего модуля ПервыйОбщийМодуль", 15, 26, 30)
60+
.hasSize(10);
61+
}
62+
63+
@Test
64+
void testWithoutMetadata() {
65+
66+
List<Diagnostic> diagnostics = getDiagnostics();
67+
68+
assertThat(diagnostics).isEmpty();
69+
}
70+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
Процедура Тест1()
2+
ПервыйОбщийМодуль.МетодНесуществующий(1, 2); // ошибка
3+
А = ПервыйОбщийМодуль.ДругойМетодНесуществующий(); // ошибка
4+
ПервыйОбщийМодуль.ЕщеМетодНесуществующий().Добавить(); // ошибка
5+
ПервыйОбщийМодуль.ЕщеОдинМетодНесуществующий().Реквизит = 10; // ошибка
6+
Б = ПервыйОбщийМодуль.ЕщеДругойМетодНесуществующий().Добавить(); // ошибка
7+
8+
НесуществующийОбщийМодульИлиПростоПеременная.МетодНесуществующий(1, 2); // не ошибка
9+
КонецПроцедуры
10+
11+
Процедура Тест2_ОбращениеКПриватномуМетоду()
12+
ПервыйОбщийМодуль.РегистрацияИзмененийПередУдалением(Источник, Отказ); // ошибка
13+
А = ПервыйОбщийМодуль.Тест(); // ошибка
14+
ПервыйОбщийМодуль.Тест().Добавить(); // ошибка
15+
ПервыйОбщийМодуль.Тест().Реквизит = 10; // ошибка
16+
Б = ПервыйОбщийМодуль.Тест().Добавить(); // ошибка
17+
КонецПроцедуры
18+
19+
Процедура Тест3()
20+
ПервыйОбщийМодуль.НеУстаревшаяПроцедура(); // не ошибка
21+
А = ПервыйОбщийМодуль.НеУстаревшаяФункция(); // не ошибка
22+
ПервыйОбщийМодуль.НеУстаревшаяФункция().Добавить(); // не ошибка
23+
ПервыйОбщийМодуль.НеУстаревшаяФункция().Реквизит = 10; // не ошибка
24+
Б = ПервыйОбщийМодуль.НеУстаревшаяФункция().Добавить(); // не ошибка
25+
КонецПроцедуры
26+
27+
Процедура Тест4_ИмяПараметр(ПервыйОбщийМодуль)
28+
ПервыйОбщийМодуль.МетодНесуществующий(1, 2); // не ошибка
29+
А = ПервыйОбщийМодуль.ДругойМетодНесуществующий(); // не ошибка
30+
ПервыйОбщийМодуль.ЕщеМетодНесуществующий().Добавить(); // не ошибка
31+
ПервыйОбщийМодуль.ЕщеОдинМетодНесуществующий().Реквизит = 10; // не ошибка
32+
Б = ПервыйОбщийМодуль.ЕщеДругойМетодНесуществующий().Добавить(); // не ошибка
33+
КонецПроцедуры
34+
35+
Процедура Тест5_МодулиМенеджеров()
36+
Справочники.Справочник1.НесуществующийМетод(); // пока не ошибка
37+
КонецПроцедуры

0 commit comments

Comments
 (0)