Previously the operator+=(extension) call would have re-parsed the path
and recreated the components with the right extension. Since optimising
it to not re-parse the whole string, we need to actually remove the
extension from the final filename before appending anything to it, and
append the dot to that final component too.

        PR libstdc++/89117
        * src/c++17/fs_path.cc (path::replace_extension): Erase extension from
        final component as well as from _M_pathname. Append the dot using
        operator+= instead of only to _M_pathname.
        (path::_M_find_extension): Reformat slightly.
        * testsuite/27_io/filesystem/path/modifiers/replace_extension.cc:
        Add more test cases.

Tested x86_64-linux, committed to trunk.

commit 84cc9f8045b43e8f1b07b24935fbc8a8c70e30ed
Author: Jonathan Wakely <jwak...@redhat.com>
Date:   Wed Jan 30 15:53:08 2019 +0000

    PR libstdc++/89117 fix path::replace_extension("") case
    
    Previously the operator+=(extension) call would have re-parsed the path
    and recreated the components with the right extension. Since optimising
    it to not re-parse the whole string, we need to actually remove the
    extension from the final filename before appending anything to it, and
    append the dot to that final component too.
    
            PR libstdc++/89117
            * src/c++17/fs_path.cc (path::replace_extension): Erase extension 
from
            final component as well as from _M_pathname. Append the dot using
            operator+= instead of only to _M_pathname.
            (path::_M_find_extension): Reformat slightly.
            * testsuite/27_io/filesystem/path/modifiers/replace_extension.cc:
            Add more test cases.

diff --git a/libstdc++-v3/src/c++17/fs_path.cc 
b/libstdc++-v3/src/c++17/fs_path.cc
index 34de52f3a0f..db6a1cb29d8 100644
--- a/libstdc++-v3/src/c++17/fs_path.cc
+++ b/libstdc++-v3/src/c++17/fs_path.cc
@@ -1258,17 +1258,16 @@ path::replace_extension(const path& replacement)
        _M_pathname.erase(ext.second);
       else
        {
-         const auto& back = _M_cmpts.back();
-         if (ext.first != &back._M_pathname)
-           _GLIBCXX_THROW_OR_ABORT(
-               std::logic_error("path::replace_extension failed"));
+         auto& back = _M_cmpts.back();
+         __glibcxx_assert( ext.first == &back._M_pathname );
+         back._M_pathname.erase(ext.second);
          _M_pathname.erase(back._M_pos + ext.second);
        }
     }
    // If replacement is not empty and does not begin with a dot character,
    // a dot character is appended
   if (!replacement.empty() && replacement.native()[0] != dot)
-    _M_pathname += dot;
+    operator+=(".");
   operator+=(replacement);
   return *this;
 }
@@ -1803,8 +1802,9 @@ path::_M_find_extension() const
        {
          if (sz <= 2 && (*s)[0] == dot)
            return { s, string_type::npos };
-         const auto pos = s->rfind(dot);
-         return { s, pos ? pos : string_type::npos };
+         if (const auto pos = s->rfind(dot))
+           return { s , pos };
+         return { s, string_type::npos };
        }
     }
   return {};
diff --git 
a/libstdc++-v3/testsuite/27_io/filesystem/path/modifiers/replace_extension.cc 
b/libstdc++-v3/testsuite/27_io/filesystem/path/modifiers/replace_extension.cc
index df4b77aa116..98f2e6e4c41 100644
--- 
a/libstdc++-v3/testsuite/27_io/filesystem/path/modifiers/replace_extension.cc
+++ 
b/libstdc++-v3/testsuite/27_io/filesystem/path/modifiers/replace_extension.cc
@@ -33,6 +33,15 @@ test01()
   compare_paths( path("/foo.txt").replace_extension("cpp"), "/foo.cpp" );
   compare_paths( path("/foo.txt").replace_extension(".cpp"), "/foo.cpp" );
   compare_paths( path("/").replace_extension("bar"), "/.bar" );
+  compare_paths( path("/").replace_extension(".bar"), "/.bar" );
+  compare_paths( path("/dir/").replace_extension("bar"), "/dir/.bar" );
+  compare_paths( path("dir/foo").replace_extension("bar"), "dir/foo.bar" );
+
+  // PR 89117:
+  compare_paths( path("/foo.txt").replace_extension(), "/foo" );
+  compare_paths( path("foo.txt").replace_extension(), "foo" );
+  compare_paths( path("/foo").replace_extension(), "/foo" );
+  compare_paths( path("foo").replace_extension(), "foo" );
 }
 
 void

Reply via email to