Repository: pantsbuild/pants

Issue:
Lint Goal Failing on  pants.engine.internals.build_files.resolve_address Because of InvalidTargetName
We have been using `pants` for a while. Some of our oldest code that was ported to `pants` and runs fine under v1.25 is having trouble under v2.0.0.

Is `house/cellar` an invalid name for a `python_library` target?
```
python_library(
    name="house/cellar",
    sources=[
...
```

When I run `./pants --changed-since='origin/master' lint` with changes in projects that contains targets with `/` in their name, I trigger the following code: https://github.com/pantsbuild/pants/blob/79e2b09b8d077ff1f1d42ef97dcf0a8ee25fd455/src/python/pants/build_graph/address.py#L172-L178

Part of the error output looks like:
```
Engine traceback:
  in select
  in pants.core.goals.lint.lint
  in pants.engine.internals.graph.resolve_targets
  in pants.engine.internals.graph.resolve_unexpanded_targets
  in pants.engine.internals.build_files.strip_address_origins
  in pants.engine.internals.graph.resolve_addresses_with_origins
  in pants.engine.internals.build_files.addresses_with_origins_from_address_specs
  in pants.engine.internals.build_files.resolve_address
Traceback (most recent call last):
  File "/Users/myaccount/.cache/pants/setup/bootstrap-Darwin-x86_64/2.0.0_py36/lib/python3.6/site-packages/pants/engine/internals/native.py", line 69, in generator_send
    res = func.send(arg)
  File "/Users/myaccount/.cache/pants/setup/bootstrap-Darwin-x86_64/2.0.0_py36/lib/python3.6/site-packages/pants/engine/internals/build_files.py", line 66, in resolve_address
    return address_input.file_to_address()
  File "/Users/myaccount/.cache/pants/setup/bootstrap-Darwin-x86_64/2.0.0_py36/lib/python3.6/site-packages/pants/build_graph/address.py", line 176, in file_to_address
    "A target may only be defined in a directory containing a file that it owns in "
pants.build_graph.address.InvalidTargetName: A target may only be defined in a directory containing a file that it owns in the filesystem: `../../house/cellar` is not at-or-above the file `house/cellar/__init__.py`.
```

When I run `./pants lint //:house/cellar` it works as expected. 

## Added Complication That Might Be Very Relevant
This target is defined in a `BUILD` file at the top of the repo. When I ran debug, I get back changed addresses that look like `Address(//action/cellar/mouse.py:../../action/cellar_tests)`. Shouldn't it look like `Address(//action/cellar/mouse.py://action/cellar_tests)`? I don't think whatever is generating the `Address()` objects is handling targets defined in top level `BUILD` files correctly.


Discussion:
In trying to find a way to tell which commit causes it to fail, I ran `./pants --changed-since='master' list`. It fails on `list`, too.
I think I've confirmed this is the problem:
```
In [5]: parent_count = 2
    ...: expected_prefix = f"..{os.path.sep}" * parent_count
    ...: target_component = "../../house_cellar"
    ...: if target_component[: target_component.rfind(os.path.sep) + 1] != expected_prefix:
    ...:    print("error")
    ...: else:
    ...:     print("don't panic")
    ...:
don't panic

In [6]: parent_count = 2
    ...: expected_prefix = f"..{os.path.sep}" * parent_count
    ...: target_component = "../../house/cellar"
    ...: if target_component[: target_component.rfind(os.path.sep) + 1] != expected_prefix:
    ...:    print("error")
    ...: else:
    ...:     print("don't panic")
    ...:
error
```

If you have the target name (`house/cellar`) without the `../../` prefix, then this check can take into account target names with `/`. Otherwise, we need to make names with `/` in them invalid.

Why is this check done? What parts of the codebase won't work if we violate this rule: "A target may only be defined in a directory containing a file that it owns in the filesystem"?
I've verified that this did not produce an error in v1.25.
@rcuza : Sorry for the trouble here. As you've discovered, `2.0.x` no longer supports slashes in target names, and it doesn't look like we can/should readd that support. I've opened #11115 to improve the error message for this case to suggest renaming the target.
With a little search and replace magic, I was able to refactor our target names without much pain. Having a warning in v1.30 and better error messages will help other people migrating a lot. 

Resolving patch:
diff --git a/src/python/pants/build_graph/address.py b/src/python/pants/build_graph/address.py
--- a/src/python/pants/build_graph/address.py
+++ b/src/python/pants/build_graph/address.py
@@ -11,7 +11,7 @@
 from pants.util.strutil import strip_prefix
 
 # @ is currently unused, but reserved for possible future needs.
-BANNED_CHARS_IN_TARGET_NAME = frozenset("@")
+BANNED_CHARS_IN_TARGET_NAME = frozenset(r"@!?/\:=")
 
 
 def parse_spec(
@@ -204,14 +204,26 @@ def check_target_name(cls, spec_path: str, name: str) -> None:
             )
 
         banned_chars = BANNED_CHARS_IN_TARGET_NAME & set(name)
-
-        if banned_chars:
-            raise InvalidTargetName(
-                "banned chars found in target name",
-                "{banned_chars} not allowed in target name: {name}".format(
-                    banned_chars=banned_chars, name=name
+        deprecated_banned_chars = banned_chars & set(r"/\:!=?")
+        if deprecated_banned_chars:
+            warn_or_error(
+                removal_version="2.2.0.dev1",
+                deprecated_entity_description=(
+                    r"Using any of the `\`, `/`, `:`, `!`, `=`, or `?` characters in a target name."
+                ),
+                hint=(
+                    f"The target name {name} (defined in directory {spec_path}) "
+                    f"contains deprecated characters (`{deprecated_banned_chars}`), which will "
+                    "cause some usecases to fail. Please replace these characters with another "
+                    "separator character like `_` or `-`."
                 ),
             )
+        elif banned_chars:
+            raise InvalidTargetName(
+                f"The target name {name} (defined in directory {spec_path}) "
+                f"contains banned characters (`{banned_chars}`). Please replace these "
+                "characters with another separator character like `_` or `-`."
+            )
 
     def __init__(self, spec_path: str, target_name: str) -> None:
         """


Test patch: