Repository navigation
[Add] C# identifier naming with escaping of reserved keywords; fixes #513 - #519
Merged
Merged
Conversation
added 3 commits
October 11, 2026 18:16
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.



Fixes #513
Changes
New in
uml4net.Extensions:string.QueryIsCSharpKeyword(): whether a string is one of the 77 reserved keywords of C#, or a word that a project has registered. Case-sensitive. A contextual keyword such asvarorvalueis a legal identifier and is not a reserved keyword, unless it has been registered.string.EscapeCSharpKeyword(): the string prefixed with@when it is a reserved keyword, or a registered word, unchanged otherwise.StringExtensions.QueryCSharpKeywords(): the list of reserved keywords, in alphabetical order. Registered words are not included.StringExtensions.RegisterAdditionalCSharpKeywords(params string[] keywords): registers words that are escaped like a reserved keyword, for examplevarandvalue. The registration is static and applies to the whole process, so it also reachesQueryCSharpIdentifierand the Handlebars helpers. A generator calls it once, before it generates. A null, empty or white-space word throws anArgumentExceptionand nothing of that call is registered; the same holds for a word that starts or ends with white space or starts with@, because such a word never equals a name. A word that is a reserved keyword or is already registered is ignored.StringExtensions.QueryAdditionalCSharpKeywords(): the registered words, in alphabetical order.StringExtensions.ClearAdditionalCSharpKeywords(): removes the registered words.INamedElement.QueryCSharpIdentifier(CSharpIdentifierCasing casing = AsModelled): the name of the element with the requested casing of its first letter, escaped when the result is a keyword. Throws anInvalidOperationExceptionthat names the xmi:id for an element without a name. Throws anArgumentOutOfRangeExceptionfor a casing that is not a value of the enumeration.CSharpIdentifierCasing:AsModelled,UpperCaseFirstLetter,LowerCaseFirstLetter.New Handlebars helpers in
uml4net.HandleBars:String.EscapeCSharpKeyword, with a string as argumentNamedElement.WriteCSharpIdentifier, with the element and optionally the casing ("AsModelled","UpperCaseFirstLetter"or"LowerCaseFirstLetter", not case-sensitive) as arguments Only these three names are accepted: a number, a list of names or a value that is not a string throws anArgumentException.uml4net itself now uses the new methods:
Property.WriteForClassescaped the name of the backing field of a containment property for six names:class,interface,namespace,object,typeandusing. It now callsEscapeCSharpKeyword, so all reserved keywords are escaped.typeis not a keyword and is no longer escaped.Operation.WriteForPOCOInterface,Operation.WriteForPOCOExtendandOperation.WriteForPOCOClassescape the name of a parameter. A parameter namedin,reforeventgave code that does not compile.The generated code of uml4net does not change: the only containment property of the UML metamodel that is named as a keyword is
object, which was escaped before and still is, and no operation parameter has such a name. The tests ofuml4net.CodeGenerator.Teststhat compare with the expected files pass.The keyword list
The list of reserved keywords is fixed in
uml4net.Extensions, which targets .NET Standard 2.0 and does not reference Roslyn.CSharpKeywordsTestFixtureinuml4net.CodeGenerator.Tests, a project that does reference Roslyn, compares it withSyntaxFacts.GetReservedKeywordKinds(), so a difference with the compiler fails a test. The compiler also reserves four undocumented keywords that start with two underscores (__arglist,__makeref,__reftype,__refvalue); these are left out.A project extends the list with
RegisterAdditionalCSharpKeywords. The registered words are kept apart from the fixed list, so the comparison with the compiler is not affected. The registration is done from C#; there is no Handlebars helper for it.What the downstream projects have today
ReservedCSharpNameMapperescapesin,out,refandvar, and maps operator symbols andtrue/falseto namesvaris escaped when SysML2.NET registers it withRegisterAdditionalCSharpKeywords. The mapping of symbols stays in SysML2.NET;trueandfalsebecome@trueand@falseunless it keeps its mapping.ReservedCSharpNameMapperasks RoslynAtwrites@in front of its argumentString.EscapeCSharpKeywordonly writes@for a keywordProperty.WritePropertyName(SysML2.NET, mycelium-forge, mycelium-sdk) andEnumerationLiteral.WriteNamedElement.WriteCSharpIdentifierwith the casing as argument. The rule that a derived property starts with a lower-case letter stays in SysML2.NET and mycelium-forge.Tests
StringExtensionsTestFixtureandNamedElementExtensionsTestFixture: nine tests for the new methods, including an element without a name, a casing outside the enumeration, the null argument, the registration of additional words with its invalid input, the read-only snapshots, and registration from eight threads at once.StringHelperTestFixtureandNamedElementHelperTestFixture: five tests for the two new helpers, including a registered word and a casing argument that is not one of the three names.OperationHelperTestFixture: a parameter that is named as a keyword is escaped by the three operation helpers; with several parameters only the keywords and the registered words are escaped.PropertyHelperTestFixture:Property.WriteForClassescapes the backing field foreventandclass, not fortype, and forvalueonly when it is registered.CSharpKeywordsTestFixture: the list against the compiler, and no contextual keyword in the list.uml4net.Extensionsanduml4net.HandleBarsare covered.Not in this pull request
DocumentationHelperwrites the name of a parameter in a<param name="...">tag without@, which is what the compiler expects there.