]> gerrit.simantics Code Review - simantics/platform.git/blobdiff - bundles/org.simantics.scl.compiler/src/org/simantics/scl/compiler/elaboration/chr/CHRRule.java
Merge "Warn for existential variables in head pattern referred only once"
[simantics/platform.git] / bundles / org.simantics.scl.compiler / src / org / simantics / scl / compiler / elaboration / chr / CHRRule.java
index 78224ebb0d75d876f0da2528a4ab8fba56a33129..e698d72c618ee79fca251e8689ae0267f057e5a4 100644 (file)
@@ -1,6 +1,7 @@
 package org.simantics.scl.compiler.elaboration.chr;
 
 import java.util.ArrayList;
+import java.util.HashMap;
 
 import org.simantics.scl.compiler.compilation.CompilationContext;
 import org.simantics.scl.compiler.elaboration.chr.plan.CHRSearchPlan;
@@ -9,9 +10,13 @@ import org.simantics.scl.compiler.elaboration.chr.relations.CHRConstraint;
 import org.simantics.scl.compiler.elaboration.contexts.SimplificationContext;
 import org.simantics.scl.compiler.elaboration.contexts.TranslationContext;
 import org.simantics.scl.compiler.elaboration.contexts.TypingContext;
+import org.simantics.scl.compiler.elaboration.expressions.EAsPattern;
 import org.simantics.scl.compiler.elaboration.expressions.EVariable;
+import org.simantics.scl.compiler.elaboration.expressions.Expression;
+import org.simantics.scl.compiler.elaboration.expressions.ExpressionVisitor;
 import org.simantics.scl.compiler.elaboration.expressions.Variable;
 import org.simantics.scl.compiler.elaboration.expressions.printing.ExpressionToStringVisitor;
+import org.simantics.scl.compiler.elaboration.expressions.visitors.StandardExpressionVisitor;
 import org.simantics.scl.compiler.errors.Locations;
 import org.simantics.scl.compiler.internal.parsing.Symbol;
 import org.simantics.scl.compiler.types.Types;
@@ -54,6 +59,52 @@ public class CHRRule extends Symbol {
         context.disallowNewExistentials();
         body.resolve(context);
         existentialVariables = context.popExistentialFrame();
+        
+        warnForExistentialsUsedOnlyOnce(context);
+    }
+
+    private static final Object NEVER_USED = new Object();
+    
+    private void warnForExistentialsUsedOnlyOnce(TranslationContext context) {
+        // Initialize the hash map
+        HashMap<Variable, Object> usageCount = new HashMap<>(existentialVariables.length);
+        for(Variable var : existentialVariables)
+            if(!var.getName().equals("_"))
+                usageCount.put(var, NEVER_USED);
+    
+        // Collect variable uses
+        ExpressionVisitor visitor = new StandardExpressionVisitor() {
+            private void handle(Expression expression, Variable variable) {
+                Object object = usageCount.remove(variable);
+                if(object == NEVER_USED)
+                    usageCount.put(variable, expression);
+            }
+            @Override
+            public void visit(EVariable expression) {
+                if(expression.variable != null)
+                    handle(expression, expression.variable);
+            }
+            @Override
+            public void visit(EAsPattern expression) {
+                expression.pattern.accept(this);
+                handle(expression, expression.var);
+            }
+        };
+        head.accept(visitor);
+        body.accept(visitor);
+        
+        // Report as warnings
+        usageCount.forEach((variable, expression_) -> {
+            if(!(expression_ instanceof Expression))
+                return; // Should never happen
+            Expression expression = (Expression)expression_;
+            if(context.isExpandedFromWildcard(expression))
+                return;
+            
+            context.getErrorLog().logWarning(expression.location,
+                    "Existential variable " + variable.getName() + " is referred only once. Replace by _ if this is a wildcard.");
+        });
+        
     }
 
     public void checkType(TypingContext context) {