Exemplo n.º 1
0
        void ProcessObjcSelector(string fullname, VersionTuple objcVersion)
        {
            string[] nameParts = fullname.Split(new string[] { "::" }, StringSplitOptions.None);

            string objcClassName = nameParts[0];
            string selector      = nameParts[1];

            TypeDefinition managedType = ManagedTypes.FirstOrDefault(x => Helpers.GetName(x) == objcClassName);

            if (managedType != null)
            {
                var framework = Helpers.GetFramework(managedType);
                if (framework == null)
                {
                    return;
                }

                // If the entire type is deprecated, call it good enough
                if (AttributeHelpers.HasAnyDeprecationForCurrentPlatform(managedType))
                {
                    return;
                }

                var matchingMethod = managedType.Methods.FirstOrDefault(x => x.GetSelector() == selector && x.IsPublic);
                if (matchingMethod != null)
                {
                    ProcessItem(matchingMethod, fullname, objcVersion, framework);
                }
            }
        }
Exemplo n.º 2
0
        public override void VisitManagedType(TypeDefinition type)
        {
            // exclude non enum and nested enums, e.g. bunch of Selector enums in CTFont
            if (!type.IsEnum || type.IsNested)
            {
                return;
            }

            var name = type.Name;

            // e.g. WatchKit.WKErrorCode and WebKit.WKErrorCode :-(
            if (!enums.TryGetValue(name, out var td))
            {
                enums.Add(name, type);
            }
            else
            {
                var(t1, t2) = Helpers.Sort(type, td);
                if (t1.Namespace.StartsWith("OpenTK.", StringComparison.Ordinal))
                {
                    // OpenTK duplicate a lots of enums between it's versions
                }
                else if (t1.IsNotPublic && String.IsNullOrEmpty(t1.Namespace))
                {
                    // ignore special, non exposed types
                }
                else
                {
                    var framework = Helpers.GetFramework(t1);
                    Log.On(framework).Add($"!duplicate-type-name! {name} enum exists as both {t1.FullName} and {t2.FullName}");
                }
            }
        }
Exemplo n.º 3
0
 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]");
         }
     }
 }
Exemplo n.º 4
0
        void Visit(ObjCMethodDecl decl)
        {
            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            var method = GetMethod(decl);

            if (method == null)
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            if (!decl.HasAttr <ObjCRequiresSuperAttr> ())
            {
                if (method.RequiresSuper())
                {
                    Log.On(framework).Add($"!extra-requires-super! {method.GetName ()} is incorrectly decorated with an [RequiresSuper] attribute");
                }
            }
            else if (!method.RequiresSuper())
            {
                Log.On(framework).Add($"!missing-requires-super! {method.GetName ()} is missing an [RequiresSuper] attribute");
            }
        }
Exemplo n.º 5
0
        public override void VisitManagedType(TypeDefinition type)
        {
            // exclude non enum and nested enums, e.g. bunch of Selector enums in CTFont
            if (!type.IsEnum || type.IsNested)
            {
                return;
            }

            var name = type.Name;

            // e.g. WatchKit.WKErrorCode and WebKit.WKErrorCode :-(
            if (!enums.TryGetValue(name, out var td))
            {
                enums.Add(name, type);
            }
            else if (td.Namespace.StartsWith("OpenTK.", StringComparison.Ordinal))
            {
                // OpenTK duplicate a lots of enums between it's versions
            }
            else
            {
                var sorted    = Helpers.Sort(type.FullName, td.FullName);
                var framework = Helpers.GetFramework(type);
                Log.On(framework).Add($"!duplicate-type-name! {name} enum exists as both {sorted.Item1} and {sorted.Item2}");
            }
        }
Exemplo n.º 6
0
        public override void VisitVarDecl(VarDecl decl)
        {
            if (!decl.IsExternC)
            {
                return;
            }
            if (!decl.PresumedLoc.FileName.Contains(".framework"))
            {
                return;
            }

            if (!decl.IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var name = decl.ToString();

            if (!fields.TryGetValue(name, out var mr))
            {
                Log.On(framework).Add($"!missing-field! {name} not bound");
            }
            else
            {
                fields.Remove(name);
            }
        }
Exemplo n.º 7
0
        void ProcessObjcSelector(string fullname, VersionTuple objcVersion)
        {
            var    class_method  = fullname [0] == '+';
            var    n             = fullname.IndexOf("::");
            string objcClassName = fullname.Substring(class_method ? 1: 0, n);
            string selector      = fullname.Substring(n + 2);

            TypeDefinition managedType = ManagedTypes.FirstOrDefault(x => Helpers.GetName(x) == objcClassName);

            if (managedType != null)
            {
                var framework = Helpers.GetFramework(managedType);
                if (framework == null)
                {
                    return;
                }

                // If the entire type is deprecated, call it good enough
                if (AttributeHelpers.HasAnyDeprecationForCurrentPlatform(managedType))
                {
                    return;
                }

                var matchingMethod = managedType.Methods.FirstOrDefault(x => x.GetSelector() == selector && x.IsPublic && x.IsStatic == class_method);
                if (matchingMethod != null)
                {
                    ProcessItem(matchingMethod, fullname, objcVersion, framework);
                }
            }
        }
Exemplo n.º 8
0
        public override void VisitObjCMethodDecl(ObjCMethodDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            // protocol members are checked in ObjCProtocolCheck
            if (decl.DeclContext is ObjCProtocolDecl)
            {
                return;
            }

            // don't process methods (or types) that are unavailable for the current platform
            if (!decl.IsAvailable() || !(decl.DeclContext as Decl).IsAvailable())
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            string selector = decl.GetSelector();

            if (String.IsNullOrEmpty(selector))
            {
                return;
            }

            var  name  = (decl.IsClassMethod ? "+" : String.Empty) + decl.QualifiedName;
            bool found = qualified_selectors.Contains(name);

            if (!found)
            {
                // a category could be inlined into the type it extend
                var category = decl.DeclContext as ObjCCategoryDecl;
                if (category != null)
                {
                    var cname = category.Name;
                    if (cname == null)
                    {
                        name = GetCategoryBase(category) + name;
                    }
                    else
                    {
                        name = name.ReplaceFirstInstance(cname, GetCategoryBase(category));
                    }
                    found = qualified_selectors.Contains(name);
                }
            }
            if (!found)
            {
                Log.On(framework).Add($"!missing-selector! {name} not bound");
            }
        }
Exemplo n.º 9
0
        bool IsExtVector(Decl decl, QualType type, ref string simd_type)
        {
            var rv = false;
            var t  = type.CanonicalQualType.Type;

            // Unpoint the type
            var pointerType = t as Clang.Ast.PointerType;

            if (pointerType != null)
            {
                t = pointerType.PointeeQualType.Type;
            }

            if (t.Kind == TypeKind.ExtVector)
            {
                rv = true;
            }
            else
            {
                var r = (t as RecordType)?.Decl;
                if (r != null)
                {
                    foreach (var f in r.Fields)
                    {
                        var qt = f.QualType.CanonicalQualType.Type;
                        if (qt.Kind == TypeKind.ExtVector)
                        {
                            rv = true;
                            break;
                        }
                        var at = qt as ConstantArrayType;
                        if (at != null)
                        {
                            if (at.ElementType.Type.Kind == TypeKind.ExtVector)
                            {
                                rv = true;
                                break;
                            }
                        }
                    }
                }
            }

            var typeName = type.ToString();

            if (!rv && typeName.Contains("simd"))
            {
                var framework = Helpers.GetFramework(decl);
                Log.On(framework).Add($"!unknown-simd-type! Could not detect that {typeName} is a Simd type, but its name contains 'simd'. Something needs fixing in SimdCheck.cs");
            }

            if (rv)
            {
                simd_type = typeName;
            }

            return(rv);
        }
Exemplo n.º 10
0
        public override void VisitObjCInterfaceDecl(ObjCInterfaceDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            var name = decl.Name;

            // 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;
            }

            if (!type_map.TryGetValue(name, out var td))
            {
                Log.On(framework).Add($"!missing-type! {name} not bound");
                // other checks can't be done without an actual type to inspect
                return;
            }

            // check base type
            var nbt = decl.SuperClass?.Name;
            var mbt = td.BaseType?.Resolve().GetName();

            if (nbt != mbt)
            {
                Log.On(framework).Add($"!wrong-base-type! {name} expected {nbt} actual {mbt}");
            }

            // check protocols
            foreach (var protocol in decl.Protocols)
            {
                var pname = protocol.Name;
                if (!ImplementProtocol(pname, td))
                {
                    Log.On(framework).Add($"!missing-protocol-conformance! {name} should conform to {pname}");
                }
            }

            // TODO : check for extraneous protocols

            type_map.Remove(name);
        }
Exemplo n.º 11
0
 public override void End()
 {
     // at this stage anything else we have is not something we could find in Apple's headers
     foreach (var kvp in fields)
     {
         var extra     = kvp.Key;
         var framework = Helpers.GetFramework(kvp.Value);
         Log.On(framework).Add($"!unknown-field! {extra} bound");
     }
 }
Exemplo n.º 12
0
 public override void End()
 {
     // at this stage anything else we have is not something we could find in Apple's headers
     // e.g. a typo in the name
     foreach (var kvp in dllimports)
     {
         var extra     = kvp.Key;
         var framework = Helpers.GetFramework(kvp.Value);
         Log.On(framework).Add($"!unknown-pinvoke! {extra} bound");
     }
 }
        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;
            }

            var method = GetMethod(decl);

            // don't report missing [DesignatedInitializer] for types that are not bound - that's a different problem
            if (method == null)
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var designated_initializer = method.IsDesignatedInitializer();

            if (!method.IsConstructor)
            {
                if (designated_initializer)
                {
                    Log.On(framework).Add($"!incorrect-designated-initializer! {method.GetName ()} is not a constructor");
                }
            }
            else if (decl.IsDesignatedInitializer)
            {
                if (!designated_initializer)
                {
                    Log.On(framework).Add($"!missing-designated-initializer! {method.GetName ()} is missing an [DesignatedInitializer] attribute");
                }
            }
            else
            {
                if (designated_initializer)
                {
                    Log.On(framework).Add($"!extra-designated-initializer! {method.GetName ()} is incorrectly decorated with an [DesignatedInitializer] attribute");
                }
            }
        }
Exemplo n.º 14
0
        void ProcessObjcEntry(string objcClassName, VersionTuple objcVersion)
        {
            TypeDefinition managedType = ManagedTypes.FirstOrDefault(x => Helpers.GetName(x) == objcClassName && x.IsPublic);

            if (managedType != null)
            {
                var framework = Helpers.GetFramework(managedType);
                if (framework != null)
                {
                    ProcessItem(managedType, Helpers.GetName(managedType), objcVersion, framework);
                }
            }
        }
Exemplo n.º 15
0
 public override void End()
 {
     // report any [Native] decorated enum for which we could not find a match in the header files
     // e.g. a typo in the name
     foreach (var extra in enums)
     {
         var t = extra.Value;
         if (!IsNative(t))
         {
             continue;
         }
         var framework = Helpers.GetFramework(t);
         Log.On(framework).Add($"!unknown-native-enum! {extra.Key} bound");
     }
 }
Exemplo n.º 16
0
        public override void VisitFunctionDecl(FunctionDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            // skip macros : we generally implement them but their name is lost (so no matching is possible)
            if (!decl.IsExternC)
            {
                return;
            }

            var name = decl.Name;

            // do not consider _* or __* as public API that should be bound
            if (name [0] == '_')
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            // check availability macros to see if the API is available on the OS and not deprecated
            if (!decl.IsAvailable())
            {
                return;
            }

            if (!dllimports.ContainsKey(name))
            {
                // if we find functions without matching DllImport then we report them
                // but don't report deprecated functions
                if (!decl.IsDeprecated())
                {
                    Log.On(framework).Add($"!missing-pinvoke! {name} is not bound");
                }
                return;
            }

            dllimports.Remove(name);
        }
Exemplo n.º 17
0
        public override void VisitObjCCategoryDecl(ObjCCategoryDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }

            var categoryName = decl.Name;

            if (categoryName == null)
            {
                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 ciName = decl.ClassInterface.Name;

            if (!type_map_copy.TryGetValue(ciName, out var td))
            {
                // other checks can't be done without an actual type to inspect
                return;
            }

            // check protocols
            foreach (var protocol in decl.Protocols)
            {
                var pname = protocol.Name;
                if (!ImplementProtocol(pname, td))
                {
                    Log.On(framework).Add($"!missing-protocol-conformance! {ciName} should conform to {pname} (defined in '{categoryName}' category)");
                }
            }
        }
Exemplo n.º 18
0
        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]");
        }
Exemplo n.º 19
0
        public override void VisitObjCPropertyDecl(ObjCPropertyDecl decl)
        {
            // protocol members are checked in ObjCProtocolCheck
            if (decl.DeclContext is ObjCProtocolDecl)
            {
                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 nativeArgumentSemantic = decl.Attributes.ToArgumentSemantic();
            var nativeMethodDefinition = decl.QualifiedName;

            if (qualified_properties.TryGetValue(nativeMethodDefinition, out var managedArgumentSemanticList))
            {
                foreach (var entry in managedArgumentSemanticList)
                {
                    var method = entry.Item1;
                    var managedArgumentSemantic = entry.Item2;

                    if (managedArgumentSemantic != nativeArgumentSemantic)
                    {
                        // FIXME: only Copy mistakes are reported now
                        if (managedArgumentSemantic == Helpers.ArgumentSemantic.Copy || nativeArgumentSemantic == Helpers.ArgumentSemantic.Copy)
                        {
                            // FIXME: rule disactivated for now
                            // Log.On (framework).Add ($"!incorrect-argument-semantic! Native '{nativeMethodDefinition}' is declared as ({nativeArgumentSemantic.ToUsableString ().ToLowerInvariant ()}) but mapped to 'ArgumentSemantic.{managedArgumentSemantic.ToUsableString ()}' in '{method}'");
                        }
                    }
                }
            }
        }
Exemplo n.º 20
0
        bool IsSimdType(Decl decl, QualType type, ref string simd_type, ref bool requires_marshal_directive)
        {
            var str = Undecorate(type.ToString());

            if (type_mapping.TryGetValue(str, out var info))
            {
                requires_marshal_directive = true;
                simd_type = str;
                return(true);
            }

            if (IsExtVector(decl, type, ref simd_type))
            {
                var framework = Helpers.GetFramework(decl);
                Log.On(framework).Add($"!unknown-simd-type-mapping! The Simd type {simd_type} does not have a mapping to a managed type. Please add one in SimdCheck.cs");
            }

            return(false);
        }
Exemplo n.º 21
0
        void ProcessCFunction(string fullname, VersionTuple objcVersion)
        {
            if (dllimports.TryGetValue(fullname, out var method))
            {
                var dt        = method.DeclaringType;
                var framework = Helpers.GetFramework(dt);
                if (framework == null)
                {
                    return;
                }

                // If the entire type is deprecated, call it good enough
                if (AttributeHelpers.HasAnyDeprecationForCurrentPlatform(dt))
                {
                    return;
                }

                ProcessItem(method, fullname, objcVersion, framework);
            }
        }
Exemplo n.º 22
0
        public override void VisitManagedType(TypeDefinition type)
        {
            if (!type.HasCustomAttributes)
            {
                return;
            }

            string rname   = null;
            bool   wrapper = true;
            bool   skip    = false;

            foreach (var ca in type.CustomAttributes)
            {
                switch (ca.Constructor.DeclaringType.Name)
                {
                case "RegisterAttribute":
                    rname = type.Name;
                    if (ca.HasConstructorArguments)
                    {
                        rname = (ca.ConstructorArguments [0].Value as string);
                        if (ca.ConstructorArguments.Count > 1)
                        {
                            wrapper = (bool)ca.ConstructorArguments [1].Value;
                        }
                    }
                    if (ca.HasProperties)
                    {
                        foreach (var arg in ca.Properties)
                        {
                            switch (arg.Name)
                            {
                            case "Wrapper":
                                wrapper = (bool)arg.Argument.Value;
                                break;

                            case "SkipRegistration":
                                skip = (bool)arg.Argument.Value;
                                break;
                            }
                        }
                    }
                    break;

                case "ProtocolAttribute":
                    // exclude protocols
                    return;
                }
            }
            if (!skip && wrapper && !String.IsNullOrEmpty(rname))
            {
                TypeDefinition td;
                if (!type_map.TryGetValue(rname, out td))
                {
                    type_map.Add(rname, type);
                    type_map_copy.Add(rname, type);
                }
                else
                {
                    // always report in the same order (for unique error messages)
                    var sorted    = Helpers.Sort(type, td);
                    var framework = Helpers.GetFramework(sorted.Item1);
                    Log.On(framework).Add($"!duplicate-register! {rname} exists as both {sorted.Item1.FullName} and {sorted.Item2.FullName}");
                }
            }
        }
Exemplo n.º 23
0
        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;
            }

            var method = GetMethod(decl);

            // don't report missing nullability on types that are not bound - that's a different problem
            if (method == null)
            {
                return;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var t = method.DeclaringType;
            // look for [NullableContext] for defaults
            var managed_default_nullability = GetNullableContext(method);

            if (managed_default_nullability == Null.Oblivious)
            {
                managed_default_nullability = GetNullableContext(t);
            }

            // check parameters
            // categories have an offset of 1 for the extension method type (spotted as static types)
            int i = t.IsSealed && t.IsAbstract ? 1 : 0;

            foreach (var p in decl.Parameters)
            {
                var mp = method.Parameters [i++];
                // a managed `out` value does not need to be inialized, won't be null (but can be ignored)
                if (mp.IsOut)
                {
                    continue;
                }

                var pt = mp.ParameterType;
                // if bound as `IntPtr` then nullability attributes won't be present
                if (pt.IsValueType)
                {
                    continue;
                }

                Null parameter_nullable;

                // if we used a type by reference (e.g. `ref float foo`); or a nullable type (e.g. `[BindAs]`)
                // then assume it's meant as a nullable type) without additional decorations
                if (pt.IsByReference || pt.FullName.StartsWith("System.Nullable`1<", StringComparison.Ordinal))
                {
                    parameter_nullable = Null.Annotated;
                }
                else
                {
                    // check C# 8 compiler attributes
                    var nullable = GetNullable(mp);
                    if (nullable.Length > 1)
                    {
                        // check the type itself, TODO check the generics (don't think we have such cases yet)
                        parameter_nullable = nullable [0];
                    }
                    else if (nullable.Length == 0)
                    {
                        parameter_nullable = managed_default_nullability;
                    }
                    else
                    {
                        parameter_nullable = nullable [0];
                    }
                }

                // match with native and, if needed, report discrepancies
                p.QualType.Type.GetNullability(p.AstContext, out var nullability);
                switch (nullability)
                {
                case NullabilityKind.NonNull:
                    if (parameter_nullable == Null.Annotated)
                    {
                        Log.On(framework).Add($"!extra-null-allowed! '{method.FullName}' has a extraneous [NullAllowed] on parameter #{i-1}");
                    }
                    break;

                case NullabilityKind.Nullable:
                    if (parameter_nullable != Null.Annotated)
                    {
                        Log.On(framework).Add($"!missing-null-allowed! '{method.FullName}' is missing an [NullAllowed] on parameter #{i-1}");
                    }
                    break;

                case NullabilityKind.Unspecified:
                    break;
                }
            }

            // with .net a constructor will always return something (or throw)
            // that's not the case in ObjC where `init*` can return `nil`
            if (method.IsConstructor)
            {
                return;
            }

            var mrt = method.ReturnType;

            // if bound as an `IntPtr` then the nullability will not be visible in the metadata
            if (mrt.IsValueType)
            {
                return;
            }

            Null return_nullable;

            // if we used a nullable type (e.g. [BindAs] then assume it's meant as a nullable type) without additional decorations
            if (mrt.FullName.StartsWith("System.Nullable`1<", StringComparison.Ordinal))
            {
                return_nullable = Null.Annotated;
            }
            else
            {
                ICustomAttributeProvider cap;
                // the managed attributes are on the property, not the special methods
                if (method.IsGetter)
                {
                    var property = method.FindProperty();
                    // also `null_resettable` will only show something (natively) on the setter (since it does not return null, but accept it)
                    // in this case we'll trust xtro checking the setter only (if it exists, if not then it can't be `null_resettable`)
                    if (property.SetMethod != null)
                    {
                        return;
                    }
                    cap = property;
                }
                else
                {
                    cap = method.MethodReturnType;
                }
                Null [] mrt_nullable = GetNullable(cap);

                if (mrt_nullable.Length > 1)
                {
                    // check the type itself, TODO check the generics (don't think we have such cases yet)
                    return_nullable = mrt_nullable [0];
                }
                else if (mrt_nullable.Length == 0)
                {
                    return_nullable = managed_default_nullability;
                }
                else
                {
                    return_nullable = mrt_nullable [0];
                }
            }

            var rt = decl.ReturnQualType;

            rt.Type.GetNullability(decl.AstContext, out var rnull);
            switch (rnull)
            {
            case NullabilityKind.NonNull:
                if (return_nullable == Null.Annotated)
                {
                    Log.On(framework).Add($"!extra-null-allowed! '{method}' has a extraneous [NullAllowed] on return type");
                }
                break;

            case NullabilityKind.Nullable:
                if (return_nullable != Null.Annotated)
                {
                    Log.On(framework).Add($"!missing-null-allowed! '{method}' is missing an [NullAllowed] on return type");
                }
                break;

            case NullabilityKind.Unspecified:
                break;
            }
        }
Exemplo n.º 24
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 framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var            name = decl.Name;
            TypeDefinition td;

            if (!protocol_map.TryGetValue(name, out td))
            {
                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 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
                    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);
        }
Exemplo n.º 25
0
        public override void VisitEnumDecl(EnumDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            string name = decl.Name;

            if (name == null)
            {
                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 mname = Helpers.GetManagedName(name);

            if (!enums.TryGetValue(mname, out var type))
            {
                Log.On(framework).Add($"!missing-enum! {name} not bound");
                return;
            }
            else
            {
                enums.Remove(mname);
            }

            int  native_size = 4;
            bool native      = false;

            // FIXME: this can be simplified
            switch (decl.IntegerQualType.ToString())
            {
            case "NSInteger":
            case "NSUInteger":
            case "CFIndex":
            case "CFOptionFlags":
            case "AVAudioInteger":
                native_size = 8;                 // in managed code it's always the largest size
                native      = true;
                break;

            case "unsigned long":
            case "unsigned int":
            case "int32_t":
            case "uint32_t":
            case "int":
            case "GLint":
            case "GLuint":
            case "GLenum":
            case "SInt32":
            case "UInt32":
            case "OptionBits":             // UInt32
            case "long":
            case "FourCharCode":
            case "OSStatus":
                break;

            case "int64_t":
            case "uint64_t":
            case "unsigned long long":
            case "CVOptionFlags":             // uint64_t
                native_size = 8;
                break;

            case "UInt16":
            case "int16_t":
            case "uint16_t":
            case "short":
                native_size = 2;
                break;

            case "UInt8":
            case "int8_t":
            case "uint8_t":
                native_size = 1;
                break;

            default:
                throw new NotImplementedException(decl.IntegerQualType.ToString());
            }

            // check correct [Native] decoration
            if (native)
            {
                if (!IsNative(type))
                {
                    Log.On(framework).Add($"!missing-enum-native! {name}");
                }
            }
            else
            {
                if (IsNative(type))
                {
                    Log.On(framework).Add($"!extra-enum-native! {name}");
                }
            }

            int managed_size = 4;

            switch (GetEnumUnderlyingType(type).Name)
            {
            case "Byte":
            case "SByte":
                managed_size = 1;
                break;

            case "Int16":
            case "UInt16":
                managed_size = 2;
                break;

            case "Int32":
            case "UInt32":
                break;

            case "Int64":
            case "UInt64":
                managed_size = 8;
                break;

            default:
                throw new NotImplementedException();
            }
            if (native_size != managed_size)
            {
                Log.On(framework).Add($"!wrong-enum-size! {name} managed {managed_size} vs native {native_size}");
            }
        }
Exemplo n.º 26
0
        // most selectors will be found in [Export] attributes
        public override void VisitManagedMethod(MethodDefinition method)
        {
            // Don't care about methods that don't have [Export] attributes
            if (!method.HasCustomAttributes)
            {
                return;
            }

            // We don't care about 'void' functions
            if (method.ReturnType.FullName == "System.Void")
            {
                return;
            }

            // Value types can't need '[return: Release]'
            if (method.ReturnType.IsValueType)
            {
                return;
            }

            string family              = null;
            string selector            = null;
            bool   hasReleaseAttribute = false;

            if (method.MethodReturnType.HasCustomAttributes)
            {
                foreach (var ca in method.MethodReturnType.CustomAttributes)
                {
                    switch (ca.Constructor.DeclaringType.Name)
                    {
                    case "ReleaseAttribute":
                        hasReleaseAttribute = true;
                        break;
                    }
                }
            }

            foreach (var ca in method.CustomAttributes)
            {
                switch (ca.Constructor.DeclaringType.Name)
                {
                case "ExportAttribute":
                    selector = (string)ca.ConstructorArguments [0].Value;

                    // We need to compute the selector's method family
                    // https://clang.llvm.org/docs/AutomaticReferenceCounting.html#method-families

                    // A selector is in a certain selector family if ignoring any leading underscore the first component of the selector either consists entirely
                    // of the name of the method family or it begins with that name followed by a character other than a lowercase letter
                    var firstLetter             = 0;
                    var firstNonLowercaseLetter = selector.Length;
                    for (var i = 0; i < selector.Length; i++)
                    {
                        var c = selector [i];

                        if (firstLetter == i && c == '_')
                        {
                            // ... ignoring any leading underscores ...
                            firstLetter++;
                        }
                        else if (c < 'a' || c > 'z')
                        {
                            firstNonLowercaseLetter = i;
                            break;
                        }
                    }
                    family = selector.Substring(0, firstNonLowercaseLetter - firstLetter);
                    break;
                }
            }

            switch (family)
            {
            case "init":             // in many cases we have custom init/constructor code, which seems to be correct, so ignore the 'init' family for now.
                break;

            case "alloc":
            case "copy":
            case "mutableCopy":
            case "new":
                if (!hasReleaseAttribute)
                {
                    var framework = Helpers.GetFramework(method);
                    Log.On(framework).Add($"!missing-release-attribute-on-return-value! {method.FullName}'s selector's ('{selector}') Objective-C method family ('{family}') indicates that the native method returns a retained object, and as such a '[return: Release]' attribute is required.");
                }
                break;

            default:
                break;
            }
        }
Exemplo n.º 27
0
        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;
            }

            var framework = Helpers.GetFramework(decl);

            if (framework == null)
            {
                return;
            }

            var simd_type = string.Empty;
            var requires_marshal_directive = false;
            var native_simd = ContainsSimdTypes(decl, ref simd_type, ref requires_marshal_directive);

            ManagedSimdInfo info;

            managed_methods.TryGetValue(decl.GetName(), out info);
            var method = info?.Method;

            if (!native_simd)
            {
                if (method != null)
                {
                    // The managed method uses types that were incorrectly used in place of the correct Simd types,
                    // but the native method doesn't use the native Simd types. This means the binding is correct.
                }
                else
                {
                    // Neither the managed nor the native method have anything to do with Simd types.
                }
                return;
            }

            if (method == null)
            {
                // Could not map the native method to a managed method.
                // This needs investigation, to see why the native method couldn't be mapped.

                // Check if this is new API, in which case it probably couldn't be mapped because we haven't bound it.
                var is_new      = false;
                var attrs       = decl.Attrs.ToList();
                var parentClass = decl.DeclContext as Decl;
                if (parentClass != null)
                {
                    attrs.AddRange(parentClass.Attrs);
                }

                foreach (var attr in attrs)
                {
                    var av_attr = attr as AvailabilityAttr;
                    if (av_attr == null)
                    {
                        continue;
                    }
                    if (av_attr.Platform.Name != "ios")
                    {
                        continue;
                    }
                    if (av_attr.Introduced.Major >= 11)
                    {
                        is_new = true;
                        break;
                    }
                }
                if (is_new && !very_strict)
                {
                    return;
                }
                if (!strict)
                {
                    return;
                }
                Log.On(framework).Add($"!missing-simd-managed-method! {decl}: could not find a managed method for the native method {decl.GetName ()} (selector: {decl.Selector}). Found the simd type '{simd_type}' in the native signature.");
                return;
            }

            if (!info.ContainsInvalidMappingForSimd)
            {
                // The managed method does not have any types that are incorrect for Simd.
                if (requires_marshal_directive)
                {
                    CheckMarshalDirective(method, simd_type);
                }
                return;
            }

            if (method.IsObsolete())
            {
                // We have a potentially broken managed method, but it's obsolete. That's fine.
                return;
            }

            if (requires_marshal_directive)
            {
                CheckMarshalDirective(method, simd_type);
            }

            // We have a potentially broken managed method. This needs fixing/investigation.
            Log.On(framework).Add($"!unknown-simd-type-in-signature! {method}: the native signature has a simd type ({simd_type}), while the corresponding managed method is using an incorrect (non-simd) type.");
        }
Exemplo n.º 28
0
        public override void VisitEnumDecl(EnumDecl decl, VisitKind visitKind)
        {
            if (visitKind != VisitKind.Enter)
            {
                return;
            }
            if (!decl.IsDefinition)
            {
                return;
            }

            string name = decl.Name;

            if (name == null)
            {
                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 mname = Helpers.GetManagedName(name);

            // If our enum is obsoleted, then don't process it.
            if (obsoleted_enums.ContainsKey(mname))
            {
                return;
            }

            if (!enums.TryGetValue(mname, out var type))
            {
                Log.On(framework).Add($"!missing-enum! {name} not bound");
                return;
            }
            else
            {
                enums.Remove(mname);
            }

            int  native_size = 4;
            bool native      = false;

            // FIXME: this can be simplified
            switch (decl.IntegerQualType.ToString())
            {
            case "NSInteger":
            case "NSUInteger":
            case "CFIndex":
            case "CFOptionFlags":
            case "AVAudioInteger":
                native_size = 8;                 // in managed code it's always the largest size
                native      = true;
                break;

            case "unsigned long":
            case "unsigned int":
            case "int32_t":
            case "uint32_t":
            case "int":
            case "GLint":
            case "GLuint":
            case "GLenum":
            case "SInt32":
            case "UInt32":
            case "OptionBits":             // UInt32
            case "long":
            case "FourCharCode":
            case "OSStatus":
                break;

            case "int64_t":
            case "uint64_t":
            case "unsigned long long":
            case "CVOptionFlags":             // uint64_t
                native_size = 8;
                break;

            case "UInt16":
            case "int16_t":
            case "uint16_t":
            case "short":
                native_size = 2;
                break;

            case "UInt8":
            case "int8_t":
            case "uint8_t":
                native_size = 1;
                break;

            default:
                throw new NotImplementedException(decl.IntegerQualType.ToString());
            }

            // check correct [Native] decoration
            if (native)
            {
                if (!IsNative(type))
                {
                    Log.On(framework).Add($"!missing-enum-native! {name}");
                }
            }
            else
            {
                if (IsNative(type))
                {
                    Log.On(framework).Add($"!extra-enum-native! {name}");
                }
            }

            int  managed_size = 4;
            bool signed       = true;

            switch (GetEnumUnderlyingType(type).Name)
            {
            case "Byte":
                signed       = false;
                managed_size = 1;
                break;

            case "SByte":
                managed_size = 1;
                break;

            case "Int16":
                managed_size = 2;
                break;

            case "UInt16":
                signed       = false;
                managed_size = 2;
                break;

            case "Int32":
                break;

            case "UInt32":
                signed = false;
                break;

            case "Int64":
                managed_size = 8;
                break;

            case "UInt64":
                signed       = false;
                managed_size = 8;
                break;

            default:
                throw new NotImplementedException();
            }

            var fields = type.Fields;

            if (signed)
            {
                managed_signed_values.Clear();
                native_signed_values.Clear();
                foreach (var f in fields)
                {
                    // skip special `value__`
                    if (f.IsRuntimeSpecialName && !f.IsStatic)
                    {
                        continue;
                    }
                    if (!f.IsObsolete())
                    {
                        managed_signed_values [Convert.ToInt64(f.Constant)] = f;
                    }
                }

                long n = 0;
                foreach (var value in decl.Values)
                {
                    if ((value.InitExpr != null) && value.InitExpr.EvaluateAsInt(decl.AstContext, out var integer))
                    {
                        n = integer.SExtValue;
                    }

                    native_signed_values [n] = value.ToString();
                    // assume, sequentially assigned (in case next `value.InitExpr` is null)
                    n++;
                }

                foreach (var value in native_signed_values.Keys)
                {
                    if (!managed_signed_values.ContainsKey(value))
                    {
                        Log.On(framework).Add($"!missing-enum-value! {type.Name} native value {native_signed_values [value]} = {value} not bound");
                    }
                    else
                    {
                        managed_signed_values.Remove(value);
                    }
                }

                foreach (var value in managed_signed_values.Keys)
                {
                    if ((value == 0) && IsExtraZeroValid(type.Name, managed_signed_values [0].Name))
                    {
                        continue;
                    }
                    // value could be decorated with `[No*]` and those should not be reported
                    if (managed_signed_values [value].IsAvailable())
                    {
                        Log.On(framework).Add($"!extra-enum-value! Managed value {value} for {type.Name}.{managed_signed_values [value].Name} not found in native headers");
                    }
                }
            }
            else
            {
                managed_unsigned_values.Clear();
                native_unsigned_values.Clear();
                foreach (var f in fields)
                {
                    // skip special `value__`
                    if (f.IsRuntimeSpecialName && !f.IsStatic)
                    {
                        continue;
                    }
                    if (!f.IsObsolete())
                    {
                        managed_unsigned_values [Convert.ToUInt64(f.Constant)] = f;
                    }
                }

                ulong n = 0;
                foreach (var value in decl.Values)
                {
                    if ((value.InitExpr != null) && value.InitExpr.EvaluateAsInt(decl.AstContext, out var integer))
                    {
                        n = integer.ZExtValue;
                    }

                    native_unsigned_values [n] = value.ToString();
                    // assume, sequentially assigned (in case next `value.InitExpr` is null)
                    n++;
                }

                foreach (var value in native_unsigned_values.Keys)
                {
                    if (!managed_unsigned_values.ContainsKey(value))
                    {
                        // only for unsigned (flags) native enums we allow all bits set on 32 bits (UInt32.MaxValue)
                        // to be equal to all bit set on 64 bits (UInt64.MaxValue) since the MaxValue differs between
                        // 32bits (e.g. watchOS) and 64bits (all others) platforms
                        var log = true;
                        if (native && (value == UInt32.MaxValue))
                        {
                            log = !managed_unsigned_values.ContainsKey(UInt64.MaxValue);
                            managed_unsigned_values.Remove(UInt64.MaxValue);
                        }
                        if (log)
                        {
                            Log.On(framework).Add($"!missing-enum-value! {type.Name} native value {native_unsigned_values [value]} = {value} not bound");
                        }
                    }
                    else
                    {
                        managed_unsigned_values.Remove(value);
                    }
                }

                foreach (var value in managed_unsigned_values.Keys)
                {
                    if ((value == 0) && IsExtraZeroValid(type.Name, managed_unsigned_values [0].Name))
                    {
                        continue;
                    }
                    // value could be decorated with `[No*]` and those should not be reported
                    if (managed_unsigned_values [value].IsAvailable())
                    {
                        Log.On(framework).Add($"!extra-enum-value! Managed value {value} for {type.Name}.{managed_unsigned_values [value].Name} not found in native headers");
                    }
                }
            }

            if (native_size != managed_size)
            {
                Log.On(framework).Add($"!wrong-enum-size! {name} managed {managed_size} vs native {native_size}");
            }
        }