From b2a6bfc344f986bfdc523cb5faebba0a91d50cad Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Mar 2026 21:33:54 +0000
Subject: [PATCH 1/6] Initial plan
From b6169cd33dc02ac1f4728c00a028b18ec595e9d9 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Mon, 2 Mar 2026 21:52:08 +0000
Subject: [PATCH 2/6] Refactor compatibility tools to use parameter objects
instead of long parameter lists
Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com>
---
...icrosoft.DotNet.ApiCompat.Shared.projitems | 4 +-
.../ValidateAssemblies.cs | 69 ++++------
.../ValidateAssembliesOptions.cs | 107 ++++++++++++++++
.../ValidatePackage.cs | 70 ++++-------
.../ValidatePackageOptions.cs | 118 ++++++++++++++++++
.../ValidateAssembliesTask.cs | 37 +++---
.../ValidatePackageTask.cs | 42 ++++---
.../Program.cs | 81 ++++++------
.../Microsoft.DotNet.ApiDiff.Tool/Program.cs | 13 +-
.../DiffGeneratorFactory.cs | 50 +++-----
.../GenAPITask.cs | 20 +--
.../Microsoft.DotNet.GenAPI.Tool/Program.cs | 21 ++--
.../Microsoft.DotNet.GenAPI/GenAPIApp.cs | 33 ++---
.../Microsoft.DotNet.GenAPI/GenAPIOptions.cs | 61 +++++++++
.../Diff.Disk.Tests.cs | 26 ++--
15 files changed, 486 insertions(+), 266 deletions(-)
create mode 100644 src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs
create mode 100644 src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs
create mode 100644 src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems
index 524152c56342..70da7b738d25 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/Microsoft.DotNet.ApiCompat.Shared.projitems
@@ -11,8 +11,10 @@
-
+
+
+
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs
index 5ca7266c730c..a30b70b63561 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssemblies.cs
@@ -11,52 +11,35 @@ namespace Microsoft.DotNet.ApiCompat
internal static class ValidateAssemblies
{
public static int Run(Func logFactory,
- bool generateSuppressionFile,
- bool preserveUnnecessarySuppressions,
- bool permitUnnecessarySuppressions,
- string[]? suppressionFiles,
- string? suppressionOutputFile,
- string? noWarn,
- bool respectInternals,
- bool enableRuleAttributesMustMatch,
- string[]? excludeAttributesFiles,
- bool enableRuleCannotChangeParameterName,
- string[] leftAssemblies,
- string[] rightAssemblies,
- bool enableStrictMode,
- string[][]? leftAssembliesReferences,
- string[][]? rightAssembliesReferences,
- bool createWorkItemPerAssembly,
- (string CaptureGroupPattern, string ReplacementString)[]? leftAssembliesTransformationPatterns,
- (string CaptureGroupPattern, string ReplacementString)[]? rightAssembliesTransformationPatterns)
+ ValidateAssembliesOptions options)
{
// Initialize the service provider
ApiCompatServiceProvider serviceProvider = new(logFactory,
- () => SuppressionFileHelper.CreateSuppressionEngine(suppressionFiles, noWarn, generateSuppressionFile),
+ () => SuppressionFileHelper.CreateSuppressionEngine(options.SuppressionFiles, options.NoWarn, options.GenerateSuppressionFile),
(log) => new RuleFactory(log,
- enableRuleAttributesMustMatch,
- enableRuleCannotChangeParameterName),
- respectInternals,
- excludeAttributesFiles);
+ options.EnableRuleAttributesMustMatch,
+ options.EnableRuleCannotChangeParameterName),
+ options.RespectInternals,
+ options.ExcludeAttributesFiles);
IApiCompatRunner apiCompatRunner = serviceProvider.ApiCompatRunner;
- ApiCompatRunnerOptions apiCompatOptions = new(enableStrictMode);
+ ApiCompatRunnerOptions apiCompatOptions = new(options.EnableStrictMode);
// Optionally provide a string transformer if a transformation pattern is passed in.
- RegexStringTransformer? leftAssembliesStringTransformer = leftAssembliesTransformationPatterns != null ? new RegexStringTransformer(leftAssembliesTransformationPatterns) : null;
- RegexStringTransformer? rightAssembliesStringTransformer = rightAssembliesTransformationPatterns != null ? new RegexStringTransformer(rightAssembliesTransformationPatterns) : null;
+ RegexStringTransformer? leftAssembliesStringTransformer = options.LeftAssembliesTransformationPatterns != null ? new RegexStringTransformer(options.LeftAssembliesTransformationPatterns) : null;
+ RegexStringTransformer? rightAssembliesStringTransformer = options.RightAssembliesTransformationPatterns != null ? new RegexStringTransformer(options.RightAssembliesTransformationPatterns) : null;
- if (createWorkItemPerAssembly)
+ if (options.CreateWorkItemPerAssembly)
{
- if (leftAssemblies.Length != rightAssemblies.Length)
+ if (options.LeftAssemblies.Length != options.RightAssemblies.Length)
{
throw new Exception(CommonResources.CreateWorkItemPerAssemblyAssembliesNotEqual);
}
- for (int i = 0; i < leftAssemblies.Length; i++)
+ for (int i = 0; i < options.LeftAssemblies.Length; i++)
{
- List leftMetadataInformation = GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer);
- List rightMetadataInformation = GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer);
+ List leftMetadataInformation = GetMetadataInformation(options.LeftAssemblies[i], GetAssemblyReferences(options.LeftAssembliesReferences, i), leftAssembliesStringTransformer);
+ List rightMetadataInformation = GetMetadataInformation(options.RightAssemblies[i], GetAssemblyReferences(options.RightAssembliesReferences, i), rightAssembliesStringTransformer);
// Enqueue the work item
ApiCompatRunnerWorkItem workItem = new(leftMetadataInformation, apiCompatOptions, rightMetadataInformation);
@@ -66,16 +49,16 @@ public static int Run(Func logFactory,
else
{
// Create the work item that corresponds to the passed in left assembly.
- List leftAssembliesMetadataInformation = new(leftAssemblies.Length);
- for (int i = 0; i < leftAssemblies.Length; i++)
+ List leftAssembliesMetadataInformation = new(options.LeftAssemblies.Length);
+ for (int i = 0; i < options.LeftAssemblies.Length; i++)
{
- leftAssembliesMetadataInformation.AddRange(GetMetadataInformation(leftAssemblies[i], GetAssemblyReferences(leftAssembliesReferences, i), leftAssembliesStringTransformer));
+ leftAssembliesMetadataInformation.AddRange(GetMetadataInformation(options.LeftAssemblies[i], GetAssemblyReferences(options.LeftAssembliesReferences, i), leftAssembliesStringTransformer));
}
- List rightAssembliesMetadataInformation = new(rightAssemblies.Length);
- for (int i = 0; i < rightAssemblies.Length; i++)
+ List rightAssembliesMetadataInformation = new(options.RightAssemblies.Length);
+ for (int i = 0; i < options.RightAssemblies.Length; i++)
{
- rightAssembliesMetadataInformation.AddRange(GetMetadataInformation(rightAssemblies[i], GetAssemblyReferences(rightAssembliesReferences, i), rightAssembliesStringTransformer));
+ rightAssembliesMetadataInformation.AddRange(GetMetadataInformation(options.RightAssemblies[i], GetAssemblyReferences(options.RightAssembliesReferences, i), rightAssembliesStringTransformer));
}
// Enqueue the work item
@@ -86,17 +69,17 @@ public static int Run(Func logFactory,
// Execute the enqueued work item(s).
apiCompatRunner.ExecuteWorkItems();
- SuppressionFileHelper.LogApiCompatSuccessOrFailure(generateSuppressionFile, serviceProvider.SuppressibleLog);
+ SuppressionFileHelper.LogApiCompatSuccessOrFailure(options.GenerateSuppressionFile, serviceProvider.SuppressibleLog);
- if (generateSuppressionFile)
+ if (options.GenerateSuppressionFile)
{
SuppressionFileHelper.GenerateSuppressionFile(serviceProvider.SuppressionEngine,
serviceProvider.SuppressibleLog,
- preserveUnnecessarySuppressions,
- suppressionFiles,
- suppressionOutputFile);
+ options.PreserveUnnecessarySuppressions,
+ options.SuppressionFiles,
+ options.SuppressionOutputFile);
}
- else if (!permitUnnecessarySuppressions)
+ else if (!options.PermitUnnecessarySuppressions)
{
SuppressionFileHelper.ValidateUnnecessarySuppressions(serviceProvider.SuppressionEngine, serviceProvider.SuppressibleLog);
}
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs
new file mode 100644
index 000000000000..485fd1b07da5
--- /dev/null
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs
@@ -0,0 +1,107 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.DotNet.ApiCompat
+{
+ ///
+ /// Options for running ValidateAssemblies.
+ ///
+ internal sealed class ValidateAssembliesOptions
+ {
+ public ValidateAssembliesOptions(string[] leftAssemblies, string[] rightAssemblies)
+ {
+ LeftAssemblies = leftAssemblies;
+ RightAssemblies = rightAssemblies;
+ }
+
+ ///
+ /// The assemblies that represent the contract (left side).
+ ///
+ public string[] LeftAssemblies { get; }
+
+ ///
+ /// The assemblies that represent the implementation (right side).
+ ///
+ public string[] RightAssemblies { get; }
+
+ ///
+ /// If true, generates a suppression file that contains the api compatibility errors.
+ ///
+ public bool GenerateSuppressionFile { get; set; }
+
+ ///
+ /// If true, preserves unnecessary suppressions when re-generating the suppression file.
+ ///
+ public bool PreserveUnnecessarySuppressions { get; set; }
+
+ ///
+ /// If true, permits unnecessary suppressions in the suppression file.
+ ///
+ public bool PermitUnnecessarySuppressions { get; set; }
+
+ ///
+ /// The path to one or more suppression files to read from.
+ ///
+ public string[]? SuppressionFiles { get; set; }
+
+ ///
+ /// The path to a suppression output file to write to when is true.
+ ///
+ public string? SuppressionOutputFile { get; set; }
+
+ ///
+ /// A NoWarn string that allows disabling specific rules.
+ ///
+ public string? NoWarn { get; set; }
+
+ ///
+ /// If true, includes both internal and public API.
+ ///
+ public bool RespectInternals { get; set; }
+
+ ///
+ /// Enables rule to check that attributes match.
+ ///
+ public bool EnableRuleAttributesMustMatch { get; set; }
+
+ ///
+ /// The path to one or more attribute exclusion files with types in DocId format.
+ ///
+ public string[]? ExcludeAttributesFiles { get; set; }
+
+ ///
+ /// Enables rule to check that the parameter names between public methods do not change.
+ ///
+ public bool EnableRuleCannotChangeParameterName { get; set; }
+
+ ///
+ /// Performs api comparison checks in strict mode.
+ ///
+ public bool EnableStrictMode { get; set; }
+
+ ///
+ /// The left assemblies' references. The index in the array maps to the index of the passed in left assembly.
+ ///
+ public string[][]? LeftAssembliesReferences { get; set; }
+
+ ///
+ /// The right assemblies' references. The index in the array maps to the index of the passed in right assembly.
+ ///
+ public string[][]? RightAssembliesReferences { get; set; }
+
+ ///
+ /// Create dedicated api compatibility checks for each left and right assembly tuple.
+ ///
+ public bool CreateWorkItemPerAssembly { get; set; }
+
+ ///
+ /// Regex transformation patterns (regex + replacement string) that transform left assembly paths.
+ ///
+ public (string CaptureGroupPattern, string ReplacementString)[]? LeftAssembliesTransformationPatterns { get; set; }
+
+ ///
+ /// Regex transformation patterns (regex + replacement string) that transform right assembly paths.
+ ///
+ public (string CaptureGroupPattern, string ReplacementString)[]? RightAssembliesTransformationPatterns { get; set; }
+ }
+}
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs
index 1b82442c985d..98168e4a14a3 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackage.cs
@@ -6,93 +6,73 @@
using Microsoft.DotNet.PackageValidation;
using Microsoft.DotNet.PackageValidation.Filtering;
using Microsoft.DotNet.PackageValidation.Validators;
-using NuGet.Frameworks;
namespace Microsoft.DotNet.ApiCompat
{
internal static class ValidatePackage
{
public static int Run(Func logFactory,
- bool generateSuppressionFile,
- bool preserveUnnecessarySuppressions,
- bool permitUnnecessarySuppressions,
- string[]? suppressionFiles,
- string? suppressionOutputFile,
- string? noWarn,
- bool respectInternals,
- bool enableRuleAttributesMustMatch,
- string[]? excludeAttributesFiles,
- bool enableRuleCannotChangeParameterName,
- string? packagePath,
- bool runApiCompat,
- bool enableStrictModeForCompatibleTfms,
- bool enableStrictModeForCompatibleFrameworksInPackage,
- bool enableStrictModeForBaselineValidation,
- string? baselinePackagePath,
- string? runtimeGraph,
- IReadOnlyDictionary>? packageAssemblyReferences,
- IReadOnlyDictionary>? baselinePackageAssemblyReferences,
- string[]? baselinePackageFrameworksToIgnore)
+ ValidatePackageOptions options)
{
// Initialize the service provider
ApiCompatServiceProvider serviceProvider = new(logFactory,
- () => SuppressionFileHelper.CreateSuppressionEngine(suppressionFiles, noWarn, generateSuppressionFile),
+ () => SuppressionFileHelper.CreateSuppressionEngine(options.SuppressionFiles, options.NoWarn, options.GenerateSuppressionFile),
(log) => new RuleFactory(log,
- enableRuleAttributesMustMatch,
- enableRuleCannotChangeParameterName),
- respectInternals,
- excludeAttributesFiles);
+ options.EnableRuleAttributesMustMatch,
+ options.EnableRuleCannotChangeParameterName),
+ options.RespectInternals,
+ options.ExcludeAttributesFiles);
// If a runtime graph is provided, parse and use it for asset selection during the in-memory package construction.
- if (runtimeGraph != null)
+ if (options.RuntimeGraph != null)
{
- Package.InitializeRuntimeGraph(runtimeGraph);
+ Package.InitializeRuntimeGraph(options.RuntimeGraph);
}
// Create the in-memory representation of the passed in package path
- Package package = Package.Create(packagePath, packageAssemblyReferences);
+ Package package = Package.Create(options.PackagePath, options.PackageAssemblyReferences);
// Invoke all validators and pass the specific validation options in. Don't execute work items, just enqueue them.
CompatibleTfmValidator tfmValidator = new(serviceProvider.SuppressibleLog, serviceProvider.ApiCompatRunner);
tfmValidator.Validate(new PackageValidatorOption(package,
- enableStrictModeForCompatibleTfms,
- enqueueApiCompatWorkItems: runApiCompat,
+ options.EnableStrictModeForCompatibleTfms,
+ enqueueApiCompatWorkItems: options.RunApiCompat,
executeApiCompatWorkItems: false));
CompatibleFrameworkInPackageValidator compatibleFrameworkInPackageValidator = new(serviceProvider.SuppressibleLog, serviceProvider.ApiCompatRunner);
compatibleFrameworkInPackageValidator.Validate(new PackageValidatorOption(package,
- enableStrictModeForCompatibleFrameworksInPackage,
- enqueueApiCompatWorkItems: runApiCompat,
+ options.EnableStrictModeForCompatibleFrameworksInPackage,
+ enqueueApiCompatWorkItems: options.RunApiCompat,
executeApiCompatWorkItems: false));
- if (!string.IsNullOrEmpty(baselinePackagePath))
+ if (!string.IsNullOrEmpty(options.BaselinePackagePath))
{
BaselinePackageValidator baselineValidator = new(serviceProvider.SuppressibleLog, serviceProvider.ApiCompatRunner);
baselineValidator.Validate(new PackageValidatorOption(package,
- enableStrictMode: enableStrictModeForBaselineValidation,
- enqueueApiCompatWorkItems: runApiCompat,
+ enableStrictMode: options.EnableStrictModeForBaselineValidation,
+ enqueueApiCompatWorkItems: options.RunApiCompat,
executeApiCompatWorkItems: false,
- Package.Create(baselinePackagePath, baselinePackageAssemblyReferences),
- baselinePackageFrameworksToIgnore is not null ? new TargetFrameworkFilter(baselinePackageFrameworksToIgnore) : null));
+ Package.Create(options.BaselinePackagePath, options.BaselinePackageAssemblyReferences),
+ options.BaselinePackageFrameworksToIgnore is not null ? new TargetFrameworkFilter(options.BaselinePackageFrameworksToIgnore) : null));
}
- if (runApiCompat)
+ if (options.RunApiCompat)
{
// Execute the work items that were enqueued.
serviceProvider.ApiCompatRunner.ExecuteWorkItems();
- SuppressionFileHelper.LogApiCompatSuccessOrFailure(generateSuppressionFile, serviceProvider.SuppressibleLog);
+ SuppressionFileHelper.LogApiCompatSuccessOrFailure(options.GenerateSuppressionFile, serviceProvider.SuppressibleLog);
}
- if (generateSuppressionFile)
+ if (options.GenerateSuppressionFile)
{
SuppressionFileHelper.GenerateSuppressionFile(serviceProvider.SuppressionEngine,
serviceProvider.SuppressibleLog,
- preserveUnnecessarySuppressions,
- suppressionFiles,
- suppressionOutputFile);
+ options.PreserveUnnecessarySuppressions,
+ options.SuppressionFiles,
+ options.SuppressionOutputFile);
}
- else if (!permitUnnecessarySuppressions)
+ else if (!options.PermitUnnecessarySuppressions)
{
SuppressionFileHelper.ValidateUnnecessarySuppressions(serviceProvider.SuppressionEngine, serviceProvider.SuppressibleLog);
}
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs
new file mode 100644
index 000000000000..3cc0157ec16e
--- /dev/null
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs
@@ -0,0 +1,118 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using NuGet.Frameworks;
+
+namespace Microsoft.DotNet.ApiCompat
+{
+ ///
+ /// Options for running ValidatePackage.
+ ///
+ internal sealed class ValidatePackageOptions
+ {
+ public ValidatePackageOptions(string packagePath)
+ {
+ PackagePath = packagePath;
+ }
+
+ ///
+ /// The path to the package to inspect.
+ ///
+ public string PackagePath { get; }
+
+ ///
+ /// If true, generates a suppression file that contains the api compatibility errors.
+ ///
+ public bool GenerateSuppressionFile { get; set; }
+
+ ///
+ /// If true, preserves unnecessary suppressions when re-generating the suppression file.
+ ///
+ public bool PreserveUnnecessarySuppressions { get; set; }
+
+ ///
+ /// If true, permits unnecessary suppressions in the suppression file.
+ ///
+ public bool PermitUnnecessarySuppressions { get; set; }
+
+ ///
+ /// The path to one or more suppression files to read from.
+ ///
+ public string[]? SuppressionFiles { get; set; }
+
+ ///
+ /// The path to a suppression output file to write to when is true.
+ ///
+ public string? SuppressionOutputFile { get; set; }
+
+ ///
+ /// A NoWarn string that allows disabling specific rules.
+ ///
+ public string? NoWarn { get; set; }
+
+ ///
+ /// If true, includes both internal and public API.
+ ///
+ public bool RespectInternals { get; set; }
+
+ ///
+ /// Enables rule to check that attributes match.
+ ///
+ public bool EnableRuleAttributesMustMatch { get; set; }
+
+ ///
+ /// The path to one or more attribute exclusion files with types in DocId format.
+ ///
+ public string[]? ExcludeAttributesFiles { get; set; }
+
+ ///
+ /// Enables rule to check that the parameter names between public methods do not change.
+ ///
+ public bool EnableRuleCannotChangeParameterName { get; set; }
+
+ ///
+ /// If true, performs api compatibility checks on the package assets.
+ ///
+ public bool RunApiCompat { get; set; } = true;
+
+ ///
+ /// Validates api compatibility in strict mode for contract and implementation assemblies for all compatible target frameworks.
+ ///
+ public bool EnableStrictModeForCompatibleTfms { get; set; } = true;
+
+ ///
+ /// Validates api compatibility in strict mode for assemblies that are compatible based on their target framework.
+ ///
+ public bool EnableStrictModeForCompatibleFrameworksInPackage { get; set; }
+
+ ///
+ /// Validates api compatibility in strict mode for package baseline checks.
+ ///
+ public bool EnableStrictModeForBaselineValidation { get; set; }
+
+ ///
+ /// The path to a baseline package to validate against the current package.
+ ///
+ public string? BaselinePackagePath { get; set; }
+
+ ///
+ /// A runtime graph that can be provided for package asset selection.
+ ///
+ public string? RuntimeGraph { get; set; }
+
+ ///
+ /// Assembly references grouped by target framework, for the assets inside the package.
+ ///
+ public IReadOnlyDictionary>? PackageAssemblyReferences { get; set; }
+
+ ///
+ /// Assembly references grouped by target framework, for the assets inside the baseline package.
+ ///
+ public IReadOnlyDictionary>? BaselinePackageAssemblyReferences { get; set; }
+
+ ///
+ /// Target frameworks to ignore from the baseline package.
+ ///
+ public string[]? BaselinePackageFrameworksToIgnore { get; set; }
+ }
+}
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs
index 424d81b4862a..9b577c55a26a 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs
@@ -134,24 +134,25 @@ protected override void ExecuteCore()
{
SuppressibleMSBuildLog logFactory(ISuppressionEngine suppressionEngine) => new(Log, suppressionEngine, NoWarn);
ValidateAssemblies.Run(logFactory,
- GenerateSuppressionFile,
- PreserveUnnecessarySuppressions,
- PermitUnnecessarySuppressions,
- SuppressionFiles,
- SuppressionOutputFile,
- NoWarn,
- RespectInternals,
- EnableRuleAttributesMustMatch,
- ExcludeAttributesFiles,
- EnableRuleCannotChangeParameterName,
- LeftAssemblies!,
- RightAssemblies!,
- EnableStrictMode,
- ParseAssembliesReferences(LeftAssembliesReferences),
- ParseAssembliesReferences(RightAssembliesReferences),
- CreateWorkItemPerAssembly,
- ParseTransformationPattern(LeftAssembliesTransformationPattern),
- ParseTransformationPattern(RightAssembliesTransformationPattern));
+ new ValidateAssembliesOptions(LeftAssemblies!, RightAssemblies!)
+ {
+ GenerateSuppressionFile = GenerateSuppressionFile,
+ PreserveUnnecessarySuppressions = PreserveUnnecessarySuppressions,
+ PermitUnnecessarySuppressions = PermitUnnecessarySuppressions,
+ SuppressionFiles = SuppressionFiles,
+ SuppressionOutputFile = SuppressionOutputFile,
+ NoWarn = NoWarn,
+ RespectInternals = RespectInternals,
+ EnableRuleAttributesMustMatch = EnableRuleAttributesMustMatch,
+ ExcludeAttributesFiles = ExcludeAttributesFiles,
+ EnableRuleCannotChangeParameterName = EnableRuleCannotChangeParameterName,
+ EnableStrictMode = EnableStrictMode,
+ LeftAssembliesReferences = ParseAssembliesReferences(LeftAssembliesReferences),
+ RightAssembliesReferences = ParseAssembliesReferences(RightAssembliesReferences),
+ CreateWorkItemPerAssembly = CreateWorkItemPerAssembly,
+ LeftAssembliesTransformationPatterns = ParseTransformationPattern(LeftAssembliesTransformationPattern),
+ RightAssembliesTransformationPatterns = ParseTransformationPattern(RightAssembliesTransformationPattern),
+ });
if (SemaphoreFile != null)
{
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs
index 196d90c47272..b7ef29a0c187 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs
@@ -142,26 +142,28 @@ protected override void ExecuteCore()
{
SuppressibleMSBuildLog logFactory(ISuppressionEngine suppressionEngine) => new(Log, suppressionEngine, NoWarn);
ValidatePackage.Run(logFactory,
- GenerateSuppressionFile,
- PreserveUnnecessarySuppressions,
- PermitUnnecessarySuppressions,
- SuppressionFiles,
- SuppressionOutputFile,
- NoWarn,
- RespectInternals,
- EnableRuleAttributesMustMatch,
- ExcludeAttributesFiles,
- EnableRuleCannotChangeParameterName,
- PackageTargetPath!,
- RunApiCompat,
- EnableStrictModeForCompatibleTfms,
- EnableStrictModeForCompatibleFrameworksInPackage,
- EnableStrictModeForBaselineValidation,
- BaselinePackageTargetPath,
- RuntimeGraph,
- ParsePackageAssemblyReferences(PackageAssemblyReferences),
- ParsePackageAssemblyReferences(BaselinePackageAssemblyReferences),
- BaselinePackageFrameworksToIgnore);
+ new ValidatePackageOptions(PackageTargetPath!)
+ {
+ GenerateSuppressionFile = GenerateSuppressionFile,
+ PreserveUnnecessarySuppressions = PreserveUnnecessarySuppressions,
+ PermitUnnecessarySuppressions = PermitUnnecessarySuppressions,
+ SuppressionFiles = SuppressionFiles,
+ SuppressionOutputFile = SuppressionOutputFile,
+ NoWarn = NoWarn,
+ RespectInternals = RespectInternals,
+ EnableRuleAttributesMustMatch = EnableRuleAttributesMustMatch,
+ ExcludeAttributesFiles = ExcludeAttributesFiles,
+ EnableRuleCannotChangeParameterName = EnableRuleCannotChangeParameterName,
+ RunApiCompat = RunApiCompat,
+ EnableStrictModeForCompatibleTfms = EnableStrictModeForCompatibleTfms,
+ EnableStrictModeForCompatibleFrameworksInPackage = EnableStrictModeForCompatibleFrameworksInPackage,
+ EnableStrictModeForBaselineValidation = EnableStrictModeForBaselineValidation,
+ BaselinePackagePath = BaselinePackageTargetPath,
+ RuntimeGraph = RuntimeGraph,
+ PackageAssemblyReferences = ParsePackageAssemblyReferences(PackageAssemblyReferences),
+ BaselinePackageAssemblyReferences = ParsePackageAssemblyReferences(BaselinePackageAssemblyReferences),
+ BaselinePackageFrameworksToIgnore = BaselinePackageFrameworksToIgnore,
+ });
}
private static Dictionary>? ParsePackageAssemblyReferences(ITaskItem[]? packageAssemblyReferences)
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
index 4d1f8e416df8..a413ea8404b4 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
@@ -198,24 +198,25 @@ static int Main(string[] args)
SuppressibleConsoleLog logFactory(ISuppressionEngine suppressionEngine) => new(suppressionEngine, verbosity, noWarn);
int exitCode = ValidateAssemblies.Run(logFactory,
- generateSuppressionFile,
- preserveUnnecessarySuppressions,
- permitUnnecessarySuppressions,
- suppressionFiles,
- suppressionOutputFile,
- noWarn,
- respectInternals,
- enableRuleAttributesMustMatch,
- excludeAttributesFiles,
- enableRuleCannotChangeParameterName,
- leftAssemblies,
- rightAssemblies,
- strictMode,
- leftAssembliesReferences,
- rightAssembliesReferences,
- createWorkItemPerAssembly,
- leftAssembliesTransformationPattern,
- rightAssembliesTransformationPattern);
+ new ValidateAssembliesOptions(leftAssemblies, rightAssemblies)
+ {
+ GenerateSuppressionFile = generateSuppressionFile,
+ PreserveUnnecessarySuppressions = preserveUnnecessarySuppressions,
+ PermitUnnecessarySuppressions = permitUnnecessarySuppressions,
+ SuppressionFiles = suppressionFiles,
+ SuppressionOutputFile = suppressionOutputFile,
+ NoWarn = noWarn,
+ RespectInternals = respectInternals,
+ EnableRuleAttributesMustMatch = enableRuleAttributesMustMatch,
+ ExcludeAttributesFiles = excludeAttributesFiles,
+ EnableRuleCannotChangeParameterName = enableRuleCannotChangeParameterName,
+ EnableStrictMode = strictMode,
+ LeftAssembliesReferences = leftAssembliesReferences,
+ RightAssembliesReferences = rightAssembliesReferences,
+ CreateWorkItemPerAssembly = createWorkItemPerAssembly,
+ LeftAssembliesTransformationPatterns = leftAssembliesTransformationPattern,
+ RightAssembliesTransformationPatterns = rightAssembliesTransformationPattern,
+ });
roslynResolver.Unregister();
@@ -322,26 +323,28 @@ static int Main(string[] args)
SuppressibleConsoleLog logFactory(ISuppressionEngine suppressionEngine) => new(suppressionEngine, verbosity, noWarn);
int exitCode = ValidatePackage.Run(logFactory,
- generateSuppressionFile,
- preserveUnnecessarySuppressions,
- permitUnnecessarySuppressions,
- suppressionFiles,
- suppressionOutputFile,
- noWarn,
- respectInternals,
- enableRuleAttributesMustMatch,
- excludeAttributesFiles,
- enableRuleCannotChangeParameterName,
- package,
- runApiCompat,
- enableStrictModeForCompatibleTfms,
- enableStrictModeForCompatibleFrameworksInPackage,
- enableStrictModeForBaselineValidation,
- baselinePackage,
- runtimeGraph,
- packageAssemblyReferences,
- baselinePackageAssemblyReferences,
- baselinePackageFrameworksToIgnore);
+ new ValidatePackageOptions(package!)
+ {
+ GenerateSuppressionFile = generateSuppressionFile,
+ PreserveUnnecessarySuppressions = preserveUnnecessarySuppressions,
+ PermitUnnecessarySuppressions = permitUnnecessarySuppressions,
+ SuppressionFiles = suppressionFiles,
+ SuppressionOutputFile = suppressionOutputFile,
+ NoWarn = noWarn,
+ RespectInternals = respectInternals,
+ EnableRuleAttributesMustMatch = enableRuleAttributesMustMatch,
+ ExcludeAttributesFiles = excludeAttributesFiles,
+ EnableRuleCannotChangeParameterName = enableRuleCannotChangeParameterName,
+ RunApiCompat = runApiCompat,
+ EnableStrictModeForCompatibleTfms = enableStrictModeForCompatibleTfms,
+ EnableStrictModeForCompatibleFrameworksInPackage = enableStrictModeForCompatibleFrameworksInPackage,
+ EnableStrictModeForBaselineValidation = enableStrictModeForBaselineValidation,
+ BaselinePackagePath = baselinePackage,
+ RuntimeGraph = runtimeGraph,
+ PackageAssemblyReferences = packageAssemblyReferences,
+ BaselinePackageAssemblyReferences = baselinePackageAssemblyReferences,
+ BaselinePackageFrameworksToIgnore = baselinePackageFrameworksToIgnore,
+ });
roslynResolver.Unregister();
@@ -376,7 +379,7 @@ private static string[] ParseAssemblyArgument(ArgumentResult argumentResult)
private static (string CaptureGroupPattern, string ReplacementString)[]? ParseTransformationPattern(ArgumentResult argumentResult)
{
- var patterns = new(string CaptureGroupPattern, string ReplacementPattern)[argumentResult.Tokens.Count];
+ var patterns = new (string CaptureGroupPattern, string ReplacementPattern)[argumentResult.Tokens.Count];
for (int i = 0; i < argumentResult.Tokens.Count; i++)
{
string[] parts = argumentResult.Tokens[i].Value.Split(';');
diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs
index 0a8f5cbf47e7..02ab54101f22 100644
--- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs
+++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff.Tool/Program.cs
@@ -183,18 +183,7 @@ private static Task HandleCommandAsync(DiffConfiguration diffConfig, Cancellatio
}
IDiffGenerator diffGenerator = DiffGeneratorFactory.Create(log,
- diffConfig.BeforeAssembliesFolderPath,
- diffConfig.BeforeAssemblyReferencesFolderPath,
- diffConfig.AfterAssembliesFolderPath,
- diffConfig.AfterAssemblyReferencesFolderPath,
- diffConfig.OutputFolderPath,
- diffConfig.BeforeFriendlyName,
- diffConfig.AfterFriendlyName,
- diffConfig.TableOfContentsTitle,
- diffConfig.FilesWithAssembliesToExclude,
- diffConfig.FilesWithAttributesToExclude,
- diffConfig.FilesWithApisToExclude,
- diffConfig.AddPartialModifier,
+ diffConfig,
writeToDisk: true,
diagnosticOptions: null // TODO: If needed, add CLI option to pass specific diagnostic options
);
diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs
index bc30f66251da..8de0d942fa89 100644
--- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs
+++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs
@@ -24,51 +24,29 @@ public static class DiffGeneratorFactory
/// Creates a new instance of that writes the diff to disk.
///
/// The logger to use for logging messages.
- /// The folder path containing the assemblies before the change.
- /// The folder path containing the assembly references before the change.
- /// The folder path containing the assemblies after the change.
- /// The folder path containing the assembly references after the change.
- /// The folder path where the output will be written.
- /// The friendly name for the assemblies before the change.
- /// The friendly name for the assemblies after the change.
- /// The title for the table of contents in the generated diff.
- /// An optional array of filepaths each containing a list of assemblies to avoid showing in the diff. If , no assemblies are excluded.
- /// An optional array of filepaths each containing a list of attributes to avoid showing in the diff.
- /// An optional array of filepaths each containing a list of APIs to avoid showing in the diff.
- /// Indicates whether to add the partial modifier to types.
+ /// The configuration options for the diff.
/// If , when calling , the generated markdown files get written to disk, and no item is added to the dictionary. If , when calling , the generated markdown files get added to the dictionary (with the file path as the dictionary key) and none of them is written to disk. This is meant for testing purposes.
/// An optional list of diagnostic options to use when generating the diff.
/// A new instance of that writes the diff to disk.
///
public static IDiffGenerator Create(ILog log,
- string beforeAssembliesFolderPath,
- string? beforeAssemblyReferencesFolderPath,
- string afterAssembliesFolderPath,
- string? afterAssemblyReferencesFolderPath,
- string outputFolderPath,
- string beforeFriendlyName,
- string afterFriendlyName,
- string tableOfContentsTitle,
- FileInfo[]? filesWithAssembliesToExclude,
- FileInfo[]? filesWithAttributesToExclude,
- FileInfo[]? filesWithApisToExclude,
- bool addPartialModifier,
+ DiffConfiguration diffConfig,
bool writeToDisk,
IEnumerable>? diagnosticOptions = null)
{
return new FileOutputDiffGenerator(log,
- beforeAssembliesFolderPath,
- beforeAssemblyReferencesFolderPath,
- afterAssembliesFolderPath,
- afterAssemblyReferencesFolderPath,
- outputFolderPath,
- beforeFriendlyName,
- afterFriendlyName,
- tableOfContentsTitle,
- filesWithAssembliesToExclude,
- filesWithAttributesToExclude,
- filesWithApisToExclude,
- addPartialModifier,
+ diffConfig.BeforeAssembliesFolderPath,
+ diffConfig.BeforeAssemblyReferencesFolderPath,
+ diffConfig.AfterAssembliesFolderPath,
+ diffConfig.AfterAssemblyReferencesFolderPath,
+ diffConfig.OutputFolderPath,
+ diffConfig.BeforeFriendlyName,
+ diffConfig.AfterFriendlyName,
+ diffConfig.TableOfContentsTitle,
+ diffConfig.FilesWithAssembliesToExclude,
+ diffConfig.FilesWithAttributesToExclude,
+ diffConfig.FilesWithApisToExclude,
+ diffConfig.AddPartialModifier,
writeToDisk,
diagnosticOptions);
}
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs
index 674c6f68b212..34263794d8cd 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Task/GenAPITask.cs
@@ -66,15 +66,17 @@ protected override void ExecuteCore()
Debug.Assert(Assemblies != null, "Assemblies cannot be null.");
GenAPIApp.Run(new MSBuildLog(Log),
- Assemblies,
- AssemblyReferences,
- OutputPath,
- HeaderFile,
- ExceptionMessage,
- ExcludeApiFiles,
- ExcludeAttributesFiles,
- RespectInternals,
- IncludeAssemblyAttributes);
+ new GenAPIOptions(Assemblies)
+ {
+ AssemblyReferencesPaths = AssemblyReferences,
+ OutputPath = OutputPath,
+ HeaderFile = HeaderFile,
+ ExceptionMessage = ExceptionMessage,
+ ExcludeApiFiles = ExcludeApiFiles,
+ ExcludeAttributesFiles = ExcludeAttributesFiles,
+ RespectInternals = RespectInternals,
+ IncludeAssemblyAttributes = IncludeAssemblyAttributes,
+ });
}
}
}
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs
index 0595e4c0c5b5..f4b01e10dce5 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI.Tool/Program.cs
@@ -105,16 +105,17 @@ static int Main(string[] args)
Debug.Assert(assemblies != null, "Assemblies cannot be null.");
GenAPIApp.Run(log,
- assemblies,
- parseResult.GetValue(assemblyReferencesOption),
- parseResult.GetValue(outputPathOption),
- parseResult.GetValue(headerFileOption),
- parseResult.GetValue(exceptionMessageOption),
- parseResult.GetValue(excludeApiFilesOption),
- parseResult.GetValue(excludeAttributesFilesOption),
- respectInternals,
- parseResult.GetValue(includeAssemblyAttributesOption)
- );
+ new GenAPIOptions(assemblies)
+ {
+ AssemblyReferencesPaths = parseResult.GetValue(assemblyReferencesOption),
+ OutputPath = parseResult.GetValue(outputPathOption),
+ HeaderFile = parseResult.GetValue(headerFileOption),
+ ExceptionMessage = parseResult.GetValue(exceptionMessageOption),
+ ExcludeApiFiles = parseResult.GetValue(excludeApiFilesOption),
+ ExcludeAttributesFiles = parseResult.GetValue(excludeAttributesFilesOption),
+ RespectInternals = respectInternals,
+ IncludeAssemblyAttributes = parseResult.GetValue(includeAssemblyAttributesOption),
+ });
});
return rootCommand.Parse(args).Invoke();
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
index 25ef58d6481b..740f666f6d7a 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
@@ -18,36 +18,27 @@ namespace Microsoft.DotNet.GenAPI
public static class GenAPIApp
{
///
- /// Initialize and run Roslyn-based GenAPI tool specifying the assemblies to load.
+ /// Initialize and run Roslyn-based GenAPI tool using options.
///
- public static void Run(ILog log,
- string[] assembliesPaths,
- string[]? assemblyReferencesPaths,
- string? outputPath,
- string? headerFile,
- string? exceptionMessage,
- string[]? excludeApiFiles,
- string[]? excludeAttributesFiles,
- bool respectInternals,
- bool includeAssemblyAttributes)
+ public static void Run(ILog log, GenAPIOptions options)
{
(IAssemblySymbolLoader loader, Dictionary assemblySymbols) = AssemblySymbolLoader.CreateFromFiles(
log,
- assembliesPaths,
- assemblyReferencesPaths,
+ options.AssembliesPaths,
+ options.AssemblyReferencesPaths,
assembliesToExclude: [],
- respectInternals: respectInternals);
+ respectInternals: options.RespectInternals);
Run(log,
loader,
assemblySymbols,
- outputPath,
- headerFile,
- exceptionMessage,
- excludeApiFiles,
- excludeAttributesFiles,
- respectInternals,
- includeAssemblyAttributes);
+ options.OutputPath,
+ options.HeaderFile,
+ options.ExceptionMessage,
+ options.ExcludeApiFiles,
+ options.ExcludeAttributesFiles,
+ options.RespectInternals,
+ options.IncludeAssemblyAttributes);
}
///
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
new file mode 100644
index 000000000000..92f4f192d980
--- /dev/null
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
@@ -0,0 +1,61 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+namespace Microsoft.DotNet.GenAPI;
+
+///
+/// Options for running GenAPI via .
+///
+public sealed class GenAPIOptions
+{
+ public GenAPIOptions(string[] assembliesPaths)
+ {
+ AssembliesPaths = assembliesPaths;
+ }
+
+ ///
+ /// The path to one or more assemblies or directories with assemblies.
+ ///
+ public string[] AssembliesPaths { get; }
+
+ ///
+ /// Paths to assembly references or their underlying directories.
+ ///
+ public string[]? AssemblyReferencesPaths { get; set; }
+
+ ///
+ /// Output path. Default is the console. Can specify an existing directory as well and
+ /// then a file will be created for each assembly with the matching name of the assembly.
+ ///
+ public string? OutputPath { get; set; }
+
+ ///
+ /// Specify a file with an alternate header content to prepend to output.
+ ///
+ public string? HeaderFile { get; set; }
+
+ ///
+ /// Method bodies should throw PlatformNotSupportedException.
+ ///
+ public string? ExceptionMessage { get; set; }
+
+ ///
+ /// The path to one or more api exclusion files with types in DocId format.
+ ///
+ public string[]? ExcludeApiFiles { get; set; }
+
+ ///
+ /// The path to one or more attribute exclusion files with types in DocId format.
+ ///
+ public string[]? ExcludeAttributesFiles { get; set; }
+
+ ///
+ /// If true, includes both internal and public API.
+ ///
+ public bool RespectInternals { get; set; }
+
+ ///
+ /// Includes assembly attributes which are values that provide information about an assembly.
+ ///
+ public bool IncludeAssemblyAttributes { get; set; }
+}
diff --git a/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs b/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs
index 97af160dbaad..c1b8894f97a5 100644
--- a/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs
+++ b/test/Microsoft.DotNet.ApiDiff.Tests/Diff.Disk.Tests.cs
@@ -473,18 +473,20 @@ private IDiffGenerator TestDiskShared(
Mock log = new();
return DiffGeneratorFactory.Create(log.Object,
- beforeAssembliesFolderPath,
- beforeAssemblyReferencesFolderPath: null,
- afterAssembliesFolderPath,
- afterAssemblyReferencesFolderPath: null,
- outputFolderPath,
- beforeFriendlyName,
- afterFriendlyName,
- tableOfContentsTitle,
- filesWithAssembliesToExclude ?? [],
- filesWithAttributesToExclude ?? [],
- filesWithAPIsToExclude ?? [],
- addPartialModifier: false,
+ new DiffConfiguration(
+ AfterAssembliesFolderPath: afterAssembliesFolderPath,
+ AfterAssemblyReferencesFolderPath: null,
+ BeforeAssembliesFolderPath: beforeAssembliesFolderPath,
+ BeforeAssemblyReferencesFolderPath: null,
+ OutputFolderPath: outputFolderPath,
+ BeforeFriendlyName: beforeFriendlyName,
+ AfterFriendlyName: afterFriendlyName,
+ TableOfContentsTitle: tableOfContentsTitle,
+ FilesWithAssembliesToExclude: filesWithAssembliesToExclude ?? [],
+ FilesWithAttributesToExclude: filesWithAttributesToExclude ?? [],
+ FilesWithApisToExclude: filesWithAPIsToExclude ?? [],
+ AddPartialModifier: false,
+ AttachDebugger: false),
writeToDisk,
DiffGeneratorFactory.DefaultDiagnosticOptions);
}
From 2f32747f1a7f97a68a9e44398db53fefcdd46dfe Mon Sep 17 00:00:00 2001
From: Eric StJohn
Date: Mon, 2 Mar 2026 15:32:56 -0800
Subject: [PATCH 3/6] Apply suggestions from code review
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
.../ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs | 2 +-
.../ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs | 2 +-
.../GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs | 4 ++--
3 files changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
index a413ea8404b4..d78ef0f863bf 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
@@ -379,7 +379,7 @@ private static string[] ParseAssemblyArgument(ArgumentResult argumentResult)
private static (string CaptureGroupPattern, string ReplacementString)[]? ParseTransformationPattern(ArgumentResult argumentResult)
{
- var patterns = new (string CaptureGroupPattern, string ReplacementPattern)[argumentResult.Tokens.Count];
+ var patterns = new (string CaptureGroupPattern, string ReplacementString)[argumentResult.Tokens.Count];
for (int i = 0; i < argumentResult.Tokens.Count; i++)
{
string[] parts = argumentResult.Tokens[i].Value.Split(';');
diff --git a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs
index 8de0d942fa89..e158e8dfe0cb 100644
--- a/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs
+++ b/src/Compatibility/ApiDiff/Microsoft.DotNet.ApiDiff/DiffGeneratorFactory.cs
@@ -28,7 +28,7 @@ public static class DiffGeneratorFactory
/// If , when calling , the generated markdown files get written to disk, and no item is added to the dictionary. If , when calling , the generated markdown files get added to the dictionary (with the file path as the dictionary key) and none of them is written to disk. This is meant for testing purposes.
/// An optional list of diagnostic options to use when generating the diff.
/// A new instance of that writes the diff to disk.
- ///
+
public static IDiffGenerator Create(ILog log,
DiffConfiguration diffConfig,
bool writeToDisk,
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
index 92f4f192d980..c0380147e5af 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
@@ -35,8 +35,8 @@ public GenAPIOptions(string[] assembliesPaths)
public string? HeaderFile { get; set; }
///
- /// Method bodies should throw PlatformNotSupportedException.
- ///
+ /// When set, method bodies will consist of throw new PlatformNotSupportedException(ExceptionMessage);.
+ /// When is , method bodies will instead be emitted as throw null;.
public string? ExceptionMessage { get; set; }
///
From 5588a0c78e37c623e91e0560c4a1ffd17899ceba Mon Sep 17 00:00:00 2001
From: Eric StJohn
Date: Tue, 3 Mar 2026 08:58:01 -0800
Subject: [PATCH 4/6] Apply suggestion from @ericstj
---
.../GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs | 1 +
1 file changed, 1 insertion(+)
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
index c0380147e5af..5196d53655bc 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
@@ -37,6 +37,7 @@ public GenAPIOptions(string[] assembliesPaths)
///
/// When set, method bodies will consist of throw new PlatformNotSupportedException(ExceptionMessage);.
/// When is , method bodies will instead be emitted as throw null;.
+ ///
public string? ExceptionMessage { get; set; }
///
From 6d2da4761eb0a8029970955e358e746ee591e47e Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 6 Mar 2026 01:07:56 +0000
Subject: [PATCH 5/6] Apply jeffhandley's suggestions: null checks in
constructors and see cref in summaries
Co-authored-by: ericstj <8918108+ericstj@users.noreply.github.com>
---
.../ValidateAssembliesOptions.cs | 6 +++---
.../ValidatePackageOptions.cs | 4 ++--
.../GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs | 2 +-
.../GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs | 2 +-
4 files changed, 7 insertions(+), 7 deletions(-)
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs
index 485fd1b07da5..33fa6f5a5afa 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidateAssembliesOptions.cs
@@ -4,14 +4,14 @@
namespace Microsoft.DotNet.ApiCompat
{
///
- /// Options for running ValidateAssemblies.
+ /// Options for running .
///
internal sealed class ValidateAssembliesOptions
{
public ValidateAssembliesOptions(string[] leftAssemblies, string[] rightAssemblies)
{
- LeftAssemblies = leftAssemblies;
- RightAssemblies = rightAssemblies;
+ LeftAssemblies = leftAssemblies ?? throw new ArgumentNullException(nameof(leftAssemblies));
+ RightAssemblies = rightAssemblies ?? throw new ArgumentNullException(nameof(rightAssemblies));
}
///
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs
index 3cc0157ec16e..a401ef2ef27c 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Shared/ValidatePackageOptions.cs
@@ -6,13 +6,13 @@
namespace Microsoft.DotNet.ApiCompat
{
///
- /// Options for running ValidatePackage.
+ /// Options for running .
///
internal sealed class ValidatePackageOptions
{
public ValidatePackageOptions(string packagePath)
{
- PackagePath = packagePath;
+ PackagePath = packagePath ?? throw new ArgumentNullException(nameof(packagePath));
}
///
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
index 740f666f6d7a..50d30828d6f1 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIApp.cs
@@ -18,7 +18,7 @@ namespace Microsoft.DotNet.GenAPI
public static class GenAPIApp
{
///
- /// Initialize and run Roslyn-based GenAPI tool using options.
+ /// Initialize and run Roslyn-based GenAPI tool using .
///
public static void Run(ILog log, GenAPIOptions options)
{
diff --git a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
index 5196d53655bc..0f7f2b51f4b3 100644
--- a/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
+++ b/src/Compatibility/GenAPI/Microsoft.DotNet.GenAPI/GenAPIOptions.cs
@@ -10,7 +10,7 @@ public sealed class GenAPIOptions
{
public GenAPIOptions(string[] assembliesPaths)
{
- AssembliesPaths = assembliesPaths;
+ AssembliesPaths = assembliesPaths ?? throw new ArgumentNullException(nameof(assembliesPaths));
}
///
From 07d4f159a3ee5f55779ecddfb3e765c5e46ea4d1 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Fri, 6 Mar 2026 01:25:24 +0000
Subject: [PATCH 6/6] Eliminate null-forgiving ! operators with explicit null
guards in callers
Co-authored-by: jeffhandley <1031940+jeffhandley@users.noreply.github.com>
---
.../ValidateAssembliesTask.cs | 5 ++++-
.../Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs | 4 +++-
.../ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs | 4 +++-
3 files changed, 10 insertions(+), 3 deletions(-)
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs
index 9b577c55a26a..63166e69ea1f 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidateAssembliesTask.cs
@@ -132,9 +132,12 @@ public override bool Execute()
protected override void ExecuteCore()
{
+ if (LeftAssemblies is null) throw new ArgumentNullException(nameof(LeftAssemblies));
+ if (RightAssemblies is null) throw new ArgumentNullException(nameof(RightAssemblies));
+
SuppressibleMSBuildLog logFactory(ISuppressionEngine suppressionEngine) => new(Log, suppressionEngine, NoWarn);
ValidateAssemblies.Run(logFactory,
- new ValidateAssembliesOptions(LeftAssemblies!, RightAssemblies!)
+ new ValidateAssembliesOptions(LeftAssemblies, RightAssemblies)
{
GenerateSuppressionFile = GenerateSuppressionFile,
PreserveUnnecessarySuppressions = PreserveUnnecessarySuppressions,
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs
index b7ef29a0c187..5dcab0c4a5e2 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Task/ValidatePackageTask.cs
@@ -140,9 +140,11 @@ public override bool Execute()
protected override void ExecuteCore()
{
+ if (PackageTargetPath is null) throw new ArgumentNullException(nameof(PackageTargetPath));
+
SuppressibleMSBuildLog logFactory(ISuppressionEngine suppressionEngine) => new(Log, suppressionEngine, NoWarn);
ValidatePackage.Run(logFactory,
- new ValidatePackageOptions(PackageTargetPath!)
+ new ValidatePackageOptions(PackageTargetPath)
{
GenerateSuppressionFile = GenerateSuppressionFile,
PreserveUnnecessarySuppressions = PreserveUnnecessarySuppressions,
diff --git a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
index d78ef0f863bf..ea6afd102be3 100644
--- a/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
+++ b/src/Compatibility/ApiCompat/Microsoft.DotNet.ApiCompat.Tool/Program.cs
@@ -311,6 +311,8 @@ static int Main(string[] args)
bool enableRuleCannotChangeParameterName = parseResult.GetValue(enableRuleCannotChangeParameterNameOption);
string? package = parseResult.GetValue(packageArgument);
+ if (package is null) throw new ArgumentNullException(nameof(package));
+
bool runApiCompat = parseResult.GetValue(runApiCompatOption);
bool enableStrictModeForCompatibleTfms = parseResult.GetValue(enableStrictModeForCompatibleTfmsOption);
bool enableStrictModeForCompatibleFrameworksInPackage = parseResult.GetValue(enableStrictModeForCompatibleFrameworksInPackageOption);
@@ -323,7 +325,7 @@ static int Main(string[] args)
SuppressibleConsoleLog logFactory(ISuppressionEngine suppressionEngine) => new(suppressionEngine, verbosity, noWarn);
int exitCode = ValidatePackage.Run(logFactory,
- new ValidatePackageOptions(package!)
+ new ValidatePackageOptions(package)
{
GenerateSuppressionFile = generateSuppressionFile,
PreserveUnnecessarySuppressions = preserveUnnecessarySuppressions,