[clang-tools-extra] [clang] Fixed clang-tidy rewriter not properly handling symlinks (PR #76350)

FĂ©lix-Antoine Constantin via cfe-commits cfe-commits at lists.llvm.org
Sun Dec 24 19:24:12 PST 2023


https://github.com/felix642 updated https://github.com/llvm/llvm-project/pull/76350

>From 0a5ae9bfff7597794889c93e4da5789714be3a4f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?F=C3=A9lix-Antoine=20Constantin?=
 <felix-antoine.constantin at polymtl.ca>
Date: Sun, 24 Dec 2023 21:01:32 -0500
Subject: [PATCH] [clang][tidy] Fixed clang-tidy rewriter not properly handling
 symlinks

Rewriter would not properly fix files if they were symlinked and using
--fix with clang-tidy would overwrite the symlink with the corrections
rather than the file.

With these changes the Rewriter now properly resolves the symlink before
applying the fixes.

Fixes #60845
---
 clang-tools-extra/docs/ReleaseNotes.rst   |  2 ++
 clang/lib/Rewrite/Rewriter.cpp            | 24 +++++++++++++---------
 clang/unittests/libclang/LibclangTest.cpp | 25 +++++++++++++++++++++++
 3 files changed, 41 insertions(+), 10 deletions(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 6d91748e4cef18..8a16ce1a7988a2 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -119,6 +119,8 @@ Improvements to clang-tidy
 
 - Improved `--dump-config` to print check options in alphabetical order.
 
+- Improved `--fix` to properly apply corrections on files that are symlinked.
+
 - Improved :program:`clang-tidy-diff.py` script. It now returns exit code `1`
   if any :program:`clang-tidy` subprocess exits with a non-zero code or if
   exporting fixes fails. It now accepts a directory as a value for
diff --git a/clang/lib/Rewrite/Rewriter.cpp b/clang/lib/Rewrite/Rewriter.cpp
index 0e6ae365064463..edc147ec304801 100644
--- a/clang/lib/Rewrite/Rewriter.cpp
+++ b/clang/lib/Rewrite/Rewriter.cpp
@@ -14,6 +14,7 @@
 #include "clang/Rewrite/Core/Rewriter.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/DiagnosticIDs.h"
+#include "clang/Basic/FileEntry.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Lex/Lexer.h"
@@ -412,16 +413,19 @@ bool Rewriter::overwriteChangedFiles() {
   unsigned OverwriteFailure = Diag.getCustomDiagID(
       DiagnosticsEngine::Error, "unable to overwrite file %0: %1");
   for (buffer_iterator I = buffer_begin(), E = buffer_end(); I != E; ++I) {
-    OptionalFileEntryRef Entry = getSourceMgr().getFileEntryRefForID(I->first);
-    llvm::SmallString<128> Path(Entry->getName());
-    getSourceMgr().getFileManager().makeAbsolutePath(Path);
-    if (auto Error = llvm::writeToOutput(Path, [&](llvm::raw_ostream &OS) {
-          I->second.write(OS);
-          return llvm::Error::success();
-        })) {
-      Diag.Report(OverwriteFailure)
-          << Entry->getName() << llvm::toString(std::move(Error));
-      AllWritten = false;
+    if (OptionalFileEntryRef fileEntry =
+            getSourceMgr().getFileEntryRefForID(I->first)) {
+      llvm::StringRef FileName =
+          getSourceMgr().getFileManager().getCanonicalName(*fileEntry);
+      if (auto Error =
+              llvm::writeToOutput(FileName, [&](llvm::raw_ostream &OS) {
+                I->second.write(OS);
+                return llvm::Error::success();
+              })) {
+        Diag.Report(OverwriteFailure)
+            << FileName << llvm::toString(std::move(Error));
+        AllWritten = false;
+      }
     }
   }
   return !AllWritten;
diff --git a/clang/unittests/libclang/LibclangTest.cpp b/clang/unittests/libclang/LibclangTest.cpp
index 87075a46d75187..cde19d9004cd81 100644
--- a/clang/unittests/libclang/LibclangTest.cpp
+++ b/clang/unittests/libclang/LibclangTest.cpp
@@ -16,6 +16,7 @@
 #include "llvm/Support/raw_ostream.h"
 #include "gtest/gtest.h"
 #include <cstring>
+#include <filesystem>
 #include <fstream>
 #include <functional>
 #include <map>
@@ -1379,3 +1380,27 @@ TEST_F(LibclangRewriteTest, RewriteRemove) {
   ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(Rew), 0);
   EXPECT_EQ(getFileContent(Filename), "int () { return 0; }");
 }
+
+TEST_F(LibclangRewriteTest, Symlink) {
+  std::filesystem::path Symlink = "link.cpp";
+  std::filesystem::create_symlink(Filename, Symlink);
+  ASSERT_TRUE(std::filesystem::exists(Symlink));
+  FilesAndDirsToRemove.emplace(Symlink);
+
+  CXTranslationUnit SymTu = clang_parseTranslationUnit(
+      Index, Symlink.c_str(), nullptr, 0, nullptr, 0, TUFlags);
+  CXFile SymlinkFile = clang_getFile(SymTu, Symlink.c_str());
+  CXRewriter SymRew = clang_CXRewriter_create(SymTu);
+
+  CXSourceLocation B = clang_getLocation(SymTu, SymlinkFile, 1, 5);
+  CXSourceLocation E = clang_getLocation(SymTu, SymlinkFile, 1, 9);
+  CXSourceRange Rng = clang_getRange(B, E);
+
+  clang_CXRewriter_removeText(SymRew, Rng);
+
+  ASSERT_EQ(clang_CXRewriter_overwriteChangedFiles(SymRew), 0);
+  EXPECT_EQ(getFileContent(Filename), "int () { return 0; }");
+  EXPECT_TRUE(std::filesystem::is_symlink(Symlink));
+
+  clang_CXRewriter_dispose(SymRew);
+}



More information about the cfe-commits mailing list