aboutsummaryrefslogtreecommitdiffstats
path: root/subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java
diff options
context:
space:
mode:
Diffstat (limited to 'subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java')
-rw-r--r--subprojects/language/src/main/java/tools/refinery/language/validation/ProblemValidator.java125
1 files changed, 107 insertions, 18 deletions
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 8bda4b95..d9eb5fd3 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
@@ -1,5 +1,5 @@
1/* 1/*
2 * SPDX-FileCopyrightText: 2021-2023 The Refinery Authors <https://refinery.tools/> 2 * SPDX-FileCopyrightText: 2021-2024 The Refinery Authors <https://refinery.tools/>
3 * 3 *
4 * SPDX-License-Identifier: EPL-2.0 4 * SPDX-License-Identifier: EPL-2.0
5 */ 5 */
@@ -13,16 +13,16 @@ import com.google.inject.Inject;
13import org.eclipse.emf.ecore.EObject; 13import org.eclipse.emf.ecore.EObject;
14import org.eclipse.emf.ecore.EReference; 14import org.eclipse.emf.ecore.EReference;
15import org.eclipse.xtext.EcoreUtil2; 15import org.eclipse.xtext.EcoreUtil2;
16import org.eclipse.xtext.naming.IQualifiedNameConverter;
16import org.eclipse.xtext.validation.Check; 17import org.eclipse.xtext.validation.Check;
17import org.jetbrains.annotations.Nullable; 18import org.jetbrains.annotations.Nullable;
18import tools.refinery.language.model.problem.*; 19import tools.refinery.language.model.problem.*;
20import tools.refinery.language.naming.NamingUtil;
21import tools.refinery.language.scoping.imports.ImportAdapter;
19import tools.refinery.language.utils.ProblemDesugarer; 22import tools.refinery.language.utils.ProblemDesugarer;
20import tools.refinery.language.utils.ProblemUtil; 23import tools.refinery.language.utils.ProblemUtil;
21 24
22import java.util.ArrayList; 25import java.util.*;
23import java.util.LinkedHashMap;
24import java.util.LinkedHashSet;
25import java.util.Set;
26 26
27/** 27/**
28 * This class contains custom validation rules. 28 * This class contains custom validation rules.
@@ -33,6 +33,10 @@ import java.util.Set;
33public class ProblemValidator extends AbstractProblemValidator { 33public class ProblemValidator extends AbstractProblemValidator {
34 private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator."; 34 private static final String ISSUE_PREFIX = "tools.refinery.language.validation.ProblemValidator.";
35 35
36 public static final String UNEXPECTED_MODULE_NAME_ISSUE = ISSUE_PREFIX + "UNEXPECTED_MODULE_NAME";
37
38 public static final String INVALID_IMPORT_ISSUE = ISSUE_PREFIX + "INVALID_IMPORT";
39
36 public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE"; 40 public static final String SINGLETON_VARIABLE_ISSUE = ISSUE_PREFIX + "SINGLETON_VARIABLE";
37 41
38 public static final String NODE_CONSTANT_ISSUE = ISSUE_PREFIX + "NODE_CONSTANT_ISSUE"; 42 public static final String NODE_CONSTANT_ISSUE = ISSUE_PREFIX + "NODE_CONSTANT_ISSUE";
@@ -65,6 +69,61 @@ public class ProblemValidator extends AbstractProblemValidator {
65 @Inject 69 @Inject
66 private ProblemDesugarer desugarer; 70 private ProblemDesugarer desugarer;
67 71
72 @Inject
73 private IQualifiedNameConverter qualifiedNameConverter;
74
75 @Check
76 public void checkModuleName(Problem problem) {
77 var nameString = problem.getName();
78 if (nameString == null) {
79 return;
80 }
81 var resource = problem.eResource();
82 if (resource == null) {
83 return;
84 }
85 var resourceSet = resource.getResourceSet();
86 if (resourceSet == null) {
87 return;
88 }
89 var adapter = ImportAdapter.getOrInstall(resourceSet);
90 var expectedName = adapter.getQualifiedName(resource.getURI());
91 if (expectedName == null) {
92 return;
93 }
94 var name = NamingUtil.stripRootPrefix(qualifiedNameConverter.toQualifiedName(nameString));
95 if (!expectedName.equals(name)) {
96 var moduleKindName = switch (problem.getKind()) {
97 case PROBLEM -> "problem";
98 case MODULE -> "module";
99 };
100 var message = "Expected %s to have name '%s', got '%s' instead.".formatted(
101 moduleKindName, qualifiedNameConverter.toString(expectedName),
102 qualifiedNameConverter.toString(name));
103 error(message, problem, ProblemPackage.Literals.NAMED_ELEMENT__NAME, INSIGNIFICANT_INDEX,
104 UNEXPECTED_MODULE_NAME_ISSUE);
105 }
106 }
107
108 @Check
109 public void checkImportStatement(ImportStatement importStatement) {
110 var importedModule = importStatement.getImportedModule();
111 if (importedModule == null || importedModule.eIsProxy()) {
112 return;
113 }
114 String message = null;
115 var problem = EcoreUtil2.getContainerOfType(importStatement, Problem.class);
116 if (importedModule == problem) {
117 message = "A module cannot import itself.";
118 } else if (importedModule.getKind() != ModuleKind.MODULE) {
119 message = "Only modules can be imported.";
120 }
121 if (message != null) {
122 error(message, importStatement, ProblemPackage.Literals.IMPORT_STATEMENT__IMPORTED_MODULE,
123 INSIGNIFICANT_INDEX, INVALID_IMPORT_ISSUE);
124 }
125 }
126
68 @Check 127 @Check
69 public void checkSingletonVariable(VariableOrNodeExpr expr) { 128 public void checkSingletonVariable(VariableOrNodeExpr expr) {
70 var variableOrNode = expr.getVariableOrNode(); 129 var variableOrNode = expr.getVariableOrNode();
@@ -84,10 +143,10 @@ public class ProblemValidator extends AbstractProblemValidator {
84 @Check 143 @Check
85 public void checkNodeConstants(VariableOrNodeExpr expr) { 144 public void checkNodeConstants(VariableOrNodeExpr expr) {
86 var variableOrNode = expr.getVariableOrNode(); 145 var variableOrNode = expr.getVariableOrNode();
87 if (variableOrNode instanceof Node node && !ProblemUtil.isIndividualNode(node)) { 146 if (variableOrNode instanceof Node node && !ProblemUtil.isAtomNode(node)) {
88 var name = node.getName(); 147 var name = node.getName();
89 var message = ("Only individuals can be referenced in predicates. " + 148 var message = ("Only atoms can be referenced in predicates. " +
90 "Mark '%s' as individual with the declaration 'indiv %s.'").formatted(name, name); 149 "Mark '%s' as an atom with the declaration 'atom %s.'").formatted(name, name);
91 error(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE, 150 error(message, expr, ProblemPackage.Literals.VARIABLE_OR_NODE_EXPR__VARIABLE_OR_NODE,
92 INSIGNIFICANT_INDEX, NODE_CONSTANT_ISSUE); 151 INSIGNIFICANT_INDEX, NODE_CONSTANT_ISSUE);
93 } 152 }
@@ -96,16 +155,16 @@ public class ProblemValidator extends AbstractProblemValidator {
96 @Check 155 @Check
97 public void checkUniqueDeclarations(Problem problem) { 156 public void checkUniqueDeclarations(Problem problem) {
98 var relations = new ArrayList<Relation>(); 157 var relations = new ArrayList<Relation>();
99 var individuals = new ArrayList<Node>(); 158 var nodes = new ArrayList<Node>();
100 for (var statement : problem.getStatements()) { 159 for (var statement : problem.getStatements()) {
101 if (statement instanceof Relation relation) { 160 if (statement instanceof Relation relation) {
102 relations.add(relation); 161 relations.add(relation);
103 } else if (statement instanceof IndividualDeclaration individualDeclaration) { 162 } else if (statement instanceof NodeDeclaration nodeDeclaration) {
104 individuals.addAll(individualDeclaration.getNodes()); 163 nodes.addAll(nodeDeclaration.getNodes());
105 } 164 }
106 } 165 }
107 checkUniqueSimpleNames(relations); 166 checkUniqueSimpleNames(relations);
108 checkUniqueSimpleNames(individuals); 167 checkUniqueSimpleNames(nodes);
109 } 168 }
110 169
111 @Check 170 @Check
@@ -302,6 +361,10 @@ public class ProblemValidator extends AbstractProblemValidator {
302 @Check 361 @Check
303 public void checkTypeScope(TypeScope typeScope) { 362 public void checkTypeScope(TypeScope typeScope) {
304 checkArity(typeScope, ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE, 1); 363 checkArity(typeScope, ProblemPackage.Literals.TYPE_SCOPE__TARGET_TYPE, 1);
364 if (typeScope.isIncrement() && ProblemUtil.isInModule(typeScope)) {
365 acceptError("Incremental type scopes are not supported in modules", typeScope, null, 0,
366 INVALID_MULTIPLICITY_ISSUE);
367 }
305 } 368 }
306 369
307 private void checkArity(EObject eObject, EReference reference, int expectedArity) { 370 private void checkArity(EObject eObject, EReference reference, int expectedArity) {
@@ -351,9 +414,9 @@ public class ProblemValidator extends AbstractProblemValidator {
351 } 414 }
352 415
353 private void checkExistsAssertion(Assertion assertion, LogicValue value) { 416 private void checkExistsAssertion(Assertion assertion, LogicValue value) {
354 if (value == LogicValue.TRUE || value == LogicValue.UNKNOWN) { 417 if (value == LogicValue.UNKNOWN) {
355 // {@code true} is always a valid value for {@code exists}, while {@code unknown} values may always be 418 // {@code unknown} values may always be refined to {@code true} of {@code false} if necessary (e.g., for
356 // refined to {@code true} if necessary (e.g., for individual nodes). 419 // atom nodes or removed multi-objects).
357 return; 420 return;
358 } 421 }
359 var arguments = assertion.getArguments(); 422 var arguments = assertion.getArguments();
@@ -361,9 +424,16 @@ public class ProblemValidator extends AbstractProblemValidator {
361 // We already report an error on invalid arity. 424 // We already report an error on invalid arity.
362 return; 425 return;
363 } 426 }
364 var node = getNodeArgumentForMultiObjectAssertion(arguments.get(0)); 427 var node = getNodeArgumentForMultiObjectAssertion(arguments.getFirst());
365 if (node != null && !node.eIsProxy() && ProblemUtil.isIndividualNode(node)) { 428 if (node == null || node.eIsProxy()) {
366 acceptError("Individual nodes must exist.", assertion, null, 0, UNSUPPORTED_ASSERTION_ISSUE); 429 return;
430 }
431 if (ProblemUtil.isAtomNode(node) && value != LogicValue.TRUE) {
432 acceptError("Atom nodes must exist.", assertion, null, 0, UNSUPPORTED_ASSERTION_ISSUE);
433 }
434 if (ProblemUtil.isMultiNode(node) && value != LogicValue.FALSE && ProblemUtil.isInModule(node)) {
435 acceptError("Multi-objects in modules cannot be required to exist.", assertion, null, 0,
436 UNSUPPORTED_ASSERTION_ISSUE);
367 } 437 }
368 } 438 }
369 439
@@ -403,4 +473,23 @@ public class ProblemValidator extends AbstractProblemValidator {
403 } 473 }
404 throw new IllegalArgumentException("Unknown assertion argument: " + argument); 474 throw new IllegalArgumentException("Unknown assertion argument: " + argument);
405 } 475 }
476
477 @Check
478 private void checkImplicitNodeInModule(Assertion assertion) {
479 if (!ProblemUtil.isInModule(assertion)) {
480 return;
481 }
482 for (var argument : assertion.getArguments()) {
483 if (argument instanceof NodeAssertionArgument nodeAssertionArgument) {
484 var node = nodeAssertionArgument.getNode();
485 if (node != null && !node.eIsProxy() && ProblemUtil.isImplicitNode(node)) {
486 var name = node.getName();
487 var message = ("Implicit nodes are not allowed in modules. Explicitly declare '%s' as a node " +
488 "with the declaration 'declare %s.'").formatted(name, name);
489 acceptError(message, nodeAssertionArgument, ProblemPackage.Literals.NODE_ASSERTION_ARGUMENT__NODE,
490 0, UNSUPPORTED_ASSERTION_ISSUE);
491 }
492 }
493 }
494 }
406} 495}