diff --git a/src/uikit.cs b/src/uikit.cs index c95d81de21..a8ed1bcd3a 100644 --- a/src/uikit.cs +++ b/src/uikit.cs @@ -8354,6 +8354,7 @@ namespace UIKit { [Export ("buttonWithType:")] [Static] UIButton FromType (UIButtonType type); + [Appearance] [Export ("contentEdgeInsets")] UIEdgeInsets ContentEdgeInsets {get;set;} @@ -8497,6 +8498,7 @@ namespace UIKit { [Export ("attributedTitleForState:")] NSAttributedString GetAttributedTitle (UIControlState state); + [Appearance] [Watch (6,0), TV (13,0), iOS (13,0)] [Export ("setPreferredSymbolConfiguration:forImageInState:")] void SetPreferredSymbolConfiguration ([NullAllowed] UIImageSymbolConfiguration configuration, UIControlState state); @@ -9327,6 +9329,7 @@ namespace UIKit { [Export ("backgroundImageForBarPosition:barMetrics:")] UIImage GetBackgroundImage (UIBarPosition barPosition, UIBarMetrics barMetrics); + [Appearance] [NoTV] [iOS (11,0)] [Export ("prefersLargeTitles")] @@ -11106,9 +11109,11 @@ namespace UIKit { [NullAllowed] UIImage ScopeBarBackgroundImage { get; set; } + [Appearance] [Export ("searchFieldBackgroundPositionAdjustment")] UIOffset SearchFieldBackgroundPositionAdjustment { get; set; } + [Appearance] [Export ("searchTextPositionAdjustment")] UIOffset SearchTextPositionAdjustment { get; set; } @@ -11152,9 +11157,11 @@ namespace UIKit { [Appearance] NSDictionary _GetScopeBarButtonTitleTextAttributes (UIControlState state); + [Appearance] [Export ("setPositionAdjustment:forSearchBarIcon:")] void SetPositionAdjustmentforSearchBarIcon (UIOffset adjustment, UISearchBarIcon icon); + [Appearance] [Export ("positionAdjustmentForSearchBarIcon:")] UIOffset GetPositionAdjustmentForSearchBarIcon (UISearchBarIcon icon); @@ -11564,6 +11571,7 @@ namespace UIKit { [Export ("selectedSegmentIndex")] nint SelectedSegment { get; set; } + [Appearance] [iOS (13,0), TV (13,0), Watch (6,0)] [NullAllowed, Export ("selectedSegmentTintColor", ArgumentSemantic.Strong)] UIColor SelectedSegmentTintColor { get; set; } @@ -11983,19 +11991,23 @@ namespace UIKit { [NullAllowed] UIColor BarTintColor { get; set; } + [Appearance] [NoTV] [iOS (7,0)] [Export ("itemPositioning")] UITabBarItemPositioning ItemPositioning { get; set; } + [Appearance] [iOS (7,0)] [Export ("itemWidth")] nfloat ItemWidth { get; set; } + [Appearance] [iOS (7,0)] [Export ("itemSpacing")] nfloat ItemSpacing { get; set; } + [Appearance] [NoTV] [iOS (7,0)] [Export ("barStyle")] @@ -12005,6 +12017,7 @@ namespace UIKit { [Export ("translucent")] bool Translucent { [Bind ("isTranslucent")] get; set; } + [Appearance] [iOS (10,0), TV (10,0)] [NullAllowed, Export ("unselectedItemTintColor", ArgumentSemantic.Copy)] UIColor UnselectedItemTintColor { get; set; } @@ -12206,10 +12219,12 @@ namespace UIKit { [Export ("selectedImage", ArgumentSemantic.Retain)][NullAllowed] UIImage SelectedImage { get; set; } + [Appearance] [iOS (10,0), TV (10,0)] [NullAllowed, Export ("badgeColor", ArgumentSemantic.Copy)] UIColor BadgeColor { get; set; } + [Appearance] [iOS (10,0), TV (10,0)] [Export ("setBadgeTextAttributes:forState:")] [Internal] @@ -12219,13 +12234,15 @@ namespace UIKit { [Wrap ("SetBadgeTextAttributes (textAttributes.GetDictionary (), state)")] void SetBadgeTextAttributes (UIStringAttributes textAttributes, UIControlState state); + [Appearance] [iOS (10,0), TV (10,0)] [Export ("badgeTextAttributesForState:")] - [Internal] - NSDictionary _GetBadgeTextAttributes (UIControlState state); + [EditorBrowsable (EditorBrowsableState.Advanced)] + [return: NullAllowed] + NSDictionary GetBadgeTextAttributesDictionary (UIControlState state); [iOS (10,0), TV (10,0)] - [Wrap ("new UIStringAttributes (_GetBadgeTextAttributes(state))")] + [Wrap ("new UIStringAttributes (GetBadgeTextAttributesDictionary(state))")] UIStringAttributes GetBadgeTextAttributes (UIControlState state); [Appearance] @@ -12943,11 +12960,13 @@ namespace UIKit { [Export ("multipleSelectionBackgroundView", ArgumentSemantic.Retain), NullAllowed] UIView MultipleSelectionBackgroundView { get; set; } + [Appearance] [NoTV] [iOS (7,0)] [Export ("separatorInset")] UIEdgeInsets SeparatorInset { get; set; } + [Appearance] [iOS (9,0)] // introduced in Xcode 7.1 SDK (iOS 9.1 but hidden in 9.0) [Export ("focusStyle", ArgumentSemantic.Assign)] UITableViewCellFocusStyle FocusStyle { get; set; } @@ -13676,12 +13695,14 @@ namespace UIKit { [Export ("initWithFrame:")] IntPtr Constructor (CGRect frame); + [Appearance] [Export ("barStyle")] UIBarStyle BarStyle { get; set; } [Export ("items", ArgumentSemantic.Copy)][NullAllowed] UIBarButtonItem [] Items { get; set; } + [Appearance] [Export ("translucent", ArgumentSemantic.Assign)] bool Translucent { [Bind ("isTranslucent")] get; set; } diff --git a/tests/xtro-sharpie/Runner.cs b/tests/xtro-sharpie/Runner.cs index 7c80d58980..a734247f9e 100644 --- a/tests/xtro-sharpie/Runner.cs +++ b/tests/xtro-sharpie/Runner.cs @@ -30,6 +30,7 @@ namespace Extrospection { new RequiresSuperCheck (), new DeprecatedCheck (), new NullabilityCheck (), + new UIAppearanceCheck (), // new ListNative (), // for debug }; foreach (var assemblyName in assemblyNames) { diff --git a/tests/xtro-sharpie/UIAppearanceCheck.cs b/tests/xtro-sharpie/UIAppearanceCheck.cs new file mode 100644 index 0000000000..bba95ee9bc --- /dev/null +++ b/tests/xtro-sharpie/UIAppearanceCheck.cs @@ -0,0 +1,138 @@ +// +// The rule reports +// +// !missing-ui-appearance-support! +// when an API does not have a [Appearance] attribute while ObjC headers have a UI_APPEARANCE_SELECTOR +// +// !extra-ui-appearance-support! +// when an API is decorated with [Appearance] attribute but ObjC headers don't have a UI_APPEARANCE_SELECTOR +// + +using System; +using System.Collections.Generic; + +using Mono.Cecil; + +using Clang.Ast; + +namespace Extrospection { + + public class UIAppearanceCheck : BaseVisitor { + + static HashSet appearance_types = new HashSet (); + static HashSet appearance_methods = new HashSet (); + static Dictionary methods = new Dictionary (); + + static MethodDefinition GetMethod (ObjCMethodDecl decl) + { + methods.TryGetValue (decl.GetName (), out var md); + return md; + } + + public override void VisitManagedType (TypeDefinition type) + { + if (!type.HasNestedTypes) + return; + + var tn = type.Name + "Appearance"; + foreach (var nt in type.NestedTypes) { + if (nt.Name == tn) + appearance_types.Add (nt); + } + } + + public override void VisitManagedMethod (MethodDefinition method) + { + var key = method.GetName (); + if (key != null) + methods [key] = method; + } + + public override void VisitObjCPropertyDecl (ObjCPropertyDecl decl) + { + // don't process methods (or types) that are unavailable for the current platform + if (!decl.IsAvailable () || !(decl.DeclContext as Decl).IsAvailable ()) + return; + + // does not look exposed, but part of the dump + if (decl.DumpToString ().IndexOf ("UI_APPEARANCE_SELECTOR", StringComparison.OrdinalIgnoreCase) < 0) + return; + + var getter = decl.Getter; + if (getter != null) + VisitObjCMethodDecl (getter); + var setter = decl.Setter; + if (setter != null) + VisitObjCMethodDecl (setter); + } + + public override void VisitObjCMethodDecl (ObjCMethodDecl decl, VisitKind visitKind) + { + if (visitKind != VisitKind.Enter) + return; + + // don't process methods (or types) that are unavailable for the current platform + if (!decl.IsAvailable () || !(decl.DeclContext as Decl).IsAvailable ()) + return; + + // does not look exposed, but part of the dump + if (decl.DumpToString ().IndexOf ("UI_APPEARANCE_SELECTOR", StringComparison.OrdinalIgnoreCase) < 0) + return; + + VisitObjCMethodDecl (decl); + } + + void VisitObjCMethodDecl (ObjCMethodDecl decl) + { + var framework = Helpers.GetFramework (decl); + if (framework == null) + return; + + var method = GetMethod (decl); + if (method == null) + return; + + var dt = method.DeclaringType; + if (dt.HasNestedTypes) { + foreach (var nt in dt.NestedTypes) { + if (nt.Name != dt.Name + "Appearance") + continue; + // find matching method, including parameters (we have overloads) + var fn = method.FullName; + int ms = fn.IndexOf ("::"); + fn = fn.Insert (ms, $"/{dt.Name}Appearance"); + foreach (var m in nt.Methods) { + if (m.FullName != fn) + continue; + appearance_methods.Add (m); // legit one + return; + } + } + } + + Log.On (framework).Add ($"!missing-ui-appearance-support! {method.GetName ()} is missing [Appearance]"); + } + + public override void End () + { + // looking for extra [Appearance] attributes + foreach (var t in appearance_types) { + if (!t.HasMethods) + continue; + foreach (var m in t.Methods) { + if (m.IsConstructor) + continue; + if (appearance_methods.Contains (m)) + continue; + var framework = Helpers.GetFramework (m); + var fn = m.FullName; + // don't report on the *Appearance type - but where the attribute was used + int ns = fn.IndexOf ('/'); + int ms = fn.IndexOf ("::", ns); + fn = fn.Remove (ns, ms - ns); + Log.On (framework).Add ($"!extra-ui-appearance-support! {fn} should NOT be decorated with [Appearance]"); + } + } + } + } +} diff --git a/tests/xtro-sharpie/common-UIKit.ignore b/tests/xtro-sharpie/common-UIKit.ignore index c214e2fcbd..3a0a9f331e 100644 --- a/tests/xtro-sharpie/common-UIKit.ignore +++ b/tests/xtro-sharpie/common-UIKit.ignore @@ -170,6 +170,30 @@ !missing-protocol! UIResponderStandardEditActions not bound !missing-protocol-conformance! UIResponder should conform to UIResponderStandardEditActions +## there's no UI_APPEARANCE_SELECTOR in headers - but they can (unofficially) work, YYMV +!extra-ui-appearance-support! UIKit.UIColor UIKit.UIBarButtonItem::get_TintColor() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UIBarButtonItem::set_TintColor(UIKit.UIColor) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UIButton::SetImage(UIKit.UIImage,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIColor UIKit.UIButton::get_CurrentTitleColor() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIColor UIKit.UIButton::get_CurrentTitleShadowColor() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIColor UIKit.UIButton::TitleColor(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIColor UIKit.UIButton::TitleShadowColor(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UIButton::BackgroundImageForState(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UIButton::get_CurrentBackgroundImage() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UIButton::get_CurrentImage() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UIButton::ImageForState(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UIView::set_TintColor(UIKit.UIColor) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIColor UIKit.UIView::get_TintColor() should NOT be decorated with [Appearance] + +## manually bound (better, stronger signature) on `[setS|s]copeBarButtonTitleTextAttributes:forState:` which is decorated with `UI_APPEARANCE_SELECTOR` +!extra-ui-appearance-support! System.Void UIKit.UISearchBar::SetScopeBarButtonTitle(UIKit.UITextAttributes,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UITextAttributes UIKit.UISearchBar::GetScopeBarButtonTitleTextAttributes(UIKit.UIControlState) should NOT be decorated with [Appearance] + +## [Wrap] over `[largeT|t]itleTextAttributes` which is decorated with `UI_APPEARANCE_SELECTOR` +!extra-ui-appearance-support! UIKit.UIStringAttributes UIKit.UINavigationBar::get_LargeTitleTextAttributes() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIStringAttributes UIKit.UINavigationBar::get_TitleTextAttributes() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UINavigationBar::set_LargeTitleTextAttributes(UIKit.UIStringAttributes) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UINavigationBar::set_TitleTextAttributes(UIKit.UIStringAttributes) should NOT be decorated with [Appearance] ## unsorted diff --git a/tests/xtro-sharpie/iOS-UIKit.ignore b/tests/xtro-sharpie/iOS-UIKit.ignore index 0197245024..1223ffc198 100644 --- a/tests/xtro-sharpie/iOS-UIKit.ignore +++ b/tests/xtro-sharpie/iOS-UIKit.ignore @@ -156,7 +156,6 @@ !missing-null-allowed! 'Foundation.NSDictionary UIKit.UIMotionEffect::ComputeKeyPathsAndRelativeValues(UIKit.UIOffset)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSDictionary UIKit.UISearchBar::_GetScopeBarButtonTitleTextAttributes(UIKit.UIControlState)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSDictionary UIKit.UISegmentedControl::_GetTitleTextAttributes(UIKit.UIControlState)' is missing an [NullAllowed] on return type -!missing-null-allowed! 'Foundation.NSDictionary`2 UIKit.UITabBarItem::_GetBadgeTextAttributes(UIKit.UIControlState)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSExtensionContext UIKit.UIViewController::get_ExtensionContext()' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSFileWrapper Foundation.NSAttributedString::GetFileWrapperFromRange(Foundation.NSRange,Foundation.NSDictionary,Foundation.NSError&)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSIndexPath UIKit.UICollectionView::IndexPathForCell(UIKit.UICollectionViewCell)' is missing an [NullAllowed] on return type @@ -517,3 +516,23 @@ ## These are inlined by protocol adoption so you can't really add such decoration !missing-requires-super! UIControl::contextMenuInteraction:willDisplayMenuForConfiguration:animator: is missing an [RequiresSuper] attribute !missing-requires-super! UIControl::contextMenuInteraction:willEndForConfiguration:animator: is missing an [RequiresSuper] attribute + +## manually bound (better, stronger signature) on `[setT|t]itleTextAttributes:forState:` which is decorated with `UI_APPEARANCE_SELECTOR` +!extra-ui-appearance-support! UIKit.UITextAttributes UIKit.UIBarItem::GetTitleTextAttributes(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UIBarItem::SetTitleTextAttributes(UIKit.UITextAttributes,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UITextAttributes UIKit.UINavigationBar::GetTitleTextAttributes() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UINavigationBar::SetTitleTextAttributes(UIKit.UITextAttributes) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UITextAttributes UIKit.UISegmentedControl::GetTitleTextAttributes(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISegmentedControl::SetTitleTextAttributes(UIKit.UITextAttributes,UIKit.UIControlState) should NOT be decorated with [Appearance] + +## there's no UI_APPEARANCE_SELECTOR in headers - but they can (unofficially) work, YYMV +!extra-ui-appearance-support! System.Void UIKit.UISlider::set_MaxValueImage(UIKit.UIImage) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISlider::set_MinValueImage(UIKit.UIImage) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UISlider::get_MaxValueImage() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UISlider::get_MinValueImage() should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISlider::SetMaxTrackImage(UIKit.UIImage,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISlider::SetMinTrackImage(UIKit.UIImage,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISlider::SetThumbImage(UIKit.UIImage,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UISlider::MaxTrackImage(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UISlider::MinTrackImage(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIImage UIKit.UISlider::ThumbImage(UIKit.UIControlState) should NOT be decorated with [Appearance] diff --git a/tests/xtro-sharpie/tvOS-UIKit.ignore b/tests/xtro-sharpie/tvOS-UIKit.ignore index 775dc50b9e..f6785497fa 100644 --- a/tests/xtro-sharpie/tvOS-UIKit.ignore +++ b/tests/xtro-sharpie/tvOS-UIKit.ignore @@ -158,7 +158,6 @@ !missing-null-allowed! 'Foundation.NSDictionary UIKit.UIMotionEffect::ComputeKeyPathsAndRelativeValues(UIKit.UIOffset)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSDictionary UIKit.UISearchBar::_GetScopeBarButtonTitleTextAttributes(UIKit.UIControlState)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSDictionary UIKit.UISegmentedControl::_GetTitleTextAttributes(UIKit.UIControlState)' is missing an [NullAllowed] on return type -!missing-null-allowed! 'Foundation.NSDictionary`2 UIKit.UITabBarItem::_GetBadgeTextAttributes(UIKit.UIControlState)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSExtensionContext UIKit.UIViewController::get_ExtensionContext()' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSFileWrapper Foundation.NSAttributedString::GetFileWrapperFromRange(Foundation.NSRange,Foundation.NSDictionary,Foundation.NSError&)' is missing an [NullAllowed] on return type !missing-null-allowed! 'Foundation.NSIndexPath UIKit.UICollectionView::IndexPathForCell(UIKit.UICollectionViewCell)' is missing an [NullAllowed] on return type @@ -405,3 +404,11 @@ !missing-null-allowed! 'UIKit.UIWindow UIKit.UIApplication::get_KeyWindow()' is missing an [NullAllowed] on return type !missing-null-allowed! 'UIKit.UIWindow UIKit.UITouch::get_Window()' is missing an [NullAllowed] on return type !missing-null-allowed! 'UIKit.UIWindow UIKit.UIView::get_Window()' is missing an [NullAllowed] on return type + +## same as iOS but we used the newer name if `UIStringAttributes` so it requires a different entry +!extra-ui-appearance-support! System.Void UIKit.UIBarItem::SetTitleTextAttributes(UIKit.UIStringAttributes,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISearchBar::SetScopeBarButtonTitle(UIKit.UIStringAttributes,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! System.Void UIKit.UISegmentedControl::SetTitleTextAttributes(UIKit.UIStringAttributes,UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIStringAttributes UIKit.UIBarItem::GetTitleTextAttributes(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIStringAttributes UIKit.UISearchBar::GetScopeBarButtonTitleTextAttributes(UIKit.UIControlState) should NOT be decorated with [Appearance] +!extra-ui-appearance-support! UIKit.UIStringAttributes UIKit.UISegmentedControl::GetTitleTextAttributes(UIKit.UIControlState) should NOT be decorated with [Appearance] \ No newline at end of file diff --git a/tests/xtro-sharpie/xtro-sharpie.csproj b/tests/xtro-sharpie/xtro-sharpie.csproj index 894b15c234..6e79e4c27b 100644 --- a/tests/xtro-sharpie/xtro-sharpie.csproj +++ b/tests/xtro-sharpie/xtro-sharpie.csproj @@ -38,12 +38,12 @@ Project - iphoneos13.4-arm64.pch ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/64bits/Xamarin.iOS.dll ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/Xamarin.iOS/OpenTK-1.0.dll + iphoneos14.0-arm64.pch ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/64bits/Xamarin.iOS.dll ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/Current/lib/mono/Xamarin.iOS/OpenTK-1.0.dll . Project - appletvos12.2-arm64.pch ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono/Xamarin.TVOS/Xamarin.TVOS.dll ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono/Xamarin.TVOS/OpenTK-1.0.dll + appletvos13.4-arm64.pch ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono/Xamarin.TVOS/Xamarin.TVOS.dll ../../_ios-build/Library/Frameworks/Xamarin.iOS.framework/Versions/git/lib/mono/Xamarin.TVOS/OpenTK-1.0.dll . @@ -80,6 +80,7 @@ +