1
0
mirror of https://github.com/1C-Company/v8-code-style.git synced 2025-12-03 09:25:22 +02:00

#952 проверка добавления в типизированную коллекцию

This commit is contained in:
Dmitriy Marmyshev
2022-02-14 19:30:22 +03:00
parent 288286bdc7
commit 43f56faf27
5 changed files with 228 additions and 10 deletions

View File

@@ -50,6 +50,7 @@
- Ограничения на использование экспортных процедур и функций в модулях команд и форм
- Вызов "Заблокировать()" находится вне попытки
- Для проверок dynamic-access-method-not-found и property-return-type добавлена возможность исключения по типам (COM-Объекты)
- Проверка типов invocation-parameter-type-intersect проверяет типы элементов коллекций: Массив, Соотвествие, СписокЗначений
#### Запросы

View File

@@ -21,7 +21,9 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;
@@ -36,11 +38,14 @@ import org.eclipse.xtext.EcoreUtil2;
import org.eclipse.xtext.naming.IQualifiedNameConverter;
import org.eclipse.xtext.resource.IResourceServiceProvider;
import org.eclipse.xtext.scoping.IScope;
import org.eclipse.xtext.util.Triple;
import org.eclipse.xtext.util.Tuples;
import com._1c.g5.v8.dt.bsl.common.IBslPreferences;
import com._1c.g5.v8.dt.bsl.documentation.comment.BslCommentUtils;
import com._1c.g5.v8.dt.bsl.documentation.comment.BslDocumentationComment;
import com._1c.g5.v8.dt.bsl.model.BslPackage;
import com._1c.g5.v8.dt.bsl.model.DynamicFeatureAccess;
import com._1c.g5.v8.dt.bsl.model.EmptyExpression;
import com._1c.g5.v8.dt.bsl.model.Expression;
import com._1c.g5.v8.dt.bsl.model.FeatureAccess;
@@ -61,6 +66,8 @@ import com._1c.g5.v8.dt.mcore.McorePackage;
import com._1c.g5.v8.dt.mcore.NamedElement;
import com._1c.g5.v8.dt.mcore.ParamSet;
import com._1c.g5.v8.dt.mcore.Parameter;
import com._1c.g5.v8.dt.mcore.Property;
import com._1c.g5.v8.dt.mcore.Type;
import com._1c.g5.v8.dt.mcore.TypeItem;
import com._1c.g5.v8.dt.mcore.util.Environments;
import com._1c.g5.v8.dt.mcore.util.McoreUtil;
@@ -81,11 +88,43 @@ import com.google.inject.Inject;
public class InvocationParamIntersectionCheck
extends AbstractTypeCheck
{
private static final String METHOD_INSERT = "Insert"; //$NON-NLS-1$
private static final String MAP_GET = "Get"; //$NON-NLS-1$
private static final String MAP_DELETE = "Delete"; //$NON-NLS-1$
private static final String MAP_KEY = "Key"; //$NON-NLS-1$
private static final String MAP_VALUE = "Value"; //$NON-NLS-1$
private static final String CHECK_ID = "invocation-parameter-type-intersect"; //$NON-NLS-1$
private static final String PARAM_ALLOW_DYNAMIC_TYPES_CHECK = "allowDynamicTypesCheck"; //$NON-NLS-1$
//@formatter:off
private static final Map<String, Map<String, Collection<Integer>>> COLLECTION_ADD_METHODS = Map.of(
"Add", Map.of(IEObjectTypeNames.ARRAY, Set.of(0), //$NON-NLS-1$
IEObjectTypeNames.VALUE_LIST, Set.of(0)),
METHOD_INSERT, Map.of(
IEObjectTypeNames.ARRAY, Set.of(1),
IEObjectTypeNames.VALUE_LIST, Set.of(1),
IEObjectTypeNames.MAP, Set.of(0, 1)),
"Set", Map.of( //$NON-NLS-1$
IEObjectTypeNames.ARRAY, Set.of(1)),
MAP_GET, Map.of(IEObjectTypeNames.MAP, Set.of(0)),
MAP_DELETE, Map.of(IEObjectTypeNames.MAP, Set.of(0)),
"Find", Map.of(IEObjectTypeNames.ARRAY, Set.of(0)) //$NON-NLS-1$
);
private static final Map<String, Map<Integer, String>> MAP_KEY_VALUE_TYPES = Map.of(
METHOD_INSERT, Map.of(0, MAP_KEY, 1, MAP_VALUE),
MAP_GET, Map.of(0, MAP_KEY),
MAP_DELETE, Map.of(0, MAP_KEY)
);
//@formatter:on
private final IV8ProjectManager v8ProjectManager;
private final ExportMethodTypeProvider exportMethodTypeProvider;
@@ -184,6 +223,13 @@ public class InvocationParamIntersectionCheck
Environments actualEnvs = getActualEnvironments(inv);
// This allows to check collection item type
Triple<Collection<TypeItem>, Collection<Integer>, Boolean> collectionItemContext =
getCollectionItemContext(inv, method, actualEnvs);
Collection<TypeItem> collectionItemTypes = collectionItemContext.getFirst();
Collection<Integer> parameterNumbers = collectionItemContext.getSecond();
boolean isMap = collectionItemContext.getThird();
for (int i = 0; i < inv.getParams().size(); i++)
{
if (monitor.isCanceled())
@@ -196,10 +242,12 @@ public class InvocationParamIntersectionCheck
boolean isUndefined = param == null || param instanceof UndefinedLiteral || param instanceof EmptyExpression
|| isUndefinedType(sorceTypes);
Collection<TypeItem> targetTypes = Collections.emptyList();
boolean isIntersect = false;
Collection<TypeItem> targetTypes =
getDefaultTargetOrCollectionItemTypes(method, collectionItemTypes, parameterNumbers, isMap, i, inv);
boolean isIntersect = !targetTypes.isEmpty() && intersectTypeItem(targetTypes, sorceTypes, inv);
Parameter parameter = null;
for (Iterator<ParamSet> iterator = paramSets.iterator(); iterator.hasNext();)
for (Iterator<ParamSet> iterator = paramSets.iterator(); !isIntersect && targetTypes.isEmpty()
&& iterator.hasNext();)
{
ParamSet paramSet = iterator.next();
@@ -252,10 +300,6 @@ public class InvocationParamIntersectionCheck
}
isIntersect = intersectTypeItem(targetTypes, sorceTypes, inv);
if (isIntersect)
{
break;
}
}
if (!isIntersect && !targetTypes.isEmpty())
@@ -265,6 +309,79 @@ public class InvocationParamIntersectionCheck
}
}
private Triple<Collection<TypeItem>, Collection<Integer>, Boolean> getCollectionItemContext(Invocation inv,
com._1c.g5.v8.dt.mcore.Method method, Environments actualEnvs)
{
Collection<TypeItem> collectionItemTypes = new ArrayList<>();
Collection<Integer> parameterNumbers = Collections.emptyList();
boolean isMap = false;
if (!(method instanceof SourceObjectLinkProvider) && inv.getMethodAccess() instanceof DynamicFeatureAccess)
{
Map<String, Collection<Integer>> typesAndParams = COLLECTION_ADD_METHODS.get(method.getName());
if (typesAndParams != null)
{
TypeItem collectionType = EcoreUtil2.getContainerOfType(method, TypeItem.class);
String typeName = McoreUtil.getTypeName(collectionType);
if (typeName != null && typesAndParams.containsKey(typeName))
{
List<TypeItem> types = typeComputer
.computeTypes(((DynamicFeatureAccess)inv.getMethodAccess()).getSource(), actualEnvs);
for (TypeItem type : types)
{
type = (TypeItem)EcoreUtil.resolve(type, inv);
if (type instanceof Type && typeName.equals(McoreUtil.getTypeName(type)))
{
collectionItemTypes.addAll(((Type)type).getCollectionElementTypes().allTypes());
isMap = IEObjectTypeNames.MAP.equals(typeName);
parameterNumbers = typesAndParams.get(typeName);
}
}
// Remove Arbitrary type which do not need to check
for (Iterator<TypeItem> iterator = collectionItemTypes.iterator(); iterator.hasNext();)
{
TypeItem typeItem = iterator.next();
if (IEObjectTypeNames.ARBITRARY.equals(McoreUtil.getTypeName(typeItem)))
{
iterator.remove();
}
}
}
}
}
return Tuples.create(collectionItemTypes, parameterNumbers, isMap);
}
private Collection<TypeItem> getDefaultTargetOrCollectionItemTypes(com._1c.g5.v8.dt.mcore.Method method,
Collection<TypeItem> collectionItemTypes, Collection<Integer> parameterNumbers, boolean isMap,
int parameterNumber, EObject context)
{
Collection<TypeItem> targetTypes = Collections.emptyList();
if (!collectionItemTypes.isEmpty() && parameterNumbers.contains(parameterNumber))
{
// use collection item types
if (isMap)
{
String name = MAP_KEY_VALUE_TYPES.get(method.getName()).get(parameterNumber);
Optional<Property> property =
dynamicFeatureAccessComputer.getAllProperties(collectionItemTypes, context.eResource())
.stream()
.flatMap(p -> p.getFirst().stream())
.filter(p -> name.equals(p.getName()))
.findFirst();
if (property.isPresent())
{
targetTypes = property.get().getTypes();
}
}
else
{
targetTypes = collectionItemTypes;
}
}
return targetTypes;
}
private boolean isUndefinedType(List<TypeItem> types)
{
if (types.size() == 1)
@@ -313,7 +430,7 @@ public class InvocationParamIntersectionCheck
FormalParam formalParam = targetParams.get(i);
String paramName = formalParam.getName();
Collection<TypeItem> targetTypes = Collections.emptyList();
Collection<TypeItem> targetTypes = null;
if (docComment.isPresent() && docComment.get().getParametersSection().getParameterByName(paramName) != null)
{
// if parameter declared in doc-comment then check only declared types

View File

@@ -0,0 +1,31 @@
// @strict-types
// Parameters:
// MapParamenter - Map of KeyAndValue:
// * Key - Number -
// * Value - Number -
Procedure NonComplaint(MapParamenter) Export
Array = new Array; // Array of Number
Array.Add("");
MapParamenter.Insert("",
False);
EndProcedure
// Parameters:
// MapParamenter - Map of KeyAndValue:
// * Key - Number -
// * Value - Number -
Procedure Complaint(MapParamenter) Export
Array = new Array; // Array of Number
Array.Add(10);
MapParamenter.Insert(10,
10);
EndProcedure

View File

@@ -0,0 +1,17 @@
// @strict-types
Procedure NonComplaint() Export
Complaint("1");
EndProcedure
// Parameters:
// Strings - Array
Procedure Complaint(Strings)
Complaint(Strings);
EndProcedure

View File

@@ -407,6 +407,34 @@ public class CommonModuleStrictTypesTest
}
/**
* Test of {@link InvocationParamIntersectionCheck} that invokable method parameter type intersects
* with caller type for collections with typed items.
*
* @throws Exception the exception
*/
@Test
public void testInvocationParamIntersectionCollectionItemCheck() throws Exception
{
String checkId = "invocation-parameter-type-intersect";
String resouceName = "invocation-parameter-type-intersect-collection-item";
Module module = updateAndGetModule(resouceName);
List<Marker> markers = getMarters(checkId, module);
assertEquals(3, markers.size());
Marker marker = markers.get(0);
assertEquals("10", marker.getExtraInfo().get(IExtraInfoKeys.TEXT_EXTRA_INFO_LINE_KEY));
marker = markers.get(1);
assertEquals("13", marker.getExtraInfo().get(IExtraInfoKeys.TEXT_EXTRA_INFO_LINE_KEY));
marker = markers.get(2);
assertEquals("12", marker.getExtraInfo().get(IExtraInfoKeys.TEXT_EXTRA_INFO_LINE_KEY));
}
/**
* Test of {@link InvocationParamIntersectionCheck} that invokable method parameter type intersects
* with caller type, and skip checking if method has default value parameters.
@@ -431,6 +459,30 @@ public class CommonModuleStrictTypesTest
}
/**
* Test of {@link InvocationParamIntersectionCheck} that invokable method parameter type intersects
* with caller type that is local method with documentation comment.
*
* @throws Exception the exception
*/
@Test
public void testInvocationParamIntersectionCheckLocalDocComment() throws Exception
{
String checkId = "invocation-parameter-type-intersect";
String resouceName = "invocation-parameter-type-intersect-local-doc-comment";
Module module = updateAndGetModule(resouceName);
List<Marker> markers = getMarters(checkId, module);
assertEquals(1, markers.size());
Marker marker = markers.get(0);
assertEquals("5", marker.getExtraInfo().get(IExtraInfoKeys.TEXT_EXTRA_INFO_LINE_KEY));
}
private IDtProject getProject()
{
return dtProject;
@@ -446,9 +498,9 @@ public class CommonModuleStrictTypesTest
return markers;
}
private Module updateAndGetModule(String checkId) throws CoreException, IOException
private Module updateAndGetModule(String resourceName) throws CoreException, IOException
{
try (InputStream in = getClass().getResourceAsStream(FOLDER + checkId + ".bsl"))
try (InputStream in = getClass().getResourceAsStream(FOLDER + resourceName + ".bsl"))
{
IFile file = getProject().getWorkspaceProject().getFile(COMMON_MODULE_FILE_NAME);
file.setContents(in, true, true, new NullProgressMonitor());