Repository: pallets/click

Issue:
Custom default label ignored if default is None
I found out that custom default label is not shown when `default` is `None`. In some cases `default = None` is intentional, but custom default label would be still helpful.

An example with `--limit` option, with `None` as `default` meaning no limit:
```python
import click

@click.command()
@click.option('--limit', type=int, show_default='unlimited')
def main(limit: int):
    print('Limit: {}'.format(limit))

if __name__ == '__main__':
    main()
```

### Expected Behavior
Tell us what should happen.

```sh
$ python3 /tmp/foo.py --help
Usage: foo.py [OPTIONS]

Options:
  --limit INTEGER  [default: unlimited]
  --help           Show this message and exit.
```

### Actual Behavior
Tell us what happens instead.

```sh
$ python3 /tmp/foo.py --help
Usage: foo.py [OPTIONS]

Options:
  --limit INTEGER
  --help           Show this message and exit.
```
Default is not shown, making the description less useful.

### Environment

* Python version: 3.9.1
* Click version: 7.1.2


Discussion:
`show_default` is supposed to be a Boolean, triggering to show the value in `default`. Since there is no default set I think the best would be to just set `help='[default: unlimited]'`.

Otherwise you could achive your result with:
```
import click

@click.command()
@click.option('--limit', type=int, show_default='unlimited', default=-1)
def main(limit: int):
    if limit >= 0:
      print('Limit: {}'.format(limit))

if __name__ == '__main__':
    main()
```
According to docs, string is a perfectly valid value for a `show_default`, see https://click.palletsprojects.com/en/7.x/api/?highlight=show_default#click.Option.

Yes, I found out it works, when I change the default value to something else, but that's not something I want to do. It makes code less readable.

Resolving patch:
diff --git a/src/click/core.py b/src/click/core.py
--- a/src/click/core.py
+++ b/src/click/core.py
@@ -2405,9 +2405,12 @@ def _write_opts(opts):
                 extra.append(f"env var: {var_str}")
 
         default_value = self.get_default(ctx, call=False)
+        show_default_is_str = isinstance(self.show_default, str)
 
-        if default_value is not None and (self.show_default or ctx.show_default):
-            if isinstance(self.show_default, str):
+        if show_default_is_str or (
+            default_value is not None and (self.show_default or ctx.show_default)
+        ):
+            if show_default_is_str:
                 default_string = f"({self.show_default})"
             elif isinstance(default_value, (list, tuple)):
                 default_string = ", ".join(str(d) for d in default_value)


Test patch:
diff --git a/tests/test_options.py b/tests/test_options.py
--- a/tests/test_options.py
+++ b/tests/test_options.py
@@ -660,6 +660,14 @@ def test_show_default_boolean_flag_value(runner):
     assert "[default: False]" in message
 
 
+def test_show_default_string(runner):
+    """When show_default is a string show that value as default."""
+    opt = click.Option(["--limit"], show_default="unlimited")
+    ctx = click.Context(click.Command("cli"))
+    message = opt.get_help_record(ctx)[1]
+    assert "[default: (unlimited)]" in message
+
+
 @pytest.mark.parametrize(
     ("args", "expect"),
     [