aganea created this revision.
aganea added reviewers: hans, thakis, rsmith.
aganea added a project: clang.

Previously, `-fdiagnostics-absolute-paths` did not have an effect in some 
cases, for example: when using relative include paths, or relative CPP paths, 
or `--show-includes`, or `-E` or displaying notes. We have a peculiar use-case 
on our end with Fastbuild, where all this was exposed: CPP files are being are 
preprocessed on one PC, then compiled on another PC (which doesn't have the 
source-code); then the compiler's stdout is displayed on the first PC.


Repository:
  rC Clang

https://reviews.llvm.org/D63648

Files:
  include/clang/Frontend/TextDiagnostic.h
  lib/Frontend/HeaderIncludeGen.cpp
  lib/Frontend/PrintPreprocessedOutput.cpp
  lib/Frontend/TextDiagnostic.cpp
  test/Frontend/absolute-paths.c
  test/Modules/build-fail-notes.m
  test/Preprocessor/pp-modules.c

Index: test/Preprocessor/pp-modules.c
===================================================================
--- test/Preprocessor/pp-modules.c
+++ test/Preprocessor/pp-modules.c
@@ -13,3 +13,30 @@
 #include "pp-modules.h" // CHECK: # 1 "{{.*}}pp-modules.h" 1
 // CHECK: #pragma clang module import Module /* clang -E: implicit import for #include <Module/Module.h> */{{$}}
 // CHECK: # 14 "{{.*}}pp-modules.c" 2
+
+// Also test path canonicalization 
+// RUN: cd %S
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c pp-modules.c -F %S/../Modules/Inputs -E -o - | FileCheck -check-prefix=CHECK-RELATIVE %s
+
+// CHECK-RELATIVE: # 1 "pp-modules.c"
+// CHECK-RELATIVE-NEXT: # 1 "<built-in>" 1
+// CHECK-RELATIVE-NEXT: # 1 "<built-in>" 3
+// CHECK-RELATIVE-NEXT: # {{[0-9]+}} "<built-in>" 3
+// CHECK-RELATIVE-NEXT: # 1 "<command line>" 1
+// CHECK-RELATIVE-NEXT: # 1 "<built-in>" 2
+// CHECK-RELATIVE-NEXT: # 1 "pp-modules.c" 2
+// CHECK-RELATIVE: # 1 "./pp-modules.h" 1
+// CHECK-RELATIVE: # 14 "pp-modules.c" 2
+
+// RUN: %clang_cc1 -fmodules -fimplicit-module-maps -fmodules-cache-path=%t -x objective-c pp-modules.c -F %S/../Modules/Inputs -E -o - -fdiagnostics-absolute-paths | FileCheck -check-prefix=CHECK-ABSOLUTE %s
+
+// CHECK-ABSOLUTE: # 1 "{{.+(/|\\\\)test(/|\\\\)Preprocessor(/|\\\\)}}pp-modules.c"
+// CHECK-ABSOLUTE-NEXT: # 1 "<built-in>" 1
+// CHECK-ABSOLUTE-NEXT: # 1 "<built-in>" 3
+// CHECK-ABSOLUTE-NEXT: # {{[0-9]+}} "<built-in>" 3
+// CHECK-ABSOLUTE-NEXT: # 1 "<command line>" 1
+// CHECK-ABSOLUTE-NEXT: # 1 "<built-in>" 2
+// CHECK-ABSOLUTE-NEXT: # 1 "{{.+(/|\\\\)test(/|\\\\)Preprocessor(/|\\\\)}}pp-modules.c" 2
+
+// CHECK-ABSOLUTE: # 1 "{{.+(/|\\\\)test(/|\\\\)Preprocessor(/|\\\\)}}pp-modules.h" 1
+// CHECK-ABSOLUTE: # 14 "{{.+(/|\\\\)test(/|\\\\)Preprocessor(/|\\\\)}}pp-modules.c" 2
Index: test/Modules/build-fail-notes.m
===================================================================
--- test/Modules/build-fail-notes.m
+++ test/Modules/build-fail-notes.m
@@ -29,3 +29,16 @@
 // CHECK-SDIAG: DependsOnModule.h:1:10: fatal: could not build module 'Module'
 // CHECK-SDIAG: build-fail-notes.m:4:9: note: while building module 'DependsOnModule' imported from
 
+// Also test path canonicalization 
+// RUN: cd %S
+// RUN: not %clang_cc1 -Wincomplete-umbrella -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -F Inputs -DgetModuleVersion="epic fail" -serialize-diagnostic-file %t/tmp.diag build-fail-notes.m 2>&1 -fdiagnostics-show-note-include-stack | FileCheck -check-prefix=CHECK-RELATIVE %s
+// CHECK-RELATIVE: While building module 'DependsOnModule' imported from build-fail-notes.m:4:
+// CHECK-RELATIVE-NEXT: While building module 'Module' imported from Inputs{{/|\\}}DependsOnModule.framework{{/|\\}}Headers{{/|\\}}DependsOnModule.h:1:
+// CHECK-RELATIVE-NEXT: In file included from <module-includes>:1:
+// CHECK-RELATIVE-NEXT: Inputs{{/|\\}}Module.framework{{/|\\}}Headers{{/|\\}}Module.h:9:13: error: expected ';' after top level declarator
+
+// RUN: not %clang_cc1 -Wincomplete-umbrella -fmodules-cache-path=%t -fmodules -fimplicit-module-maps -F Inputs -DgetModuleVersion="epic fail" -serialize-diagnostic-file %t/tmp.diag build-fail-notes.m 2>&1 -fdiagnostics-show-note-include-stack -fdiagnostics-absolute-paths | FileCheck -check-prefix=CHECK-ABSOLUTE %s
+// CHECK-ABSOLUTE: While building module 'DependsOnModule' imported from {{.+(/|\\)test(/|\\)Modules(/|\\)}}build-fail-notes.m:4:
+// CHECK-ABSOLUTE-NEXT: While building module 'Module' imported from {{.+(/|\\)test(/|\\)Modules(/|\\)Inputs(/|\\)DependsOnModule.framework(/|\\)Headers(/|\\)}}DependsOnModule.h:1:
+// CHECK-ABSOLUTE-NEXT: In file included from {{.+(/|\\)test(/|\\)Modules(/|\\)}}<module-includes>:1:
+// CHECK-ABSOLUTE-NEXT: {{.+(/|\\)test(/|\\)Modules(/|\\)Inputs(/|\\)Module.framework(/|\\)Headers(/|\\)}}Module.h:9:13: error: expected ';' after top level declarator
Index: test/Frontend/absolute-paths.c
===================================================================
--- test/Frontend/absolute-paths.c
+++ test/Frontend/absolute-paths.c
@@ -15,3 +15,19 @@
 int g() {}
 // NORMAL: non-existant.c:123:10: warning: control reaches end of non-void function
 // ABSOLUTE: non-existant.c:123:10: warning: control reaches end of non-void function
+
+// Also test with relative include paths
+// RUN: cd %S
+// RUN: %clang_cc1 -fsyntax-only -I Inputs --show-includes absolute-paths.c 2>&1 | FileCheck -check-prefix=RELATIVE %s
+
+// RELATIVE: Note: including file: Inputs{{/|\\}}absolute-paths.h
+// RELATIVE-NEXT: In file included from absolute-paths.c:4:
+// RELATIVE-NEXT: Inputs{{/|\\}}absolute-paths.h:3:1: warning: control reaches end of non-void function
+// RELATIVE: non-existant.c:123:10: warning: control reaches end of non-void function
+
+// RUN: %clang_cc1 -fsyntax-only -I Inputs --show-includes -fdiagnostics-absolute-paths absolute-paths.c 2>&1 | FileCheck -check-prefix=RELATIVE2 %s
+
+// RELATIVE2: Note: including file: {{.+(/|\\)test(/|\\)Frontend(/|\\)Inputs(/|\\)}}absolute-paths.h
+// RELATIVE2-NEXT: In file included from {{.+(/|\\)test(/|\\)Frontend(/|\\)}}absolute-paths.c:4:
+// RELATIVE2-NEXT: {{.+(/|\\)test(/|\\)Frontend(/|\\)Inputs(/|\\)}}absolute-paths.h:3:1: warning: control reaches end of non-void function
+// RELATIVE2: {{.+(/|\\)test(/|\\)Frontend(/|\\)}}non-existant.c:123:10: warning: control reaches end of non-void function
Index: lib/Frontend/TextDiagnostic.cpp
===================================================================
--- lib/Frontend/TextDiagnostic.cpp
+++ lib/Frontend/TextDiagnostic.cpp
@@ -759,46 +759,67 @@
   OS << '\n';
 }
 
-void TextDiagnostic::emitFilename(StringRef Filename, const SourceManager &SM) {
-  SmallVector<char, 128> AbsoluteFilename;
-  if (DiagOpts->AbsolutePath) {
-    const DirectoryEntry *Dir = SM.getFileManager().getDirectory(
-        llvm::sys::path::parent_path(Filename));
-    if (Dir) {
-      // We want to print a simplified absolute path, i. e. without "dots".
-      //
-      // The hardest part here are the paths like "<part1>/<link>/../<part2>".
-      // On Unix-like systems, we cannot just collapse "<link>/..", because
-      // paths are resolved sequentially, and, thereby, the path
-      // "<part1>/<part2>" may point to a different location. That is why
-      // we use FileManager::getCanonicalName(), which expands all indirections
-      // with llvm::sys::fs::real_path() and caches the result.
-      //
-      // On the other hand, it would be better to preserve as much of the
-      // original path as possible, because that helps a user to recognize it.
-      // real_path() expands all links, which sometimes too much. Luckily,
-      // on Windows we can just use llvm::sys::path::remove_dots(), because,
-      // on that system, both aforementioned paths point to the same place.
+StringRef
+TextDiagnostic::canonicalizePath(StringRef Filename, const SourceManager &SM,
+                                 SmallVectorImpl<char> &AbsolutePath) {
+  DiagnosticOptions &Opts = SM.getDiagnostics().getDiagnosticOptions();
+  if (!Opts.AbsolutePath)
+    return Filename;
+
+  SmallString<4096> Path(Filename);
+  SM.getFileManager().makeAbsolutePath(Path);
+
+  const DirectoryEntry *Dir =
+      SM.getFileManager().getDirectory(llvm::sys::path::parent_path(Path));
+  if (!Dir)
+    return Filename;
+
+  // FIXME: convert to an enum
+  if (Filename == "<command line>" || Filename == "<scratch space>" ||
+      Filename == "<built-in>")
+    return Filename;
+
+  // We want to print a simplified absolute path, i. e. without "dots".
+  //
+  // The hardest part here are the paths like "<part1>/<link>/../<part2>".
+  // On Unix-like systems, we cannot just collapse "<link>/..", because
+  // paths are resolved sequentially, and, thereby, the path
+  // "<part1>/<part2>" may point to a different location. That is why
+  // we use FileManager::getCanonicalName(), which expands all indirections
+  // with llvm::sys::fs::real_path() and caches the result.
+  //
+  // On the other hand, it would be better to preserve as much of the
+  // original path as possible, because that helps a user to recognize it.
+  // real_path() expands all links, which sometimes too much. Luckily,
+  // on Windows we can just use llvm::sys::path::remove_dots(), because,
+  // on that system, both aforementioned paths point to the same place.
 #ifdef _WIN32
-      SmallString<4096> DirName = Dir->getName();
-      llvm::sys::fs::make_absolute(DirName);
-      llvm::sys::path::native(DirName);
-      llvm::sys::path::remove_dots(DirName, /* remove_dot_dot */ true);
+  SmallString<4096> DirName = Dir->getName();
+  llvm::sys::fs::make_absolute(DirName);
+  llvm::sys::path::native(DirName);
+  llvm::sys::path::remove_dots(DirName, /* remove_dot_dot */ true);
 #else
-      StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
+  StringRef DirName = SM.getFileManager().getCanonicalName(Dir);
 #endif
-      llvm::sys::path::append(AbsoluteFilename, DirName,
-                              llvm::sys::path::filename(Filename));
-      Filename = StringRef(AbsoluteFilename.data(), AbsoluteFilename.size());
-    }
-  }
+  llvm::sys::path::append(AbsolutePath, DirName,
+                          llvm::sys::path::filename(Filename));
+  return StringRef(AbsolutePath.data(), AbsolutePath.size());
+}
+
+std::string TextDiagnostic::emitFilename(StringRef Filename,
+                                         const SourceManager &SM) {
+  SmallString<128> AbsolutePath;
+  return canonicalizePath(Filename, SM, AbsolutePath);
+}
 
-  OS << Filename;
+std::string TextDiagnostic::emitFilename(FullSourceLoc Loc, PresumedLoc PLoc) {
+  SmallString<128> AbsolutePath;
+  return canonicalizePath(PLoc.getFilename(), Loc.getManager(), AbsolutePath);
 }
 
 /// Print out the file/line/column information and include trace.
 ///
-/// This method handlen the emission of the diagnostic location information.
+/// This method handles the emission of the diagnostic location information.
 /// This includes extracting as much location information as is present for
 /// the diagnostic and printing it, as well as any include stack or source
 /// ranges necessary.
@@ -811,7 +832,7 @@
     if (FID.isValid()) {
       const FileEntry *FE = Loc.getFileEntry();
       if (FE && FE->isValid()) {
-        emitFilename(FE->getName(), Loc.getManager());
+        OS << emitFilename(FE->getName(), Loc.getManager());
         OS << ": ";
       }
     }
@@ -825,7 +846,7 @@
   if (DiagOpts->ShowColors)
     OS.changeColor(savedColor, true);
 
-  emitFilename(PLoc.getFilename(), Loc.getManager());
+  OS << emitFilename(Loc, PLoc);
   switch (DiagOpts->getFormat()) {
   case DiagnosticOptions::Clang: OS << ':'  << LineNo; break;
   case DiagnosticOptions::MSVC:  OS << '('  << LineNo; break;
@@ -905,7 +926,8 @@
 
 void TextDiagnostic::emitIncludeLocation(FullSourceLoc Loc, PresumedLoc PLoc) {
   if (DiagOpts->ShowLocation && PLoc.isValid())
-    OS << "In file included from " << PLoc.getFilename() << ':'
+    OS << "In file included from "
+       << emitFilename(Loc, PLoc) << ':'
        << PLoc.getLine() << ":\n";
   else
     OS << "In included file:\n";
@@ -915,7 +937,8 @@
                                         StringRef ModuleName) {
   if (DiagOpts->ShowLocation && PLoc.isValid())
     OS << "In module '" << ModuleName << "' imported from "
-       << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
+       << emitFilename(Loc, PLoc) << ':'
+       << PLoc.getLine() << ":\n";
   else
     OS << "In module '" << ModuleName << "':\n";
 }
@@ -925,7 +948,8 @@
                                                 StringRef ModuleName) {
   if (DiagOpts->ShowLocation && PLoc.isValid())
     OS << "While building module '" << ModuleName << "' imported from "
-      << PLoc.getFilename() << ':' << PLoc.getLine() << ":\n";
+       << emitFilename(Loc, PLoc) << ':'
+       << PLoc.getLine() << ":\n";
   else
     OS << "While building module '" << ModuleName << "':\n";
 }
@@ -1351,7 +1375,7 @@
       break;
 
     OS << "fix-it:\"";
-    OS.write_escaped(PLoc.getFilename());
+    OS.write_escaped(emitFilename(PLoc.getFilename(), SM));
     OS << "\":{" << SM.getLineNumber(BInfo.first, BInfo.second)
       << ':' << SM.getColumnNumber(BInfo.first, BInfo.second)
       << '-' << SM.getLineNumber(EInfo.first, EInfo.second)
Index: lib/Frontend/PrintPreprocessedOutput.cpp
===================================================================
--- lib/Frontend/PrintPreprocessedOutput.cpp
+++ lib/Frontend/PrintPreprocessedOutput.cpp
@@ -11,11 +11,12 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Frontend/Utils.h"
 #include "clang/Basic/CharInfo.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/SourceManager.h"
 #include "clang/Frontend/PreprocessorOutputOptions.h"
+#include "clang/Frontend/TextDiagnostic.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Lex/MacroInfo.h"
 #include "clang/Lex/PPCallbacks.h"
 #include "clang/Lex/Pragma.h"
@@ -27,6 +28,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cstdio>
+
 using namespace clang;
 
 /// PrintMacroDefinition - Print a macro definition in a form that will be
@@ -286,7 +288,11 @@
   CurLine = NewLine;
 
   CurFilename.clear();
-  CurFilename += UserLoc.getFilename();
+
+  SmallString<128> AbsolutePath;
+  CurFilename +=
+      TextDiagnostic::canonicalizePath(UserLoc.getFilename(), SM, AbsolutePath);
+
   FileType = NewFileType;
 
   if (DisableLineMarkers) {
Index: lib/Frontend/HeaderIncludeGen.cpp
===================================================================
--- lib/Frontend/HeaderIncludeGen.cpp
+++ lib/Frontend/HeaderIncludeGen.cpp
@@ -6,13 +6,15 @@
 //
 //===----------------------------------------------------------------------===//
 
-#include "clang/Frontend/DependencyOutputOptions.h"
-#include "clang/Frontend/Utils.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/Frontend/DependencyOutputOptions.h"
 #include "clang/Frontend/FrontendDiagnostic.h"
+#include "clang/Frontend/TextDiagnostic.h"
+#include "clang/Frontend/Utils.h"
 #include "clang/Lex/Preprocessor.h"
 #include "llvm/ADT/SmallString.h"
 #include "llvm/Support/raw_ostream.h"
+
 using namespace clang;
 
 namespace {
@@ -173,7 +175,9 @@
   // "<command line>" and "<built-in>" in a bunch of places.
   if (ShowHeader && Reason == PPCallbacks::EnterFile &&
       UserLoc.getFilename() != StringRef("<command line>")) {
-    PrintHeaderInfo(OutputFile, UserLoc.getFilename(), ShowDepth, IncludeDepth,
-                    MSStyle);
+    SmallString<128> AbsolutePath;
+    StringRef Filename = TextDiagnostic::canonicalizePath(UserLoc.getFilename(),
+                                                          SM, AbsolutePath);
+    PrintHeaderInfo(OutputFile, Filename, ShowDepth, IncludeDepth, MSStyle);
   }
 }
Index: include/clang/Frontend/TextDiagnostic.h
===================================================================
--- include/clang/Frontend/TextDiagnostic.h
+++ include/clang/Frontend/TextDiagnostic.h
@@ -73,6 +73,10 @@
                                      StringRef Message, unsigned CurrentColumn,
                                      unsigned Columns, bool ShowColors);
 
+  /// Canonicalize a file path iff we want it absolute
+  static StringRef canonicalizePath(StringRef Filename, const SourceManager &SM,
+                                    SmallVectorImpl<char> &AbsolutePath);
+
 protected:
   void emitDiagnosticMessage(FullSourceLoc Loc, PresumedLoc PLoc,
                              DiagnosticsEngine::Level Level, StringRef Message,
@@ -98,7 +102,8 @@
                                   StringRef ModuleName) override;
 
 private:
-  void emitFilename(StringRef Filename, const SourceManager &SM);
+  static std::string emitFilename(StringRef Filename, const SourceManager &SM);
+  static std::string emitFilename(FullSourceLoc Loc, PresumedLoc PLoc);
 
   void emitSnippetAndCaret(FullSourceLoc Loc, DiagnosticsEngine::Level Level,
                            SmallVectorImpl<CharSourceRange> &Ranges,
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to