Index: resources/data/validator/combinations.mapcss
===================================================================
--- resources/data/validator/combinations.mapcss	(revision 16713)
+++ resources/data/validator/combinations.mapcss	(working copy)
@@ -72,6 +72,7 @@
 *[source:addr:postcode         ][!addr:postcode],
 *[source:addr:housenumber      ][!addr:housenumber] {
   throwWarning: tr("{0} without {1}", "{0.key}", "{1.key}");
+  set MissingKeyWarning;
   group: tr("missing tag");
   assertMatch: "way lanes=42";
   assertNoMatch: "way lanes=42 highway=unclassified";
@@ -578,18 +579,6 @@
   group: tr("missing tag");
 }
 
-/* see #8519, #11529, limited to motorways and trunks to avoid false positives for streets with lanes for non motorized traffic */
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][turn:lanes              ][tag(lanes)!=eval(count(split("|", tag("turn:lanes"))))],
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][change:lanes            ][tag(lanes)!=eval(count(split("|", tag("change:lanes"))))],
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][maxspeed:lanes          ][tag(lanes)!=eval(count(split("|", tag("maxspeed:lanes"))))],
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][minspeed:lanes          ][tag(lanes)!=eval(count(split("|", tag("minspeed:lanes"))))],
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][destination:lanes       ][tag(lanes)!=eval(count(split("|", tag("destination:lanes"))))],
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][destination:ref:lanes   ][tag(lanes)!=eval(count(split("|", tag("destination:ref:lanes"))))],
-way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][destination:symbol:lanes][tag(lanes)!=eval(count(split("|", tag("destination:symbol:lanes"))))] {
-    throwWarning: tr("Different number of lanes in the keys {0} and {1}", "{1.key}", "{2.key}");
-    group: tr("suspicious tag combination");
-}
-
 /* #13680 */
 area[golf=bunker][natural=beach] {
   throwWarning: tr("{0} together with {1}", "{0.tag}", "{1.tag}");
@@ -608,6 +597,18 @@
   group: tr("missing tag");
 }
 
+/* see #8519, #11529, limited to motorways and trunks to avoid false positives for streets with lanes for non motorized traffic */
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][turn:lanes              ][tag(lanes)!=eval(count(split("|", tag("turn:lanes"))))],
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][change:lanes            ][tag(lanes)!=eval(count(split("|", tag("change:lanes"))))],
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][maxspeed:lanes          ][tag(lanes)!=eval(count(split("|", tag("maxspeed:lanes"))))],
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][minspeed:lanes          ][tag(lanes)!=eval(count(split("|", tag("minspeed:lanes"))))],
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][destination:lanes       ][tag(lanes)!=eval(count(split("|", tag("destination:lanes"))))],
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][destination:ref:lanes   ][tag(lanes)!=eval(count(split("|", tag("destination:ref:lanes"))))],
+way[highway =~ /^(motorway|motorway_link|trunk|trunk_link)$/][lanes][destination:symbol:lanes][tag(lanes)!=eval(count(split("|", tag("destination:symbol:lanes"))))] {
+    throwWarning: tr("Different number of lanes in the keys {0} and {1}", "{1.key}", "{2.key}");
+    group: tr("suspicious tag combination");
+}
+
 /* #14125, #14323, #18185 */
 way[highway][lanes][!lanes:forward][!lanes:backward][oneway!=yes][oneway!=-1][oneway!=reversible][junction!=roundabout][lanes>2][get(split(".", tag(lanes)/2), 1)=5] {
   throwWarning: tr("street with odd number of {0}, but without {1} and {2} or {3}", "{1.key}", "{2.key}", "{3.key}", "{4.key}");
@@ -619,6 +620,19 @@
   assertMatch: "way highway=primary lanes=3";
 }
 
+/* #10932 */
+way[highway][!lanes][/^.*:lanes$/]!.MissingKeyWarning, 
+way[highway][!lanes:forward][/^.*:lanes:forward$/], 
+way[highway][!lanes:backward][/^.*:lanes:backward$/],
+way[highway][!lanes:both_ways][/^.*:lanes:both_ways$/] {
+  throwWarning: tr("Highway with `*:{0}` in a key but without `{0}` ", "{1.key}");
+  group: tr("missing tag");
+  assertMatch: "way highway=primary turn:lanes=left|right";
+  assertNoMatch: "way highway=primary turn:lanes=left|right lanes=2";
+  assertMatch: "way highway=primary turn:lanes:forward=left|right";
+  assertNoMatch: "way highway=primary turn:lanes:forward=left|right lanes:forward=2";
+}
+
 /* #15774 */
 node[emergency=fire_hydrant][fire_hydrant:count][fire_hydrant:count!=1] {
   throwOther: tr("{0} together with {1}. Tag each hydrant separately.", "{0.tag}", "{1.tag}");
@@ -747,4 +761,4 @@
   assertNoMatch: "node man_made=communications_tower height=\"328.22 ft\"";
   assertNoMatch: "node man_made=communications_tower height=4358'8\"";
   assertNoMatch: "node height=4358'";
-}
\ No newline at end of file
+}
