Index: Common/ErrorHandler.cpp =================================================================== --- Common/ErrorHandler.cpp +++ Common/ErrorHandler.cpp @@ -73,15 +73,37 @@ [&](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 paranthesised. + // - 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. + // 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.str() : 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) { @@ -97,27 +119,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); @@ -126,7 +143,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 @@ -52,7 +52,8 @@ 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 @@ -327,7 +327,10 @@ 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); 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,16 @@ concatLTOPluginOptions(Vec); Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + errorHandler().VSDiagnostics = + Args.hasArg(OPT_visual_studio_diagnostics_format); + + // We need to set a Visual Studio compatible errorHandler.LogName, + //which is the diagnostic message prefix, before any calls to error() or warn(). + if (errorHandler().VSDiagnostics) + errorHandler().LogName = sys::path::stem(sys::path::filename(linkerExeName)); + 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 paranthesised 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/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -355,6 +355,9 @@ def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"