diff options
author | Kristóf Marussy <kristof@marussy.com> | 2024-06-17 01:33:53 +0200 |
---|---|---|
committer | Kristóf Marussy <kristof@marussy.com> | 2024-06-17 01:33:53 +0200 |
commit | 9c6eb852e9277537bf1a771bb337eb5e23f4f81c (patch) | |
tree | d162aa24f92953f792d5e4f5437cf8303510ebb5 | |
parent | refactor(language): allow rules without preconditions (diff) | |
download | refinery-9c6eb852e9277537bf1a771bb337eb5e23f4f81c.tar.gz refinery-9c6eb852e9277537bf1a771bb337eb5e23f4f81c.tar.zst refinery-9c6eb852e9277537bf1a771bb337eb5e23f4f81c.zip |
feat(language): warn about partial references
2 files changed, 88 insertions, 0 deletions
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ActionTargetCollector.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ActionTargetCollector.java new file mode 100644 index 00000000..67cd5777 --- /dev/null +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ActionTargetCollector.java | |||
@@ -0,0 +1,73 @@ | |||
1 | /* | ||
2 | * SPDX-FileCopyrightText: 2024 The Refinery Authors <https://refinery.tools/> | ||
3 | * | ||
4 | * SPDX-License-Identifier: EPL-2.0 | ||
5 | */ | ||
6 | package tools.refinery.language.validation; | ||
7 | |||
8 | import com.google.inject.Inject; | ||
9 | import com.google.inject.Singleton; | ||
10 | import org.eclipse.xtext.EcoreUtil2; | ||
11 | import org.eclipse.xtext.util.IResourceScopeCache; | ||
12 | import org.eclipse.xtext.util.Tuples; | ||
13 | import tools.refinery.language.model.problem.*; | ||
14 | |||
15 | import java.util.HashSet; | ||
16 | import java.util.Set; | ||
17 | |||
18 | @Singleton | ||
19 | public class ActionTargetCollector { | ||
20 | private static final String ACTION_TARGETS = | ||
21 | "tools.refinery.language.validation.ActionTargetCollector.ACTION_TARGETS"; | ||
22 | |||
23 | @Inject | ||
24 | private IResourceScopeCache cache = IResourceScopeCache.NullImpl.INSTANCE; | ||
25 | |||
26 | public boolean isActionTarget(Relation relation) { | ||
27 | var problem = EcoreUtil2.getContainerOfType(relation, Problem.class); | ||
28 | return problem != null && isActionTarget(problem, relation); | ||
29 | } | ||
30 | |||
31 | public boolean isActionTarget(Problem problem, Relation relation) { | ||
32 | var targets = getActionTargets(problem); | ||
33 | if (targets.contains(relation)) { | ||
34 | return true; | ||
35 | } | ||
36 | if (relation instanceof ReferenceDeclaration referenceDeclaration) { | ||
37 | var opposite = referenceDeclaration.getOpposite(); | ||
38 | return opposite != null && targets.contains(opposite); | ||
39 | } | ||
40 | return false; | ||
41 | } | ||
42 | |||
43 | protected Set<Relation> getActionTargets(Problem problem) { | ||
44 | var resource = problem.eResource(); | ||
45 | if (resource == null) { | ||
46 | return doGetActionTargets(problem); | ||
47 | } | ||
48 | return cache.get(Tuples.create(problem, ACTION_TARGETS), resource, () -> doGetActionTargets(problem)); | ||
49 | } | ||
50 | |||
51 | protected Set<Relation> doGetActionTargets(Problem problem) { | ||
52 | var targets = new HashSet<Relation>(); | ||
53 | for (var statement : problem.getStatements()) { | ||
54 | if (statement instanceof RuleDefinition ruleDefinition) { | ||
55 | collectTargets(ruleDefinition, targets); | ||
56 | } | ||
57 | } | ||
58 | return targets; | ||
59 | } | ||
60 | |||
61 | private static void collectTargets(RuleDefinition ruleDefinition, HashSet<Relation> targets) { | ||
62 | for (var consequent : ruleDefinition.getConsequents()) { | ||
63 | for (var action : consequent.getActions()) { | ||
64 | if (action instanceof AssertionAction assertionAction) { | ||
65 | var target = assertionAction.getRelation(); | ||
66 | if (target != null) { | ||
67 | targets.add(target); | ||
68 | } | ||
69 | } | ||
70 | } | ||
71 | } | ||
72 | } | ||
73 | } | ||
diff --git a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java index 36a77ac7..84218f17 100644 --- a/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java +++ b/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java | |||
@@ -54,6 +54,7 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
54 | public static final String INVALID_ASSIGNMENT_ISSUE = ISSUE_PREFIX + "INVALID_ASSIGNMENT"; | 54 | public static final String INVALID_ASSIGNMENT_ISSUE = ISSUE_PREFIX + "INVALID_ASSIGNMENT"; |
55 | public static final String TYPE_ERROR = ISSUE_PREFIX + "TYPE_ERROR"; | 55 | public static final String TYPE_ERROR = ISSUE_PREFIX + "TYPE_ERROR"; |
56 | public static final String VARIABLE_WITHOUT_EXISTS = ISSUE_PREFIX + "VARIABLE_WITHOUT_EXISTS"; | 56 | public static final String VARIABLE_WITHOUT_EXISTS = ISSUE_PREFIX + "VARIABLE_WITHOUT_EXISTS"; |
57 | public static final String UNUSED_PARTIAL_RELATION = ISSUE_PREFIX + "UNUSED_PARTIAL_RELATION"; | ||
57 | 58 | ||
58 | @Inject | 59 | @Inject |
59 | private ReferenceCounter referenceCounter; | 60 | private ReferenceCounter referenceCounter; |
@@ -62,6 +63,9 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
62 | private ExistsVariableCollector existsVariableCollector; | 63 | private ExistsVariableCollector existsVariableCollector; |
63 | 64 | ||
64 | @Inject | 65 | @Inject |
66 | private ActionTargetCollector actionTargetCollector; | ||
67 | |||
68 | @Inject | ||
65 | private IQualifiedNameConverter qualifiedNameConverter; | 69 | private IQualifiedNameConverter qualifiedNameConverter; |
66 | 70 | ||
67 | @Inject | 71 | @Inject |
@@ -336,6 +340,17 @@ public class ProblemValidator extends AbstractProblemValidator { | |||
336 | } | 340 | } |
337 | 341 | ||
338 | @Check | 342 | @Check |
343 | public void checkPartialReference(ReferenceDeclaration referenceDeclaration) { | ||
344 | if (referenceDeclaration.getKind() == ReferenceKind.PARTIAL && | ||
345 | !actionTargetCollector.isActionTarget(referenceDeclaration)) { | ||
346 | var message = "Add decision or propagation rules to refine partial relation '%s'." | ||
347 | .formatted(referenceDeclaration.getName()); | ||
348 | acceptWarning(message, referenceDeclaration, ProblemPackage.Literals.REFERENCE_DECLARATION__KIND, 0, | ||
349 | UNUSED_PARTIAL_RELATION); | ||
350 | } | ||
351 | } | ||
352 | |||
353 | @Check | ||
339 | public void checkSupertypes(ClassDeclaration classDeclaration) { | 354 | public void checkSupertypes(ClassDeclaration classDeclaration) { |
340 | var supertypes = classDeclaration.getSuperTypes(); | 355 | var supertypes = classDeclaration.getSuperTypes(); |
341 | int supertypeCount = supertypes.size(); | 356 | int supertypeCount = supertypes.size(); |