static string GetName(ObjCProtocolDecl decl, ObjCMethodDecl method) { var sb = new StringBuilder(); if (method.IsClassMethod) { sb.Append('+'); } sb.Append(decl.Name); sb.Append("::"); sb.Append(GetSelector(method)); return(sb.ToString()); }
public override void VisitObjCProtocolDecl(ObjCProtocolDecl decl, VisitKind visitKind) { if (visitKind != VisitKind.Enter) { return; } if (!decl.IsDefinition) { return; } // check availability macros to see if the API is available on the OS and not deprecated if (!decl.IsAvailable()) { return; } var name = decl.Name; TypeDefinition td; if (!protocol_map.TryGetValue(name, out td)) { Console.WriteLine("!missing-protocol! {0} not bound", name); // other checks can't be done without an actual protocol to inspect return; } // build type selector-required map var map = new Dictionary <string, bool> (); foreach (var ca in td.CustomAttributes) { string export = null; string g_export = null; string s_export = null; bool is_required = false; bool is_property = false; switch (ca.Constructor.DeclaringType.Name) { case "ProtocolMemberAttribute": foreach (var p in ca.Properties) { switch (p.Name) { case "Selector": export = p.Argument.Value as string; break; case "GetterSelector": g_export = p.Argument.Value as string; break; case "SetterSelector": s_export = p.Argument.Value as string; break; case "IsRequired": is_required = (bool)p.Argument.Value; break; case "IsProperty": is_property = (bool)p.Argument.Value; break; } } break; } if (is_property) { if (g_export != null) { map.Add(g_export, is_required); } if (s_export != null) { map.Add(s_export, is_required); } } else { if (export != null) { map.Add(export, is_required); } } } var remaining = new Dictionary <string, bool> (map); // check that required members match the [Abstract] members foreach (ObjCMethodDecl method in decl.Methods) { // some members might not be part of the current platform if (!method.IsAvailable()) { continue; } var selector = GetSelector(method); if (selector == null) { continue; } // a .NET interface cannot have constructors - so we cannot enforce that on the interface if (IsInit(selector)) { continue; } bool is_abstract; if (map.TryGetValue(selector, out is_abstract)) { bool required = method.ImplementationControl == ObjCImplementationControl.Required; if (required) { if (!is_abstract) { Console.WriteLine("!incorrect-protocol-member! {0} is REQUIRED and should be abstract", GetName(decl, method)); } } else { if (is_abstract) { Console.WriteLine("!incorrect-protocol-member! {0} is OPTIONAL and should NOT be abstract", GetName(decl, method)); } } remaining.Remove(selector); } else if (!method.IsClassMethod) { // a .NET interface cannot have static methods - so we can only report missing instance methods Console.WriteLine("!missing-protocol-member! {0} not found", GetName(decl, method)); remaining.Remove(selector); } } foreach (var selector in remaining.Keys) { Console.WriteLine("!extra-protocol-member! unexpected selector {0}::{1} found", decl.Name, selector); } remaining.Clear(); map.Clear(); protocol_map.Remove(name); }
public override void VisitObjCProtocolDecl(ObjCProtocolDecl decl, VisitKind visitKind) { if (visitKind != VisitKind.Enter) { return; } if (!decl.IsDefinition) { return; } // check availability macros to see if the API is available on the OS and not deprecated if (!decl.IsAvailable()) { return; } var framework = Helpers.GetFramework(decl); if (framework == null) { return; } var name = decl.Name; TypeDefinition td; if (!protocol_map.TryGetValue(name, out td)) { if (!decl.IsDeprecated()) { Log.On(framework).Add($"!missing-protocol! {name} not bound"); } // other checks can't be done without an actual protocol to inspect return; } // build type selector-required map var map = new Dictionary <string, bool> (); foreach (var ca in td.CustomAttributes) { string export = null; string g_export = null; string s_export = null; bool is_required = false; bool is_property = false; bool is_static = false; switch (ca.Constructor.DeclaringType.Name) { case "ProtocolMemberAttribute": foreach (var p in ca.Properties) { switch (p.Name) { case "Selector": export = p.Argument.Value as string; break; case "GetterSelector": g_export = p.Argument.Value as string; break; case "SetterSelector": s_export = p.Argument.Value as string; break; case "IsRequired": is_required = (bool)p.Argument.Value; break; case "IsProperty": is_property = (bool)p.Argument.Value; break; case "IsStatic": is_static = (bool)p.Argument.Value; break; } } break; } if (is_property) { if (g_export != null) { if (is_static) { g_export = "+" + g_export; } map.Add(g_export, is_required); } if (s_export != null) { if (is_static) { s_export = "+" + s_export; } map.Add(s_export, is_required); } } else if (export != null) { if (is_static) { export = "+" + export; } map.Add(export, is_required); } } var deprecatedProtocol = (decl.DeclContext as Decl).IsDeprecated(); // don't report anything for deprecated protocols // (we still report some errors for deprecated members of non-deprecated protocols - because abstract/non-abstract can // change the managed API and we want to get things right, even if for deprecated members). if (!deprecatedProtocol) { var remaining = new Dictionary <string, bool> (map); // check that required members match the [Abstract] members foreach (ObjCMethodDecl method in decl.Methods) { // some members might not be part of the current platform if (!method.IsAvailable()) { continue; } var selector = GetSelector(method); if (selector == null) { continue; } // a .NET interface cannot have constructors - so we cannot enforce that on the interface if (IsInit(selector)) { continue; } if (method.IsClassMethod) { selector = "+" + selector; } bool is_abstract; if (map.TryGetValue(selector, out is_abstract)) { bool required = method.ImplementationControl == ObjCImplementationControl.Required; if (required) { if (!is_abstract) { Log.On(framework).Add($"!incorrect-protocol-member! {GetName (decl, method)} is REQUIRED and should be abstract"); } } else { if (is_abstract) { Log.On(framework).Add($"!incorrect-protocol-member! {GetName (decl, method)} is OPTIONAL and should NOT be abstract"); } } remaining.Remove(selector); } else if (!method.IsClassMethod) { // a .NET interface cannot have static methods - so we can only report missing instance methods if (!decl.IsDeprecated()) { Log.On(framework).Add($"!missing-protocol-member! {GetName (decl, method)} not found"); } remaining.Remove(selector); } } foreach (var selector in remaining.Keys) { Log.On(framework).Add($"!extra-protocol-member! unexpected selector {decl.Name}::{selector} found"); } remaining.Clear(); } map.Clear(); protocol_map.Remove(name); }