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); }