From 4263b3ae4a4a885ce574d806c89342dc1fd255a6 Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Fri, 19 Jul 2024 15:55:42 -0400 Subject: [PATCH 1/8] VS-153: Handle Custom Collections --- .../Builders/BuilderExpressionProcessor.cs | 18 +- .../Core/Linq/LinqExpressionProcessor.cs | 4 + .../Core/Poco/PocoExpressionProcessor.cs | 5 + .../Core/ReferencesProvider.cs | 1 + src/MongoDB.Analyzer/Core/TypesProcessor.cs | 77 ++++- .../Core/Utilities/SymbolExtensions.cs | 75 +++-- .../Builders/BuildersCollections.cs | 125 +++++++- .../Linq/LinqCollections.cs | 268 +++++++++++++++-- .../Poco/PocoCollections.cs | 272 ++++++++++++++++++ .../DataModel/Collections.cs | 148 ++++++++++ 10 files changed, 934 insertions(+), 59 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs b/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs index 12bd51c1..e8b97014 100644 --- a/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs +++ b/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs @@ -81,9 +81,9 @@ public static ExpressionsAnalysis ProcessSemanticModel(MongoAnalysisContext cont try { - foreach (var typeArgument in namedType.TypeArguments) + if (!ProcessTypeArguments(namedType.TypeArguments, typesProcessor)) { - typesProcessor.ProcessTypeSymbol(typeArgument); + continue; } var rewriteContext = RewriteContext.Builders(expressionNode, nodesToRewrite, semanticModel, typesProcessor); @@ -253,4 +253,18 @@ private static (NodeType NodeType, INamedTypeSymbol NamedSymbol, SyntaxNode Expr return (nodeType, namedType, expressionNode); } + + private static bool ProcessTypeArguments(IEnumerable typeArguments, TypesProcessor typesProcessor) + { + foreach (var typeArgument in typeArguments) + { + var remappedName = typesProcessor.ProcessTypeSymbol(typeArgument); + if (remappedName == null) + { + return false; + } + } + + return true; + } } diff --git a/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs b/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs index 97167464..b80815c0 100644 --- a/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs +++ b/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs @@ -98,6 +98,10 @@ mongoQueryableTypeInfo.Type is not INamedTypeSymbol mongoQueryableNamedType || if (PreanalyzeLinqExpression(node, semanticModel, invalidExpressionNodes)) { var generatedMongoQueryableTypeName = typesProcessor.ProcessTypeSymbol(mongoQueryableNamedType.TypeArguments[0]); + if (generatedMongoQueryableTypeName == null) + { + continue; + } var rewriteContext = RewriteContext.Linq(node, deepestMongoQueryableNode, semanticModel, typesProcessor); var (newLinqExpression, constantsMapper) = RewriteExpression(rewriteContext); diff --git a/src/MongoDB.Analyzer/Core/Poco/PocoExpressionProcessor.cs b/src/MongoDB.Analyzer/Core/Poco/PocoExpressionProcessor.cs index 18c7a037..842f3140 100644 --- a/src/MongoDB.Analyzer/Core/Poco/PocoExpressionProcessor.cs +++ b/src/MongoDB.Analyzer/Core/Poco/PocoExpressionProcessor.cs @@ -47,6 +47,11 @@ public static ExpressionsAnalysis ProcessSemanticModel(MongoAnalysisContext cont if (PreanalyzeClassDeclaration(context, classSymbol)) { var generatedClassName = typesProcessor.ProcessTypeSymbol(classSymbol); + if (generatedClassName == null) + { + continue; + } + var generatedClassNode = (ClassDeclarationSyntax)(typesProcessor.GetTypeSymbolToMemberDeclarationMapping(classSymbol)); var expressionContext = new ExpressionAnalysisContext(new ExpressionAnalysisNode(classNode, null, generatedClassNode, null, classNode.GetLocation())); analysisContexts.Add(expressionContext); diff --git a/src/MongoDB.Analyzer/Core/ReferencesProvider.cs b/src/MongoDB.Analyzer/Core/ReferencesProvider.cs index 547428a0..a1d6003f 100644 --- a/src/MongoDB.Analyzer/Core/ReferencesProvider.cs +++ b/src/MongoDB.Analyzer/Core/ReferencesProvider.cs @@ -88,6 +88,7 @@ public static ReferencesContainer GetReferences(IEnumerable m resultReferences.Add(MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)); resultReferences.Add(MetadataReference.CreateFromFile(typeof(IEnumerator).Assembly.Location)); resultReferences.Add(MetadataReference.CreateFromFile(typeof(Queryable).Assembly.Location)); + resultReferences.Add(MetadataReference.CreateFromFile(typeof(Stack).Assembly.Location)); resultReferences.Add(MetadataReference.CreateFromFile(typeof(System.Dynamic.DynamicObject).Assembly.Location)); resultReferences.Add(MetadataReference.CreateFromFile(typeof(System.Runtime.AssemblyTargetedPatchBandAttribute).Assembly.Location)); resultReferences.Add(MetadataReference.CreateFromFile(typeof(Task).Assembly.Location)); diff --git a/src/MongoDB.Analyzer/Core/TypesProcessor.cs b/src/MongoDB.Analyzer/Core/TypesProcessor.cs index 737bbd2e..12da2354 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -69,6 +69,12 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) var rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); + if (rewrittenDeclarationSyntax == null) + { + _processedTypes.Remove(fullTypeName); + return null; + } + var typeCode = rewrittenDeclarationSyntax.ToFullString(); var newTypeDeclaration = SyntaxFactory.ParseMemberDeclaration(typeCode); @@ -93,17 +99,29 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) arrayRankSpecifiers = SyntaxFactory.List(new[] { SyntaxFactory.ArrayRankSpecifier(ranksList) }); var nextTypeSyntax = CreateTypeSyntaxForSymbol(arrayTypeSymbol.ElementType); + if (nextTypeSyntax == null) + { + return null; + } + result = SyntaxFactory.ArrayType(nextTypeSyntax, arrayRankSpecifiers.Value); } // TODO optimize else if (typeSymbol is INamedTypeSymbol namedTypeSymbol && - namedTypeSymbol.TypeArguments.Length == 1 && + namedTypeSymbol.TypeArguments.Length >= 1 && namedTypeSymbol.IsSupportedCollection()) { - var underlyingTypeSyntax = CreateTypeSyntaxForSymbol(namedTypeSymbol.TypeArguments.Single()); - var listSyntax = SyntaxFactory.GenericName( - SyntaxFactory.Identifier("List"), - SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(new[] { underlyingTypeSyntax }))); + var underlyingTypeSyntaxes = namedTypeSymbol.TypeArguments.Select(typeArgument => CreateTypeSyntaxForSymbol(typeArgument)); + if (underlyingTypeSyntaxes.Any(underlyingTypeSyntax => underlyingTypeSyntax == null)) + { + return null; + } + + var collectionIdentifier = namedTypeSymbol.Name; + + var collectionSyntax = SyntaxFactory.GenericName( + SyntaxFactory.Identifier(collectionIdentifier), + SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(underlyingTypeSyntaxes))); result = SyntaxFactory.QualifiedName( SyntaxFactory.QualifiedName( @@ -111,13 +129,22 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) SyntaxFactory.IdentifierName("System"), SyntaxFactory.IdentifierName("Collections")), SyntaxFactory.IdentifierName("Generic")), - listSyntax); + collectionSyntax); + } + else if (typeSymbol.IsUserDefinedCollection()) + { + return null; } else { var (isNullable, underlingTypeSymbol) = typeSymbol.DiscardNullable(); - var newTypeName = ProcessTypeSymbol(underlingTypeSymbol); + + if (newTypeName == null) + { + return null; + } + result = isNullable ? SyntaxFactoryUtilities.GetNullableType(newTypeName) : SyntaxFactory.ParseTypeName(newTypeName); } @@ -171,7 +198,7 @@ private ExpressionSyntax GenerateExpressionFromBsonAttributeArgumentInfo(TypedCo _ => null }; - private void GenerateFields(ITypeSymbol typeSymbol, List members) + private bool GenerateFields(ITypeSymbol typeSymbol, List members) { var typeFields = typeSymbol .GetMembers() @@ -184,6 +211,10 @@ private void GenerateFields(ITypeSymbol typeSymbol, List members) + private bool GenerateProperties(ITypeSymbol typeSymbol, List members) { var typeProperties = typeSymbol .GetMembers() @@ -215,6 +248,10 @@ private void GenerateProperties(ITypeSymbol typeSymbol, List private (string RemappedName, string FullTypeName) GetGeneratedTypeMapping(ITypeSymbol typeSymbol, bool userOnlyTypes) { - if (typeSymbol == null) + if (typeSymbol == null || + typeSymbol.IsUserDefinedCollection()) { return default; } @@ -285,8 +325,11 @@ private TypeDeclarationSyntax GetSyntaxForClassOrStruct(ITypeSymbol typeSymbol, var members = new List(); - GenerateProperties(typeSymbol, members); - GenerateFields(typeSymbol, members); + if (!GenerateProperties(typeSymbol, members) || + !GenerateFields(typeSymbol, members)) + { + return null; + } typeDeclaration = typeDeclaration .AddMembers(members.ToArray()) @@ -298,6 +341,11 @@ private TypeDeclarationSyntax GetSyntaxForClassOrStruct(ITypeSymbol typeSymbol, { var baseTypeNameGenerated = ProcessTypeSymbol(typeSymbol.BaseType); + if (baseTypeNameGenerated == null) + { + return null; + } + typeDeclaration = typeDeclaration.WithBaseList(GetBaseListSyntax(baseTypeNameGenerated)); } @@ -351,6 +399,11 @@ private BaseTypeDeclarationSyntax GetSyntaxNodeFromSymbol(ITypeSymbol typeSymbol throw new NotSupportedException($"Symbol type {typeSymbol.TypeKind} is not supported."); } + if (typeDeclaration == null) + { + return null; + } + typeDeclaration = typeDeclaration.NormalizeWhitespace(); return typeDeclaration; } diff --git a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs index a7389514..b3cdd516 100644 --- a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs +++ b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs @@ -16,7 +16,9 @@ namespace MongoDB.Analyzer.Core; internal static class SymbolExtensions { + private const string AssemblyMongoDBBson = "MongoDB.Bson"; private const string AssemblyMongoDBDriver = "MongoDB.Driver"; + private const string NamespaceCollectionGeneric = "System.Collections.Generic"; private const string NamespaceEF = "Microsoft.EntityFrameworkCore"; private const string NamespaceMongoDBBson = "MongoDB.Bson"; private const string NamespaceMongoDBBsonAttributes = "MongoDB.Bson.Serialization.Attributes"; @@ -54,13 +56,6 @@ internal static class SymbolExtensions "MongoDB.Bson.Serialization.Options.TimeSpanUnits" }; - private static readonly HashSet s_supportedCollections = new() - { - "System.Collections.Generic.IEnumerable", - "System.Collections.Generic.IList", - "System.Collections.Generic.List" - }; - private static readonly HashSet s_supportedSystemTypes = new() { "System.DateTime", @@ -160,7 +155,11 @@ public static bool IsDBSet(this ITypeSymbol typeSymbol) => typeSymbol?.Name == "DbSet" && typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceEF; - public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; + public static bool IsDefinedInMongoBson(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBBson && + symbol?.ContainingAssembly.Name == AssemblyMongoDBBson; + + public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && + symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; public static bool IsDefinedInMongoLinqOrSystemLinq(this ISymbol symbol) { @@ -173,6 +172,22 @@ public static bool IsDefinedInMongoLinqOrSystemLinq(this ISymbol symbol) symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; } + public static bool IsDefinedInSystem(this ISymbol symbol) + { + var containingNamespace = symbol?.ContainingNamespace; + while (containingNamespace != null) + { + if (containingNamespace.Name == NamespaceSystem) + { + return true; + } + + containingNamespace = containingNamespace.ContainingNamespace; + } + + return false; + } + public static bool IsFindFluent(this ITypeSymbol typeSymbol) => typeSymbol?.Name switch { @@ -222,21 +237,43 @@ TypeKind.Enum or _ => false }; - public static bool IsSupportedCollection(this ITypeSymbol typeSymbol) + public static bool IsSupportedCollection(this ITypeSymbol typeSymbol) => + typeSymbol is INamedTypeSymbol namedTypeSymbol && + namedTypeSymbol.ContainingNamespace.ToDisplayString() == NamespaceCollectionGeneric; + + public static bool IsSupportedIMongoCollection(this ITypeSymbol typeSymbol) => + typeSymbol.IsIMongoCollection() && + typeSymbol is INamedTypeSymbol namedType && + namedType.TypeArguments.Length == 1 && + namedType.TypeArguments[0].IsSupportedMongoCollectionType(); + + public static bool IsSupportedMongoCollectionType(this ITypeSymbol typeSymbol) => + typeSymbol.TypeKind == TypeKind.Class && + !typeSymbol.IsAnonymousType; + + public static bool IsSupportedSystemType(this ITypeSymbol typeSymbol, string fullTypeName) => + (typeSymbol.SpecialType != SpecialType.None || s_supportedSystemTypes.Contains(fullTypeName)) && + typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceSystem; + + public static bool IsUserDefinedCollection(this ITypeSymbol typeSymbol) { - if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) + if (typeSymbol is not INamedTypeSymbol namedTypeSymbol || + namedTypeSymbol.IsDefinedInMongoBson() || + namedTypeSymbol.IsDefinedInMongoDriver() || + namedTypeSymbol.IsDefinedInSystem()) { return false; } while (namedTypeSymbol != null) { - if (s_supportedCollections.Contains(namedTypeSymbol.ConstructedFrom?.ToDisplayString())) + if (namedTypeSymbol.IsSupportedCollection()) { return true; } - if (namedTypeSymbol.Interfaces.Any(i => s_supportedCollections.Contains(i.ConstructedFrom?.ToDisplayString()))){ + if (namedTypeSymbol.Interfaces.Any(i => i.IsSupportedCollection())) + { return true; } @@ -246,20 +283,6 @@ public static bool IsSupportedCollection(this ITypeSymbol typeSymbol) return false; } - public static bool IsSupportedIMongoCollection(this ITypeSymbol typeSymbol) => - typeSymbol.IsIMongoCollection() && - typeSymbol is INamedTypeSymbol namedType && - namedType.TypeArguments.Length == 1 && - namedType.TypeArguments[0].IsSupportedMongoCollectionType(); - - public static bool IsSupportedMongoCollectionType(this ITypeSymbol typeSymbol) => - typeSymbol.TypeKind == TypeKind.Class && - !typeSymbol.IsAnonymousType; - - public static bool IsSupportedSystemType(this ITypeSymbol typeSymbol, string fullTypeName) => - (typeSymbol.SpecialType != SpecialType.None || s_supportedSystemTypes.Contains(fullTypeName)) && - typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceSystem; - private static SyntaxToken[] GetPublicFieldModifiers() => new[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword) }; diff --git a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Builders/BuildersCollections.cs b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Builders/BuildersCollections.cs index 47679a53..615fa8fc 100644 --- a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Builders/BuildersCollections.cs +++ b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Builders/BuildersCollections.cs @@ -20,14 +20,43 @@ namespace MongoDB.Analyzer.Tests.Common.TestCases.Builders { public sealed class BuildersCollections : TestCasesBase { - [BuildersMQL("{ \"$or\" : [{ \"Enumerable1.0\" : 2 }, { \"Enumerable2.1.Enumerable1.0\" : 2 }] }")] + [NoDiagnostics] + public void CustomDictionaries() + { + _ = Builders.Filter.Eq(t => t.IntDictionary["key"], 1) | + Builders.Filter.Eq(t => t.StringDictionary["key"], "Value") | + Builders.Filter.Eq(t => t.PesonsDictionary["string"].Name, "Bob") | + Builders.Filter.Eq(t => t.NestedDictionariesHolderDictionary["key"].IntDictionary["key"], 1) | + Builders.Filter.Eq(t => t.IntIDictionary["key"], 3) | + Builders.Filter.Eq(t => t.NestedDictionariesHolderIDictionary["key"].IntDictionary["key"], 3); + } + + [NoDiagnostics] public void CustomEnumerables() { _ = Builders.Filter.Eq(t => t.Enumerable1.ElementAt(0), 2) | Builders.Filter.Eq(t => t.Enumerable2.ElementAt(1).Enumerable1.ElementAt(0), 2); } - [BuildersMQL("{ \"$or\" : [{ \"IntList.2\" : 1 }, { \"StringList.3\" : \"Value\" }, { \"PesonsList.4.Name\" : \"Bob\" }, { \"NestedListsHolderList.5.IntList.1\" : 1 }, { \"IntIList.4\" : 3 }, { \"NestedListsHolderIList.15.IntList.3\" : 3 }] }")] + [NoDiagnostics] + public void CustomHashSets() + { + _ = Builders.Filter.Eq(t => t.IntHashSet.ElementAt(0), 2) | + Builders.Filter.Eq(t => t.PesonsHashSet.ElementAt(0).SiblingsCount, 2) | + Builders.Filter.Eq(t => t.StringHashSet.ElementAt(0), "Value") | + Builders.Filter.Eq(t => t.NestedHashSetsHolderHashSet.ElementAt(0).IntHashSet.ElementAt(0), 2); + } + + [NoDiagnostics] + public void CustomLinkedLists() + { + _ = Builders.Filter.Eq(t => t.IntLinkedList.ElementAt(0), 1) | + Builders.Filter.Eq(t => t.StringLinkedList.ElementAt(0), "Value") | + Builders.Filter.Eq(t => t.PesonsLinkedList.ElementAt(0).Name, "Bob") | + Builders.Filter.Eq(t => t.NestedLinkedListsHolderLinkedList.ElementAt(0).IntLinkedList.ElementAt(0), 1); + } + + [NoDiagnostics] public void CustomLists() { _ = Builders.Filter.Eq(t => t.IntList[2], 1) | @@ -38,6 +67,44 @@ public void CustomLists() Builders.Filter.Eq(t => t.NestedListsHolderIList[15].IntList[3], 3); } + [NoDiagnostics] + public void CustomQueues() + { + _ = Builders.Filter.Eq(t => t.IntQueue.Dequeue(), 1) | + Builders.Filter.Eq(t => t.PesonsQueue.Dequeue().Name, "Name") | + Builders.Filter.Eq(t => t.StringQueue.Dequeue(), "Value") | + Builders.Filter.Eq(t => t.NestedQueuesHolderQueue.Dequeue().IntQueue.Dequeue(), 1); + } + + [NoDiagnostics] + public void CustomSortedDictionaries() + { + _ = Builders.Filter.Eq(t => t.IntSortedDictionary["key"], 1) | + Builders.Filter.Eq(t => t.StringSortedDictionary["key"], "Value") | + Builders.Filter.Eq(t => t.PesonsSortedDictionary["string"].Name, "Bob") | + Builders.Filter.Eq(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"], 1); + } + + [NoDiagnostics] + public void CustomStacks() + { + _ = Builders.Filter.Eq(t => t.IntStack.Pop(), 1) | + Builders.Filter.Eq(t => t.PesonsStack.Pop().Name, "Name") | + Builders.Filter.Eq(t => t.StringStack.Pop(), "Value") | + Builders.Filter.Eq(t => t.NestedStacksHolderStack.Pop().IntStack.Pop(), 1); + } + + [BuildersMQL("{ \"$or\" : [{ \"IntDictionary.key\" : 1 }, { \"StringDictionary.key\" : \"Value\" }, { \"PesonsDictionary.string.Name\" : \"Bob\" }, { \"NestedDictionariesHolderDictionary.key.IntDictionary.key\" : 1 }, { \"IntIDictionary.key\" : 3 }, { \"NestedDictionariesHolderIDictionary.key.IntDictionary.key\" : 3 }] }")] + public void Dictionaries() + { + _ = Builders.Filter.Eq(t => t.IntDictionary["key"], 1) | + Builders.Filter.Eq(t => t.StringDictionary["key"], "Value") | + Builders.Filter.Eq(t => t.PesonsDictionary["string"].Name, "Bob") | + Builders.Filter.Eq(t => t.NestedDictionariesHolderDictionary["key"].IntDictionary["key"], 1) | + Builders.Filter.Eq(t => t.IntIDictionary["key"], 3) | + Builders.Filter.Eq(t => t.NestedDictionariesHolderIDictionary["key"].IntDictionary["key"], 3); + } + [BuildersMQL("{ \"$or\" : [{ \"Enumerable1.0\" : 2 }, { \"Enumerable2.1.Enumerable1.0\" : 2 }] }")] public void Enumerables() { @@ -45,6 +112,33 @@ public void Enumerables() Builders.Filter.Eq(t => t.Enumerable2.ElementAt(1).Enumerable1.ElementAt(0), 2); } + [BuildersMQL("{ \"$or\" : [{ \"IntHashSet.0\" : 2 }, { \"PesonsHashSet.0.SiblingsCount\" : 2 }, { \"StringHashSet.0\" : \"Value\" }, { \"NestedHashSetsHolderHashSet.0.IntHashSet.0\" : 2 }] }")] + public void HashSets() + { + _ = Builders.Filter.Eq(t => t.IntHashSet.ElementAt(0), 2) | + Builders.Filter.Eq(t => t.PesonsHashSet.ElementAt(0).SiblingsCount, 2) | + Builders.Filter.Eq(t => t.StringHashSet.ElementAt(0), "Value") | + Builders.Filter.Eq(t => t.NestedHashSetsHolderHashSet.ElementAt(0).IntHashSet.ElementAt(0), 2); + } + + [BuildersMQL("{ \"$or\" : [{ \"IntLinkedList.0\" : 1 }, { \"StringLinkedList.0\" : \"Value\" }, { \"PesonsLinkedList.0.Name\" : \"Bob\" }, { \"NestedLinkedListsHolderLinkedList.0.IntLinkedList.0\" : 1 }] }")] + public void LinkedLists() + { + _ = Builders.Filter.Eq(t => t.IntLinkedList.ElementAt(0), 1) | + Builders.Filter.Eq(t => t.StringLinkedList.ElementAt(0), "Value") | + Builders.Filter.Eq(t => t.PesonsLinkedList.ElementAt(0).Name, "Bob") | + Builders.Filter.Eq(t => t.NestedLinkedListsHolderLinkedList.ElementAt(0).IntLinkedList.ElementAt(0), 1); + } + + [BuildersMQL("{ \"$or\" : [{ \"IntLinkedListNode.Value\" : 1 }, { \"StringLinkedListNode.Value\" : \"Value\" }, { \"PesonsLinkedListNode.Value.Name\" : \"Bob\" }, { \"NestedLinkedListNodesHolderLinkedListNode.Value.IntLinkedListNode.Value\" : 1 }] }")] + public void LinkedListNodes() + { + _ = Builders.Filter.Eq(t => t.IntLinkedListNode.Value, 1) | + Builders.Filter.Eq(t => t.StringLinkedListNode.Value, "Value") | + Builders.Filter.Eq(t => t.PesonsLinkedListNode.Value.Name, "Bob") | + Builders.Filter.Eq(t => t.NestedLinkedListNodesHolderLinkedListNode.Value.IntLinkedListNode.Value, 1); + } + [BuildersMQL("{ \"$or\" : [{ \"IntList.2\" : 1 }, { \"StringList.3\" : \"Value\" }, { \"PesonsList.4.Name\" : \"Bob\" }, { \"NestedListsHolderList.5.IntList.1\" : 1 }, { \"IntIList.4\" : 3 }, { \"NestedListsHolderIList.15.IntList.3\" : 3 }] }")] public void Lists() { @@ -55,5 +149,32 @@ public void Lists() Builders.Filter.Eq(t => t.IntIList[4], 3) | Builders.Filter.Eq(t => t.NestedListsHolderIList[15].IntList[3], 3); } + + [BuildersMQL("{ \"$or\" : [{ \"IntQueue.0\" : 1 }, { \"PesonsQueue.0.Name\" : \"Name\" }, { \"StringQueue.0\" : \"Value\" }, { \"NestedQueuesHolderQueue.0.IntQueue.0\" : 1 }] }")] + public void Queues() + { + _ = Builders.Filter.Eq(t => t.IntQueue.ElementAt(0), 1) | + Builders.Filter.Eq(t => t.PesonsQueue.ElementAt(0).Name, "Name") | + Builders.Filter.Eq(t => t.StringQueue.ElementAt(0), "Value") | + Builders.Filter.Eq(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0), 1); + } + + [BuildersMQL("{ \"$or\" : [{ \"IntSortedDictionary.key\" : 1 }, { \"StringSortedDictionary.key\" : \"Value\" }, { \"PesonsSortedDictionary.string.Name\" : \"Bob\" }, { \"NestedSortedDictionariesHolderSortedDictionary.key.IntSortedDictionary.key\" : 1 }] }")] + public void SortedDictionaries() + { + _ = Builders.Filter.Eq(t => t.IntSortedDictionary["key"], 1) | + Builders.Filter.Eq(t => t.StringSortedDictionary["key"], "Value") | + Builders.Filter.Eq(t => t.PesonsSortedDictionary["string"].Name, "Bob") | + Builders.Filter.Eq(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"], 1); + } + + [BuildersMQL("{ \"$or\" : [{ \"IntStack.0\" : 1 }, { \"PesonsStack.0.Name\" : \"Name\" }, { \"StringStack.0\" : \"Value\" }, { \"NestedStacksHolderStack.0.IntStack.0\" : 1 }] }")] + public void Stack() + { + _ = Builders.Filter.Eq(t => t.IntStack.ElementAt(0), 1) | + Builders.Filter.Eq(t => t.PesonsStack.ElementAt(0).Name, "Name") | + Builders.Filter.Eq(t => t.StringStack.ElementAt(0), "Value") | + Builders.Filter.Eq(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0), 1); + } } } diff --git a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs index 83522682..0a57d11d 100644 --- a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs +++ b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs @@ -20,7 +20,19 @@ namespace MongoDB.Analyzer.Tests.Common.TestCases.Linq { public sealed class LinqCollections : TestCasesBase { - [MQL("aggregate([{ \"$match\" : { \"Enumerable1\" : { \"$size\" : 121 }, \"Enumerable1.12\" : 1, \"Enumerable2\" : { \"$size\" : 22 }, \"Enumerable2.12.Enumerable2.21.Enumerable1.1\" : 2 } }])")] + [NoDiagnostics] + public void CustomDictionaries() + { + _ = GetMongoQueryable().Where(t => + t.IntDictionary["key"] == 1 && + t.StringDictionary["key"] == "Value" && + t.PesonsDictionary["string"].Name == "Bob" && + t.NestedDictionariesHolderDictionary["key"].IntDictionary["key"] == 1 && + t.IntIDictionary["key"] == 3 && + t.NestedDictionariesHolderIDictionary["key"].IntDictionary["key"] == 3); + } + + [NoDiagnostics] public void CustomEnumerables() { _ = GetMongoQueryable().Where(t => @@ -30,7 +42,27 @@ public void CustomEnumerables() t.Enumerable2.ElementAt(12).Enumerable2.ElementAt(21).Enumerable1.ElementAt(1) == 2); } - [MQL("aggregate([{ \"$match\" : { \"IntList.0\" : 2 } }, { \"$match\" : { \"StringList\" : { \"$size\" : 12 } } }, { \"$match\" : { \"PesonsList.2.Address.City\" : \"Hamburg\" } }, { \"$match\" : { \"NestedListsHolderList.2.StringList.4\" : \"Nested\" } }, { \"$match\" : { \"IntIList.1\" : 12 } }, { \"$match\" : { \"NestedListsHolderIList.12.IntIList.12\" : 2 } }])")] + [NoDiagnostics] + public void CustomHashSets() + { + _ = GetMongoQueryable().Where(t => + t.IntHashSet.ElementAt(0) == 2 && + t.PesonsHashSet.ElementAt(0).SiblingsCount == 2 && + t.StringHashSet.ElementAt(0) == "Value" && + t.NestedHashSetsHolderHashSet.ElementAt(0).IntHashSet.ElementAt(0) == 2); + } + + [NoDiagnostics] + public void CustomLinkedLists() + { + _ = GetMongoQueryable().Where(t => + t.IntLinkedList.ElementAt(0) == 1 && + t.StringLinkedList.ElementAt(0) == "Value" && + t.PesonsLinkedList.ElementAt(0).Name == "Bob" && + t.NestedLinkedListsHolderLinkedList.ElementAt(0).IntLinkedList.ElementAt(0) == 1); + } + + [NoDiagnostics] public void CustomLists() { _ = GetMongoQueryable() @@ -42,6 +74,48 @@ public void CustomLists() .Where(t => t.NestedListsHolderIList[12].IntIList[12] == 2); } + [NoDiagnostics] + public void CustomQueues() + { + _ = GetMongoQueryable() + .Where(t => t.IntQueue.ElementAt(0) == 1) + .Where(t => t.PesonsQueue.ElementAt(0).Name == "Name") + .Where(t => t.StringQueue.ElementAt(0) == "Value") + .Where(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1); + } + + [NoDiagnostics] + public void CustomSortedDictionaries() + { + _ = GetMongoQueryable() + .Where(t => t.IntSortedDictionary["key"] == 1) + .Where(t => t.StringSortedDictionary["key"] == "Value") + .Where(t => t.PesonsSortedDictionary["string"].Name == "Bob") + .Where(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1); + } + + [NoDiagnostics] + public void CustomStacks() + { + _ = GetMongoQueryable() + .Where(t => t.IntStack.ElementAt(0) == 1) + .Where(t => t.PesonsStack.ElementAt(0).Name == "Name") + .Where(t => t.StringStack.ElementAt(0) == "Value") + .Where(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1); + } + + [MQL("aggregate([{ \"$match\" : { \"IntDictionary.key\" : 1, \"StringDictionary.key\" : \"Value\", \"PesonsDictionary.string.Name\" : \"Bob\", \"NestedDictionariesHolderDictionary.key.IntDictionary.key\" : 1, \"IntIDictionary.key\" : 3, \"NestedDictionariesHolderIDictionary.key.IntDictionary.key\" : 3 } }])")] + public void Dictionaries() + { + _ = GetMongoQueryable().Where(t => + t.IntDictionary["key"] == 1 && + t.StringDictionary["key"] == "Value" && + t.PesonsDictionary["string"].Name == "Bob" && + t.NestedDictionariesHolderDictionary["key"].IntDictionary["key"] == 1 && + t.IntIDictionary["key"] == 3 && + t.NestedDictionariesHolderIDictionary["key"].IntDictionary["key"] == 3); + } + [MQL("aggregate([{ \"$match\" : { \"Enumerable1\" : { \"$size\" : 121 }, \"Enumerable1.12\" : 1, \"Enumerable2\" : { \"$size\" : 22 }, \"Enumerable2.12.Enumerable2.21.Enumerable1.1\" : 2 } }])")] public void Enumerables() { @@ -52,6 +126,36 @@ public void Enumerables() t.Enumerable2.ElementAt(12).Enumerable2.ElementAt(21).Enumerable1.ElementAt(1) == 2); } + [MQL("aggregate([{ \"$match\" : { \"IntHashSet.0\" : 2, \"PesonsHashSet.0.SiblingsCount\" : 2, \"StringHashSet.0\" : \"Value\", \"NestedHashSetsHolderHashSet.0.IntHashSet.0\" : 2 } }])")] + public void HashSets() + { + _ = GetMongoQueryable().Where(t => + t.IntHashSet.ElementAt(0) == 2 && + t.PesonsHashSet.ElementAt(0).SiblingsCount == 2 && + t.StringHashSet.ElementAt(0) == "Value" && + t.NestedHashSetsHolderHashSet.ElementAt(0).IntHashSet.ElementAt(0) == 2); + } + + [MQL("aggregate([{ \"$match\" : { \"IntLinkedList.0\" : 1, \"StringLinkedList.0\" : \"Value\", \"PesonsLinkedList.0.Name\" : \"Bob\", \"NestedLinkedListsHolderLinkedList.0.IntLinkedList.0\" : 1 } }])")] + public void LinkedLists() + { + _ = GetMongoQueryable().Where(t => + t.IntLinkedList.ElementAt(0) == 1 && + t.StringLinkedList.ElementAt(0) == "Value" && + t.PesonsLinkedList.ElementAt(0).Name == "Bob" && + t.NestedLinkedListsHolderLinkedList.ElementAt(0).IntLinkedList.ElementAt(0) == 1); + } + + [MQL("aggregate([{ \"$match\" : { \"IntLinkedListNode.Value\" : 1, \"StringLinkedListNode.Value\" : \"Value\", \"PesonsLinkedListNode.Value.Name\" : \"Bob\", \"NestedLinkedListNodesHolderLinkedListNode.Value.IntLinkedListNode.Value\" : 1 } }])")] + public void LinkedListNodes() + { + _ = GetMongoQueryable().Where(t => + t.IntLinkedListNode.Value == 1 && + t.StringLinkedListNode.Value == "Value" && + t.PesonsLinkedListNode.Value.Name == "Bob" && + t.NestedLinkedListNodesHolderLinkedListNode.Value.IntLinkedListNode.Value == 1); + } + [MQL("aggregate([{ \"$match\" : { \"IntList.0\" : 2 } }, { \"$match\" : { \"StringList\" : { \"$size\" : 12 } } }, { \"$match\" : { \"PesonsList.2.Address.City\" : \"Hamburg\" } }, { \"$match\" : { \"NestedListsHolderList.2.StringList.4\" : \"Nested\" } }, { \"$match\" : { \"IntIList.1\" : 12 } }, { \"$match\" : { \"NestedListsHolderIList.12.IntIList.12\" : 2 } }])")] public void Lists() { @@ -64,13 +168,54 @@ public void Lists() .Where(t => t.NestedListsHolderIList[12].IntIList[12] == 2); } - - [MQL("aggregate([{ \"$match\" : { \"IntList.0\" : 2 } }, { \"$match\" : { \"StringList\" : { \"$size\" : 12 } } }, { \"$match\" : { \"PesonsList.2.Address.City\" : \"Hamburg\" } }, { \"$match\" : { \"NestedListsHolderList.2.StringList.4\" : \"Nested\" } }, { \"$match\" : { \"IntIList.1\" : 12 } }, { \"$match\" : { \"NestedListsHolderIList.12.IntIList.12\" : 2 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntDictionary.key\" : 1 } }, { \"$match\" : { \"StringDictionary.key\" : \"Value\" } }, { \"$match\" : { \"PesonsDictionary.string.Name\" : \"Bob\" } }, { \"$match\" : { \"NestedDictionariesHolderDictionary.key.IntDictionary.key\" : 1 } }, { \"$match\" : { \"IntIDictionary.key\" : 3 } }, { \"$match\" : { \"NestedDictionariesHolderIDictionary.key.IntDictionary.key\" : 3 } }])")] [MQL("aggregate([{ \"$match\" : { \"Enumerable1\" : { \"$size\" : 121 }, \"Enumerable1.12\" : 1, \"Enumerable2\" : { \"$size\" : 22 }, \"Enumerable2.12.Enumerable2.21.Enumerable1.1\" : 2 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntHashSet.0\" : 2, \"PesonsHashSet.0.SiblingsCount\" : 2, \"StringHashSet.0\" : \"Value\", \"NestedHashSetsHolderHashSet.0.IntHashSet.0\" : 2 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntLinkedList.0\" : 1, \"StringLinkedList.0\" : \"Value\", \"PesonsLinkedList.0.Name\" : \"Bob\", \"NestedLinkedListsHolderLinkedList.0.IntLinkedList.0\" : 1 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntLinkedListNode.Value\" : 1, \"StringLinkedListNode.Value\" : \"Value\", \"PesonsLinkedListNode.Value.Name\" : \"Bob\", \"NestedLinkedListNodesHolderLinkedListNode.Value.IntLinkedListNode.Value\" : 1 } }])")] [MQL("aggregate([{ \"$match\" : { \"IntList.0\" : 2 } }, { \"$match\" : { \"StringList\" : { \"$size\" : 12 } } }, { \"$match\" : { \"PesonsList.2.Address.City\" : \"Hamburg\" } }, { \"$match\" : { \"NestedListsHolderList.2.StringList.4\" : \"Nested\" } }, { \"$match\" : { \"IntIList.1\" : 12 } }, { \"$match\" : { \"NestedListsHolderIList.12.IntIList.12\" : 2 } }])")] - [MQL("aggregate([{ \"$match\" : { \"Enumerable1\" : { \"$size\" : 121 }, \"Enumerable1.12\" : 1, \"Enumerable2\" : { \"$size\" : 22 }, \"Enumerable2.12.Enumerable2.21.Enumerable1.1\" : 2 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntQueue.0\" : 1, \"PesonsQueue.0.Name\" : \"Name\", \"StringQueue.0\" : \"Value\", \"NestedQueuesHolderQueue.0.IntQueue.0\" : 1 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntSortedDictionary.key\" : 1, \"StringSortedDictionary.key\" : \"Value\", \"PesonsSortedDictionary.string.Name\" : \"Bob\", \"NestedSortedDictionariesHolderSortedDictionary.key.IntSortedDictionary.key\" : 1 } }])")] + [MQL("aggregate([{ \"$match\" : { \"IntStack.0\" : 1, \"PesonsStack.0.Name\" : \"Name\", \"StringStack.0\" : \"Value\", \"NestedStacksHolderStack.0.IntStack.0\" : 1 } }])")] public void Query_syntax() { + _ = from dictionariesHolder in GetMongoQueryable() + where dictionariesHolder.IntDictionary["key"] == 1 + where dictionariesHolder.StringDictionary["key"] == "Value" + where dictionariesHolder.PesonsDictionary["string"].Name == "Bob" + where dictionariesHolder.NestedDictionariesHolderDictionary["key"].IntDictionary["key"] == 1 + where dictionariesHolder.IntIDictionary["key"] == 3 + where dictionariesHolder.NestedDictionariesHolderIDictionary["key"].IntDictionary["key"] == 3 + select dictionariesHolder; + + _ = from enumerableHolder in GetMongoQueryable() + where enumerableHolder.Enumerable1.Count() == 121 && + enumerableHolder.Enumerable1.ElementAt(12) == 1 && + enumerableHolder.Enumerable2.Count() == 22 && + enumerableHolder.Enumerable2.ElementAt(12).Enumerable2.ElementAt(21).Enumerable1.ElementAt(1) == 2 + select enumerableHolder; + + _ = from hashSetHolder in GetMongoQueryable() + where hashSetHolder.IntHashSet.ElementAt(0) == 2 && + hashSetHolder.PesonsHashSet.ElementAt(0).SiblingsCount == 2 && + hashSetHolder.StringHashSet.ElementAt(0) == "Value" && + hashSetHolder.NestedHashSetsHolderHashSet.ElementAt(0).IntHashSet.ElementAt(0) == 2 + select hashSetHolder; + + _ = from linkedListHolder in GetMongoQueryable() + where linkedListHolder.IntLinkedList.ElementAt(0) == 1 && + linkedListHolder.StringLinkedList.ElementAt(0) == "Value" && + linkedListHolder.PesonsLinkedList.ElementAt(0).Name == "Bob" && + linkedListHolder.NestedLinkedListsHolderLinkedList.ElementAt(0).IntLinkedList.ElementAt(0) == 1 + select linkedListHolder; + + _ = from linkedListNodeHolder in GetMongoQueryable() + where linkedListNodeHolder.IntLinkedListNode.Value == 1 && + linkedListNodeHolder.StringLinkedListNode.Value == "Value" && + linkedListNodeHolder.PesonsLinkedListNode.Value.Name == "Bob" && + linkedListNodeHolder.NestedLinkedListNodesHolderLinkedListNode.Value.IntLinkedListNode.Value == 1 + select linkedListNodeHolder; + _ = from listsHolder in GetMongoQueryable() where listsHolder.IntList[0] == 2 where listsHolder.StringList.Count == 12 @@ -80,12 +225,57 @@ public void Query_syntax() where listsHolder.NestedListsHolderIList[12].IntIList[12] == 2 select listsHolder; - _ = from enumerableHolder in GetMongoQueryable() - where enumerableHolder.Enumerable1.Count() == 121 && - enumerableHolder.Enumerable1.ElementAt(12) == 1 && - enumerableHolder.Enumerable2.Count() == 22 && - enumerableHolder.Enumerable2.ElementAt(12).Enumerable2.ElementAt(21).Enumerable1.ElementAt(1) == 2 - select enumerableHolder; + + _ = from queueHolder in GetMongoQueryable() + where queueHolder.IntQueue.ElementAt(0) == 1 && + queueHolder.PesonsQueue.ElementAt(0).Name == "Name" && + queueHolder.StringQueue.ElementAt(0) == "Value" && + queueHolder.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1 + select queueHolder; + + _ = from sortedDictionaryHolder in GetMongoQueryable() + where sortedDictionaryHolder.IntSortedDictionary["key"] == 1 && + sortedDictionaryHolder.StringSortedDictionary["key"] == "Value" && + sortedDictionaryHolder.PesonsSortedDictionary["string"].Name == "Bob" && + sortedDictionaryHolder.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1 + select sortedDictionaryHolder; + + _ = from stackHolder in GetMongoQueryable() + where stackHolder.IntStack.ElementAt(0) == 1 && + stackHolder.PesonsStack.ElementAt(0).Name == "Name" && + stackHolder.StringStack.ElementAt(0) == "Value" && + stackHolder.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1 + select stackHolder; + + _ = from customDictionariesHolder in GetMongoQueryable() + where customDictionariesHolder.IntDictionary["key"] == 1 + where customDictionariesHolder.StringDictionary["key"] == "Value" + where customDictionariesHolder.PesonsDictionary["string"].Name == "Bob" + where customDictionariesHolder.NestedDictionariesHolderDictionary["key"].IntDictionary["key"] == 1 + where customDictionariesHolder.IntIDictionary["key"] == 3 + where customDictionariesHolder.NestedDictionariesHolderIDictionary["key"].IntDictionary["key"] == 3 + select customDictionariesHolder; + + _ = from customEnumerableHolder in GetMongoQueryable() + where customEnumerableHolder.Enumerable1.Count() == 121 && + customEnumerableHolder.Enumerable1.ElementAt(12) == 1 && + customEnumerableHolder.Enumerable2.Count() == 22 && + customEnumerableHolder.Enumerable2.ElementAt(12).Enumerable2.ElementAt(21).Enumerable1.ElementAt(1) == 2 + select customEnumerableHolder; + + _ = from customHashSetsHolder in GetMongoQueryable() + where customHashSetsHolder.IntHashSet.ElementAt(0) == 2 && + customHashSetsHolder.PesonsHashSet.ElementAt(0).SiblingsCount == 2 && + customHashSetsHolder.StringHashSet.ElementAt(0) == "Value" && + customHashSetsHolder.NestedHashSetsHolderHashSet.ElementAt(0).IntHashSet.ElementAt(0) == 2 + select customHashSetsHolder; + + _ = from customLinkedListHolder in GetMongoQueryable() + where customLinkedListHolder.IntLinkedList.ElementAt(0) == 1 && + customLinkedListHolder.StringLinkedList.ElementAt(0) == "Value" && + customLinkedListHolder.PesonsLinkedList.ElementAt(0).Name == "Bob" && + customLinkedListHolder.NestedLinkedListsHolderLinkedList.ElementAt(0).IntLinkedList.ElementAt(0) == 1 + select customLinkedListHolder; _ = from customListsHolder in GetMongoQueryable() where customListsHolder.IntList[0] == 2 @@ -96,12 +286,56 @@ where enumerableHolder.Enumerable1.Count() == 121 && where customListsHolder.NestedListsHolderIList[12].IntIList[12] == 2 select customListsHolder; - _ = from customEnumerableHolder in GetMongoQueryable() - where customEnumerableHolder.Enumerable1.Count() == 121 && - customEnumerableHolder.Enumerable1.ElementAt(12) == 1 && - customEnumerableHolder.Enumerable2.Count() == 22 && - customEnumerableHolder.Enumerable2.ElementAt(12).Enumerable2.ElementAt(21).Enumerable1.ElementAt(1) == 2 - select customEnumerableHolder; + _ = from customQueueHolder in GetMongoQueryable() + where customQueueHolder.IntQueue.ElementAt(0) == 1 && + customQueueHolder.PesonsQueue.ElementAt(0).Name == "Name" && + customQueueHolder.StringQueue.ElementAt(0) == "Value" && + customQueueHolder.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1 + select customQueueHolder; + + _ = from customSortedDictionaryHolder in GetMongoQueryable() + where customSortedDictionaryHolder.IntSortedDictionary["key"] == 1 && + customSortedDictionaryHolder.StringSortedDictionary["key"] == "Value" && + customSortedDictionaryHolder.PesonsSortedDictionary["string"].Name == "Bob" && + customSortedDictionaryHolder.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1 + select customSortedDictionaryHolder; + + _ = from customStackHolder in GetMongoQueryable() + where customStackHolder.IntStack.ElementAt(0) == 1 && + customStackHolder.PesonsStack.ElementAt(0).Name == "Name" && + customStackHolder.StringStack.ElementAt(0) == "Value" && + customStackHolder.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1 + select customStackHolder; + } + + [MQL("aggregate([{ \"$match\" : { \"IntQueue.0\" : 1 } }, { \"$match\" : { \"PesonsQueue.0.Name\" : \"Name\" } }, { \"$match\" : { \"StringQueue.0\" : \"Value\" } }, { \"$match\" : { \"NestedQueuesHolderQueue.0.IntQueue.0\" : 1 } }])")] + public void Queues() + { + _ = GetMongoQueryable() + .Where(t => t.IntQueue.ElementAt(0) == 1) + .Where(t => t.PesonsQueue.ElementAt(0).Name == "Name") + .Where(t => t.StringQueue.ElementAt(0) == "Value") + .Where(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1); + } + + [MQL("aggregate([{ \"$match\" : { \"IntSortedDictionary.key\" : 1 } }, { \"$match\" : { \"StringSortedDictionary.key\" : \"Value\" } }, { \"$match\" : { \"PesonsSortedDictionary.string.Name\" : \"Bob\" } }, { \"$match\" : { \"NestedSortedDictionariesHolderSortedDictionary.key.IntSortedDictionary.key\" : 1 } }])")] + public void SortedDictionaries() + { + _ = GetMongoQueryable() + .Where(t => t.IntSortedDictionary["key"] == 1) + .Where(t => t.StringSortedDictionary["key"] == "Value") + .Where(t => t.PesonsSortedDictionary["string"].Name == "Bob") + .Where(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1); + } + + [MQL("aggregate([{ \"$match\" : { \"IntStack.0\" : 1 } }, { \"$match\" : { \"PesonsStack.0.Name\" : \"Name\" } }, { \"$match\" : { \"StringStack.0\" : \"Value\" } }, { \"$match\" : { \"NestedStacksHolderStack.0.IntStack.0\" : 1 } }])")] + public void Stacks() + { + _ = GetMongoQueryable() + .Where(t => t.IntStack.ElementAt(0) == 1) + .Where(t => t.PesonsStack.ElementAt(0).Name == "Name") + .Where(t => t.StringStack.ElementAt(0) == "Value") + .Where(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1); } } } diff --git a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Poco/PocoCollections.cs b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Poco/PocoCollections.cs index c1c499b7..95ab3af1 100644 --- a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Poco/PocoCollections.cs +++ b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Poco/PocoCollections.cs @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +using System.Collections; using System.Collections.Generic; using MongoDB.Analyzer.Tests.Common.DataModel; @@ -19,11 +20,74 @@ namespace MongoDB.Analyzer.Tests.Common.TestCases.Poco { public sealed class PocoCollections : TestCasesBase { + [NoDiagnostics] + public void CustomDictionariesHolder() + { + } + + [NoDiagnostics] + public void CustomEnumerableHolder() + { + } + + [NoDiagnostics] + public void CustomHashSetsHolder() + { + } + + [NoDiagnostics] + public void CustomLinkedListHolder() + { + } + + [NoDiagnostics] + public void CustomListsHolder() + { + } + + [NoDiagnostics] + public void CustomQueueHolder() + { + } + + [NoDiagnostics] + public void CustomSortedDictionaryHolder() + { + } + + [NoDiagnostics] + public void CustomStackHolder() + { + } + + [PocoJson("{ \"IntDictionary\" : { }, \"PesonsDictionary\" : { }, \"StringDictionary\" : { }, \"NestedDictionariesHolderDictionary\" : { }, \"IntIDictionary\" : null, \"NestedDictionariesHolderIDictionary\" : null }")] + public void DictionariesHolder() + { + } + [PocoJson("{ \"Enumerable1\" : [], \"Enumerable2\" : [] }")] public void EnumerableHolder() { } + [PocoJson("{ \"IntHashSet\" : null, \"PesonsHashSet\" : null, \"StringHashSet\" : null, \"NestedHashSetsHolderHashSet\" : null }")] + public void HashSetHolder() + { + + } + + [PocoJson("{ \"IntLinkedList\" : null, \"PesonsLinkedList\" : null, \"StringLinkedList\" : null, \"NestedLinkedListsHolderLinkedList\" : null }")] + public void LinkedListHolder() + { + + } + + [PocoJson("{ \"IntLinkedListNode\" : null, \"PesonsLinkedListNode\" : null, \"StringLinkedListNode\" : null, \"NestedLinkedListNodesHolderLinkedListNode\" : null }")] + public void LinkedListNodeHolder() + { + + } + [PocoJson("{ \"IntList\" : [], \"PesonsList\" : [], \"StringList\" : [], \"NestedListsHolderList\" : [], \"IntIList\" : [], \"NestedListsHolderIList\" : [] }")] public void ListsHolder() { @@ -34,14 +98,198 @@ public void NestedCollectionHolder() { } + [PocoJson("{ \"IntQueue\" : null, \"PesonsQueue\" : null, \"StringQueue\" : null, \"NestedQueuesHolderQueue\" : null }")] + public void QueueHolder() + { + + } + + [PocoJson("{ \"IntSortedDictionary\" : { }, \"PesonsSortedDictionary\" : { }, \"StringSortedDictionary\" : { }, \"NestedSortedDictionariesHolderSortedDictionary\" : { } }")] + public void SortedDictionaryHolder() + { + + } + + [PocoJson("{ \"IntStack\" : null, \"PesonsStack\" : null, \"StringStack\" : null, \"NestedStacksHolderStack\" : null }")] + public void StackHolder() + { + + } + public class TestClasses { + public class CustomDictionariesHolder + { + public CustomDictionary IntDictionary { get; set; } + public CustomDictionary PesonsDictionary { get; set; } + public CustomDictionary StringDictionary { get; set; } + public CustomDictionary NestedDictionariesHolderDictionary { get; set; } + + public CustomIDictionary IntIDictionary { get; set; } + public CustomIDictionary NestedDictionariesHolderIDictionary { get; set; } + } + + public class CustomEnumerableHolder + { + public CustomIEnumerable Enumerable1 { get; set; } + public CustomIEnumerable Enumerable2 { get; set; } + } + + public class CustomDictionary : Dictionary { } + + public class CustomIDictionary : IDictionary + { + public TValue this[TKey key] { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + + public ICollection Keys => throw new System.NotImplementedException(); + + public ICollection Values => throw new System.NotImplementedException(); + + public int Count => throw new System.NotImplementedException(); + + public bool IsReadOnly => throw new System.NotImplementedException(); + + public void Add(TKey key, TValue value) => throw new System.NotImplementedException(); + public void Add(KeyValuePair item) => throw new System.NotImplementedException(); + public void Clear() => throw new System.NotImplementedException(); + public bool Contains(KeyValuePair item) => throw new System.NotImplementedException(); + public bool ContainsKey(TKey key) => throw new System.NotImplementedException(); + public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new System.NotImplementedException(); + public IEnumerator> GetEnumerator() => throw new System.NotImplementedException(); + public bool Remove(TKey key) => throw new System.NotImplementedException(); + public bool Remove(KeyValuePair item) => throw new System.NotImplementedException(); + public bool TryGetValue(TKey key, out TValue value) => throw new System.NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); + } + + public class CustomIEnumerable : IEnumerable + { + public IEnumerator GetEnumerator() => throw new System.NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); + } + + public class CustomIList : IList + { + public T this[int index] { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + public int Count => throw new System.NotImplementedException(); + public bool IsReadOnly => throw new System.NotImplementedException(); + public void Add(T item) => throw new System.NotImplementedException(); + public void Clear() => throw new System.NotImplementedException(); + public bool Contains(T item) => throw new System.NotImplementedException(); + public void CopyTo(T[] array, int arrayIndex) => throw new System.NotImplementedException(); + public IEnumerator GetEnumerator() => throw new System.NotImplementedException(); + public int IndexOf(T item) => throw new System.NotImplementedException(); + public void Insert(int index, T item) => throw new System.NotImplementedException(); + public bool Remove(T item) => throw new System.NotImplementedException(); + public void RemoveAt(int index) => throw new System.NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); + } + + public class CustomHashSet : HashSet { } + + public class CustomHashSetsHolder + { + public CustomHashSet IntHashSet { get; set; } + public CustomHashSet PesonsHashSet { get; set; } + public CustomHashSet StringHashSet { get; set; } + public CustomHashSet NestedHashSetsHolderHashSet { get; set; } + } + + public class CustomLinkedList : LinkedList { } + + public class CustomLinkedListHolder + { + public CustomLinkedList IntLinkedList { get; set; } + public CustomLinkedList PesonsLinkedList { get; set; } + public CustomLinkedList StringLinkedList { get; set; } + public CustomLinkedList NestedLinkedListsHolderLinkedList { get; set; } + } + + + public class CustomList : List { } + + public class CustomListsHolder + { + public CustomList IntList { get; set; } + public CustomList PesonsList { get; set; } + public CustomList StringList { get; set; } + public CustomList NestedListsHolderList { get; set; } + + public CustomIList IntIList { get; set; } + public CustomIList NestedListsHolderIList { get; set; } + } + + public class CustomQueue : Queue { } + + public class CustomQueueHolder + { + public CustomQueue IntQueue { get; set; } + public CustomQueue PesonsQueue { get; set; } + public CustomQueue StringQueue { get; set; } + public CustomQueue NestedQueuesHolderQueue { get; set; } + } + + public class CustomSortedDictionary : SortedDictionary { } + + public class CustomSortedDictionaryHolder + { + public CustomSortedDictionary IntSortedDictionary { get; set; } + public CustomSortedDictionary PesonsSortedDictionary { get; set; } + public CustomSortedDictionary StringSortedDictionary { get; set; } + public CustomSortedDictionary NestedSortedDictionariesHolderSortedDictionary { get; set; } + } + + public class CustomStack : Stack { } + + public class CustomStackHolder + { + public CustomStack IntStack { get; set; } + public CustomStack PesonsStack { get; set; } + public CustomStack StringStack { get; set; } + public CustomStack NestedStacksHolderStack { get; set; } + } + + public class DictionariesHolder + { + public Dictionary IntDictionary { get; set; } + public Dictionary PesonsDictionary { get; set; } + public System.Collections.Generic.Dictionary StringDictionary { get; set; } + public System.Collections.Generic.Dictionary NestedDictionariesHolderDictionary { get; set; } + + public IDictionary IntIDictionary { get; set; } + public System.Collections.Generic.IDictionary NestedDictionariesHolderIDictionary { get; set; } + } + public class EnumerableHolder { public IEnumerable Enumerable1 { get; set; } public System.Collections.Generic.IEnumerable Enumerable2 { get; set; } } + public class HashSetHolder + { + public HashSet IntHashSet { get; set; } + public HashSet PesonsHashSet { get; set; } + public System.Collections.Generic.HashSet StringHashSet { get; set; } + public System.Collections.Generic.HashSet NestedHashSetsHolderHashSet { get; set; } + } + + public class LinkedListHolder + { + public LinkedList IntLinkedList { get; set; } + public LinkedList PesonsLinkedList { get; set; } + public System.Collections.Generic.LinkedList StringLinkedList { get; set; } + public System.Collections.Generic.LinkedList NestedLinkedListsHolderLinkedList { get; set; } + } + + public class LinkedListNodeHolder + { + public LinkedListNode IntLinkedListNode { get; set; } + public LinkedListNode PesonsLinkedListNode { get; set; } + public System.Collections.Generic.LinkedListNode StringLinkedListNode { get; set; } + public System.Collections.Generic.LinkedListNode NestedLinkedListNodesHolderLinkedListNode { get; set; } + } + public class ListsHolder { public List IntList { get; set; } @@ -69,6 +317,30 @@ public class NestedCollectionHolder public IEnumerable> NestedStringIEnumerable { get; set; } public IEnumerable> NestedIntArrayIEnumerable { get; set; } } + + public class QueueHolder + { + public Queue IntQueue { get; set; } + public Queue PesonsQueue { get; set; } + public System.Collections.Generic.Queue StringQueue { get; set; } + public System.Collections.Generic.Queue NestedQueuesHolderQueue { get; set; } + } + + public class SortedDictionaryHolder + { + public SortedDictionary IntSortedDictionary { get; set; } + public SortedDictionary PesonsSortedDictionary { get; set; } + public System.Collections.Generic.SortedDictionary StringSortedDictionary { get; set; } + public System.Collections.Generic.SortedDictionary NestedSortedDictionariesHolderSortedDictionary { get; set; } + } + + public class StackHolder + { + public Stack IntStack { get; set; } + public Stack PesonsStack { get; set; } + public System.Collections.Generic.Stack StringStack { get; set; } + public System.Collections.Generic.Stack NestedStacksHolderStack { get; set; } + } } } } diff --git a/tests/MongoDB.Analyzer.Tests.Common/DataModel/Collections.cs b/tests/MongoDB.Analyzer.Tests.Common/DataModel/Collections.cs index c2d84465..0ac95e48 100644 --- a/tests/MongoDB.Analyzer.Tests.Common/DataModel/Collections.cs +++ b/tests/MongoDB.Analyzer.Tests.Common/DataModel/Collections.cs @@ -17,12 +17,48 @@ namespace MongoDB.Analyzer.Tests.Common.DataModel { + public class CustomDictionariesHolder + { + public CustomDictionary IntDictionary { get; set; } + public CustomDictionary PesonsDictionary { get; set; } + public CustomDictionary StringDictionary { get; set; } + public CustomDictionary NestedDictionariesHolderDictionary { get; set; } + + public CustomIDictionary IntIDictionary { get; set; } + public CustomIDictionary NestedDictionariesHolderIDictionary { get; set; } + } + public class CustomEnumerableHolder { public CustomIEnumerable Enumerable1 { get; set; } public CustomIEnumerable Enumerable2 { get; set; } } + public class CustomIDictionary : IDictionary + { + public TValue this[TKey key] { get => throw new System.NotImplementedException(); set => throw new System.NotImplementedException(); } + + public ICollection Keys => throw new System.NotImplementedException(); + + public ICollection Values => throw new System.NotImplementedException(); + + public int Count => throw new System.NotImplementedException(); + + public bool IsReadOnly => throw new System.NotImplementedException(); + + public void Add(TKey key, TValue value) => throw new System.NotImplementedException(); + public void Add(KeyValuePair item) => throw new System.NotImplementedException(); + public void Clear() => throw new System.NotImplementedException(); + public bool Contains(KeyValuePair item) => throw new System.NotImplementedException(); + public bool ContainsKey(TKey key) => throw new System.NotImplementedException(); + public void CopyTo(KeyValuePair[] array, int arrayIndex) => throw new System.NotImplementedException(); + public IEnumerator> GetEnumerator() => throw new System.NotImplementedException(); + public bool Remove(TKey key) => throw new System.NotImplementedException(); + public bool Remove(KeyValuePair item) => throw new System.NotImplementedException(); + public bool TryGetValue(TKey key, out TValue value) => throw new System.NotImplementedException(); + IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); + } + public class CustomIEnumerable : IEnumerable { public IEnumerator GetEnumerator() => throw new System.NotImplementedException(); @@ -46,6 +82,29 @@ public class CustomIList : IList IEnumerator IEnumerable.GetEnumerator() => throw new System.NotImplementedException(); } + public class CustomDictionary : Dictionary { } + + public class CustomHashSet : HashSet { } + + public class CustomHashSetsHolder + { + public CustomHashSet IntHashSet { get; set; } + public CustomHashSet PesonsHashSet { get; set; } + public CustomHashSet StringHashSet { get; set; } + public CustomHashSet NestedHashSetsHolderHashSet { get; set; } + } + + public class CustomLinkedList : LinkedList { } + + public class CustomLinkedListHolder + { + public CustomLinkedList IntLinkedList { get; set; } + public CustomLinkedList PesonsLinkedList { get; set; } + public CustomLinkedList StringLinkedList { get; set; } + public CustomLinkedList NestedLinkedListsHolderLinkedList { get; set; } + } + + public class CustomList : List { } public class CustomListsHolder @@ -59,12 +118,77 @@ public class CustomListsHolder public CustomIList NestedListsHolderIList { get; set; } } + public class CustomQueue: Queue { } + + public class CustomQueueHolder + { + public CustomQueue IntQueue { get; set; } + public CustomQueue PesonsQueue { get; set; } + public CustomQueue StringQueue { get; set; } + public CustomQueue NestedQueuesHolderQueue { get; set; } + } + + public class CustomSortedDictionary : SortedDictionary { } + + public class CustomSortedDictionaryHolder + { + public CustomSortedDictionary IntSortedDictionary { get; set; } + public CustomSortedDictionary PesonsSortedDictionary { get; set; } + public CustomSortedDictionary StringSortedDictionary { get; set; } + public CustomSortedDictionary NestedSortedDictionariesHolderSortedDictionary { get; set; } + } + + public class CustomStack : Stack { } + + public class CustomStackHolder + { + public CustomStack IntStack { get; set; } + public CustomStack PesonsStack { get; set; } + public CustomStack StringStack { get; set; } + public CustomStack NestedStacksHolderStack { get; set; } + } + + public class DictionariesHolder + { + public Dictionary IntDictionary { get; set; } + public Dictionary PesonsDictionary { get; set; } + public System.Collections.Generic.Dictionary StringDictionary { get; set; } + public System.Collections.Generic.Dictionary NestedDictionariesHolderDictionary { get; set; } + + public IDictionary IntIDictionary { get; set; } + public System.Collections.Generic.IDictionary NestedDictionariesHolderIDictionary { get; set; } + } + public class EnumerableHolder { public IEnumerable Enumerable1 { get; set; } public System.Collections.Generic.IEnumerable Enumerable2 { get; set; } } + public class HashSetHolder + { + public HashSet IntHashSet { get; set; } + public HashSet PesonsHashSet { get; set; } + public System.Collections.Generic.HashSet StringHashSet { get; set; } + public System.Collections.Generic.HashSet NestedHashSetsHolderHashSet { get; set; } + } + + public class LinkedListHolder + { + public LinkedList IntLinkedList { get; set; } + public LinkedList PesonsLinkedList { get; set; } + public System.Collections.Generic.LinkedList StringLinkedList { get; set; } + public System.Collections.Generic.LinkedList NestedLinkedListsHolderLinkedList { get; set; } + } + + public class LinkedListNodeHolder + { + public LinkedListNode IntLinkedListNode { get; set; } + public LinkedListNode PesonsLinkedListNode { get; set; } + public System.Collections.Generic.LinkedListNode StringLinkedListNode { get; set; } + public System.Collections.Generic.LinkedListNode NestedLinkedListNodesHolderLinkedListNode { get; set; } + } + public class ListsHolder { public List IntList { get; set; } @@ -75,4 +199,28 @@ public class ListsHolder public IList IntIList { get; set; } public System.Collections.Generic.IList NestedListsHolderIList { get; set; } } + + public class QueueHolder + { + public Queue IntQueue { get; set; } + public Queue PesonsQueue { get; set; } + public System.Collections.Generic.Queue StringQueue { get; set; } + public System.Collections.Generic.Queue NestedQueuesHolderQueue { get; set; } + } + + public class SortedDictionaryHolder + { + public SortedDictionary IntSortedDictionary { get; set; } + public SortedDictionary PesonsSortedDictionary { get; set; } + public System.Collections.Generic.SortedDictionary StringSortedDictionary { get; set; } + public System.Collections.Generic.SortedDictionary NestedSortedDictionariesHolderSortedDictionary { get; set; } + } + + public class StackHolder + { + public Stack IntStack { get; set; } + public Stack PesonsStack { get; set; } + public System.Collections.Generic.Stack StringStack { get; set; } + public System.Collections.Generic.Stack NestedStacksHolderStack { get; set; } + } } From bb0d5546b7855f1959e6b70469a63efad5a7fa24 Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Mon, 5 Aug 2024 17:02:08 -0400 Subject: [PATCH 2/8] Added try-catch for name caching in TypesProcessor --- .../Builders/BuilderExpressionProcessor.cs | 16 +--- src/MongoDB.Analyzer/Core/TypesProcessor.cs | 24 ++++-- .../Core/Utilities/SymbolExtensions.cs | 73 +++++++++++-------- .../Linq/LinqCollections.cs | 72 +++++++++--------- 4 files changed, 94 insertions(+), 91 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs b/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs index e8b97014..37bc9814 100644 --- a/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs +++ b/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs @@ -81,7 +81,7 @@ public static ExpressionsAnalysis ProcessSemanticModel(MongoAnalysisContext cont try { - if (!ProcessTypeArguments(namedType.TypeArguments, typesProcessor)) + if (!namedType.TypeArguments.All(t => typesProcessor.ProcessTypeSymbol(t) != null)) { continue; } @@ -253,18 +253,4 @@ private static (NodeType NodeType, INamedTypeSymbol NamedSymbol, SyntaxNode Expr return (nodeType, namedType, expressionNode); } - - private static bool ProcessTypeArguments(IEnumerable typeArguments, TypesProcessor typesProcessor) - { - foreach (var typeArgument in typeArguments) - { - var remappedName = typesProcessor.ProcessTypeSymbol(typeArgument); - if (remappedName == null) - { - return false; - } - } - - return true; - } } diff --git a/src/MongoDB.Analyzer/Core/TypesProcessor.cs b/src/MongoDB.Analyzer/Core/TypesProcessor.cs index 12da2354..08fa9686 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -65,11 +65,19 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) } remappedName = GetNewNameForSymbol(typeSymbol); - _processedTypes[fullTypeName] = (remappedName, null); // Cache the name, for self referencing types + BaseTypeDeclarationSyntax rewrittenDeclarationSyntax; - var rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); + try + { + _processedTypes[fullTypeName] = (remappedName, null); // Cache the name, for self referencing types + rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); - if (rewrittenDeclarationSyntax == null) + if (rewrittenDeclarationSyntax == null) + { + throw new NotSupportedException("This type symbol is not supported."); + } + } + catch { _processedTypes.Remove(fullTypeName); return null; @@ -109,7 +117,7 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) // TODO optimize else if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.TypeArguments.Length >= 1 && - namedTypeSymbol.IsSupportedCollection()) + namedTypeSymbol.IsDerivedFromSystemCollectionGenerics()) { var underlyingTypeSyntaxes = namedTypeSymbol.TypeArguments.Select(typeArgument => CreateTypeSyntaxForSymbol(typeArgument)); if (underlyingTypeSyntaxes.Any(underlyingTypeSyntax => underlyingTypeSyntax == null)) @@ -131,7 +139,7 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) SyntaxFactory.IdentifierName("Generic")), collectionSyntax); } - else if (typeSymbol.IsUserDefinedCollection()) + else if (typeSymbol.IsDerivedFromSystemCollectionGenerics(recursive: true)) { return null; } @@ -205,7 +213,7 @@ private bool GenerateFields(ITypeSymbol typeSymbol, List() .Where(p => !p.IsStatic && - (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSupportedCollection()) && + (p.Type.TypeKind != TypeKind.Interface || p.Type.IsDerivedFromSystemCollectionGenerics()) && p.DeclaredAccessibility == Accessibility.Public); foreach (var fieldSymbol in typeFields) @@ -242,7 +250,7 @@ private bool GenerateProperties(ITypeSymbol typeSymbol, List !p.IsStatic && !p.IsIndexer && - (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSupportedCollection()) && + (p.Type.TypeKind != TypeKind.Interface || p.Type.IsDerivedFromSystemCollectionGenerics()) && p.DeclaredAccessibility == Accessibility.Public); foreach (var propertySymbol in typeProperties) @@ -284,7 +292,7 @@ private string GetFullName(ITypeSymbol typeSymbol) => private (string RemappedName, string FullTypeName) GetGeneratedTypeMapping(ITypeSymbol typeSymbol, bool userOnlyTypes) { if (typeSymbol == null || - typeSymbol.IsUserDefinedCollection()) + typeSymbol.IsDerivedFromSystemCollectionGenerics(recursive: true)) { return default; } diff --git a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs index b3cdd516..684cb814 100644 --- a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs +++ b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs @@ -56,6 +56,12 @@ internal static class SymbolExtensions "MongoDB.Bson.Serialization.Options.TimeSpanUnits" }; + private static readonly HashSet s_supportedCollectionInterfaces = new() + { + "System.Collections.Generic.ICollection", + "System.Collections.Generic.IReadOnlyCollection" + }; + private static readonly HashSet s_supportedSystemTypes = new() { "System.DateTime", @@ -188,6 +194,41 @@ public static bool IsDefinedInSystem(this ISymbol symbol) return false; } + public static bool IsDerivedFromSystemCollectionGenerics(this ITypeSymbol typeSymbol, bool recursive = false) + { + if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) + { + return false; + } + + if (!recursive) + { + return namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric; + } + + // Check Interfaces + if (namedTypeSymbol.AllInterfaces.Any(interfaceSymbol => interfaceSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric) && + !namedTypeSymbol.IsDefinedInMongoBson() && + !namedTypeSymbol.IsDefinedInMongoDriver() && + !namedTypeSymbol.IsDefinedInSystem()) + { + return true; + } + + // Check Base Types + while (namedTypeSymbol != null) + { + if (namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric) + { + return true; + } + + namedTypeSymbol = namedTypeSymbol.BaseType; + } + + return false; + } + public static bool IsFindFluent(this ITypeSymbol typeSymbol) => typeSymbol?.Name switch { @@ -237,10 +278,6 @@ TypeKind.Enum or _ => false }; - public static bool IsSupportedCollection(this ITypeSymbol typeSymbol) => - typeSymbol is INamedTypeSymbol namedTypeSymbol && - namedTypeSymbol.ContainingNamespace.ToDisplayString() == NamespaceCollectionGeneric; - public static bool IsSupportedIMongoCollection(this ITypeSymbol typeSymbol) => typeSymbol.IsIMongoCollection() && typeSymbol is INamedTypeSymbol namedType && @@ -255,34 +292,6 @@ public static bool IsSupportedSystemType(this ITypeSymbol typeSymbol, string ful (typeSymbol.SpecialType != SpecialType.None || s_supportedSystemTypes.Contains(fullTypeName)) && typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceSystem; - public static bool IsUserDefinedCollection(this ITypeSymbol typeSymbol) - { - if (typeSymbol is not INamedTypeSymbol namedTypeSymbol || - namedTypeSymbol.IsDefinedInMongoBson() || - namedTypeSymbol.IsDefinedInMongoDriver() || - namedTypeSymbol.IsDefinedInSystem()) - { - return false; - } - - while (namedTypeSymbol != null) - { - if (namedTypeSymbol.IsSupportedCollection()) - { - return true; - } - - if (namedTypeSymbol.Interfaces.Any(i => i.IsSupportedCollection())) - { - return true; - } - - namedTypeSymbol = namedTypeSymbol.BaseType; - } - - return false; - } - private static SyntaxToken[] GetPublicFieldModifiers() => new[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword) }; diff --git a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs index 0a57d11d..ff071b4e 100644 --- a/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs +++ b/tests/MongoDB.Analyzer.Tests.Common.TestCases/Linq/LinqCollections.cs @@ -66,42 +66,42 @@ public void CustomLinkedLists() public void CustomLists() { _ = GetMongoQueryable() - .Where(t => t.IntList[0] == 2) - .Where(t => t.StringList.Count == 12) - .Where(t => t.PesonsList[2].Address.City == "Hamburg") - .Where(t => t.NestedListsHolderList[2].StringList[4] == "Nested") - .Where(t => t.IntIList[1] == 12) - .Where(t => t.NestedListsHolderIList[12].IntIList[12] == 2); + .Where(t => t.IntList[0] == 2) + .Where(t => t.StringList.Count == 12) + .Where(t => t.PesonsList[2].Address.City == "Hamburg") + .Where(t => t.NestedListsHolderList[2].StringList[4] == "Nested") + .Where(t => t.IntIList[1] == 12) + .Where(t => t.NestedListsHolderIList[12].IntIList[12] == 2); } [NoDiagnostics] public void CustomQueues() { _ = GetMongoQueryable() - .Where(t => t.IntQueue.ElementAt(0) == 1) - .Where(t => t.PesonsQueue.ElementAt(0).Name == "Name") - .Where(t => t.StringQueue.ElementAt(0) == "Value") - .Where(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1); + .Where(t => t.IntQueue.ElementAt(0) == 1) + .Where(t => t.PesonsQueue.ElementAt(0).Name == "Name") + .Where(t => t.StringQueue.ElementAt(0) == "Value") + .Where(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1); } [NoDiagnostics] public void CustomSortedDictionaries() { _ = GetMongoQueryable() - .Where(t => t.IntSortedDictionary["key"] == 1) - .Where(t => t.StringSortedDictionary["key"] == "Value") - .Where(t => t.PesonsSortedDictionary["string"].Name == "Bob") - .Where(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1); + .Where(t => t.IntSortedDictionary["key"] == 1) + .Where(t => t.StringSortedDictionary["key"] == "Value") + .Where(t => t.PesonsSortedDictionary["string"].Name == "Bob") + .Where(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1); } [NoDiagnostics] public void CustomStacks() { _ = GetMongoQueryable() - .Where(t => t.IntStack.ElementAt(0) == 1) - .Where(t => t.PesonsStack.ElementAt(0).Name == "Name") - .Where(t => t.StringStack.ElementAt(0) == "Value") - .Where(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1); + .Where(t => t.IntStack.ElementAt(0) == 1) + .Where(t => t.PesonsStack.ElementAt(0).Name == "Name") + .Where(t => t.StringStack.ElementAt(0) == "Value") + .Where(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1); } [MQL("aggregate([{ \"$match\" : { \"IntDictionary.key\" : 1, \"StringDictionary.key\" : \"Value\", \"PesonsDictionary.string.Name\" : \"Bob\", \"NestedDictionariesHolderDictionary.key.IntDictionary.key\" : 1, \"IntIDictionary.key\" : 3, \"NestedDictionariesHolderIDictionary.key.IntDictionary.key\" : 3 } }])")] @@ -160,12 +160,12 @@ public void LinkedListNodes() public void Lists() { _ = GetMongoQueryable() - .Where(t => t.IntList[0] == 2) - .Where(t => t.StringList.Count == 12) - .Where(t => t.PesonsList[2].Address.City == "Hamburg") - .Where(t => t.NestedListsHolderList[2].StringList[4] == "Nested") - .Where(t => t.IntIList[1] == 12) - .Where(t => t.NestedListsHolderIList[12].IntIList[12] == 2); + .Where(t => t.IntList[0] == 2) + .Where(t => t.StringList.Count == 12) + .Where(t => t.PesonsList[2].Address.City == "Hamburg") + .Where(t => t.NestedListsHolderList[2].StringList[4] == "Nested") + .Where(t => t.IntIList[1] == 12) + .Where(t => t.NestedListsHolderIList[12].IntIList[12] == 2); } [MQL("aggregate([{ \"$match\" : { \"IntDictionary.key\" : 1 } }, { \"$match\" : { \"StringDictionary.key\" : \"Value\" } }, { \"$match\" : { \"PesonsDictionary.string.Name\" : \"Bob\" } }, { \"$match\" : { \"NestedDictionariesHolderDictionary.key.IntDictionary.key\" : 1 } }, { \"$match\" : { \"IntIDictionary.key\" : 3 } }, { \"$match\" : { \"NestedDictionariesHolderIDictionary.key.IntDictionary.key\" : 3 } }])")] @@ -312,30 +312,30 @@ where customStackHolder.IntStack.ElementAt(0) == 1 && public void Queues() { _ = GetMongoQueryable() - .Where(t => t.IntQueue.ElementAt(0) == 1) - .Where(t => t.PesonsQueue.ElementAt(0).Name == "Name") - .Where(t => t.StringQueue.ElementAt(0) == "Value") - .Where(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1); + .Where(t => t.IntQueue.ElementAt(0) == 1) + .Where(t => t.PesonsQueue.ElementAt(0).Name == "Name") + .Where(t => t.StringQueue.ElementAt(0) == "Value") + .Where(t => t.NestedQueuesHolderQueue.ElementAt(0).IntQueue.ElementAt(0) == 1); } [MQL("aggregate([{ \"$match\" : { \"IntSortedDictionary.key\" : 1 } }, { \"$match\" : { \"StringSortedDictionary.key\" : \"Value\" } }, { \"$match\" : { \"PesonsSortedDictionary.string.Name\" : \"Bob\" } }, { \"$match\" : { \"NestedSortedDictionariesHolderSortedDictionary.key.IntSortedDictionary.key\" : 1 } }])")] public void SortedDictionaries() { _ = GetMongoQueryable() - .Where(t => t.IntSortedDictionary["key"] == 1) - .Where(t => t.StringSortedDictionary["key"] == "Value") - .Where(t => t.PesonsSortedDictionary["string"].Name == "Bob") - .Where(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1); + .Where(t => t.IntSortedDictionary["key"] == 1) + .Where(t => t.StringSortedDictionary["key"] == "Value") + .Where(t => t.PesonsSortedDictionary["string"].Name == "Bob") + .Where(t => t.NestedSortedDictionariesHolderSortedDictionary["key"].IntSortedDictionary["key"] == 1); } [MQL("aggregate([{ \"$match\" : { \"IntStack.0\" : 1 } }, { \"$match\" : { \"PesonsStack.0.Name\" : \"Name\" } }, { \"$match\" : { \"StringStack.0\" : \"Value\" } }, { \"$match\" : { \"NestedStacksHolderStack.0.IntStack.0\" : 1 } }])")] public void Stacks() { _ = GetMongoQueryable() - .Where(t => t.IntStack.ElementAt(0) == 1) - .Where(t => t.PesonsStack.ElementAt(0).Name == "Name") - .Where(t => t.StringStack.ElementAt(0) == "Value") - .Where(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1); + .Where(t => t.IntStack.ElementAt(0) == 1) + .Where(t => t.PesonsStack.ElementAt(0).Name == "Name") + .Where(t => t.StringStack.ElementAt(0) == "Value") + .Where(t => t.NestedStacksHolderStack.ElementAt(0).IntStack.ElementAt(0) == 1); } } } From 1f3ca8d7fac8898ddc1160bf7838575908fedf4a Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Mon, 5 Aug 2024 17:32:23 -0400 Subject: [PATCH 3/8] Added try-catch for name caching in TypesProcessor --- src/MongoDB.Analyzer/Core/TypesProcessor.cs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/TypesProcessor.cs b/src/MongoDB.Analyzer/Core/TypesProcessor.cs index 08fa9686..e22c77a2 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -67,17 +67,18 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) remappedName = GetNewNameForSymbol(typeSymbol); BaseTypeDeclarationSyntax rewrittenDeclarationSyntax; + _processedTypes[fullTypeName] = (remappedName, null); // Cache the name, for self referencing types + try { - _processedTypes[fullTypeName] = (remappedName, null); // Cache the name, for self referencing types rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); - - if (rewrittenDeclarationSyntax == null) - { - throw new NotSupportedException("This type symbol is not supported."); - } } catch + { + throw new NotSupportedException($"Symbol type {typeSymbol.ToDisplayString()} is not supported."); + } + + if (rewrittenDeclarationSyntax == null) { _processedTypes.Remove(fullTypeName); return null; From 9e52acaaea3f9b3e9c0e527d468bfafee58ea912 Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Mon, 5 Aug 2024 17:34:17 -0400 Subject: [PATCH 4/8] Added try-catch for name caching in TypesProcessor --- src/MongoDB.Analyzer/Core/TypesProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/TypesProcessor.cs b/src/MongoDB.Analyzer/Core/TypesProcessor.cs index e22c77a2..6d728e36 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -73,9 +73,9 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) { rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); } - catch + catch (Exception ex) { - throw new NotSupportedException($"Symbol type {typeSymbol.ToDisplayString()} is not supported."); + throw new NotSupportedException($"Symbol type {typeSymbol.ToDisplayString()} is not supported with {ex.Message}."); } if (rewrittenDeclarationSyntax == null) From 80f42b92a9d80f56eef3ebb13658ffc381327109 Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Tue, 6 Aug 2024 09:47:45 -0400 Subject: [PATCH 5/8] Modified Try-Catch part --- src/MongoDB.Analyzer/Core/TypesProcessor.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/TypesProcessor.cs b/src/MongoDB.Analyzer/Core/TypesProcessor.cs index 6d728e36..50bc5177 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -73,9 +73,10 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) { rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); } - catch (Exception ex) + catch { - throw new NotSupportedException($"Symbol type {typeSymbol.ToDisplayString()} is not supported with {ex.Message}."); + _processedTypes.Remove(fullTypeName); + throw; } if (rewrittenDeclarationSyntax == null) From 02402354408596d55d84a859ec26dff30e2e2a6b Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Tue, 6 Aug 2024 18:20:48 -0400 Subject: [PATCH 6/8] Made Changes --- src/MongoDB.Analyzer/Core/TypesProcessor.cs | 24 +++-- .../Core/Utilities/SymbolExtensions.cs | 91 ++++++------------- 2 files changed, 41 insertions(+), 74 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/TypesProcessor.cs b/src/MongoDB.Analyzer/Core/TypesProcessor.cs index 50bc5177..a9017c72 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -85,11 +85,8 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) return null; } - var typeCode = rewrittenDeclarationSyntax.ToFullString(); - var newTypeDeclaration = SyntaxFactory.ParseMemberDeclaration(typeCode); - remappedName = rewrittenDeclarationSyntax.Identifier.Text; - _processedTypes[fullTypeName] = (remappedName, newTypeDeclaration); + _processedTypes[fullTypeName] = (remappedName, rewrittenDeclarationSyntax); return remappedName; } @@ -119,7 +116,7 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) // TODO optimize else if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.TypeArguments.Length >= 1 && - namedTypeSymbol.IsDerivedFromSystemCollectionGenerics()) + typeSymbol.IsSystemCollection()) { var underlyingTypeSyntaxes = namedTypeSymbol.TypeArguments.Select(typeArgument => CreateTypeSyntaxForSymbol(typeArgument)); if (underlyingTypeSyntaxes.Any(underlyingTypeSyntax => underlyingTypeSyntax == null)) @@ -141,10 +138,6 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) SyntaxFactory.IdentifierName("Generic")), collectionSyntax); } - else if (typeSymbol.IsDerivedFromSystemCollectionGenerics(recursive: true)) - { - return null; - } else { var (isNullable, underlingTypeSymbol) = typeSymbol.DiscardNullable(); @@ -215,7 +208,7 @@ private bool GenerateFields(ITypeSymbol typeSymbol, List() .Where(p => !p.IsStatic && - (p.Type.TypeKind != TypeKind.Interface || p.Type.IsDerivedFromSystemCollectionGenerics()) && + (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSystemCollection()) && p.DeclaredAccessibility == Accessibility.Public); foreach (var fieldSymbol in typeFields) @@ -252,7 +245,7 @@ private bool GenerateProperties(ITypeSymbol typeSymbol, List !p.IsStatic && !p.IsIndexer && - (p.Type.TypeKind != TypeKind.Interface || p.Type.IsDerivedFromSystemCollectionGenerics()) && + (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSystemCollection()) && p.DeclaredAccessibility == Accessibility.Public); foreach (var propertySymbol in typeProperties) @@ -293,8 +286,7 @@ private string GetFullName(ITypeSymbol typeSymbol) => private (string RemappedName, string FullTypeName) GetGeneratedTypeMapping(ITypeSymbol typeSymbol, bool userOnlyTypes) { - if (typeSymbol == null || - typeSymbol.IsDerivedFromSystemCollectionGenerics(recursive: true)) + if (typeSymbol == null) { return default; } @@ -315,6 +307,12 @@ private string GetFullName(ITypeSymbol typeSymbol) => return (fullTypeName, fullTypeName); } + if (!userOnlyTypes && typeSymbol.IsSystemCollection(includeBaseTypesAndInterfaces: true)) + { + // Types derived from System.Collections.Generic are not supported + return default; + } + return (null, fullTypeName); } diff --git a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs index 684cb814..a9f2db55 100644 --- a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs +++ b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs @@ -16,7 +16,6 @@ namespace MongoDB.Analyzer.Core; internal static class SymbolExtensions { - private const string AssemblyMongoDBBson = "MongoDB.Bson"; private const string AssemblyMongoDBDriver = "MongoDB.Driver"; private const string NamespaceCollectionGeneric = "System.Collections.Generic"; private const string NamespaceEF = "Microsoft.EntityFrameworkCore"; @@ -56,12 +55,6 @@ internal static class SymbolExtensions "MongoDB.Bson.Serialization.Options.TimeSpanUnits" }; - private static readonly HashSet s_supportedCollectionInterfaces = new() - { - "System.Collections.Generic.ICollection", - "System.Collections.Generic.IReadOnlyCollection" - }; - private static readonly HashSet s_supportedSystemTypes = new() { "System.DateTime", @@ -161,9 +154,6 @@ public static bool IsDBSet(this ITypeSymbol typeSymbol) => typeSymbol?.Name == "DbSet" && typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceEF; - public static bool IsDefinedInMongoBson(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBBson && - symbol?.ContainingAssembly.Name == AssemblyMongoDBBson; - public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; @@ -178,57 +168,6 @@ public static bool IsDefinedInMongoLinqOrSystemLinq(this ISymbol symbol) symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; } - public static bool IsDefinedInSystem(this ISymbol symbol) - { - var containingNamespace = symbol?.ContainingNamespace; - while (containingNamespace != null) - { - if (containingNamespace.Name == NamespaceSystem) - { - return true; - } - - containingNamespace = containingNamespace.ContainingNamespace; - } - - return false; - } - - public static bool IsDerivedFromSystemCollectionGenerics(this ITypeSymbol typeSymbol, bool recursive = false) - { - if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) - { - return false; - } - - if (!recursive) - { - return namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric; - } - - // Check Interfaces - if (namedTypeSymbol.AllInterfaces.Any(interfaceSymbol => interfaceSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric) && - !namedTypeSymbol.IsDefinedInMongoBson() && - !namedTypeSymbol.IsDefinedInMongoDriver() && - !namedTypeSymbol.IsDefinedInSystem()) - { - return true; - } - - // Check Base Types - while (namedTypeSymbol != null) - { - if (namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric) - { - return true; - } - - namedTypeSymbol = namedTypeSymbol.BaseType; - } - - return false; - } - public static bool IsFindFluent(this ITypeSymbol typeSymbol) => typeSymbol?.Name switch { @@ -292,6 +231,36 @@ public static bool IsSupportedSystemType(this ITypeSymbol typeSymbol, string ful (typeSymbol.SpecialType != SpecialType.None || s_supportedSystemTypes.Contains(fullTypeName)) && typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceSystem; + public static bool IsSystemCollection(this ITypeSymbol typeSymbol, bool includeBaseTypesAndInterfaces = false) + { + if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) + { + return default; + } + + if (!includeBaseTypesAndInterfaces) + { + return namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric; + } + + if (namedTypeSymbol.AllInterfaces.Any(interfaceSymbol => interfaceSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric)) + { + return true; + } + + while (namedTypeSymbol != null) + { + if (namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric) + { + return true; + } + + namedTypeSymbol = namedTypeSymbol.BaseType; + } + + return false; + } + private static SyntaxToken[] GetPublicFieldModifiers() => new[] { SyntaxFactory.Token(SyntaxKind.PublicKeyword) }; From 989c9ddce7326cebeca2c3e467eabb8756afcdf3 Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Tue, 6 Aug 2024 18:22:00 -0400 Subject: [PATCH 7/8] Made Changes --- src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs index a9f2db55..49bafea9 100644 --- a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs +++ b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs @@ -154,8 +154,7 @@ public static bool IsDBSet(this ITypeSymbol typeSymbol) => typeSymbol?.Name == "DbSet" && typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceEF; - public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && - symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; + public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; public static bool IsDefinedInMongoLinqOrSystemLinq(this ISymbol symbol) { From 84ae7c60c86f243e9bb7a7c14f4c31fedc4082f8 Mon Sep 17 00:00:00 2001 From: Ravi Raghavan Date: Tue, 6 Aug 2024 18:22:37 -0400 Subject: [PATCH 8/8] Made Changes --- src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs index 49bafea9..a9f2db55 100644 --- a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs +++ b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs @@ -154,7 +154,8 @@ public static bool IsDBSet(this ITypeSymbol typeSymbol) => typeSymbol?.Name == "DbSet" && typeSymbol?.ContainingNamespace?.ToDisplayString() == NamespaceEF; - public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; + public static bool IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && + symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; public static bool IsDefinedInMongoLinqOrSystemLinq(this ISymbol symbol) {