Files
UA-.NETStandard/common.props
T
romanett 9b034f8291 Enable nullable reference types centrally; migrate all unset and annotations-only projects (#4700)
# Description

Nullable reference types are now enabled once, in `common.props`, and
every project in the repository builds warning-clean with them.

**Configuration**
- `<Nullable>enable</Nullable>` lives in `common.props`. All per-project
`<Nullable>` settings are removed: 178 project files (enable,
annotations, and the `disable` in the three Aot test projects).
- 419 per-file `#nullable enable`/`restore` directives and the 4
`#nullable disable` directives are removed.
- Kept on purpose:
- The 5 `<auto-generated>` schema files (`UANodeSet.cs`, `UA Type
Dictionary.cs`, `OPCBinarySchema.cs`, `SecuredApplication.cs`,
`PInvokeHelper.cs`) keep `#nullable enable`. The compiler ignores the
project setting in generated code, so the directive is required there.
- `tests/Opc.Ua.Interop.LegacyPeer` keeps `disable`. It builds against
the 1.5.378 packages and does not import the repository props.
- `docs/DeveloperGuide.md` and `.github/copilot-instructions.md` now
describe the central setting.

**Migration** (the 43 unset and 13 annotations-only projects, roughly
10k diagnostics)
- **Production code: annotated at the declarations.** Covers
`tools/Opc.Ua.SourceGeneration.Core`, `src/Opc.Ua.Lds.Server` and the
LDS sample.
- Members, parameters and returns are nullable where null actually
flows.
- Null-check helpers carry `[NotNullWhen]` (`IsNull`, `IsValidXmlName`,
`IsMethodTypeDesign`).
- `CoreUtils.Format`, `Utils.Format`, `ITemplateWriter.WriteLine` take
`params object?[]`. `Template.AddReplacement` takes `string?` (rendering
already skips null replacements).
- `IRegisteredServerStore.RegisterAsync` returns
`Task<RegistrationEntry?>`: it returns null when a registration is
removed.
- Dereferences backed by an invariant (for example, the validated model)
use `!`, so runtime behaviour is unchanged.
- **Behavioural changes in production code** (small, deliberate):
- `NodeSetToModelDesign` throws `InvalidOperationException` when the
file is not a NodeSet. Before, it threw a NullReferenceException on the
next line.
- `ModelDesignValidator` starts its lookup tables empty instead of null.
A redundant `if (m_nodes == null) return;` guard is gone.
- **Test code: minimal annotations.**
  - `?` on values that can be null, `!` where the test asserts non-null.
  - `= null!` for fixture members assigned in SetUp.
- `TryGetStructure<T>(out T? x)` where the inferred `T?` broke the
`IEncodeable` constraint.
- A few redundant `== null` checks on structs (`NodeId`, `ArrayOf<T>`)
folded into the adjacent `IsNull`/`Count` checks.

**Notes for reviewers**
- The diff is large (~1,500 files) but almost entirely mechanical: `?`,
`!` and directive removal. The non-annotation changes are listed above.
- Known follow-up, not done here:
`IModelDesign.Namespaces/TargetNamespace/NamespaceUris` stay
non-nullable even though the validator returns null before it loads a
dictionary. Making them nullable would ripple through every generator
call site.

## Related Issues

- Fixes #4439

## Checklist

- [ ] I have signed the
[CLA](https://opcfoundation.org/license/cla/ContributorLicenseAgreementv1.0.pdf)
and read the
[CONTRIBUTING](https://github.com/OPCFoundation/UA-.NETStandard/blob/master/CONTRIBUTING.md)
doc.
- [ ] I have added tests that prove my fix is effective or that my
feature works and increased code coverage.
- [x] I have added all necessary documentation.
- [x] I have verified that my changes do not introduce (new) build or
analyzer warnings.
- [ ] I ran **all** tests locally using the **UA.slnx** solution against
at least .net **framework** and .net **10**, and all passed.
- [ ] I fixed **all** failing and flaky tests in the CI pipelines and
**all** CodeQL warnings.
- [ ] I have addressed **all** PR feedback received.

The "no new warnings" box covers net10.0 and net48: full non-incremental
builds of `UA.slnx`, `tools/SourceGeneration.slnx`,
`tools/MigrationAnalyzer.slnx` and the Aot test projects finished with 0
warnings and 0 errors under the repository's warnings-as-errors
settings. net9.0 and net8.0 also had no nullable diagnostics, but they
were not rebuilt after the final fixes. Tests have not been run locally
yet.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 08:48:13 +02:00

151 lines
7.4 KiB
XML

<Project>
<PropertyGroup>
<Product>OPC UA .NET Standard Library</Product>
<RepositoryUrl>https://github.com/OPCFoundation/UA-.NETStandard</RepositoryUrl>
<VersionPrefix>2.0</VersionPrefix>
<VersionSuffix>preview-$([System.DateTime]::Now.ToString("yyyyMMdd"))</VersionSuffix>
<Copyright>Copyright © 2004-2025 OPC Foundation, Inc</Copyright>
<PackagePrefix>OPCFoundation.NetStandard</PackagePrefix>
<AssemblyPrefix>Opc.Ua</AssemblyPrefix>
<Company>OPC Foundation</Company>
<Authors>OPC Foundation</Authors>
<NeutralLanguage>en-US</NeutralLanguage>
<HighEntropyVA>true</HighEntropyVA>
<IsPackable>false</IsPackable>
<PlatformTarget>AnyCPU</PlatformTarget>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<LangVersion>14.0</LangVersion>
<!--
Nullable reference types are enabled for every project in the repository.
Do not add per-project <Nullable> settings or per-file '#nullable enable'
directives; a project or file that must opt out does so explicitly with
<Nullable>disable</Nullable> / '#nullable disable' and says why.
-->
<Nullable>enable</Nullable>
<!-- to enable package validation, uncomment here -->
<!--<EnablePackageValidation>true</EnablePackageValidation>-->
<!-- to check the package validation on a release build, uncomment here -->
<!--<PackageValidationBaselineVersion>1.5.374.27</PackageValidationBaselineVersion>-->
<!-- to regenerate the compatibility suppression file, uncomment here -->
<!--<GenerateCompatibilitySuppressionFile>true</GenerateCompatibilitySuppressionFile>-->
<NuGetAuditMode>all</NuGetAuditMode>
<NuGetAudit>true</NuGetAudit>
</PropertyGroup>
<PropertyGroup>
<AccelerateBuildsInVisualStudio>true</AccelerateBuildsInVisualStudio>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
<RunAnalyzersDuringBuild Condition="'$(RunAnalyzersDuringBuild)' == ''">true</RunAnalyzersDuringBuild>
<CodeAnalysisTreatWarningsAsErrors>true</CodeAnalysisTreatWarningsAsErrors>
<AnalysisLevel>preview</AnalysisLevel>
<AnalysisMode>all</AnalysisMode>
<AnalysisModeStyle>default</AnalysisModeStyle>
</PropertyGroup>
<PropertyGroup Condition="'$(BuildingInsideVisualStudio)' == 'true'">
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
</PropertyGroup>
<ItemGroup Condition="'$(NoRoslynatorAnalzers)' == ''">
<PackageReference Include="Roslynator.Analyzers" PrivateAssets="All" />
<PackageReference Include="Roslynator.Formatting.Analyzers" PrivateAssets="All" />
</ItemGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);NU1510</NoWarn>
<PackageIcon>images/logo.jpg</PackageIcon>
<PackageProjectUrl>$(RepositoryUrl)</PackageProjectUrl>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<!--
Every package ships the OPC Foundation MIT License 1.00 as a license
file. Do NOT replace this with
<PackageLicenseExpression>MIT</PackageLicenseExpression>: the SPDX `MIT`
license is a different text than the OPC Foundation one, and NuGet
rejects a project that carries both an expression and a license file
(NU5033). `Directory.Build.targets` enforces this.
-->
<PackageLicenseFile>LICENSE.txt</PackageLicenseFile>
<PackageReleaseNotes>$(RepositoryUrl)/releases</PackageReleaseNotes>
<!--
Every package ships its own package-local README (`NugetREADME.md`
next to the csproj, or the established `McpREADME.md` for the
command-line tool package). Per-package READMEs are wired in each
csproj via <PackageReadmeFile> + a matching <None Include=...
Pack="true">. A generic shared README at this `common.props`
level was deliberately removed in favor of per-package READMEs
because it was misleading on nuget.org (every package described
itself identically).
-->
<PackageTags>OPCFoundation OPC UA ios linux dotnet net netcore</PackageTags>
</PropertyGroup>
<PropertyGroup Condition="'$(SignAssembly)' == 'true'">
<!-- OPC Foundation public key -->
<PublicKey>0024000004800000940000000602000000240000</PublicKey>
<PublicKey>$(PublicKey)525341310004000001000100d987b12f068b3580</PublicKey>
<PublicKey>$(PublicKey)429f3dde01397508880fc7e62621397618456ca1</PublicKey>
<PublicKey>$(PublicKey)549aeacfbdb90c62adfe918f05ce3677b390f783</PublicKey>
<PublicKey>$(PublicKey)57b8745cb6e1334655afce1a9527ac92fc829ff5</PublicKey>
<PublicKey>$(PublicKey)85ea79f007e52ba0f83ead627e3edda40bec5ae5</PublicKey>
<PublicKey>$(PublicKey)74128fc9342cb57cb8285aa4e5b589c0ebef3be5</PublicKey>
<PublicKey>$(PublicKey)71b5c8f2ab1067f7c880e8f8882a73c80a12a1ef</PublicKey>
</PropertyGroup>
<Choose>
<When Condition="$([MSBuild]::IsTargetFrameworkCompatible('$(TargetFramework)', 'net5.0'))">
<PropertyGroup>
<!-- Disable strongname warning as that's not been enforced since .net 5.-->
<NoWarn>$(NoWarn);CS8002</NoWarn>
</PropertyGroup>
</When>
<Otherwise>
<PropertyGroup>
<!-- Disable nullable warning as it is not as meaningful on .net framework as on .net -->
<NoWarn>$(NoWarn);nullable</NoWarn>
</PropertyGroup>
</Otherwise>
</Choose>
<PropertyGroup Condition="'$(TargetFramework)' == 'net48'">
<!--
The Microsoft.Extensions.* graph used across the stack (Http.Resilience and
its transitive Telemetry / AmbientMetadata / Diagnostics packages) ships
net462 assets that build and run on .NET Framework, but their buildTransitive
support targets still emit "doesn't support net48" warnings. These are
benign on net462-compatible TFMs, so suppress them once here for all projects
instead of scattering per-project SuppressTfmSupportBuildWarnings blocks.
TODO: remove when the library matrix drops .NET Framework or the packages stop warning.
-->
<SuppressTfmSupportBuildWarnings>true</SuppressTfmSupportBuildWarnings>
</PropertyGroup>
<ItemGroup Condition="'$(IsPackable)' != 'false'">
<None
Include="$(MSBuildThisFileDirectory)/LICENSE.txt"
Pack="true"
PackagePath="$(PackageLicenseFile)"
/>
<None
Include="$(MSBuildThisFileDirectory)/nuget/logo.jpg"
Pack="true"
PackagePath="$(PackageIcon)"
/>
<!--
Each NuGet package owns its own package-local README -
`NugetREADME.md` next to the csproj, or the established
`McpREADME.md` for the command-line tool package. Each csproj
wires its README via <PackageReadmeFile> and packs the file at
the package root with
<None Include="..." Pack="true" PackagePath="\" />.
A generic shared README would be misleading on nuget.org because
every package would describe itself identically.
-->
</ItemGroup>
<ItemGroup Condition="'$(NBGV_PublicRelease)' != ''">
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
</ItemGroup>
<!-- Deterministic build is currently not supported for code coverage tests. -->
<PropertyGroup Condition="'$(CollectCoverage)' != 'true' AND ('$(TF_BUILD)' == 'true' OR '$(GITHUB_ACTIONS)' == 'true')">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<PropertyGroup>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
</Project>