Repository: apple/swift-docc (swift)

Issue:
Incorrect error reporting on older topic-style symbol reference that isn't used in code or documentation markdown
### Description

WIth the Swift 5.9 updates to DocC that change the symbols to allow full unicode character support, I've updated my documentation - but am continuing to get warnings about incorrect symbols. I'm opening this issue per [a discussion on the swift forums](https://forums.swift.org/t/new-docc-supported-symbol-structure-in-swift-5-9-warnings-and-a-question-of-how-to-deal-with-them/65770/2).

The example that shows this is [MeshGenerator](https://github.com/heckj/MeshGenerator), and a perfect example is the symbol `TextureCoordinates/_=(_:_:)-38jci`.

The kind of warning that is displayed (in both Xcode and from DocC on the command line) is:
```
/Users/heckj/src/MeshGenerator/Sources/MeshGenerator/MeshGenerator.docc/Extensions/TextureCoordinates.md: warning: '_=(_:_:)-38jci' doesn't exist at '/MeshGenerator/TextureCoordinates'
```
This package exhibits several of these warnings, all apparently related to synthesized methods from default protocol conformance.

I've also verified this behavior exists on the current main branch of DocC (commit [2131c4358d9345cb72007dd53cca33ef410cf6b6](https://github.com/apple/swift-docc/commit/2131c4358d9345cb72007dd53cca33ef410cf6b6)).

### Checklist

- [X] If possible, I've reproduced the issue using the `main` branch of this package.
- [X] This issue hasn't been addressed in an [existing GitHub issue](https://github.com/apple/swift-docc/issues).

### Expected Behavior

No warnings displayed

### Actual behavior

Warnings displayed, such as:
```
/Users/heckj/src/MeshGenerator/Sources/MeshGenerator/MeshGenerator.docc/Extensions/TextureCoordinates.md: warning: '_=(_:_:)-38jci' doesn't exist at '/MeshGenerator/TextureCoordinates'
```

When I've not added the symbol to any markdown (or more explicitly, I've replaced it)

### Steps To Reproduce

## Project: MeshGenerator

- `git clone https://github.com/heckj/MeshGenerator`
- `cd MeshGenerator`
- `git checkout 9912f61d3222a76364b156c6055be35fd48c348c`

## verify that the symbol isn't in use in code or markdown files:

- `grep -R "\_=(_:_:)-38jci" .`

## exhibit the errors

- `./docbuild.bash`

or the more explicit version:

- `$(xcrun --find swift) package  generate-documentation  --target MeshGenerator`

### Swift-DocC Version Information

5.9.0

### Swift Compiler Version Information

```shell
swift-driver version: 1.82.2 Apple Swift version 5.9 (swiftlang-5.9.0.114.10 clang-1500.0.29.1)
Target: arm64-apple-macosx13.0
```
```

Pull request:
<!--
If you're opening a PR to cherry-pick a change for a release branch, use this template instead:
https://github.com/apple/swift-docc/blob/main/.github/PULL_REQUEST_TEMPLATE/CHERRY_PICK.md
-->

Bug/issue #, if applicable: https://github.com/apple/swift-docc/issues/640 (rdar://111227479)

## Summary

This fixes a false positive warning when a symbol with characters that aren't allowed in a resolved reference URL is curated. The warning happened because 2nd pass curation visited the link again before it was added to the referenceIndex.

## Dependencies

None

## Testing

Build documentation for the MeshGenerator project, as described in this issue https://github.com/apple/swift-docc/issues/640

## Checklist

Make sure you check off the following items. If they cannot be completed, provide a reason.

- [x] Added tests
- [x] Ran the `./bin/test` script and it succeeded
- [x] Updated documentation if necessary

Patch:
diff --git a/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift b/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift
index 278090caf3..dea18e46ce 100644
--- a/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift
+++ b/Sources/SwiftDocC/Infrastructure/DocumentationContext.swift
@@ -658,9 +658,12 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
 
         for result in results.sync({ $0 }) {
             documentationCache[result.reference] = result.node
-            if let preciseIdentifier = result.node.symbol?.identifier.precise {
-                symbolIndex[preciseIdentifier] = result.reference
-            }
+            assert(
+                // If this is a symbol, verify that the reference exist in the in the symbolIndex
+                result.node.symbol.map { symbolIndex[$0.identifier.precise] == result.reference }
+                ?? true, // Nothing to check for non-symbols
+                "Previous versions updated the symbolIndex here. This assert verifies that that's no longer necessary."
+            )
             diagnosticEngine.emit(result.problems)
         }
     }
@@ -2378,6 +2381,15 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
             bundle: bundle
         )
         
+        // After the resolving links in tutorial content all the local references are known and can be added to the referenceIndex for fast lookup.
+        referenceIndex.reserveCapacity(knownIdentifiers.count + nodeAnchorSections.count)
+        for reference in knownIdentifiers {
+            referenceIndex[reference.absoluteString] = reference
+        }
+        for reference in nodeAnchorSections.keys {
+            referenceIndex[reference.absoluteString] = reference
+        }
+        
         try shouldContinueRegistration()
         var allCuratedReferences: Set<ResolvedTopicReference>
         if LinkResolutionMigrationConfiguration.shouldUseHierarchyBasedLinkResolver {
@@ -2433,22 +2445,14 @@ public class DocumentationContext: DocumentationContextDataProviderDelegate {
 
         // Sixth - fetch external entities and merge them in the context
         mergeExternalEntities(withReferences: Array(externallyResolvedSymbols))
+        for case .success(let reference) in externallyResolvedLinks.values {
+            referenceIndex[reference.absoluteString] = reference
+        }
         
         // Seventh, the complete topic graph—with all nodes and all edges added—is analyzed.
         topicGraphGlobalAnalysis()
         
         preResolveModuleNames()
-        
-        referenceIndex.reserveCapacity(knownIdentifiers.count + nodeAnchorSections.count)
-        for reference in knownIdentifiers {
-            referenceIndex[reference.absoluteString] = reference
-        }
-        for case .success(let reference) in externallyResolvedLinks.values {
-            referenceIndex[reference.absoluteString] = reference
-        }
-        for reference in nodeAnchorSections.keys {
-            referenceIndex[reference.absoluteString] = reference
-        }
     }
     
     /// Given a list of topics that have been automatically curated, checks if a topic has been additionally manually curated


Test patch:
diff --git a/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift b/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift
index 8236cb4097..7ab877e2f5 100644
--- a/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift
+++ b/Tests/SwiftDocCTests/Infrastructure/DocumentationContext/DocumentationContextTests.swift
@@ -1667,6 +1667,28 @@ let expected = """
         XCTAssertEqual(node.kind, .unknown)
     }
     
+    func testCuratingSymbolsWithSpecialCharacters() throws {
+        try XCTSkipUnless(LinkResolutionMigrationConfiguration.shouldUseHierarchyBasedLinkResolver)
+
+        let (_, _, context) = try testBundleAndContext(copying: "InheritedOperators") { root in
+            try """
+            # ``Operators/MyNumber``
+            
+            A documentation extension that curates symbosl with characters not allowed in a resolved reference URL.
+
+            ## Topics
+
+            - ``<(_:_:)``
+            - ``>(_:_:)``
+            - ``<=(_:_:)``
+            - ``>=(_:_:)``
+            """.write(to: root.appendingPathComponent("doc-extension.md"), atomically: true, encoding: .utf8)
+        }
+        
+        let unresolvedTopicProblems = context.problems.filter({ $0.diagnostic.identifier == "org.swift.docc.unresolvedTopicReference" })
+        XCTAssertEqual(unresolvedTopicProblems.map(\.diagnostic.summary), [], "All links should resolve without warnings")
+    }
+    
     func testSpecialCharactersInLinks() throws {
         try XCTSkipUnless(LinkResolutionMigrationConfiguration.shouldUseHierarchyBasedLinkResolver)