Skip to content

Comments

Fix Uri.GetHashCode hash/equality contract violation for file: URIs#124652

Merged
MihaZupan merged 2 commits intomainfrom
copilot/fix-uri-gethashcode-issue
Feb 23, 2026
Merged

Fix Uri.GetHashCode hash/equality contract violation for file: URIs#124652
MihaZupan merged 2 commits intomainfrom
copilot/fix-uri-gethashcode-issue

Conversation

Copy link
Contributor

Copilot AI commented Feb 20, 2026

Uri.GetHashCode uses IsUncOrDosPath to select between OrdinalIgnoreCase and ordinal hashing, but Equals uses this.IsUncOrDosPath for comparison mode — so two URIs with identical normalized RemoteUrl but different IsUncOrDosPath flags (e.g. a UNC-derived URI and an explicit file:///) compare equal yet produce different hash codes.

var uri1 = new Uri("\\\\\u202a"); // UNC path, IsUncOrDosPath=true → normalizes to file:///
var uri2 = new Uri("file:///");   // IsUncOrDosPath=false

Console.WriteLine(uri1 == uri2);        // True
Console.WriteLine(uri1.GetHashCode());  // -1626931478  ← mismatch
Console.WriteLine(uri2.GetHashCode());  //  558067402   ← mismatch

Description

Root cause: Flags.DosPath and Flags.UncPath are only ever set alongside syntax = UriParser.FileUri, so IsUncOrDosPath ⇒ IsFile is a provable invariant. A plain file:/// URI never sets those flags, so GetHashCode used a different hash algorithm than a UNC-derived URI with the same normalized form.

Fix: Replace if (IsUncOrDosPath) with if (IsFile) in GetHashCode. This extends OrdinalIgnoreCase hashing to all file: URIs, ensuring consistent hash codes for any two file: URIs that Equals can report as equal. No existing behavior changes for UNC/DOS paths; the only difference is that plain file: URIs now also use OrdinalIgnoreCase.GetHashCode.

Test: Added new Uri("\\\\\u202a") vs new Uri("file:///") to Equals_TestData, which is already consumed by EqualsTest — a harness that automatically verifies both equality symmetry and hash code consistency for equal pairs.

Original prompt

This section details on the original issue you should resolve

<issue_title>Uri GetHashCode mismatch for instances that compare as Equal</issue_title>
<issue_description>```c#
var uri1 = new Uri("\\\u202a");
var uri2 = new Uri("file:///");

Console.WriteLine(uri1 == uri2);
Console.WriteLine(uri1.GetHashCode());
Console.WriteLine(uri2.GetHashCode());


## Comments on the Issue (you are @copilot in this section)

<comments>
</comments>


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

@dotnet-policy-service
Copy link
Contributor

Tagging subscribers to this area: @karelz, @dotnet/ncl
See info in area-owners.md if you want to be subscribed.

@MihaZupan MihaZupan marked this pull request as ready for review February 21, 2026 16:43
Copilot AI review requested due to automatic review settings February 21, 2026 16:43
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes a hash/equals contract violation in Uri.GetHashCode for file: URIs. The issue occurs because GetHashCode was using IsUncOrDosPath to determine whether to use case-insensitive hashing, while Equals can return true for two file: URIs where one has IsUncOrDosPath=false (plain file:/// URI) and the other has IsUncOrDosPath=true (UNC-derived URI). Since IsUncOrDosPath only covers a subset of file: URIs, two equal file: URIs could produce different hash codes.

Changes:

  • Changed Uri.GetHashCode() from using IsUncOrDosPath to IsFile when selecting hash algorithm
  • Added test case verifying UNC-derived file: URI equals plain file: URI with consistent hash codes

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.

File Description
src/libraries/System.Private.Uri/src/System/Uri.cs Fixed GetHashCode to use IsFile instead of IsUncOrDosPath for selecting case-insensitive hashing
src/libraries/System.Private.Uri/tests/FunctionalTests/UriMethodsTests.cs Added test case comparing UNC path \\\\\u202a with file:/// to verify hash/equals contract

@MihaZupan MihaZupan merged commit 1b3a93f into main Feb 23, 2026
94 of 96 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Uri GetHashCode mismatch for instances that compare as Equal

3 participants