From e6d0b4a23b16aced207160a9172dc4651b55a6c0 Mon Sep 17 00:00:00 2001
From: overflowerror <mail@overflowerror.com>
Date: Sun, 20 Feb 2022 18:44:39 +0100
Subject: [PATCH] changed context analysis to a blacklist model

instead of just ignoring the path when there is no progress in an
endless recursion. Now we can reset the considerCardinalities flag after
using it.
---
 .../hoisting/pathAnalysis/TokenAnalysis.java  | 52 ++++++++++++-------
 .../pathAnalysis/TokenAnalysisPaths.java      |  8 +++
 2 files changed, 42 insertions(+), 18 deletions(-)

diff --git a/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysis.java b/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysis.java
index 89dc064fe..ad184b015 100644
--- a/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysis.java
+++ b/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysis.java
@@ -173,10 +173,16 @@ public class TokenAnalysis {
 	}
 	
 	private TokenAnalysisPaths getTokenPathsContext(AbstractElement last, TokenAnalysisPaths prefix) {
-		return getTokenPathsContext(last, prefix, true, new HashSet<>());
+		return getTokenPathsContext(last, prefix, true, new HashSet<>(), new HashSet<>());
 	}
 	
-	private TokenAnalysisPaths getTokenPathsContext(AbstractElement last, TokenAnalysisPaths prefix, boolean considerCardinalities, Set<AbstractElement> visitedElements) {
+	private TokenAnalysisPaths getTokenPathsContext(
+			AbstractElement last, 
+			TokenAnalysisPaths prefix, 
+			boolean considerCardinalities, 
+			Set<AbstractElement> visitedElements,
+			Set<AbstractElement> blacklistedElements
+	) {
 		if (config.isDebug())
 			log.info("get context for: " + abstractElementToShortString(last) + 
 				(considerCardinalities ? " with" : " without") + " cardinalities");
@@ -192,27 +198,35 @@ public class TokenAnalysis {
 		for (AbstractElement element : context) {
 			if (config.isDebug())
 				log.info("context element: " + abstractElementToShortString(element));
+			
+			if (blacklistedElements.contains(element)) {
+				if (config.isDebug())
+					log.info("blacklisted element");
+				
+				continue;
+			}
+			
 			TokenAnalysisPaths path = new TokenAnalysisPaths(prefix);
 			path.resetProgress();
-			
-			// shortcut endless loops instead of throwing exception
 			path = getTokenPaths(element, path, false, false);
 			
 			if (!path.isDone() && element != null) {
-				boolean _considerCardinalities = considerCardinalities;
-				if (visitedElements.contains(element) && !path.hasProgress()) {
-					if (_considerCardinalities) {
-						_considerCardinalities = false;
-					} else {
-						// considerCardinalities is already false
-						log.info("failed to analyse cardinalities in context");
-						// ignore this branch
-						continue;
-					}
-				}
+				boolean _considerCardinalities = true;
+				
 				Set<AbstractElement> localVisitedElements = new HashSet<>(visitedElements);
-				localVisitedElements.add(element);
-				path = getTokenPathsContext(element, path, _considerCardinalities, localVisitedElements);
+				Set<AbstractElement> localBlacklst = new HashSet<>(blacklistedElements);
+				
+				if (visitedElements.contains(element) && !path.hasCandidates()) {
+					_considerCardinalities = false;
+					if (!considerCardinalities) {
+						log.warn("context analysis: analyzing cardinalities failed: blacklisting element");
+						localBlacklst.add(element);
+					}
+				} else {
+					localVisitedElements.add(element);
+				}
+				
+				path = getTokenPathsContext(element, path, _considerCardinalities, localVisitedElements, localBlacklst);
 			}
 			if (path.isDone()) {
 				result = result.merge(path);
@@ -576,12 +590,14 @@ public class TokenAnalysis {
 		}
 		
 		tokenCombinations(indexList -> {
+			if (config.isDebug())
+				log.info(indexList);
 			
 			// will throw TokenAnalysisAborted if any path is too short
 			List<List<List<Token>>> tokenListsForPaths = paths.stream()
 					.peek(p -> {
 						if (config.isDebug())
-							log.info("next path: " + p);
+							log.info("next path: " + abstractElementToShortString(p));
 					})
 					.map(p -> getTokenPathsContext(p, indexList).getTokenPaths())
 					.collect(Collectors.toList());
diff --git a/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysisPaths.java b/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysisPaths.java
index 58bb83d2c..35171007a 100644
--- a/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysisPaths.java
+++ b/org.eclipse.xtext.xtext.generator/src/org/eclipse/xtext/xtext/generator/parser/antlr/hoisting/pathAnalysis/TokenAnalysisPaths.java
@@ -22,6 +22,7 @@ public class TokenAnalysisPaths {
 	private List<TokenAnalysisPath> tokenPaths = new ArrayList<>(10);
 	private boolean isEmpty = false;
 	private boolean hasProgress = false;
+	private boolean hasCandidates = false;
 	
 	public TokenAnalysisPaths(List<Integer> indexes) {
 		tokenPaths.add(new TokenAnalysisPath(indexes));
@@ -49,11 +50,17 @@ public class TokenAnalysisPaths {
 		return hasProgress;
 	}
 	
+	public boolean hasCandidates() {
+		return hasCandidates;
+	}
+	
 	public void resetProgress() {
 		hasProgress = false;
+		hasCandidates = false;
 	}
 	
 	public void add(AbstractElement element) {
+		hasCandidates = true;
 		tokenPaths.forEach(p -> hasProgress = p.add(element) || hasProgress);
 	}
 	
@@ -78,6 +85,7 @@ public class TokenAnalysisPaths {
 			if (addAllDistinct(other)) {
 				this.hasProgress |= other.hasProgress;
 			}
+			this.hasCandidates |= other.hasCandidates;
 			return this;
 		}
 	}