127 строки
4.1 KiB
C#
127 строки
4.1 KiB
C#
//
|
|
// The rule reports
|
|
//
|
|
// !missing-release-attribute-on-return-value!
|
|
// for methods whose objc family indicates the returned value is retained, and the method doesn't have a [return: Release] attribute.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
using Mono.Cecil;
|
|
|
|
using Clang.Ast;
|
|
|
|
namespace Extrospection {
|
|
|
|
public class ReleaseAttributeCheck : BaseVisitor {
|
|
|
|
// 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;
|
|
}
|
|
|
|
}
|
|
|
|
// We should also look at the native definition for family attributes: __attribute__((objc_method_family(...))
|
|
// but unfortunately ObjectiveSharpie doesn't support getting the actual family from the attribute yet,
|
|
// so this will have to wait. In any case there only seems to be a single method with the family attribute in the SDKs,
|
|
// so we can live with a manual exception.
|
|
//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;
|
|
|
|
// string selector = decl.GetSelector ();
|
|
// if (string.IsNullOrEmpty (selector))
|
|
// return;
|
|
|
|
// foreach (var attr in decl.Attrs) {
|
|
// switch (attr.Kind) {
|
|
// case AttrKind.ObjCMethodFamily:
|
|
// ObjCMethodFamilyAttr familyAttr = (ObjCMethodFamilyAttr)attr;
|
|
// Console.WriteLine ("Family attribute {0} for {1}", familyAttr, selector);
|
|
// break;
|
|
// default:
|
|
// break;
|
|
// }
|
|
// }
|
|
//}
|
|
}
|
|
} |