diff --git a/docs/diagnostics/MetadataBorders.md b/docs/diagnostics/MetadataBorders.md new file mode 100644 index 00000000000..2fb5c5da376 --- /dev/null +++ b/docs/diagnostics/MetadataBorders.md @@ -0,0 +1,75 @@ +# Границы метаданных (MetadataBorders) + +| Тип | Поддерживаются
языки | Важность | Включена
по умолчанию | Время на
исправление (мин) | Теги | +|:-------------:|:-----------------------------:|:----------------:|:------------------------------:|:-----------------------------------:|:--------:| +| `Дефект кода` | `BSL`
`OS` | `Информационный` | `Нет` | `1` | `design` | + +## Параметры + + +| Имя | Тип | Описание | Значение
по умолчанию | +|:---------------------------:|:--------:|:---------------------------------------------------------------------------------------:|:------------------------------:| +| `metadataBordersParameters` | `Строка` | `{"регулярное выражение для операторов":"регулярное выражение для имени файла модуля"}` | `` | + +## Описание диагностики +Диагностика обращения к метаданным за пределами разрешенных границ. Нужна, если в компании установлены, +например, правила обращения к каким-то метаданным только из определенного общего модуля. + +## Примеры +Получение контактной информации по внутренним правилам должно происходить только через функции +общего модуля РаботаСКонтактами. Тогда заданные настройки вида + +```json +{"Регистры?Сведений.КонтактнаяИнформация":"CommonModules/РаботаСКонтактами"} +``` + +вызовут срабатывание диагностики на запросе с текстом + +``` +"ВЫБРАТЬ + ... +ИЗ + РегистрСведений.КонтактнаяИнформация КАК КИ" +``` + +если этот запрос будет НЕ в общем модуле РаботаСКонтактами. +Так же эта диагностика с такой настройкой сработает и на оператор + +```НаборЗаписей = РегистрыСведений.КонтактнаяИнформация.СоздатьНаборЗаписей();``` + +В настройках обе части - это регулярные выражения java. +Вторая часть настройки сравнивается с путем к файлу выгрузки конфигурации. + +Можно запретить использование нескольких метаданных: + +```json +{"Регистры?Сведений.КонтактнаяИнформация|Справочники?.Контакты":"CommonModules/РаботаСКонтактами"} +``` + +Настройки задаются в виде json-строки: +```json +{ + "Регистры?Сведений.КонтактнаяИнформация":"CommonModules/РаботаСКонтактами", + "Справочники?.СтрокиСоединения":"DataProcessors/ВнешниеСоединения|CommonModules/ПодключениеК.*" +} +``` + +## Источники + +## Сниппеты + + +### Экранирование кода + +```bsl +// BSLLS:MetadataBorders-off +// BSLLS:MetadataBorders-on +``` + +### Параметр конфигурационного файла + +```json +"MetadataBorders": { + "metadataBordersParameters": "" +} +``` diff --git a/docs/diagnostics/index.md b/docs/diagnostics/index.md index 1b41768f33c..f3f21290e03 100644 --- a/docs/diagnostics/index.md +++ b/docs/diagnostics/index.md @@ -8,12 +8,12 @@ ## Список реализованных диагностик -Общее количество: **145** +Общее количество: **146** * Потенциальная уязвимость: **4** * Уязвимость: **4** * Ошибка: **44** -* Дефект кода: **93** +* Дефект кода: **94** | Ключ | Название | Включена по умолчанию | Важность | Тип | Тэги | @@ -91,6 +91,7 @@ [LogicalOrInTheWhereSectionOfQuery](LogicalOrInTheWhereSectionOfQuery.md) | Использование логического "ИЛИ" в секции "ГДЕ" запроса | Да | Важный | Дефект кода | `sql`
`performance`
`standard` [MagicDate](MagicDate.md) | Магические даты | Да | Незначительный | Дефект кода | `badpractice`
`brainoverload` [MagicNumber](MagicNumber.md) | Магические числа | Да | Незначительный | Дефект кода | `badpractice` + [MetadataBorders](MetadataBorders.md) | Границы метаданных | Да | Информационный | Дефект кода | `error` [MetadataObjectNameLength](MetadataObjectNameLength.md) | Имена объектов метаданных не должны превышать допустимой длины наименования | Да | Важный | Ошибка | `standard` [MethodSize](MethodSize.md) | Ограничение на размер метода | Да | Важный | Дефект кода | `badpractice` [MissingCodeTryCatchEx](MissingCodeTryCatchEx.md) | Конструкция "Попытка...Исключение...КонецПопытки" не содержит кода в исключении | Да | Важный | Ошибка | `standard`
`badpractice` diff --git a/docs/en/diagnostics/MetadataBorders.md b/docs/en/diagnostics/MetadataBorders.md new file mode 100644 index 00000000000..1d619845361 --- /dev/null +++ b/docs/en/diagnostics/MetadataBorders.md @@ -0,0 +1,44 @@ +# Metadata borders (MetadataBorders) + +| Type | Scope | Severity | Activated
by default | Minutes
to fix | Tags | +|:------------:|:-------------------:|:--------:|:-----------------------------:|:-----------------------:|:--------:| +| `Code smell` | `BSL`
`OS` | `Info` | `No` | `1` | `design` | + +## Parameters + + +| Name | Type | Description | Default value | +|:---------------------------:|:--------:|:---------------------------------------------------:|:-------------:| +| `metadataBordersParameters` | `String` | `{"regex for statements":"regex for module names"}` | `` | + +## Description + + +## Examples + + +## Sources + + + +## Snippets + + +### Diagnostic ignorance in code + +```bsl +// BSLLS:MetadataBorders-off +// BSLLS:MetadataBorders-on +``` + +### Parameter for config + +```json +"MetadataBorders": { + "metadataBordersParameters": "" +} +``` diff --git a/docs/en/diagnostics/index.md b/docs/en/diagnostics/index.md index 4427c56ec4f..ba21f17f3e5 100644 --- a/docs/en/diagnostics/index.md +++ b/docs/en/diagnostics/index.md @@ -8,12 +8,12 @@ To escape individual sections of code or files from triggering diagnostics, you ## Implemented diagnostics -Total: **145** +Total: **146** * Security Hotspot: **4** * Vulnerability: **4** * Error: **44** -* Code smell: **93** +* Code smell: **94** | Key | Name| Enabled by default | Severity | Type | Tags | @@ -91,6 +91,7 @@ Total: **145** [LogicalOrInTheWhereSectionOfQuery](LogicalOrInTheWhereSectionOfQuery.md) | Using a logical "OR" in the "WHERE" section of a query | Yes | Major | Code smell | `sql`
`performance`
`standard` [MagicDate](MagicDate.md) | Magic dates | Yes | Minor | Code smell | `badpractice`
`brainoverload` [MagicNumber](MagicNumber.md) | Magic numbers | Yes | Minor | Code smell | `badpractice` + [MetadataBorders](MetadataBorders.md) | Metadata borders | Yes | Info | Code smell | `error` [MetadataObjectNameLength](MetadataObjectNameLength.md) | Metadata object names must not exceed the allowed length | Yes | Major | Error | `standard` [MethodSize](MethodSize.md) | Method size | Yes | Major | Code smell | `badpractice` [MissingCodeTryCatchEx](MissingCodeTryCatchEx.md) | Missing code in Raise block in "Try ... Raise ... EndTry" | Yes | Major | Error | `standard`
`badpractice` diff --git a/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic.java b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic.java new file mode 100644 index 00000000000..39e56ed0d7a --- /dev/null +++ b/src/main/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic.java @@ -0,0 +1,109 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2021 + * Alexey Sosnoviy , Nikita Gryzlov 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.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.type.MapType; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticMetadata; +import com.github._1c_syntax.bsl.languageserver.diagnostics.metadata.DiagnosticParameter; +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.parser.BSLParser; +import com.github._1c_syntax.utils.CaseInsensitivePattern; +import org.antlr.v4.runtime.tree.ParseTree; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +@DiagnosticMetadata( + type = DiagnosticType.CODE_SMELL, + severity = DiagnosticSeverity.INFO, + minutesToFix = 1, + activatedByDefault = false, + tags = { + DiagnosticTag.DESIGN + } + +) +public class MetadataBordersDiagnostic extends AbstractVisitorDiagnostic { + + private static final String METADATA_BORDERS_DEFAULT = ""; + + @DiagnosticParameter( + type = String.class, + defaultValue = "" + ) + private Map metadataBordersParameters = mapFromJSON(METADATA_BORDERS_DEFAULT); + + private List statementPatterns = Collections.emptyList(); + + private static Map mapFromJSON(String userSettings) { + ObjectMapper mapper = new ObjectMapper(); + MapType mapType = mapper.getTypeFactory().constructMapType(HashMap.class, String.class, String.class); + try { + Map stringMap = mapper.readValue(userSettings, mapType); + return stringMap.entrySet().stream() + .filter(entry -> !entry.getKey().isBlank() && !entry.getValue().isBlank()) + .collect(Collectors.toMap( + entry -> CaseInsensitivePattern.compile(entry.getKey()), + entry -> CaseInsensitivePattern.compile(entry.getValue()))); + } catch (JsonProcessingException e) { + return Collections.emptyMap(); + } + } + + @Override + public void configure(Map configuration) { + metadataBordersParameters = mapFromJSON( + (String) configuration.getOrDefault("metadataBordersParameters", METADATA_BORDERS_DEFAULT)); + } + + @Override + public ParseTree visitFile(BSLParser.FileContext ctx) { + statementPatterns = metadataBordersParameters.entrySet().stream() + .filter(entry -> ! entry.getValue().matcher(documentContext.getUri().getPath()).find()) + .map(Map.Entry::getKey) + .collect(Collectors.toList()); + + return super.visitFile(ctx); + } + + @Override + public ParseTree visitStatement(BSLParser.StatementContext ctx){ + + statementPatterns.forEach(pattern -> { + Matcher matcher = pattern.matcher(ctx.getText()); + while (matcher.find()) { + diagnosticStorage.addDiagnostic(ctx); + } + }); + + return super.visitStatement(ctx); + } +} 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 3bc5a8c04f7..0fef9898faf 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 @@ -924,6 +924,24 @@ }, "$id": "#/definitions/MagicNumber" }, + "MetadataBorders": { + "description": "Metadata borders", + "default": false, + "type": [ + "boolean", + "object" + ], + "title": "Metadata borders", + "properties": { + "metadataBordersParameters": { + "description": "{\"regex for statements\":\"regex for module names\"}", + "default": "", + "type": "string", + "title": "{\"regex for statements\":\"regex for module names\"}" + } + }, + "$id": "#/definitions/MetadataBorders" + }, "MetadataObjectNameLength": { "description": "Metadata object names must not exceed the allowed length", "default": true, diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json index 986f3105d67..a1e6f770ee2 100644 --- a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/configuration/schema.json @@ -242,6 +242,9 @@ "MagicNumber": { "$ref": "parameters-schema.json#/definitions/MagicNumber" }, + "MetadataBorders": { + "$ref": "parameters-schema.json#/definitions/MetadataBorders" + }, "MetadataObjectNameLength": { "$ref": "parameters-schema.json#/definitions/MetadataObjectNameLength" }, diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic_en.properties b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic_en.properties new file mode 100644 index 00000000000..6ea09f1828a --- /dev/null +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic_en.properties @@ -0,0 +1,4 @@ +diagnosticMessage=Use metadata outside the borders +diagnosticName=Metadata borders +metadataBordersParameters={"regex for statements":"regex for module names"} + diff --git a/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic_ru.properties b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic_ru.properties new file mode 100644 index 00000000000..6c61ef0fa57 --- /dev/null +++ b/src/main/resources/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnostic_ru.properties @@ -0,0 +1,3 @@ +diagnosticMessage=Обращение к метаданным за пределами разрешенных границ +diagnosticName=Границы метаданных +metadataBordersParameters={"регулярное выражение для операторов":"регулярное выражение для имени файла модуля"} diff --git a/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnosticTest.java b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnosticTest.java new file mode 100644 index 00000000000..1f1f49d58c6 --- /dev/null +++ b/src/test/java/com/github/_1c_syntax/bsl/languageserver/diagnostics/MetadataBordersDiagnosticTest.java @@ -0,0 +1,86 @@ +/* + * This file is a part of BSL Language Server. + * + * Copyright (c) 2018-2021 + * Alexey Sosnoviy , Nikita Gryzlov 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 org.eclipse.lsp4j.Diagnostic; +import org.junit.jupiter.api.Test; + +import java.util.List; +import java.util.Map; + +import static com.github._1c_syntax.bsl.languageserver.util.Assertions.assertThat; + +class MetadataBordersDiagnosticTest extends AbstractDiagnosticTest { + MetadataBordersDiagnosticTest() { + super(MetadataBordersDiagnostic.class); + } + + @Test + void testWithoutSettings() { + List diagnostics = getDiagnostics(); + assertThat(diagnostics).hasSize(0); + } + + @Test + void testMatchesInProperModule() { + + Map configuration = diagnosticInstance.info.getDefaultConfiguration(); + configuration.put("metadataBordersParameters", + "{\"Регистры?Сведений.КонтактнаяИнформация|Справочники?.Контакты\":\"fake-uri\"}"); + diagnosticInstance.configure(configuration); + List diagnostics = getDiagnostics(); + + assertThat(diagnostics).hasSize(0); + } + + @Test + void testMatchesInWrongModule() { + + Map configuration = diagnosticInstance.info.getDefaultConfiguration(); + configuration.put("metadataBordersParameters", + "{\"Регистры?Сведений.КонтактнаяИнформация|Справочники?.Контакты\":\"MetadataBordersDiagnostic\"}"); + diagnosticInstance.configure(configuration); + List diagnostics = getDiagnostics(); + + assertThat(diagnostics).hasSize(3); + assertThat(diagnostics, true) + .hasRange(3, 0, 6, 49) + .hasRange(8, 0, 8, 69) + .hasRange(10, 0, 10, 103); + } + + @Test + void testSeveralSettings() { + + Map configuration = diagnosticInstance.info.getDefaultConfiguration(); + configuration.put("metadataBordersParameters", + "{\"Регистры?Сведений.КонтактнаяИнформация\":\"fake-uri\"," + + "\"Справочники?.Контакты\":\"MetadataBordersDiagnostic\"}"); + diagnosticInstance.configure(configuration); + + List diagnostics = getDiagnostics(); + + assertThat(diagnostics).hasSize(1); + assertThat(diagnostics, true) + .hasRange(10, 0, 10, 103); + } +} diff --git a/src/test/resources/diagnostics/MetadataBordersDiagnostic.bsl b/src/test/resources/diagnostics/MetadataBordersDiagnostic.bsl new file mode 100644 index 00000000000..2f437dab0a3 --- /dev/null +++ b/src/test/resources/diagnostics/MetadataBordersDiagnostic.bsl @@ -0,0 +1,11 @@ +// в комментарии срабатываний быть не должно РегистрыСведений.КонтактнаяИнформация + +Запрос = Новый Запрос; +Запрос.Текст = "ВЫБРАТЬ +| КИ.ЗначениеКонтакта КАК Адрес +|ИЗ +| РегистрСведений.КонтактнаяИнформация КАК КИ"; //срабатывание + +Адреса = РегистрыСведений.КонтактнаяИнформация.СоздатьНаборЗаписей(); //срабатывание + +АдресИзСправочника = ЗначениеРеквизитаОбъекта(Справочники.Контакты.НайтиПоКоду(1), "ЗначениеКонтакта"); // срабатывание