Skip to content

Commit

Permalink
Merge pull request #1804 from microsoft/namespacesFix
Browse files Browse the repository at this point in the history
[Dotnet] Fix for imports/usings reserved names replacement
  • Loading branch information
andrueastman authored Aug 31, 2022
2 parents f63adb6 + a9542e8 commit 57ad7e4
Show file tree
Hide file tree
Showing 6 changed files with 75 additions and 9 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Removed/fixed passing in the current instance to fields deserializers in Ruby. [#1663](https://github.com/microsoft/kiota/issues/1663)
- Fix issue with duplicate variable declaration in command handlers (Shell)
- Update namespace qualification algorithm (helps in resolving when a type name appears in multiple namespaces) to use case insensitive string comparison (CSharp).
- Fix an issue where namespace reserved name replacement would not include replacing import names in the declared areas in CSharp. [#1799](https://github.com/microsoft/kiota/issues/1799)

## [0.4.0] - 2022-08-18

Expand Down
3 changes: 2 additions & 1 deletion src/Kiota.Builder/Refiners/CSharpRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ public override void Refine(CodeNamespace generatedCode)
new CSharpReservedNamesProvider(), x => $"@{x.ToFirstCharacterUpperCase()}",
new HashSet<Type>{ typeof(CodeClass), typeof(ClassDeclaration), typeof(CodeProperty), typeof(CodeUsing), typeof(CodeNamespace), typeof(CodeMethod), typeof(CodeEnum) }
);
// Replace the reserved types
// Replace the reserved types and namespace segments
ReplaceReservedModelTypes(generatedCode, new CSharpReservedTypesProvider(), x => $"{x}Object");
ReplaceReservedNamespaceTypeNames(generatedCode, new CSharpReservedTypesProvider(), static x => $"{x}Namespace");
DisambiguatePropertiesWithClassNames(generatedCode);
AddConstructorsForDefaultValues(generatedCode, false);
AddSerializationModulesImport(generatedCode);
Expand Down
34 changes: 29 additions & 5 deletions src/Kiota.Builder/Refiners/CommonLanguageRefiner.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Kiota.Builder.Extensions;
Expand Down Expand Up @@ -165,7 +165,10 @@ protected static void AddConstructorsForDefaultValues(CodeElement current, bool
}

protected static void ReplaceReservedModelTypes(CodeElement current, IReservedNamesProvider provider, Func<string, string> replacement) =>
ReplaceReservedNames(current, provider, replacement, shouldReplaceCallback: (codeElement) => codeElement is CodeClass || codeElement is CodeMethod || codeElement is CodeProperty);
ReplaceReservedNames(current, provider, replacement,codeElementExceptions: new HashSet<Type>{typeof(CodeNamespace)} ,shouldReplaceCallback: (codeElement) => codeElement is CodeClass || codeElement is CodeMethod || codeElement is CodeProperty);

protected static void ReplaceReservedNamespaceTypeNames(CodeElement current, IReservedNamesProvider provider, Func<string, string> replacement) =>
ReplaceReservedNames(current, provider, replacement, shouldReplaceCallback: (codeElement) => codeElement is CodeNamespace || codeElement is CodeClass);

protected static void ReplaceReservedNames(CodeElement current, IReservedNamesProvider provider, Func<string, string> replacement, HashSet<Type> codeElementExceptions = null, Func<CodeElement, bool> shouldReplaceCallback = null) {
var shouldReplace = shouldReplaceCallback?.Invoke(current) ?? true;
Expand All @@ -174,7 +177,11 @@ protected static void ReplaceReservedNames(CodeElement current, IReservedNamesPr
isNotInExceptions &&
shouldReplace &&
currentClass.StartBlock is ClassDeclaration currentDeclaration) {
ReplaceReservedCodeUsings(currentDeclaration, provider, replacement);
ReplaceReservedCodeUsingDeclarationNames(currentDeclaration, provider, replacement);
// if we are don't have a CodeNamespace exception, the namespace segments are also being replaced
// in the CodeNamespace if-block so we also need to update the using references
if (!codeElementExceptions?.Contains(typeof(CodeNamespace)) ?? true)
ReplaceReservedCodeUsingNamespaceSegmentNames(currentDeclaration, provider, replacement);
if(provider.ReservedNames.Contains(currentDeclaration.Inherits?.Name))
currentDeclaration.Inherits.Name = replacement(currentDeclaration.Inherits.Name);
} else if(current is CodeNamespace currentNamespace &&
Expand Down Expand Up @@ -222,7 +229,7 @@ currentProperty.Type is CodeType propertyType &&
}
current.Name = replacement.Invoke(current.Name);
}

CrawlTree(current, x => ReplaceReservedNames(x, provider, replacement, codeElementExceptions, shouldReplaceCallback));
}
private static void ReplaceReservedEnumNames(CodeEnum currentEnum, IReservedNamesProvider provider, Func<string, string> replacement)
Expand All @@ -235,8 +242,9 @@ private static void ReplaceReservedEnumNames(CodeEnum currentEnum, IReservedName
x.Name = replacement.Invoke(x.Name);
});
}
private static void ReplaceReservedCodeUsings(ClassDeclaration currentDeclaration, IReservedNamesProvider provider, Func<string, string> replacement)
private static void ReplaceReservedCodeUsingDeclarationNames(ClassDeclaration currentDeclaration, IReservedNamesProvider provider, Func<string, string> replacement)
{
// replace the using declaration type names that are internally defined by the generator
currentDeclaration.Usings
.Select(x => x.Declaration)
.Where(x => x != null && !x.IsExternal)
Expand All @@ -246,6 +254,22 @@ private static void ReplaceReservedCodeUsings(ClassDeclaration currentDeclaratio
x.Name = replacement.Invoke(x.Name);
});
}

private static void ReplaceReservedCodeUsingNamespaceSegmentNames(ClassDeclaration currentDeclaration, IReservedNamesProvider provider, Func<string, string> replacement)
{
// replace the using namespace segment names that are internally defined by the generator
currentDeclaration.Usings
.Where(static codeUsing => codeUsing is { IsExternal: false })
.Select(static codeUsing => new Tuple<CodeUsing, string[]>(codeUsing, codeUsing.Name.Split('.')))
.Where(tuple => tuple.Item2.Any(x => provider.ReservedNames.Contains(x)))
.ToList()
.ForEach(tuple => {
tuple.Item1.Name = tuple.Item2.Select(x => provider.ReservedNames.Contains(x) ? replacement.Invoke(x) : x)
.Aggregate(static (x, y) => $"{x}.{y}");
});
}


private static void ReplaceReservedNamespaceSegments(CodeNamespace currentNamespace, IReservedNamesProvider provider, Func<string, string> replacement)
{
var segments = currentNamespace.Name.Split('.');
Expand Down
1 change: 1 addition & 0 deletions src/Kiota.Builder/Refiners/ShellRefiner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public override void Refine(CodeNamespace generatedCode)
);
// Replace the reserved types
ReplaceReservedModelTypes(generatedCode, new CSharpReservedTypesProvider(), x => $"{x}Object");
ReplaceReservedNamespaceTypeNames(generatedCode, new CSharpReservedTypesProvider(), static x => $"{x}Namespace");
AddParentClassToErrorClasses(
generatedCode,
"ApiException",
Expand Down
6 changes: 3 additions & 3 deletions src/Kiota.Builder/Writers/CSharp/CSharpConventionService.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using Kiota.Builder.Extensions;
Expand Down Expand Up @@ -101,10 +101,10 @@ private string TranslateTypeAndAvoidUsingNamespaceSegmentNames(CodeType currentT
var duplicateMappingTypes = discriminatorMethod.DiscriminatorMappings.Select(x => x.Value).OfType<CodeType>()
.Where(x => !DoesTypeExistsInSameNamesSpaceAsTarget(x, targetElement))
.Select(x => x.Name)
.GroupBy(x => x)
.GroupBy(static x => x, StringComparer.OrdinalIgnoreCase)
.Where(group => group.Count() > 1)
.Select(x => x.Key);

parentElements.AddRange(duplicateMappingTypes);
}
}
Expand Down
39 changes: 39 additions & 0 deletions tests/Kiota.Builder.Tests/Refiners/CSharpLanguageRefinerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,45 @@ public void DoesNotEscapesReservedKeywordsForClassOrPropertyKind() {
Assert.DoesNotContain("@", property.Name); // classname will be capitalized
}
[Fact]
public void EscapesReservedKeywordsForReservedNamespaceNameSegments() {
var subNS = root.AddNamespace($"{root.Name}.task"); // otherwise the import gets trimmed
var requestBuilder = subNS.AddClass(new CodeClass {
Name = "tasksRequestBuilder",
Kind = CodeClassKind.RequestBuilder,
}).First();

var indexerCodeType = new CodeType { Name = "taskItemRequestBuilder" };
var indexer = new CodeIndexer {
Name = "idx",
SerializationName = "id",
IndexType = new CodeType {
Name = "string",
},
ReturnType = indexerCodeType
};
requestBuilder.SetIndexer(indexer);


var itemSubNamespace = root.AddNamespace($"{subNS.Name}.item"); // otherwise the import gets trimmed
var itemRequestBuilder = itemSubNamespace.AddClass(new CodeClass {
Name = "taskItemRequestBuilder",
Kind = CodeClassKind.RequestBuilder,
}).First();

var requestExecutor = itemRequestBuilder.AddMethod(new CodeMethod() {
Name = "get",
Kind = CodeMethodKind.IndexerBackwardCompatibility,
ReturnType = new CodeType {
Name = "String"
},
}).First();

ILanguageRefiner.Refine(new GenerationConfiguration { Language = GenerationLanguage.CSharp }, root);

Assert.Contains("TaskNamespace", subNS.Name);
Assert.Contains("TaskNamespace", itemSubNamespace.Name);
}
[Fact]
public void ConvertsUnionTypesToWrapper() {
var model = root.AddClass(new CodeClass {
Name = "model",
Expand Down

0 comments on commit 57ad7e4

Please sign in to comment.