Repository: dotnet/roslyn
File: src/Compilers/Core/MSBuildTask/ManagedToolTask.cs (C#)

Code before:
        /// arguments in the response file.
        /// </summary>
        /// <remarks>
        /// This will include target specific arguments like 'exec'
        /// </remarks>
        internal string GenerateCommandLineContents() => GenerateCommandLineCommands();

        /// <summary>
        /// Generate the arguments to pass via a response file. 
        /// </summary>
        /// <remarks>
        /// This will be the same value whether the build occurs on .NET Core or .NET Framework. 
        /// </remarks>
        internal string GenerateResponseFileContents() => GenerateResponseFileCommands();

        /// <summary>
        /// This generates the path to the executable that is directly ran.
        /// This could be the executable apphost or a runtime such as dotnet.
        /// </summary>
        protected sealed override string GenerateFullPathToTool()
        {
            if (UsingBuiltinTool)
            {
                // Even if we return full path to tool as "C:\Program Files\dotnet\dotnet.exe" for example,
                // an MSBuild logic (caller of this function) can turn that into "C:\Program Files\dotnet\csc.exe" if `ToolExe` is set explicitly to "csc.exe".
                // Resetting `ToolExe` to `null` skips that logic. That is a correct thing to do wince we already checked `UsingBuiltinTool`
                // which means `ToolExe` is not really overridden by user (yes, the user sets it but basically to its default value).
                ToolExe = null;

                return UseAppHost ? PathToBuiltInTool : RuntimeHostInfo.GetDotNetPathOrDefault();
            }

            return Path.Combine(ToolPath ?? "", ToolExe);
        }

        protected abstract string ToolNameWithoutExtension { get; }

        protected abstract void AddCommandLineCommands(CommandLineBuilderExtension commandLine);

        protected abstract void AddResponseFileCommands(CommandLineBuilderExtension commandLine);

        /// <summary>
        /// This is the file name of the builtin tool that will be executed.
        /// </summary>
        /// <remarks>
        /// ToolName is only used in cases where <see cref="UsingBuiltinTool"/> returns true.
        /// It returns the name of the managed assembly, which might not be the path returned by
        /// <see cref="GenerateFullPathToTool"/>, which can return the path to e.g. the dotnet executable.
        /// </remarks>
        protected sealed override string ToolName
        {

Review comment (suggestion):

```suggestion
                // Resetting `ToolExe` to `null` skips that logic. That is a correct thing to do since we already checked `UsingBuiltinTool`
```

Code after:
        /// arguments in the response file.
        /// </summary>
        /// <remarks>
        /// This will include target specific arguments like 'exec'
        /// </remarks>
        internal string GenerateCommandLineContents() => GenerateCommandLineCommands();

        /// <summary>
        /// Generate the arguments to pass via a response file. 
        /// </summary>
        /// <remarks>
        /// This will be the same value whether the build occurs on .NET Core or .NET Framework. 
        /// </remarks>
        internal string GenerateResponseFileContents() => GenerateResponseFileCommands();

        /// <summary>
        /// This generates the path to the executable that is directly ran.
        /// This could be the executable apphost or a runtime such as dotnet.
        /// </summary>
        protected sealed override string GenerateFullPathToTool()
        {
            if (UsingBuiltinTool)
            {
                // Even if we return full path to tool as "C:\Program Files\dotnet\dotnet.exe" for example,
                // an MSBuild logic (caller of this function) can turn that into "C:\Program Files\dotnet\csc.exe" if `ToolExe` is set explicitly to "csc.exe".
                // Resetting `ToolExe` to `null` skips that logic. That is a correct thing to do since we already checked `UsingBuiltinTool`
                // which means `ToolExe` is not really overridden by user (yes, the user sets it but basically to its default value).
                ToolExe = null;

                return UseAppHost ? PathToBuiltInTool : RuntimeHostInfo.GetDotNetPathOrDefault();
            }

            return Path.Combine(ToolPath ?? "", ToolExe);
        }

        protected abstract string ToolNameWithoutExtension { get; }

        protected abstract void AddCommandLineCommands(CommandLineBuilderExtension commandLine);

        protected abstract void AddResponseFileCommands(CommandLineBuilderExtension commandLine);

        /// <summary>
        /// This is the file name of the builtin tool that will be executed.
        /// </summary>
        /// <remarks>
        /// ToolName is only used in cases where <see cref="UsingBuiltinTool"/> returns true.
        /// It returns the name of the managed assembly, which might not be the path returned by
        /// <see cref="GenerateFullPathToTool"/>, which can return the path to e.g. the dotnet executable.
        /// </remarks>
        protected sealed override string ToolName
        {