Skip to content

Commit f983f0e

Browse files
authored
Merge pull request #6358 from Youssef1313/using-system
Don't import System when it's already imported by global using
2 parents f10e7f2 + fde7601 commit f983f0e

File tree

9 files changed

+48
-113
lines changed

9 files changed

+48
-113
lines changed

src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpPreferStreamAsyncMemoryOverloads.Fixer.cs

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
22

3-
using System.Collections.Generic;
3+
using System.Composition;
44
using System.Linq;
55
using Microsoft.CodeAnalysis;
66
using Microsoft.CodeAnalysis.CodeFixes;
@@ -11,7 +11,7 @@
1111

1212
namespace Microsoft.NetCore.CSharp.Analyzers.Runtime
1313
{
14-
[ExportCodeFixProvider(LanguageNames.CSharp)]
14+
[ExportCodeFixProvider(LanguageNames.CSharp), Shared]
1515
public sealed class CSharpPreferStreamAsyncMemoryOverloadsFixer : PreferStreamAsyncMemoryOverloadsFixer
1616
{
1717
protected override SyntaxNode? GetArgumentByPositionOrName(IInvocationOperation invocation, int index, string name, out bool isNamed)
@@ -54,19 +54,6 @@ public sealed class CSharpPreferStreamAsyncMemoryOverloadsFixer : PreferStreamAs
5454
return null;
5555
}
5656

57-
protected override bool IsSystemNamespaceImported(IReadOnlyList<SyntaxNode> importList)
58-
{
59-
foreach (SyntaxNode import in importList)
60-
{
61-
if (import is UsingDirectiveSyntax { Name: IdentifierNameSyntax { Identifier.Text: nameof(System) } })
62-
{
63-
return true;
64-
}
65-
}
66-
67-
return false;
68-
}
69-
7057
protected override bool IsPassingZeroAndBufferLength(SemanticModel model, SyntaxNode bufferValueNode, SyntaxNode offsetValueNode, SyntaxNode countValueNode)
7158
{
7259
// First argument should be an identifier name node

src/NetAnalyzers/CSharp/Microsoft.NetCore.Analyzers/Runtime/CSharpUseSpanBasedStringConcat.Fixer.cs

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
22

3-
using System.Collections.Generic;
43
using Microsoft.CodeAnalysis;
54
using Microsoft.CodeAnalysis.CodeFixes;
65
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -21,17 +20,6 @@ private protected override SyntaxNode ReplaceInvocationMethodName(SyntaxGenerato
2120
return invocationSyntax.ReplaceNode(oldNameSyntax, newNameSyntax);
2221
}
2322

24-
private protected override bool IsSystemNamespaceImported(Project project, IReadOnlyList<SyntaxNode> namespaceImports)
25-
{
26-
foreach (var import in namespaceImports)
27-
{
28-
if (import is UsingDirectiveSyntax { Name: IdentifierNameSyntax { Identifier.ValueText: nameof(System) } })
29-
return true;
30-
}
31-
32-
return false;
33-
}
34-
3523
private protected override IOperation WalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand)
3624
{
3725
return UseSpanBasedStringConcat.CSharpWalkDownBuiltInImplicitConversionOnConcatOperand(operand);

src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/PreferStreamAsyncMemoryOverloads.Fixer.cs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
22

3-
using System.Collections.Generic;
43
using System.Collections.Immutable;
54
using System.Threading;
65
using System.Threading.Tasks;
6+
using Analyzer.Utilities;
77
using Microsoft.CodeAnalysis;
88
using Microsoft.CodeAnalysis.CodeActions;
99
using Microsoft.CodeAnalysis.CodeFixes;
@@ -30,12 +30,11 @@ namespace Microsoft.NetCore.Analyzers.Runtime
3030
/// </summary>
3131
public abstract class PreferStreamAsyncMemoryOverloadsFixer : CodeFixProvider
3232
{
33+
private static readonly SyntaxAnnotation s_asMemorySymbolAnnotation = new("SymbolId", "System.MemoryExtensions");
34+
3335
// Checks if the argument in the specified index has a name. If it doesn't, returns that arguments. If it does, then looks for the argument using the specified name, and returns it, or null if not found.
3436
protected abstract SyntaxNode? GetArgumentByPositionOrName(IInvocationOperation invocation, int index, string name, out bool isNamed);
3537

36-
// Verifies if a namespace has already been added to the usings/imports list.
37-
protected abstract bool IsSystemNamespaceImported(IReadOnlyList<SyntaxNode> importList);
38-
3938
// Verifies if the user passed `0` as the 1st argument (`offset`) and `buffer.Length` as the 2nd argument (`count`),
4039
// where `buffer` is the name of the variable passed as the 0th argument.
4140
protected abstract bool IsPassingZeroAndBufferLength(SemanticModel model, SyntaxNode bufferValueNode, SyntaxNode offsetValueNode, SyntaxNode countValueNode);
@@ -152,7 +151,7 @@ private Task<Document> FixInvocationAsync(SemanticModel model, Document doc, Syn
152151
SyntaxNode asMemoryInvocationNode = generator.InvocationExpression(
153152
asMemoryExpressionNode,
154153
namedStartNode.WithTriviaFrom(offsetNode),
155-
namedLengthNode.WithTriviaFrom(countNode));
154+
namedLengthNode.WithTriviaFrom(countNode)).WithAddImportsAnnotation().WithAdditionalAnnotations(s_asMemorySymbolAnnotation);
156155

157156
// Generate the new buffer argument, ensuring we include the buffer argument name if the user originally indicated one
158157
replacedInvocationNode = GetNamedArgument(generator, asMemoryInvocationNode, isBufferNamed, "buffer")
@@ -175,14 +174,9 @@ private Task<Document> FixInvocationAsync(SemanticModel model, Document doc, Syn
175174
}
176175

177176
SyntaxNode newInvocationExpression = generator.InvocationExpression(asyncMethodNode, nodeArguments).WithTriviaFrom(streamInstanceNode);
178-
179-
bool containsSystemImport = IsSystemNamespaceImported(generator.GetNamespaceImports(root));
180-
181-
// The invocation needs to be replaced before adding the import/using, it won't work the other way around
182177
SyntaxNode newRoot = generator.ReplaceNode(root, invocation.Syntax, newInvocationExpression.WithTriviaFrom(invocation.Syntax));
183-
SyntaxNode newRootWithImports = containsSystemImport ? newRoot : generator.AddNamespaceImports(newRoot, generator.NamespaceImportDeclaration(nameof(System)));
184178

185-
return Task.FromResult(doc.WithSyntaxRoot(newRootWithImports));
179+
return Task.FromResult(doc.WithSyntaxRoot(newRoot));
186180
}
187181
}
188182
}

src/NetAnalyzers/Core/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcat.Fixer.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
22

33
using System;
4-
using System.Collections.Generic;
54
using System.Collections.Immutable;
65
using System.Diagnostics.CodeAnalysis;
76
using System.Linq;
@@ -23,11 +22,10 @@ public abstract class UseSpanBasedStringConcatFixer : CodeFixProvider
2322
private protected const string AsSpanName = nameof(MemoryExtensions.AsSpan);
2423
private protected const string AsSpanStartParameterName = "start";
2524
private protected const string ToStringName = nameof(ToString);
25+
private static readonly SyntaxAnnotation s_asSpanSymbolAnnotation = new("SymbolId", "System.MemoryExtensions");
2626

2727
private protected abstract SyntaxNode ReplaceInvocationMethodName(SyntaxGenerator generator, SyntaxNode invocationSyntax, string newName);
2828

29-
private protected abstract bool IsSystemNamespaceImported(Project project, IReadOnlyList<SyntaxNode> namespaceImports);
30-
3129
private protected abstract IOperation WalkDownBuiltInImplicitConversionOnConcatOperand(IOperation operand);
3230

3331
private protected abstract bool IsNamedArgument(IArgumentOperation argumentOperation);
@@ -110,13 +108,6 @@ async Task<Document> FixConcatOperationChain(CancellationToken cancellationToken
110108

111109
SyntaxNode newRoot = generator.ReplaceNode(root, concatExpressionSyntax, concatMethodInvocationSyntax);
112110

113-
// Import 'System' namespace if it's absent.
114-
if (!IsSystemNamespaceImported(context.Document.Project, generator.GetNamespaceImports(newRoot)))
115-
{
116-
SyntaxNode systemNamespaceImport = generator.NamespaceImportDeclaration(nameof(System));
117-
newRoot = generator.AddNamespaceImports(newRoot, systemNamespaceImport);
118-
}
119-
120111
editor.ReplaceNode(root, newRoot);
121112
return editor.GetChangedDocument();
122113
}
@@ -140,7 +131,7 @@ private SyntaxNode ConvertOperandToArgument(in RequiredSymbols symbols, SyntaxGe
140131
invocationSyntax = generator.ReplaceNode(invocationSyntax, namedStartIndexArgument.Syntax, renamedArgumentSyntax);
141132
}
142133

143-
var asSpanInvocationSyntax = ReplaceInvocationMethodName(generator, invocationSyntax, AsSpanName);
134+
var asSpanInvocationSyntax = ReplaceInvocationMethodName(generator, invocationSyntax, AsSpanName).WithAddImportsAnnotation().WithAdditionalAnnotations(s_asSpanSymbolAnnotation);
144135
return generator.Argument(asSpanInvocationSyntax);
145136
}
146137
// Character literals become string literals.

src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/PreferStreamReadAsyncMemoryOverloadsTests.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Threading.Tasks;
55
using Microsoft.CodeAnalysis.Testing;
6+
using Test.Utilities;
67
using Xunit;
78

89
#pragma warning disable CA1305 // Specify IFormatProvider in string.Format
@@ -576,7 +577,7 @@ public static IEnumerable<object[]> CSharpInlineByteArrayTestData()
576577
"(new byte[s.Length]).AsMemory(0, (int)s.Length), new CancellationToken()" };
577578
}
578579

579-
[Fact]
580+
[WindowsOnlyFact] // https://github.com/dotnet/roslyn/issues/65081
580581
public Task CS_Fixer_Diagnostic_EnsureSystemNamespaceAutoAddedAsync()
581582
{
582583
string originalCode = @"
@@ -594,10 +595,9 @@ public async void M()
594595
}
595596
}";
596597
string fixedCode = @"
598+
using System;
597599
using System.IO;
598600
using System.Threading;
599-
using System;
600-
601601
class C
602602
{
603603
public async void M()
@@ -890,7 +890,7 @@ public static IEnumerable<object[]> VisualBasicInlineByteArrayTestData()
890890
@"(New Byte(s.Length - 1) {}).AsMemory(0, s.Length), New CancellationToken()" };
891891
}
892892

893-
[Fact]
893+
[WindowsOnlyFact] // https://github.com/dotnet/roslyn/issues/65081
894894
public Task VB_Fixer_Diagnostic_EnsureSystemNamespaceAutoAddedAsync()
895895
{
896896
string originalCode = @"
@@ -906,10 +906,9 @@ End Sub
906906
End Class
907907
";
908908
string fixedCode = @"
909+
Imports System
909910
Imports System.IO
910911
Imports System.Threading
911-
Imports System
912-
913912
Class C
914913
Public Async Sub M()
915914
Using s As FileStream = New FileStream(""path.txt"", FileMode.Create)
@@ -1211,4 +1210,4 @@ private DiagnosticResult GetVisualBasicResult(int startLine, int startColumn, in
12111210

12121211
#endregion
12131212
}
1214-
}
1213+
}

src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/PreferStreamWriteAsyncMemoryOverloadsTests.cs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
using System.Collections.Generic;
44
using System.Threading.Tasks;
55
using Microsoft.CodeAnalysis.Testing;
6+
using Test.Utilities;
67
using Xunit;
78

89
#pragma warning disable CA1305 // Specify IFormatProvider in string.Format
@@ -566,7 +567,7 @@ public static IEnumerable<object[]> CSharpInlinedByteArrayTestData()
566567
"(new byte[]{ 0xBA, 0x5E, 0xBA, 0x11, 0xF0, 0x07, 0xBA, 0x11 }).AsMemory(0, 8), new CancellationToken()" };
567568
}
568569

569-
[Fact]
570+
[WindowsOnlyFact] // https://github.com/dotnet/roslyn/issues/65081
570571
public Task CS_Fixer_Diagnostic_EnsureSystemNamespaceAutoAddedAsync()
571572
{
572573
string originalCode = @"
@@ -584,10 +585,9 @@ public async void M()
584585
}
585586
}";
586587
string fixedCode = @"
588+
using System;
587589
using System.IO;
588590
using System.Threading;
589-
using System;
590-
591591
class C
592592
{
593593
public async void M()
@@ -850,7 +850,7 @@ public static IEnumerable<object[]> VisualBasicInlinedByteArrayTestData()
850850
@"(New Byte() {&HBA, &H5E, &HBA, &H11, &HF0, &H07, &HBA, &H11}).AsMemory(0, 8), New CancellationToken()" };
851851
}
852852

853-
[Fact]
853+
[WindowsOnlyFact] // https://github.com/dotnet/roslyn/issues/65081
854854
public Task VB_Fixer_Diagnostic_EnsureSystemNamespaceAutoAddedAsync()
855855
{
856856
string originalCode = @"
@@ -866,10 +866,9 @@ End Sub
866866
End Class
867867
";
868868
string fixedCode = @"
869+
Imports System
869870
Imports System.IO
870871
Imports System.Threading
871-
Imports System
872-
873872
Class C
874873
Public Async Sub M()
875874
Using s As FileStream = File.Open(""path.txt"", FileMode.Open)
@@ -1139,4 +1138,4 @@ protected DiagnosticResult GetVisualBasicResult(int startLine, int startColumn,
11391138

11401139
#endregion
11411140
}
1142-
}
1141+
}

src/NetAnalyzers/UnitTests/Microsoft.NetCore.Analyzers/Runtime/UseSpanBasedStringConcatTests.cs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,6 +761,31 @@ public Task WithNonStringNonCharLiteralOperands_NoDiagnostic_VBAsync(string expr
761761
};
762762
return test.RunAsync();
763763
}
764+
765+
[Fact]
766+
public Task TestSystemImportedFromGlobalUsing()
767+
{
768+
var test = new VerifyCS.Test
769+
{
770+
TestState =
771+
{
772+
Sources =
773+
{
774+
"global using System;", CSWithBody("var _ = [|foo + bar.Substring(1)|];"),
775+
},
776+
},
777+
FixedState =
778+
{
779+
Sources =
780+
{
781+
"global using System;", CSWithBody("var _ = string.Concat(foo, bar.AsSpan(1));"),
782+
},
783+
},
784+
ReferenceAssemblies = ReferenceAssemblies.Net.Net50,
785+
LanguageVersion = CodeAnalysis.CSharp.LanguageVersion.CSharp10,
786+
};
787+
return test.RunAsync();
788+
}
764789
#endregion
765790

766791
#region Helpers

src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicPreferStreamAsyncMemoryOverloads.Fixer.vb

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the MIT license. See License.txt in the project root for license information.
22

3+
Imports System.Composition
34
Imports Microsoft.CodeAnalysis
45
Imports Microsoft.CodeAnalysis.CodeFixes
56
Imports Microsoft.CodeAnalysis.Editing
@@ -9,7 +10,7 @@ Imports Microsoft.NetCore.Analyzers.Runtime
910

1011
Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime
1112

12-
<ExportCodeFixProvider(LanguageNames.VisualBasic)>
13+
<ExportCodeFixProvider(LanguageNames.VisualBasic), [Shared]>
1314
Public NotInheritable Class BasicPreferStreamAsyncMemoryOverloadsFixer
1415

1516
Inherits PreferStreamAsyncMemoryOverloadsFixer
@@ -39,25 +40,6 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime
3940
Return Nothing
4041
End Function
4142

42-
Protected Overrides Function IsSystemNamespaceImported(importList As IReadOnlyList(Of SyntaxNode)) As Boolean
43-
For Each import As SyntaxNode In importList
44-
Dim importsStatement = TryCast(import, ImportsStatementSyntax)
45-
If importsStatement IsNot Nothing Then
46-
For Each clause As ImportsClauseSyntax In importsStatement.ImportsClauses
47-
Dim simpleClause = TryCast(clause, SimpleImportsClauseSyntax)
48-
If simpleClause IsNot Nothing Then
49-
Dim identifier = TryCast(simpleClause.Name, IdentifierNameSyntax)
50-
If identifier IsNot Nothing AndAlso String.Equals(identifier.Identifier.Text, "System", StringComparison.OrdinalIgnoreCase) Then
51-
Return True
52-
End If
53-
End If
54-
Next
55-
End If
56-
Next
57-
58-
Return False
59-
End Function
60-
6143
Protected Overrides Function IsPassingZeroAndBufferLength(model As SemanticModel, bufferValueNode As SyntaxNode, offsetValueNode As SyntaxNode, countValueNode As SyntaxNode) As Boolean
6244
' First argument should be an identifier name node
6345
Dim arg1 = TryCast(bufferValueNode, ArgumentSyntax)

src/NetAnalyzers/VisualBasic/Microsoft.NetCore.Analyzers/Runtime/BasicUseSpanBasedStringConcat.Fixer.vb

Lines changed: 0 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ Imports Microsoft.CodeAnalysis
44
Imports Microsoft.CodeAnalysis.CodeFixes
55
Imports Microsoft.CodeAnalysis.Editing
66
Imports Microsoft.CodeAnalysis.Operations
7-
Imports Microsoft.CodeAnalysis.VisualBasic
87
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
98
Imports Microsoft.NetCore.Analyzers.Runtime
109

@@ -22,35 +21,6 @@ Namespace Microsoft.NetCore.VisualBasic.Analyzers.Runtime
2221
Return invocationSyntax.ReplaceNode(oldNameSyntax, newNameSyntax)
2322
End Function
2423

25-
Private Protected Overrides Function IsSystemNamespaceImported(project As Project, namespaceImports As IReadOnlyList(Of SyntaxNode)) As Boolean
26-
27-
Dim options = DirectCast(project.CompilationOptions, VisualBasicCompilationOptions)
28-
If options.GlobalImports.Any(Function(x) String.Compare(x.Name, NameOf(System), StringComparison.OrdinalIgnoreCase) = 0) Then
29-
Return True
30-
End If
31-
32-
For Each node As SyntaxNode In namespaceImports
33-
Dim importsStatement = TryCast(node, ImportsStatementSyntax)
34-
If importsStatement Is Nothing Then
35-
Continue For
36-
End If
37-
38-
For Each importsClause As ImportsClauseSyntax In importsStatement.ImportsClauses
39-
Dim simpleClause = TryCast(importsClause, SimpleImportsClauseSyntax)
40-
Dim identifierName = TryCast(simpleClause?.Name, IdentifierNameSyntax)
41-
If identifierName Is Nothing Then
42-
Continue For
43-
End If
44-
45-
If identifierName.Identifier.ValueText = NameOf(System) Then
46-
Return True
47-
End If
48-
Next
49-
Next
50-
51-
Return False
52-
End Function
53-
5424
Private Protected Overrides Function WalkDownBuiltInImplicitConversionOnConcatOperand(operand As IOperation) As IOperation
5525

5626
Return UseSpanBasedStringConcat.BasicWalkDownBuiltInImplicitConversionOnConcatOperand(operand)

0 commit comments

Comments
 (0)