2121 */
2222package com .github ._1c_syntax .bsl .languageserver .diagnostics ;
2323
24+ import com .github ._1c_syntax .bsl .languageserver .context .symbol .VariableSymbol ;
2425import com .github ._1c_syntax .bsl .languageserver .diagnostics .metadata .DiagnosticMetadata ;
2526import com .github ._1c_syntax .bsl .languageserver .diagnostics .metadata .DiagnosticParameter ;
2627import 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