Index: trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java
===================================================================
--- trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java	(revision 15573)
+++ trunk/src/org/openstreetmap/josm/actions/CombineWayAction.java	(revision 15574)
@@ -32,5 +32,10 @@
 import org.openstreetmap.josm.data.osm.TagCollection;
 import org.openstreetmap.josm.data.osm.Way;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
+import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.JoinedWay;
 import org.openstreetmap.josm.data.preferences.BooleanProperty;
+import org.openstreetmap.josm.data.validation.Test;
+import org.openstreetmap.josm.data.validation.tests.OverlappingWays;
+import org.openstreetmap.josm.data.validation.tests.SelfIntersectingWay;
 import org.openstreetmap.josm.gui.ExtendedDialog;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -123,7 +128,6 @@
 
         // try to build a new way which includes all the combined ways
-        NodeGraph graph = NodeGraph.createNearlyUndirectedGraphFromNodeWays(ways);
-        List<Node> path = graph.buildSpanningPathNoRemove();
-        if (path == null) {
+        List<Node> path = tryJoin(ways);
+        if (path.isEmpty()) {
             warnCombiningImpossible();
             return null;
@@ -137,12 +141,5 @@
         List<Way> reversedWays = new LinkedList<>();
         List<Way> unreversedWays = new LinkedList<>();
-        for (Way w: ways) {
-            // Treat zero or one-node ways as unreversed as Combine action action is a good way to fix them (see #8971)
-            if (w.getNodesCount() < 2 || (path.indexOf(w.getNode(0)) + 1) == path.lastIndexOf(w.getNode(1))) {
-                unreversedWays.add(w);
-            } else {
-                reversedWays.add(w);
-            }
-        }
+        detectReversedWays(ways, path, reversedWays, unreversedWays);
         // reverse path if all ways have been reversed
         if (unreversedWays.isEmpty()) {
@@ -164,5 +161,5 @@
             }
             // if there are still reversed ways with direction-dependent tags, reverse their tags
-            if (!reversedWays.isEmpty() && PROP_REVERSE_WAY.get()) {
+            if (!reversedWays.isEmpty() && Boolean.TRUE.equals(PROP_REVERSE_WAY.get())) {
                 List<Way> unreversedTagWays = new ArrayList<>(ways);
                 unreversedTagWays.removeAll(reversedWays);
@@ -211,4 +208,61 @@
 
         return new Pair<>(targetWay, sequenceCommand);
+    }
+
+    protected static void detectReversedWays(Collection<Way> ways, List<Node> path, List<Way> reversedWays,
+            List<Way> unreversedWays) {
+        for (Way w: ways) {
+            // Treat zero or one-node ways as unreversed as Combine action action is a good way to fix them (see #8971)
+            if (w.getNodesCount() < 2) {
+                unreversedWays.add(w);
+            } else {
+                boolean foundStartSegment = false;
+                int last = path.lastIndexOf(w.getNode(0));
+
+                for (int i = path.indexOf(w.getNode(0)); i <= last; i++) {
+                    if (path.get(i) == w.getNode(0) && i + 1 < path.size() && w.getNode(1) == path.get(i + 1)) {
+                        foundStartSegment = true;
+                        break;
+                    }
+                }
+                if (foundStartSegment) {
+                    unreversedWays.add(w);
+                } else {
+                    reversedWays.add(w);
+                }
+            }
+        }
+    }
+
+    protected static List<Node> tryJoin(Collection<Way> ways) {
+        List<Node> path = joinWithMultipolygonCode(ways);
+        if (path.isEmpty()) {
+            NodeGraph graph = NodeGraph.createNearlyUndirectedGraphFromNodeWays(ways);
+            path = graph.buildSpanningPathNoRemove();
+        }
+        return path;
+    }
+
+    /**
+     * Use {@link Multipolygon#joinWays(Collection)} to join ways.
+     * @param ways the ways
+     * @return List of nodes of the combined ways or null if ways could not be combined to a single way.
+     * Result may contain overlapping segments.
+     */
+    private static List<Node> joinWithMultipolygonCode(Collection<Way> ways) {
+        // sort so that old unclosed ways appear first
+        LinkedList<Way> toJoin = new LinkedList<>(ways);
+        toJoin.sort((o1, o2) -> {
+            int d = Boolean.compare(o1.isNew(), o2.isNew());
+            if (d == 0)
+                d = Boolean.compare(o1.isClosed(), o2.isClosed());
+            return d;
+        });
+        Collection<JoinedWay> list = Multipolygon.joinWays(toJoin);
+        if (list.size() == 1) {
+            // ways form a single line string
+            return new ArrayList<>(list.iterator().next().getNodes());
+        }
+        return Collections.emptyList();
     }
 
@@ -238,6 +292,23 @@
         if (combineResult == null)
             return;
+
         final Way selectedWay = combineResult.a;
         UndoRedoHandler.getInstance().add(combineResult.b);
+        Test test = new OverlappingWays();
+        test.startTest(null);
+        test.visit(combineResult.a);
+        test.endTest();
+        if (test.getErrors().isEmpty()) {
+            test = new SelfIntersectingWay();
+            test.startTest(null);
+            test.visit(combineResult.a);
+            test.endTest();
+        }
+        if (!test.getErrors().isEmpty()) {
+            new Notification(test.getErrors().get(0).getMessage())
+            .setIcon(JOptionPane.WARNING_MESSAGE)
+            .setDuration(Notification.TIME_SHORT)
+            .show();
+        }
         if (selectedWay != null) {
             GuiHelper.runInEDT(() -> ds.setSelected(selectedWay));
@@ -262,3 +333,4 @@
         setEnabled(numWays >= 2);
     }
+
 }
Index: trunk/src/org/openstreetmap/josm/data/osm/NodeGraph.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/osm/NodeGraph.java	(revision 15573)
+++ trunk/src/org/openstreetmap/josm/data/osm/NodeGraph.java	(revision 15574)
@@ -12,5 +12,4 @@
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
-import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
@@ -18,5 +17,4 @@
 import java.util.Optional;
 import java.util.Set;
-import java.util.Stack;
 import java.util.TreeMap;
 
@@ -249,10 +247,10 @@
     }
 
-    protected List<Node> buildPathFromNodePairs(Stack<NodePair> path) {
-        List<Node> ret = new LinkedList<>();
-        for (NodePair pair: path) {
+    protected List<Node> buildPathFromNodePairs(Deque<NodePair> path) {
+        List<Node> ret = new ArrayList<>(path.size() + 1);
+        for (NodePair pair : path) {
             ret.add(pair.getA());
         }
-        ret.add(path.peek().getB());
+        ret.add(path.peekLast().getB());
         return ret;
     }
@@ -264,23 +262,23 @@
      *
      * @param startNode the start node
-     * @return the spanning path; null, if no path is found
+     * @return the spanning path; empty list if no path is found
      */
     protected List<Node> buildSpanningPath(Node startNode) {
         if (startNode != null) {
-            // do not simply replace `Stack` by `ArrayDeque` because of different iteration behaviour, see #11957
-            Stack<NodePair> path = new Stack<>();
+            Deque<NodePair> path = new ArrayDeque<>();
             Set<NodePair> dupCheck = new HashSet<>();
-            Stack<NodePair> nextPairs = new Stack<>();
+            Deque<NodePair> nextPairs = new ArrayDeque<>();
             nextPairs.addAll(getOutboundPairs(startNode));
             while (!nextPairs.isEmpty()) {
-                NodePair cur = nextPairs.pop();
+                NodePair cur = nextPairs.removeLast();
                 if (!dupCheck.contains(cur) && !dupCheck.contains(cur.swap())) {
-                    while (!path.isEmpty() && !path.peek().isPredecessorOf(cur)) {
-                        dupCheck.remove(path.pop());
+                    while (!path.isEmpty() && !path.peekLast().isPredecessorOf(cur)) {
+                        dupCheck.remove(path.removeLast());
                     }
-                    path.push(cur);
+                    path.addLast(cur);
                     dupCheck.add(cur);
-                    if (isSpanningWay(path)) return buildPathFromNodePairs(path);
-                    nextPairs.addAll(getOutboundPairs(path.peek()));
+                    if (isSpanningWay(path))
+                        return buildPathFromNodePairs(path);
+                    nextPairs.addAll(getOutboundPairs(path.peekLast()));
                 }
             }
@@ -324,11 +322,12 @@
      * any duplicated edge was removed.
      *
-     * @return the path; null, if no path was found or duplicated edges were found
-     * @since 15555
+     * @return List of nodes that build the path; an empty list if no path or duplicated edges were found
+     * @since 15573 (return value not null)
      */
     public List<Node> buildSpanningPathNoRemove() {
-        if (edges.size() != addedEdges)
-            return null;
-        return buildSpanningPath();
+        List<Node> path = null;
+        if (edges.size() == addedEdges)
+            path = buildSpanningPath();
+        return path == null ? Collections.emptyList() : path;
     }
 
