Esempio n. 1
0
        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);
        }