Skip to content

Commit f17fa5d

Browse files
committed
feat(diagnostics): улучшена диагностика MagicDate и MagicNumber для обработки назначений свойств структуры.
- Добавлена ​​обработка исключений для назначения свойств структуры в диагностике MagicDate и MagicNumber. - Обновлена ​​документация с учетом новых исключений и примеров для ясности. - Улучшена логика диагностики для предотвращения ложных срабатываний при работе со свойствами структуры. - Добавлены модульные тесты для проверки корректности работы новых исключений и обеспечения сохранности существующей функциональности.
1 parent bb7196c commit f17fa5d

File tree

9 files changed

+375
-9
lines changed

9 files changed

+375
-9
lines changed

docs/diagnostics/MagicDate.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,26 @@
3535
ХоверБордБудетИзобретен = Неопределено;
3636
КонецЕсли;
3737
```
38+
39+
## Исключения
40+
41+
Диагностика не срабатывает в следующих случаях:
42+
43+
### Присваивание к свойствам структуры
44+
45+
```bsl
46+
// НЕ срабатывает - осмысленное имя свойства
47+
СтруктураДанных.ДатаНачала = '20230101';
48+
СтруктураДанных.ДатаОкончания = '20241231';
49+
50+
// НЕ срабатывает - присваивание к свойству структуры
51+
НоваяСтруктура = Новый Структура("ДатаСоздания");
52+
НоваяСтруктура.ДатаСоздания = '20240101';
53+
```
54+
55+
### Вызовы методов
56+
57+
```bsl
58+
// НЕ срабатывает - вызов метода
59+
НоваяСтруктура.Вставить("ДатаСоздания", '20240101');
60+
```

docs/diagnostics/MagicNumber.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
Возврат Длительность < 10 * 60 * 60;
1616
1717
КонецФункции
18-
```
18+
```
1919

2020
Хорошо
2121

@@ -30,3 +30,27 @@
3030
3131
КонецФункции
3232
```
33+
34+
## Исключения
35+
36+
Диагностика не срабатывает в следующих случаях:
37+
38+
### Присваивание к свойствам структуры
39+
40+
```bsl
41+
// НЕ срабатывает - осмысленное имя свойства
42+
СтруктураДанных.Код = 100;
43+
СтруктураДанных.Количество = 5;
44+
45+
// НЕ срабатывает - присваивание к свойству структуры
46+
НоваяСтруктура = Новый Структура("МояПеременная");
47+
НоваяСтруктура.МояПеременная = 20;
48+
```
49+
50+
### Вызовы методов
51+
52+
```bsl
53+
// НЕ срабатывает - вызов метода
54+
НоваяСтруктура.Вставить("МояПеременная", 20);
55+
Массив.Добавить(1);
56+
```

docs/en/diagnostics/MagicDate.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,3 +35,26 @@ If CurrentDate < DateInventionHover() Then
3535
HoverBoardWillBeInvented = Undefined;
3636
EndIf;
3737
```
38+
39+
## Exceptions
40+
41+
The diagnostic does not trigger in the following cases:
42+
43+
### Structure property assignment
44+
45+
```bsl
46+
// Does NOT trigger - meaningful property name
47+
DataStructure.StartDate = '20230101';
48+
DataStructure.EndDate = '20241231';
49+
50+
// Does NOT trigger - structure property assignment
51+
NewStructure = New Structure("CreationDate");
52+
NewStructure.CreationDate = '20240101';
53+
```
54+
55+
### Method calls
56+
57+
```bsl
58+
// Does NOT trigger - method call
59+
NewStructure.Insert("CreationDate", '20240101');
60+
```

docs/en/diagnostics/MagicNumber.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,3 +30,27 @@ Function GetsTheInterval (Duration in Seconds)
3030
3131
End Function
3232
```
33+
34+
## Exceptions
35+
36+
The diagnostic does not trigger in the following cases:
37+
38+
### Structure property assignment
39+
40+
```bsl
41+
// Does NOT trigger - meaningful property name
42+
DataStructure.Code = 100;
43+
DataStructure.Quantity = 5;
44+
45+
// Does NOT trigger - structure property assignment
46+
NewStructure = New Structure("MyVariable");
47+
NewStructure.MyVariable = 20;
48+
```
49+
50+
### Method calls
51+
52+
```bsl
53+
// Does NOT trigger - method call
54+
NewStructure.Insert("MyVariable", 20);
55+
Array.Add(1);
56+
```

src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/AbstractVisitorDiagnostic.java

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323

2424
import com.github._1c_syntax.bsl.languageserver.context.DocumentContext;
2525
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticInfo;
26+
import com.github._1c_syntax.bsl.parser.BSLParser;
2627
import com.github._1c_syntax.bsl.parser.BSLParserBaseVisitor;
2728
import lombok.Getter;
2829
import lombok.Setter;
30+
import org.antlr.v4.runtime.ParserRuleContext;
2931
import org.antlr.v4.runtime.tree.ParseTree;
3032
import org.eclipse.lsp4j.Diagnostic;
3133

@@ -47,4 +49,21 @@ public List<Diagnostic> getDiagnostics(DocumentContext documentContext) {
4749
return diagnosticStorage.getDiagnostics();
4850
}
4951

52+
/**
53+
* Находит контекст присваивания для заданного узла AST.
54+
*
55+
* @param ctx контекст узла для поиска
56+
* @return контекст присваивания или null, если не найден
57+
*/
58+
protected static BSLParser.AssignmentContext findAssignmentContext(ParserRuleContext ctx) {
59+
var current = ctx.getParent();
60+
while (current != null) {
61+
if (current instanceof BSLParser.AssignmentContext assignmentContext) {
62+
return assignmentContext;
63+
}
64+
current = current.getParent();
65+
}
66+
return null;
67+
}
68+
5069
}

src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MagicDateDiagnostic.java

Lines changed: 113 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
*/
2222
package com.github._1c_syntax.bsl.languageserver.diagnostics;
2323

24+
import com.github._1c_syntax.bsl.languageserver.context.symbol.VariableSymbol;
2425
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticMetadata;
2526
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticParameter;
2627
import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticSeverity;
@@ -73,6 +74,11 @@ public class MagicDateDiagnostic extends AbstractVisitorDiagnostic {
7374
)
7475
private final Set<String> authorizedDates = new HashSet<>(Arrays.asList(DEFAULT_AUTHORIZED_DATES.split(",")));
7576

77+
/**
78+
* Настраивает диагностику с указанными параметрами.
79+
*
80+
* @param configuration карта параметров конфигурации
81+
*/
7682
@Override
7783
public void configure(Map<String, Object> configuration) {
7884
var authorizedDatesString = (String) configuration.getOrDefault("authorizedDates", DEFAULT_AUTHORIZED_DATES);
@@ -83,6 +89,12 @@ public void configure(Map<String, Object> configuration) {
8389
authorizedDates.addAll(authD);
8490
}
8591

92+
/**
93+
* Обрабатывает константные значения и проверяет их на наличие магических дат.
94+
*
95+
* @param ctx контекст константного значения
96+
* @return результат обработки
97+
*/
8698
@Override
8799
public ParseTree visitConstValue(BSLParser.ConstValueContext ctx) {
88100
var tNode = ctx.DATETIME();
@@ -93,15 +105,23 @@ public ParseTree visitConstValue(BSLParser.ConstValueContext ctx) {
93105
}
94106

95107
final var expressionContext = getExpression(Optional.of(ctx));
96-
if (!insideSimpleDateAssignment(expressionContext) && !insideReturnSimpleDate(expressionContext)
97-
&& !insideAssignmentWithDateMethodForSimpleDate(expressionContext)) {
108+
if (!insideSimpleDateAssignment(expressionContext)
109+
&& !insideReturnSimpleDate(expressionContext)
110+
&& !insideAssignmentWithDateMethodForSimpleDate(expressionContext)
111+
&& !expressionContext.map(MagicDateDiagnostic::insideStructurePropertyAssignment).orElse(false)) {
98112
diagnosticStorage.addDiagnostic(ctx, info.getMessage(ctx.getText()));
99113
}
100114
}
101115

102116
return defaultResult();
103117
}
104118

119+
/**
120+
* Проверяет, является ли строка валидной датой.
121+
*
122+
* @param ctx контекст строки для проверки
123+
* @return true, если строка является валидной датой, иначе false
124+
*/
105125
private static boolean isValidDate(BSLParser.StringContext ctx) {
106126
final var text = ctx.getText();
107127
if (!paramPattern.matcher(text).matches()) {
@@ -111,6 +131,12 @@ private static boolean isValidDate(BSLParser.StringContext ctx) {
111131
return isValidDate(strDate);
112132
}
113133

134+
/**
135+
* Проверяет, является ли строка валидной датой в формате YYYYMMDD или YYYYMMDDHHMMSS.
136+
*
137+
* @param strDate строка даты для проверки
138+
* @return true, если строка является валидной датой, иначе false
139+
*/
114140
private static boolean isValidDate(String strDate) {
115141
var year = parseInt(strDate.substring(0, 4));
116142
if (year < 1 || year > MAX_YEAR_BY_1C) {
@@ -130,6 +156,12 @@ private static boolean isValidDate(String strDate) {
130156
return hh <= 24 && mm <= 60 && ss <= 60;
131157
}
132158

159+
/**
160+
* Парсит строку в целое число, удаляя ведущие нули.
161+
*
162+
* @param text строка для парсинга
163+
* @return целое число или 0, если парсинг не удался
164+
*/
133165
private static int parseInt(String text) {
134166
String s = zeroPattern.matcher(text).replaceAll("");
135167
try {
@@ -139,16 +171,34 @@ private static int parseInt(String text) {
139171
}
140172
}
141173

174+
/**
175+
* Проверяет, должно ли константное значение быть обработано диагностикой.
176+
*
177+
* @param ctx контекст константного значения
178+
* @return true, если значение должно быть обработано, иначе false
179+
*/
142180
private boolean isAccepted(BSLParser.ConstValueContext ctx) {
143181
String text = ctx.getText();
144182
return text != null && !text.isEmpty() && !isExcluded(text);
145183
}
146184

185+
/**
186+
* Проверяет, исключено ли значение из проверки (находится в списке авторизованных дат).
187+
*
188+
* @param text текст для проверки
189+
* @return true, если значение исключено, иначе false
190+
*/
147191
private boolean isExcluded(String text) {
148192
String s = nonNumberPattern.matcher(text).replaceAll("");
149193
return authorizedDates.contains(s);
150194
}
151195

196+
/**
197+
* Получает контекст выражения для заданного константного значения.
198+
*
199+
* @param constValue контекст константного значения
200+
* @return контекст выражения или пустой Optional, если не найден
201+
*/
152202
private static Optional<BSLParser.ExpressionContext> getExpression(Optional<BSLParser.ConstValueContext> constValue) {
153203
return constValue
154204
.map(BSLParserRuleContext::getParent)
@@ -159,10 +209,23 @@ private static Optional<BSLParser.ExpressionContext> getExpression(Optional<BSLP
159209
.map(BSLParser.ExpressionContext.class::cast);
160210
}
161211

212+
/**
213+
* Проверяет, находится ли выражение внутри простого присваивания даты.
214+
*
215+
* @param expression контекст выражения для проверки
216+
* @return true, если выражение находится внутри присваивания, иначе false
217+
*/
162218
private static boolean insideSimpleDateAssignment(Optional<BSLParser.ExpressionContext> expression) {
163219
return insideContext(expression, BSLParser.AssignmentContext.class);
164220
}
165221

222+
/**
223+
* Проверяет, находится ли выражение внутри контекста заданного типа.
224+
*
225+
* @param expression контекст выражения для проверки
226+
* @param assignmentContextClass класс контекста для проверки
227+
* @return true, если выражение находится внутри контекста заданного типа, иначе false
228+
*/
166229
private static boolean insideContext(Optional<BSLParser.ExpressionContext> expression,
167230
Class<? extends BSLParserRuleContext> assignmentContextClass) {
168231
return expression
@@ -171,10 +234,22 @@ private static boolean insideContext(Optional<BSLParser.ExpressionContext> expre
171234
.isPresent();
172235
}
173236

237+
/**
238+
* Проверяет, находится ли выражение внутри оператора возврата.
239+
*
240+
* @param expression контекст выражения для проверки
241+
* @return true, если выражение находится внутри оператора возврата, иначе false
242+
*/
174243
private static boolean insideReturnSimpleDate(Optional<BSLParser.ExpressionContext> expression) {
175244
return insideContext(expression, BSLParser.ReturnStatementContext.class);
176245
}
177246

247+
/**
248+
* Проверяет, находится ли выражение внутри присваивания с методом Дата().
249+
*
250+
* @param expression контекст выражения для проверки
251+
* @return true, если выражение находится внутри присваивания с методом Дата(), иначе false
252+
*/
178253
private static boolean insideAssignmentWithDateMethodForSimpleDate(Optional<BSLParser.ExpressionContext> expression) {
179254
return expression
180255
.map(BSLParserRuleContext::getParent) // callParam
@@ -196,4 +271,40 @@ private static boolean insideAssignmentWithDateMethodForSimpleDate(Optional<BSLP
196271
.filter(BSLParser.AssignmentContext.class::isInstance)
197272
.isPresent();
198273
}
274+
275+
/**
276+
* Проверяет, находится ли выражение внутри присваивания свойства структуры.
277+
*
278+
* @param expression контекст выражения для проверки
279+
* @return true, если выражение находится внутри присваивания свойства структуры, иначе false
280+
*/
281+
private static boolean insideStructurePropertyAssignment(BSLParser.ExpressionContext expression) {
282+
var assignment = findAssignmentContext(expression);
283+
if (assignment == null) {
284+
return false;
285+
}
286+
287+
var lValue = assignment.lValue();
288+
var acceptor = lValue != null && !lValue.isEmpty() ? lValue.acceptor() : null;
289+
290+
return acceptor != null && acceptor.accessProperty() != null;
291+
}
292+
293+
/**
294+
* Находит контекст присваивания для заданного узла AST.
295+
*
296+
* @param ctx контекст узла для поиска
297+
* @return контекст присваивания или null, если не найден
298+
*/
299+
private static BSLParser.AssignmentContext findAssignmentContext(BSLParserRuleContext ctx) {
300+
var current = ctx.getParent();
301+
while (current != null) {
302+
if (current instanceof BSLParser.AssignmentContext assignmentContext) {
303+
return assignmentContext;
304+
}
305+
current = current.getParent();
306+
}
307+
return null;
308+
}
309+
199310
}

0 commit comments

Comments
 (0)