You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Paths: since #504, DirectoryPath("a").Parent / Name is "./a" and FilePath("x.txt").DirectoryPath / FileName is "./x.txt", because DirectoryPath's / operators use Path.Combine #539
#504 changed the parent of a one-segment relative path, and the directory of a bare file name, from "" to ".". It also taught PooledStringBuilder.CombinePaths to drop a leading ".", so RelativeDirectoryPath's / operators still give back "a" and not "./a".
The primitive DirectoryPath type did not get that change. Its four / operators (Primitives/DirectoryPath.cs:88, :104, :120, :136) call Path.Combine(left.WeakString, right.WeakString), and Path.Combine(".", "a") is "./a". Before #504, the left side was "", and Path.Combine("", "a") returned "a", so this is a regression.
SemanticPath.MakeCanonical does not strip a leading ./, and path equality compares the stored string, so the rebuilt path is not equal to the original.
vardir=DirectoryPath.Create<DirectoryPath>("a");dir.Parent/dir.Name;// "./a" (equal to dir: False)varfile=FilePath.Create<FilePath>("x.txt");file.DirectoryPath/file.FileName;// "./x.txt" (equal to file: False)// The Relative* types are fine, because they go through CombinePaths:varrdir=RelativeDirectoryPath.Create<RelativeDirectoryPath>("a");rdir.Parent/rdir.Name;// "a" (equal: True)
DirectoryPath.Name documents that "Parent / Name is this directory again". DirectoryNameRebuildTests.DirectoryPath_ParentSlashName_IsTheSameDirectory tests only an absolute path, so it did not catch this. Code that rebuilds or re-parents a relative path, or uses the result as a dictionary key or in an equality check, now gets a different, unequal value.
This is not the ./..-suffix case in #503. Here the input is a plain single-segment path.
Suggested fix
Route DirectoryPath's / operators through PooledStringBuilder.CombinePaths, as the Relative* and Absolute* directory types already do. Alternatively, drop a leading "." left operand before calling Path.Combine.
Add tests:
DirectoryPath("a").Parent / Name == DirectoryPath("a")
Category: Bug (regression from #504) Priority: High Area / suggested assignment:Semantics.Paths maintainer. The fix is in Primitives/DirectoryPath.cs.
Why High: the wrong result is silent. On main, DirectoryPath("a").Parent / Name returns "./a", and FilePath("x.txt").DirectoryPath / FileName returns "./x.txt". Neither is equal to the original path. That breaks the round trip DirectoryPath.Name documents. It also breaks equality and dictionary-key lookups for any caller that rebuilds or re-parents a single-segment relative path, and nothing throws. It is a regression in a shipped library, while the fix is small.
Verified: all four / operators in Semantics.Paths/Primitives/DirectoryPath.cs (lines 88, 104, 120, 136) still call Path.Combine(left.WeakString, right.WeakString) rather than PooledStringBuilder.CombinePaths, as the issue says.
Next step: route DirectoryPath's / operators through PooledStringBuilder.CombinePaths, the way the Relative* and Absolute* types do. Then add the three regression tests listed in the issue. The existing DirectoryPath_ParentSlashName_IsTheSameDirectory covers only an absolute path, so it needs a relative case as well.
Activity
matt-edmondson commentedon Nov 4, 2026
Triage
Category: Bug (regression from #504)
Priority: High
Area / suggested assignment:
Semantics.Pathsmaintainer. The fix is inPrimitives/DirectoryPath.cs.Why High: the wrong result is silent. On
main,DirectoryPath("a").Parent / Namereturns"./a", andFilePath("x.txt").DirectoryPath / FileNamereturns"./x.txt". Neither is equal to the original path. That breaks the round tripDirectoryPath.Namedocuments. It also breaks equality and dictionary-key lookups for any caller that rebuilds or re-parents a single-segment relative path, and nothing throws. It is a regression in a shipped library, while the fix is small.Verified: all four
/operators inSemantics.Paths/Primitives/DirectoryPath.cs(lines 88, 104, 120, 136) still callPath.Combine(left.WeakString, right.WeakString)rather thanPooledStringBuilder.CombinePaths, as the issue says.Duplicates / related:
dir.Parent / dir.Namenames a different directory when the path ends in..or., breaking the documented round trip #503 covers the./..-suffix case of the same round trip, and the fix there may touch the same operators, so the two are worth fixing together."."parent, which is the source of the change.\in a name is a separator to Normalize/Parent/Make but a name character elsewhere, soRelativePath.Maketurnsweird\name.txtintoweird/name.txt#532 (separator handling on Linux/macOS) is in the same area but has a different cause.Next step: route
DirectoryPath's/operators throughPooledStringBuilder.CombinePaths, the way theRelative*andAbsolute*types do. Then add the three regression tests listed in the issue. The existingDirectoryPath_ParentSlashName_IsTheSameDirectorycovers only an absolute path, so it needs a relative case as well.Generated by Claude Code