Skip to content

Log warnings for skipped STR resource keys instead of failing the build#13291

Open
OvesN wants to merge 5 commits intodotnet:mainfrom
OvesN:dev/veronikao/dev/str-skip-property-warnings
Open

Log warnings for skipped STR resource keys instead of failing the build#13291
OvesN wants to merge 5 commits intodotnet:mainfrom
OvesN:dev/veronikao/dev/str-skip-property-warnings

Conversation

@OvesN
Copy link

@OvesN OvesN commented Feb 25, 2026

Fixes #13061

Context

The GenerateResource task with MSBuild:Compile failed the entire build when a resource key (e.g. containing = or `) could not be converted to a valid C# identifier. In contrast, ResXFileCodeGenerator silently skipped such entries. This aligns the behavior by skipping invalid keys with warnings instead of failing.

Changes Made

  • Added warning messages MSB3827–MSB3830 for resource keys that are skipped during STR property generation.
  • STR files are now still generated even when some keys cannot produce valid identifiers.

Testing

Added unit tests verifying that invalid resource keys emit the expected warnings and valid keys still generate STR properties.

Notes

ResXFileCodeGenerator in VS silently skips invalid keys with no diagnostic. A separate VS issue has been created to add warnings there as well.

…stead of failing the build, ensuring STR files are still generated when possible
Copilot AI review requested due to automatic review settings February 25, 2026 07:21
@OvesN
Copy link
Author

OvesN commented Feb 25, 2026

@dotnet-policy-service agree company="Microsoft"

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR changes the behavior of the GenerateResource task when processing resource keys that cannot be converted to valid C# identifiers in strongly typed resource (STR) files. Instead of failing the entire build with errors, the task now logs warnings and skips problematic keys while still generating the STR file with valid properties. This aligns MSBuild's MSBuild:Compile generator behavior with Visual Studio's ResXFileCodeGenerator, which silently skips invalid keys.

Changes:

  • Modified StronglyTypedResourceBuilder to return structured warning information (key, reason, extraArg) instead of error strings
  • Added four new warning codes: MSB3827 (reserved name), MSB3828 (invalid identifier), MSB3829 (name collision), MSB3830 (void type)
  • Updated GenerateResource.cs to log warnings instead of errors and mark STR generation as successful even when some properties are skipped
  • Added comprehensive unit tests covering reserved names, invalid identifiers, name collisions, and the no-warnings case

Reviewed changes

Copilot reviewed 17 out of 17 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/Tasks/system.design/stronglytypedresourcebuilder.cs Changed error tracking from string array to tuple array with structured warning data (key, reason, extraArg); updated all error-adding sites to include reason codes
src/Tasks/GenerateResource.cs Changed from error logging to warning logging for STR property generation issues; task now succeeds even with warnings
src/Tasks/Resources/Strings.resx Added four new warning message strings: MSB3827, MSB3828, MSB3829, MSB3830
src/Tasks/Resources/xlf/*.xlf Added localization placeholders (marked as "new") for the four new warning messages across all language files
src/Tasks.UnitTests/ResourceHandling/GenerateResource_Tests.cs Added four comprehensive tests covering reserved names, invalid identifiers, name collisions, and the no-warnings case

@OvesN OvesN self-assigned this Feb 25, 2026
@OvesN OvesN requested a review from JanProvaznik February 25, 2026 07:27
OvesN and others added 2 commits February 25, 2026 08:30
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
The input dictionary uses StringComparer.InvariantCultureIgnoreCase,
so pure case-insensitive collisions (e.g. "MyKey" vs "mykey") cannot
exist in the input.
Copy link
Member

@JanProvaznik JanProvaznik left a comment

Choose a reason for hiding this comment

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

I would suggest partitioning the logic so that the builder tries to create the file and notes down what it had to skip to do that and for what reason (with enum for reasons), and then the Task decides how to log that information.

Architecture fixes - StronglyTypedResourceBuilder now produces list of records that represents warning, logic to choose resource key for message loggind moved to  GenFixes after code review. StronglyTypedResourceBuilder now produces a list of records representing warnings; the logic to choose the resource key for message logging has been moved to GenerateResource. Added a HashSet to optimize searching for already-warned keys.erateResource.
Added hashset to optimize sraching of already warned keys
Copy link
Member

@JanProvaznik JanProvaznik left a comment

Choose a reason for hiding this comment

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

looking better, still some code quality comments and a logical oversight, your extra else branch was actually correct

StronglyTypedResourceBuilder.ResourceWarningReason.NameCollision => "GenerateResource.STRPropertySkippedNameCollision",
_ => throw new InvalidOperationException(),
};
_logger.LogWarningWithCodeFromResources(messageKey, warning.Key, warning.ExtraArg);
Copy link
Member

Choose a reason for hiding this comment

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

don't pass the second argument to those resources that don't take it

Copy link
Member

Choose a reason for hiding this comment

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

I suppose that requires switching what logging method is called instead of switching to retrive key

private const int DocCommentLengthThreshold = 512;

// Describes a resource key that could not be converted to a valid STR property.
internal enum ResourceWarningReason
Copy link
Member

Choose a reason for hiding this comment

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

I'm not a fan of the "Warning" naming in general, that we warn about this is concern of the presentation layer (the Task), can it be something like KeySkippedReason/unmatchable something? because that's what happens, no?

private const String ResMgrPropertyName = "ResourceManager";
private const String CultureInfoFieldName = "resourceCulture";
private const String CultureInfoPropertyName = "Culture";
private static readonly String reservedNames = String.Join(", ", ResMgrPropertyName, CultureInfoPropertyName);
Copy link
Member

Choose a reason for hiding this comment

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

why not const?
nit: i'd write just $"{ResMgrPropertyName}, {CultureInfoPropertyName}"

NameCollision,
}

internal sealed record ResourceWarning(string Key, ResourceWarningReason Reason, string ExtraArg);
Copy link
Member

Choose a reason for hiding this comment

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

ExtraArg is not clear, name it more clearly what is stored there

feel free to have multiple properties and fill the one that's relevant,
use string? to express the intent that null is valid

errors.Add(entry.Key);

// Warn about the current entry.
warnings.Add(new ResourceWarning(entry.Key, ResourceWarningReason.NameCollision, alreadyAddedOriginalName));
Copy link
Member

Choose a reason for hiding this comment

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

alreadyAddedOriginalName is null when two valid identifiers
collide only by case (e.g. "MyRes" vs "myRes") — neither goes through fixup,
so reverseFixupTable has no entry and TryGetValue on line 824 doesn't set it.
The warning renders as a name collision exists with resource "". Restore the
IndexOfKey lookup from the earlier revision to retrieve the actual stored key
name

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

GenerateResource task fails with a resource string key/name containing an equals or back tick character in a resources file (resx)

3 participants