From 283804d7aacc62be51e93dffe2f40cb67edd8e75 Mon Sep 17 00:00:00 2001 From: Artem Iliukhin <20300020+iArtemv@users.noreply.github.com> Date: Sat, 15 Apr 2023 18:47:44 -0700 Subject: [PATCH] =?UTF-8?q?#1224=20=D0=9F=D1=80=D0=BE=D0=B2=D0=B5=D1=80?= =?UTF-8?q?=D1=8F=D0=B5=D1=82=20=D0=BE=D1=82=D1=81=D1=83=D1=82=D1=81=D1=82?= =?UTF-8?q?=D0=B2=D0=B8=D0=B5=20=D0=BA=D0=BE=D0=B4=D0=B0=20=D0=BF=D0=BE?= =?UTF-8?q?=D1=81=D0=BB=D0=B5=20=D0=B0=D1=81=D0=B8=D0=BD=D1=85=D1=80=D0=BE?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=B3=D0=BE=20=D0=B2=D1=8B=D0=B7=D0=BE=D0=B2?= =?UTF-8?q?=D0=B0=20(#1234)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 +- .../META-INF/MANIFEST.MF | 1 + .../markdown/code-after-async-call.md | 24 ++ .../markdown/ru/code-after-async-call.md | 54 ++++ bundles/com.e1c.v8codestyle.bsl/plugin.xml | 4 + .../bsl/IAsyncInvocationProvider.java | 44 +++ .../bsl/check/CodeAfterAsyncCallCheck.java | 261 ++++++++++++++++++ .../e1c/v8codestyle/bsl/check/Messages.java | 8 + .../v8codestyle/bsl/check/messages.properties | 34 ++- .../bsl/check/messages_ru.properties | 22 +- .../internal/bsl/AsyncInvocationProvider.java | 242 ++++++++++++++++ .../v8codestyle/internal/bsl/BslPlugin.java | 2 + .../bsl/ExternalDependenciesModule.java | 2 + .../internal/bsl/ServiceModule.java | 2 + .../resources/async-call-back-descr.bsl | 16 ++ .../resources/async-call-promise.bsl | 7 + .../resources/async-call-promise2.bsl | 8 + .../resources/async-call-promise3.bsl | 6 + .../resources/async-call-promise4.bsl | 7 + .../resources/async-call-spread-sh-doc.bsl | 10 + .../code-after-async-call-existence.bsl | 9 + .../itests/CodeAfterAsyncCallCheckTest.java | 135 +++++++++ 22 files changed, 879 insertions(+), 21 deletions(-) create mode 100644 bundles/com.e1c.v8codestyle.bsl/markdown/code-after-async-call.md create mode 100644 bundles/com.e1c.v8codestyle.bsl/markdown/ru/code-after-async-call.md create mode 100644 bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/IAsyncInvocationProvider.java create mode 100644 bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/CodeAfterAsyncCallCheck.java create mode 100644 bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/AsyncInvocationProvider.java create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-back-descr.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise2.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise3.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise4.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-spread-sh-doc.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/resources/code-after-async-call-existence.bsl create mode 100644 tests/com.e1c.v8codestyle.bsl.itests/src/com/e1c/v8codestyle/bsl/check/itests/CodeAfterAsyncCallCheckTest.java diff --git a/CHANGELOG.md b/CHANGELOG.md index 8df072d39..9f21c1fc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ #### Код модулей +- Проверка отсутствия кода после асинхронного вызова - Проверка использования метода ДанныеФормыВЗначение вместо РеквизитФормыВЗначение - В проверку использования нерекомендуемых методов (use-non-recommended-method) добавлен метод ПолучитьФорму(GetForm) - Использование устаревшего метода Найти @@ -74,7 +75,6 @@ - Устаревшая процедура (функция) расположена вне области "УстаревшиеПроцедурыИФункции" - Использован обработчик событий, подключаемый из кода и не содержащий префикса "Подключаемый_" - ### Новые быстрые исправления (Quick-fix) - Исправление превышения максимального количества допустимых пустых строк diff --git a/bundles/com.e1c.v8codestyle.bsl/META-INF/MANIFEST.MF b/bundles/com.e1c.v8codestyle.bsl/META-INF/MANIFEST.MF index c673047c7..6ad695c18 100644 --- a/bundles/com.e1c.v8codestyle.bsl/META-INF/MANIFEST.MF +++ b/bundles/com.e1c.v8codestyle.bsl/META-INF/MANIFEST.MF @@ -20,6 +20,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-11 Automatic-Module-Name: com.e1c.v8codestyle.bsl Bundle-ActivationPolicy: lazy Import-Package: com._1c.g5.v8.bm.core;version="[7.0.0,8.0.0)", + com._1c.g5.v8.dt.bm.xtext;version="[14.1.100,15.0.0)", com._1c.g5.v8.dt.bsl;version="[5.0.0,6.0.0)", com._1c.g5.v8.dt.bsl.comment;version="[3.0.0,4.0.0)", com._1c.g5.v8.dt.bsl.common;version="[6.0.0,7.0.0)", diff --git a/bundles/com.e1c.v8codestyle.bsl/markdown/code-after-async-call.md b/bundles/com.e1c.v8codestyle.bsl/markdown/code-after-async-call.md new file mode 100644 index 000000000..755bba6b8 --- /dev/null +++ b/bundles/com.e1c.v8codestyle.bsl/markdown/code-after-async-call.md @@ -0,0 +1,24 @@ +# The asynchronous method is not followed by lines of code + +In the asynchronous approach the method is called as usual, but control returns to the caller before the asynchronous +method is completed. After that, execution of the caller continues. + +## Noncompliant Code Example + +```bsl +Text = "Warning text"; +ShowMessageBox( , Text); +Message("Warning is closed"); +``` + +## Compliant Solution + +```bsl +Text = "Warning text"; +Await DoMessageBoxAsync(Text); +Message("Warning is closed"); +``` + +## See + +- [Synchronous and asynchronous operations](https://kb.1ci.com/1C_Enterprise_Platform/Guides/Developer_Guides/1C_Enterprise_8.3.19_Developer_Guide/Chapter_4._1C_Enterprise_language/4.7._Queries/4.7.9._Synchronous_and_asynchronous_operations/) \ No newline at end of file diff --git a/bundles/com.e1c.v8codestyle.bsl/markdown/ru/code-after-async-call.md b/bundles/com.e1c.v8codestyle.bsl/markdown/ru/code-after-async-call.md new file mode 100644 index 000000000..de490bf20 --- /dev/null +++ b/bundles/com.e1c.v8codestyle.bsl/markdown/ru/code-after-async-call.md @@ -0,0 +1,54 @@ +# Код расположен после асинхронного вызова + +При асинхронном подходе вызов метода выполняется как обычно, но управление возвращается вызывающему коду до того, +как асинхронный метод завершит свою работу. После этого вызывающий код продолжает свое выполнение. +Особенность асинхронного выполнения: исполнение на стороне вызывающего кода продолжится до того, +как полностью закончилось исполнение вызванного метода. + +Для правильного решения нужно вынести весь код, который должен быть выполнен после выполнения асинхронного действия, +в экспортный метод и указать его имя в обработке оповещения, которая будет вызвана после завершения асинхронного действия. +Или использовать асинхронность через обещания, например, Ждать ПредупреждениеАсинх(Текст). + +## Неправильно + +```bsl +Текст = "Текст предупреждения"; +ПоказатьПредупреждение( , Текст); +Сообщить("Закрыли предупреждение"); +``` + +```bsl +ПоказатьПредупреждение(,ТекстПредупреждения); +Отказ = Истина; +``` + +## Правильно + +```bsl +Текст = "Текст предупреждения"; +Ждать ПредупреждениеАсинх(Текст); +Сообщить("Закрыли предупреждение"); +``` + +```bsl +&НаКлиенте +Процедура Команда1(Команда) + Оповещение = Новый ОписаниеОповещения("ПредупреждениеЗавершение", ЭтотОбъект); + Текст = "Текст предупреждения"; + ПоказатьПредупреждение(Оповещение, Текст); +КонецПроцедуры + +&НаКлиенте +Процедура ПредупреждениеЗавершение(ДополнительныеПараметры) Экспорт + Сообщить("Закрыли предупреждение"); +КонецПроцедуры; +``` + +```bsl +Отказ = Истина; +ПоказатьПредупреждение(,ТекстПредупреждения); +``` + +## См. + +- [Синхронные и асинхронные методы работы](https://its.1c.ru/db/v8319doc#bookmark:dev:TI000001505) diff --git a/bundles/com.e1c.v8codestyle.bsl/plugin.xml b/bundles/com.e1c.v8codestyle.bsl/plugin.xml index d723c581e..d4f108ca7 100644 --- a/bundles/com.e1c.v8codestyle.bsl/plugin.xml +++ b/bundles/com.e1c.v8codestyle.bsl/plugin.xml @@ -347,6 +347,10 @@ category="com.e1c.v8codestyle.bsl" class="com.e1c.v8codestyle.internal.bsl.ExecutableExtensionFactory:com.e1c.v8codestyle.bsl.check.DeprecatedProcedureOutsideDeprecatedRegionCheck"> + + diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/IAsyncInvocationProvider.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/IAsyncInvocationProvider.java new file mode 100644 index 000000000..113e1fbfe --- /dev/null +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/IAsyncInvocationProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (C) 2023, 1C-Soft LLC and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * 1C-Soft LLC - initial API and implementation + *******************************************************************************/ +package com.e1c.v8codestyle.bsl; + +import java.util.Collection; +import java.util.Map; + +import com._1c.g5.v8.dt.platform.version.Version; + +/** + * Platform context asynchronous methods provider + * + * @author Artem Iliukhin + */ +public interface IAsyncInvocationProvider +{ + + /** + * Global context methods. + * + * @param version the version of platform, cannot be {@code null} + * @return the asynchronous invocation names + */ + Collection getAsyncInvocationNames(Version version); + + /** + * Methods with a list of types in which they are used. + * + * @param version the version of platform, cannot be {@code null} + * @return the asynchronous type method names + */ + Map> getAsyncTypeMethodNames(Version version); + +} diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/CodeAfterAsyncCallCheck.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/CodeAfterAsyncCallCheck.java new file mode 100644 index 000000000..7274b0122 --- /dev/null +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/CodeAfterAsyncCallCheck.java @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright (C) 2023, 1C-Soft LLC and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * 1C-Soft LLC - initial API and implementation + *******************************************************************************/ +package com.e1c.v8codestyle.bsl.check; + +import static com._1c.g5.v8.dt.bsl.model.BslPackage.Literals.INVOCATION; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.emf.ecore.EObject; +import org.eclipse.xtext.EcoreUtil2; + +import com._1c.g5.v8.dt.bsl.model.AwaitStatement; +import com._1c.g5.v8.dt.bsl.model.BslPackage; +import com._1c.g5.v8.dt.bsl.model.Conditional; +import com._1c.g5.v8.dt.bsl.model.DynamicFeatureAccess; +import com._1c.g5.v8.dt.bsl.model.EmptyStatement; +import com._1c.g5.v8.dt.bsl.model.Expression; +import com._1c.g5.v8.dt.bsl.model.FeatureAccess; +import com._1c.g5.v8.dt.bsl.model.IfStatement; +import com._1c.g5.v8.dt.bsl.model.Invocation; +import com._1c.g5.v8.dt.bsl.model.LoopStatement; +import com._1c.g5.v8.dt.bsl.model.PreprocessorConditional; +import com._1c.g5.v8.dt.bsl.model.PreprocessorItemStatements; +import com._1c.g5.v8.dt.bsl.model.ReturnStatement; +import com._1c.g5.v8.dt.bsl.model.Statement; +import com._1c.g5.v8.dt.bsl.model.StaticFeatureAccess; +import com._1c.g5.v8.dt.bsl.model.TryExceptStatement; +import com._1c.g5.v8.dt.bsl.resource.TypesComputer; +import com._1c.g5.v8.dt.mcore.Environmental; +import com._1c.g5.v8.dt.mcore.TypeItem; +import com._1c.g5.v8.dt.mcore.util.McoreUtil; +import com._1c.g5.v8.dt.platform.version.IRuntimeVersionSupport; +import com._1c.g5.v8.dt.platform.version.Version; +import com.e1c.g5.v8.dt.check.CheckComplexity; +import com.e1c.g5.v8.dt.check.ICheckParameters; +import com.e1c.g5.v8.dt.check.components.BasicCheck; +import com.e1c.g5.v8.dt.check.settings.IssueSeverity; +import com.e1c.g5.v8.dt.check.settings.IssueType; +import com.e1c.v8codestyle.bsl.IAsyncInvocationProvider; +import com.e1c.v8codestyle.check.CommonSenseCheckExtension; +import com.e1c.v8codestyle.internal.bsl.BslPlugin; +import com.google.common.collect.Lists; +import com.google.inject.Inject; + +/** + * Checks that the asynchronous method is not followed by lines of code, + * since in this case the specified lines of code are executed immediately, + * without waiting for the asynchronous method to execute. + * + * @author Artem Iliukhin + */ +public final class CodeAfterAsyncCallCheck + extends BasicCheck +{ + + private static final String CHECK_ID = "code-after-async-call"; //$NON-NLS-1$ + private static final String DEFAULT_CHECK = Boolean.toString(Boolean.TRUE); + private static final String PARAMETER_NAME = "notifyDescriptionIsDefined"; //$NON-NLS-1$ + private static final String TYPE_NAME = "NotifyDescription"; //$NON-NLS-1$ + private final IAsyncInvocationProvider asyncInvocationProvider; + private final IRuntimeVersionSupport runtimeVersionSupport; + private final TypesComputer typesComputer; + + @Inject + public CodeAfterAsyncCallCheck(IRuntimeVersionSupport runtimeVersionSupport, + IAsyncInvocationProvider asyncInvocationProvider, TypesComputer typesComputer) + { + super(); + this.asyncInvocationProvider = asyncInvocationProvider; + this.runtimeVersionSupport = runtimeVersionSupport; + this.typesComputer = typesComputer; + } + + @Override + public String getCheckId() + { + return CHECK_ID; + } + + @Override + protected void configureCheck(CheckConfigurer builder) + { + builder.title(Messages.CodeAfterAsyncCallCheck_Title) + .description(Messages.CodeAfterAsyncCallCheck_Description) + .complexity(CheckComplexity.NORMAL) + .severity(IssueSeverity.MAJOR) + .issueType(IssueType.WARNING) + .extension(new CommonSenseCheckExtension(getCheckId(), BslPlugin.PLUGIN_ID)) + .module() + .checkedObjectType(INVOCATION) + .parameter(PARAMETER_NAME, Boolean.class, DEFAULT_CHECK, + Messages.CodeAfterAsyncCallCheck_Parameter); + } + + @Override + protected void check(Object object, ResultAcceptor resultAceptor, ICheckParameters parameters, + IProgressMonitor monitor) + { + Version version = runtimeVersionSupport.getRuntimeVersionOrDefault((EObject)object, Version.LATEST); + + Invocation inv = (Invocation)object; + FeatureAccess featureAccess = inv.getMethodAccess(); + if (featureAccess instanceof StaticFeatureAccess) + { + Collection asyncMethodsNames = asyncInvocationProvider.getAsyncInvocationNames(version); + if (asyncMethodsNames.contains(featureAccess.getName()) + && (isNotifyDescriptionDefined(inv) || !parameters.getBoolean(PARAMETER_NAME))) + { + checkNeighboringStatement(resultAceptor, inv); + } + } + else if (featureAccess instanceof DynamicFeatureAccess) + { + Map> names = asyncInvocationProvider.getAsyncTypeMethodNames(version); + if (names.containsKey(featureAccess.getName()) + && (isNotifyDescriptionDefined(inv) || !parameters.getBoolean(PARAMETER_NAME))) + { + Expression source = ((DynamicFeatureAccess)featureAccess).getSource(); + List sourceTypes = computeTypes(source); + Collection collection = names.get(featureAccess.getName()); + if (collection.retainAll(sourceTypes.stream().map(McoreUtil::getTypeName).collect(Collectors.toSet()))) + { + checkNeighboringStatement(resultAceptor, inv); + } + } + } + } + + private boolean isNotifyDescriptionDefined(Invocation inv) + { + for (Expression param : inv.getParams()) + { + List sourceTypes = computeTypes(param); + for (TypeItem typeItem : sourceTypes) + { + if (TYPE_NAME.equals(McoreUtil.getTypeName(typeItem))) + { + return true; + } + } + } + return false; + } + + private List computeTypes(Expression expression) + { + Environmental environmental = EcoreUtil2.getContainerOfType(expression, Environmental.class); + if (environmental != null) + { + return typesComputer.computeTypes(expression, environmental.environments()); + } + return List.of(); + } + + private void checkNeighboringStatement(ResultAcceptor resultAceptor, Invocation inv) + { + Statement statement = getStatementFromInvoc(inv); + if (statement != null && !(statement instanceof AwaitStatement)) + { + statement = getNextStatement(statement); + if (statement != null && !(statement instanceof ReturnStatement) + && !(statement instanceof EmptyStatement) && !(statement instanceof AwaitStatement)) + { + resultAceptor.addIssue(Messages.CodeAfterAsyncCallCheck_Issue, statement); + } + } + } + + private Statement getStatementFromInvoc(Invocation invocation) + { + EObject container = invocation.eContainer(); + while (!(container instanceof Statement)) + { + container = container.eContainer(); + } + return container instanceof Statement ? (Statement)container : null; + } + + private Statement getNextStatement(Statement statement) + { + Iterator it = EcoreUtil2.getAllContainers(statement).iterator(); + while (it.hasNext()) + { + EObject container = it.next(); + if (container instanceof PreprocessorConditional) + { + continue; + } + List st = getContainer(container); + if (st != null) + { + int index = st.indexOf(statement); + if (index != -1 && index + 1 < st.size()) + { + return st.get(index + 1); + } + } + } + return null; + } + + private List getContainer(EObject container) + { + List statements = null; + if (container instanceof LoopStatement) + { + statements = ((LoopStatement)container).getStatements(); + } + else if (container instanceof Conditional) + { + statements = ((Conditional)container).getStatements(); + } + else if (container instanceof IfStatement) + { + statements = ((IfStatement)container).getElseStatements(); + } + else if (container instanceof TryExceptStatement) + { + statements = getStatementsFromContainer((TryExceptStatement)container); + } + else if (container instanceof PreprocessorItemStatements) + { + statements = ((PreprocessorItemStatements)container).getStatements(); + } + else + { + statements = getStatementsFromContainer(container); + } + return statements; + } + + private List getStatementsFromContainer(TryExceptStatement container) + { + List res = Lists.newArrayList(); + res.addAll(container.getTryStatements()); + res.addAll(container.getExceptStatements()); + return res; + } + + @SuppressWarnings("unchecked") + private List getStatementsFromContainer(EObject container) + { + Object obj = container.eGet(BslPackage.Literals.BLOCK__STATEMENTS); + return obj instanceof List ? (List)obj : null; + } +} diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/Messages.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/Messages.java index 497dae479..10e4c7598 100644 --- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/Messages.java +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/Messages.java @@ -61,6 +61,14 @@ final class Messages public static String ChangeAndValidateInsteadOfAroundCheck_Use_ChangeAndValidate_instead_of_Around; public static String ChangeAndValidateInsteadOfAroundCheck_title; + public static String CodeAfterAsyncCallCheck_Description; + + public static String CodeAfterAsyncCallCheck_Issue; + + public static String CodeAfterAsyncCallCheck_Parameter; + + public static String CodeAfterAsyncCallCheck_Title; + public static String CommitTransactionCheck_Commit_transaction_must_be_in_try_catch; public static String CommitTransactionCheck_No_begin_transaction_for_commit_transaction; diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages.properties b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages.properties index 244143238..e751b369d 100644 --- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages.properties +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages.properties @@ -62,6 +62,14 @@ ChangeAndValidateInsteadOfAroundCheck_description = Checks that pragma &ChangeAn ChangeAndValidateInsteadOfAroundCheck_title = Use pragma &ChangeAndValidate instead of &Around +CodeAfterAsyncCallCheck_Description = Checks that the asynchronous method is not followed by lines of code, since in this case the specified lines of code are executed immediately, without waiting for the asynchronous method to execute + +CodeAfterAsyncCallCheck_Issue = The asynchronous method is followed by lines of code + +CodeAfterAsyncCallCheck_Parameter = Only with the defined parameter NotifyDescription, if applicable + +CodeAfterAsyncCallCheck_Title = The code should not follow an asynchronous call + CommitTransactionCheck_Commit_transaction_must_be_in_try_catch = Commit transaction must be in a try-catch CommitTransactionCheck_No_begin_transaction_for_commit_transaction = There is no begin transaction for commit transaction @@ -146,6 +154,12 @@ ExportMethodInCommandModule_Do_not_emded_export_method_in_modules_of_command_res ExportMethodInCommandModule_Do_not_use_export_method_in_commands_module = Restrictions on the use of export procedures and functions +ExportProcedureMissingCommentCheck_Export_procedure_missing_comment = Export procedure (function) "{0}" should be described by adding comment + +ExportProcedureMissingCommentCheck_description = Export procedure (function) should be described by adding comment + +ExportProcedureMissingCommentCheck_title = Export procedure (function) should be described by adding comment + ExportVariableInObjectModuleCheck_Description = Use of an export variable is not recommended ExportVariableInObjectModuleCheck_Issue = It's not recommended to use the export variable in the object module @@ -156,12 +170,6 @@ ExtensionMethodPrefixCheck_Description = The procedure (function) in the module ExtensionMethodPrefixCheck_Ext_method__0__should_have__1__prefix = The method "{0}" should have "{1}" prefix -ExportProcedureMissingCommentCheck_description=Export procedure (function) should be described by adding comment - -ExportProcedureMissingCommentCheck_Export_procedure_missing_comment=Export procedure (function) "{0}" should be described by adding comment - -ExportProcedureMissingCommentCheck_title=Export procedure (function) should be described by adding comment - ExtensionMethodPrefixCheck_Title = Extension method does not have extension prefix ExtensionVariablePrefixCheck_Description = The variable in the module of the extension object does not have a prefix corresponding to the prefix of the extension itself @@ -464,16 +472,16 @@ UseNonRecommendedMethods_parameter = List of non-recommended methods UseNonRecommendedMethods_title = Using non-recommended methods -VariableNameInvalidCheck_description=Variable name is invalid +VariableNameInvalidCheck_description = Variable name is invalid -VariableNameInvalidCheck_message_variable_length_is_less_than=variable length is less than {0} +VariableNameInvalidCheck_message_variable_length_is_less_than = variable length is less than {0} -VariableNameInvalidCheck_param_MIN_NAME_LENGTH_PARAM_title=Min. lenght of variable name +VariableNameInvalidCheck_param_MIN_NAME_LENGTH_PARAM_title = Min. lenght of variable name -VariableNameInvalidCheck_title=Variable name is invalid +VariableNameInvalidCheck_title = Variable name is invalid -VariableNameInvalidCheck_variable_name_is_invalid=Variable name {0} is invalid: {1} +VariableNameInvalidCheck_variable_name_is_invalid = Variable name {0} is invalid: {1} -VariableNameInvalidCheck_variable_name_must_start_with_a_capital_letter=variable name must start with a capital letter +VariableNameInvalidCheck_variable_name_must_start_with_a_capital_letter = variable name must start with a capital letter -VariableNameInvalidCheck_variable_name_starts_with_an_underline=variable name starts with an underline \ No newline at end of file +VariableNameInvalidCheck_variable_name_starts_with_an_underline = variable name starts with an underline diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages_ru.properties b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages_ru.properties index c7b0e9218..cad31d2a2 100644 --- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages_ru.properties +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/bsl/check/messages_ru.properties @@ -62,6 +62,14 @@ ChangeAndValidateInsteadOfAroundCheck_description = Проверяет, что ChangeAndValidateInsteadOfAroundCheck_title = Используется аннотация &ИзменениеИКонтроль вместо &Вместо +CodeAfterAsyncCallCheck_Description = Проверяет, что за асинхронным методом не следуют строки кода, поскольку в этом случае указанные строки кода выполняются немедленно, не дожидаясь выполнения асинхронного метода + +CodeAfterAsyncCallCheck_Issue = За асинхронным методом следуют строки кода + +CodeAfterAsyncCallCheck_Parameter = Tолько с заданным параметром ОписаниеОповещения, если применимо + +CodeAfterAsyncCallCheck_Title = Код не должен следовать за асинхронным вызовом + CommitTransactionCheck_Commit_transaction_must_be_in_try_catch = Вызов "ЗафиксироватьТранзакцию()" находится вне конструкции "Попытка... Исключение" CommitTransactionCheck_No_begin_transaction_for_commit_transaction = Отсутствует вызов "НачатьТранзакцию()", хотя вызываются "ЗафиксироватьТранзакцию()" @@ -146,6 +154,12 @@ ExportMethodInCommandModule_Do_not_emded_export_method_in_modules_of_command_res ExportMethodInCommandModule_Do_not_use_export_method_in_commands_module = Ограничения на использование экспортных процедур и функций +ExportProcedureMissingCommentCheck_Export_procedure_missing_comment = Отсутствует комментарий к экспортной процедуре (функции) "{0}" + +ExportProcedureMissingCommentCheck_description = Отсутствует комментарий к экспортной процедуре (функции) + +ExportProcedureMissingCommentCheck_title = Отсутствует комментарий к экспортной процедуре (функции) + ExportVariableInObjectModuleCheck_Description = Использование экспортной переменной не рекомендовано ExportVariableInObjectModuleCheck_Issue = Не рекомендуется использовать экспортную переменную в модуле объекта @@ -162,12 +176,6 @@ ExtensionVariablePrefixCheck_Description = Переменная модуля р ExtensionVariablePrefixCheck_Title = Переменная расширения должна содержать в имени префикс расширения -ExportProcedureMissingCommentCheck_description=Отсутствует комментарий к экспортной процедуре (функции) - -ExportProcedureMissingCommentCheck_Export_procedure_missing_comment=Отсутствует комментарий к экспортной процедуре (функции) "{0}" - -ExportProcedureMissingCommentCheck_title=Отсутствует комментарий к экспортной процедуре (функции) - ExtensionVariablePrefixCheck_Variable_0_should_have_1_prefix = Переменная "{0}" должна иметь префикс "{1}" FormDataToValueCheck_Description = Использование ДанныеФормыВЗначение вместо РеквизитФормыВЗначение @@ -476,4 +484,4 @@ VariableNameInvalidCheck_variable_name_is_invalid = Имя переменной VariableNameInvalidCheck_variable_name_must_start_with_a_capital_letter = имя переменной должно начинаться с заглавной буквы -VariableNameInvalidCheck_variable_name_starts_with_an_underline = имя переменной начинается с символа подчеркивания \ No newline at end of file +VariableNameInvalidCheck_variable_name_starts_with_an_underline = имя переменной начинается с символа подчеркивания diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/AsyncInvocationProvider.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/AsyncInvocationProvider.java new file mode 100644 index 000000000..488f0d54a --- /dev/null +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/AsyncInvocationProvider.java @@ -0,0 +1,242 @@ +/******************************************************************************* + * Copyright (C) 2023, 1C-Soft LLC and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * 1C-Soft LLC - initial API and implementation + *******************************************************************************/ +package com.e1c.v8codestyle.internal.bsl; + +import java.util.Collection; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.TreeMap; +import java.util.TreeSet; +import java.util.concurrent.ConcurrentHashMap; + +import org.eclipse.emf.ecore.EObject; +import org.eclipse.emf.ecore.resource.ResourceSet; +import org.eclipse.emf.ecore.util.EcoreUtil; +import org.eclipse.xtext.resource.IEObjectDescription; + +import com._1c.g5.v8.dt.bm.xtext.BmAwareResourceSetProvider; +import com._1c.g5.v8.dt.core.platform.IV8Project; +import com._1c.g5.v8.dt.core.platform.IV8ProjectManager; +import com._1c.g5.v8.dt.mcore.ContextDef; +import com._1c.g5.v8.dt.mcore.McorePackage; +import com._1c.g5.v8.dt.mcore.Method; +import com._1c.g5.v8.dt.mcore.ParamSet; +import com._1c.g5.v8.dt.mcore.Parameter; +import com._1c.g5.v8.dt.mcore.Type; +import com._1c.g5.v8.dt.mcore.TypeItem; +import com._1c.g5.v8.dt.mcore.TypeSet; +import com._1c.g5.v8.dt.mcore.util.Environment; +import com._1c.g5.v8.dt.mcore.util.McoreUtil; +import com._1c.g5.v8.dt.platform.IEObjectProvider; +import com._1c.g5.v8.dt.platform.version.Version; +import com.e1c.v8codestyle.bsl.IAsyncInvocationProvider; +import com.google.inject.Inject; + +/** + * Implementing service to provide asynchronous methods + * + * @author Artem Iliukhin + */ +public class AsyncInvocationProvider + implements IAsyncInvocationProvider +{ + + private static final String RET_TYPE_NAME = "Promise"; //$NON-NLS-1$ + private static final String EXEPTION_NAME = "RunCallback"; //$NON-NLS-1$ + private static final String TYPE_NAME = "NotifyDescription"; //$NON-NLS-1$ + private final Map> cashNames; + private final Map>> cashTypesMethodNames; + private final IV8ProjectManager v8ProjectManager; + private final BmAwareResourceSetProvider resourceSetProvider; + private final Set clientEnv; + + @Inject + public AsyncInvocationProvider(IV8ProjectManager v8ProjectManager, BmAwareResourceSetProvider resourceSetProvider) + { + super(); + this.v8ProjectManager = v8ProjectManager; + this.resourceSetProvider = resourceSetProvider; + this.cashNames = new ConcurrentHashMap<>(); + this.cashTypesMethodNames = new ConcurrentHashMap<>(); + this.clientEnv = Set.of(Environment.CLIENT, Environment.MNG_CLIENT, Environment.MOBILE_CLIENT, + Environment.MOBILE_THIN_CLIENT, Environment.THIN_CLIENT, Environment.WEB_CLIENT); + } + + @Override + public Collection getAsyncInvocationNames(Version version) + { + return cashNames.computeIfAbsent(version, this::collectGlobalAsyncMethods); + } + + @Override + public Map> getAsyncTypeMethodNames(Version version) + { + return cashTypesMethodNames.computeIfAbsent(version, this::collectAsyncMethods); + } + + private Collection collectGlobalAsyncMethods(Version version) + { + Collection asyncMethodsNames = new TreeSet<>(String.CASE_INSENSITIVE_ORDER); + Iterator iterator = v8ProjectManager.getProjects().iterator(); + if (iterator.hasNext()) + { + ResourceSet context = resourceSetProvider.get(iterator.next().getProject()); + IEObjectProvider provider = IEObjectProvider.Registry.INSTANCE.get(McorePackage.Literals.METHOD, version); + Iterable items = provider.getEObjectDescriptions(null); + for (IEObjectDescription item : items) + { + EObject object = EcoreUtil.resolve(item.getEObjectOrProxy(), context); + if (object instanceof Method) + { + collectMethod(asyncMethodsNames, (Method)object); + } + } + } + return asyncMethodsNames; + } + + private Map> collectAsyncMethods(Version version) + { + Map> asyncMethodsNames = new TreeMap<>(String.CASE_INSENSITIVE_ORDER); + Iterator iterator = v8ProjectManager.getProjects().iterator(); + if (iterator.hasNext()) + { + ResourceSet context = resourceSetProvider.get(iterator.next().getProject()); + IEObjectProvider provider = + IEObjectProvider.Registry.INSTANCE.get(McorePackage.Literals.TYPE_ITEM, version); + Iterable items = provider.getEObjectDescriptions(null); + + for (IEObjectDescription item : items) + { + EObject object = EcoreUtil.resolve(item.getEObjectOrProxy(), context); + if (object instanceof Type) + { + Type type = (Type)object; + process(asyncMethodsNames, type); + } + else if (object instanceof TypeSet) + { + TypeSet typeSet = (TypeSet)object; + for (Type type : typeSet.getTypes()) + { + process(asyncMethodsNames, type); + } + } + } + } + + return asyncMethodsNames; + } + + private void process(Map> asyncMethodsNames, Type type) + { + if (type == null || type.eIsProxy()) + { + return; + } + + ContextDef contextDef = type.getContextDef(); + if (contextDef == null) + { + return; + } + + for (Method method : contextDef.allMethods()) + { + if (isClient(method) && isRetTypePromise(method)) + { + if (asyncMethodsNames.get(method.getName()) == null) + { + asyncMethodsNames.putIfAbsent(method.getName(), new TreeSet<>()); + asyncMethodsNames.putIfAbsent(method.getNameRu(), new TreeSet<>()); + } + asyncMethodsNames.get(method.getName()).add(type.getName()); + asyncMethodsNames.get(method.getName()).add(type.getNameRu()); + asyncMethodsNames.get(method.getNameRu()).add(type.getName()); + asyncMethodsNames.get(method.getNameRu()).add(type.getNameRu()); + } + } + } + + private void collectMethod(Collection asyncMethodsNames, Method method) + { + if (EXEPTION_NAME.equals(method.getName())) + { + return; + } + + if (isMethodAsync(method)) + { + asyncMethodsNames.add(method.getName()); + asyncMethodsNames.add(method.getNameRu()); + } + } + + private boolean isMethodAsync(Method method) + { + if (isClient(method) && isRetTypePromise(method)) + { + return true; + } + + for (ParamSet paramSet : method.getParamSet()) + { + for (Parameter param : paramSet.getParams()) + { + if (isCallbackDescription(param)) + { + return true; + } + } + } + return false; + } + + private boolean isClient(Method method) + { + for (Environment env : method.environments().toArray()) + { + if (!clientEnv.contains(env)) + { + return false; + } + } + return true; + } + + private boolean isRetTypePromise(Method method) + { + for (TypeItem type : method.getRetValType()) + { + if (RET_TYPE_NAME.equals(McoreUtil.getTypeName(type))) + { + return true; + } + } + return false; + } + + private boolean isCallbackDescription(Parameter param) + { + for (TypeItem type : param.getType()) + { + if (TYPE_NAME.equals(McoreUtil.getTypeName(type))) + { + return true; + } + } + return false; + } + +} diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/BslPlugin.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/BslPlugin.java index 74df5ee6b..ad1f2ad26 100644 --- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/BslPlugin.java +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/BslPlugin.java @@ -20,6 +20,7 @@ import com._1c.g5.v8.dt.bsl.model.BslPackage; import com._1c.g5.wiring.InjectorAwareServiceRegistrator; import com._1c.g5.wiring.ServiceInitialization; +import com.e1c.v8codestyle.bsl.IAsyncInvocationProvider; import com.e1c.v8codestyle.bsl.IModuleStructureProvider; import com.google.inject.Guice; import com.google.inject.Injector; @@ -131,6 +132,7 @@ public void start(BundleContext bundleContext) throws Exception ServiceInitialization.schedule(() -> { // register services from injector registrator.service(IModuleStructureProvider.class).registerInjected(); + registrator.service(IAsyncInvocationProvider.class).registerInjected(); registrator.managedService(MultiCheckFixRegistrator.class).activateBeforeRegistration().registerInjected(); }); } diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ExternalDependenciesModule.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ExternalDependenciesModule.java index 27757dcca..389314050 100644 --- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ExternalDependenciesModule.java +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ExternalDependenciesModule.java @@ -25,6 +25,7 @@ import org.eclipse.xtext.resource.impl.ResourceDescriptionsProvider; import org.eclipse.xtext.scoping.IScopeProvider; +import com._1c.g5.v8.dt.bm.xtext.BmAwareResourceSetProvider; import com._1c.g5.v8.dt.bsl.common.IBslPreferences; import com._1c.g5.v8.dt.bsl.contextdef.IBslModuleContextDefService; import com._1c.g5.v8.dt.bsl.documentation.comment.BslMultiLineCommentDocumentationProvider; @@ -91,5 +92,6 @@ protected void doConfigure() bind(ResourceDescriptionsProvider.class).toService(); bind(IConfigurationProvider.class).toService(); bind(BslGrammarAccess.class).toProvider(() -> rsp.get(BslGrammarAccess.class)); + bind(BmAwareResourceSetProvider.class).toProvider(() -> rsp.get(BmAwareResourceSetProvider.class)); } } diff --git a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ServiceModule.java b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ServiceModule.java index fa7b056f1..eabb0233f 100644 --- a/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ServiceModule.java +++ b/bundles/com.e1c.v8codestyle.bsl/src/com/e1c/v8codestyle/internal/bsl/ServiceModule.java @@ -12,6 +12,7 @@ *******************************************************************************/ package com.e1c.v8codestyle.internal.bsl; +import com.e1c.v8codestyle.bsl.IAsyncInvocationProvider; import com.e1c.v8codestyle.bsl.IModuleStructureProvider; import com.google.inject.AbstractModule; import com.google.inject.Singleton; @@ -29,6 +30,7 @@ public class ServiceModule protected void configure() { bind(IModuleStructureProvider.class).to(ModuleStructureProvider.class).in(Singleton.class); + bind(IAsyncInvocationProvider.class).to(AsyncInvocationProvider.class).in(Singleton.class); } } diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-back-descr.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-back-descr.bsl new file mode 100644 index 000000000..77361aa36 --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-back-descr.bsl @@ -0,0 +1,16 @@ + +&НаКлиенте +Процедура Тест() + + Оповещение = Новый ОписаниеОповещения("ПредупреждениеЗавершение", ЭтотОбъект); + Текст = "Текст предупреждения"; + ПоказатьПредупреждение(Оповещение, Текст); + +КонецПроцедуры + +&НаКлиенте +Процедура ПредупреждениеЗавершение(ДополнительныеПараметры) Экспорт + + Сообщить("Закрыли предупреждение"); + +КонецПроцедуры; diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise.bsl new file mode 100644 index 000000000..acfeb509c --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise.bsl @@ -0,0 +1,7 @@ + +Асинх Процедура Тест1(Параметры) + + Ждать ВопросАсинх(Параметры,); + Сообщить("Закрыли предупреждение"); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise2.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise2.bsl new file mode 100644 index 000000000..64638c9ec --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise2.bsl @@ -0,0 +1,8 @@ + +Асинх Процедура Тест2(Параметры) + + Обещание = ПредупреждениеАсинх(Параметры); + Ждать Обещание; + Сообщить("Закрыли предупреждение"); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise3.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise3.bsl new file mode 100644 index 000000000..05f172745 --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise3.bsl @@ -0,0 +1,6 @@ + +Асинх Функция Тест3(Параметры) + + Возврат Ждать ПредупреждениеАсинх(Параметры); + +КонецФункции \ No newline at end of file diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise4.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise4.bsl new file mode 100644 index 000000000..49e79a2c5 --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-promise4.bsl @@ -0,0 +1,7 @@ + +Асинх Функция Тест4(Параметры) + + Обещание = ПредупреждениеАсинх(Параметры); + Возврат Ждать Обещание; + +КонецФункции \ No newline at end of file diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-spread-sh-doc.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-spread-sh-doc.bsl new file mode 100644 index 000000000..07fe097a0 --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/async-call-spread-sh-doc.bsl @@ -0,0 +1,10 @@ + + +Процедура Тест() + + ТабДок = Новый ТабличныйДокумент; + ТабДок.ЗаписатьАсинх(); + + Сообщить("кто быстрее?"); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/com.e1c.v8codestyle.bsl.itests/resources/code-after-async-call-existence.bsl b/tests/com.e1c.v8codestyle.bsl.itests/resources/code-after-async-call-existence.bsl new file mode 100644 index 000000000..b9d95d628 --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/resources/code-after-async-call-existence.bsl @@ -0,0 +1,9 @@ + +&НаКлиенте +Процедура Тест() + + Текст = "Текст предупреждения"; + ПоказатьПредупреждение(, Текст); + Сообщить("Закрыли предупреждение"); + +КонецПроцедуры \ No newline at end of file diff --git a/tests/com.e1c.v8codestyle.bsl.itests/src/com/e1c/v8codestyle/bsl/check/itests/CodeAfterAsyncCallCheckTest.java b/tests/com.e1c.v8codestyle.bsl.itests/src/com/e1c/v8codestyle/bsl/check/itests/CodeAfterAsyncCallCheckTest.java new file mode 100644 index 000000000..6789b20a0 --- /dev/null +++ b/tests/com.e1c.v8codestyle.bsl.itests/src/com/e1c/v8codestyle/bsl/check/itests/CodeAfterAsyncCallCheckTest.java @@ -0,0 +1,135 @@ +/******************************************************************************* + * Copyright (C) 2023, 1C-Soft LLC and others. + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * 1C-Soft LLC - initial API and implementation + *******************************************************************************/ +package com.e1c.v8codestyle.bsl.check.itests; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; + +import java.util.Collections; + +import org.eclipse.core.resources.IProject; +import org.junit.Test; + +import com._1c.g5.v8.dt.core.platform.IDtProject; +import com._1c.g5.v8.dt.validation.marker.IExtraInfoKeys; +import com._1c.g5.v8.dt.validation.marker.Marker; +import com.e1c.g5.v8.dt.check.settings.CheckUid; +import com.e1c.g5.v8.dt.check.settings.ICheckSettings; +import com.e1c.v8codestyle.bsl.check.CodeAfterAsyncCallCheck; +import com.e1c.v8codestyle.internal.bsl.BslPlugin; + +/** + * Tests for {@link CodeAfterAsyncCallCheck} check. + * + * @author Artem Iliukhin + */ +public class CodeAfterAsyncCallCheckTest + extends AbstractSingleModuleTestBase +{ + private static final String CHECK_ID = "code-after-async-call"; //$NON-NLS-1$ + private static final String PARAMETER_NAME = "notifyDescriptionIsDefined"; //$NON-NLS-1$ + + /** + * Instantiates a new code after async call check test. + */ + public CodeAfterAsyncCallCheckTest() + { + super(CodeAfterAsyncCallCheck.class); + } + + @Test + public void testCodeAfterExistence() throws Exception + { + setParameterValue(Boolean.FALSE); + + updateModule(FOLDER_RESOURCE + "code-after-async-call-existence.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNotNull(marker); + assertEquals("7", marker.getExtraInfo().get(IExtraInfoKeys.TEXT_EXTRA_INFO_LINE_KEY)); + } + + @Test + public void testCallBackDescriptionCompliant() throws Exception + { + updateModule(FOLDER_RESOURCE + "async-call-back-descr.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNull(marker); + } + + @Test + public void testPromiseCompliant() throws Exception + { + setParameterValue(Boolean.FALSE); + + updateModule(FOLDER_RESOURCE + "async-call-promise.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNull(marker); + } + + @Test + public void testPromiseCompliant2() throws Exception + { + setParameterValue(Boolean.FALSE); + + updateModule(FOLDER_RESOURCE + "async-call-promise2.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNull(marker); + } + + @Test + public void testPromiseCompliant3() throws Exception + { + setParameterValue(Boolean.FALSE); + + updateModule(FOLDER_RESOURCE + "async-call-promise3.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNull(marker); + } + + @Test + public void testPromiseComplian4() throws Exception + { + setParameterValue(Boolean.FALSE); + + updateModule(FOLDER_RESOURCE + "async-call-promise4.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNull(marker); + } + + @Test + public void testSpreadsheetDocumentNonCompliant() throws Exception + { + updateModule(FOLDER_RESOURCE + "async-call-spread-sh-doc.bsl"); + + Marker marker = getFirstMarker(CHECK_ID, getModuleId(), getProject()); + assertNotNull(marker); + assertEquals("8", marker.getExtraInfo().get(IExtraInfoKeys.TEXT_EXTRA_INFO_LINE_KEY)); + } + + private void setParameterValue(Boolean value) + { + IDtProject dtProject = getProject(); + IProject project = dtProject.getWorkspaceProject(); + ICheckSettings settings = checkRepository.getSettings(new CheckUid(CHECK_ID, BslPlugin.PLUGIN_ID), project); + settings.getParameters().get(PARAMETER_NAME).setValue(Boolean.toString(value)); + checkRepository.applyChanges(Collections.singleton(settings), project); + waitForDD(dtProject); + } +}