[xtro] Report incorrect 'ArgumentSemantic' enum value usage (#5114)

- ⚠️ Rule deactivated until we have an `xcode10.2` branch where we'll fix the issues.
- We only report `copy` mistakes since they're the ones we really care about fixing (we can end up with invalid pointers).
- Fixes #4018: [xtro] Report incorrect 'ArgumentSemantic' enum value usage
  (https://github.com/xamarin/xamarin-macios/issues/4018).
- Performance with the added test:
  - Extrospection.SelectorCheck: Elapsed=00:00:00.7263742
  - Extrospection.SelectorCheck: Elapsed=00:00:01.2136911
  - Extrospection.SelectorCheck: Elapsed=00:00:01.2747602
  - Extrospection.SelectorCheck: Elapsed=00:00:01.7494063
This commit is contained in:
Vincent Dondain 2018-11-29 11:33:37 -05:00 коммит произвёл GitHub
Родитель ae13240b20
Коммит 9be55e2bf4
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
2 изменённых файлов: 76 добавлений и 31 удалений

Просмотреть файл

@ -397,5 +397,43 @@ namespace Extrospection {
else
return (o1, o2);
}
public enum ArgumentSemantic {
None = -1,
Assign = 0,
Copy = 1,
Retain = 2,
Weak = 3,
Strong = Retain,
UnsafeUnretained = Assign,
}
public static ArgumentSemantic ToArgumentSemantic (this ObjCPropertyAttributeKind attr)
{
if ((attr & ObjCPropertyAttributeKind.Retain) != 0)
return ArgumentSemantic.Retain;
else if ((attr & ObjCPropertyAttributeKind.Copy) != 0)
return ArgumentSemantic.Copy;
else if ((attr & ObjCPropertyAttributeKind.Assign) != 0)
return ArgumentSemantic.Assign;
else if ((attr & ObjCPropertyAttributeKind.Weak) != 0)
return ArgumentSemantic.Weak;
else if ((attr & ObjCPropertyAttributeKind.Strong) != 0)
return ArgumentSemantic.Strong;
else if ((attr & ObjCPropertyAttributeKind.UnsafeUnretained) != 0)
return ArgumentSemantic.UnsafeUnretained;
else
return ArgumentSemantic.Assign; // Default
}
public static string ToUsableString (this ArgumentSemantic argSem)
{
if (argSem == ArgumentSemantic.Retain)
return "Strong|Retain";
if (argSem == ArgumentSemantic.Assign)
return "UnsafeUnretained|Assign";
return argSem.ToString ();
}
}
}

Просмотреть файл

@ -4,9 +4,6 @@
// !missing-selector!
// if headers defines a selector for which we have no bindings
//
// !unknown-selector!
// if we have a selector that is not part of the header files
//
using System;
using System.Collections.Generic;
@ -18,24 +15,9 @@ using Clang.Ast;
namespace Extrospection {
public class SelectorCheck : BaseVisitor {
// Dictionary<string,List<MethodDefinition>> exports = new Dictionary<string, List<MethodDefinition>> ();
// missing
// -> it's not in the type or ancestor or their interface (protocols)
// -> it's not in a category
// unknown
// -> quick check (HashSet) to see if it's used anywhere
// ->
// duplicate
// -> the selector is defined more than once for the same type
HashSet<string> known_selectors = new HashSet<string> ();
HashSet<string> qualified_selectors = new HashSet<string> ();
//Dictionary<TypeDefinition,HashSet<string>> type_exports = new Dictionary<TypeDefinition,HashSet<string>> ();
Dictionary<string, Helpers.ArgumentSemantic> qualified_properties = new Dictionary<string, Helpers.ArgumentSemantic> ();
// most selectors will be found in [Export] attribtues
public override void VisitManagedMethod (MethodDefinition method)
@ -51,24 +33,49 @@ namespace Extrospection {
foreach (var ca in method.CustomAttributes) {
switch (ca.Constructor.DeclaringType.Name) {
case "ExportAttribute":
string selector = ca.ConstructorArguments [0].Value as string;
if (!known_selectors.Contains (selector))
known_selectors.Add (selector);
var methodDefinition = method.GetName ();
if (!string.IsNullOrEmpty (methodDefinition)) {
var argumentSemantic = Helpers.ArgumentSemantic.Assign; // Default
if (ca.ConstructorArguments.Count > 1) {
argumentSemantic = (Helpers.ArgumentSemantic)ca.ConstructorArguments [1].Value;
qualified_properties.Add (methodDefinition, argumentSemantic);
}
qualified_selectors.Add (methodDefinition);
}
qualified_selectors.Add (method.GetName ());
//
// TypeDefinition type = method.DeclaringType;
// HashSet<string> list;
// if (!type_exports.TryGetValue (type, out list)) {
// list = new HashSet<string> ();
// type_exports.Add (type, list);
// }
// list.Add (selector);
break;
}
}
}
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;
bool found = qualified_properties.TryGetValue (nativeMethodDefinition, out var managedArgumentSemantic);
if (found && 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 ()}'");
}
}
}
public override void VisitObjCMethodDecl (ObjCMethodDecl decl, VisitKind visitKind)
{
if (visitKind != VisitKind.Enter)