Index: Common/ErrorHandler.cpp =================================================================== --- Common/ErrorHandler.cpp +++ Common/ErrorHandler.cpp @@ -84,15 +84,39 @@ [&](ErrorInfoBase &EIB) { error(EIB.message()); }); } -void ErrorHandler::print(StringRef S, raw_ostream::Colors C) { - *ErrorOS << LogName << ": "; - if (ColorDiagnostics) { - ErrorOS->changeColor(C, true); - *ErrorOS << S; - ErrorOS->resetColor(); +void ErrorHandler::print(const Twine &Msg, const Twine &Origin, + const Twine &LevelText, + const raw_ostream::Colors Color) { + newline(ErrorOS, Msg); + + // Visual Studio diagnostics must conform to several formatting rules that + // change the output from the default: + // - line/column information must be paranthesized. + // - Origin provides the source file and line information. While Origin can be + // blank when source information is not available, we default to the lld + // executable name without the path and '.exe. extensions. The current lld + // behaviour is to use the full lld executable path, but visual studio + // interprets this as a path to the source file. Locally we store this as + // LogName, but this is a reference set with Driver->diagnosticSrcDefault(). + // + // For details on the Visual Studio diagnostics format see: + // https://blogs.msdn.microsoft.com/msbuild/2006/11/02/msbuild-visual-studio-aware-error-messages-and-message-formats/ + if (VSDiagnostics) { + std::string OriginStr = (Origin.str().empty()) ? LogName : Origin.str(); + *ErrorOS << OriginStr << " : "; } else { - *ErrorOS << S; + *ErrorOS << LogName << " : "; } + + if (ColorDiagnostics) + ErrorOS->changeColor(Color); + + *ErrorOS << LevelText; + + if (ColorDiagnostics) + ErrorOS->resetColor(); + + *ErrorOS << ": " << Msg << "\n"; } void ErrorHandler::log(const Twine &Msg) { @@ -108,27 +132,22 @@ outs().flush(); } -void ErrorHandler::warn(const Twine &Msg) { +void ErrorHandler::warn(const Twine &Msg, const Twine &Origin) { if (FatalWarnings) { - error(Msg); + error(Msg, Origin); return; } std::lock_guard Lock(Mu); - newline(ErrorOS, Msg); - print("warning: ", raw_ostream::MAGENTA); - *ErrorOS << Msg << "\n"; + print(Msg, Origin, "warning", raw_ostream::MAGENTA); } -void ErrorHandler::error(const Twine &Msg) { +void ErrorHandler::error(const Twine &Msg, const Twine &Origin) { std::lock_guard Lock(Mu); - newline(ErrorOS, Msg); if (ErrorLimit == 0 || ErrorCount < ErrorLimit) { - print("error: ", raw_ostream::RED); - *ErrorOS << Msg << "\n"; + print(Msg, Origin, "error", raw_ostream::RED); } else if (ErrorCount == ErrorLimit) { - print("error: ", raw_ostream::RED); *ErrorOS << ErrorLimitExceededMsg << "\n"; if (ExitEarly) exitLld(1); @@ -137,7 +156,7 @@ ++ErrorCount; } -void ErrorHandler::fatal(const Twine &Msg) { - error(Msg); +void ErrorHandler::fatal(const Twine &Msg, const Twine &Origin) { + error(Msg, Origin); exitLld(1); } Index: ELF/Driver.h =================================================================== --- ELF/Driver.h +++ ELF/Driver.h @@ -29,6 +29,8 @@ void main(ArrayRef Args); void addFile(StringRef Path, bool WithLOption); void addLibrary(StringRef Name); + void setDiagnosticSrcDefault(const StringRef srcDefault); + const StringRef diagnosticSrcDefault(); private: void readConfigs(llvm::opt::InputArgList &Args); @@ -36,6 +38,7 @@ void inferMachineType(); template void link(llvm::opt::InputArgList &Args); + // True if we are in --whole-archive and --no-whole-archive. bool InWholeArchive = false; @@ -46,13 +49,18 @@ bool InBinary = false; std::vector Files; + + // String for diagnostic Src parameter, when there is no source file. + // Previously stored as 'LogName' in the ErrorHandler class. + std::string DiagnosticSourceDefault = "lld"; }; // Parses command line options. class ELFOptTable : public llvm::opt::OptTable { public: ELFOptTable(); - llvm::opt::InputArgList parse(ArrayRef Argv); + llvm::opt::InputArgList parse(StringRef linkerExeName, + ArrayRef Argv); }; // Create enum with OPT_xxx values for each option in Options.td Index: ELF/Driver.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -346,7 +346,11 @@ void LinkerDriver::main(ArrayRef ArgsArr) { ELFOptTable Parser; - opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); + + // Extract the executable name for diagnostic print outs + StringRef linkerExecutableName = ArgsArr[0]; + opt::InputArgList Args = Parser.parse(linkerExecutableName, ArgsArr.slice(1)); + // Interpret this flag early because error() depends on them. errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20); @@ -1403,3 +1407,13 @@ // Write the result to the file. writeResult(); } + +void LinkerDriver::setDiagnosticSrcDefault(const StringRef srcDefault) +{ + DiagnosticSourceDefault = srcDefault; +} + +const StringRef LinkerDriver::diagnosticSrcDefault() +{ + return DiagnosticSourceDefault; +} Index: ELF/DriverUtils.cpp =================================================================== --- ELF/DriverUtils.cpp +++ ELF/DriverUtils.cpp @@ -112,7 +112,8 @@ } // Parses a given list of options. -opt::InputArgList ELFOptTable::parse(ArrayRef Argv) { +opt::InputArgList ELFOptTable::parse(StringRef linkerExeName, + ArrayRef Argv) { // Make InputArgList from string vectors. unsigned MissingIndex; unsigned MissingCount; @@ -129,7 +130,20 @@ concatLTOPluginOptions(Vec); Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + // We need to set a Visual Studio compatible default source, + // which is the diagnostic message prefix or 'origin', before any calls to + // error(), warn() or fatal(). + if (Args.hasArg(OPT_visual_studio_diagnostics_format)) { + Driver->setDiagnosticSrcDefault( + sys::path::stem(sys::path::filename(linkerExeName))); + errorHandler().VSDiagnostics = true; + } else { + Driver->setDiagnosticSrcDefault(linkerExeName); + } + + errorHandler().LogName = Driver->diagnosticSrcDefault(); handleColorDiagnostics(Args); + if (MissingCount) error(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -81,6 +81,11 @@ // Concatenates arguments to construct a string representing an error location. static std::string createFileLineMsg(StringRef Path, unsigned Line) { + // Visual Studio diagnostics format stipulates paranthesized line and + // column information. + if (errorHandler().VSDiagnostics) + return Path.str() + " (" + std::to_string(Line) + ")"; + std::string Filename = path::filename(Path); std::string Lineno = ":" + std::to_string(Line); if (Filename == Path) Index: ELF/InputSection.cpp =================================================================== --- ELF/InputSection.cpp +++ ELF/InputSection.cpp @@ -243,10 +243,15 @@ } // This function is intended to be used for constructing an error message. -// The returned message looks like this: +// By default, the returned message looks like this: // // foo.c:42 (/home/alice/possibly/very/long/path/foo.c:42) // +// If Visual Studio diagnostics compatibility is enabled the format +// is different: +// +// /home/alice/possibly/very/long/path/foo.c (42) +// // Returns an empty string if there's no way to get line info. std::string InputSectionBase::getSrcMsg(const Symbol &Sym, uint64_t Offset) { // Synthetic sections don't have input files. @@ -703,7 +708,7 @@ ": has non-ABS relocation " + toString(Type) + " against symbol '" + toString(Sym) + "'"; if (Expr != R_PC) { - error(Msg); + error(Msg, errorHandler().LogName); return; } @@ -714,7 +719,7 @@ // relocations without any errors and relocate them as if they were at // address 0. For bug-compatibilty, we accept them with warnings. We // know Steel Bank Common Lisp as of 2018 have this bug. - warn(Msg); + warn(Msg, errorHandler().LogName); Target->relocateOne(BufLoc, Type, SignExtend64(Sym.getVA(Addend - Offset))); continue; Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -355,6 +355,9 @@ def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"