diff --git a/src/Generator/Passes/CheckMacrosPass.cs b/src/Generator/Passes/CheckMacrosPass.cs new file mode 100644 index 00000000..c894681a --- /dev/null +++ b/src/Generator/Passes/CheckMacrosPass.cs @@ -0,0 +1,145 @@ +using System.Linq; +using CppSharp.AST; + +namespace CppSharp.Passes +{ + /// + /// This pass implements checks for helper macros in declarations. + /// The supported macros are listed below with a "CS" prefix which + /// stands for CppSharp. Using custom prefixes is also supported by + /// passing the value to the constructor of the pass. + /// + /// CS_IGNORE (declarations) + /// Used to ignore declarations from being processed. + /// + /// CS_VALUE_TYPE (classes and structs) + /// Used to flag that a class or struct is a value type. + /// + /// CS_IN_OUT / CS_OUT (parameters) + /// Used in function parameters to specify their usage kind. + /// + /// CS_FLAGS (enums) + /// Used to specify that enumerations represent bitwise flags. + /// + /// CS_READONLY (fields and properties) + /// Used in fields and properties to specify read-only semantics. + /// + /// CS_EQUALS / CS_HASHCODE (methods) + /// Used to flag method as representing the .NET Equals or + /// Hashcode methods. + /// + /// There isn't a standardized header provided by CppSharp so you will + /// have to define these on your own. + /// + public class CheckMacroPass : TranslationUnitPass + { + private readonly string Prefix; + + public CheckMacroPass(string prefix = "CS") + { + Prefix = prefix; + } + + public override bool VisitDeclaration(Declaration decl) + { + if (AlreadyVisited(decl)) + return false; + + var expansions = decl.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_IGNORE" && + e.Location != MacroLocation.ClassBody && + e.Location != MacroLocation.FunctionBody && + e.Location != MacroLocation.FunctionParameters)) + decl.ExplicityIgnored = true; + + return true; + } + + public override bool VisitClassDecl(Class @class) + { + var expansions = @class.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_VALUE_TYPE")) + @class.Type = ClassType.ValueType; + + return base.VisitClassDecl(@class); + } + + public override bool VisitParameterDecl(Parameter parameter) + { + if (!VisitDeclaration(parameter)) + return false; + + var expansions = parameter.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_OUT")) + parameter.Usage = ParameterUsage.Out; + + if (expansions.Any(e => e.Text == Prefix + "_IN_OUT")) + parameter.Usage = ParameterUsage.InOut; + + return true; + } + + public override bool VisitEnumDecl(Enumeration @enum) + { + if (!VisitDeclaration(@enum)) + return false; + + var expansions = @enum.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_FLAGS")) + @enum.SetFlags(); + + return true; + } + + public override bool VisitMethodDecl(Method method) + { + var expansions = method.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_HASHCODE" + || e.Text == Prefix + "_EQUALS")) + method.ExplicityIgnored = true; + + return base.VisitMethodDecl(method); + } + + public override bool VisitFieldDecl(Field field) + { + if (!VisitDeclaration(field)) + return false; + + var expansions = field.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_READONLY")) + { + var quals = field.QualifiedType.Qualifiers; + quals.IsConst = true; + + var qualType = field.QualifiedType; + qualType.Qualifiers = quals; + + field.QualifiedType = qualType; + } + + return true; + } + + public override bool VisitProperty(Property property) + { + var expansions = property.PreprocessedEntities.OfType(); + + if (expansions.Any(e => e.Text == Prefix + "_READONLY")) + { + var setMethod = property.SetMethod; + + if (setMethod != null) + property.SetMethod.ExplicityIgnored = true; + } + + return base.VisitProperty(property); + } + } +}