Index: include/llvm/Support/FileSystem.h =================================================================== --- include/llvm/Support/FileSystem.h +++ include/llvm/Support/FileSystem.h @@ -377,8 +377,12 @@ /// @brief Can we write this file? /// /// @param Path Input path. -/// @returns True if we can write to it, false otherwise. -bool can_write(const Twine &Path); +/// @param Err If file file is not writable and @p Err is not @c nullptr, +/// @c *Err is set to a platform-specific @c error_code indicating +/// the error that would be produced by an attempt to write to the +/// file. +/// @returns True if the file exists and we can write to it, false otherwise. +bool can_write(const Twine &Path, std::error_code *Err = nullptr); /// @brief Do file_status's represent the same thing? /// Index: lib/Frontend/CompilerInstance.cpp =================================================================== --- lib/Frontend/CompilerInstance.cpp +++ lib/Frontend/CompilerInstance.cpp @@ -623,7 +623,7 @@ llvm::sys::fs::status(OutputPath, Status); if (llvm::sys::fs::exists(Status)) { // Fail early if we can't write to the final destination. - if (!llvm::sys::fs::can_write(OutputPath)) + if (!llvm::sys::fs::can_write(OutputPath, &Error)) return nullptr; // Don't use a temporary if the output is a special file. This handles Index: lib/Support/Unix/Path.inc =================================================================== --- lib/Support/Unix/Path.inc +++ lib/Support/Unix/Path.inc @@ -335,10 +335,15 @@ return std::error_code(); } -bool can_write(const Twine &Path) { +bool can_write(const Twine &Path, std::error_code *Err) { SmallString<128> PathStorage; StringRef P = Path.toNullTerminatedStringRef(PathStorage); - return 0 == access(P.begin(), W_OK); + if (0 != access(P.begin(), W_OK)) { + if (Err) + *Err = std::error_code(errno, std::generic_category()); + return false; + } + return true; } bool can_execute(const Twine &Path) { Index: lib/Support/Windows/Path.inc =================================================================== --- lib/Support/Windows/Path.inc +++ lib/Support/Windows/Path.inc @@ -273,16 +273,27 @@ return std::error_code(); } -bool can_write(const Twine &Path) { +bool can_write(const Twine &Path, std::error_code *Err) { // FIXME: take security attributes into account. SmallString<128> PathStorage; SmallVector PathUtf16; - if (UTF8ToUTF16(Path.toStringRef(PathStorage), PathUtf16)) + if (std::error_code ec = + UTF8ToUTF16(Path.toStringRef(PathStorage), PathUtf16)) { + if (Err) *Err = ec; return false; + } DWORD Attr = ::GetFileAttributesW(PathUtf16.begin()); - return (Attr != INVALID_FILE_ATTRIBUTES) && !(Attr & FILE_ATTRIBUTE_READONLY); + if (Attr == INVALID_FILE_ATTRIBUTES) { + if (Err) *Err = windows_error(::GetLastError()); + return false; + } + if (Attr & FILE_ATTRIBUTE_READONLY) { + if (Err) *Err = windows_error(ERROR_ACCESS_DENIED); + return false; + } + return true; } bool can_execute(const Twine &Path) { Index: test/Frontend/output-failures.c =================================================================== --- test/Frontend/output-failures.c +++ test/Frontend/output-failures.c @@ -1,4 +1,12 @@ -// RUN: not %clang_cc1 -emit-llvm -o %S/doesnotexist/somename %s 2> %t -// RUN: FileCheck -check-prefix=OUTPUTFAIL -input-file=%t %s +// RUN: not %clang_cc1 -emit-llvm -o %S/doesnotexist/somename %s 2>&1 >/dev/null \ +// RUN: | FileCheck -check-prefix=ENOENT %s +// RUN: touch %t +// RUN: chmod a-w %t +// RUN: not %clang_cc1 -emit-llvm -o %t %s 2>&1 >/dev/null \ +// RUN: | FileCheck -check-prefix=EACCES %s +// RUN: rm -f %t -// OUTPUTFAIL: error: unable to open output file '{{.*}}{{[/\\]}}test{{[/\\]}}Frontend{{[/\\]}}doesnotexist{{[/\\]}}somename': '{{[nN]}}o such file or directory' +// REQUIRES: shell + +// ENOENT: error: unable to open output file '{{.*}}{{[/\\]}}test{{[/\\]}}Frontend{{[/\\]}}doesnotexist{{[/\\]}}somename': '{{[nN]}}o such file or directory' +// EACCES: error: unable to open output file '{{.*}}{{[/\\]}}test{{[/\\]}}Frontend{{[/\\]}}doesnotexist{{[/\\]}}somename': '{{[pP]}}ermission denied'