Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 18756)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java	(revision 18757)
@@ -232,7 +232,8 @@
                 final Selector selector = check.whichSelectorMatchesEnvironment(env);
                 if (selector != null) {
-                    check.rule.declaration.execute(env);
+                    final Environment envWithSelector = env.withSelector(selector);
+                    check.rule.declaration.execute(envWithSelector);
                     if (!ignoreError && !check.errors.isEmpty()) {
-                        r.addAll(check.getErrorsForPrimitive(p, selector, env, new MapCSSTagCheckerAndRule(check.rule)));
+                        r.addAll(check.getErrorsForPrimitive(p, selector, envWithSelector, new MapCSSTagCheckerAndRule(check.rule)));
                     }
                 }
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java	(revision 18756)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java	(revision 18757)
@@ -75,6 +75,6 @@
                 Command fix = check.fixPrimitive(p);
                 if (fix != null && fix.executeCommand() && !MapCSSTagChecker.getErrorsForPrimitive(p, true, checksToRun).isEmpty()) {
-                    assertionConsumer.accept(MessageFormat.format("Autofix does not work for test ''{0}'' (i.e., {1})",
-                            check.getMessage(p), check.rule.selectors));
+                    assertionConsumer.accept(MessageFormat.format("Autofix does not work for test ''{0}'' (i.e., {1}). Failing test: {2}",
+                            check.getMessage(p), check.rule.selectors, i.getKey()));
                 }
             }
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerFixCommand.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerFixCommand.java	(revision 18756)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerFixCommand.java	(revision 18757)
@@ -49,5 +49,5 @@
         final String s;
         if (obj instanceof Expression) {
-            s = (String) ((Expression) obj).evaluate(new Environment(p));
+            s = (String) ((Expression) obj).evaluate(new Environment(p).withSelector(matchingSelector));
         } else if (obj instanceof String) {
             s = (String) obj;
Index: trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java
===================================================================
--- trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java	(revision 18756)
+++ trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java	(revision 18757)
@@ -18,6 +18,4 @@
 import java.util.function.Consumer;
 import java.util.function.Predicate;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
 import java.util.stream.Collectors;
 
@@ -27,5 +25,4 @@
 import org.openstreetmap.josm.data.osm.IPrimitive;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WaySegment;
@@ -35,10 +32,11 @@
 import org.openstreetmap.josm.gui.mappaint.Environment;
 import org.openstreetmap.josm.gui.mappaint.Keyword;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition;
-import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.TagCondition;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Expression;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.PlaceholderExpression;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
@@ -215,5 +213,5 @@
 
     Selector whichSelectorMatchesPrimitive(OsmPrimitive primitive) {
-        return whichSelectorMatchesEnvironment(new Environment(primitive));
+        return whichSelectorMatchesEnvironment(new Environment(primitive, new MultiCascade(), Environment.DEFAULT_LAYER, null));
     }
 
@@ -226,35 +224,4 @@
 
     /**
-     * Determines the {@code index}-th key/value/tag (depending on {@code type}) of the
-     * {@link org.openstreetmap.josm.gui.mappaint.mapcss.Selector.GeneralSelector}.
-     *
-     * @param matchingSelector matching selector
-     * @param index            index
-     * @param type             selector type ("key", "value" or "tag")
-     * @param p                OSM primitive
-     * @return argument value, can be {@code null}
-     */
-    static String determineArgument(Selector.GeneralSelector matchingSelector, int index, String type, OsmPrimitive p) {
-        try {
-            final Condition c = matchingSelector.getConditions().get(index);
-            final Tag tag = c instanceof TagCondition
-                    ? ((TagCondition) c).asTag(p)
-                    : null;
-            if (tag == null) {
-                return null;
-            } else if ("key".equals(type)) {
-                return tag.getKey();
-            } else if ("value".equals(type)) {
-                return tag.getValue();
-            } else if ("tag".equals(type)) {
-                return tag.toString();
-            }
-        } catch (IndexOutOfBoundsException ignore) {
-            Logging.debug(ignore);
-        }
-        return null;
-    }
-
-    /**
      * Replaces occurrences of <code>{i.key}</code>, <code>{i.value}</code>, <code>{i.tag}</code> in {@code s} by the corresponding
      * key/value/tag of the {@code index}-th {@link Condition} of {@code matchingSelector}.
@@ -266,23 +233,5 @@
      */
     static String insertArguments(Selector matchingSelector, String s, OsmPrimitive p) {
-        if (s != null && matchingSelector instanceof Selector.ChildOrParentSelector) {
-            return insertArguments(((Selector.ChildOrParentSelector) matchingSelector).right, s, p);
-        } else if (s == null || !(matchingSelector instanceof Selector.GeneralSelector)) {
-            return s;
-        }
-        final Matcher m = Pattern.compile("\\{(\\d+)\\.(key|value|tag)\\}").matcher(s);
-        final StringBuffer sb = new StringBuffer();
-        while (m.find()) {
-            final String argument = determineArgument((Selector.GeneralSelector) matchingSelector,
-                    Integer.parseInt(m.group(1)), m.group(2), p);
-            try {
-                // Perform replacement with null-safe + regex-safe handling
-                m.appendReplacement(sb, String.valueOf(argument).replace("^(", "").replace(")$", ""));
-            } catch (IndexOutOfBoundsException | IllegalArgumentException e) {
-                Logging.log(Logging.LEVEL_ERROR, tr("Unable to replace argument {0} in {1}: {2}", argument, sb, e.getMessage()), e);
-            }
-        }
-        m.appendTail(sb);
-        return sb.toString();
+        return PlaceholderExpression.insertArguments(matchingSelector, s, p);
     }
 
@@ -329,5 +278,5 @@
             return String.valueOf(
                     val instanceof Expression
-                            ? ((Expression) val).evaluate(new Environment(p))
+                            ? ((Expression) val).evaluate(new Environment(p).withSelector(p == null ? null : whichSelectorMatchesPrimitive(p)))
                             : val
             );
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/Environment.java
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/Environment.java	(revision 18756)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/Environment.java	(revision 18757)
@@ -13,4 +13,5 @@
 import org.openstreetmap.josm.data.osm.WaySegment;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.Context;
+import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.LinkSelector;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
@@ -42,4 +43,7 @@
     private Context context = Context.PRIMITIVE;
 
+    /** The selector that is currently being evaluated */
+    private final Selector selector;
+
     /**
      * The name of the default layer. It is used if no layer is specified in the MapCSS rule
@@ -98,4 +102,5 @@
     public Environment() {
         // environment can be initialized later through with* methods
+        this.selector = null;
     }
 
@@ -107,5 +112,5 @@
      */
     public Environment(IPrimitive osm) {
-        this.osm = osm;
+        this(osm, null, null, null);
     }
 
@@ -123,4 +128,5 @@
         this.layer = layer;
         this.source = source;
+        this.selector = null;
     }
 
@@ -132,4 +138,15 @@
      */
     public Environment(Environment other) {
+        this(other, other.selector);
+    }
+
+    /**
+     * Creates a clone of the environment {@code other}.
+     *
+     * @param other the other environment. Must not be null.
+     * @param selector the selector for this environment. May be null.
+     * @throws IllegalArgumentException if {@code param} is {@code null}
+     */
+    private Environment(Environment other, Selector selector) {
         CheckParameterUtil.ensureParameterNotNull(other);
         this.osm = other.osm;
@@ -147,4 +164,5 @@
         this.mpAreaCache = other.mpAreaCache;
         this.toMatchForSurrounding = other.toMatchForSurrounding;
+        this.selector = selector;
     }
 
@@ -264,4 +282,14 @@
 
     /**
+     * Creates a clone of this environment, with the selector set
+     * @param selector The selector to use
+     * @return A clone of this environment, with the specified selector
+     * @since xxx
+     */
+    public Environment withSelector(Selector selector) {
+        return new Environment(this, selector);
+    }
+
+    /**
      * Determines if the context of this environment is {@link Context#LINK}.
      * @return {@code true} if the context of this environment is {@code Context#LINK}, {@code false} otherwise
@@ -302,4 +330,13 @@
             return ((Relation) osm).getMember(index).getRole();
         return null;
+    }
+
+    /**
+     * Get the selector for this environment
+     * @return The selector. May be {@code null}.
+     * @since xxx
+     */
+    public Selector selector() {
+        return this.selector;
     }
 
Index: trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj
===================================================================
--- trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 18756)
+++ trunk/src/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParser.jj	(revision 18757)
@@ -32,4 +32,5 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
+import org.openstreetmap.josm.gui.mappaint.mapcss.PlaceholderExpression;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
@@ -1053,4 +1054,7 @@
             if (lit == null)
                 return NullExpression.INSTANCE;
+            else if (lit instanceof String && PlaceholderExpression.PATTERN_PLACEHOLDER.matcher((String) lit).find()) {
+                return new PlaceholderExpression((String) lit);
+            }
             return new LiteralExpression(lit);
         }
