Skip to content

Commit 1683e3a

Browse files
Ravi-RaghavanBorisDog
authored andcommitted
VS-57: Support anonymous objects in Builders, rebase and tests enhancement
1 parent 7706333 commit 1683e3a

File tree

8 files changed

+223
-79
lines changed

8 files changed

+223
-79
lines changed

src/MongoDB.Analyzer/Core/Builders/BuilderExpressionProcessor.cs

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -577,12 +577,9 @@ private static RewriteResult SubstituteExpressionWithConst(
577577
SymbolInfo symbolInfo,
578578
TypeInfo typeInfo)
579579
{
580-
if (IsChildOfLambdaParameterOrBuilders(rewriteContext, simpleNameSyntax, symbolInfo))
581-
{
582-
return RewriteResult.Ignore;
583-
}
584-
585-
if (typeInfo.Type == null)
580+
if (IsChildOfLambdaParameterOrBuilders(rewriteContext, simpleNameSyntax, symbolInfo) ||
581+
simpleNameSyntax.IsMemberOfAnonymousObject() ||
582+
typeInfo.Type == null)
586583
{
587584
return RewriteResult.Ignore;
588585
}

src/MongoDB.Analyzer/Core/Utilities/SymbolExtensions.cs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -106,12 +106,6 @@ public static bool IsLinqEnumerable(this ITypeSymbol typeSymbol) =>
106106
public static bool IsMongoQueryable(this ITypeSymbol typeSymbol) =>
107107
typeSymbol?.Name == "MongoQueryable";
108108

109-
public static bool IsSupportedBuilderType(this ITypeSymbol typeSymbol) =>
110-
(typeSymbol.TypeKind == TypeKind.Class ||
111-
typeSymbol.TypeKind == TypeKind.Struct ||
112-
typeSymbol.TypeKind == TypeKind.Enum) &&
113-
!typeSymbol.IsAnonymousType;
114-
115109
public static bool IsSupportedCollection(this ITypeSymbol typeSymbol) =>
116110
typeSymbol is INamedTypeSymbol namedTypeSymbol &&
117111
s_supportedCollections.Contains(namedTypeSymbol.ConstructedFrom?.ToDisplayString());
@@ -123,6 +117,15 @@ public static bool IsSupportedMongoCollectionType(this ITypeSymbol typeSymbol) =
123117
public static bool IsString(this ITypeSymbol typeSymbol) =>
124118
typeSymbol?.SpecialType == SpecialType.System_String;
125119

120+
public static bool IsSupportedBuilderType(this ITypeSymbol typeSymbol) =>
121+
typeSymbol?.TypeKind switch
122+
{
123+
TypeKind.Class or
124+
TypeKind.Enum or
125+
TypeKind.Struct => true,
126+
_ => false
127+
};
128+
126129
public static bool IsSupportedIMongoCollection(this ITypeSymbol typeSymbol) =>
127130
typeSymbol.IsIMongoCollection() &&
128131
typeSymbol is INamedTypeSymbol namedType &&
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
// Copyright 2021-present MongoDB Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License")
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
using System;
16+
using MongoDB.Analyzer.Tests.Common.DataModel;
17+
using MongoDB.Driver;
18+
19+
namespace MongoDB.Analyzer.Tests.Common.TestCases.Builders
20+
{
21+
public sealed class BuildersAnonymousObjects : TestCasesBase
22+
{
23+
[BuildersMQL("{ \"Age\" : new { Age = value << 2, IntField = new { Value = 10 }.Value, Address = \"White House\" }.Age }")]
24+
[BuildersMQL("{ \"Age\" : new { Age = value << 2, IntField = Math.Round(2.35, new { DecimalPoints = 2 }.DecimalPoints), Address = \"White House\".ToLower() }.Age }")]
25+
[BuildersMQL("{ \"Age\" : new { Age = new { ShiftedValue = value << 2 }.ShiftedValue, IntField = Math.Round(2.35, new { DecimalPoints = 2 }.DecimalPoints), Address = address.City.ToUpper() }.Age }")]
26+
public void Complex_anonymous_object()
27+
{
28+
int value = 10;
29+
_ = Builders<User>.Filter.Eq(u => u.Age, new { Age = value << 2, IntField = new { Value = 10 }.Value, Address = "White House" }.Age);
30+
_ = Builders<User>.Filter.Eq(u => u.Age, new { Age = value << 2, IntField = Math.Round(2.35, new { DecimalPoints = 2 }.DecimalPoints), Address = "White House".ToLower() }.Age);
31+
32+
var address = new Address();
33+
_ = Builders<User>.Filter.Eq(u => u.Age, new { Age = new { ShiftedValue = value << 2 }.ShiftedValue, IntField = Math.Round(2.35, new { DecimalPoints = 2 }.DecimalPoints), Address = address.City.ToUpper() }.Age);
34+
}
35+
36+
[BuildersMQL("{ \"Age\" : new { Age = value << 2, IntField = 22 }.Age }")]
37+
[BuildersMQL("{ \"Scores\" : { \"$gt\" : new { Item = value, IntField = 22 }.Item } }")]
38+
[BuildersMQL("{ \"PesonsList\" : { \"$elemMatch\" : { \"SiblingsCount\" : new { SiblingsCount = 2 }.SiblingsCount } } }")]
39+
[BuildersMQL("{ \"SiblingsCount\" : { \"$lte\" : anonymousObject.Item } }")]
40+
[BuildersMQL("{ \"Root.Data\" : { \"$lt\" : new { RootValue = 22 }.RootValue } }")]
41+
[BuildersMQL("{ new { Field = \"IntList\" }.Field : { \"$gt\" : new { Value = 22 }.Value } }")]
42+
[BuildersMQL("{ new { Field = \"Age\" }.Field : { \"$mod\" : [NumberLong(new { Value = 21 }.Value), NumberLong(new { Value = 23 }.Value)] } }")]
43+
[BuildersMQL("{ new { Field = \"Age\" }.Field : { \"$ne\" : new { Age = (value << 2) * (value / 2) }.Age } }")]
44+
[BuildersMQL("{ new { Field = \"IntArray\" }.Field : { \"$in\" : [11, 22, 33] } }")]
45+
[BuildersMQL("{ \"$or\" : [{ new { Field = \"IntArray\" }.Field : { \"$gt\" : new { Value = 123 }.Value } }, { new { Field = \"ObjectArray\" }.Field : { \"$ne\" : null } }] }")]
46+
public void Filter()
47+
{
48+
int value = 10;
49+
_ = Builders<User>.Filter.Eq(u => u.Age, new { Age = value << 2, IntField = 22 }.Age);
50+
_ = Builders<User>.Filter.AnyGt("Scores", new { Item = value, IntField = 22 }.Item);
51+
_ = Builders<ListsHolder>.Filter.ElemMatch(u => u.PesonsList, p => p.SiblingsCount == new { SiblingsCount = 2 }.SiblingsCount);
52+
53+
var anonymousObject = new { Item = 10, BooleanValue = false, StringValue = "String" };
54+
_ = Builders<Person>.Filter.Lte(u => u.SiblingsCount, anonymousObject.Item);
55+
_ = Builders<Tree>.Filter.Lt(t => t.Root.Data, new { RootValue = 22 }.RootValue);
56+
_ = Builders<ListsHolder>.Filter.AnyGt(new { Field = "IntList" }.Field, new { Value = 22 }.Value);
57+
58+
_ = Builders<User>.Filter.Mod(new { Field = "Age" }.Field, new { Value = 21 }.Value, new { Value = 23 }.Value);
59+
_ = Builders<User>.Filter.Ne(new { Field = "Age" }.Field, new { Age = (value << 2) * (value / 2) }.Age);
60+
_ = Builders<SimpleTypesArraysHolder>.Filter.In(new { Field = "IntArray" }.Field, new[] { 11, 22, 33 });
61+
62+
_ = Builders<SimpleTypesArraysHolder>.Filter.AnyGt(new { Field = "IntArray" }.Field, new { Value = 123 }.Value) |
63+
Builders<SimpleTypesArraysHolder>.Filter.AnyNe<object>(new { Field = "ObjectArray" }.Field, null);
64+
}
65+
66+
[BuildersMQL("find({ \"Age\" : { \"$lt\" : 10 } }, { \"_id\" : 0 })")]
67+
[BuildersMQL("find({ \"Age\" : { \"$lt\" : 10 } }, { \"Address\" : 1, \"_id\" : 0 })")]
68+
[BuildersMQL("find({ }, { \"Address\" : 1, \"Age\" : 1, \"Scores\" : 1, \"_id\" : 0 })")]
69+
[BuildersMQL("find({ \"$text\" : { \"$search\" : \"testSearch\" } }, { \"Address\" : 1, \"Age\" : 1, \"Height\" : 1, \"Scores.0\" : 1, \"Scores.10\" : 1, \"_id\" : 0 })")]
70+
public void FluentApi_project()
71+
{
72+
GetMongoCollection()
73+
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
74+
.Project(u => new { });
75+
76+
GetMongoCollection()
77+
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
78+
.Project(u => new { Address = u.Address });
79+
80+
GetMongoCollection()
81+
.Find(Builders<User>.Filter.Empty)
82+
.Project(u => new { A = u.Address, B = u.Scores, C = u.Age });
83+
84+
GetMongoCollection()
85+
.Find(Builders<User>.Filter.Text("testSearch"))
86+
.Project(u => new { A = u.Address, B = u.Scores[0], C = u.Age * 10, D = u.Height * u.Age * u.Scores[10] });
87+
}
88+
89+
[BuildersMQL("{ new { Field = \"Name\" }.Field : 1 }")]
90+
[BuildersMQL("{ new { Field = \"TicksSinceBirth\" }.Field : \"2d\", new { Field = \"Name\" }.Field : \"2dsphere\" }")]
91+
[BuildersMQL("{ new { Field = \"Age\" }.Field : \"text\", new { Field = \"LastName\" }.Field : \"text\" }")]
92+
[BuildersMQL("{ new { Field = \"LicenseNumber\" }.Field : \"geoHaystack\" }")]
93+
[BuildersMQL("{ new { Field = \"Name\", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field : \"2d\", new { Field = \"LastName\", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field : \"2d\", new { Field = \"Address\", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field : \"text\", new { Field = \"Vehicle\", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field : \"text\" }")]
94+
public void IndexKey()
95+
{
96+
_ = Builders<Person>.IndexKeys.Ascending(new { Field = "Name" }.Field);
97+
_ = Builders<Person>.IndexKeys.Geo2D(new { Field = "TicksSinceBirth" }.Field).Geo2DSphere(new { Field = "Name" }.Field);
98+
_ = Builders<User>.IndexKeys.Text(new { Field = "Age" }.Field).Text(new { Field = "LastName" }.Field);
99+
_ = Builders<Vehicle>.IndexKeys.GeoHaystack(new { Field = "LicenseNumber" }.Field);
100+
_ = Builders<Person>.IndexKeys.Combine(
101+
Builders<Person>.IndexKeys.Geo2D(new { Field = "Name", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field)
102+
.Geo2D(new { Field = "LastName", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field),
103+
Builders<Person>.IndexKeys.Text(new { Field = "Address", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field)
104+
.Text(new { Field = "Vehicle", IntValue = 10, BoolValue = false, DoubleValue = 2.5 }.Field));
105+
}
106+
107+
[BuildersMQL("{ \"Address\" : 1, \"Age\" : 1, \"_id\" : 0 }")]
108+
[BuildersMQL("{ \"SiblingsCount\" : 1, \"TicksSinceBirth\" : 1, \"_id\" : 0 }")]
109+
[BuildersMQL("{ \"Age\" : 1, \"Height\" : 1, \"_id\" : 0 }")]
110+
[BuildersMQL("{ new { FieldToExclude = \"Age\" }.FieldToExclude : 0 }")]
111+
[BuildersMQL("{ new { FieldToInclude = \"Scores\" }.FieldToInclude : 1 }")]
112+
[BuildersMQL("{ new { Field = \"IntArray\" }.Field : { \"$slice\" : [10, 5] } }")]
113+
[BuildersMQL("{ new { Field = \"Age\" }.Field : 1, new { Field = \"LastName\" }.Field : 1 }")]
114+
public void Projection()
115+
{
116+
_ = Builders<User>.Projection.Expression(u => new { Age = u.Age, Address = u.Address });
117+
_ = Builders<Person>.Projection.Expression(u => new { Average = (u.SiblingsCount + u.TicksSinceBirth) / 2, Total = u.SiblingsCount + u.TicksSinceBirth });
118+
_ = Builders<User>.Projection.Expression(u => new { Avg = (u.Age * 2 + u.Height) / 2 });
119+
_ = Builders<User>.Projection.Exclude(new { FieldToExclude = "Age" }.FieldToExclude);
120+
_ = Builders<User>.Projection.Include(new { FieldToInclude = "Scores" }.FieldToInclude);
121+
_ = Builders<SimpleTypesArraysHolder>.Projection.Slice(new { Field = "IntArray" }.Field, 10, 5);
122+
_ = Builders<User>.Projection.Combine(
123+
Builders<User>.Projection.Include(new { Field = "Age" }.Field),
124+
Builders<User>.Projection.Include(new { Field = "LastName" }.Field));
125+
}
126+
127+
[BuildersMQL("{ new { Address = \"Address\" }.Address : 1, new { Name = \"Name\" }.Name : -1 }")]
128+
[BuildersMQL("{ new { Address = \"Address\" }.Address : 1, new { Name = \"Name\" }.Name : -1 }")]
129+
public void Sort()
130+
{
131+
_ = Builders<User>.Sort
132+
.Ascending(new { Address = "Address" }.Address)
133+
.Descending(new { Name = "Name" }.Name);
134+
135+
_ = Builders<User>.Sort.Combine(
136+
Builders<User>.Sort.Ascending(new { Address = "Address" }.Address),
137+
Builders<User>.Sort.Descending(new { Name = "Name" }.Name));
138+
}
139+
140+
[BuildersMQL("{ \"$set\" : { new { Field = \"Age\", IntField = 22 }.Field : new { Value = 22 }.Value } }")]
141+
[BuildersMQL("{ \"$setOnInsert\" : { new { Field = \"Address\", IntField = 22 }.Field : new { Value = \"White House\" }.Value } }")]
142+
[BuildersMQL("{ \"$rename\" : { new { FieldToRename = \"Name\", IntField = 22 }.FieldToRename : new { newFieldName = \"FirstName\" }.newFieldName } }")]
143+
[BuildersMQL("{ \"$addToSet\" : { new { FieldUnset = \"IntList\", IntField = 22 }.FieldUnset : new { ValueToAdd = 11 }.ValueToAdd } }")]
144+
[BuildersMQL("{ \"$bit\" : { new { Field = \"Age\", IntField = 22 }.Field : { \"and\" : new { AgeValue = 22 }.AgeValue } } }")]
145+
[BuildersMQL("{ \"$inc\" : { new { Field = \"Height\", IntField = 22 }.Field : new { Value = 11 }.Value } }")]
146+
[BuildersMQL("{ \"$max\" : { new { Field = \"SiblingsCount\", IntField = 22 }.Field : new { Value = 22 }.Value } }")]
147+
[BuildersMQL("{ \"$mul\" : { new { Field = \"Age\", IntField = 22 }.Field : new { Value = 23 }.Value } }")]
148+
[BuildersMQL("{ \"$push\" : { new { Field = \"IntList\", IntField = 22 }.Field : new { Value = 22 }.Value } }")]
149+
[BuildersMQL("{ \"$pull\" : { new { Field = \"IntList\", IntField = 22 }.Field : new { Value = 22 }.Value } }")]
150+
public void Updates()
151+
{
152+
_ = Builders<User>.Update.Set(new { Field = "Age", IntField = 22 }.Field, new { Value = 22 }.Value);
153+
_ = Builders<User>.Update.SetOnInsert(new { Field = "Address", IntField = 22 }.Field, new { Value = "White House" }.Value);
154+
_ = Builders<User>.Update.Rename(new { FieldToRename = "Name", IntField = 22 }.FieldToRename, new { newFieldName = "FirstName" }.newFieldName);
155+
_ = Builders<ListsHolder>.Update.AddToSet(new { FieldUnset = "IntList", IntField = 22 }.FieldUnset, new { ValueToAdd = 11 }.ValueToAdd);
156+
_ = Builders<User>.Update.BitwiseAnd(new { Field = "Age", IntField = 22 }.Field, new { AgeValue = 22 }.AgeValue);
157+
_ = Builders<User>.Update.Inc(new { Field = "Height", IntField = 22 }.Field, new { Value = 11 }.Value);
158+
_ = Builders<Person>.Update.Max(new { Field = "SiblingsCount", IntField = 22 }.Field, new { Value = 22 }.Value);
159+
_ = Builders<User>.Update.Mul(new { Field = "Age", IntField = 22 }.Field, new { Value = 23 }.Value);
160+
_ = Builders<ListsHolder>.Update.Push(new { Field = "IntList", IntField = 22 }.Field, new { Value = 22 }.Value);
161+
_ = Builders<ListsHolder>.Update.Pull(new { Field = "IntList", IntField = 22 }.Field, new { Value = 22 }.Value);
162+
}
163+
}
164+
}
165+

tests/MongoDB.Analyzer.Tests.Common.TestCases/Builders/BuildersFluentApi.cs

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -127,21 +127,25 @@ public void Filter_sort_project()
127127
[NoDiagnostics]
128128
public void Filter_sort_project_to_new_object_should_be_ignored()
129129
{
130+
GetMongoCollection()
131+
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
132+
.Project(u => new User() { });
133+
130134
GetMongoCollection()
131135
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
132136
.Project(u => new User() { Address = u.Address });
133137

134138
GetMongoCollection()
135139
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
136-
.Project(u => new User() { });
140+
.Project(u => new Person() { LastName = u.LastName });
137141

138142
GetMongoCollection()
139143
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
140-
.Project(u => new { Address = u.Address });
144+
.Project(u => new Person() { LastName = u.LastName });
141145

142146
GetMongoCollection()
143147
.Find(Builders<User>.Filter.Lt(u => u.Age, 10))
144-
.Project(u => new { });
148+
.Project(u => new MultipleTypeGeneric<Person, User, int, string>() { Data = u.Age });
145149
}
146150

147151
[BuildersMQL("find({ \"Age\" : 1 }).sort({ \"Address\" : 1, \"Name\" : 1 })")]

0 commit comments

Comments
 (0)