Skip to content

Commit

Permalink
Проверяет отсутствие кода после асинхронного вызова
Browse files Browse the repository at this point in the history
  • Loading branch information
iArtemv committed Jan 16, 2023
1 parent 89a2fea commit dd5e714
Show file tree
Hide file tree
Showing 11 changed files with 408 additions and 0 deletions.
24 changes: 24 additions & 0 deletions bundles/com.e1c.v8codestyle.bsl/markdown/code-after-async-call.md
Original file line number Diff line number Diff line change
@@ -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/)
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Код расположен после асинхронного вызова

При асинхронном подходе вызов метода выполняется как обычно, но управление возвращается вызывающему коду до того,
как асинхронный метод завершит свою работу. После этого вызывающий код продолжает свое выполнение.
Особенность асинхронного выполнения: исполнение на стороне вызывающего кода продолжится до того,
как полностью закончилось исполнение вызванного метода

Для правильного решения нужно вынести весь код, который должен быть выполнен после выполнения асинхронного действия,
в экспортный метод и указать его имя в обработке оповещения, которая будет вызвана после завершения асинхронного действия.
Или использовать асинхронность через обещания, например, Ждать ПредупреждениеАсинх(Текст);

## Неправильно

```bsl
Текст = "Текст предупреждения";
ПоказатьПредупреждение( , Текст);
Сообщить("Закрыли предупреждение");
```

## Правильно

```bsl
Текст = "Текст предупреждения";
Ждать ПредупреждениеАсинх(Текст);
Сообщить("Закрыли предупреждение");
```

```bsl
&НаКлиенте
Процедура Команда1(Команда)
Оповещение = Новый ОписаниеОповещения("ПредупреждениеЗавершение", ЭтотОбъект);
Текст = "Текст предупреждения";
ПоказатьПредупреждение(Оповещение, Текст);
КонецПроцедуры
&НаКлиенте
Процедура ПредупреждениеЗавершение(ДополнительныеПараметры) Экспорт
Сообщить("Закрыли предупреждение");
КонецПроцедуры;
```

## См.

- [Синхронные и асинхронные методы работы](https://its.1c.ru/db/v8319doc#bookmark:dev:TI000001505)
4 changes: 4 additions & 0 deletions bundles/com.e1c.v8codestyle.bsl/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,10 @@
category="com.e1c.v8codestyle.bsl"
class="com.e1c.v8codestyle.internal.bsl.ExecutableExtensionFactory:com.e1c.v8codestyle.bsl.check.DeprecatedProcedureOutsideDeprecatedRegionCheck">
</check>
<check
category="com.e1c.v8codestyle.bsl"
class="com.e1c.v8codestyle.internal.bsl.ExecutableExtensionFactory:com.e1c.v8codestyle.bsl.check.CodeAfterAsyncCallCheck">
</check>
</extension>
<extension
point="org.eclipse.core.runtime.preferences">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
/*******************************************************************************
* 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.Iterator;
import java.util.List;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.naming.IQualifiedNameConverter;

import com._1c.g5.v8.dt.bsl.common.IBslPreferences;
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.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.PreprocessorItemStatements;
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.core.platform.IResourceLookup;
import com.e1c.g5.v8.dt.check.CheckComplexity;
import com.e1c.g5.v8.dt.check.ICheckParameters;
import com.e1c.g5.v8.dt.check.settings.IssueSeverity;
import com.e1c.g5.v8.dt.check.settings.IssueType;
import com.e1c.v8codestyle.bsl.strict.check.AbstractTypeCheck;
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 AbstractTypeCheck
{

private static final String CHECK_ID = "code-after-async-call"; //$NON-NLS-1$

// @formatter:off
private static final String[] ASYNCHRONOUS_METHODS = {
"ShowMessageBox", "ПоказатьПредупреждение", //$NON-NLS-1$ //$NON-NLS-2$
"ShowInputDate", "ПоказатьВводДаты", //$NON-NLS-1$ //$NON-NLS-2$
"ShowInputValue", "ПоказатьВводЗначения", //$NON-NLS-1$ //$NON-NLS-2$
"ShowInputString", "ПоказатьВводСтроки", //$NON-NLS-1$ //$NON-NLS-2$
"ShowInputNumber", "ПоказатьВводЧисла", //$NON-NLS-1$ //$NON-NLS-2$
"ShowQueryBox", "ПоказатьВопрос", //$NON-NLS-1$ //$NON-NLS-2$
"BeginRequestingUserPermission", "НачатьЗапросРазрешенияПользователя", //$NON-NLS-1$ //$NON-NLS-2$
"BeginRunningApplication", "НачатьЗапускПриложения", //$NON-NLS-1$ //$NON-NLS-2$
"BeginGettingTempFilesDir", "НачатьПолучениеКаталогаВременныхФайлов", //$NON-NLS-1$ //$NON-NLS-2$
"BeginGettingDocumentsDir", "НачатьПолучениеКаталогаДокументов", //$NON-NLS-1$ //$NON-NLS-2$
"BeginCopyingFile", "НачатьКопированиеФайла", //$NON-NLS-1$ //$NON-NLS-2$
"BeginFindingFiles", "НачатьПоискФайлов", //$NON-NLS-1$ //$NON-NLS-2$
"ShowValue", "ПоказатьЗначение", //$NON-NLS-1$ //$NON-NLS-2$
"OpenForm", "ОткрытьФорму", //$NON-NLS-1$ //$NON-NLS-2$
"BeginMovingFile", "НачатьПеремещениеФайла", //$NON-NLS-1$ //$NON-NLS-2$
"BeginAttachingCryptoExtension", "НачатьПодключениеРасширенияРаботыСКриптографией", //$NON-NLS-1$ //$NON-NLS-2$
"BeginAttachingFileSystemExtension", "НачатьПодключениеРасширенияРаботыСФайлами", //$NON-NLS-1$ //$NON-NLS-2$
"BeginGetFilesFromServer", "НачатьПолучениеФайловССервера", //$NON-NLS-1$ //$NON-NLS-2$
"BeginPutFileToServer", "НачатьПомещениеФайлаНаСервер", //$NON-NLS-1$ //$NON-NLS-2$
"BeginPutFilesToServer", "НачатьПомещениеФайловНаСервер", //$NON-NLS-1$ //$NON-NLS-2$
"BeginGettingUserDataWorkDir", "НачатьПолучениеРабочегоКаталогаДанныхПользователя", //$NON-NLS-1$ //$NON-NLS-2$
"BeginCreatingDirectory", "НачатьСозданиеКаталога", //$NON-NLS-1$ //$NON-NLS-2$
"BeginInstallAddIn", "НачатьУстановкуВнешнейКомпоненты", //$NON-NLS-1$ //$NON-NLS-2$
"BeginInstallCryptoExtension", "НачатьУстановкуРасширенияРаботыСКриптографией", //$NON-NLS-1$ //$NON-NLS-2$
"BeginInstallFileSystemExtension", "НачатьУстановкуРасширенияРаботыСФайлами"}; //$NON-NLS-1$ //$NON-NLS-2$
// @formatter:on

@Inject
public CodeAfterAsyncCallCheck(IResourceLookup resourceLookup, IBslPreferences bslPreferences,
IQualifiedNameConverter qualifiedNameConverter)
{
super(resourceLookup, bslPreferences, qualifiedNameConverter);
}

@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);
}

@Override
protected void check(Object object, ResultAcceptor resultAceptor, ICheckParameters parameters,
IProgressMonitor monitor)
{
Invocation inv = (Invocation)object;
FeatureAccess featureAccess = inv.getMethodAccess();
if (featureAccess instanceof StaticFeatureAccess)
{
if (monitor.isCanceled())
{
return;
}

String nameFeature = featureAccess.getName();
for (int i = 0; i < ASYNCHRONOUS_METHODS.length; i++)
{
if (ASYNCHRONOUS_METHODS[i].equalsIgnoreCase(nameFeature))
{
Statement statement = getStatementFromInvoc(inv);
if (statement != null)
{
statement = getNextStatement(statement);
if (statement != null)
{
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<EObject> it = EcoreUtil2.getAllContainers(statement).iterator();
while (it.hasNext())
{
EObject container = it.next();
List<Statement> st = null;
if (container instanceof LoopStatement)
{
st = ((LoopStatement)container).getStatements();
}
else if (container instanceof Conditional)
{
st = ((Conditional)container).getStatements();
}
else if (container instanceof IfStatement)
{
st = ((IfStatement)container).getElseStatements();
}
else if (container instanceof TryExceptStatement)
{
st = getStatementsFromContainer((TryExceptStatement)container);
}
else if (container instanceof PreprocessorItemStatements)
{
st = ((PreprocessorItemStatements)container).getStatements();
}
else
{
st = getStatementsFromContainer(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<Statement> getStatementsFromContainer(TryExceptStatement container)
{
List<Statement> res = Lists.newArrayList();
res.addAll(container.getTryStatements());
res.addAll(container.getExceptStatements());
return res;
}

@SuppressWarnings("unchecked")
private List<Statement> getStatementsFromContainer(EObject container)
{
Object obj = container.eGet(BslPackage.Literals.BLOCK__STATEMENTS);
return obj instanceof List ? (List<Statement>)obj : null;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ 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_Title;

public static String CommitTransactionCheck_Commit_transaction_must_be_in_try_catch;

public static String CommitTransactionCheck_No_begin_transaction_for_commit_transaction;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ 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_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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,12 @@ ChangeAndValidateInsteadOfAroundCheck_description = Проверяет, что

ChangeAndValidateInsteadOfAroundCheck_title = Используется аннотация &ИзменениеИКонтроль вместо &Вместо

CodeAfterAsyncCallCheck_Description=Проверяет, что за асинхронным методом не следуют строки кода, поскольку в этом случае указанные строки кода выполняются немедленно, не дожидаясь выполнения асинхронного метода

CodeAfterAsyncCallCheck_Issue=За асинхронным методом следуют строки кода

CodeAfterAsyncCallCheck_Title=Код не должен следовать за асинхронным вызовом

CommitTransactionCheck_Commit_transaction_must_be_in_try_catch=Вызов "ЗафиксироватьТранзакцию()" находится вне конструкции "Попытка... Исключение"

CommitTransactionCheck_No_begin_transaction_for_commit_transaction=Отсутствует вызов "НачатьТранзакцию()", хотя вызываются "ЗафиксироватьТранзакцию()"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

&НаКлиенте
Процедура Тест()

Оповещение = Новый ОписаниеОповещения("ПредупреждениеЗавершение", ЭтотОбъект);
Текст = "Текст предупреждения";
ПоказатьПредупреждение(Оповещение, Текст);

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

&НаКлиенте
Процедура ПредупреждениеЗавершение(ДополнительныеПараметры) Экспорт

Сообщить("Закрыли предупреждение");

КонецПроцедуры;
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

&НаКлиенте
Процедура Тест()

Текст = "Текст предупреждения";
Ждать ПредупреждениеАсинх(Текст);
Сообщить("Закрыли предупреждение");

КонецПроцедуры
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@

&НаКлиенте
Процедура Тест()

Текст = "Текст предупреждения";
ПоказатьПредупреждение(, Текст);
Сообщить("Закрыли предупреждение");

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

0 comments on commit dd5e714

Please sign in to comment.