Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Properly Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors #6308

Merged
merged 8 commits into from Mar 31, 2021

Conversation

benvillalobos
Copy link
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 changed the title Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors Properly Consider WarningsAsMessages In TaskLoggingHelper's HasLoggedErrors Mar 29, 2021
@benvillalobos benvillalobos added this to In Progress in 16.10 via automation 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
Member

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
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
Member

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
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
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
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.

public bool ShouldTreatWarningAsError(string warningCode)
{
if (WarningsAsErrors == null)
// Warnings as messages overrides warnings as errors.
if (WarningsAsErrors == null || (WarningsAsMessages != null && WarningsAsMessages.Contains(warningCode)))
Copy link
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
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
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.

public bool ShouldTreatWarningAsError(string warningCode)
{
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
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)

translator.Translate(collection: ref _warningsAsMessages,
objectTranslator: (ITranslator t, ref string s) => t.Translate(ref s),
#if CLR2COMPATIBILITY
collectionFactory: count => new HashSet<string>());
Copy link
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
Member

@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.

/// </returns>
public ICollection<string> GetWarningsToBeLoggedAsErrorsByProject(BuildEventContext context)
public ICollection<string> GetWarningsAsErrors(BuildEventContext context)
Copy link
Member

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
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
16.10 automation moved this from In Progress to Done 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
16.10
Done
Development

Successfully merging this pull request may close these issues.

Tasks Should Be Aware of WarningsAsMessages
4 participants