Explorar o código

Implement warning for "duplicate" names that only differ in case (#1879)

* Add warning for case insensitive duplicate names

* Add test for duplicate names (/case insensitive too)

* Soften warning preconditions: require same EClass instead of common superclass
Rene Beckmann %!s(int64=7) %!d(string=hai) anos
pai
achega
3c77cd72ba

+ 26 - 2
plugins/org.yakindu.sct.model.stext/src/org/yakindu/sct/model/stext/validation/STextNamesAreUniqueValidationHelper.java

@@ -35,6 +35,7 @@ public class STextNamesAreUniqueValidationHelper extends NamesAreUniqueValidatio
 	protected OperationCanceledManager operationCanceledManager = new OperationCanceledManager();
 
 	protected Map<QualifiedName, IEObjectDescription> nameMap;
+	protected Map<QualifiedName, IEObjectDescription> caseInsensitiveMap;
 	
 	@Override
 	public void checkUniqueNames(Iterable<IEObjectDescription> descriptions, ValidationMessageAcceptor acceptor) {
@@ -53,6 +54,7 @@ public class STextNamesAreUniqueValidationHelper extends NamesAreUniqueValidatio
 			ValidationMessageAcceptor acceptor) {
 		Iterator<IEObjectDescription> iter = descriptions.iterator();
 		this.nameMap = new HashMap<>();
+		this.caseInsensitiveMap = new HashMap<>();
 		if (!iter.hasNext())
 			return;
 		while (iter.hasNext()) {
@@ -66,17 +68,39 @@ public class STextNamesAreUniqueValidationHelper extends NamesAreUniqueValidatio
 			ValidationMessageAcceptor acceptor) {
 		QualifiedName qName = description.getName();
 		IEObjectDescription put = nameMap.put(qName, description);
+		IEObjectDescription lowerCasePut = caseInsensitiveMap.put(qName.toLowerCase(), description);
 		if(put != null) {
-			EClass common = checkForCommonSuperClass(put, description, acceptor);
+			EClass common = checkForCommonSuperClass(put, description);
 			if(common != null) {
 				createDuplicateNameError(description, common, acceptor);
 				createDuplicateNameError(put, common, acceptor);
 			}
+		} else if(lowerCasePut != null) {
+			if(lowerCasePut.getEClass().equals(description.getEClass())) {
+				createDuplicateNameWarning(description, description.getEClass(), acceptor);
+				createDuplicateNameWarning(lowerCasePut, description.getEClass(), acceptor);
+			}
 		}
 	}
 
-	protected EClass checkForCommonSuperClass(IEObjectDescription one, IEObjectDescription two,
+	protected void createDuplicateNameWarning(IEObjectDescription description, EClass eClass,
 			ValidationMessageAcceptor acceptor) {
+		EObject object = description.getEObjectOrProxy();
+		EStructuralFeature feature = getNameFeature(object);
+		acceptor.acceptWarning(
+				getDuplicateNameWarningMessage(description, eClass, feature), 
+				object, 
+				feature,
+				ValidationMessageAcceptor.INSIGNIFICANT_INDEX,
+				getErrorCode());
+	}
+
+	protected String getDuplicateNameWarningMessage(IEObjectDescription description, EClass eClass,
+			EStructuralFeature feature) {
+		return getDuplicateNameErrorMessage(description, eClass, feature) + ". Names differ only in case, which can lead to compilation problems.";
+	}
+
+	protected EClass checkForCommonSuperClass(IEObjectDescription one, IEObjectDescription two) {
 		
 		
 		List<EClass> flatOne = buildSuperClassList(one.getEClass());

+ 67 - 2
test-plugins/org.yakindu.sct.model.stext.test/src/org/yakindu/sct/model/stext/test/validation/STextJavaValidatorTest.java

@@ -13,6 +13,7 @@ package org.yakindu.sct.model.stext.test.validation;
 
 import static org.eclipse.xtext.junit4.validation.AssertableDiagnostics.errorCode;
 import static org.eclipse.xtext.junit4.validation.AssertableDiagnostics.errorMsg;
+import static org.eclipse.xtext.junit4.validation.AssertableDiagnostics.warningMsg;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 import static org.yakindu.base.expressions.validation.ExpressionsJavaValidator.ERROR_ASSIGNMENT_TO_CONST_MSG;
@@ -22,8 +23,6 @@ import static org.yakindu.base.expressions.validation.ExpressionsJavaValidator.E
 import static org.yakindu.base.expressions.validation.ExpressionsJavaValidator.ERROR_WRONG_NUMBER_OF_ARGUMENTS_CODE;
 import static org.yakindu.sct.test.models.AbstractTestModelsUtil.VALIDATION_TESTMODEL_DIR;
 
-import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
-
 import java.util.HashMap;
 import java.util.Iterator;
 
@@ -38,6 +37,7 @@ import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.yakindu.base.expressions.expressions.Expression;
 import org.yakindu.base.types.Operation;
+import org.yakindu.base.types.inferrer.ITypeSystemInferrer;
 import org.yakindu.base.types.typesystem.ITypeSystem;
 import org.yakindu.sct.model.sgraph.Entry;
 import org.yakindu.sct.model.sgraph.Exit;
@@ -931,5 +931,70 @@ public class STextJavaValidatorTest extends AbstractSTextValidationTest implemen
 		result.assertError(ITypeSystemInferrer.NOT_COMPATIBLE_CODE);
 	}
 	
+	@Test
+	public void testDuplicateNames() {
+		EObject model;
+		AssertableDiagnostics result;
+		String scope;
+		
+		
+		scope = "interface: var x: integer var x: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertAll(errorMsg("Duplicate"), errorMsg("Duplicate"));
+		
+		
+		scope = "interface: var x: integer internal: var x: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertAll(errorMsg("Duplicate"), errorMsg("Duplicate"));
+		
+		
+		scope = "interface: var x: integer interface d: var x: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertOK();
+		
+		
+		scope = "interface: var x: integer interface x: var d: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertAll(errorMsg("Duplicate"), errorMsg("Duplicate"));
+		
+		
+		scope = "interface: var x: integer var X: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertAll(warningMsg("Duplicate"), warningMsg("Duplicate"));
+		
+		scope = "interface: var x: integer interface X: var d: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertOK();
+		
+		
+		scope = "interface: " + 
+				"var X: integer " + 
+				"var x: integer " + 
+				"" + 
+				"var d: integer " + 
+				" " + 
+				"interface D: " + 
+				"var x: integer " + 
+				" " + 
+				"interface x: " + 
+				"var i: integer";
+		model = super.parseExpression(scope, StatechartSpecification.class.getSimpleName());
+		
+		result = tester.validate(model);
+		result.assertAll(warningMsg("Duplicate"), warningMsg("Duplicate"), errorMsg("Duplicate"), errorMsg("Duplicate"));
+	}
+	
 
 }