diff --git a/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs b/src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs index 12bd51c1..37bc9814 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 (!namedType.TypeArguments.All(t => typesProcessor.ProcessTypeSymbol(t) != null)) { - typesProcessor.ProcessTypeSymbol(typeArgument); + continue; } var rewriteContext = RewriteContext.Builders(expressionNode, nodesToRewrite, semanticModel, typesProcessor); diff --git a/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs b/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs index 0681c348..c1f2c4a7 100644 --- a/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs +++ b/src/MongoDB.Analyzer/Core/Linq/LinqExpressionProcessor.cs @@ -99,6 +99,10 @@ mongoQueryableTypeInfo.Type is not INamedTypeSymbol mongoQueryableNamedType || (analysisType == AnalysisType.EF && PreanalyzeEFExpression(node, semanticModel, invalidExpressionNodes, mongoQueryableNamedType))) { 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..a9017c72 100644 --- a/src/MongoDB.Analyzer/Core/TypesProcessor.cs +++ b/src/MongoDB.Analyzer/Core/TypesProcessor.cs @@ -65,15 +65,28 @@ public string ProcessTypeSymbol(ITypeSymbol typeSymbol) } remappedName = GetNewNameForSymbol(typeSymbol); + BaseTypeDeclarationSyntax rewrittenDeclarationSyntax; + _processedTypes[fullTypeName] = (remappedName, null); // Cache the name, for self referencing types - var rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); + try + { + rewrittenDeclarationSyntax = GetSyntaxNodeFromSymbol(typeSymbol, remappedName); + } + catch + { + _processedTypes.Remove(fullTypeName); + throw; + } - var typeCode = rewrittenDeclarationSyntax.ToFullString(); - var newTypeDeclaration = SyntaxFactory.ParseMemberDeclaration(typeCode); + if (rewrittenDeclarationSyntax == null) + { + _processedTypes.Remove(fullTypeName); + return null; + } remappedName = rewrittenDeclarationSyntax.Identifier.Text; - _processedTypes[fullTypeName] = (remappedName, newTypeDeclaration); + _processedTypes[fullTypeName] = (remappedName, rewrittenDeclarationSyntax); return remappedName; } @@ -93,17 +106,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.IsSupportedCollection()) + namedTypeSymbol.TypeArguments.Length >= 1 && + typeSymbol.IsSystemCollection()) { - 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 +136,18 @@ private TypeSyntax CreateTypeSyntaxForSymbol(ITypeSymbol typeSymbol) SyntaxFactory.IdentifierName("System"), SyntaxFactory.IdentifierName("Collections")), SyntaxFactory.IdentifierName("Generic")), - listSyntax); + collectionSyntax); } else { var (isNullable, underlingTypeSymbol) = typeSymbol.DiscardNullable(); - var newTypeName = ProcessTypeSymbol(underlingTypeSymbol); + + if (newTypeName == null) + { + return null; + } + result = isNullable ? SyntaxFactoryUtilities.GetNullableType(newTypeName) : SyntaxFactory.ParseTypeName(newTypeName); } @@ -171,19 +201,23 @@ private ExpressionSyntax GenerateExpressionFromBsonAttributeArgumentInfo(TypedCo _ => null }; - private void GenerateFields(ITypeSymbol typeSymbol, List members) + private bool GenerateFields(ITypeSymbol typeSymbol, List members) { var typeFields = typeSymbol .GetMembers() .OfType() .Where(p => !p.IsStatic && - (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSupportedCollection()) && + (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSystemCollection()) && p.DeclaredAccessibility == Accessibility.Public); foreach (var fieldSymbol in typeFields) { var typeSyntax = CreateTypeSyntaxForSymbol(fieldSymbol.Type); + if (typeSyntax == null) + { + return false; + } var variableDeclaration = SyntaxFactory.VariableDeclaration(typeSyntax, SyntaxFactory.SingletonSeparatedList(SyntaxFactory.VariableDeclarator(fieldSymbol.Name))); @@ -199,9 +233,11 @@ private void GenerateFields(ITypeSymbol typeSymbol, List members) + private bool GenerateProperties(ITypeSymbol typeSymbol, List members) { var typeProperties = typeSymbol .GetMembers() @@ -209,12 +245,16 @@ private void GenerateProperties(ITypeSymbol typeSymbol, List !p.IsStatic && !p.IsIndexer && - (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSupportedCollection()) && + (p.Type.TypeKind != TypeKind.Interface || p.Type.IsSystemCollection()) && p.DeclaredAccessibility == Accessibility.Public); foreach (var propertySymbol in typeProperties) { var typeSyntax = CreateTypeSyntaxForSymbol(propertySymbol.Type); + if (typeSyntax == null) + { + return false; + } var propertyDeclaration = SyntaxFactory.PropertyDeclaration(typeSyntax, propertySymbol.Name); @@ -229,6 +269,8 @@ private void GenerateProperties(ITypeSymbol typeSymbol, List return (fullTypeName, fullTypeName); } + if (!userOnlyTypes && typeSymbol.IsSystemCollection(includeBaseTypesAndInterfaces: true)) + { + // Types derived from System.Collections.Generic are not supported + return default; + } + return (null, fullTypeName); } @@ -285,8 +333,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 +349,11 @@ private TypeDeclarationSyntax GetSyntaxForClassOrStruct(ITypeSymbol typeSymbol, { var baseTypeNameGenerated = ProcessTypeSymbol(typeSymbol.BaseType); + if (baseTypeNameGenerated == null) + { + return null; + } + typeDeclaration = typeDeclaration.WithBaseList(GetBaseListSyntax(baseTypeNameGenerated)); } @@ -351,6 +407,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..a9f2db55 100644 --- a/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs +++ b/src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs @@ -17,6 +17,7 @@ namespace MongoDB.Analyzer.Core; internal static class SymbolExtensions { 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 +55,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 +154,8 @@ 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 IsDefinedInMongoDriver(this ISymbol symbol) => symbol?.ContainingNamespace?.ToDisplayString() == NamespaceMongoDBDriver && + symbol?.ContainingAssembly.Name == AssemblyMongoDBDriver; public static bool IsDefinedInMongoLinqOrSystemLinq(this ISymbol symbol) { @@ -222,44 +217,50 @@ TypeKind.Enum or _ => false }; - public static bool IsSupportedCollection(this ITypeSymbol typeSymbol) + 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 IsSystemCollection(this ITypeSymbol typeSymbol, bool includeBaseTypesAndInterfaces = false) { if (typeSymbol is not INamedTypeSymbol namedTypeSymbol) { - return false; + return default; + } + + if (!includeBaseTypesAndInterfaces) + { + return namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric; + } + + if (namedTypeSymbol.AllInterfaces.Any(interfaceSymbol => interfaceSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric)) + { + return true; } while (namedTypeSymbol != null) { - if (s_supportedCollections.Contains(namedTypeSymbol.ConstructedFrom?.ToDisplayString())) + if (namedTypeSymbol.ContainingNamespace?.ToDisplayString() == NamespaceCollectionGeneric) { return true; } - if (namedTypeSymbol.Interfaces.Any(i => s_supportedCollections.Contains(i.ConstructedFrom?.ToDisplayString()))){ - return true; - } - namedTypeSymbol = namedTypeSymbol.BaseType; } 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..ff071b4e 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,16 +42,78 @@ 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() - .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); + } + + [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 } }])")] @@ -52,25 +126,96 @@ 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() { _ = 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\" : { \"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; } + } }