Skip to content

Add PublicAPI.Unshipped.txt BOM sort warning to Copilot instructions#34327

Merged
jfversluis merged 1 commit intomainfrom
fix/publicapi-sort-instructions
Mar 5, 2026
Merged

Add PublicAPI.Unshipped.txt BOM sort warning to Copilot instructions#34327
jfversluis merged 1 commit intomainfrom
fix/publicapi-sort-instructions

Conversation

@jfversluis
Copy link
Copy Markdown
Member

Note

Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!

Description

PR #34320 fixed RS0017 analyzer errors caused by #nullable enable being sorted to the bottom of 14 Maps PublicAPI.Unshipped.txt files. The root cause was a prior Copilot agent session that used LC_ALL=C sort -u to resolve merge conflicts — the BOM bytes (0xEF 0xBB 0xBF) sort after all ASCII characters, pushing the directive below the API entries.

This updates the Copilot instructions to prevent this from recurring:

  • Explains that #nullable enable must remain on line 1
  • Warns against using plain sort on these files (BOM sort ordering)
  • Provides a safe conflict resolution script that preserves the header before sorting API entries

Copilot AI review requested due to automatic review settings March 4, 2026 12:31
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Mar 4, 2026

🚀 Dogfood this PR with:

⚠️ WARNING: Do not do this without first carefully reviewing the code of this PR to satisfy yourself it is safe.

curl -fsSL https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.sh | bash -s -- 34327

Or

  • Run remotely in PowerShell:
iex "& { $(irm https://raw.githubusercontent.com/dotnet/maui/main/eng/scripts/get-maui-pr.ps1) } 34327"

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates Copilot guidance to avoid PublicAPI.Unshipped.txt BOM sorting issues (RS0017), and includes a broad set of Controls fixes across handlers/platforms plus additional device test coverage.

Changes:

  • Document safe conflict-resolution/sorting guidance for PublicAPI.Unshipped.txt files while preserving #nullable enable on line 1.
  • Add/adjust platform fixes across Controls (e.g., Android toolbar/menu item handling, iOS selection/scroll behaviors, Windows enabled-state propagation).
  • Add/expand device tests related to navigation/handler lifecycle and memory/leak regressions.

Reviewed changes

Copilot reviewed 44 out of 673 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
src/Controls/tests/DeviceTests/Memory/MemoryTests.cs Updates leak test coverage for NavigationPage on Android.
src/Controls/tests/DeviceTests/Elements/NavigationPage/NavigationPageTests.Android.cs Adds Android device test validating StackNavigationManager clears references on disconnect.
src/Controls/src/Core/TabbedPage/TabbedPage.Windows.cs Propagates tab IsEnabled into Windows tab view model.
src/Controls/src/Core/SwipeView/SwipeView.cs Closes open SwipeView on iOS/MacCatalyst when binding context changes.
src/Controls/src/Core/Shapes/Shape.cs Adds shape versioning via IVersionedShape and increments version on property changes.
src/Controls/src/Core/PublicAPI/netstandard/PublicAPI.Unshipped.txt Tracks new/changed API surface for Shape.OnPropertyChanged.
src/Controls/src/Core/PublicAPI/net/PublicAPI.Unshipped.txt Tracks new/changed API surface for Shape.OnPropertyChanged.
src/Controls/src/Core/PublicAPI/net-windows/PublicAPI.Unshipped.txt Tracks new/changed API surface for Shape.OnPropertyChanged.
src/Controls/src/Core/PublicAPI/net-tizen/PublicAPI.Unshipped.txt Tracks new/changed API surface for Shape.OnPropertyChanged.
src/Controls/src/Core/PublicAPI/net-maccatalyst/PublicAPI.Unshipped.txt Tracks new/changed API surface for Shape.OnPropertyChanged.
src/Controls/src/Core/PublicAPI/net-ios/PublicAPI.Unshipped.txt Tracks new/changed API surface for Shape.OnPropertyChanged.
src/Controls/src/Core/PublicAPI/net-android/PublicAPI.Unshipped.txt Tracks new/changed API surface (Shape.OnPropertyChanged, RecyclerView touch overrides).
src/Controls/src/Core/Platform/iOS/Extensions/FormattedStringExtensions.cs Refactors span font resolution via GetEffectiveFont.
src/Controls/src/Core/Platform/Windows/TabbedPage/TabbedPageStyle.xaml Binds IsEnabled for Windows tab items.
src/Controls/src/Core/Platform/Windows/Extensions/FormattedStringExtensions.cs Refactors span font resolution via GetEffectiveFont.
src/Controls/src/Core/Platform/Android/Extensions/ToolbarExtensions.cs Adds menu-item mapping to guard async icon updates + requests insets apply.
src/Controls/src/Core/Platform/Android/Extensions/FormattedStringExtensions.cs Refactors span font resolution via GetEffectiveFont.
src/Controls/src/Core/NavigationPage/NavigationPageToolbar.cs Re-applies toolbar changes for FlyoutLayoutBehavior updates.
src/Controls/src/Core/NavigationPage/NavigationPage.cs Removes duplicate RemoveFromInnerChildren call.
src/Controls/src/Core/Label/Label.cs Removes handler attribute (handler registration now centralized).
src/Controls/src/Core/Label/Label.Android.cs Improves handler change lifecycle for both MauiTextView and MauiMaterialTextView.
src/Controls/src/Core/Hosting/AppHostBuilderExtensions.cs Conditionally registers Material3 handler variants on Android.
src/Controls/src/Core/Handlers/Shell/ShellItemHandler.Windows.cs Updates Windows Shell item mapping and enables state propagation.
src/Controls/src/Core/Handlers/Items2/iOS/SelectableItemsViewController2.cs Adjusts selection timing based on CollectionView.IsLoaded().
src/Controls/src/Core/Handlers/Items2/iOS/ItemsViewDelegator2.cs Ensures offsets are updated even when no visible items are detected.
src/Controls/src/Core/Handlers/Items2/ItemsViewHandler2.iOS.cs Uses actual scroll direction when mapping scroll-to position.
src/Controls/src/Core/Handlers/Items/iOS/SelectableItemsViewController.cs Mirrors selection timing fix in non-Items2 handler.
src/Controls/src/Core/Handlers/Items/iOS/ItemsViewDelegator.cs Mirrors offset update behavior in non-Items2 handler.
src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs Changes first-layout scroll behavior + refines threshold reached logic.
src/Controls/src/Core/Handlers/Items/Android/MauiRecyclerView.cs Adds disabled-state touch interception/handling for ItemsView.
src/Controls/src/Core/FontExtensions.cs Adds Span.GetEffectiveFont helper.
src/Controls/src/Core/FlyoutPage/FlyoutPage.cs Triggers toolbar reevaluation on Android/Windows orientation changes.
src/Controls/src/Core/Editor/Editor.Mapper.cs Extends mapper updates for EditorHandler2 under Material3 on Android.
src/Controls/src/Core/Editor/Editor.Android.cs Adds MapText overload for EditorHandler2.
src/Controls/src/Core/ContentPresenter.cs Restores inherited binding context propagation for hosted content.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellItemRendererBase.cs Disposes Shell pages when fragments are removed.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutTemplatedContentRenderer.cs Ensures vertical scroll mode is updated when flyout content is rebuilt.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRenderer.cs Updates flow direction handling + null-guard ordering.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellFlyoutRecyclerAdapter.cs Adjusts flyout item measuring for non-enabled vertical scroll mode.
src/Controls/src/Core/Compatibility/Handlers/Shell/Android/ShellContentFragment.cs Adds explicit page disposal/disconnect path.
src/Controls/src/Core/Compatibility/Handlers/NavigationPage/iOS/NavigationRenderer.cs Multiple iOS navigation bar updates (iOS 26 tinting, flyout icon updates, stage manager resize).
src/Controls/src/Core/Compatibility/Handlers/FlyoutPage/iOS/PhoneFlyoutPageRenderer.cs Applies safe-area top inset to FlyoutPage container.
src/Controls/src/Build.Tasks/nuget/buildTransitive/netstandard2.0/maui-sc.aotprofile.txt Removes AOT profile entries for removed/changed interop methods.
.github/copilot-instructions.md Adds warned/safe guidance for PublicAPI.Unshipped.txt sorting with BOM and #nullable enable.
Comments suppressed due to low confidence (1)

src/Controls/src/Core/Handlers/Items/Android/RecyclerViewScrollListener.cs:1

  • The header/footer bounds check and threshold math appear to mix “count” and “adapter index”. With a footer and no header (headerValue=0), a footer position can slip through (e.g., Last == actualItemCount) and incorrectly trigger RemainingItemsThresholdReached. A safer approach is to compute the last data adapter index (e.g., lastDataIndex = headerValue + dataCount - 1) and compare Last against [headerValue, headerValue + dataCount - 1], then compute distance-to-end using Last - headerValue.
#nullable disable

Comment on lines +126 to +129
foreach (var item in shellItemController.GetItems())
{
item.PropertyChanged -= OnShellItemPropertyChanged;
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MapMenuItems() unconditionally adds PropertyChanged += OnShellItemPropertyChanged each time it runs. If MapMenuItems() is invoked multiple times, this can lead to duplicate subscriptions (and -= in DisconnectHandler will remove only one instance per call). Consider tracking subscribed items (e.g., a HashSet<BaseShellItem>), unsubscribing before re-subscribing, or ensuring MapMenuItems() first detaches existing handlers for the current item set.

Copilot uses AI. Check for mistakes.

foreach (var item in shellItemController.GetItems())
{
item.PropertyChanged += OnShellItemPropertyChanged;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MapMenuItems() unconditionally adds PropertyChanged += OnShellItemPropertyChanged each time it runs. If MapMenuItems() is invoked multiple times, this can lead to duplicate subscriptions (and -= in DisconnectHandler will remove only one instance per call). Consider tracking subscribed items (e.g., a HashSet<BaseShellItem>), unsubscribing before re-subscribing, or ensuring MapMenuItems() first detaches existing handlers for the current item set.

Copilot uses AI. Check for mistakes.
}

// Check if we're at or within threshold distance from the last data item
bool isThresholdReached = (Last == actualItemCount - 1) || (actualItemCount - 1 - Last <= _itemsView.RemainingItemsThreshold);
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The header/footer bounds check and threshold math appear to mix “count” and “adapter index”. With a footer and no header (headerValue=0), a footer position can slip through (e.g., Last == actualItemCount) and incorrectly trigger RemainingItemsThresholdReached. A safer approach is to compute the last data adapter index (e.g., lastDataIndex = headerValue + dataCount - 1) and compare Last against [headerValue, headerValue + dataCount - 1], then compute distance-to-end using Last - headerValue.

Copilot uses AI. Check for mistakes.
Comment on lines +527 to +547
public override bool OnTouchEvent(MotionEvent e)
{
// If ItemsView is disabled, don't handle touch events
if (ItemsView?.IsEnabled == false)
{
return false;
}

return base.OnTouchEvent(e);
}

public override bool OnInterceptTouchEvent(MotionEvent e)
{
// If ItemsView is disabled, intercept all touch events to prevent interactions
if (ItemsView?.IsEnabled == false)
{
return true;
}

return base.OnInterceptTouchEvent(e);
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The disabled-path logic is inconsistent: OnInterceptTouchEvent returns true (intercept), but OnTouchEvent returns false (not handled). This can lead to unpredictable gesture behavior since the view claims the stream but then doesn’t consume it. Consider making both methods consistently “consume and do nothing” when disabled (return true in both), or consistently “don’t participate” (return false in both) depending on the intended UX.

Copilot uses AI. Check for mistakes.
Comment on lines +12 to +13
MauiTextView _mauiTextView;
MauiMaterialTextView _mauiMaterialTextView;
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_mauiTextView and _mauiMaterialTextView are declared as non-nullable but are assigned null in DetachLayoutChanged(). In builds where nullable warnings are treated as errors, this can break compilation. These fields should be nullable (e.g., MauiTextView? / MauiMaterialTextView?) or otherwise structured so they’re never assigned null.

Suggested change
MauiTextView _mauiTextView;
MauiMaterialTextView _mauiMaterialTextView;
MauiTextView? _mauiTextView;
MauiMaterialTextView? _mauiMaterialTextView;

Copilot uses AI. Check for mistakes.
Comment on lines 40 to 53
void DetachLayoutChanged()
{
if (_mauiTextView is not null)
{
if (_mauiTextView != null)
{
_mauiTextView.LayoutChanged -= OnLayoutChanged;
_mauiTextView = null;
}
_mauiTextView.LayoutChanged -= OnLayoutChanged;
_mauiTextView = null;
}

if (_mauiMaterialTextView is not null)
{
_mauiMaterialTextView.LayoutChanged -= OnLayoutChanged;
_mauiMaterialTextView = null;
}
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

_mauiTextView and _mauiMaterialTextView are declared as non-nullable but are assigned null in DetachLayoutChanged(). In builds where nullable warnings are treated as errors, this can break compilation. These fields should be nullable (e.g., MauiTextView? / MauiMaterialTextView?) or otherwise structured so they’re never assigned null.

Copilot uses AI. Check for mistakes.
SetInheritedBindingContext(BottomItems, BindingContext);

// In Windows and Android, new cells are created when rebinding, so _isOpen is false.
// In iOS, CollectionView reuses cells, so SwipeView remain open (_isOpen = true).
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct grammar: “SwipeView remain open” → “SwipeView remains open”.

Suggested change
// In iOS, CollectionView reuses cells, so SwipeView remain open (_isOpen = true).
// In iOS, CollectionView reuses cells, so SwipeView remains open (_isOpen = true).

Copilot uses AI. Check for mistakes.
Comment on lines +77 to +106
}
else
{
handlersCollection.AddHandler<Label, LabelHandler>();
}
#else
handlersCollection.AddHandler<Label, LabelHandler>();
#endif
#if ANDROID
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Editor, EditorHandler2>();
}
else
{
handlersCollection.AddHandler<Editor, EditorHandler>();
}
#else
handlersCollection.AddHandler<Editor, EditorHandler>();
#endif
#if ANDROID
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Picker, PickerHandler2>();
}
else
{
handlersCollection.AddHandler<Picker, PickerHandler>();
}
#else
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Material3 handler registration pattern is repeated for multiple controls, which increases the chance of future drift (e.g., one control updated but others missed). Consider consolidating this into a small helper method/local function (or a single if (IsMaterial3Enabled) block registering all “2” handlers) to reduce duplication while keeping the preprocessor conditions readable.

Suggested change
}
else
{
handlersCollection.AddHandler<Label, LabelHandler>();
}
#else
handlersCollection.AddHandler<Label, LabelHandler>();
#endif
#if ANDROID
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Editor, EditorHandler2>();
}
else
{
handlersCollection.AddHandler<Editor, EditorHandler>();
}
#else
handlersCollection.AddHandler<Editor, EditorHandler>();
#endif
#if ANDROID
if (RuntimeFeature.IsMaterial3Enabled)
{
handlersCollection.AddHandler<Picker, PickerHandler2>();
}
else
{
handlersCollection.AddHandler<Picker, PickerHandler>();
}
#else
handlersCollection.AddHandler<Editor, EditorHandler2>();
handlersCollection.AddHandler<Picker, PickerHandler2>();
}
else
{
handlersCollection.AddHandler<Label, LabelHandler>();
handlersCollection.AddHandler<Editor, EditorHandler>();
handlersCollection.AddHandler<Picker, PickerHandler>();
}
#else
handlersCollection.AddHandler<Label, LabelHandler>();
handlersCollection.AddHandler<Editor, EditorHandler>();

Copilot uses AI. Check for mistakes.
Comment on lines +527 to +533
public override bool OnTouchEvent(MotionEvent e)
{
// If ItemsView is disabled, don't handle touch events
if (ItemsView?.IsEnabled == false)
{
return false;
}
Copy link

Copilot AI Mar 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This introduces new interaction behavior for disabled ItemsView on Android (touch handling/interception). Please add a corresponding automated test across TestCases.HostApp (UI surface) and TestCases.Shared.Tests (assertion) to verify disabled ItemsViews do not scroll/click and that re-enabling restores interactions, per repository device/UI testing guidelines.

Copilot uses AI. Check for mistakes.
The prior agent session used LC_ALL=C sort to resolve merge conflicts
in PublicAPI.Unshipped.txt files, which pushed the BOM-prefixed
#nullable enable line to the bottom. This caused RS0017 analyzer errors
fixed by PR #34320.

Add explicit guidance to prevent this: explain why plain sort breaks
these files and provide a safe conflict resolution script.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@jfversluis jfversluis force-pushed the fix/publicapi-sort-instructions branch from 34c0e26 to 224a93f Compare March 4, 2026 12:42
@jfversluis jfversluis merged commit 2c6474f into main Mar 5, 2026
4 of 5 checks passed
@jfversluis jfversluis deleted the fix/publicapi-sort-instructions branch March 5, 2026 11:17
This was referenced Mar 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants