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 494763fbc..4ac3068e7 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
@@ -196,7 +196,7 @@ public class TokenAnalysis {
 			path.resetProgress();
 			
 			// shortcut endless loops instead of throwing exception
-			path = getTokenPaths(element, path, false, false, true);
+			path = getTokenPaths(element, path, false, false);
 			
 			if (!path.isDone() && element != null) {
 				boolean _considerCardinalities = considerCardinalities;
@@ -238,11 +238,11 @@ public class TokenAnalysis {
 		return result;
 	}
 	
-	private TokenAnalysisPaths getTokenPathsTrivial(Group path, TokenAnalysisPaths prefix, boolean shortcutEndlessLoops) {
+	private TokenAnalysisPaths getTokenPathsTrivial(Group path, TokenAnalysisPaths prefix) {
 		TokenAnalysisPaths result = new TokenAnalysisPaths(prefix);
 		
 		for(AbstractElement element : path.getElements()) {
-			result = getTokenPaths(element, result, false, false, shortcutEndlessLoops);
+			result = getTokenPaths(element, result, false, false);
 			if (result.isDone()) {
 				break;
 			}
@@ -250,16 +250,16 @@ public class TokenAnalysis {
 		
 		return result;
 	}
-	private TokenAnalysisPaths getTokenPathsTrivial(Alternatives path, TokenAnalysisPaths prefix, boolean shortcutEndlessLoops) {
+	private TokenAnalysisPaths getTokenPathsTrivial(Alternatives path, TokenAnalysisPaths prefix) {
 		TokenAnalysisPaths result = TokenAnalysisPaths.empty(prefix);
 		
 		for(AbstractElement element : path.getElements()) {
-			result = result.merge(getTokenPaths(element, prefix, false, false, shortcutEndlessLoops));
+			result = result.merge(getTokenPaths(element, prefix, false, false));
 		}
 		
 		return result;
 	}
-	private TokenAnalysisPaths getTokenPathsTrivial(UnorderedGroup path, TokenAnalysisPaths prefix, boolean shortcutEndlessLoops) {		
+	private TokenAnalysisPaths getTokenPathsTrivial(UnorderedGroup path, TokenAnalysisPaths prefix) {		
 		TokenAnalysisPaths result;
 		TokenAnalysisPaths current;
 		
@@ -272,53 +272,36 @@ public class TokenAnalysis {
 		do {
 			current = TokenAnalysisPaths.empty(result);
 			current.resetProgress();
-			int currentPosition = current.getMinPosition();
 			
 			for (AbstractElement element : path.getElements()) {
-				current = current.merge(getTokenPaths(element, result, false, false, shortcutEndlessLoops));
+				current = current.merge(getTokenPaths(element, result, false, false));
 			}
 			
 			result.resetProgress();
 			result = result.merge(current);
 			
-			if (current.getMinPosition() == currentPosition) {
-				// endless loop
-				// current will never be done since there is no progress to the shortest path
-				if (shortcutEndlessLoops) {
-					if (!result.hasProgress()) {
-						// no progress
-						// abort endless loop
-						break;
-					} else {
-						// there is still some progress done
-						continue;
-					}
-				} else {
-					throw new TokenAnalysisAbortedException("no progress in loop");
-				}
-			}
-		} while(!current.isDone());
+		} while(result.hasProgress());
 		
 		return result;
 	}
 	
-	private TokenAnalysisPaths getTokenPathsTrivial(AbstractElement path, TokenAnalysisPaths prefix, boolean shortcutEndlessLoops) {
+	private TokenAnalysisPaths getTokenPathsTrivial(AbstractElement path, TokenAnalysisPaths prefix) {
 		return new XtextSwitch<TokenAnalysisPaths>() {
 			@Override
 			public TokenAnalysisPaths caseGroup(Group group) {
-				return getTokenPathsTrivial(group, prefix, shortcutEndlessLoops);
+				return getTokenPathsTrivial(group, prefix);
 			};
 			@Override
 			public TokenAnalysisPaths caseAlternatives(Alternatives alternatives) {
-				return getTokenPathsTrivial(alternatives, prefix, shortcutEndlessLoops);
+				return getTokenPathsTrivial(alternatives, prefix);
 			};
 			@Override
 			public TokenAnalysisPaths caseUnorderedGroup(UnorderedGroup unorderedGroup) {
-				return getTokenPathsTrivial(unorderedGroup, prefix, shortcutEndlessLoops);
+				return getTokenPathsTrivial(unorderedGroup, prefix);
 			};
 			@Override
 			public TokenAnalysisPaths caseAssignment(Assignment assignment) {
-				return getTokenPaths(assignment.getTerminal(), prefix, false, false, shortcutEndlessLoops);
+				return getTokenPaths(assignment.getTerminal(), prefix, false, false);
 			};
 			@Override
 			public TokenAnalysisPaths caseRuleCall(RuleCall call) {
@@ -326,7 +309,7 @@ public class TokenAnalysis {
 					isParserRuleCall(call) || 
 					isEnumRuleCall(call)
 				) {
-					return getTokenPaths(call.getRule().getAlternatives(), prefix, false, false, shortcutEndlessLoops);
+					return getTokenPaths(call.getRule().getAlternatives(), prefix, false, false);
 				} else {
 					// probably terminal rule call -> go to default case
 					return null;
@@ -358,7 +341,7 @@ public class TokenAnalysis {
 	}
 	
 	private TokenAnalysisPaths getTokenPaths(
-			AbstractElement path, TokenAnalysisPaths prefix, boolean analyseContext, boolean needsLength, boolean shortcutEndlessLoops
+			AbstractElement path, TokenAnalysisPaths prefix, boolean analyseContext, boolean needsLength
 	) {
 		if (prefix.isDone()) {
 			return prefix;
@@ -373,11 +356,11 @@ public class TokenAnalysis {
 		}
 		
 		// use actual cardinality from path
-		return getTokenPaths(path, path.getCardinality(), prefix, analyseContext, needsLength, shortcutEndlessLoops);
+		return getTokenPaths(path, path.getCardinality(), prefix, analyseContext, needsLength);
 	}
 	
 	private TokenAnalysisPaths getTokenPaths(
-			AbstractElement path, String cardinality, TokenAnalysisPaths prefix, boolean analyseContext, boolean needsLength, boolean shortcutEndlessLoops
+			AbstractElement path, String cardinality, TokenAnalysisPaths prefix, boolean analyseContext, boolean needsLength
 	) {
 		// analyseContext implies needsLength
 		
@@ -409,10 +392,9 @@ public class TokenAnalysis {
 		boolean loop = isVirtualMultipleCardinality(cardinality);
 		
 		do {
-			int currentMinPosition = result.getMinPosition();
 			result.resetProgress();
 			
-			TokenAnalysisPaths tokenPaths = getTokenPathsTrivial(path, result, shortcutEndlessLoops);
+			TokenAnalysisPaths tokenPaths = getTokenPathsTrivial(path, result);
 			
 			if (tokenPaths.isDone()) {
 				result = result.merge(tokenPaths);
@@ -426,45 +408,21 @@ public class TokenAnalysis {
 				result = result.merge(tokenPaths);
 			}
 			
-			if (loop) {
-				if (result.getMinPosition() == currentMinPosition) {
-					// endless loop
-					// result will never be done since there is no progress to the shortest path
-					if (shortcutEndlessLoops) {
-						if (!result.hasProgress()) {
-							// no progress
-							// abort endless loop
-							break;
-						} else {
-							// there is still some progress done
-							continue;
-						}
-					} else {
-						throw new TokenAnalysisAbortedException("no progress in loop");
-					}
-				} else {
-					currentMinPosition = result.getMinPosition();
-				}
-			}
-		} while (loop);
+		} while (loop && result.hasProgress());
 		
 		return result;
 	}
 	
-	private TokenAnalysisPaths getTokenPaths(AbstractElement path, List<Integer> indexes, boolean analyseContext, boolean needsLength, boolean shortcutEndlessLoops) {
-		return getTokenPaths(path, new TokenAnalysisPaths(indexes), analyseContext, needsLength, shortcutEndlessLoops);
-	}
-	
 	private TokenAnalysisPaths getTokenPathsNoLength(AbstractElement path, List<Integer> indexes) {
-		return getTokenPaths(path, new TokenAnalysisPaths(indexes), false, false, false);
+		return getTokenPaths(path, new TokenAnalysisPaths(indexes), false, false);
 	}
 	
 	private TokenAnalysisPaths getTokenPathsContext(AbstractElement path, List<Integer> indexes) {
-		return getTokenPaths(path, new TokenAnalysisPaths(indexes), true, true, false);
+		return getTokenPaths(path, new TokenAnalysisPaths(indexes), true, true);
 	}
 	
-	private TokenAnalysisPaths getTokenPaths(AbstractElement path, String virtualCardinality, List<Integer> indexes, boolean analyseContext, boolean needsLength, boolean shortcutEndlessLoops) {
-		return getTokenPaths(path, virtualCardinality, new TokenAnalysisPaths(indexes), analyseContext, needsLength, shortcutEndlessLoops);
+	private TokenAnalysisPaths getTokenPaths(AbstractElement path, String virtualCardinality, List<Integer> indexes, boolean analyseContext, boolean needsLength) {
+		return getTokenPaths(path, virtualCardinality, new TokenAnalysisPaths(indexes), analyseContext, needsLength);
 	}
 	
 	private TokenAnalysisPaths getTokenPathsContextOnly(AbstractElement path, List<Integer> indexes) throws TokenAnalysisAbortedException {
@@ -583,7 +541,7 @@ public class TokenAnalysis {
 		tokenCombinations(indexList -> {
 			
 			// no context analysis // TODO why?
-			List<List<Token>> tokenListsForPath = getTokenPaths(element, virtualCardinality, indexList, false, true, false)
+			List<List<Token>> tokenListsForPath = getTokenPaths(element, virtualCardinality, indexList, false, true)
 				.getTokenPaths();
 			List<List<Token>> tokenListForContext = getTokenPathsContextOnly(element, indexList)
 				.getTokenPaths();
@@ -658,7 +616,7 @@ public class TokenAnalysis {
 	
 	public List<List<AbstractElement>> getAllPossiblePaths(AbstractElement path) {
 		// token limit + 2 so identity analysis will recognize paths that are identical up to the token limit on the flattened tree
-		return getTokenPaths(path, range(0, config.getTokenLimit() + 2), false, false, true)
+		return getTokenPathsNoLength(path, range(0, config.getTokenLimit() + 2))
 				.getTokenPaths()
 				.stream()
 				.map(l -> l.stream()
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 c49fbb849..6c39062b0 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
@@ -10,6 +10,7 @@ package org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.pathAnalysis;
 
 import java.util.LinkedHashSet;
 import java.util.List;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.eclipse.xtext.AbstractElement;
@@ -20,7 +21,7 @@ import org.eclipse.xtext.xtext.generator.parser.antlr.hoisting.utils.StreamUtils
  * @author overflow - Initial contribution and APILinkedHashSet
  */
 public class TokenAnalysisPaths {
-	private LinkedHashSet<TokenAnalysisPath> tokenPaths = new LinkedHashSet<>();
+	private Set<TokenAnalysisPath> tokenPaths = new LinkedHashSet<>();
 	private boolean isEmpty = false;
 	private boolean hasProgress = false;
 	
@@ -60,7 +61,9 @@ public class TokenAnalysisPaths {
 	
 	public TokenAnalysisPaths merge(TokenAnalysisPaths other) {
 		if (isEmpty) {
-			return other;
+			return other.clone();
+		} else if (other.isEmpty) {
+			return this.clone();
 		} else {
 			// set hasProgress if other has progress and progress is merged
 			if (this.tokenPaths.addAll(other.tokenPaths)) {
@@ -98,4 +101,11 @@ public class TokenAnalysisPaths {
 					+ "\n)";
 		}
 	}
+	
+	@Override
+	public TokenAnalysisPaths clone() {
+		TokenAnalysisPaths clone = new TokenAnalysisPaths(this);
+		clone.isEmpty = this.isEmpty;
+		return clone;
+	}
 }