Skip to content

Commit ffdfdd9

Browse files
author
Linh Phan
committed
2 parents 2ffa43b + 03b9633 commit ffdfdd9

19 files changed

Lines changed: 961 additions & 9 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
changeKind: fix
3+
packages:
4+
- "@typespec/http-client-python"
5+
---
6+
7+
Contain emitter changes when used with `generation-subdir` to solely within that subdirectory

packages/http-client-csharp/generator/Microsoft.TypeSpec.Generator.ClientModel/src/Providers/ClientProvider.cs

Lines changed: 163 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1256,16 +1256,29 @@ protected override ScmMethodProvider[] BuildMethods()
12561256

12571257
protected sealed override IReadOnlyList<MethodProvider> BuildMethodsForBackCompatibility(IEnumerable<MethodProvider> originalMethods)
12581258
{
1259+
List<MethodProvider> materializedMethods = [.. originalMethods];
1260+
12591261
if (LastContractView?.Methods == null || LastContractView.Methods.Count == 0)
12601262
{
1261-
return [.. originalMethods];
1263+
return materializedMethods;
12621264
}
12631265

1264-
var currentMethodSignatures = BuildCurrentMethodSignatures(originalMethods);
1266+
var currentMethodSignatures = BuildCurrentMethodSignatures(materializedMethods);
1267+
1268+
ProcessBackCompatForParameterReordering(materializedMethods, currentMethodSignatures);
1269+
ProcessBackCompatForNewOptionalParameters(materializedMethods, currentMethodSignatures);
1270+
1271+
return materializedMethods;
1272+
}
1273+
1274+
private void ProcessBackCompatForParameterReordering(
1275+
IList<MethodProvider> materializedMethods,
1276+
Dictionary<MethodSignature, MethodProvider> currentMethodSignatures)
1277+
{
12651278
var updatedSignatureToOriginal = new Dictionary<MethodSignature, MethodSignature>(MethodSignature.MethodSignatureComparer);
12661279
var methodsWithReorderedParams = new List<MethodProvider>();
12671280

1268-
foreach (var previousMethod in LastContractView.Methods)
1281+
foreach (var previousMethod in LastContractView!.Methods)
12691282
{
12701283
if (!ShouldProcessMethodForBackCompat(previousMethod.Signature, currentMethodSignatures))
12711284
{
@@ -1290,10 +1303,8 @@ protected sealed override IReadOnlyList<MethodProvider> BuildMethodsForBackCompa
12901303

12911304
if (methodsWithReorderedParams.Count > 0)
12921305
{
1293-
UpdateConvenienceMethodsForBackCompat(originalMethods, methodsWithReorderedParams, updatedSignatureToOriginal);
1306+
UpdateConvenienceMethodsForBackCompat(materializedMethods, methodsWithReorderedParams, updatedSignatureToOriginal);
12941307
}
1295-
1296-
return [.. originalMethods];
12971308
}
12981309

12991310
private Dictionary<MethodSignature, MethodProvider> BuildCurrentMethodSignatures(IEnumerable<MethodProvider> originalMethods)
@@ -1748,5 +1759,151 @@ private static void UpdateXmlDocProviderForParamReorder(
17481759
xmlDocs.Update(parameters: reorderedParamDocs);
17491760
}
17501761
}
1762+
1763+
private void ProcessBackCompatForNewOptionalParameters(
1764+
List<MethodProvider> methods,
1765+
Dictionary<MethodSignature, MethodProvider> currentMethodSignatures)
1766+
{
1767+
var currentMethodsByName = new Dictionary<string, List<MethodProvider>>();
1768+
foreach (var method in currentMethodSignatures.Values)
1769+
{
1770+
if (method is ScmMethodProvider { Kind: ScmMethodKind.CreateRequest })
1771+
{
1772+
continue;
1773+
}
1774+
1775+
if (!currentMethodsByName.TryGetValue(method.Signature.Name, out var list))
1776+
{
1777+
list = [];
1778+
currentMethodsByName[method.Signature.Name] = list;
1779+
}
1780+
list.Add(method);
1781+
}
1782+
1783+
foreach (var previousMethod in LastContractView!.Methods)
1784+
{
1785+
var previousSignature = previousMethod.Signature;
1786+
1787+
if (!previousSignature.Modifiers.HasFlag(MethodSignatureModifiers.Public) &&
1788+
!previousSignature.Modifiers.HasFlag(MethodSignatureModifiers.Protected))
1789+
{
1790+
continue;
1791+
}
1792+
1793+
if (currentMethodSignatures.ContainsKey(previousSignature) ||
1794+
!currentMethodsByName.TryGetValue(previousSignature.Name, out var candidates))
1795+
{
1796+
continue;
1797+
}
1798+
1799+
ScmMethodProvider? matchedCurrent = null;
1800+
foreach (var candidate in candidates)
1801+
{
1802+
if (candidate is ScmMethodProvider { Kind: ScmMethodKind.Convenience or ScmMethodKind.Protocol } scmCandidate &&
1803+
HasNewOptionalNonBodyParametersOnly(previousSignature, scmCandidate.Signature))
1804+
{
1805+
matchedCurrent = scmCandidate;
1806+
break;
1807+
}
1808+
}
1809+
1810+
if (matchedCurrent is null)
1811+
{
1812+
continue;
1813+
}
1814+
1815+
var overload = BuildBackCompatOverloadForNewOptionalParameters(previousMethod, matchedCurrent);
1816+
if (overload == null || !currentMethodSignatures.TryAdd(overload.Signature, overload))
1817+
{
1818+
continue;
1819+
}
1820+
1821+
methods.Add(overload);
1822+
CodeModelGenerator.Instance.Emitter.Debug(
1823+
$"Added back-compat overload for '{Name}.{previousSignature.Name}' to handle new optional parameter(s) introduced relative to the last contract.",
1824+
BackCompatibilityChangeCategory.SvcMethodNewOptionalParameterOverloadAdded);
1825+
}
1826+
}
1827+
1828+
// Returns true when currentSignature contains all parameters of previousSignature in the same
1829+
// relative order, every "extra" parameter is optional, and none of the extras are body parameters.
1830+
private static bool HasNewOptionalNonBodyParametersOnly(
1831+
MethodSignature previousSignature,
1832+
MethodSignature currentSignature)
1833+
{
1834+
if (currentSignature.Parameters.Count <= previousSignature.Parameters.Count)
1835+
{
1836+
return false;
1837+
}
1838+
1839+
if (previousSignature.ReturnType is null
1840+
? currentSignature.ReturnType is not null
1841+
: !previousSignature.ReturnType.AreNamesEqual(currentSignature.ReturnType))
1842+
{
1843+
return false;
1844+
}
1845+
1846+
// Walk current parameters and ensure previous parameters appear in the same relative order
1847+
// (matched by variable name and type), with every "extra" parameter being optional and non-body.
1848+
int previousIndex = 0;
1849+
for (int currentIndex = 0; currentIndex < currentSignature.Parameters.Count; currentIndex++)
1850+
{
1851+
var currentParam = currentSignature.Parameters[currentIndex];
1852+
1853+
if (previousIndex < previousSignature.Parameters.Count)
1854+
{
1855+
var previousParam = previousSignature.Parameters[previousIndex];
1856+
if (currentParam.Name.ToVariableName() == previousParam.Name.ToVariableName() &&
1857+
currentParam.Type.AreNamesEqual(previousParam.Type))
1858+
{
1859+
previousIndex++;
1860+
continue;
1861+
}
1862+
}
1863+
1864+
if (currentParam.DefaultValue is null)
1865+
{
1866+
return false;
1867+
}
1868+
1869+
if (currentParam.Location == ParameterLocation.Body)
1870+
{
1871+
return false;
1872+
}
1873+
}
1874+
1875+
return previousIndex == previousSignature.Parameters.Count;
1876+
}
1877+
1878+
private ScmMethodProvider? BuildBackCompatOverloadForNewOptionalParameters(
1879+
MethodProvider previousMethod,
1880+
ScmMethodProvider currentMethod)
1881+
{
1882+
var previousSignature = previousMethod.Signature;
1883+
var currentSignature = currentMethod.Signature;
1884+
1885+
var previousParamsByName = new Dictionary<string, ParameterProvider>();
1886+
foreach (var p in previousSignature.Parameters)
1887+
{
1888+
previousParamsByName.TryAdd(p.Name, p);
1889+
}
1890+
1891+
var arguments = new List<ValueExpression>(currentSignature.Parameters.Count);
1892+
foreach (var currentParam in currentSignature.Parameters)
1893+
{
1894+
ValueExpression value = previousParamsByName.TryGetValue(currentParam.Name, out var prevParam)
1895+
? prevParam
1896+
: (currentParam.DefaultValue ?? Default);
1897+
arguments.Add(PositionalReference(currentParam.Name, value));
1898+
}
1899+
1900+
return new ScmMethodProvider(
1901+
signature: MethodSignatureHelper.BuildBackCompatMethodSignature(previousSignature, hideMethod: true),
1902+
bodyStatements: Return(This.Invoke(currentSignature.Name, arguments)),
1903+
enclosingType: this,
1904+
methodKind: currentMethod.Kind,
1905+
xmlDocProvider: previousMethod.XmlDocs,
1906+
serviceMethod: currentMethod.ServiceMethod);
1907+
}
17511908
}
17521909
}

0 commit comments

Comments
 (0)