Repository: sphinx-doc/sphinx

Issue:
Intersphinx cannot link to section headers with uppercase names
### Describe the bug

In https://github.com/sphinx-doc/sphinx/blob/8aa5edd585957ae28ec6a8d1a5a581d2865fc013/sphinx/domains/std/__init__.py#L544-L548

it is specified that `:ref:` and `:numref:` references should lowercase the name of the target before resolving the link. When combined with `intersphinx`, this makes an implicit assumption that any "name" for a `std:label` entry in a loaded inventory is lowercase. Typically, the name corresponds to the HTML-anchor of the section header. Since Sphinx generates lowercase anchors by default, this assumption holds. However, other documentation generators may not choose lowercase anchors. For example, [Documenter.jl](https://documenter.juliadocs.org/stable/) currently uses capitalization in anchors for section titles, and also writes an `objects.inv` file with names matching those anchors.

It would be unreasonable to require that all inventory files may only contain lowercase names: since the [inventory format](https://sphobjinv.readthedocs.io/en/stable/syntax.html) specifies

> If [`{uri}`](https://sphobjinv.readthedocs.io/en/stable/syntax.html#uri) has an anchor (technically a “[fragment identifier](https://en.wikipedia.org/wiki/Fragment_identifier),” the portion following the # symbol) and the tail of the anchor is identical to [`{name}`](https://sphobjinv.readthedocs.io/en/stable/syntax.html#name), that tail is [replaced](https://github.com/sphinx-doc/sphinx/blob/2f60b44999d7e610d932529784f082fc1c6af989/sphinx/util/inventory.py#L157-L159) with `$`.

there is a strong incentive for the `name` to match the anchor. In the case of `Documenter`-generated inventory files, deviating from this would significantly increase the size of the inventory file.

The correct behavior would be for Sphinx to normalize the names of `std:label` to lowercase [when reading in the `objects.inv` file](https://github.com/sphinx-doc/sphinx/blob/8aa5edd585957ae28ec6a8d1a5a581d2865fc013/sphinx/util/inventory.py#L141), simply by adding

```python
if type == 'std:label':
    name = name.lower()
```

before line 141 in [`sphinx/util/inventory.py`](https://github.com/sphinx-doc/sphinx/blob/8aa5edd585957ae28ec6a8d1a5a581d2865fc013/sphinx/util/inventory.py)

### How to Reproduce

See the MWE at [`sphinx-to-documenter-links`](https://github.com/JuliaDocs/DocumenterInterLinks.jl/tree/0f75d8303e148392fa347298f238ac717b6c99c7/docs/src/sphinx-to-documenter-links).

[The problematic inventory file](https://github.com/JuliaDocs/DocumenterInterLinks.jl/blob/0f75d8303e148392fa347298f238ac717b6c99c7/docs/src/sphinx-to-documenter-links/docs/source/conf.py#L37-L43) is, e.g., http://juliadocs.org/DocumenterInterLinks.jl/stable/objecs.inv from the [`DocumenterInterLinks`](http://juliadocs.org/DocumenterInterLinks.jl/stable/) project.

The problem is demonstrated in the following [line in `docs/source/index.rst`](https://github.com/JuliaDocs/DocumenterInterLinks.jl/blob/0f75d8303e148392fa347298f238ac717b6c99c7/docs/src/sphinx-to-documenter-links/docs/source/index.rst?plain=1#L15):

```
* Referencing a heading is currently not possible due to a bug in Sphinx:
  ``:external+DocumenterInterLinks:std:ref:`Syntax``` does not work because
  Sphinx lowercases the "Syntax"
```


### Environment Information

```text
Platform:              darwin; (macOS-14.3.1-arm64-arm-64bit)
Python version:        3.10.10 (main, Mar 31 2023, 17:38:48) [Clang 14.0.0 (clang-1400.0.29.202)])
Python implementation: CPython
Sphinx version:        7.2.6
Docutils version:      0.20.1
Jinja2 version:        3.1.3
Pygments version:      2.17.2
```


### Sphinx extensions

Not relevant, since the issue is with `std:label`, not objects in the `jl` domain, but technically [`julia_domain.py`](https://github.com/JuliaDocs/DocumenterInterLinks.jl/blob/0f75d8303e148392fa347298f238ac717b6c99c7/docs/src/sphinx-to-documenter-links/docs/source/_extensions/julia_domain.py)


### Additional context

https://github.com/sphinx-doc/sphinx/issues/8982 seems related

Discussion:
goerz: Done. Switched list of case-insensitive types to `set` and updated `CHANGES`

Patch:
diff --git a/CHANGES.rst b/CHANGES.rst
index 16253c72c..af56692f7 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -84,6 +84,8 @@ Bugs fixed
   Patch by James Addison.
 * #11962: Fix target resolution when using ``:paramtype:`` fields.
   Patch by Bénédikt Tran.
+* #12008: Fix case-sensitive lookup of ``std:label`` names in intersphinx inventory.
+  Patch by Michael Goerz.
 
 Testing
 -------
diff --git a/sphinx/ext/intersphinx.py b/sphinx/ext/intersphinx.py
index cf59f386e..5eb8f0759 100644
--- a/sphinx/ext/intersphinx.py
+++ b/sphinx/ext/intersphinx.py
@@ -334,8 +334,10 @@ def _resolve_reference_in_domain_by_target(
         if target in inventory[objtype]:
             # Case sensitive match, use it
             data = inventory[objtype][target]
-        elif objtype == 'std:term':
-            # Check for potential case insensitive matches for terms only
+        elif objtype in {'std:label', 'std:term'}:
+            # Some types require case insensitive matches:
+            # * 'term': https://github.com/sphinx-doc/sphinx/issues/9291
+            # * 'label': https://github.com/sphinx-doc/sphinx/issues/12008
             target_lower = target.lower()
             insensitive_matches = list(filter(lambda k: k.lower() == target_lower,
                                               inventory[objtype].keys()))


Test patch:
diff --git a/tests/test_extensions/test_ext_intersphinx.py b/tests/test_extensions/test_ext_intersphinx.py
index bbe08d66b..51ca7f5ed 100644
--- a/tests/test_extensions/test_ext_intersphinx.py
+++ b/tests/test_extensions/test_ext_intersphinx.py
@@ -236,6 +236,16 @@ def test_missing_reference_stddomain(tmp_path, app, status, warning):
     rn = missing_reference(app, app.env, node, contnode)
     assert rn.astext() == 'A TERM'
 
+    # label reference (normal)
+    node, contnode = fake_node('std', 'ref', 'The-Julia-Domain', 'The-Julia-Domain')
+    rn = missing_reference(app, app.env, node, contnode)
+    assert rn.astext() == 'The Julia Domain'
+
+    # label reference (case insensitive)
+    node, contnode = fake_node('std', 'ref', 'the-julia-domain', 'the-julia-domain')
+    rn = missing_reference(app, app.env, node, contnode)
+    assert rn.astext() == 'The Julia Domain'
+
 
 @pytest.mark.sphinx('html', testroot='ext-intersphinx-cppdomain')
 def test_missing_reference_cppdomain(tmp_path, app, status, warning):
diff --git a/tests/test_util/test_util_inventory.py b/tests/test_util/test_util_inventory.py
index c63b4e0ce..2d9b7462f 100644
--- a/tests/test_util/test_util_inventory.py
+++ b/tests/test_util/test_util_inventory.py
@@ -41,6 +41,7 @@
 foo.bar.baz js:method 1 index.html#foo.bar.baz -
 foo.bar.qux js:data 1 index.html#foo.bar.qux -
 a term including:colon std:term -1 glossary.html#term-a-term-including-colon -
+The-Julia-Domain std:label -1 write_inventory/#$ The Julia Domain
 ''')
 
 inventory_v2_not_having_version = b'''\