Index: /trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java	(revision 6572)
+++ /trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java	(revision 6573)
@@ -22,4 +22,5 @@
 import org.openstreetmap.josm.data.validation.Test;
 import org.openstreetmap.josm.data.validation.TestError;
+import org.openstreetmap.josm.tools.Predicate;
 import org.openstreetmap.josm.tools.Utils;
 
@@ -36,4 +37,5 @@
     protected static final int SOURCE_MAXSPEED_CONTEXT_MISMATCH_VS_MAXSPEED = 2705;
     protected static final int SOURCE_MAXSPEED_CONTEXT_MISMATCH_VS_HIGHWAY = 2706;
+    protected static final int SOURCE_WRONG_LINK = 2707;
 
     /**
@@ -104,4 +106,5 @@
                 testSourceMaxspeed(w, true);
             }
+            testHighwayLink(w);
         }
     }
@@ -140,4 +143,29 @@
     }
 
+    public static boolean isHighwayLinkOkay(final Way way) {
+        final String highway = way.get("highway");
+        if (highway == null || !highway.endsWith("_link")) {
+            return true;
+        }
+
+        final HashSet<OsmPrimitive> referrers = new HashSet<OsmPrimitive>();
+        referrers.addAll(way.firstNode().getReferrers());
+        referrers.addAll(way.lastNode().getReferrers());
+
+        return Utils.exists(Utils.filteredCollection(referrers, Way.class), new Predicate<Way>() {
+            @Override
+            public boolean evaluate(final Way otherWay) {
+                return !way.equals(otherWay) && otherWay.hasTag("highway", highway, highway.replaceAll("_link$", ""));
+            }
+        });
+    }
+
+    private void testHighwayLink(final Way way) {
+        if (!isHighwayLinkOkay(way)) {
+            errors.add(new TestError(this, Severity.WARNING,
+                    tr("Highway link is not linked to adequate highway/link"), SOURCE_WRONG_LINK, way));
+        }
+    }
+
     private void testMissingPedestrianCrossing(Node n) {
         leftByPedestrians = false;
Index: /trunk/src/org/openstreetmap/josm/tools/Predicates.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/tools/Predicates.java	(revision 6572)
+++ /trunk/src/org/openstreetmap/josm/tools/Predicates.java	(revision 6573)
@@ -1,3 +1,5 @@
 package org.openstreetmap.josm.tools;
+
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
 
 import java.util.regex.Pattern;
@@ -46,3 +48,15 @@
         };
     }
+
+    /**
+     * Returns a {@link Predicate} executing {@link OsmPrimitive#hasTag(String, String...)}.
+     */
+    public static Predicate<OsmPrimitive> hasTag(final String key, final String... values) {
+        return new Predicate<OsmPrimitive>() {
+            @Override
+            public boolean evaluate(OsmPrimitive p) {
+                return p.hasTag(key, values);
+            }
+        };
+    }
 }
Index: /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/HighwaysTest.groovy
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/HighwaysTest.groovy	(revision 6573)
+++ /trunk/test/unit/org/openstreetmap/josm/data/validation/tests/HighwaysTest.groovy	(revision 6573)
@@ -0,0 +1,65 @@
+// License: GPL. See LICENSE file for details.
+package org.openstreetmap.josm.data.validation.tests
+
+import org.openstreetmap.josm.Main
+import org.openstreetmap.josm.data.coor.LatLon
+import org.openstreetmap.josm.data.osm.DataSet
+import org.openstreetmap.josm.data.osm.Way
+import org.openstreetmap.josm.data.projection.Projections
+
+class HighwaysTest extends GroovyTestCase {
+
+    @Override
+    void setUp() {
+        Main.initApplicationPreferences()
+        Main.setProjection(Projections.getProjectionByCode("EPSG:3857"));
+    }
+
+    public static Way createTestSetting(String highway, String highwayLink) {
+        def ds = new DataSet()
+
+        def n00 = new org.openstreetmap.josm.data.osm.Node(new LatLon(0, 0))
+        def n10 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1, 0))
+        def n20 = new org.openstreetmap.josm.data.osm.Node(new LatLon(2, 0))
+        def n01 = new org.openstreetmap.josm.data.osm.Node(new LatLon(0, 1))
+        def n11 = new org.openstreetmap.josm.data.osm.Node(new LatLon(1, 1))
+        def n21 = new org.openstreetmap.josm.data.osm.Node(new LatLon(2, 1))
+
+        ds.addPrimitive(n00)
+        ds.addPrimitive(n10)
+        ds.addPrimitive(n20)
+        ds.addPrimitive(n01)
+        ds.addPrimitive(n11)
+        ds.addPrimitive(n21)
+
+        def major = new Way()
+        major.addNode(n00)
+        major.addNode(n10)
+        major.addNode(n20)
+        major.put("highway", highway)
+        def link = new Way()
+        link.addNode(n10)
+        link.addNode(n11)
+        link.put("highway", highwayLink)
+        def unclassified = new Way()
+        unclassified.addNode(n01)
+        unclassified.addNode(n11)
+        unclassified.addNode(n21)
+        unclassified.put("highway", "unclassified")
+
+        ds.addPrimitive(major)
+        ds.addPrimitive(link)
+        ds.addPrimitive(unclassified)
+
+        return link
+    }
+
+    void testCombinations() {
+        assert Highways.isHighwayLinkOkay(createTestSetting("primary", "primary_link"))
+        assert Highways.isHighwayLinkOkay(createTestSetting("primary", "primary"))
+        assert !Highways.isHighwayLinkOkay(createTestSetting("primary", "secondary_link"))
+        assert !Highways.isHighwayLinkOkay(createTestSetting("secondary", "primary_link"))
+        assert !Highways.isHighwayLinkOkay(createTestSetting("secondary", "tertiary_link"))
+        assert Highways.isHighwayLinkOkay(createTestSetting("residential", "residential"))
+    }
+}
