public void VisitMethodCall(MethodBase originatingMethod,
                                    int ilOffsetOfCall,
                                    MethodBase calledMethod,
                                    int referencingMethodId,
                                    int referencingClassId)
        {
            var targetClassId    = 0;
            var limitedReference = false;
            // do not collect members of foreign assemblies
            var topReferencedType = calledMethod.GetTopmostNonGeneratedType();

            if (typeHandler.ShouldCollectType(topReferencedType))
            {
                targetClassId = typeHandler.AddToDbIfValid(topReferencedType);
            }
            if (targetClassId == 0 && GlobalOptions.CollectAllInvocations)
            {
                targetClassId    = typeHandler.AddToDb(topReferencedType, true);
                limitedReference = true;
            }
            // check, if this is an async method
            if (calledMethod.DeclaringType.IsAsyncStateMachineOf(originatingMethod, out var asyncWorker))
            {
                // dive into the async worker, as that is where the "meat" of the method is
                ParseMethod?.Invoke(this, new CollectedMethodEventArgs(new CollectedMethod(asyncWorker, referencingMethodId, referencingClassId)));
            }
            // collect method parameters
            // note that we just assume the parameters are used, e.g. the code may actually pass null as an argument
            // to get this right, we would have to look at the actual arguments, that get passed to the method
            // this is a bit more involved, maybe something for a future version...
            // (i think, i'll leave it this way, the value passed may be a parameter itself, so we can never be sure in these cases)
            foreach (var mParam in calledMethod.GetParameters())
            {
                var paramTypeId = typeHandler.AddToDbIfValid(mParam.ParameterType);
                if (paramTypeId > 0 && paramTypeId != referencingClassId)  // ignore self-references (e.g. when passing "this" as a parameter)
                {
                    dataCollector.CollectReference(referencingClassId, paramTypeId, ReferenceKind.REFERENCE_TYPE_USAGE);
                    var refId = dataCollector.CollectReference(referencingMethodId, paramTypeId, ReferenceKind.REFERENCE_TYPE_USAGE);
                    CollectReferenceLocation(originatingMethod, refId, ilOffsetOfCall);
                }
            }
            if (targetClassId > 0)
            {
                if (referencingClassId != targetClassId)   // ignore self-references
                {
                    dataCollector.CollectReference(referencingClassId, targetClassId, ReferenceKind.REFERENCE_TYPE_USAGE);
                    var refId = dataCollector.CollectReference(referencingMethodId, targetClassId, ReferenceKind.REFERENCE_TYPE_USAGE);
                    CollectReferenceLocation(originatingMethod, refId, ilOffsetOfCall);
                }
                var targetMethodId = typeHandler.CollectMember(calledMethod, limitedReference, out var targetKind);
                if (targetMethodId > 0)
                {
                    var refId = dataCollector.CollectReference(referencingMethodId, targetMethodId,
                                                               targetKind == SymbolKind.SYMBOL_METHOD ? ReferenceKind.REFERENCE_CALL : ReferenceKind.REFERENCE_USAGE);
                    CollectReferenceLocation(originatingMethod, refId, ilOffsetOfCall);
                }
                // if target is an interface, link to implementors as well
                // (seems to be consistent with behavior of VS 2019 when looking at references)
                List <Type> implementors = new List <Type>();
                if (calledMethod.DeclaringType.IsInterface)
                {
                    implementors.AddRange(typeHandler.GetInterfaceImplementors(calledMethod.DeclaringType));
                }
                foreach (var implementor in implementors)
                {
                    var implTypeId = typeHandler.AddToDbIfValid(implementor);
                    if (implTypeId > 0)
                    {
                        dataCollector.CollectReference(referencingClassId, implTypeId, ReferenceKind.REFERENCE_TYPE_USAGE);
                        var refId = dataCollector.CollectReference(referencingMethodId, implTypeId, ReferenceKind.REFERENCE_TYPE_USAGE);
                        CollectReferenceLocation(originatingMethod, refId, ilOffsetOfCall);
                    }
                    foreach (var implMethod in implementor.GetMethods(flags))
                    {
                        // use correct overload
                        if (implMethod.Name == calledMethod.Name && implMethod.HasSameParameters(calledMethod))
                        {
                            var implMethodId = typeHandler.CollectMember(implMethod, limitedReference, out var targetImplKind);
                            var refId        = dataCollector.CollectReference(referencingMethodId, implMethodId,
                                                                              targetImplKind == SymbolKind.SYMBOL_METHOD ? ReferenceKind.REFERENCE_CALL : ReferenceKind.REFERENCE_USAGE);
                            CollectReferenceLocation(originatingMethod, refId, ilOffsetOfCall);
                            break;
                        }
                    }
                }
            }
        }
        public int AddToDb(Type type, bool skipMembers = false)
        {
            if (!Cache.Namespaces.TryGetValue(type.Namespace, out _))
            {
                var nsId = dataCollector.CollectSymbol(type.Namespace, SymbolKind.SYMBOL_NAMESPACE);
                Cache.Namespaces.Add(type.Namespace, nsId);
            }

            var name = type.GetPrettyName();

            if (name == null)
            {
                return(0);
            }

            var kind = SymbolKind.SYMBOL_CLASS;

            if (type.IsEnum)
            {
                kind = SymbolKind.SYMBOL_ENUM;
            }
            else if (type.IsInterface)
            {
                kind = SymbolKind.SYMBOL_INTERFACE;
            }
            else if (type.IsGenericTypeDefinition)
            {
                kind = SymbolKind.SYMBOL_TYPEDEF;
            }
            // if class inherits from Attribute, treat as annotation
            if (typeof(Attribute).IsAssignableFrom(type))
            {
                kind = SymbolKind.SYMBOL_ANNOTATION;
            }

            var classId = dataCollector.CollectSymbol(name, kind);

            if (classId > 0 && !skipMembers)
            {
                foreach (var genType in type.GetGenericArguments())
                {
                    var genTypeId = AddToDbIfValid(genType);
                    if (genTypeId > 0)
                    {
                        dataCollector.CollectReference(classId, genTypeId, ReferenceKind.REFERENCE_TYPE_ARGUMENT);
                    }
                }
                if (type.IsGenericType && !type.IsGenericTypeDefinition)
                {
                    var genBaseType = type.GetGenericTypeDefinition();
                    var baseTypeId  = AddToDbIfValid(genBaseType);
                    if (baseTypeId > 0)
                    {
                        dataCollector.CollectReference(classId, baseTypeId, ReferenceKind.REFERENCE_TEMPLATE_SPECIALIZATION);
                    }
                }

                // do not collect members for types from foreign assemblies unless they match the specified filter
                if (ShouldCollectType(type))
                {
                    CollectTypeMembers(type, classId);
                }
            }
            return(classId);
        }