Skip to content

Properly Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors#6308

Merged
Forgind merged 8 commits intodotnet:mainfrom
benvillalobos:fix-warnaserr
Mar 31, 2021
Merged

Properly Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors#6308
Forgind merged 8 commits intodotnet:mainfrom
benvillalobos:fix-warnaserr

Conversation

@benvillalobos
Copy link
Copy Markdown
Member

@benvillalobos benvillalobos commented Mar 29, 2021

Fixes #6306

Context

#6174 didn't properly account for warningsaserrors and warningsasmessages together. This PR makes each taskhost aware of both WarningsAsMessages and WarningsAsErrors when telling tasks whether or not an error should be treated as a warning.

Changes Made

LoggingService and TaskLoggingContext expose a GetWarningsAsMessages to each taskhost for them to store.
No changes to IBE8 API
Taskhosts first check if a warning would be treated as a message when telling tasks whether or not a warning should be treated as a warning.

Testing

Added test cases for WarningsAsMessages getting translated properly for the OOPTHN.
Added test cases for WarningsAsErrors and WarningsAsMessages for the same warning code.
Added test case for MSBuildTreatWarningsAsErrors and WarningsAsMessages on a logged warning (WAM takes priority)

Notes

Does MSBuildTreatWarningsAsMessages exist as a property a user can set?
If WarningsAsMessages is an empty set, does it follow the rules of WarningsAsErrors? (empty set = all warnings as errors) /cc: @jeffkl

@benvillalobos benvillalobos requested a review from jeffkl March 29, 2021 01:04
@benvillalobos benvillalobos changed the title Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors Properly Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors Mar 29, 2021
Comment on lines +533 to +553
if (WarningsAsErrors == null && (_warningsAsErrorsByProject == null || !_warningsAsErrorsByProject.ContainsKey(key)))
{
return null;
}

HashSet<string> allWarningsAsErrors = new HashSet<string>();

if (WarningsAsErrors != null)
{
allWarningsAsErrors.UnionWith(WarningsAsErrors);
}

if (_warningsAsErrorsByProject != null)
{
if (_warningsAsErrorsByProject.TryGetValue(key, out ISet<string> warningsAsErrors))
{
allWarningsAsErrors.UnionWith(warningsAsErrors);
}
}

return allWarningsAsErrors;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be simplified a good bit. Also, if they're both null, we should return null and not an empty HashSet, right? Otherwise, we'd be indicating that all warnings should be counted as errors. I'd recommend something similar below. Also, since they're essentially the same method, it seems a lot easier to make them one method and call it with slightly different parameters. (You don't have to change the public API for that, since you can make the heart of the implementation private.)

Suggested change
if (WarningsAsErrors == null && (_warningsAsErrorsByProject == null || !_warningsAsErrorsByProject.ContainsKey(key)))
{
return null;
}
HashSet<string> allWarningsAsErrors = new HashSet<string>();
if (WarningsAsErrors != null)
{
allWarningsAsErrors.UnionWith(WarningsAsErrors);
}
if (_warningsAsErrorsByProject != null)
{
if (_warningsAsErrorsByProject.TryGetValue(key, out ISet<string> warningsAsErrors))
{
allWarningsAsErrors.UnionWith(warningsAsErrors);
}
}
return allWarningsAsErrors;
ISet<string> warningsAsErrors = null;
if (_warningsAsErrorsByProject is not null && _warningsAsErrorsByProject.TryGetValue(key, out warningsAsErrors)
{
if (WarningsAsErrors is not null)
{
warningsAsErrors.UnionWith(WarningsAsErrors);
}
return warningsAsErrors;
}
else
{
return WarningsAsErrors;
}

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If they're both null they'll return through the first if statement. I do like your suggested change though, less to sift through.

[Fact]
public void TaskReturnsHasLoggedErrorAndLogsWarningAsError_WarningIsAlsoMessage_BuildShouldContinue()
{
using (TestEnvironment env = TestEnvironment.Create(_output))
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't normally look critically at tests, but please make sure your tests are substantially different from each other. I diffed your first and third tests with the second one, and they differed by one and two lines respectively. That could trivially become a theory and cut out almost a hundred lines of code.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair point. Streamlined the tests, also realized the two batched builds test were covering the same scenario. Removed one of them.

Copy link
Copy Markdown
Member

@ladipro ladipro left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have added a few very minor comments, totally optional.


Assert.NotNull(deserializedConfig.WarningsAsMessages);
config.WarningsAsMessages.SequenceEqual(deserializedConfig.WarningsAsMessages, StringComparer.Ordinal).ShouldBeTrue();

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super-nit: Extra empty line.

{
if (WarningsAsErrors == null)
// Warnings as messages overrides warnings as errors.
if (WarningsAsErrors == null || (WarningsAsMessages != null && WarningsAsMessages.Contains(warningCode)))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

super-nit:

Suggested change
if (WarningsAsErrors == null || (WarningsAsMessages != null && WarningsAsMessages.Contains(warningCode)))
if (WarningsAsErrors == null || WarningsAsMessages?.Contains(warningCode) == true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, does empty WarningsAsMessages mean that all warnings should be treated as messages or does only WarningsAsErrors have this behavior?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great question, I had the same when doing this PR. It doesn't look like it. ShouldTreatWarningAsError has this check:

            if (WarningsAsErrors != null)
            {
                // Global warnings as errors apply to all projects.  If the list is empty or contains the code, the warning should be treated as an error
                //
                if (WarningsAsErrors.Count == 0 || WarningsAsErrors.Contains(warningEvent.Code))
                {
                    return true;
                }
            }

whereas ShouldTreatWarningAsMessage checks like so:

            // This only applies if the user specified /nowarn at the command-line or added the warning code through the object model
            //
            if (WarningsAsMessages?.Contains(warningEvent.Code) == true)
            {
                return true;
            }

There's no check for the count being zero, so I don't think that rule applies.

Comment thread src/MSBuild/OutOfProcTaskHostNode.cs Outdated
{
return WarningsAsErrors != null && (WarningsAsErrors.Count == 0 || WarningsAsErrors.Contains(warningCode));
// Warnings as messages overrides warnings as errors.
if (WarningsAsErrors == null || (WarningsAsMessages != null && WarningsAsMessages.Contains(warningCode)))
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (WarningsAsErrors == null || (WarningsAsMessages != null && WarningsAsMessages.Contains(warningCode)))
if (WarningsAsErrors == null || WarningsAsMessages?.Contains(warningCode) == true)

Comment thread src/Shared/TaskHostConfiguration.cs Outdated
translator.Translate(collection: ref _warningsAsMessages,
objectTranslator: (ITranslator t, ref string s) => t.Translate(ref s),
#if CLR2COMPATIBILITY
collectionFactory: count => new HashSet<string>());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see that this is the same as _warningsAsErrors a few lines above but I'm wondering if we shouldn't be constructing with StringComparer.OrdinalIgnoreCase on CLR2 as well. The constructor taking both capacity and comparer was introduced later but the one taking IEqualityComparer<T> should be available.

Copy link
Copy Markdown
Contributor

@Forgind Forgind left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for refactoring.

/// Null if no warnings should be treated as errors.
/// </returns>
public ICollection<string> GetWarningsToBeLoggedAsErrorsByProject(BuildEventContext context)
public ICollection<string> GetWarningsAsErrors(BuildEventContext context)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd still prefer for this method and the next to just call HelperMethodName(context, _warningsAsErrorsByProject, WarningsAsErrors) or the equivalent and have HelperMethodName include the real logic, but that's a little nit.

@benvillalobos
Copy link
Copy Markdown
Member Author

Not sure what this test case is and why it's failing: OutOfProcProjectInstanceBasedBuildDoesNotReloadFromDisk. Rerunning.

@benvillalobos benvillalobos added the merge-when-branch-open PRs that are approved, except that there is a problem that means we are not merging stuff right now. label Mar 31, 2021
@Forgind Forgind merged commit 80f316e into dotnet:main Mar 31, 2021
@benvillalobos benvillalobos self-assigned this Apr 1, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

merge-when-branch-open PRs that are approved, except that there is a problem that means we are not merging stuff right now.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Tasks Should Be Aware of WarningsAsMessages

4 participants