feat(analyzers): regenerate Orleans contracts project-wide - #10869
Conversation
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
test/Orleans.Analyzers.Tests/GrainInterfaceVersionAnalyzerTest.cs — CreateProjectWithAdditionalFilesForCodeFix can throw when configuredContractsPath is a relative… |
What changed in this PR
Adds deterministic, project-/solution-wide regeneration support for OrleansContracts.txt in the Orleans analyzer/code-fix pipeline, aligning identity parsing/matching with runtime conventions and improving developer guidance around wire-compatibility.
Changes:
- Introduces a Regenerate OrleansContracts.txt code fix with Fix All (project/solution) support, honoring configured manifest paths and excluding generated code.
- Updates the analyzer/parsing logic to better match runtime identity conventions (including legacy declarations and nested generic types) and to preserve/retire historical entries.
- Refreshes diagnostics messaging, documentation, and generated manifest headers to include regeneration workflow and compatibility-review guidance.
| File | Description |
|---|---|
| test/Orleans.Analyzers.Tests/GrainInterfaceVersionAnalyzerTest.cs | Expands code-fix and Fix All test coverage for regeneration, configured paths, legacy identity behavior, and generated-code exclusion. |
| src/Orleans.Analyzers/GrainInterfaceVersionCodeFix.cs | Implements regeneration code action + Fix All provider; adds manifest discovery/configured path support and improves retirement matching. |
| src/Orleans.Analyzers/GrainInterfaceVersionAnalyzer.cs | Aligns identity matching with runtime conventions; improves missing-manifest diagnostic location and legacy declaration handling. |
| src/Orleans.Analyzers/Resources.resx | Updates diagnostic messages with regeneration instructions and a compatibility-review link. |
| src/Orleans.Analyzers/build/Microsoft.Orleans.Analyzers.props | Exposes OrleansContractsPath as a compiler-visible property for analyzer/config access. |
| src/Directory.Build.targets | Exposes OrleansContractsPath for compiler/analyzer visibility at the repo level. |
| src/Orleans.Core.Abstractions/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Core/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Runtime/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Streaming/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Reminders/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Persistence.Memory/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.EventSourcing/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.DurableJobs/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Transactions/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.Transactions.TestKit.Base/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.TestingHost/OrleansContracts.txt | Updates generated header guidance text. |
| src/Orleans.BroadcastChannel/OrleansContracts.txt | Updates generated header guidance text. |
| src/Dashboard/Orleans.Dashboard/OrleansContracts.txt | Updates generated header guidance text. |
| docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md | Documents regeneration workflow (IDE + dotnet format), and adds diff interpretation guidance focused on compatibility impact. |
| docs/site/src/content/docs/diagnostics/orleans0025.md | Updates diagnostic guidance to reflect “effective identity” semantics and regeneration workflow. |
| docs/site/src/content/docs/diagnostics/orleans0024.md | Adds regeneration workflow guidance and updated references. |
| docs/site/src/content/docs/diagnostics/orleans0023.md | Adds regeneration workflow guidance and updated references. |
| docs/site/src/content/docs/diagnostics/orleans0022.md | Adds regeneration workflow guidance and updated references. |
| docs/site/src/content/docs/diagnostics/orleans0021.md | Updates diagnostic guidance to reflect “effective identity” semantics. |
| docs/site/src/content/docs/diagnostics/orleans0020.md | Marks the code fix as available and updates remediation guidance to regeneration/Fix All. |
| docs/site/src/content/docs/diagnostics/orleans0019.md | Adds regeneration workflow guidance and updated references. |
| docs/site/src/content/docs/diagnostics/orleans0018.md | Adds regeneration workflow guidance and updated references. |
| docs/site/src/content/docs/diagnostics/orleans0017.md | Adds regeneration workflow guidance and updated references. |
| docs/site/src/content/docs/diagnostics/orleans0016.md | Adds regeneration workflow guidance and updated references. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 2
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/Orleans.Analyzers/GrainInterfaceVersionAnalyzer.cs — _grainInterfacesFile.GetText(...) is called inside AnalyzeNamedType for every RPC interface… |
Pre-existing issues (1)
| Severity | Finding |
|---|---|
test/Orleans.Analyzers.Tests/GrainInterfaceVersionAnalyzerTest.cs — CreateProjectWithAdditionalFilesForCodeFix can throw when configuredContractsPath is a relative… View comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
28cfd74 to
6d8ed50
Compare
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 2
Pre-existing issues (2)
| Severity | Finding |
|---|---|
src/Orleans.Analyzers/GrainInterfaceVersionAnalyzer.cs — _grainInterfacesFile.GetText(...) is called inside AnalyzeNamedType for every RPC interface… View comment |
|
test/Orleans.Analyzers.Tests/GrainInterfaceVersionAnalyzerTest.cs — CreateProjectWithAdditionalFilesForCodeFix can throw when configuredContractsPath is a relative… View comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
Suppressed comments (1)
test/Orleans.Analyzers.Tests/GrainInterfaceVersionAnalyzerTest.cs:1286
Path.GetDirectoryName(configuredContractsPath)!can be null whenconfiguredContractsPathis just a filename (no directory component), which would throw in these tests. Avoid the null-forgiving operator here and fall back to a safe path for the analyzer config document.
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: None
Issues resolved since last review (2)
| Severity | Finding |
|---|---|
src/Orleans.Analyzers/GrainInterfaceVersionAnalyzer.cs — _grainInterfacesFile.GetText(...) is called inside AnalyzeNamedType for every RPC interface… View resolved comment |
|
test/Orleans.Analyzers.Tests/GrainInterfaceVersionAnalyzerTest.cs — CreateProjectWithAdditionalFilesForCodeFix can throw when configuredContractsPath is a relative… View resolved comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md:45
- The docs say to apply Regenerate OrleansContracts.txt “from any Orleans contract diagnostic”, but
ORLEANS0027explicitly has no code fix and the code-fix provider doesn’t includeORLEANS0027inFixableDiagnosticIds(so the regenerate action won’t be offered when ORLEANS0027 is the only diagnostic). Please narrow this wording to diagnostics which offer code fixes, or clarify the exception for ORLEANS0027.
Apply **Regenerate OrleansContracts.txt** from any Orleans contract diagnostic. One application regenerates the entire project manifest. In an IDE, use **Fix all in project** or **Fix all in solution** to regenerate every affected project.
Code coverage78.21% line coverage - 99,551 / 127,293 lines Coverage details
|
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md — The docs say the Regenerate OrleansContracts.txt code fix can be applied "from any Orleans… |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: None
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md — The docs say the Regenerate OrleansContracts.txt code fix can be applied "from any Orleans… View resolved comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/Orleans.Analyzers/GrainInterfaceVersionAnalyzer.cs:543
- ORLEANS0023 (grain class identity mismatch) currently returns early when the manifest declaration omits an explicit
[GrainType(...)](i.e.,declaredClass.Alias is null). That makes a legacy entry likeclass MyGrainappear to match a source class with[GrainType("my-grain")], even though the effective identities differ (derived-from-CLR vs explicit). This can hide a real runtime identity mismatch.
Compare the source identity against the manifest’s effective identity (Alias when present, otherwise the derived default) and report ORLEANS0023 when they differ. Also use the effective manifest identity in the diagnostic message instead of "".
_visitedClasses.TryAdd(GetDeclarationKey(declaredClass), true);
if (declaredClass.Alias is null
|| string.Equals(codeAlias, declaredClass.Alias, StringComparison.Ordinal))
{
return;
ac1dd4e to
57fe208
Compare
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/diagnostics/orleans0027.md — The .editorconfig suppression snippet uses [*.cs], but ORLEANS0027 is reported on the manifest… |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
src/Orleans.Analyzers/GrainInterfaceVersionCodeFix.cs — FindContractsDocument only recognizes the manifest via (a) a file named OrleansContracts.txt or (b)… |
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/diagnostics/orleans0027.md — The .editorconfig suppression snippet uses [*.cs], but ORLEANS0027 is reported on the manifest… View resolved comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: None
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
src/Orleans.Analyzers/GrainInterfaceVersionCodeFix.cs — FindContractsDocument only recognizes the manifest via (a) a file named OrleansContracts.txt or (b)… View resolved comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/Orleans.Analyzers/GrainInterfaceVersionAnalyzer.cs:806
- GetDefaultGrainInterfaceType(string) returns the input unchanged when there are no generic type parameters. For nested types represented in the manifest using C# syntax (eg
Outer.IInnerGrain), this produces an identity with.separators instead of the runtime’s+nested-type separator (and this differs from GetDefaultGrainInterfaceType(INamedTypeSymbol), which always uses+for nested types). This can cause identity-based matching/duplicate detection (eg in GrainInterfaceFileParser.Parse) to treat semantically identical declarations as different identities, potentially missing ORLEANS0021/ORLEANS0025 duplicates when one entry uses explicit GrainInterfaceType/GrainType and another uses a legacy declaration.
internal static string GetDefaultGrainInterfaceType(string typeName)
{
var segments = typeName.Split('.');
var firstGenericSegment = Array.FindIndex(segments, segment => segment.IndexOf('<') >= 0);
if (firstGenericSegment < 0)
{
return typeName;
}
for (var index = firstGenericSegment; index < segments.Length; index++)
{
var genericStart = segments[index].IndexOf('<');
if (genericStart < 0)
{
continue;
}
var genericEnd = segments[index].LastIndexOf('>');
var arity = 1;
for (var characterIndex = genericStart + 1; characterIndex < genericEnd; characterIndex++)
{
if (segments[index][characterIndex] == ',')
{
arity++;
}
}
segments[index] = $"{segments[index].Substring(0, genericStart)}`{arity}";
}
return string.Join(".", segments.Take(firstGenericSegment))
+ (firstGenericSegment > 0 ? "." : string.Empty)
+ string.Join("+", segments.Skip(firstGenericSegment));
}
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
New issues introduced by this change (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md — In the manifest-format example code block, the method line is missing the two-space indentation… |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: 1
Pre-existing issues (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md — In the manifest-format example code block, the method line is missing the two-space indentation… View comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/Orleans.Analyzers/GrainInterfaceVersionCodeFix.cs:371
- HasGeneratedCodeAttribute compares attribute types using constructor?.ContainingType.ToDisplayString() without a SymbolDisplayFormat, but then matches against fully-qualified type names. Depending on Roslyn's default display format, this can fail to recognize [GeneratedCode]/[CompilerGenerated] attributes and accidentally include generated types during regeneration. Use FullyQualifiedFormat (and strip the global:: prefix) for the comparison, consistent with other type-name handling in this file (e.g., GetFullyQualifiedName).
return declaration.AncestorsAndSelf()
.OfType<TypeDeclarationSyntax>()
.SelectMany(type => type.AttributeLists)
.SelectMany(list => list.Attributes)
.Select(attribute => semanticModel.GetSymbolInfo(attribute).Symbol as IMethodSymbol)
.Select(constructor => constructor?.ContainingType.ToDisplayString())
.Any(attributeType => attributeType is
"System.CodeDom.Compiler.GeneratedCodeAttribute"
or "System.Runtime.CompilerServices.CompilerGeneratedAttribute");
There was a problem hiding this comment.
Copilot review overview
Review tier: Lite
Findings: None
Issues resolved since last review (1)
| Severity | Finding |
|---|---|
docs/site/src/content/docs/grains/grain-versioning/contract-compatibility-analyzer.md — In the manifest-format example code block, the method line is missing the two-space indentation… View resolved comment |
Files not reviewed (1)
- src/Orleans.Analyzers/Resources.Designer.cs: Generated file
Suppressed comments (1)
Previously missed (1) — in code that hasn't changed since the last review.
src/Orleans.CodeGenerator.Shared/MethodIdProvider.cs:20
- MethodIdProvider.Create converts the 4-byte xxHash result to hex via a StringBuilder plus per-byte
ToString("X2"), which allocates a new string for each byte. Since the codebase already has Orleans.CodeGenerator.Hashing.HexConverter (used elsewhere for xxHash output), using it here would be allocation-free and consistent.
var result = new StringBuilder(hash.Length * 2);
foreach (var value in hash)
{
result.Append(value.ToString("X2"));
}


OrleansContracts.txtupdates currently require applying individual fixes, which makes project- or solution-wide regeneration cumbersome and leaves generated diffs without enough guidance about their wire-compatibility significance.This adds a deterministic Regenerate OrleansContracts.txt code fix with project and solution Fix All support. The fix creates missing manifests, honors configured manifest paths, rebuilds active contracts from source, excludes generated code, and preserves removed identities as retired history. Contract parsing and identity matching now follow Orleans runtime conventions, including legacy declarations and nested generic grain types.
The analyzer now reports
ORLEANS0027when a manifest RPC method is absent from source. Regeneration retains removed method signatures so a potential wire break remains visible until the developer explicitly accepts the removal.Diagnostics and generated file headers now link to
https://aka.ms/orleans/OrleansContracts.txtand tell developers and coding agents how to regenerate the file and review identity, version, and signature changes as potential wire breaks. The contract analyzer documentation includes IDE anddotnet formatworkflows plus actionable diff interpretation guidance.Fixes #10870
Fixes #10871
Microsoft Reviewers: Open in CodeFlow