Skip to content

Commit

Permalink
Merge pull request #3207 from artbear/magicDate
Browse files Browse the repository at this point in the history
[MOD] MagicDate - Магические даты находятся в параметрах методов, в т.ч. и платформенном методе Выполнить - ГОТОВО
  • Loading branch information
theshadowco committed Jun 4, 2024
2 parents b39efaf + 247cd8c commit 945317c
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 49 deletions.
13 changes: 13 additions & 0 deletions docs/diagnostics/MagicDate.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,16 @@
ХоверБордБудетИзобретен = Неопределено;
КонецЕсли;
```

Также хорошим решением является использование специального метода с говорящим названием, который возвращает
дату-константу

```bsl
Функция ДатаИзобретенияХовера()
Возврат '20151021';
КонецФункции
Если текДата < ДатаИзобретенияХовера() Тогда
ХоверБордБудетИзобретен = Неопределено;
КонецЕсли;
```
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,7 @@
import com.github._1c_syntax.bsl.parser.BSLParser;
import com.github._1c_syntax.bsl.parser.BSLParserRuleContext;
import com.github._1c_syntax.utils.CaseInsensitivePattern;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.tree.ParseTree;
import org.antlr.v4.runtime.tree.TerminalNode;

import java.util.Arrays;
import java.util.HashSet;
Expand Down Expand Up @@ -60,12 +58,14 @@ public class MagicDateDiagnostic extends AbstractVisitorDiagnostic {
);

private static final Pattern paramPattern = CaseInsensitivePattern.compile(
"\"\\d{8}.*"
"\"[0123]{1}\\d{7}\"|\"[0123]{1}\\d{13}\""
);
private static final Pattern zeroPattern = Pattern.compile("^0+");

private static final Pattern nonNumberPattern = CaseInsensitivePattern.compile(
"\\D"
);
public static final int MAX_YEAR_BY_1C = 9999;

@DiagnosticParameter(
type = String.class,
Expand All @@ -84,49 +84,116 @@ public void configure(Map<String, Object> configuration) {
}

@Override
public ParseTree visitGlobalMethodCall(BSLParser.GlobalMethodCallContext ctx) {
Optional.of(ctx)
.filter(it -> methodPattern.matcher(it.methodName().getText()).matches())
.map(BSLParser.GlobalMethodCallContext::doCall)
.map(BSLParser.DoCallContext::callParamList)
.filter(callParamList -> paramPattern.matcher(callParamList.getText()).matches())
.ifPresent(this::checkExclAddDiagnostic);

return super.visitGlobalMethodCall(ctx);
public ParseTree visitConstValue(BSLParser.ConstValueContext ctx) {
var tNode = ctx.DATETIME();
var sNode = ctx.string();
if ((tNode != null || sNode != null) && isAccepted(ctx)) {
if (sNode != null && !isValidDate(sNode)) {
return defaultResult();
}

final var expressionContext = getExpression(Optional.of(ctx));
if (!insideSimpleDateAssignment(expressionContext) && !insideReturnSimpleDate(expressionContext)
&& !insideAssignmentWithDateMethodForSimpleDate(expressionContext)) {
diagnosticStorage.addDiagnostic(ctx, info.getMessage(ctx.getText()));
}
}

return defaultResult();
}

@Override
public ParseTree visitConstValue(BSLParser.ConstValueContext ctx) {
TerminalNode tNode = ctx.DATETIME();
if (tNode != null) {
checkExclAddDiagnostic(ctx);
private static boolean isValidDate(BSLParser.StringContext ctx) {
final var text = ctx.getText();
if (!paramPattern.matcher(text).matches()) {
return false;
}
var strDate = text.substring(1, text.length() - 1); // убрать кавычки
return isValidDate(strDate);
}

return ctx;
private static boolean isValidDate(String strDate) {
var year = parseInt(strDate.substring(0, 4));
if (year < 1 || year > MAX_YEAR_BY_1C) {
return false;
}
var month = parseInt(strDate.substring(4, 6));
var day = parseInt(strDate.substring(6, 8));
if (month < 1 || month > 12 || day < 1 || day > 31) {
return false;
}
if (strDate.length() == 8) {
return true;
}
var hh = parseInt(strDate.substring(8, 10));
var mm = parseInt(strDate.substring(10, 12));
var ss = parseInt(strDate.substring(12, 14));
return hh <= 24 && mm <= 60 && ss <= 60;
}

private void checkExclAddDiagnostic(BSLParserRuleContext ctx) {
String checked = ctx.getText();
if (checked != null && !isExcluded(checked)) {
ParserRuleContext expression;
if (ctx instanceof BSLParser.CallParamListContext) {
expression = ctx.getParent().getParent().getParent().getParent().getParent();
} else {
expression = ctx.getParent().getParent();
}
if (expression instanceof BSLParser.ExpressionContext expressionContext
&& (!isAssignExpression(expressionContext))) {
diagnosticStorage.addDiagnostic(ctx.stop, info.getMessage(checked));
}
private static int parseInt(String text) {
String s = zeroPattern.matcher(text).replaceAll("");
try {
return Integer.parseInt(s);
} catch (NumberFormatException e) {
return 0;
}
}

private boolean isExcluded(String sIn) {
String s = nonNumberPattern.matcher(sIn).replaceAll("");
private boolean isAccepted(BSLParser.ConstValueContext ctx) {
String text = ctx.getText();
return text != null && !text.isEmpty() && !isExcluded(text);
}

private boolean isExcluded(String text) {
String s = nonNumberPattern.matcher(text).replaceAll("");
return authorizedDates.contains(s);
}

private static boolean isAssignExpression(BSLParser.ExpressionContext expression) {
return (expression.getChildCount() <= 1);
private static Optional<BSLParser.ExpressionContext> getExpression(Optional<BSLParser.ConstValueContext> constValue) {
return constValue
.map(BSLParserRuleContext::getParent)
.filter(context -> context.getChildCount() == 1)
.map(BSLParserRuleContext::getParent)
.filter(context -> context.getChildCount() == 1)
.filter(BSLParser.ExpressionContext.class::isInstance)
.map(BSLParser.ExpressionContext.class::cast);
}

private static boolean insideSimpleDateAssignment(Optional<BSLParser.ExpressionContext> expression) {
return insideContext(expression, BSLParser.AssignmentContext.class);
}

private static boolean insideContext(Optional<BSLParser.ExpressionContext> expression,
Class<? extends BSLParserRuleContext> assignmentContextClass) {
return expression
.map(BSLParserRuleContext::getParent)
.filter(assignmentContextClass::isInstance)
.isPresent();
}

private static boolean insideReturnSimpleDate(Optional<BSLParser.ExpressionContext> expression) {
return insideContext(expression, BSLParser.ReturnStatementContext.class);
}

private static boolean insideAssignmentWithDateMethodForSimpleDate(Optional<BSLParser.ExpressionContext> expression) {
return expression
.map(BSLParserRuleContext::getParent) // callParam
.filter(context -> context.getChildCount() == 1)
.map(BSLParserRuleContext::getParent) // callParamList
.filter(context -> context.getChildCount() == 1)
.map(BSLParserRuleContext::getParent) // doCall
.map(BSLParserRuleContext::getParent) // globalCall - метод Дата(ХХХ)
.filter(BSLParser.GlobalMethodCallContext.class::isInstance)
.map(BSLParser.GlobalMethodCallContext.class::cast)
.filter(context -> methodPattern.matcher(context.methodName().getText()).matches())
.map(BSLParserRuleContext::getParent) // complexId
.filter(context -> context.getChildCount() == 1)
.map(BSLParserRuleContext::getParent) // member
.filter(context -> context.getChildCount() == 1)
.map(BSLParserRuleContext::getParent) // expression
.filter(context -> context.getChildCount() == 1)
.map(BSLParserRuleContext::getParent)
.filter(BSLParser.AssignmentContext.class::isInstance)
.isPresent();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,30 +39,48 @@ void test() {

List<Diagnostic> diagnostics = getDiagnostics();

assertThat(diagnostics).hasSize(5);
assertThat(diagnostics).hasSize(18);
assertThat(diagnostics, true)
.hasRange(1, 12, 1, 22)
.hasRange(2, 12, 2, 28)
.hasRange(3, 7, 3, 17)
.hasRange(4, 14, 4, 24)
.hasRange(13, 7, 13, 26);

.hasRange(11, 12, 22)
.hasRange(12, 12, 28)
.hasRange(13, 7, 17)
.hasRange(14, 14, 24)
.hasRange(23, 7, 26)
.hasRange(25, 87, 97)
.hasRange(26, 80, 90)
.hasRange(26, 92, 102)
.hasRange(27, 22, 32)
.hasRange(28, 19, 35)
.hasRange(29, 10, 26)
.hasRange(29, 29, 39)
.hasRange(31, 64, 80)
.hasRange(58, 17, 27)
.hasRange(58, 29, 45)
.hasRange(58, 47, 63)
.hasRange(60, 19, 29)
.hasRange(65, 22, 32);
}

@Test
void testConfigure() {

Map<String, Object> configuration = diagnosticInstance.getInfo().getDefaultConfiguration();
configuration.put("authorizedDates", "00010101,00010101000000,000101010000,12340101, 00020101 ,,");
configuration.put("authorizedDates", "00010101,00010101000000,000101010000,00050101,00020501121314,12340101, 00020101 ,,");
diagnosticInstance.configure(configuration);

List<Diagnostic> diagnostics = getDiagnostics();

assertThat(diagnostics).hasSize(3);
assertThat(diagnostics).hasSize(10);
assertThat(diagnostics, true)
.hasRange(2, 12, 2, 28)
.hasRange(3, 7, 3, 17)
.hasRange(13, 7, 13, 26);

.hasRange(12, 12, 28)
.hasRange(13, 7, 17)
.hasRange(23, 7, 26)
.hasRange(27, 22, 32)
.hasRange(29, 29, 39)
.hasRange(31, 64, 80)
.hasRange(58, 17, 27)
.hasRange(58, 29, 45)
.hasRange(58, 47, 63)
.hasRange(65, 22, 32);
}
}
55 changes: 54 additions & 1 deletion src/test/resources/diagnostics/MagicDateDiagnostic.bsl
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
Функция МаксимальнаяДата() Экспорт
Возврат '39991231235959';
КонецФункции

Функция МаксимальнаяДатаПриПродолжении()
Возврат '99990101000000';
КонецФункции

Процедура Тест3()

День = Дата("00020101");
День = Дата("00020101") + Шаг; // ошибка
День = Дата("00020101121314") + Шаг; // ошибка
Expand All @@ -11,4 +21,47 @@
КонецЦикла;
День = Дата("00010101") + Шаг; // исключение
День = '0001-01why not?01' + Шаг; // исключение
День = '0001-01why not?02' + Шаг; // ошибка
День = '0001-01why not?02' + Шаг; // ошибка

ИменаПараметров = СтроковыеФункции.РазложитьСтрокуВМассивПодстрок(ИмяПараметра, , Дата("00050101")); // замечание
ИменаПараметров = СтроковыеФункции.РазложитьСтрокуВМассивПодстрок(ИмяПараметра, "00050101", "00050101"); // замечание
Настройки = Настройки('12350101'); // замечание
Настройки.Свойство("00020501121314", ЗначениеЕдиничногоПараметра); // замечание
Выполнить("00020501121314" + '12350101'); // замечание

ОтборЭлемента.ПравоеЗначение = Новый СтандартнаяДатаНачала(Дата('19800101000000'));

Значение = Метод("%1/%2");
Если Условие Тогда
ВызватьИсключение "Не указано значение константы ХХХ";
КонецЕсли;

УИДЗамера = Новый УникальныйИдентификатор("00000000-0000-0000-0000-000000000000");
ПосторонниеСимволы = СтрСоединить(СтрРазделить(СтрокаСЧислом, "0123456789", Ложь), "");
Объект.GetStringValue("2147483649","Software\Buhphone","ProgramPath", Значение);
НедопустимыеСимволы = НедопустимыеСимволыВСтроке(СтрАдрес.АдресСервера,
"0123456789aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ_-.:*?");
НедопустимыеСимволы = НедопустимыеСимволы + "1234567890 ";

Дата = Метод("04937803-5dba-11df-a1d4-005056c00008");
Набор.Идентификатор = Новый УникальныйИдентификатор("70425541-23e3-4e5a-8bd3-9587cc949dfa");

НецифровыеСимволы = СтрСоединить(СтрРазделить(Значение, "1234567890,."));
Результат = Сред("0123456789abcdef", ОстатокОтДеления + 1, 1) + Результат;

Результат.Добавить(СтрокаОбъектаАдресации("10100000", НСтр("ru = 'Почтовый индекс'")));
КодСтроки = КодРодителя + Прав("00000000" + Формат(Счетчик, "ЧГ="), ЧислоЦифр);
КлючНаборовСвойств = НачалоКлюча + Лев(КлючНазначения + "00000000000000000000000000000000", 32);

КодыКЛАДР.Регион = Лев(КодКЛАДР, 2) + "00000000000";
КодыКЛАДР.Район = Лев(КодКЛАДР, 5) + "00000000";

Значение = ?= '39990202', '39991231235959', '39990101000000');

Если Сейчас < Дата("12340101") Тогда
КонецЕсли;

КонецПроцедуры

Функция Метод(Дата1 = '39990202')
КонецФункции

0 comments on commit 945317c

Please sign in to comment.