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 &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 &Ranges, 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 @@ // "" and "" in a bunch of places. if (ShowHeader && Reason == PPCallbacks::EnterFile && UserLoc.getFilename() != StringRef("")) { - 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: 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 + 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/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 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 "//../". - // On Unix-like systems, we cannot just collapse "/..", because - // paths are resolved sequentially, and, thereby, the path - // "/" 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 &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 == "" || Filename == "" || + Filename == "") + return Filename; + + // We want to print a simplified absolute path, i. e. without "dots". + // + // The hardest part here are the paths like "//../". + // On Unix-like systems, we cannot just collapse "/..", because + // paths are resolved sequentially, and, thereby, the path + // "/" 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: 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: 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 :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(/|\\)}}:1: +// CHECK-ABSOLUTE-NEXT: {{.+(/|\\)test(/|\\)Modules(/|\\)Inputs(/|\\)Module.framework(/|\\)Headers(/|\\)}}Module.h:9:13: error: expected ';' after top level declarator 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 */{{$}} // 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 "" 1 +// CHECK-RELATIVE-NEXT: # 1 "" 3 +// CHECK-RELATIVE-NEXT: # {{[0-9]+}} "" 3 +// CHECK-RELATIVE-NEXT: # 1 "" 1 +// CHECK-RELATIVE-NEXT: # 1 "" 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 "" 1 +// CHECK-ABSOLUTE-NEXT: # 1 "" 3 +// CHECK-ABSOLUTE-NEXT: # {{[0-9]+}} "" 3 +// CHECK-ABSOLUTE-NEXT: # 1 "" 1 +// CHECK-ABSOLUTE-NEXT: # 1 "" 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