Introduce flag --incompatible_restrict_attribute_names

When the flag is enabled, attribute names must be syntactically valid identifiers. For example, they cannot contain special characters.

Fixes https://github.com/bazelbuild/bazel/issues/6437

RELNOTES: Attribute names are going to be restricted and must be syntactically valid identifiers. https://github.com/bazelbuild/bazel/issues/6437
PiperOrigin-RevId: 255986287
diff --git a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
index 6fd4db7..749c2d8 100644
--- a/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
+++ b/src/test/java/com/google/devtools/build/lib/packages/SkylarkSemanticsConsistencyTest.java
@@ -160,6 +160,7 @@
         "--incompatible_objc_framework_cleanup=" + rand.nextBoolean(),
         "--incompatible_remap_main_repo=" + rand.nextBoolean(),
         "--incompatible_remove_native_maven_jar=" + rand.nextBoolean(),
+        "--incompatible_restrict_attribute_names=" + rand.nextBoolean(),
         "--incompatible_restrict_named_params=" + rand.nextBoolean(),
         "--incompatible_run_shell_command_string=" + rand.nextBoolean(),
         "--incompatible_string_join_requires_strings=" + rand.nextBoolean(),
@@ -212,6 +213,7 @@
         .incompatibleObjcFrameworkCleanup(rand.nextBoolean())
         .incompatibleRemapMainRepo(rand.nextBoolean())
         .incompatibleRemoveNativeMavenJar(rand.nextBoolean())
+        .incompatibleRestrictAttributeNames(rand.nextBoolean())
         .incompatibleRestrictNamedParams(rand.nextBoolean())
         .incompatibleRunShellCommandString(rand.nextBoolean())
         .incompatibleStringJoinRequiresStrings(rand.nextBoolean())
diff --git a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
index 91b5427..54ca283 100644
--- a/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
+++ b/src/test/java/com/google/devtools/build/lib/skylark/SkylarkRuleClassFunctionsTest.java
@@ -198,6 +198,46 @@
   }
 
   @Test
+  public void testAttrNameSpecialCharactersAreForbidden() throws Exception {
+    ev =
+        createEvaluationTestCase(
+            StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
+                .incompatibleRestrictAttributeNames(true)
+                .build());
+    ev.initialize();
+
+    ev.setFailFast(false);
+    evalAndExport("def impl(ctx): return", "r = rule(impl, attrs = {'ab$c': attr.int()})");
+    ev.assertContainsError("attribute name `ab$c` is not a valid identfier");
+  }
+
+  @Test
+  public void testAttrNameCannotStartWithDigit() throws Exception {
+    ev =
+        createEvaluationTestCase(
+            StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
+                .incompatibleRestrictAttributeNames(true)
+                .build());
+    ev.initialize();
+
+    ev.setFailFast(false);
+    evalAndExport("def impl(ctx): return", "r = rule(impl, attrs = {'2_foo': attr.int()})");
+    ev.assertContainsError("attribute name `2_foo` is not a valid identfier");
+  }
+
+  @Test
+  public void testAttrNameSpecialCharactersLegacy() throws Exception {
+    ev =
+        createEvaluationTestCase(
+            StarlarkSemantics.DEFAULT_SEMANTICS.toBuilder()
+                .incompatibleRestrictAttributeNames(false)
+                .build());
+    ev.initialize();
+
+    evalAndExport("def impl(ctx): return", "r = rule(impl, attrs = {'ab$c': attr.int()})");
+  }
+
+  @Test
   public void testDisableDeprecatedParams() throws Exception {
     ev =
         createEvaluationTestCase(