Skip to content

Name the starting directory "." in Parent and a bare file's directory [patch] - #504

Merged
matt-edmondson merged 2 commits into
mainfrom
fix/502-starting-directory-is-dot
Nov 4, 2026
Merged

matt-edmondson merged 2 commits into
mainfrom
fix/502-starting-directory-is-dot

Conversation

@matt-edmondson

Copy link
Copy Markdown
Contributor

Fixes #502

What was wrong

Three getters returned the empty path to mean "the starting directory":

  • RelativeDirectoryPath.Parent for a single-segment path
  • RelativeFilePath.RelativeDirectoryPath for a bare file name
  • SemanticFilePath.DirectoryPath for a bare file name

The BCL rejects or misreads the empty path. Path.GetFullPath("") throws, so AsAbsolute() threw for any file in the current directory. Directory.Exists("") is false, so IsDirectory was wrong.

Change

  • The three getters now return ".". That is how Normalize() already writes the starting directory. The Parent XML doc is updated to match. A null from GetDirectoryName (a root) still maps to "" in SemanticFilePath.DirectoryPath.
  • PooledStringBuilder.CombinePaths drops a leading ".", as it already drops "". Without this, dir.Parent / dir.Name would turn a into ./a, and file.RelativeDirectoryPath / file.FileName would turn x.txt into ./x.txt. Those still name the same location, but they are no longer equal by string. As a side effect, RelativeDirectoryPath(".") / x now gives x instead of ./x.
  • PathPolyfill.GetFullPath (netstandard2.0) resolves an empty path to the base path, which matches Path.GetFullPath(path, basePath) on newer frameworks.

Tests

  • Updated: four existing tests pinned "" and now expect ".". They are RelativeDirectoryPath_Parent_WithSingleComponent_*, RelativeFilePath_RelativeDirectoryPath_WithBareFileName_*, the ("a", ".") row of Parent_ResolvesDotSegments, PathHierarchy_ParentTraversal_WorksCorrectly, and SemanticFilePath_DirectoryPath_OfABareFileName_IsTheStartingDirectory. They also assert that AsAbsolute() returns the current directory and that IsDirectory is true.
  • Added: a check that RelativeFilePath.DirectoryPath of a bare name is a real directory. Also added: RelativeDirectoryOperators_SingleSegmentParent_RoundTrips (Parent / Name gives back a and x.txt) and a base-directory resolution check.
  • EmptyLeftOperand_StayRelative now builds its empty operand directly, since Parent no longer produces one.
  • Results: with the Semantics.Paths changes reverted, 6 tests fail. With them, the full Semantics.Test suite passes 1965/1979, with 14 skipped as before.
  • Builds: Semantics.Paths builds clean for every target, netstandard2.0 included. The test project is net10.0 only, so the netstandard2.0 polyfill branch is compiled but not exercised by a test.

#503, the Parent / Name round trip for paths ending in .. or ., is related but separate and is left for its own change.


Generated by Claude Code

RelativeDirectoryPath.Parent of a single-segment path, and the directory
of a bare file name (RelativeFilePath.RelativeDirectoryPath and
SemanticFilePath.DirectoryPath), were the empty path. Path.GetFullPath
rejects that, so AsAbsolute() threw, and Directory.Exists reads it as
missing, so IsDirectory was false. They are now ".", matching Normalize.

CombinePaths drops a leading "." the way it already drops "", so
dir.Parent / dir.Name still gives back "a" rather than "./a".
PathPolyfill.GetFullPath on netstandard2.0 resolves an empty path to the
base path, as the BCL overload does on newer frameworks.

Fixes #502

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017vfJW2PeSdxz6omjxYwwT4
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Paths: the parent of a one-segment relative path (or the directory of a bare file name) is "", so AsAbsolute() throws and IsDirectory is false

2 participants