You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/analyzers/LQRS002.md
-32Lines changed: 0 additions & 32 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -22,21 +22,6 @@ Detects `System.Linq` `Select` calls performed on `IQueryable<T>` whose selector
22
22
- Convert `Select(...)` → `SelectExpr(...)` preserving the anonymous projection.
23
23
- Convert `Select(...)` → `SelectExpr<TSource, TDto>(...)` with a generated DTO type when that is preferable.
24
24
25
-
### Automatic Ternary Null Check Simplification
26
-
As of version 0.5.0, the code fix also **automatically simplifies ternary null check patterns** (previously handled by LQRS004). When converting `Select` to `SelectExpr`, patterns like:
27
-
28
-
```csharp
29
-
prop=x.A!=null?new { x.A.B } :null
30
-
```
31
-
32
-
areautomaticallyconvertedto:
33
-
34
-
```csharp
35
-
prop=new { B=x.A?.B }
36
-
```
37
-
38
-
Thisprovidesmoreconcisecodeusingnull-conditionaloperators. See [LQRS004](LQRS004.md) fordetailsonthistransformation.
39
-
40
25
## Example
41
26
Before:
42
27
```csharp
@@ -67,20 +52,3 @@ var result = query.SelectExpr<Product, ResultDto_XXXXXXXX>(x => new
`SelectToSelectExprNamedCodeFixProvider` registers three distinct fixes (titles shown are from the provider):
22
22
23
-
-**Convert to SelectExpr<T, TDto>**
24
-
- Converts the `Select` method to a generic `SelectExpr<TSource, TDto>` and **converts the named object creation into an anonymous object**. This variant recursively converts nested object creations as well (deep conversion). It also runs the ternary-null simplifier on the generated anonymous structures, converting patterns like `x.A != null ? x.A.B : null` to `x.A?.B`.
23
+
-**Convert to SelectExpr<T, TDto> (convert all to anonymous)**
24
+
- Converts the `Select` method to a generic `SelectExpr<TSource, TDto>` and **converts the named object creation into an anonymous object**. This variant recursively converts nested object creations as well (deep conversion). It also runs the ternary-null simplifier on the generated anonymous structures.
25
25
26
-
-**Convert to SelectExpr<T, TDto> (strict)**
27
-
- Similar to the above, but **does NOT apply ternary null check simplification**. This preserves the original ternary patterns (e.g., `x.A != null ? x.A.B : null` remains unchanged). Useful when you want to maintain the exact nullability structure of the original code. You can apply [LQRS004](LQRS004.md) manually afterward if needed.
26
+
-**Convert to SelectExpr<T, TDto> (convert root only to anonymous)**
27
+
- Similar to the above, but converts only the reported (root) object creation to an anonymous object; nested object creations are left as-is. Produces a generic `SelectExpr<TSource, TDto>` and applies the ternary-null simplifier to the root anonymous creation.
28
28
29
29
-**Convert to SelectExpr (use predefined classes)**
30
-
- Replaces the method name `Select` with `SelectExpr` (no generic type arguments) and preserves the existing named DTO type in the selector. This variant **does NOT apply ternarynull check simplification**, preserving the original ternary patterns. You can apply [LQRS004](LQRS004.md) manually afterward if needed.
30
+
- Replaces the method name `Select` with `SelectExpr` (no generic type arguments) and preserves the existing named DTO type in the selector. This variant also simplifies ternary-null checks inside the selector lambda but does not convert named object creation into anonymous objects.
31
31
32
-
### Automatic Ternary Null Check Simplification
33
-
The first code fix option **automatically simplifies ternary null check patterns**. When converting `Select` to `SelectExpr`, patterns like:
Thesethreeoptionsmapdirectlytothecode-fiximplementations: `ConvertToSelectExprExplicitDtoAsync`, `ConvertToSelectExprExplicitDtoStructAsync`, and `ConvertToSelectExprPredefinedDtoAsync`.
32
+
These three options map directly to the code-fix implementations: `ConvertToSelectExprExplicitDtoAllAsync`, `ConvertToSelectExprExplicitDtoRootOnlyAsync`, and `ConvertToSelectExprPredefinedDtoAsync`.
48
33
49
34
## Examples
50
35
The examples below show the concrete transformations performed by each fix.
Details=newProductDetailDto { Desc=x.Detail.Desc} //nested remains named
109
93
})
110
94
.ToList();
111
95
```
112
96
113
97
Notes:
114
-
- The named `ProductDto` initializer is converted to an anonymous initializer.
115
-
- Ternary null check patterns are **preserved** - no simplification is applied.
116
-
- Use this option when you need to maintain the exact nullability structure.
98
+
- The named `ProductDto` initializer is converted to an anonymous initializer at the root only.
99
+
- A DTO name (e.g. `ResultDto_HASH`) is generated for the generic type argument; the code-fix inserts type arguments but replaces the selector body with an anonymous object.
117
100
118
101
3) Convert to `SelectExpr` (predefined)
119
102
@@ -124,7 +107,7 @@ var result = query.Select(x => new ProductDto
124
107
{
125
108
Id=x.Id,
126
109
Name=x.Name,
127
-
DetailDesc=x.Detail!=null?x.Detail.Desc:null
110
+
Details=newProductDetailDto { Desc=x.Detail.Desc}
128
111
});
129
112
```
130
113
@@ -134,11 +117,10 @@ var result = query.SelectExpr(x => new ProductDto // named DTO preserved
Copy file name to clipboardExpand all lines: docs/analyzers/LQRS004.md
+3-15Lines changed: 3 additions & 15 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -5,33 +5,21 @@
5
5
**Default:** Enabled
6
6
7
7
## Description
8
-
Detects conditional (ternary) expressions where one branch returns `null` (or a nullable null-cast) and the other branch returns an object creation (anonymous or named), and the condition contains a null-check expression. Such patterns are often simplifiable using null-conditional (`?.`) chains or other null-safe idioms.
8
+
Detects conditional (ternary) expressions where one branch returns `null` (or a nullable null-cast) and the other branch returns an object creation (anonymous or named), and the condition contains a null-check expression. Such patterns are often simplifyable using null-conditional (`?.`) chains or other null-safe idioms.
9
9
10
10
## When It Triggers
11
11
- The node is a conditional expression (`condition ? whenTrue : whenFalse`).
12
12
- The condition contains a null check (e.g. `x != null`, `x == null`) or a logical-and chain that includes a null check.
13
13
- One branch is `null` (or `(Type?)null`) and the other branch constructs an object (`new ...` or anonymous `new { ... }`).
14
-
- The conditional is inside a `SelectExpr` call.
15
14
16
15
## When It Doesn't Trigger
17
16
- Neither branch is an object creation.
18
17
- The condition doesn't include a null-check.
19
-
- The conditional is outside a `SelectExpr` call.
20
18
21
19
## Suggested Transformation
22
20
The analyzer reports an informational diagnostic and suggests a null-propagation style replacement when the pattern is a simple null check that guards an object creation. A common safe transformation is to move the nullable operator into the member access so that inner member accesses use the null-conditional operator.
23
21
24
-
### Relationship with LQRS002/LQRS003
25
-
26
-
Some `Select` → `SelectExpr` conversion options automatically apply this transformation:
-**LQRS003 "Explicit (strict)"**: Does NOT apply ternary simplification
32
-
-**LQRS003 "Predefined"**: Does NOT apply ternary simplification
33
-
34
-
For the conversions that preserve ternary patterns, you can use LQRS004 to manually apply the transformation afterward if desired.
22
+
Because this transformation changes the position of nullability (e.g. from the selector producing `null` to the individual property access becoming nullable), it is handled separately from the `Select` → `SelectExpr` conversions (`LQRS002`/`LQRS003`). The `SelectExpr` conversions intentionally do not perform this rewrite automatically because moving the nullable operator may change semantics in subtle ways.
35
23
36
24
### Replacement example
37
25
Given a conditional like:
@@ -43,4 +31,4 @@ the analyzer suggests replacing the object-creating ternary with a projection th
43
31
new { B=x.A?.B }
44
32
```
45
33
46
-
This effectively collapses the ternary and moves the `?.` into the member access, producing an object whose properties are null when the source is null instead of returning `null` for the whole object. Because the resulting nullability shape differs from the original expression, this transformation is offered as an informational suggestion.
34
+
This effectively collapses the ternary and moves the `?.` into the member access, producing an object whose properties are null when the source is null instead of returning `null` for the whole object. Because the resulting nullability shape differs from the original expression, the analyzer treats this as a separate informational suggestion rather than part of `Select`→`SelectExpr` automated conversions.
0 commit comments