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()) ? "lld" : 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.cpp =================================================================== --- ELF/Driver.cpp +++ ELF/Driver.cpp @@ -73,7 +73,9 @@ bool elf::link(ArrayRef Args, bool CanExitEarly, raw_ostream &Error) { - errorHandler().LogName = Args[0]; + errorHandler().LogName = errorHandler().VSDiagnostics + ? sys::path::stem(sys::path::filename(Args[0])) + : Args[0]; errorHandler().ErrorLimitExceededMsg = "too many errors emitted, stopping now (use " "-error-limit=0 to see all errors)"; @@ -327,11 +329,13 @@ void LinkerDriver::main(ArrayRef ArgsArr) { ELFOptTable Parser; - opt::InputArgList Args = Parser.parse(ArgsArr.slice(1)); + opt::InputArgList Args = Parser.parse(ArgsArr); // Interpret this flag early because error() depends on them. errorHandler().ErrorLimit = args::getInteger(Args, OPT_error_limit, 20); + + // Handle -help if (Args.hasArg(OPT_help)) { printHelp(); @@ -675,6 +679,9 @@ Args.hasFlag(OPT_fatal_warnings, OPT_no_fatal_warnings, false); ThreadsEnabled = Args.hasFlag(OPT_threads, OPT_no_threads, true); + errorHandler().VSDiagnostics = + Args.hasArg(OPT_visual_studio_diagnostics_format); + Config->AllowMultipleDefinition = Args.hasFlag(OPT_allow_multiple_definition, OPT_no_allow_multiple_definition, false) || Index: ELF/DriverUtils.cpp =================================================================== --- ELF/DriverUtils.cpp +++ ELF/DriverUtils.cpp @@ -123,6 +123,18 @@ // --rsp-quoting. opt::InputArgList Args = this->ParseArgs(Vec, MissingIndex, MissingCount); + // We need to set errorHandler.LogName, which is the diagnostic message + // prefix, before any calls to error() or warn(). Also strip the .exe + // extension as this can cause VS to interpret the filename as a source file + // and break VS compatibily + errorHandler().LogName = Args.hasArg(OPT_visual_studio_diagnostics_format) + ? sys::path::stem(sys::path::filename(Argv[0])) + : Argv[0]; + + // remove executable name from the argument vectorso it is not later + // interpreted as an input object file + Vec.erase(Vec.begin()); + // Expand response files (arguments in the form of @) // and then parse the argument again. cl::ExpandResponseFiles(Saver, getQuotingStyle(Args), Vec); @@ -130,6 +142,7 @@ Args = this->ParseArgs(Vec, MissingIndex, MissingCount); handleColorDiagnostics(Args); + if (MissingCount) error(Twine(Args.getArgString(MissingIndex)) + ": missing argument"); Index: ELF/InputFiles.cpp =================================================================== --- ELF/InputFiles.cpp +++ ELF/InputFiles.cpp @@ -81,11 +81,17 @@ // Concatenates arguments to construct a string representing an error location. static std::string createFileLineMsg(StringRef Path, unsigned Line) { - std::string Filename = path::filename(Path); - std::string Lineno = ":" + std::to_string(Line); - if (Filename == Path) - return Filename + Lineno; - return Filename + Lineno + " (" + Path.str() + Lineno + ")"; + // Visual Studio diagnostics format stipulates paranthesised line and + // column information. + if (errorHandler().VSDiagnostics) { + return Path.str() + " " + "(" + std::to_string(Line) + ")"; + } else { + std::string Filename = path::filename(Path); + std::string Lineno = ":" + std::to_string(Line); + if (Filename == Path) + return Filename + Lineno; + return Filename + Lineno + " (" + Path.str() + Lineno + ")"; + } } template Index: ELF/Options.td =================================================================== --- ELF/Options.td +++ ELF/Options.td @@ -359,6 +359,9 @@ def z: JoinedOrSeparate<["-"], "z">, MetaVarName<"