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), "ЗначениеКонтакта"); // срабатывание