using System.Linq; using System.Collections.Generic; using CppSharp.AST; using CppSharp.AST.Extensions; using CppSharp.Types; namespace CppSharp.Passes { public class CheckIgnoredDeclsPass : TranslationUnitPass { public bool CheckDecayedTypes { get; set; } = true; public bool CheckDeclarationAccess(Declaration decl) { switch (decl.Access) { case AccessSpecifier.Public: return true; case AccessSpecifier.Protected: var @class = decl.Namespace as Class; if (@class != null && @class.IsValueType) return false; return Options.IsCSharpGenerator; case AccessSpecifier.Private: return false; } return true; } public override bool VisitClassDecl(Class @class) { if (!base.VisitClassDecl(@class)) return false; if (@class.IsInjected) injectedClasses.Add(@class); if (!@class.IsTemplate) return true; if (Options.GenerateClassTemplates) IgnoreUnsupportedTemplates(@class); return true; } public override bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization) { if (!base.VisitClassTemplateSpecializationDecl(specialization)) return false; TypeMap typeMap; if (!Options.GenerateClassTemplates && !specialization.IsExplicitlyGenerated && !Context.TypeMaps.FindTypeMap(specialization, out typeMap)) { specialization.ExplicitlyIgnore(); return false; } Declaration decl = null; if (specialization.Arguments.Any(a => a.Type.Type?.TryGetDeclaration(out decl) == true)) { decl.Visit(this); if (decl.Ignore) { specialization.ExplicitlyIgnore(); return false; } } return true; } public override bool VisitDeclaration(Declaration decl) { if (AlreadyVisited(decl)) return false; if (decl.GenerationKind == GenerationKind.None) return true; if (!CheckDeclarationAccess(decl)) { Diagnostics.Debug("Decl '{0}' was ignored due to invalid access", decl.Name); decl.GenerationKind = decl is Field ? GenerationKind.Internal : GenerationKind.None; return true; } return true; } public override bool VisitFieldDecl(Field field) { if (!VisitDeclaration(field)) return false; var type = (field.Type.GetFinalPointee() ?? field.Type).Desugar(); Declaration decl; type.TryGetDeclaration(out decl); string msg = "internal"; if (!(type is FunctionType) && (decl == null || (decl.GenerationKind != GenerationKind.Internal && !HasInvalidType(field, out msg)))) return false; field.GenerationKind = GenerationKind.Internal; var @class = (Class)field.Namespace; var cppTypePrinter = new CppTypePrinter(); var typeName = field.Type.Visit(cppTypePrinter); Diagnostics.Debug("Field '{0}::{1}' was ignored due to {2} type '{3}'", @class.Name, field.Name, msg, typeName); return true; } public override bool VisitFunctionTemplateDecl(FunctionTemplate decl) { if (!base.VisitFunctionTemplateDecl(decl)) return false; if (decl.TemplatedFunction.IsDependent && !decl.IsExplicitlyGenerated) { decl.TemplatedFunction.GenerationKind = GenerationKind.None; Diagnostics.Debug("Decl '{0}' was ignored due to dependent context", decl.Name); return true; } return true; } public override bool VisitFunctionDecl(Function function) { if (!VisitDeclaration(function) || function.IsSynthetized || function.IsExplicitlyGenerated) return false; if (function.IsDependent && !(function.Namespace is Class)) { function.GenerationKind = GenerationKind.None; Diagnostics.Debug("Function '{0}' was ignored due to dependent context", function.Name); return false; } var ret = function.OriginalReturnType; string msg; if (HasInvalidType(ret.Type, function, out msg)) { function.ExplicitlyIgnore(); Diagnostics.Debug("Function '{0}' was ignored due to {1} return decl", function.Name, msg); return false; } foreach (var param in function.Parameters) { if (HasInvalidDecl(param, out msg)) { function.ExplicitlyIgnore(); Diagnostics.Debug("Function '{0}' was ignored due to {1} param", function.Name, msg); return false; } if (HasInvalidType(param, out msg)) { function.ExplicitlyIgnore(); Diagnostics.Debug("Function '{0}' was ignored due to {1} param", function.Name, msg); return false; } if (CheckDecayedTypes) { var decayedType = param.Type.Desugar() as DecayedType; if (decayedType != null) { function.ExplicitlyIgnore(); Diagnostics.Debug("Function '{0}' was ignored due to unsupported decayed type param", function.Name); return false; } } if (param.Kind == ParameterKind.IndirectReturnType) { Class retClass; param.Type.Desugar().TryGetClass(out retClass); if (retClass == null) { function.ExplicitlyIgnore(); Diagnostics.Debug( "Function '{0}' was ignored due to an indirect return param not of a tag type", function.Name); return false; } } } return true; } public override bool VisitMethodDecl(Method method) { if (!CheckIgnoredBaseOverridenMethod(method)) return false; if (method.IsMoveConstructor) { method.ExplicitlyIgnore(); return true; } return base.VisitMethodDecl(method); } bool CheckIgnoredBaseOverridenMethod(Method method) { var @class = method.Namespace as Class; if (!method.IsVirtual) return true; Class ignoredBase; if (!HasIgnoredBaseClass(method, @class, out ignoredBase)) return true; Diagnostics.Debug( "Virtual method '{0}' was ignored due to ignored base '{1}'", method.QualifiedOriginalName, ignoredBase.Name); method.ExplicitlyIgnore(); return false; } static bool HasIgnoredBaseClass(INamedDecl @override, Class @class, out Class ignoredBase) { var isIgnored = false; ignoredBase = null; foreach (var baseClassSpec in @class.Bases) { if (!baseClassSpec.IsClass) continue; var @base = baseClassSpec.Class; if (!@base.Methods.Exists(m => m.Name == @override.Name)) continue; ignoredBase = @base; isIgnored |= !@base.IsDeclared || HasIgnoredBaseClass(@override, @base, out ignoredBase); if (isIgnored) break; } return isIgnored; } public override bool VisitTypedefDecl(TypedefDecl typedef) { if (!VisitDeclaration(typedef)) return false; string msg; if (HasInvalidType(typedef, out msg) && !(typedef.Type.Desugar() is MemberPointerType)) { typedef.ExplicitlyIgnore(); Diagnostics.Debug("Typedef '{0}' was ignored due to {1} type", typedef.Name, msg); return false; } return true; } public override bool VisitProperty(Property property) { if (!VisitDeclaration(property)) return false; string msg; if (HasInvalidDecl(property, out msg)) { property.ExplicitlyIgnore(); Diagnostics.Debug("Property '{0}' was ignored due to {1} decl", property.Name, msg); return false; } if (HasInvalidType(property, out msg)) { property.ExplicitlyIgnore(); Diagnostics.Debug("Property '{0}' was ignored due to {1} type", property.Name, msg); return false; } return true; } public override bool VisitVariableDecl(Variable variable) { if (!VisitDeclaration(variable)) return false; string msg; if (HasInvalidDecl(variable, out msg)) { variable.ExplicitlyIgnore(); Diagnostics.Debug("Variable '{0}' was ignored due to {1} decl", variable.Name, msg); return false; } if (HasInvalidType(variable.Type, variable, out msg)) if (HasInvalidType(variable, out msg)) { variable.ExplicitlyIgnore(); Diagnostics.Debug("Variable '{0}' was ignored due to {1} type", variable.Name, msg); return false; } return true; } public override bool VisitEvent(Event @event) { if (!VisitDeclaration(@event)) return false; string msg; if (HasInvalidDecl(@event, out msg)) { @event.ExplicitlyIgnore(); Diagnostics.Debug("Event '{0}' was ignored due to {1} decl", @event.Name, msg); return false; } foreach (var param in @event.Parameters) { if (HasInvalidDecl(param, out msg)) { @event.ExplicitlyIgnore(); Diagnostics.Debug("Event '{0}' was ignored due to {1} param", @event.Name, msg); return false; } if (HasInvalidType(param.Type, param, out msg)) if (HasInvalidType(param, out msg)) { @event.ExplicitlyIgnore(); Diagnostics.Debug("Event '{0}' was ignored due to {1} param", @event.Name, msg); return false; } } return true; } public override bool VisitASTContext(ASTContext c) { base.VisitASTContext(c); foreach (var injectedClass in injectedClasses) injectedClass.Namespace.Declarations.Remove(injectedClass); return true; } #region Helpers /// <summary> /// Checks if a given type is invalid, which can happen for a number of /// reasons: incomplete definitions, being explicitly ignored, or also /// by being a type we do not know how to handle. /// </summary> private bool HasInvalidType(ITypedDecl decl, out string msg) { return HasInvalidType(decl.Type, (Declaration) decl, out msg); } private bool HasInvalidType(Type type, Declaration decl, out string msg) { if (type == null) { msg = "null"; return true; } if (!IsTypeComplete(type)) { msg = "incomplete"; return true; } if (IsTypeIgnored(type)) { msg = "ignored"; return true; } var module = decl.TranslationUnit.Module; if (Options.DoAllModulesHaveLibraries() && module != Options.SystemModule && ASTUtils.IsTypeExternal(module, type)) { msg = "external"; return true; } var arrayType = type as ArrayType; if (arrayType != null && arrayType.SizeType == ArrayType.ArraySize.Constant && arrayType.Size == 0) { msg = "zero-sized array"; return true; } msg = null; return false; } private bool HasInvalidDecl(Declaration decl, out string msg) { if (decl == null) { msg = "null"; return true; } var @class = decl as Class; if (@class != null && @class.IsOpaque && !@class.IsDependent && !(@class is ClassTemplateSpecialization)) { msg = null; return false; } if (decl.IsIncomplete) { msg = "incomplete"; return true; } if (IsDeclIgnored(decl)) { msg = "ignored"; return true; } msg = null; return false; } private bool IsTypeComplete(Type type) { TypeMap typeMap; if (TypeMaps.FindTypeMap(type, out typeMap) && !typeMap.IsIgnored) return true; var desugared = type.Desugar(); var finalType = (desugared.GetFinalPointee() ?? desugared).Desugar(); var templateSpecializationType = finalType as TemplateSpecializationType; if (templateSpecializationType != null) finalType = templateSpecializationType.Desugared.Type; Declaration decl; if (!finalType.TryGetDeclaration(out decl)) return true; var @class = (decl as Class); if (@class != null && @class.IsOpaque && !@class.IsDependent && !(@class is ClassTemplateSpecialization)) return true; return !decl.IsIncomplete || decl.CompleteDeclaration != null; } private bool IsTypeIgnored(Type type) { var checker = new TypeIgnoreChecker(TypeMaps, Options.GeneratorKind); type.Visit(checker); return checker.IsIgnored; } private bool IsDeclIgnored(Declaration decl) { var parameter = decl as Parameter; if (parameter != null && parameter.Type.Desugar().IsPrimitiveType(PrimitiveType.Null)) return true; TypeMap typeMap; return TypeMaps.FindTypeMap(decl, out typeMap) ? typeMap.IsIgnored : decl.Ignore; } private void IgnoreUnsupportedTemplates(Class @class) { if (@class.TemplateParameters.Any(param => param is NonTypeTemplateParameter)) foreach (var specialization in @class.Specializations) specialization.ExplicitlyIgnore(); if (!Options.IsCLIGenerator && !@class.TranslationUnit.IsSystemHeader && @class.Specializations.Count > 0) return; bool hasExplicitlyGeneratedSpecializations = false; foreach (var specialization in @class.Specializations) if (specialization.IsExplicitlyGenerated) hasExplicitlyGeneratedSpecializations = true; else specialization.ExplicitlyIgnore(); if (!hasExplicitlyGeneratedSpecializations) @class.ExplicitlyIgnore(); } #endregion private HashSet<Declaration> injectedClasses = new HashSet<Declaration>(); } }