Skip to content

Commit 4ba05fd

Browse files
agentCopilot
andcommitted
Fix nullable build errors in Fluent server API + FileSystemClient + BaseVariableState
After merging master with the new Fluent server API (PR #3765), FileSystemClient (PR #3760), and certificate group support (PR #3585), several new code paths needed nullable annotations to align with the nullable4 branch: - Stack/Opc.Ua.Types/State/BaseVariableState.cs: annotate four async value hooks (OnReadValueAsync/OnSimpleReadValueAsync/OnWriteValueAsync/ OnSimpleWriteValueAsync) as nullable; widen the matching locals in ReadAttributeAsync/WriteAttributeAsync; null-forgive the simple-handler invocations gated by the prior null check; null-forgive the final `return result` since result is flow-typed nullable after assignment. - Libraries/Opc.Ua.Client/FileSystem/FileSystemClient.cs: null-forgive QualifiedName.Name in CreateDirectory/CreateFile/MoveOrCopy leaf paths where the segment is already validated upstream. - Libraries/Opc.Ua.Server/Fluent/EventNotifierBuilderExtensions.cs: optional EventPublishOptions parameter on both Publish overloads is now nullable. - Libraries/Opc.Ua.Server/Fluent/EventPublishOptions.cs: OnError sink is nullable (already documented as defaulting to null). - Libraries/Opc.Ua.Server/Fluent/EventSourceRegistry.cs: Register options parameter, SourceEntry.WorkerCts/WorkerTask fields and matching DeactivateSource locals are nullable; null-coalesce notifier.BrowseName.Name when projecting SourceName. - Libraries/Opc.Ua.Server/Fluent/NodeBuilder.cs: ThrowIfSlotOccupied accepts Delegate? so the four async OnRead/OnWrite call sites compile. - Libraries/Opc.Ua.Server/Fluent/NodeManagerBuilder.cs: null-forgive the `(QualifiedName)null` overload trampoline; EventSources backing property starts null until AttachEventSources runs. - Libraries/Opc.Ua.Server/Fluent/VariableBuilder.cs: FromVariant returns TValue? (it already returned default on null); annotate the boxed local and null-forgive the setter call sites. No runtime behavior change. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 411c56d commit 4ba05fd

8 files changed

Lines changed: 31 additions & 31 deletions

File tree

Libraries/Opc.Ua.Client/FileSystem/FileSystemClient.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -274,7 +274,7 @@ public async ValueTask<UaDirectoryInfo> CreateDirectoryAsync(
274274
$"Cannot create '{UaPath.FormatSegment(segment)}': leaf segments must not include a namespace prefix; the server picks the BrowseName namespace.",
275275
nameof(path));
276276
}
277-
current = await CreateDirectoryInAsync(current, segment.Name, ct)
277+
current = await CreateDirectoryInAsync(current, segment.Name!, ct)
278278
.ConfigureAwait(false);
279279
}
280280
return current;
@@ -321,7 +321,7 @@ public async ValueTask<UaFileInfo> CreateFileAsync(
321321
$"Cannot create file '{UaPath.FormatSegment(leaf)}': leaf segments must not include a namespace prefix; the server picks the BrowseName namespace.",
322322
nameof(path));
323323
}
324-
return await CreateFileInAsync(parent, leaf.Name, ct).ConfigureAwait(false);
324+
return await CreateFileInAsync(parent, leaf.Name!, ct).ConfigureAwait(false);
325325
}
326326

327327
/// <summary>
@@ -706,7 +706,7 @@ internal async ValueTask<UaFileSystemInfo> MoveOrCopyAsync(
706706
string parentPath = UaPath.Format(segments.Take(segments.Length - 1).ToArray());
707707
destDir = await GetDirectoryAsync(parentPath, ct).ConfigureAwait(false);
708708
}
709-
return await MoveOrCopyAsync(source, destDir, segments[^1].Name, copy, ct)
709+
return await MoveOrCopyAsync(source, destDir, segments[^1].Name!, copy, ct)
710710
.ConfigureAwait(false);
711711
}
712712

Libraries/Opc.Ua.Server/Fluent/EventNotifierBuilderExtensions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static class EventNotifierBuilderExtensions
8585
public static INodeBuilder<TNotifier> Publish<TNotifier, TEvent>(
8686
this INodeBuilder<TNotifier> nodeBuilder,
8787
Func<TNotifier, ISystemContext, CancellationToken, IAsyncEnumerable<TEvent>> factory,
88-
EventPublishOptions options = null)
88+
EventPublishOptions? options = null)
8989
where TNotifier : BaseObjectState
9090
where TEvent : BaseEventState
9191
{
@@ -130,7 +130,7 @@ public static INodeBuilder<TNotifier> Publish<TNotifier, TEvent>(
130130
public static INodeBuilder<TNotifier> Publish<TNotifier, TEvent>(
131131
this INodeBuilder<TNotifier> nodeBuilder,
132132
IAsyncEnumerable<TEvent> source,
133-
EventPublishOptions options = null)
133+
EventPublishOptions? options = null)
134134
where TNotifier : BaseObjectState
135135
where TEvent : BaseEventState
136136
{

Libraries/Opc.Ua.Server/Fluent/EventPublishOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,6 @@ public sealed record EventPublishOptions
105105
/// callers that want to bubble the exception into their own
106106
/// telemetry. Default <c>null</c>.
107107
/// </summary>
108-
public Action<Exception> OnError { get; init; }
108+
public Action<Exception>? OnError { get; init; }
109109
}
110110
}

Libraries/Opc.Ua.Server/Fluent/EventSourceRegistry.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ public EventSourceRegistry(
9393
public void Register(
9494
BaseObjectState notifier,
9595
Func<NodeState, ISystemContext, CancellationToken, System.Collections.Generic.IAsyncEnumerable<BaseEventState>> factory,
96-
EventPublishOptions options)
96+
EventPublishOptions? options)
9797
{
9898
if (notifier == null)
9999
{
@@ -373,8 +373,8 @@ private void ActivateSource(SourceEntry entry)
373373

374374
private void DeactivateSource(SourceEntry entry, bool force)
375375
{
376-
CancellationTokenSource cts = entry.WorkerCts;
377-
Task worker = entry.WorkerTask;
376+
CancellationTokenSource? cts = entry.WorkerCts;
377+
Task? worker = entry.WorkerTask;
378378
entry.WorkerCts = null;
379379
entry.WorkerTask = null;
380380

@@ -576,7 +576,7 @@ private static void PopulateDefaults(BaseObjectState notifier, ISystemContext co
576576
{
577577
e.SourceName = PropertyState<string>.With<VariantBuilder>(
578578
e,
579-
notifier.BrowseName.Name);
579+
notifier.BrowseName.Name ?? string.Empty);
580580
}
581581

582582
if (e.Time == null || e.Time.Value.IsNull)
@@ -629,8 +629,8 @@ public SourceEntry(
629629
public BaseObjectState Notifier { get; }
630630
public Func<NodeState, ISystemContext, CancellationToken, System.Collections.Generic.IAsyncEnumerable<BaseEventState>> Factory { get; }
631631
public EventPublishOptions Options { get; }
632-
public CancellationTokenSource WorkerCts;
633-
public Task WorkerTask;
632+
public CancellationTokenSource? WorkerCts;
633+
public Task? WorkerTask;
634634
public int LeakedFaulted;
635635
}
636636

Libraries/Opc.Ua.Server/Fluent/NodeBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -321,7 +321,7 @@ private MethodState RequireMethod(string what)
321321
return m;
322322
}
323323

324-
private void ThrowIfSlotOccupied(Delegate existing, string what)
324+
private void ThrowIfSlotOccupied(Delegate? existing, string what)
325325
{
326326
if (existing != null)
327327
{

Libraries/Opc.Ua.Server/Fluent/NodeManagerBuilder.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ public IVariableBuilder<TValue> Variable<TValue>(NodeId nodeId)
261261
public IVariableBuilder<TValue> VariableFromTypeId<TValue>(NodeId typeDefinitionId)
262262
{
263263
ThrowIfSealed();
264-
NodeState node = ResolveByTypeDefinition(typeDefinitionId, (QualifiedName)null);
264+
NodeState node = ResolveByTypeDefinition(typeDefinitionId, (QualifiedName)null!);
265265
return ToVariableBuilder<TValue>(node, FormatNodeId(typeDefinitionId));
266266
}
267267

@@ -304,7 +304,7 @@ internal VariableBuilder<TValue> ToVariableBuilder<TValue>(NodeState node, strin
304304
/// <c>null</c>; the <c>Publish</c> extensions surface a
305305
/// targeted error in that case.
306306
/// </remarks>
307-
internal EventSourceRegistry EventSources { get; private set; }
307+
internal EventSourceRegistry? EventSources { get; private set; }
308308

309309
/// <summary>
310310
/// Wires the supplied registry into this builder so the

Libraries/Opc.Ua.Server/Fluent/VariableBuilder.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,7 @@ public IVariableBuilder<TValue> OnWrite(Action<ISystemContext, TValue> setter)
128128
((INodeBuilder)this).OnWrite(
129129
(ISystemContext context, NodeState _, ref Variant value) =>
130130
{
131-
setter(context, FromVariant(value));
131+
setter(context, FromVariant(value)!);
132132
return ServiceResult.Good;
133133
});
134134
return this;
@@ -156,8 +156,8 @@ public IVariableBuilder<TValue> OnWrite(
156156
((INodeBuilder)this).OnWrite(
157157
async (ISystemContext context, NodeState _, Variant value, CancellationToken ct) =>
158158
{
159-
TValue typed = FromVariant(value);
160-
await setter(context, typed, ct).ConfigureAwait(false);
159+
TValue? typed = FromVariant(value);
160+
await setter(context, typed!, ct).ConfigureAwait(false);
161161
return new AttributeWriteResult(ServiceResult.Good);
162162
});
163163
return this;
@@ -169,9 +169,9 @@ public IVariableBuilder<TValue> OnWrite(
169169
/// the variant is null so a typed lambda never has to defend
170170
/// against an empty variable.
171171
/// </summary>
172-
private static TValue FromVariant(Variant value)
172+
private static TValue? FromVariant(Variant value)
173173
{
174-
object boxed = value.AsBoxedObject(Variant.BoxingBehavior.Legacy);
174+
object? boxed = value.AsBoxedObject(Variant.BoxingBehavior.Legacy);
175175
if (boxed is null)
176176
{
177177
return default;

Stack/Opc.Ua.Types/State/BaseVariableState.cs

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -575,15 +575,15 @@ public uint AccessLevelEx
575575
/// <see cref="ApplyIndexRangeAndDataEncoding"/> on the returned
576576
/// value.
577577
/// </summary>
578-
public NodeValueEventHandlerAsync OnReadValueAsync;
578+
public NodeValueEventHandlerAsync? OnReadValueAsync;
579579

580580
/// <summary>
581581
/// Asynchronous sibling of <see cref="OnSimpleReadValue"/>. The
582582
/// returned value is post-processed by the framework
583583
/// (<see cref="ApplyIndexRangeAndDataEncoding"/> and copy policy)
584584
/// just like the synchronous path.
585585
/// </summary>
586-
public NodeValueSimpleEventHandlerAsync OnSimpleReadValueAsync;
586+
public NodeValueSimpleEventHandlerAsync? OnSimpleReadValueAsync;
587587

588588
/// <summary>
589589
/// Asynchronous sibling of <see cref="OnWriteValue"/>. When set,
@@ -593,14 +593,14 @@ public uint AccessLevelEx
593593
/// success the framework updates the cached value, status code
594594
/// and timestamp.
595595
/// </summary>
596-
public NodeValueWriteEventHandlerAsync OnWriteValueAsync;
596+
public NodeValueWriteEventHandlerAsync? OnWriteValueAsync;
597597

598598
/// <summary>
599599
/// Asynchronous sibling of <see cref="OnSimpleWriteValue"/>. Index
600600
/// range writes are not supported through this hook (just like
601601
/// the synchronous one).
602602
/// </summary>
603-
public NodeValueSimpleWriteEventHandlerAsync OnSimpleWriteValueAsync;
603+
public NodeValueSimpleWriteEventHandlerAsync? OnSimpleWriteValueAsync;
604604

605605
/// <summary>
606606
/// Raised when the DataType attribute is read.
@@ -1795,8 +1795,8 @@ public override async ValueTask<ServiceResult> ReadAttributeAsync(
17951795
DataValue value,
17961796
CancellationToken cancellationToken = default)
17971797
{
1798-
NodeValueEventHandlerAsync onReadValueAsync = OnReadValueAsync;
1799-
NodeValueSimpleEventHandlerAsync onSimpleReadValueAsync = OnSimpleReadValueAsync;
1798+
NodeValueEventHandlerAsync? onReadValueAsync = OnReadValueAsync;
1799+
NodeValueSimpleEventHandlerAsync? onSimpleReadValueAsync = OnSimpleReadValueAsync;
18001800

18011801
if (attributeId != Attributes.Value ||
18021802
(onReadValueAsync == null && onSimpleReadValueAsync == null))
@@ -1879,7 +1879,7 @@ public override async ValueTask<ServiceResult> ReadAttributeAsync(
18791879
else
18801880
{
18811881
// simple async read — framework owns post-processing.
1882-
AttributeSimpleReadResult simpleResult = await onSimpleReadValueAsync(
1882+
AttributeSimpleReadResult simpleResult = await onSimpleReadValueAsync!(
18831883
context, this, cancellationToken)
18841884
.ConfigureAwait(false);
18851885

@@ -1939,7 +1939,7 @@ public override async ValueTask<ServiceResult> ReadAttributeAsync(
19391939

19401940
value.WrappedValue = StatusCode.IsBad(value.StatusCode) ? Variant.Null : valueToRead;
19411941

1942-
return result;
1942+
return result!;
19431943
}
19441944

19451945
/// <summary>
@@ -1956,8 +1956,8 @@ public override async ValueTask<ServiceResult> WriteAttributeAsync(
19561956
DataValue value,
19571957
CancellationToken cancellationToken = default)
19581958
{
1959-
NodeValueWriteEventHandlerAsync onWriteValueAsync = OnWriteValueAsync;
1960-
NodeValueSimpleWriteEventHandlerAsync onSimpleWriteValueAsync = OnSimpleWriteValueAsync;
1959+
NodeValueWriteEventHandlerAsync? onWriteValueAsync = OnWriteValueAsync;
1960+
NodeValueSimpleWriteEventHandlerAsync? onSimpleWriteValueAsync = OnSimpleWriteValueAsync;
19611961

19621962
if (attributeId != Attributes.Value ||
19631963
(onWriteValueAsync == null && onSimpleWriteValueAsync == null))
@@ -2057,7 +2057,7 @@ public override async ValueTask<ServiceResult> WriteAttributeAsync(
20572057
valueToWrite = CoreUtils.Clone(valueToWrite);
20582058
}
20592059

2060-
AttributeWriteResult simpleResult = await onSimpleWriteValueAsync(
2060+
AttributeWriteResult simpleResult = await onSimpleWriteValueAsync!(
20612061
context, this, valueToWrite, cancellationToken)
20622062
.ConfigureAwait(false);
20632063

0 commit comments

Comments
 (0)